From ae12b157be8ea937bae952ec942ea41a8a3cc2dc Mon Sep 17 00:00:00 2001 From: ejclaw Date: Tue, 7 Apr 2026 03:38:16 +0900 Subject: [PATCH] Persist paired turn output before final delivery --- src/message-agent-executor.test.ts | 8 ++++- src/message-agent-executor.ts | 51 +++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/message-agent-executor.test.ts b/src/message-agent-executor.test.ts index 8573b0b..75b2a10 100644 --- a/src/message-agent-executor.test.ts +++ b/src/message-agent-executor.test.ts @@ -1318,6 +1318,7 @@ describe('runAgentForGroup room memory', () => { it('stores reviewer turn output before transitioning the paired task back to active', async () => { const group = { ...makeGroup(), folder: 'test-group' }; const deps = makeDeps(); + const onOutput = vi.fn(async () => {}); vi.mocked(serviceRouting.getEffectiveChannelLease).mockReturnValue({ chat_jid: 'group@test', @@ -1397,7 +1398,7 @@ describe('runAgentForGroup room memory', () => { chatJid: 'group@test', runId: 'run-reviewer-output-order', forcedRole: 'reviewer', - onOutput: async () => {}, + onOutput, }); expect(result).toBe('success'); @@ -1407,6 +1408,11 @@ describe('runAgentForGroup room memory', () => { 'reviewer', 'DONE_WITH_CONCERNS\nreviewer feedback', ); + expect( + vi.mocked(db.insertPairedTurnOutput).mock.invocationCallOrder[0], + ).toBeLessThan( + onOutput.mock.invocationCallOrder[0], + ); expect( vi.mocked(db.insertPairedTurnOutput).mock.invocationCallOrder[0], ).toBeLessThan( diff --git a/src/message-agent-executor.ts b/src/message-agent-executor.ts index 205dea0..b165207 100644 --- a/src/message-agent-executor.ts +++ b/src/message-agent-executor.ts @@ -315,9 +315,29 @@ export async function runAgentForGroup( const effectivePrompt = moaEnrichedPrompt; let pairedExecutionStatus: 'succeeded' | 'failed' = 'failed'; let pairedExecutionSummary: string | null = null; - let pairedFullOutput: string | null = null; + let pairedFinalOutput: string | null = null; let pairedExecutionCompleted = false; let pairedSawOutput = false; + let pairedTurnOutputPersisted = false; + + const persistPairedTurnOutputIfNeeded = (completedRole: PairedRoomRole) => { + if ( + !pairedExecutionContext || + pairedTurnOutputPersisted || + !pairedFinalOutput || + pairedFinalOutput.length === 0 + ) { + return; + } + const turnNumber = getLatestTurnNumber(pairedExecutionContext.task.id) + 1; + insertPairedTurnOutput( + pairedExecutionContext.task.id, + turnNumber, + completedRole, + pairedFinalOutput, + ); + pairedTurnOutputPersisted = true; + }; const maybeHandoffToCodex = ( reason: AgentTriggerReason, @@ -483,7 +503,6 @@ export async function runAgentForGroup( if (outputText && outputText.length > 0) { pairedExecutionSummary = outputText.slice(0, 500); - pairedFullOutput = outputText; } else if ( typeof output.error === 'string' && output.error.length > 0 @@ -542,6 +561,22 @@ export async function runAgentForGroup( if (outputText && outputText.length > 0) { streamedOutputHandler.markVisibleOutput(); } + if ( + outputPhase === 'final' && + output.status === 'success' && + outputText && + outputText.length > 0 + ) { + pairedFinalOutput = outputText; + try { + persistPairedTurnOutputIfNeeded(roomRoleContext?.role ?? 'owner'); + } catch (err) { + log.warn( + { pairedTaskId: pairedExecutionContext?.task.id ?? null, err }, + 'Failed to persist paired turn output before delivery', + ); + } + } if (onOutput) { await onOutput(output); } @@ -941,17 +976,9 @@ export async function runAgentForGroup( !pairedSawOutput ? 'failed' : completionStatus; - // Store full output for direct inter-agent data passing (Discord-independent). - if (pairedFullOutput && effectiveStatus === 'succeeded') { + if (effectiveStatus === 'succeeded') { try { - const turnNumber = - getLatestTurnNumber(pairedExecutionContext.task.id) + 1; - insertPairedTurnOutput( - pairedExecutionContext.task.id, - turnNumber, - completedRole, - pairedFullOutput, - ); + persistPairedTurnOutputIfNeeded(completedRole); } catch (err) { log.warn( { pairedTaskId: pairedExecutionContext.task.id, err },