diff --git a/src/db.test.ts b/src/db.test.ts index 6b9fce3..76e2a5f 100644 --- a/src/db.test.ts +++ b/src/db.test.ts @@ -556,7 +556,8 @@ describe('paired task state', () => { reviewer_service_id: 'codex-review', title: 'wire up workspaces', source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: '2026-03-28T00:00:00.000Z', @@ -594,7 +595,8 @@ describe('paired task state', () => { reviewer_service_id: 'codex-review', title: null, source_ref: null, - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: '2026-03-28T00:00:00.000Z', diff --git a/src/db.ts b/src/db.ts index b39e5b8..9cb7f72 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1550,6 +1550,13 @@ export function deleteSession(groupFolder: string): void { ).run(groupFolder, SERVICE_AGENT_TYPE); } +export function deleteAllSessionsForGroup(groupFolder: string): void { + db.prepare('DELETE FROM service_sessions WHERE group_folder = ?').run( + groupFolder, + ); + db.prepare('DELETE FROM sessions WHERE group_folder = ?').run(groupFolder); +} + export function getAllSessions(): Record { const serviceRows = db .prepare( diff --git a/src/index.ts b/src/index.ts index 558c47d..bf2313c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -37,6 +37,7 @@ import { initDatabase, setRegisteredGroup, setRouterState, + deleteAllSessionsForGroup, deleteSession, setSession, storeChatMetadata, @@ -208,9 +209,16 @@ function saveState(): void { setRouterState('last_agent_seq', JSON.stringify(lastAgentTimestamp)); } -function clearSession(groupFolder: string): void { +function clearSession( + groupFolder: string, + opts?: { allRoles?: boolean }, +): void { delete sessions[groupFolder]; - deleteSession(groupFolder); + if (opts?.allRoles) { + deleteAllSessionsForGroup(groupFolder); + } else { + deleteSession(groupFolder); + } } function registerGroup(jid: string, group: RegisteredGroup): void { diff --git a/src/message-agent-executor.test.ts b/src/message-agent-executor.test.ts index 76259db..0bd3753 100644 --- a/src/message-agent-executor.test.ts +++ b/src/message-agent-executor.test.ts @@ -326,7 +326,8 @@ describe('runAgentForGroup room memory', () => { title: null, source_ref: 'HEAD', review_requested_at: null, - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, status: 'active', created_at: '2026-03-29T00:00:00.000Z', updated_at: '2026-03-29T00:00:00.000Z', @@ -386,7 +387,8 @@ describe('runAgentForGroup room memory', () => { reviewer_service_id: 'codex-main', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: '2026-03-28T00:00:00.000Z', @@ -467,7 +469,8 @@ describe('runAgentForGroup room memory', () => { reviewer_service_id: 'claude', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: '2026-03-28T00:00:00.000Z', diff --git a/src/message-runtime.test.ts b/src/message-runtime.test.ts index 09173c7..c3a3b4c 100644 --- a/src/message-runtime.test.ts +++ b/src/message-runtime.test.ts @@ -262,7 +262,8 @@ describe('createMessageRuntime', () => { reviewer_service_id: 'codex-main', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: '2026-03-29T00:00:00.000Z', status: 'review_ready', created_at: '2026-03-29T00:00:00.000Z', @@ -354,7 +355,8 @@ describe('createMessageRuntime', () => { reviewer_service_id: 'codex-main', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: '2026-03-29T00:00:00.000Z', diff --git a/src/message-runtime.ts b/src/message-runtime.ts index cb98403..d55cdb6 100644 --- a/src/message-runtime.ts +++ b/src/message-runtime.ts @@ -101,7 +101,7 @@ export interface MessageRuntimeDeps { getLastAgentTimestamps: () => Record; saveState: () => void; persistSession: (groupFolder: string, sessionId: string) => void; - clearSession: (groupFolder: string) => void; + clearSession: (groupFolder: string, opts?: { allRoles?: boolean }) => void; } export function createMessageRuntime(deps: MessageRuntimeDeps): { @@ -542,7 +542,7 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): { deps.queue.closeStdin(chatJid, { reason: 'session-command', }), - clearSession: () => deps.clearSession(group.folder), + clearSession: (opts) => deps.clearSession(group.folder, opts), advanceCursor: (cursorOrTimestamp) => { advanceLastAgentCursor( deps.getLastAgentTimestamps(), diff --git a/src/paired-execution-context.test.ts b/src/paired-execution-context.test.ts index c74c3e9..628a255 100644 --- a/src/paired-execution-context.test.ts +++ b/src/paired-execution-context.test.ts @@ -215,7 +215,8 @@ describe('paired execution context', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: '2026-03-28T00:00:00.000Z', status: 'review_ready', created_at: '2026-03-28T00:00:00.000Z', @@ -246,7 +247,8 @@ describe('paired execution context', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: '2026-03-28T00:00:00.000Z', status: 'review_ready', created_at: '2026-03-28T00:00:00.000Z', @@ -280,7 +282,8 @@ describe('paired execution context', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: '2026-03-28T00:00:00.000Z', @@ -320,7 +323,8 @@ describe('paired execution context', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: '2026-03-28T00:00:00.000Z', status: 'in_review', created_at: '2026-03-28T00:00:00.000Z', @@ -374,7 +378,8 @@ describe('paired execution context', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: '2026-03-28T00:00:00.000Z', @@ -388,7 +393,8 @@ describe('paired execution context', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: '2026-03-28T00:00:00.000Z', status: 'review_ready', created_at: '2026-03-28T00:00:00.000Z', @@ -455,7 +461,8 @@ describe('paired execution context', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: '2026-03-28T00:00:00.000Z', @@ -469,7 +476,8 @@ describe('paired execution context', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: '2026-03-28T00:00:00.000Z', status: 'review_ready', created_at: '2026-03-28T00:00:00.000Z', @@ -511,7 +519,8 @@ describe('paired execution context', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: '2026-03-28T00:00:00.000Z', @@ -525,7 +534,8 @@ describe('paired execution context', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: '2026-03-29T00:00:00.000Z', status: 'review_ready', created_at: '2026-03-28T00:00:00.000Z', diff --git a/src/paired-execution-context.ts b/src/paired-execution-context.ts index 4ba55b1..3a73b72 100644 --- a/src/paired-execution-context.ts +++ b/src/paired-execution-context.ts @@ -1,7 +1,11 @@ import { execFileSync } from 'child_process'; import crypto from 'crypto'; -import { SERVICE_ID, normalizeServiceId, PAIRED_MAX_ROUND_TRIPS } from './config.js'; +import { + SERVICE_ID, + normalizeServiceId, + PAIRED_MAX_ROUND_TRIPS, +} from './config.js'; import { createPairedTask, getLatestPairedTaskForChat, @@ -228,7 +232,11 @@ export function completePairedExecutionContext(args: { if (role === 'owner') { if (task.round_trip_count >= PAIRED_MAX_ROUND_TRIPS) { logger.info( - { taskId, roundTrips: task.round_trip_count, max: PAIRED_MAX_ROUND_TRIPS }, + { + taskId, + roundTrips: task.round_trip_count, + max: PAIRED_MAX_ROUND_TRIPS, + }, 'Round trip limit reached, skipping auto-review', ); return; diff --git a/src/paired-workspace-manager.test.ts b/src/paired-workspace-manager.test.ts index 8b576d3..0bdb289 100644 --- a/src/paired-workspace-manager.test.ts +++ b/src/paired-workspace-manager.test.ts @@ -74,7 +74,8 @@ describe('paired workspace manager', () => { reviewer_service_id: 'codex-review', title: 'review the owner changes', source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: now, @@ -162,7 +163,8 @@ describe('paired workspace manager', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: now, @@ -214,7 +216,8 @@ describe('paired workspace manager', () => { reviewer_service_id: 'codex-review', title: 'cross service review', source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: now, @@ -289,7 +292,8 @@ describe('paired workspace manager', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: now, @@ -363,7 +367,8 @@ describe('paired workspace manager', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: now, @@ -503,7 +508,8 @@ describe('paired workspace manager', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: now, @@ -564,7 +570,8 @@ describe('paired workspace manager', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: now, @@ -619,7 +626,8 @@ describe('paired workspace manager', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: '2026-03-28T00:01:00.000Z', status: 'review_ready', created_at: now, @@ -680,7 +688,8 @@ describe('paired workspace manager', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: now, @@ -743,7 +752,8 @@ describe('paired workspace manager', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: now, @@ -831,7 +841,8 @@ describe('paired workspace manager', () => { reviewer_service_id: 'codex-review', title: null, source_ref: 'HEAD', - plan_notes: null, round_trip_count: 0, + plan_notes: null, + round_trip_count: 0, review_requested_at: null, status: 'active', created_at: now, diff --git a/src/session-commands.ts b/src/session-commands.ts index 7fe93b7..3931269 100644 --- a/src/session-commands.ts +++ b/src/session-commands.ts @@ -73,7 +73,7 @@ export interface SessionCommandDeps { onOutput: (result: AgentResult) => Promise, ) => Promise<'success' | 'error'>; closeStdin: () => void; - clearSession: () => void; + clearSession: (opts?: { allRoles?: boolean }) => void; advanceCursor: (timestamp: string) => void; formatMessages: (msgs: NewMessage[], timezone: string) => string; isAdminSender: (msg: NewMessage) => boolean; @@ -158,7 +158,7 @@ export async function handleSessionCommand(opts: { if (command === '/clear') { deps.closeStdin(); - deps.clearSession(); + deps.clearSession({ allRoles: true }); deps.advanceCursor(cmdMsg.timestamp); await deps.sendMessage( 'Current session cleared. The next message will start a new conversation.',