diff --git a/src/message-runtime.ts b/src/message-runtime.ts index 84dfb02..594f650 100644 --- a/src/message-runtime.ts +++ b/src/message-runtime.ts @@ -15,6 +15,7 @@ import { getLastHumanMessageContent, isPairedRoomJid, getLatestOpenPairedTaskForChat, + updatePairedTask, type ServiceHandoff, type WorkItem, } from './db.js'; @@ -673,6 +674,17 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): { }); return formatRoomReviewReadyMessage(result); }, + resetPairedTask: () => { + if (isPairedRoomJid(chatJid)) { + const task = getLatestOpenPairedTaskForChat(chatJid); + if (task) { + updatePairedTask(task.id, { + status: 'completed', + updated_at: new Date().toISOString(), + }); + } + } + }, killProcess: () => deps.queue.killProcess(chatJid), }, }); diff --git a/src/paired-execution-context.ts b/src/paired-execution-context.ts index e8cf732..3e19647 100644 --- a/src/paired-execution-context.ts +++ b/src/paired-execution-context.ts @@ -32,9 +32,16 @@ import type { // Reviewer verdict detection // --------------------------------------------------------------------------- -type ReviewerVerdict = 'done' | 'done_with_concerns' | 'blocked' | 'needs_context' | 'continue'; +type ReviewerVerdict = + | 'done' + | 'done_with_concerns' + | 'blocked' + | 'needs_context' + | 'continue'; -function classifyReviewerVerdict(summary: string | null | undefined): ReviewerVerdict { +function classifyReviewerVerdict( + summary: string | null | undefined, +): ReviewerVerdict { if (!summary) return 'continue'; const firstLine = summary.trimStart().split('\n')[0].trim(); if (/\bBLOCKED\b/.test(firstLine)) return 'blocked'; diff --git a/src/session-commands.ts b/src/session-commands.ts index 3931269..3dc6d09 100644 --- a/src/session-commands.ts +++ b/src/session-commands.ts @@ -80,6 +80,8 @@ export interface SessionCommandDeps { /** Whether the denied sender would normally be allowed to interact (for denial messages). */ canSenderInteract: (msg: NewMessage) => boolean; markReviewReady: () => Promise; + /** Reset/complete the active paired task so ping-pong stops. */ + resetPairedTask?: () => void; /** Kill the currently running agent process for this group. Returns true if a process was killed. */ killProcess: () => boolean; } @@ -159,6 +161,7 @@ export async function handleSessionCommand(opts: { if (command === '/clear') { deps.closeStdin(); deps.clearSession({ allRoles: true }); + deps.resetPairedTask?.(); deps.advanceCursor(cmdMsg.timestamp); await deps.sendMessage( 'Current session cleared. The next message will start a new conversation.',