diff --git a/packages/kap-server/src/services/transcript/coreEventMap.ts b/packages/kap-server/src/services/transcript/coreEventMap.ts index a1643bb36..26397d8c9 100644 --- a/packages/kap-server/src/services/transcript/coreEventMap.ts +++ b/packages/kap-server/src/services/transcript/coreEventMap.ts @@ -26,6 +26,8 @@ import type { PromptCompleted, PromptSteered, } from '@moonshot-ai/agent-core-v2/agent/prompt/promptService'; +import type { PromptAccepted } from '@moonshot-ai/agent-core-v2/agent/prompt/promptOps'; +import type { PromptQueued } from '@moonshot-ai/agent-core-v2/agent/prompt/promptService'; import type { ShellCompleted, ShellOutput, @@ -89,6 +91,8 @@ export interface ProjectorInteraction { type PlanRevisionEvent = { readonly type: 'plan.revision' } & PlanRevision; type AgentActivityUpdatedEvent = { readonly type: 'agent.activity.updated' } & AgentActivityUpdated; +type PromptAcceptedEvent = { readonly type: 'prompt.accepted' } & PromptAccepted; +type PromptQueuedEvent = { readonly type: 'prompt.queued' } & PromptQueued; type PromptCompletedEvent = { readonly type: 'prompt.completed' } & PromptCompleted; type PromptAbortedEvent = { readonly type: 'prompt.aborted' } & PromptAborted; type PromptSteeredEvent = { readonly type: 'prompt.steered' } & PromptSteered; @@ -121,6 +125,8 @@ export type ProjectorBusEvent = | ({ readonly type: 'goal.updated' } & GoalUpdated) | ({ readonly type: 'agent.status.updated' } & AgentStatusUpdated) | AgentActivityUpdatedEvent + | PromptAcceptedEvent + | PromptQueuedEvent | PromptCompletedEvent | PromptAbortedEvent | PromptSteeredEvent @@ -313,6 +319,10 @@ export class AgentTranscriptProjector { return this.onAgentStatusUpdated(event); case 'agent.activity.updated': return this.onAgentActivityUpdated(event); + case 'prompt.accepted': + return this.onPromptAccepted(event); + case 'prompt.queued': + return this.onPromptQueued(event); case 'prompt.submitted': return this.onPromptSubmitted(event); case 'prompt.completed': @@ -1297,6 +1307,25 @@ export class AgentTranscriptProjector { return this.markerOp('notice', { level, message, event: eventPayload }); } + private onPromptAccepted(event: PromptAcceptedEvent): TranscriptOperation[] { + const prompt = this.upsertPrompt(event.promptId, () => ({ + promptId: event.promptId, + status: 'running', + createdAt: nowIso(), + })); + return [{ op: 'prompt.upsert', prompt }]; + } + + private onPromptQueued(event: PromptQueuedEvent): TranscriptOperation[] { + const prompt = this.upsertPrompt(event.promptId, () => ({ + promptId: event.promptId, + status: 'queued', + content: event.content, + createdAt: nowIso(), + })); + return [{ op: 'prompt.upsert', prompt }]; + } + private onPromptSubmitted(event: ProjectorPromptSubmittedEvent): TranscriptOperation[] { const prompt = this.upsertPrompt(event.promptId, () => ({ promptId: event.promptId, diff --git a/packages/kap-server/test/services/transcript.test.ts b/packages/kap-server/test/services/transcript.test.ts index 592615193..6c0c53f93 100644 --- a/packages/kap-server/test/services/transcript.test.ts +++ b/packages/kap-server/test/services/transcript.test.ts @@ -1984,11 +1984,25 @@ describe('AgentTranscriptProjector', () => { expect(turnOps('t0', tx.getItems()).state).toBe('failed'); }); - it('mirrors turn liveness into meta.activity', () => { + it('tracks the prompt queue from accepted/queued through terminal', () => { const projector = new AgentTranscriptProjector('main'); const tx = new AgentTranscript('main'); const feed = (event: ProjectorBusEvent): void => void tx.apply(projector.map(event)); + feed(ev({ type: 'prompt.accepted', promptId: 'p1' })); + expect(tx.getPrompt('p1')).toMatchObject({ status: 'running' }); + feed(ev({ type: 'prompt.queued', promptId: 'p2', content: [{ type: 'text', text: 'later' }], queueLength: 1 })); + expect(tx.getPrompt('p2')).toMatchObject({ status: 'queued' }); + feed(ev({ type: 'prompt.completed', promptId: 'p2', finishedAt: '2026-08-20T00:00:01.000Z', reason: 'completed' })); + expect(tx.getPrompt('p2')).toMatchObject({ status: 'completed' }); + feed(ev({ type: 'prompt.aborted', promptId: 'p1', abortedAt: '2026-08-20T00:00:02.000Z' })); + expect(tx.getPrompt('p1')).toMatchObject({ status: 'aborted' }); + }); + + it('mirrors turn liveness into meta.activity', () => { const projector = new AgentTranscriptProjector('main'); + const tx = new AgentTranscript('main'); + const feed = (event: ProjectorBusEvent): void => void tx.apply(projector.map(event)); + expect(tx.getMeta().activity).toBeUndefined(); feed(ev({ type: 'turn.started', turnId: 1, origin: { kind: 'user' } })); expect(tx.getMeta().activity).toBe('turn');