feat: foundation for subagent trajectories (Stage 1)

This commit is contained in:
Aishanee Shah
2026-05-14 16:39:47 +00:00
parent a9648de39f
commit c98fdf6df2
2 changed files with 35 additions and 27 deletions

View File

@@ -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,
});
});
});

View File

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