diff --git a/packages/kap-server/src/services/transcript/coreEventMap.ts b/packages/kap-server/src/services/transcript/coreEventMap.ts index abef7f3d8..a1643bb36 100644 --- a/packages/kap-server/src/services/transcript/coreEventMap.ts +++ b/packages/kap-server/src/services/transcript/coreEventMap.ts @@ -384,6 +384,7 @@ export class AgentTranscriptProjector { this.openText = undefined; this.openThinking = undefined; ops.push({ op: 'turn.upsert', turn: this.currentTurn }); + ops.push({ op: 'meta.merge', meta: { activity: 'turn' } }); return ops; } @@ -421,6 +422,7 @@ export class AgentTranscriptProjector { usage: this.takeTurnUsage(turnId), }; ops.push({ op: 'turn.upsert', turn: this.currentTurn }); + ops.push({ op: 'meta.merge', meta: { activity: 'idle' } }); this.currentStep = undefined; this.pendingTaskNotifications = []; if (event.reason === 'cancelled' && event.interruptReason === 'user_cancelled') { diff --git a/packages/kap-server/src/services/transcript/transcriptService.ts b/packages/kap-server/src/services/transcript/transcriptService.ts index 1c07a0729..98c3ed47b 100644 --- a/packages/kap-server/src/services/transcript/transcriptService.ts +++ b/packages/kap-server/src/services/transcript/transcriptService.ts @@ -524,7 +524,13 @@ export class TranscriptService { messages, sawTurnPrompt ? { taskOriginTurnTaskIds } : undefined, ); - return foldWireRecordFacts(records, base); + const lastTurn = base.items.findLast((item) => item.kind === 'turn'); + const activity = + lastTurn?.kind === 'turn' && lastTurn.state === 'running' ? 'unknown' : 'idle'; + return foldWireRecordFacts(records, { + ...base, + meta: { ...base.meta, activity }, + }); } /** Dispose the live store + binding for a session (session closed / server shutdown). */ diff --git a/packages/kap-server/test/services/transcript.test.ts b/packages/kap-server/test/services/transcript.test.ts index 89dec55e8..592615193 100644 --- a/packages/kap-server/test/services/transcript.test.ts +++ b/packages/kap-server/test/services/transcript.test.ts @@ -1984,11 +1984,26 @@ describe('AgentTranscriptProjector', () => { expect(turnOps('t0', tx.getItems()).state).toBe('failed'); }); - it('maps cron / task origins onto the turn header', () => { + 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'); + feed(ev({ type: 'turn.ended', turnId: 1, reason: 'completed' })); + expect(tx.getMeta().activity).toBe('idle'); + feed(ev({ type: 'turn.started', turnId: 2, origin: { kind: 'user' } })); + expect(tx.getMeta().activity).toBe('turn'); + feed(ev({ type: 'turn.ended', turnId: 2, reason: 'failed' })); + expect(tx.getMeta().activity).toBe('idle'); + }); + + it('maps cron / task origins onto the turn header', () => { const projector = new AgentTranscriptProjector('main'); + const tx = new AgentTranscript('main'); + const feed = (event: ProjectorBusEvent): void => void tx.apply(projector.map(event)); + feed( ev({ type: 'turn.started',