feat: auto-notify user on paired task completion with @mention

- done →  작업 완료
- escalated → ⚠️ 자동 해결 불가
- arbiter_escalated → ⚠️ 중재자: 사람 개입 필요
- stopped → no notification (user initiated)
This commit is contained in:
Eyejoker
2026-03-31 04:29:24 +09:00
parent 60408cc1fa
commit 7085711a7f
2 changed files with 29 additions and 1 deletions

View File

@@ -48,7 +48,11 @@ vi.mock('./platform-prompts.js', () => ({
vi.mock('./db.js', () => ({ vi.mock('./db.js', () => ({
createServiceHandoff: vi.fn(), createServiceHandoff: vi.fn(),
getAllTasks: vi.fn(() => []), getAllTasks: vi.fn(() => []),
getLastHumanMessageSender: vi.fn(() => null),
getLatestOpenPairedTaskForChat: vi.fn(() => undefined), getLatestOpenPairedTaskForChat: vi.fn(() => undefined),
getLatestTurnNumber: vi.fn(() => 0),
getPairedTaskById: vi.fn(() => undefined),
insertPairedTurnOutput: vi.fn(),
})); }));
vi.mock('./service-routing.js', () => ({ vi.mock('./service-routing.js', () => ({

View File

@@ -131,7 +131,8 @@ export async function runAgentForGroup(
group.agentType, group.agentType,
); );
// Arbiter always starts fresh — never resume a previous session // Arbiter always starts fresh — never resume a previous session
const sessionId = activeRole === 'arbiter' ? undefined : sessions[sessionFolder]; const sessionId =
activeRole === 'arbiter' ? undefined : sessions[sessionFolder];
const memoryBriefing = sessionId const memoryBriefing = sessionId
? undefined ? undefined
: await buildRoomMemoryBriefing({ : await buildRoomMemoryBriefing({
@@ -1047,6 +1048,29 @@ export async function runAgentForGroup(
} }
} }
// Notify user when paired task reaches a terminal state that requires attention.
if (pairedExecutionContext) {
const finishedTask = getPairedTaskById(pairedExecutionContext.task.id);
if (finishedTask?.status === 'completed' && finishedTask.completion_reason) {
const sender = getLastHumanMessageSender(chatJid);
const mention = sender ? `<@${sender}>` : '';
const notifications: Record<string, string> = {
done: `${mention} ✅ 작업 완료.`,
escalated: `${mention} ⚠️ 자동 해결 불가 — 확인이 필요합니다.`,
arbiter_escalated: `${mention} ⚠️ 중재자 판단: 사람 개입이 필요합니다.`,
};
const message = notifications[finishedTask.completion_reason];
if (message) {
await args.onOutput?.({
status: 'success',
result: message,
output: { visibility: 'public', text: message },
phase: 'final',
});
}
}
}
// After owner/reviewer completes, enqueue the next turn so // After owner/reviewer completes, enqueue the next turn so
// the message loop picks it up without waiting for a new message. // the message loop picks it up without waiting for a new message.
// Skip if owner produced no output — likely interrupted by /stop. // Skip if owner produced no output — likely interrupted by /stop.