Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions cli/src/agent/messageConverter.roundSummary.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expect, it } from 'vitest';
import { convertAgentMessage } from './messageConverter';

describe('convertAgentMessage round summary', () => {
it('converts the generic carrier into the HAPI-owned round-summary wire contract', () => {
const converted = convertAgentMessage({
type: 'round_summary',
summary: {
usage: { inputTokens: 120, outputTokens: 30, cacheReadInputTokens: 90, cacheCreationInputTokens: 10 },
modelUsage: {
'openai/gpt-5.4': { inputTokens: 120, outputTokens: 30, cacheReadInputTokens: 90, cacheCreationInputTokens: 10 }
},
totalCostUsd: 0.034,
numTurns: 2,
durationMs: 1_250
}
} as never);

expect(converted).toEqual({
type: 'round-summary',
summary: {
usage: { input_tokens: 120, output_tokens: 30, cache_read_input_tokens: 90, cache_creation_input_tokens: 10 },
modelUsage: {
'openai/gpt-5.4': { inputTokens: 120, outputTokens: 30, cacheReadInputTokens: 90, cacheCreationInputTokens: 10 }
},
total_cost_usd: 0.034,
num_turns: 2,
duration_ms: 1_250
}
});
});
});
26 changes: 26 additions & 0 deletions cli/src/agent/messageConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ export type CodexMessage =
}
| { type: 'plan'; entries: PlanItem[] }
| { type: 'error'; message: string }
| {
type: 'round-summary';
summary: {
usage: { input_tokens: number; output_tokens: number; cache_read_input_tokens: number; cache_creation_input_tokens: number };
modelUsage: Record<string, { inputTokens: number; outputTokens: number; cacheReadInputTokens: number; cacheCreationInputTokens: number }>;
total_cost_usd?: number;
num_turns: number;
duration_ms: number;
};
}
| {
type: 'generated-image';
imageId: string;
Expand Down Expand Up @@ -120,6 +130,22 @@ export function convertAgentMessage(message: AgentMessage, model?: string | null
type: 'plan',
entries: message.items
};
case 'round_summary':
return {
type: 'round-summary',
summary: {
usage: {
input_tokens: message.summary.usage.inputTokens,
output_tokens: message.summary.usage.outputTokens,
cache_read_input_tokens: message.summary.usage.cacheReadInputTokens,
cache_creation_input_tokens: message.summary.usage.cacheCreationInputTokens
},
modelUsage: message.summary.modelUsage,
...(message.summary.totalCostUsd !== undefined ? { total_cost_usd: message.summary.totalCostUsd } : {}),
num_turns: message.summary.numTurns,
duration_ms: message.summary.durationMs
}
};
case 'generated_image':
return {
type: 'generated-image',
Expand Down
16 changes: 16 additions & 0 deletions cli/src/agent/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ export type PlanItem = {
status: 'pending' | 'in_progress' | 'completed';
};

export type AgentRoundModelUsage = {
inputTokens: number;
outputTokens: number;
cacheReadInputTokens: number;
cacheCreationInputTokens: number;
};

export type AgentRoundSummary = {
usage: AgentRoundModelUsage;
modelUsage: Record<string, AgentRoundModelUsage>;
totalCostUsd?: number;
numTurns: number;
durationMs: number;
};

export type AgentMessage =
| { type: 'text'; text: string; id?: string; live?: boolean; streamSnapshot?: boolean }
| { type: 'reasoning'; text: string; id?: string; live?: boolean }
Expand Down Expand Up @@ -57,6 +72,7 @@ export type AgentMessage =
| { type: 'plan'; items: PlanItem[] }
| { type: 'generated_image'; imageId: string; fileName: string; mimeType: string; source?: InlineMediaSource }
| { type: 'turn_complete'; stopReason: string }
| { type: 'round_summary'; summary: AgentRoundSummary }
| { type: 'error'; message: string };

export type PermissionOption = {
Expand Down
2 changes: 2 additions & 0 deletions cli/src/copilot/copilotRemoteLauncher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ export class CopilotRemoteLauncher extends RemoteLauncherBase {
case 'turn_complete':
this.messageBuffer.addMessage('Turn complete', 'status');
break;
case 'round_summary':
break;
default: {
const _exhaustive: never = message;
return _exhaustive;
Expand Down
1 change: 1 addition & 0 deletions cli/src/dsh/dshRemoteLauncher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export class DshRemoteLauncher extends RemoteLauncherBase {
case 'tool_call':
case 'tool_result':
case 'plan':
case 'round_summary':
break
default: {
const _exhaustive: never = message
Expand Down
2 changes: 2 additions & 0 deletions cli/src/grok/grokRemoteLauncher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,8 @@ class GrokRemoteLauncher extends RemoteLauncherBase {
case 'turn_complete':
this.messageBuffer.addMessage('Turn complete', 'status')
break
case 'round_summary':
break
case 'usage':
break
default: {
Expand Down
2 changes: 2 additions & 0 deletions cli/src/kimi/kimiRemoteLauncher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ class KimiRemoteLauncher extends RemoteLauncherBase {
case 'turn_complete':
this.messageBuffer.addMessage('Turn complete', 'status');
break;
case 'round_summary':
break;
default: {
const _exhaustive: never = message;
return _exhaustive;
Expand Down
Loading
Loading