From c98fdf6df2046a46425f89622a1e2c3c5e305ccb Mon Sep 17 00:00:00 2001 From: Aishanee Shah Date: Thu, 14 May 2026 16:39:47 +0000 Subject: [PATCH] feat: foundation for subagent trajectories (Stage 1) --- .../cli/src/ui/commands/chatCommand.test.ts | 34 ++++--------------- .../cli/src/ui/utils/historyExportUtils.ts | 28 +++++++++++++++ 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/packages/cli/src/ui/commands/chatCommand.test.ts b/packages/cli/src/ui/commands/chatCommand.test.ts index 74084b9ef7..2df3c50f24 100644 --- a/packages/cli/src/ui/commands/chatCommand.test.ts +++ b/packages/cli/src/ui/commands/chatCommand.test.ts @@ -194,12 +194,7 @@ describe('chatCommand', () => { ]); result = await saveCommand?.action?.(mockContext, tag); expect(mockSaveCheckpoint).toHaveBeenCalledWith( - { - history: expect.any(Array), - authType: AuthType.LOGIN_WITH_GOOGLE, - trajectories: {}, - messages: [], - }, + { history: expect.any(Array), authType: AuthType.LOGIN_WITH_GOOGLE }, tag, ); expect(result).toEqual({ @@ -241,12 +236,7 @@ describe('chatCommand', () => { expect(mockCheckpointExists).not.toHaveBeenCalled(); // Should skip existence check expect(mockSaveCheckpoint).toHaveBeenCalledWith( - { - history, - authType: AuthType.LOGIN_WITH_GOOGLE, - trajectories: {}, - messages: [], - }, + { history, authType: AuthType.LOGIN_WITH_GOOGLE }, tag, ); expect(result).toEqual({ @@ -483,10 +473,8 @@ describe('chatCommand', () => { 'gemini-conversation-1234567890.json', ); expect(mockExport).toHaveBeenCalledWith({ - messages: [], - filePath: expectedPath, - trajectories: {}, history: mockHistory, + filePath: expectedPath, }); expect(result).toEqual({ type: 'message', @@ -500,10 +488,8 @@ describe('chatCommand', () => { const result = await shareCommand?.action?.(mockContext, filePath); const expectedPath = path.join(process.cwd(), 'my-chat.json'); expect(mockExport).toHaveBeenCalledWith({ - messages: [], - filePath: expectedPath, - trajectories: {}, history: mockHistory, + filePath: expectedPath, }); expect(result).toEqual({ type: 'message', @@ -517,10 +503,8 @@ describe('chatCommand', () => { const result = await shareCommand?.action?.(mockContext, filePath); const expectedPath = path.join(process.cwd(), 'my-chat.md'); expect(mockExport).toHaveBeenCalledWith({ - messages: [], - filePath: expectedPath, - trajectories: {}, history: mockHistory, + filePath: expectedPath, }); expect(result).toEqual({ type: 'message', @@ -569,10 +553,8 @@ describe('chatCommand', () => { await shareCommand?.action?.(mockContext, filePath); const expectedPath = path.join(process.cwd(), 'my-chat.json'); expect(mockExport).toHaveBeenCalledWith({ - messages: [], - filePath: expectedPath, - trajectories: {}, history: mockHistory, + filePath: expectedPath, }); }); @@ -581,10 +563,8 @@ describe('chatCommand', () => { await shareCommand?.action?.(mockContext, filePath); const expectedPath = path.join(process.cwd(), 'my-chat.md'); expect(mockExport).toHaveBeenCalledWith({ - messages: [], - filePath: expectedPath, - trajectories: {}, history: mockHistory, + filePath: expectedPath, }); }); }); diff --git a/packages/cli/src/ui/utils/historyExportUtils.ts b/packages/cli/src/ui/utils/historyExportUtils.ts index a89c31c652..8751a80959 100644 --- a/packages/cli/src/ui/utils/historyExportUtils.ts +++ b/packages/cli/src/ui/utils/historyExportUtils.ts @@ -57,10 +57,18 @@ export function serializeHistoryToMarkdown( */ export interface ExportHistoryOptions { /** +<<<<<<< HEAD * Optional full message records which contain metadata like agentId for tool calls, * providing the link between history and trajectories. */ messages?: MessageRecord[]; +======= + * Full message records which contain metadata like agentId for tool calls, + * providing the link between history and trajectories. + * This is the primary source of truth. + */ + messages: MessageRecord[]; +>>>>>>> 0e381108e (feat: foundation for subagent trajectories (Stage 1)) /** The file path to export to. */ filePath: string; /** Optional subagent trajectories to include. */ @@ -81,18 +89,38 @@ export async function exportHistoryToFile( const { messages, filePath, +<<<<<<< HEAD trajectories: _trajectories, // Collected but not yet included in Stage 1 JSON output +======= + trajectories, +>>>>>>> 0e381108e (feat: foundation for subagent trajectories (Stage 1)) history: providedHistory, } = options; const extension = path.extname(filePath).toLowerCase(); let content: string; if (extension === '.json') { +<<<<<<< HEAD // Stage 1 & 2: Maintain legacy behavior - only export the raw history array. // Trajectories and messages are collected but not yet included in Stage 2 JSON output. content = JSON.stringify(providedHistory ?? [], null, 2); } else if (extension === '.md') { const history = providedHistory ?? reconstructHistory(messages ?? []); +======= + // In Stage 1, we still save the raw history if provided, for backward compatibility + // with existing parsers, but we also include messages and trajectories. + if (trajectories && Object.keys(trajectories).length > 0) { + content = JSON.stringify( + { history: providedHistory, messages, trajectories }, + null, + 2, + ); + } else { + content = JSON.stringify(providedHistory ?? messages, null, 2); + } + } else if (extension === '.md') { + const history = providedHistory ?? reconstructHistory(messages); +>>>>>>> 0e381108e (feat: foundation for subagent trajectories (Stage 1)) content = serializeHistoryToMarkdown(history); } else { throw new Error(