From 7085711a7fd58b32d52b1abe4bc26f4e5a1aa964 Mon Sep 17 00:00:00 2001 From: Eyejoker Date: Tue, 31 Mar 2026 04:29:24 +0900 Subject: [PATCH] feat: auto-notify user on paired task completion with @mention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - done → ✅ 작업 완료 - escalated → ⚠️ 자동 해결 불가 - arbiter_escalated → ⚠️ 중재자: 사람 개입 필요 - stopped → no notification (user initiated) --- src/message-agent-executor.test.ts | 4 ++++ src/message-agent-executor.ts | 26 +++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/message-agent-executor.test.ts b/src/message-agent-executor.test.ts index 6908b4e..2df7f9c 100644 --- a/src/message-agent-executor.test.ts +++ b/src/message-agent-executor.test.ts @@ -48,7 +48,11 @@ vi.mock('./platform-prompts.js', () => ({ vi.mock('./db.js', () => ({ createServiceHandoff: vi.fn(), getAllTasks: vi.fn(() => []), + getLastHumanMessageSender: vi.fn(() => null), getLatestOpenPairedTaskForChat: vi.fn(() => undefined), + getLatestTurnNumber: vi.fn(() => 0), + getPairedTaskById: vi.fn(() => undefined), + insertPairedTurnOutput: vi.fn(), })); vi.mock('./service-routing.js', () => ({ diff --git a/src/message-agent-executor.ts b/src/message-agent-executor.ts index a9f5a29..e28008a 100644 --- a/src/message-agent-executor.ts +++ b/src/message-agent-executor.ts @@ -131,7 +131,8 @@ export async function runAgentForGroup( group.agentType, ); // 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 ? undefined : 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 = { + 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 // the message loop picks it up without waiting for a new message. // Skip if owner produced no output — likely interrupted by /stop.