diff --git a/.changeset/fix-turn-prompt-identity.md b/.changeset/fix-turn-prompt-identity.md new file mode 100644 index 000000000..6eaaa3bb2 --- /dev/null +++ b/.changeset/fix-turn-prompt-identity.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix duplicate user messages in transcript clients. diff --git a/packages/agent-core-v2/docs/wire-manifest.d.ts b/packages/agent-core-v2/docs/wire-manifest.d.ts index ff1de49f7..ba37d2d2b 100644 --- a/packages/agent-core-v2/docs/wire-manifest.d.ts +++ b/packages/agent-core-v2/docs/wire-manifest.d.ts @@ -791,6 +791,7 @@ interface TurnPromptPayload { input: readonly ContentPart[]; /** PromptOrigin */ origin: 'user' | 'skill_activation' | 'plugin_command' | 'injection' | 'shell_command' | 'compaction_summary' | 'system_trigger' | 'task' | 'cron_job' | 'cron_missed' | 'hook_result' | 'retry'; + promptId?: string; } /** diff --git a/packages/agent-core-v2/src/agent/loop/loopService.ts b/packages/agent-core-v2/src/agent/loop/loopService.ts index 1f33dd26b..3c7ad8907 100644 --- a/packages/agent-core-v2/src/agent/loop/loopService.ts +++ b/packages/agent-core-v2/src/agent/loop/loopService.ts @@ -465,7 +465,12 @@ export class AgentLoopService extends Disposable implements IAgentLoopService { private startTurn(job: TurnJob): void { const origin = job.seed.origin; void this.dispatcher.dispatch( - new TurnPrompt({ agentId: this.scopeContext.agentId, input: job.seed.input, origin }), + new TurnPrompt({ + agentId: this.scopeContext.agentId, + input: job.seed.input, + origin, + promptId: job.seed.promptId, + }), ); job.turn.state = 'running'; this.activeTurnJob = job; @@ -473,6 +478,7 @@ export class AgentLoopService extends Disposable implements IAgentLoopService { new TurnStarted({ agentId: this.scopeContext.agentId, turnId: job.turn.id, + promptId: job.seed.promptId, origin, prompt: isDisplayablePromptOrigin(origin) ? turnPromptText(job.seed.input, origin) : undefined, promptAttachments: turnPromptAttachments(job.seed.input, origin), diff --git a/packages/agent-core-v2/src/agent/loop/turnEvents.ts b/packages/agent-core-v2/src/agent/loop/turnEvents.ts index 3dbdd396b..1199c3ebc 100644 --- a/packages/agent-core-v2/src/agent/loop/turnEvents.ts +++ b/packages/agent-core-v2/src/agent/loop/turnEvents.ts @@ -31,6 +31,7 @@ export type TurnPromptAttachment = export interface TurnStartedPayload { readonly agentId: string; readonly turnId: number; + readonly promptId?: string; readonly origin: PromptOrigin; readonly prompt?: string; readonly promptAttachments?: readonly TurnPromptAttachment[]; diff --git a/packages/agent-core-v2/src/agent/loop/turnOps.ts b/packages/agent-core-v2/src/agent/loop/turnOps.ts index 710003dcc..95029d642 100644 --- a/packages/agent-core-v2/src/agent/loop/turnOps.ts +++ b/packages/agent-core-v2/src/agent/loop/turnOps.ts @@ -33,7 +33,12 @@ const turnInputShape = { origin: z.custom(), }; -const turnPromptSchema = z.object(turnInputShape); +const turnPromptSchema = z.object({ + agentId: z.string(), + input: z.custom(), + origin: z.custom(), + promptId: z.string().optional(), +}); export class TurnPrompt extends AgentEvent2> { static override readonly type = 'turn.prompt'; @@ -44,6 +49,7 @@ export interface TurnPrompt { readonly agentId: string; readonly input: readonly ContentPart[]; readonly origin: PromptOrigin; + readonly promptId?: string; } const turnSteerSchema = z.object(turnInputShape); diff --git a/packages/agent-core-v2/test/agent/loop/loop.test.ts b/packages/agent-core-v2/test/agent/loop/loop.test.ts index ec7138704..7c19d9663 100644 --- a/packages/agent-core-v2/test/agent/loop/loop.test.ts +++ b/packages/agent-core-v2/test/agent/loop/loop.test.ts @@ -72,8 +72,8 @@ describe('Agent loop', () => { [wire] prompt.accepted { "agentId": "main", "promptId": "", "content": [ { "type": "text", "text": "Hello" } ], "time": "