Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d9459ea
fix(transcript): fold mid-turn task notifications into the current tu…
wbxl2000 Aug 19, 2026
19e513c
chore: changeset for the notification fold fix
wbxl2000 Aug 19, 2026
599d0d9
fix(transcript): key the notification fold on persisted task-turn bou…
wbxl2000 Aug 19, 2026
0ccda12
fix(transcript): collect background_task turn origins too, and fall b…
wbxl2000 Aug 19, 2026
04255a7
fix(transcript): fold consecutive task notifications into the same turn
wbxl2000 Aug 19, 2026
2f2b2c5
fix(transcript): normalize folded notification text to the live title…
wbxl2000 Aug 19, 2026
9905e3b
fix(transcript): stop folded notification text before child blocks, p…
wbxl2000 Aug 19, 2026
163ec98
fix(transcript): truncate folded notification text at the first child…
wbxl2000 Aug 19, 2026
b43e971
fix(transcript): drop folded notifications without a persisted step, …
wbxl2000 Aug 19, 2026
bbb2559
fix(transcript): attach mid-turn task notifications to the following …
wbxl2000 Aug 19, 2026
7c36d45
fix(transcript): keep other tasks' notifications in task-origin turns
wbxl2000 Aug 19, 2026
ea6d64d
fix(transcript): derive task-turn boundaries from durable turn.prompt…
wbxl2000 Aug 19, 2026
57ee405
feat(transcript): carry subagent model and thinking effort on task en…
wbxl2000 Aug 19, 2026
609d849
fix(transcript): mirror turn liveness into meta.activity, live and cold
wbxl2000 Aug 20, 2026
4733e9e
fix(transcript): populate the prompts entity from prompt.accepted/que…
wbxl2000 Aug 20, 2026
d86f3e3
fix(transcript): reconcile liveness and the prompt queue at backfill …
wbxl2000 Aug 20, 2026
3e5976e
fix(transcript): include the prompts entity in the REST transcript re…
wbxl2000 Aug 20, 2026
2ea5f79
fix(agent-core-v2): publish prompt.accepted on the event bus
wbxl2000 Aug 20, 2026
281ebf8
test(transcript): add the contract-level e2e covering every entity th…
wbxl2000 Aug 20, 2026
eae2920
fix(transcript): guard the prompt backfill against missing services; …
wbxl2000 Aug 20, 2026
7daf99e
test(agent-core-v2): re-record event stream snapshots with prompt.acc…
wbxl2000 Aug 20, 2026
11f9cd0
fix(transcript): settle the spawned agent row when its lifecycle redi…
wbxl2000 Aug 20, 2026
2f795cc
test(transcript): move the contract e2e timeout to the describe arg (…
wbxl2000 Aug 21, 2026
b72f0fb
fix(transcript): declare task model fields in the wire schema, carry …
wbxl2000 Aug 24, 2026
6c0cc98
fix(transcript): preserve task model fields across termination and ac…
wbxl2000 Aug 24, 2026
fc52080
fix(transcript): keep prompt.accepted out of the public v1 event stream
wbxl2000 Aug 24, 2026
72b7145
fix(transcript): derive cold task-turn boundaries through undo anchor…
wbxl2000 Aug 24, 2026
f32cb4b
fix(transcript): flush trailing folded notifications into the open turn
wbxl2000 Aug 24, 2026
71a941a
merge: resolve conflicts with origin/main (tower cold mode, handleOf …
wbxl2000 Aug 24, 2026
9721d36
fix(transcript): drop trailing buffered notifications to match the li…
wbxl2000 Aug 24, 2026
1e123e5
chore: split transcript changesets per logical change
wbxl2000 Aug 24, 2026
9d7120f
fix(transcript): parse only the generated Title/Severity header lines…
wbxl2000 Aug 24, 2026
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
6 changes: 6 additions & 0 deletions .changeset/transcript-activity-state.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@moonshot-ai/kap-server": patch
"@moonshot-ai/transcript": patch
---

Publish per-agent transcript activity state (idle / turn) on transcript snapshots, derived from live loop status and preserved across cold rebuilds and live backfills.
5 changes: 5 additions & 0 deletions .changeset/transcript-notification-fold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Fix the cold transcript rebuild splitting a turn at background-task completion notices; they now fold into the current turn like the live stream does.
Comment thread
wbxl2000 marked this conversation as resolved.
7 changes: 7 additions & 0 deletions .changeset/transcript-prompt-entities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@moonshot-ai/agent-core-v2": patch
"@moonshot-ai/kap-server": patch
"@moonshot-ai/node-sdk": patch
---

Expose prompt queue entities through the transcript surface: accepted and queued prompts carry their content, queued prompts keep it after dequeueing, and the schema-less prompt.accepted event no longer leaks into the v1 event stream.
6 changes: 6 additions & 0 deletions .changeset/transcript-subagent-model-meta.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@moonshot-ai/kap-server": patch
"@moonshot-ai/transcript": patch
---

Keep subagent task model and thinking-effort metadata on transcript tasks, including after the task terminates.
1 change: 1 addition & 0 deletions packages/agent-core-v2/docs/wire-manifest.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ interface PromptAcceptedPayload {
_name: 'prompt.accepted';
agentId: string;
promptId: string;
content?: any;
}

/**
Expand Down
3 changes: 3 additions & 0 deletions packages/agent-core-v2/src/agent/prompt/promptOps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ import { defineState } from '#/state/state';
const promptAcceptedSchema = z.object({
agentId: z.string(),
promptId: z.string().min(1),
content: z.unknown().optional(),
});

export class PromptAccepted extends AgentEvent2<z.infer<typeof promptAcceptedSchema>> {
static override readonly type = 'prompt.accepted';
static override readonly durable = true;
static override readonly observable = true;
Comment thread
wbxl2000 marked this conversation as resolved.
static override readonly schema = promptAcceptedSchema;
}
export interface PromptAccepted {
readonly agentId: string;
readonly promptId: string;
readonly content?: unknown;
}

export const promptAdmissionKey = defineState('promptAdmission', (): Map<string, true> => new Map())
Expand Down
6 changes: 5 additions & 1 deletion packages/agent-core-v2/src/agent/prompt/promptService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,11 @@ export class AgentPromptService implements IAgentPromptService {
submitted = true;
this.reservedPromptIds.delete(id);
await this.dispatcher.dispatch(
new PromptAccepted({ agentId: this.scopeContext.agentId, promptId: id }),
new PromptAccepted({
agentId: this.scopeContext.agentId,
promptId: id,
content: stripBundledSkillBlocks(message),
}),
);
return this.enqueue({ id, message });
},
Expand Down
9 changes: 6 additions & 3 deletions packages/agent-core-v2/test/agent/loop/loop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ describe('Agent loop', () => {

expect(await ctx.untilTurnEnd()).toMatchInlineSnapshot(`
[wire] tools.set_active_tools { "agentId": "main", "names": [], "time": "<time>" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "time": "<time>" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Hello" } ], "time": "<time>" }
[emit] prompt.accepted { "time": "<time>", "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Hello" } ] }
[wire] turn.prompt { "agentId": "main", "input": [ { "type": "text", "text": "Hello" } ], "origin": { "kind": "user" }, "time": "<time>" }
[emit] turn.started { "time": "<time>", "agentId": "main", "turnId": 0, "origin": { "kind": "user" }, "prompt": "Hello" }
[emit] agent.activity.updated { "time": "<time>", "lifecycle": "ready", "turn": { "turnId": 0, "origin": { "kind": "user" }, "phase": "running", "step": 0, "ending": false, "pendingApprovals": [], "activeToolCalls": [], "since": "<time>" }, "background": [], "agentId": "main" }
Expand Down Expand Up @@ -124,7 +125,8 @@ describe('Agent loop', () => {
await ctx.rpc.prompt({ input: [{ type: 'text', text: 'Hello' }] });

expect(await ctx.untilTurnEnd()).toMatchInlineSnapshot(`
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "time": "<time>" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Hello" } ], "time": "<time>" }
[emit] prompt.accepted { "time": "<time>", "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Hello" } ] }
[wire] turn.prompt { "agentId": "main", "input": [ { "type": "text", "text": "Hello" } ], "origin": { "kind": "user" }, "time": "<time>" }
[emit] turn.started { "time": "<time>", "agentId": "main", "turnId": 0, "origin": { "kind": "user" }, "prompt": "Hello" }
[emit] agent.activity.updated { "time": "<time>", "lifecycle": "ready", "turn": { "turnId": 0, "origin": { "kind": "user" }, "phase": "running", "step": 0, "ending": false, "pendingApprovals": [], "activeToolCalls": [], "since": "<time>" }, "background": [], "agentId": "main" }
Expand Down Expand Up @@ -346,7 +348,8 @@ describe('Agent loop', () => {
ctx.mockNextResponse({ type: 'text', text: 'The lookup result is lookup-result.' });
expect(await ctx.untilApproval(true)).toMatchInlineSnapshot(`
[wire] tools.set_active_tools { "agentId": "main", "names": [ "Lookup" ], "time": "<time>" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "time": "<time>" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Look up moon" } ], "time": "<time>" }
[emit] prompt.accepted { "time": "<time>", "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Look up moon" } ] }
[wire] turn.prompt { "agentId": "main", "input": [ { "type": "text", "text": "Look up moon" } ], "origin": { "kind": "user" }, "time": "<time>" }
[emit] turn.started { "time": "<time>", "agentId": "main", "turnId": 0, "origin": { "kind": "user" }, "prompt": "Look up moon" }
[emit] agent.activity.updated { "time": "<time>", "lifecycle": "ready", "turn": { "turnId": 0, "origin": { "kind": "user" }, "phase": "running", "step": 0, "ending": false, "pendingApprovals": [], "activeToolCalls": [], "since": "<time>" }, "background": [], "agentId": "main" }
Expand Down
6 changes: 4 additions & 2 deletions packages/agent-core-v2/test/app/config/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,8 @@ describe('Agent config', () => {
input: [{ type: 'text', text: 'Look up before config changes' }],
});
expect(await ctx.untilApproval(true)).toMatchInlineSnapshot(`
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "time": "<time>" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Look up before config changes" } ], "time": "<time>" }
[emit] prompt.accepted { "time": "<time>", "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Look up before config changes" } ] }
[wire] turn.prompt { "agentId": "main", "input": [ { "type": "text", "text": "Look up before config changes" } ], "origin": { "kind": "user" }, "time": "<time>" }
[emit] turn.started { "time": "<time>", "agentId": "main", "turnId": 0, "origin": { "kind": "user" }, "prompt": "Look up before config changes" }
[emit] agent.activity.updated { "time": "<time>", "lifecycle": "ready", "turn": { "turnId": 0, "origin": { "kind": "user" }, "phase": "running", "step": 0, "ending": false, "pendingApprovals": [], "activeToolCalls": [], "since": "<time>" }, "background": [], "agentId": "main" }
Expand Down Expand Up @@ -404,7 +405,8 @@ describe('Agent config', () => {
[emit] agent.activity.updated { "time": "<time>", "lifecycle": "ready", "lastTurn": { "turnId": 0, "reason": "completed", "at": "<time>" }, "background": [], "agentId": "main" }
[emit] agent.status.updated { "time": "<time>", "agentId": "main", "contextTokens": 102 }
[emit] prompt.completed { "time": "<time>", "agentId": "main", "promptId": "<msg-1>", "finishedAt": "<time>", "reason": "completed" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-2>", "time": "<time>" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-2>", "content": [ { "type": "text", "text": "Start a fresh turn" } ], "time": "<time>" }
[emit] prompt.accepted { "time": "<time>", "agentId": "main", "promptId": "<msg-2>", "content": [ { "type": "text", "text": "Start a fresh turn" } ] }
[wire] turn.prompt { "agentId": "main", "input": [ { "type": "text", "text": "Start a fresh turn" } ], "origin": { "kind": "user" }, "time": "<time>" }
[emit] turn.started { "time": "<time>", "agentId": "main", "turnId": 1, "origin": { "kind": "user" }, "prompt": "Start a fresh turn" }
[emit] agent.activity.updated { "time": "<time>", "lifecycle": "ready", "turn": { "turnId": 1, "origin": { "kind": "user" }, "phase": "running", "step": 0, "ending": false, "pendingApprovals": [], "activeToolCalls": [], "since": "<time>" }, "background": [], "agentId": "main" }
Expand Down
6 changes: 4 additions & 2 deletions packages/agent-core-v2/test/features/plan/plan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,8 @@ describe('Plan service', () => {
[wire] permission.set_mode { "agentId": "main", "mode": "yolo", "time": "<time>" }
[wire] plan_mode.enter { "agentId": "main", "id": "test-plan", "time": "<time>" }
[emit] agent.status.updated { "time": "<time>", "agentId": "main", "planMode": true }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "time": "<time>" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Inspect without mutating files" } ], "time": "<time>" }
[emit] prompt.accepted { "time": "<time>", "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Inspect without mutating files" } ] }
[wire] turn.prompt { "agentId": "main", "input": [ { "type": "text", "text": "Inspect without mutating files" } ], "origin": { "kind": "user" }, "time": "<time>" }
[emit] turn.started { "time": "<time>", "agentId": "main", "turnId": 0, "origin": { "kind": "user" }, "prompt": "Inspect without mutating files" }
[emit] agent.activity.updated { "time": "<time>", "lifecycle": "ready", "turn": { "turnId": 0, "origin": { "kind": "user" }, "phase": "running", "step": 0, "ending": false, "pendingApprovals": [], "activeToolCalls": [], "since": "<time>" }, "background": [], "agentId": "main" }
Expand Down Expand Up @@ -796,7 +797,8 @@ describe('Plan service', () => {
[wire] permission.set_mode { "agentId": "main", "mode": "yolo", "time": "<time>" }
[wire] plan_mode.enter { "agentId": "main", "id": "test-plan", "time": "<time>" }
[emit] agent.status.updated { "time": "<time>", "agentId": "main", "planMode": true }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "time": "<time>" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Remove forbidden.txt" } ], "time": "<time>" }
[emit] prompt.accepted { "time": "<time>", "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Remove forbidden.txt" } ] }
[wire] turn.prompt { "agentId": "main", "input": [ { "type": "text", "text": "Remove forbidden.txt" } ], "origin": { "kind": "user" }, "time": "<time>" }
[emit] turn.started { "time": "<time>", "agentId": "main", "turnId": 0, "origin": { "kind": "user" }, "prompt": "Remove forbidden.txt" }
[emit] agent.activity.updated { "time": "<time>", "lifecycle": "ready", "turn": { "turnId": 0, "origin": { "kind": "user" }, "phase": "running", "step": 0, "ending": false, "pendingApprovals": [], "activeToolCalls": [], "since": "<time>" }, "background": [], "agentId": "main" }
Expand Down
6 changes: 4 additions & 2 deletions packages/agent-core-v2/test/tool/tool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3861,7 +3861,8 @@ describe('Agent tools', () => {
).toMatchInlineSnapshot(`
[wire] permission.set_mode { "agentId": "main", "mode": "auto", "time": "<time>" }
[wire] tools.register_user_tool { "name": "Lookup", "description": "Look up a short test value.", "parameters": { "type": "object", "properties": { "query": { "type": "string" } }, "required": [ "query" ], "additionalProperties": false }, "agentId": "main", "time": "<time>" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "time": "<time>" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Look up moon" } ], "time": "<time>" }
[emit] prompt.accepted { "time": "<time>", "agentId": "main", "promptId": "<msg-1>", "content": [ { "type": "text", "text": "Look up moon" } ] }
[wire] turn.prompt { "agentId": "main", "input": [ { "type": "text", "text": "Look up moon" } ], "origin": { "kind": "user" }, "time": "<time>" }
[emit] turn.started { "time": "<time>", "agentId": "main", "turnId": 0, "origin": { "kind": "user" }, "prompt": "Look up moon" }
[emit] agent.activity.updated { "time": "<time>", "lifecycle": "ready", "turn": { "turnId": 0, "origin": { "kind": "user" }, "phase": "running", "step": 0, "ending": false, "pendingApprovals": [], "activeToolCalls": [], "since": "<time>" }, "background": [], "agentId": "main" }
Expand Down Expand Up @@ -3939,7 +3940,8 @@ describe('Agent tools', () => {
[emit] agent.status.updated { "time": "<time>", "agentId": "main", "contextTokens": 176 }
[wire] tools.unregister_user_tool { "agentId": "main", "name": "Lookup", "time": "<time>" }
[emit] prompt.completed { "time": "<time>", "agentId": "main", "promptId": "<msg-1>", "finishedAt": "<time>", "reason": "completed" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-2>", "time": "<time>" }
[wire] prompt.accepted { "agentId": "main", "promptId": "<msg-2>", "content": [ { "type": "text", "text": "Can you still use Lookup?" } ], "time": "<time>" }
[emit] prompt.accepted { "time": "<time>", "agentId": "main", "promptId": "<msg-2>", "content": [ { "type": "text", "text": "Can you still use Lookup?" } ] }
[wire] turn.prompt { "agentId": "main", "input": [ { "type": "text", "text": "Can you still use Lookup?" } ], "origin": { "kind": "user" }, "time": "<time>" }
[emit] turn.started { "time": "<time>", "agentId": "main", "turnId": 1, "origin": { "kind": "user" }, "prompt": "Can you still use Lookup?" }
[emit] agent.activity.updated { "time": "<time>", "lifecycle": "ready", "turn": { "turnId": 1, "origin": { "kind": "user" }, "phase": "running", "step": 0, "ending": false, "pendingApprovals": [], "activeToolCalls": [], "since": "<time>" }, "background": [], "agentId": "main" }
Expand Down
2 changes: 2 additions & 0 deletions packages/kap-server/src/routes/transcript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export function registerTranscriptRoutes(app: TranscriptRouteHost, deps: Transcr
interactions: [...transcript.getInteractions().values()],
attachments: [...transcript.getAttachments().values()],
todos: [...transcript.getTodos().values()],
prompts: [...transcript.getPrompts().values()],
meta: transcript.getMeta(),
agents: store.agents(),
pending_interactions: transcript.listPendingInteractions(),
Expand Down Expand Up @@ -197,6 +198,7 @@ export function registerTranscriptRoutes(app: TranscriptRouteHost, deps: Transcr
interactions: snapshot.interactions,
attachments: snapshot.attachments,
todos: snapshot.todos,
prompts: snapshot.prompts,
meta: snapshot.meta,
agents: roster,
pending_interactions: [],
Expand Down
Loading
Loading