fix: /clear now resets both owner and reviewer sessions in paired rooms

This commit is contained in:
Eyejoker
2026-03-29 19:00:42 +09:00
parent a1e5418a2f
commit 9e0de1297c
10 changed files with 87 additions and 36 deletions

View File

@@ -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',

View File

@@ -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<string, string> {
const serviceRows = db
.prepare(

View File

@@ -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 {

View File

@@ -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',

View File

@@ -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',

View File

@@ -101,7 +101,7 @@ export interface MessageRuntimeDeps {
getLastAgentTimestamps: () => Record<string, string>;
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(),

View File

@@ -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',

View File

@@ -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;

View File

@@ -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,

View File

@@ -73,7 +73,7 @@ export interface SessionCommandDeps {
onOutput: (result: AgentResult) => Promise<void>,
) => 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.',