mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-30 03:24:59 +00:00
5 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d751b6796c
|
feat(kap-server): global session work status, transcript subscribe_v2, and plan endpoint (#2094)
* feat(session): add core work aggregate and consume it at WS/REST edges
- agent-core-v2: add the sessionActivity domain (ISessionActivityView,
Session scope) folding each agent's activity view and the interaction
kernel into busy / main_turn_active / pending_interaction /
last_turn_reason, firing cause-tagged change events only on real
tuple changes
- kap-server: resolveSessionFacts reads the core view; the WS
broadcaster drops its per-agent fold/dedup and delegates to the
view, deferring turn_ended emissions until after the turn.ended
frame so busy:false never precedes it; global events now fan out
to every established connection via addGlobalTarget without any
subscription
- kimi-inspect: add src/activity (GlobalEventsWs + SessionActivityHub
+ useSessionActivities) consuming the global push; the Sidebar
renders running / approval / question / failed badges per session
* feat(kap-server): decouple transcript stream from agent_filter
- transcript frames (transcript.reset/ops) are governed by the per-agent
transcript grades alone and bypass the legacy agent allowlist; the filter
still gates session_event delivery
- client_hello is handshake-only in code: hello and subscribe share one
attach path, and hello's inline subscription fields are deprecated
(subscriptions made optional) in both protocol packages
- flush deferred work_changed(busy:false) from a microtask so it always
lands after the matching turn.ended frame
* fix(kimi-inspect): restore session activity hub in the browser
- bind the default fetch in SessionActivityHub: a member call hits the
browser's Illegal invocation (receiver is not the global object), and
the swallowed error left the REST seed never firing, so the Sidebar
badges never populated on refresh
- create the hub in useEffect + useState instead of useMemo: under
StrictMode the first mount's cleanup closes the memo-created hub for
the rest of the page's life
* chore: add changeset for the global session work status push
* feat(kap-server): add transcript plan endpoint for ExitPlanMode calls
- add GET /sessions/{id}/transcript/plan projecting one ExitPlanMode
call's plan info (content, path, options, review outcome) from the
first available fact: the linked approval interaction's request
display, the live tool frame's display, or the tool result output
- add TOOL_CALL_NOT_FOUND (40416) error code
- add transcriptPlanResponseSchema to the transcript contract
- cover live, auto-mode, cold-rebuild, and error paths in tests
- update the API surface snapshot and AGENTS.md
* feat: move transcript subscriptions to subscribe_v2 and extend the plan endpoint
- add subscribe_v2 / unsubscribe_v2 WS control frames as the only
carrier of per-agent transcript grades and the transcript_since
cursor; client_hello / subscribe no longer accept transcript fields
- serialize control-frame handling per connection so interleaved
attaches cannot overwrite fresher subscription state
- make tool_call_id optional on GET /sessions/{id}/transcript/plan:
omitted lists every ExitPlanMode call with recoverable plan content
as a plans array
- add a Plan lookup card to the kimi-inspect agent inspector via
fetchTranscriptPlan
- add TOOL_CALL_NOT_FOUND (40416) to the shared protocol error codes
- update AGENTS.md and add changesets
|
||
|
|
64f053cf46
|
feat: agent-core-v2 permission/workspace refactors and transcript durability (#2021)
* refactor(agent-core-v2): extract toolApproval domain from permissionGate
- add agent-scoped `toolApproval` domain owning the approval round-trip:
builds approval requests, drives the session/approval broker, publishes
permission.approval.* events, records session approval rules, and
resolves ask continuations
- slim permissionPolicy down to the static risk-adjudication chain; drop
the dynamic `registerPolicy` mechanism
- move harness constraints out of the policy chain into their owning
domains as toolExecutor hooks ordered before 'permission': plan-mode
guard (plan), swarm batch exclusivity and AgentSwarm approve (swarm),
goal-start review (goal), exit-plan review (plan/exitPlanModeReview),
and btw deny (session/btw)
- delete the now-unused policies (deny-all, plan-mode-guard-deny,
plan-mode-tool-approve, goal-start-review-ask, swarm-mode-agent-swarm-
approve, agent-swarm-exclusive-deny)
- update Permission.md, AGENTS.md, and check-domain-layers for the new
domain layout
* feat(kimi-inspect): add App Services view for app-scope service reflection
- add `services` top-level view (`AppServicesView`) on the NavRail, showing
the full-width app-scope Service panel grid; session/agent scopes stay in
the Chat view's Inspector
- extract shared `ServicePanels` from Inspector and add `methodArgs` helper
to build per-method argument editors from channel metadata
- support variadic service calls in `panels.ts` (`call(svc, method, ...args)`)
- update agent-core-dev skill docs for the toolApproval extraction and the
guard/review-off-chain permission design
* refactor(agent-core-v2): restructure workspace domains
- rename workspaceRegistry to workspace and workspaceLocalConfig to
projectLocalConfig
- extract id-spelling resolution into the workspaceAliases domain
(IWorkspaceAliases)
- extract workspace-centric session queries into workspaceSessions
(IWorkspaceSessions)
- update kap-server routes, klient contracts, and related tests to the
new services
* feat(transcript): add op-batch sequencing and point-to-point catch-up
- wire: transcriptSeqSchema with per-agent batch seq watermark on
transcript.reset/ops and the REST transcript response, the
transcript_since subscription cursor, and the GET transcript/ops
catch-up shape; seq stays optional everywhere so legacy peers fall
back to loss-signal-driven refreshes
- wire: drop interactionFrame, interactions live on the interaction
item in the ops stream
- kap-server: TranscriptService assigns consecutive per-agent batch
seqs and retains them in a bounded in-memory journal; the
transcript_since cursor replays journaled batches instead of a
baseline reset, and the baseline reset is now items-empty because
history always pages in over REST
- kimi-inspect: transcript REST/WS clients track the op-batch
watermark and run seq-gap/reconnect catch-up with full-refresh
fallback; add the Transcript audit panel (AuditTrail timeline,
structural diff, state tree) docked right of the chat
- docs: sync AGENTS.md and agent-core-dev skill notes with the new
transcript contract
* feat(transcript): persist plan revisions and task/interaction facts, add user-messages endpoint
- record each ExitPlanMode submission as a versioned plan blob via a reference-only plan.revision op, projected live and cold as a plan.revision marker plus the plan badge (reviewPath, version)
- persist task.started/task.terminated (with a bounded output tail) and interaction.request/interaction.resolved ops, and add foldFacts so a cold transcript rebuilds tasks, interactions, todos and goal/plan/swarm meta from the wire journal
- add GET /sessions/{session_id}/transcript/user-messages returning all turn-opening prompts grouped per agent
* fix(agent-core-v2): anchor external PreToolUse hooks before the permission gate
The toolApproval extraction forces the gate to construct early (planService injects it to anchor plan-guard), so the 'permission' hook registered ahead of 'externalHooks' and a policy ask waited on the approval broker before PreToolUse could block, hanging the turn. Fetch the gate first in registerListeners and register the PreToolUse hook with before: 'permission', falling back to appending when the gate is stubbed without its hook.
* refactor(agent-core-v2): replace ordered onBeforeExecuteTool hook with veto-event pattern
- introduce BeforeToolExecuteEvent with veto/allow/pass/waitUntil statements
- add BeforeToolExecuteEmitter with two-pass fire (immediate then deferred)
- split readiness work into separate onWillExecuteTool participation event
- migrate all domain listeners: permissionGate, plan, goal, swarm, btw,
externalHooks, toolDedupe, mcp
- remove IAgentPermissionGate force-injection for hook ordering
- update docs, tests, and domain-layer check to match
* refactor(agent-core-v2): unify veto payload on ExecutableToolResult
- veto() and waitUntil factories now carry a plain ExecutableToolResult:
isError reads as a denial, anything else as a short-circuit;
the block/reason/syntheticResult weak union is gone
- add denyToolExecution(reason) helper for the common denial shape, and
narrow the fire/authorize return to BeforeExecuteDecision ({ veto } or
{ executionMetadata })
- narrow the policy 'result' resolution to { kind: 'result'; result }
- settle vetoed calls through a single normalize/merge path in the executor
* fix(agent-core-v2): pull up IAgentPermissionGate in agent activation
The permission gate only subscribes `onBeforeExecuteTool` from its
constructor. The veto-event refactor removed the ordering-driven
force-injections that used to pull it up, so without an explicit
resolution tool execution would run without policy adjudication.
* refactor(agent-core-v2): fold systemReminder domain into contextMemory appendTagged
- add `appendTagged(content, tag, origin)` to `IAgentContextMemoryService`,
storing content pure with a `tag` field on `ContextMessage`
- apply the XML tag at projection time in `contextProjector` via the new
`tag.ts` helpers (`wrapTag` / `applyTagToContent`)
- delete the `systemReminder` domain and migrate all call sites
(contextInjector, goal, plugin, prompt, swarm, btw, sessionInit,
toolSelectAnnouncements) to `appendTagged`
- build toolDedupe reminder strings with `wrapTag`
* refactor(klient): merge providers/models/catalog into global.kosong facade
Converge three separate facade namespaces (global.providers,
global.models, global.catalog) into a single global.kosong facade
that exposes two domain concepts: provider (CRUD) and model
(read-only view). Add streaming generate() method for direct
LLM calls through the facade.
- Define ProviderAuth (api-key | oauth), ProviderInput,
AnonymousProviderInput, GenerateInput, GenerateParams,
GenerateEvent as klient-owned public types
- Extend KlientChannel with stream() for AsyncIterable transport
- Add streaming IPC protocol (stream/stream_data/stream_end/
stream_error/stream_cancel frame types)
- Add StreamingProcedureContract, ScopedStreamCaller, and
per-chunk zod validation in the contract layer
- Implement generate via dispatcher special-case routing to
IModelCatalog.getRequester().request()
- Rename events: providers.changed -> kosong.providers.changed,
models.changed -> kosong.models.changed,
catalog.changed -> kosong.changed
- Remove GlobalProvidersFacade, GlobalModelsFacade,
GlobalCatalogFacade, and ModelRecord from public exports
- Update all tests, examples, and README
BREAKING CHANGE: global.providers, global.models, and
global.catalog replaced by global.kosong; event names changed;
ModelRecord no longer exported.
* feat(transcript,kimi-inspect): add tag field to text frames and improve session creation
transcript:
- add optional `tag` field to TextFrame, textFrameSchema, and HistoryMessage
- propagate tag through contextTranscript MutableMessage
kimi-inspect:
- render tagged frames with violet badge and distinct styling in ChatView
- skip cwd prompt for workspace-based session creation in Sidebar
- auto-bind default model on new sessions via resolveDefaultModel
* refactor(transcript): rename wire/ directory to contract/
The transcript package's REST/WS schemas and event types lived in
src/wire/, which collided with the engine's persisted wire.jsonl
record vocabulary. Rename it to src/contract/ so "wire" unambiguously
refers to wire.jsonl records (foldWireRecordFacts, HistoryWireRecord
stay unchanged).
- rename src/wire/{schema,events}.ts to src/contract/
- update index exports and test imports accordingly
- reword comments: "wire shape" -> "contract shape", "on the wire" ->
"in ops" / "in transit" / "on the WS channel" / "transcript API"
- events.ts: "transcript frame" -> "transcript event" for WS envelope
messages, avoiding confusion with TranscriptFrame
- kap-server tests: TranscriptWire/TurnWire/FrameWire/OpsCatchupWire/
UserMessagesWire -> *Contract
- update AGENTS.md references
* refactor(transcript): rename wire/ directory to contract/
The transcript package's REST/WS schemas and event types lived in
src/wire/, which collided with the engine's persisted wire.jsonl
record vocabulary. Rename it to src/contract/ so "wire" unambiguously
refers to wire.jsonl records (foldWireRecordFacts, HistoryWireRecord
stay unchanged).
- rename src/wire/{schema,events}.ts to src/contract/
- update index exports and test imports accordingly
- reword comments: "wire shape" -> "contract shape", "on the wire" ->
"in ops" / "in transit" / "on the WS channel" / "transcript API"
- kap-server tests: TranscriptWire/TurnWire/FrameWire/OpsCatchupWire/
UserMessagesWire -> *Contract
- update AGENTS.md references
* Revert "refactor(agent-core-v2): fold systemReminder domain into contextMemory appendTagged"
This reverts commit 55afaa3d96f729c4f73a71f1fbd23d3f6087453b.
Restore the systemReminder domain: reminders go back to being baked
into message text at write time, and ContextMessage loses the `tag`
field (projection-time wrapping is removed with it).
* feat(transcript): add wire-equivalent detail, dedupe session events
- transcript: add step usage/timing/retry, turn durationMs/error/usage,
tool inputText/progress, task resultSummary/error, meta.agent status,
a global prompts entity, and the 'hook' marker
- kap-server: project the new fields in coreEventMap and suppress
transcript-projected session events on connections subscribed to the
transcript protocol (live fan-out and cursor replay)
- kimi-inspect: mechanical type sync for the new snapshot prompts field
* fix(klient): resolve lint errors in ipc channel stream and e2e matrix test
|
||
|
|
74da87a457
|
feat(kap-server): broadcast agent.created/agent.disposed session events (#1997)
* feat(kap-server): broadcast agent.created/agent.disposed session events - emit durable agent.created / agent.disposed facts from the SessionEventBroadcaster lifecycle callbacks, ahead of the agent's own events, and let them bypass per-subscription agent allowlists - add the agent.created / agent.disposed wire types and zod schemas - stamp disposedAt on the transcript roster entry via TranscriptStore.markDisposed so REST consumers can tell a dead agent from a live one * chore: add changeset for agent lifecycle events |
||
|
|
efacf0452d
|
ci: release packages (#1946)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
5ae60fa673
|
feat(transcript): add unified transcript layer, drop the /api/v2 RPC surface (#1888)
* feat(transcript): add unified transcript layer and v1 surface
- add packages/transcript: agent-granular L1 store, idempotent L2 ops,
off/turn/block/delta L3 granularity, L4 view registry, and turn-cursor
pagination; sole owner of all transcript wire types
- kap-server: engine-event-driven TranscriptService with history backfill,
GET /sessions/{id}/transcript, and transcript.ops WS deltas with
per-connection granularity control
- kimi-inspect: render ChatView from the transcript surface (REST pages +
delta-only WS) instead of context memory
- sync flake.nix workspace lists and document packages/transcript and
packages/server-e2e in AGENTS.md
* fix(transcript): project live prompts and anchor backfilled items
- agent-core-v2: carry the extracted prompt text on turn.started so the
transcript projector can render the user input at turn open (the context
append with the same text is not a bus event and lands later)
- kap-server: keep the prompt through turn.ended; anchor backfilled
markers/taskrefs to their following snapshot turn so replaying history
after live turns arrived keeps the historical order
- transcript: add an optional beforeTurn placement anchor to
marker.upsert / taskref.upsert; anchored inserts land before the first
turn at or past the anchor instead of appending blindly
Addresses review feedback on #1888.
* fix(transcript): adopt backfilled stream frames and group goal turns
- kap-server: on mid-stream attach, adopt the backfill-seeded stream frame
(id + offset) instead of opening an empty one whose upsert clobbered the
seeded text and whose offset-0 appends could not land
- transcript: group a turn-opening system_trigger (goal continuation) into
its own turn so cold rebuilds keep the turn boundary and stay
ordinal-aligned with the engine's live turn numbering
Addresses review feedback on #1888.
* fix(transcript): drop stale live stores on close and heal after turns
- drop a session's live transcript store when the session closes or archives
(lifecycle events plus a re-check on the cached-entry path) so reads fall
back to the cold rebuild instead of serving a stale store
- re-read an ended turn from the persisted wire records (debounced per
agent) and merge it back live-first: headers keep live state/timestamps
while origin/prompt recover from disk, and truncated text/thinking frames
from a mid-turn attach are restored only when the persisted text is
longer — a fresh live frame or a lagging flush is never reverted
Addresses review feedback on #1888.
* fix(transcript): adopt seeded tool frames and route subagent questions
- kap-server: adopt the backfill-seeded tool frame when a tool.result
arrives for a call that started before the projector attached, so the
output lands instead of being dropped (the producer-store lookups now
ride one projector options object)
- agent-core-v2: record the owning agent on question interactions
(ISessionQuestionService.request gains an agentId option, passed by
AskUserQuestionTool from its agent scope) so a subagent's questions
route to its own transcript and WS events instead of 'main'
Addresses review feedback on #1888.
* fix(transcript): adopt parent frames, namespace live markers, redact resets
- kap-server: fall back to store adoption when subagent.spawned links a
parent tool call that started before the projector attached, so the
agentRefs link is not lost on mid-bind attaches
- kap-server: id live markers in their own namespace (live-mN) — the cold
rebuild numbers its markers m1... too, and a colliding id made the
store's upsert replace the historical marker instead of appending
- transcript/kap-server: redact transcript.reset snapshots to the
subscriber's grade (below 'block' the step/frame detail is stripped), so
a 'turn'-grade subscription no longer receives full content on resets
Addresses review feedback on #1888.
* fix(transcript): fold blocked turns into failed, drop inline v2 comment
- kap-server: map turn.ended reason 'blocked' to the 'failed' transcript
state, matching the engine's TurnEndReason wire contract and the v1
mapTurnReason folding (it was presented as a user cancellation)
- agent-core-v2: move the question agentId rationale into the ask-user
file header — package rules keep comments in the top-of-file block only
Addresses review feedback on #1888.
* fix(transcript): keep roster descriptors and resolved-event agents
- kap-server: keep the metadata-seeded roster descriptor (parentAgentId /
label) when an on-demand history backfill lands its roster entry, instead
of downgrading it to { agentId, type }
- kap-server: remember each interaction's owning agent and stamp it on the
resolved question/approval v1 events — they were hard-coded to 'main', so
an agent-filtered subscriber saw a subagent's question open but never
close
Addresses review feedback on #1888.
* fix(transcript): defer pending seeding, skip ghost roster entries
- kap-server: announce interactions pending at bind time only after the
initial backfill (new TranscriptBinding.seedPendingInteractions), and
adopt the seeded tool frame on the request/resolve paths, so a
pre-existing approval lands next to its backfilled tool call and keeps
the approvalId back-link
- kap-server: skip the roster entry when a probed agent id has neither a
roster presence nor persisted content (agent_id=nope no longer conjures
a ghost subagent)
- agent-core-v2: fold the turnEvents import rationale into the loop file
header (package rule: comments live in the top-of-file block only)
Addresses review feedback on #1888.
* fix(transcript): reject hostile agent ids, honor the agent allowlist
- transcript/kap-server: validate agent ids as plain names (no separators,
no dot segments) at the REST query layer and again before the id is
joined into the wire-records path — an authenticated client could
otherwise read a wire.jsonl outside the agents directory
- kap-server: compose the legacy v1 agent allowlist with transcript
grades on every fan-out path (initial resets, per-ops fan-out,
roster-driven resets), so a filtered connection no longer receives
other agents' transcript frames
Addresses review feedback on #1888.
* fix(transcript): settle foreground shell tasks on shell.completed
- agent-core-v2: emit a transient shell.completed event when a foreground
`!` command settles (detached runs keep reporting through the task
lifecycle) — the generic task.terminated never fires for foreground
tasks, so their transcript cards were stuck at 'running'
- kap-server: map shell.completed to the terminal transcript task state
(completed/failed) and classify the event as volatile on both v1
durability gates, like its shell.* siblings
Addresses review feedback on #1888.
* fix(transcript): replay early resolves, reset on a widened filter
- kap-server: register bind-time pending interactions without frames so a
resolve arriving before the post-backfill seed still routes, then replay
it at seed time — request and resolve land together with the approvalId
back-link, instead of the interaction vanishing entirely
- kap-server: treat an agent newly admitted by a broadened legacy agent
filter as owed a transcript.reset even when its grade transition is a
no-op (delta → delta) — its ops were suppressed so far, so it has no
baseline otherwise
Addresses review feedback on #1888.
* fix(transcript): keep materialized transcripts on agent disposal
- kap-server: only the projector dies with the agent scope — the
materialized transcript and roster entry now survive disposal (the
roster mirrors session metadata, which keeps completed agents).
Dropping them lost already-served history for good: the backfill cache
dedupes per agent, so the next read rebuilt an empty shell instead of
replaying the persisted records
Addresses review feedback on #1888.
* fix(transcript): anchor refreshes by oldest turn, group slash turns
- kimi-inspect: re-cover the previously loaded window after a refresh by
paging until the previous OLDEST turn is loaded again (extracted as
recoverLoadedWindow) — a count-based stop silently dropped the window's
head once new turns shifted the server window
- transcript: group user-slash skill/plugin activations into their own
turn (marker included), mirroring the engine's isRealUserPrompt — their
assistant output no longer folds into the previous turn on cold rebuilds;
other triggers stay marker-only
Addresses review feedback on #1888.
* fix(transcript): compare all tool fields, overlay in-flight backfills
- transcript: include toolCallId/name/view/input in the tool frame
equality check — an upsert correcting only those was dropped as a no-op,
leaving stale tool metadata on clients
- kap-server: overlay the loop's active turn as 'running' after a backfill
(cold grouping marks every rebuilt turn completed, so a live turn showed
as finished until it ended); snapshot data supplies origin/prompt, and a
projector-owned running header is never downgraded
Addresses review feedback on #1888.
* fix(transcript): gate ops before the seed, re-assert running headers
- kap-server: gate the transcript ops fan-out (and roster-driven resets) on
a per-connection seeded flag set only after the baseline reset has
landed — a subscriber joining mid-stream no longer receives deltas
against an empty baseline
- kap-server: always re-assert the loop's active turn as 'running' after
the snapshot ops in a backfill (skipping the overlay when a live running
header existed let the snapshot's cold 'completed' header downgrade it);
live header fields win over the snapshot's
Addresses review feedback on #1888.
* docs(agent-core-v2): fold turnEvents notes into the file header
Move the turn.started prompt rationale from field/function TSDoc into the
module header — package rules keep comments in the top-of-file block only.
Addresses review feedback on #1888.
* fix(transcript): emit shell failure output, dispose per-agent listeners
- agent-core-v2: emit the synthesized failure text as a final shell.output
chunk before shell.completed (it was never streamed, so failed
foreground commands showed empty output in transcript tasks until a
full rebuild)
- kap-server: track each agent's bus subscription per agent and dispose it
in onDidDispose — the listener captures the projector, so a disposed
agent no longer keeps projecting late events into the store
Addresses review feedback on #1888.
* fix(transcript): route shell events by task id, tidy question docs
- agent-core-v2: keep the commandId → foreground-task-id mapping and carry
taskId on shell.output / shell.completed, so consumers attaching
mid-command (having missed shell.started) can still route output and the
terminal state
- kap-server: fall back to the event's taskId in the shell output/completed
projectors and seed the shell task before the first chunk, so output is
preserved and the terminal upsert cannot clobber it with an empty tail
- agent-core-v2: fold the question agentId note into the question.ts file
header (package rule: comments live in the top-of-file block only)
Addresses review feedback on #1888.
* fix(transcript): tighten agent id validation, match kinds in heals
- transcript: constrain agent ids to a filename-safe shape
([A-Za-z0-9._-], <=128 chars) — NUL-containing or overlong ids made the
wire-records read throw unhandled errors (500) instead of failing
validation
- kap-server: require the live frame's kind to match before the post-turn
heal's length shortcut — a kind-mismatched frame (the projector guessed
the stream kind wrong mid-turn) is now replaced by the persisted one
instead of being skipped
Addresses review feedback on #1888.
* fix(transcript): heal missed tool results, seed pendings per agent
- kap-server: re-emit tool frames in the post-turn heal when the live step
lacks the frame or the live frame lacks the outcome the persisted one
carries (a tool.result dropped in the attach race is otherwise
unrecoverable); live-only extras (display / agentRefs / approvalId) are
preserved, and frames with a live outcome stay untouched
- kap-server: scope seedPendingInteractions by agent — the initial seed
after backfillMain covers main-owned pendings, and each subagent's
pendings seed after its own on-demand backfill, so placement and the
approvalId back-link find the persisted tool frames
Addresses review feedback on #1888.
* fix(protocol): register shell.completed on the v1 event surface
- packages/protocol: add ShellCompletedEvent (plus optional taskId on
shell.output / shell.completed and prompt on turn.started) to the event
interfaces, zod schemas, the agent event union, and the volatile list —
schema-validating consumers previously rejected the forwarded
shell.completed frames outright
- kap-server: mirror the same fields in the v1 events-zod module
Addresses review feedback on #1888.
* test(node-sdk): cover shell.completed in the exhaustive event switch
The SDK's session-event type test asserts exhaustiveness with assertNever;
register the new event there (CI typecheck caught it).
Addresses review feedback on #1888.
* fix(transcript): source cold-session rosters from session metadata
- kap-server: add TranscriptService.readColdRoster (persisted state.json →
descriptors, mapped like the live seeding) and use it for the cold
transcript path — the requested agent id is only appended when it has
content (or is main), so an empty probe (agent_id=nope) no longer
fabricates a ghost roster entry, matching the live path
Addresses review feedback on #1888.
* fix(transcript): emit taskrefs when seeding missed shell commands
- kap-server: the mid-command-attach seeding in onShellOutput now emits
the matching taskref.upsert (exactly like onShellStarted), and
onShellCompleted emits one when the whole command was missed — the task
no longer exists only in the global map with no timeline item to render
Addresses review feedback on #1888.
* fix(transcript): defer unseeded live pendings, keep cold tools running
- kap-server: pendings created before their owning agent's seed has run
now defer into the same unseeded queue as bind-time ones (tracked per
agent) — announcing them during the backfill window misplaced them into
a synthetic step with no later repair
- transcript: cold grouping initializes tool frames as 'running' and lets
the tool-message branch transition them to done/error — an approval-
gated or still-executing tool no longer shows as completed on rebuilds
Addresses review feedback on #1888.
* fix(transcript): seed live-created agents, merge backfills live-first
- kap-server: agents created after binding are marked seeded immediately —
their projector covers every event from creation on, so their pendings
announce without waiting for an explicit history read (which previously
left live subagent approvals/questions stuck in the unseeded queue)
- kap-server: the initial backfill merges turns live-first via
healTurnOps (snapshotToOps gains a turn-mapper parameter) — live frame
fields landed during the disk read (display/approvalId, longer text)
are no longer replaced by the staler persisted version
Addresses review feedback on #1888.
* fix(transcript): count pages in turn segments, not head units
- transcript: the leading non-turn unit no longer consumes a turn slot —
pages are counted in turn segments and the head unit rides only with the
page reaching the first turn. A timeline with a head marker and exactly
pageSize turns used to drop the marker from the newest page and
hallucinate an older marker-only page (has_more: true with no older
turns)
Addresses review feedback on #1888.
* fix(transcript): send baseline resets after cursor replay
- kap-server: broadcaster.subscribe gains deferTranscriptReset (recording
prev grades/filter per target) plus flushTranscriptSeed; the v1
connection defers the transcript baseline on cursor-carrying
(re)subscribes and flushes it after replay — a reconnecting client no
longer sees the reset's current seq ahead of the replayed lower-seq
backlog
Addresses review feedback on #1888.
* fix(transcript): gate the ops fan-out only when a reset is coming
- kap-server: willSendTranscriptReset decides upfront whether any reset
will be sent (grade upgrade or widened legacy filter); a same-grades
resubscribe no longer un-seeds the target, so ops emitted mid-resubscribe
keep flowing instead of being silently dropped by the fan-out gate
Addresses review feedback on #1888.
* fix(transcript): seed subscribers even when no reset is owed
- kap-server: a no-reset subscription (e.g. a client subscribing to a
fresh session with an empty roster) now still marks the target seeded
after subscribeTranscript completes — roster resets and ops would
otherwise stay gated forever once agents appear
Addresses review feedback on #1888.
* fix(transcript): guard mismatched appends, expose prompt via klient
- transcript: appendAtOffset now treats an overlapping chunk whose head
does not match the local tail as a gap (diverged stream) instead of
silently rewriting from the offset and dropping local content
- klient: add the optional prompt field to the turn.started event schema
so SDK listeners receive it instead of zod stripping it
Addresses review feedback on #1888.
* fix(transcript): open turns for subagent run prompts in cold grouping
- transcript: add the subagent system trigger to the turn-opening set —
a subagent's run prompt (persisted as system_trigger/'subagent') always
launches a new engine turn, so resumed subagent histories no longer fold
the response into the previous turn or lose the prompt
Addresses review feedback on #1888.
* fix(transcript): guard bus subscriptions independently of projectors
- kap-server: subscribeAgent now guards on a dedicated subscribedAgents
set instead of projector existence — a projector seeded before its
agent's handle exists (e.g. during an on-demand backfill) no longer
blocks the bus subscription, so the agent's live events keep flowing
Addresses review feedback on #1888.
* fix(kimi-inspect): reconcile the transcript on every socket open
- apps/kimi-inspect: TranscriptWs now reports onReconnected on the FIRST
successful open too, not only on re-established ones — ops emitted
between the REST page load and the subscription (a delayed or failed
first connection) were previously lost onto a stale store; the consumer's
refresh guard drops the no-op call while the initial load is in flight
Addresses review feedback on #1888.
* fix(transcript): derive the active step, dedupe tool error rendering
- kap-server: the projector gains a stepOrdinal lookup backed by the
engine's activity view (resolved lazily through the agent lifecycle), so
deltas after a late attach at step >= 2 land in the real active step
instead of a synthesized t<N>.1
- apps/kimi-inspect: render a tool frame's error only when it differs from
its output — onToolResult sets both to the same string for failed tools,
which drew the failure twice in red
Addresses review feedback on #1888.
* fix(kimi-inspect): reconcile the transcript on the subscribe ack
- apps/kimi-inspect: TranscriptWs now fires onReconnected when the
subscribe ack for its client_hello arrives instead of at socket open —
the server attaches the transcript stream only after processing
client_hello, so a refresh fired at open could finish before the
subscription was active and still miss the ops in between
Addresses review feedback on #1888.
* fix(kimi-inspect): coalesce concurrent transcript refreshes instead of dropping
A subscribe ack landing while the initial REST load was still in flight
hit the `if (refreshing) return` guard, so ops emitted between the REST
page snapshot and the WS subscribe were neither in the page nor
delivered over the socket. Replace the drop guard with a coalesced
runner: at most one refresh in flight, and triggers during a run are
collapsed into exactly one follow-up run after it settles.
* fix(kap-server): force the transcript baseline after cursor-based replay
A cursor re-subscribe at unchanged grades deferred its baseline and then
compared against the previous grades on flush, so no reset was sent —
while volatile ops fanned out during the deferral had been dropped,
leaving the client with a permanent gap. flushTranscriptSeed now always
seeds a full baseline (previous grades no longer tracked in the deferred
record), and a regression test covers the same-grade cursor resubscribe.
Also drop the inline comments added to shellCommandService.ts — the
agent-core-v2 convention keeps commentary in the top-of-file block; the
context moved there.
* fix(kap-server): harden transcript seeding against stale and wildcard subs
Two subscribeTranscript gaps found in review:
- Re-read the target's subscription after the history awaits: subscribe
work runs asynchronously, so an overlapping downgrade/unsubscribe used
to be answered with resets computed from the stale spec. The reset
loop now uses the latest grades/filter from state.targets and bails
when the target is gone or no longer graded.
- Backfill roster agents admitted via the wildcard grade, not just
explicitly named ones: a historical subagent seeded into the roster
from session metadata had no materialized AgentTranscript, so
wildcard subscribers silently never received its baseline reset.
Adds regression tests for the wildcard backfill, the mid-seed
downgrade, and the mid-seed unsubscribe (all three fail without the
fix); makeCore now accepts persisted agent metadata for roster seeding.
* fix(transcript): let meta.merge clear mode badges on mode exit
`agent.status.updated` with `planMode: false` / `swarmMode: false` was
dropped by the transcript projector because `meta.merge` could only set
mode badges, never clear them — clients kept rendering an exited mode
until the next full reset. The merge wire shape now accepts `null` per
mode key (set = object, clear = null, absent = keep): the reducer
deletes the key and normalizes an empty `modes` away, the zod schema
validates the nullable form, and the projector emits the clearing op
for exit events.
* fix(agent-core-v2): keep system-turn steering text out of turn prompts
`turn.started.prompt` was populated from the turn input for every
origin, so system-triggered turns (goal continuation, subagent run,
cron) exposed their internal steering text to live transcript
consumers; the cold rebuild mirrored the same leak when grouping
persisted history. The loop now populates the prompt only for
displayable user origins (user input, or a user-slash skill/plugin
activation) via the new isDisplayablePromptOrigin gate, and the cold
grouping opens hidden-origin turns promptless. Turns still open
normally — only the prompt text is withheld.
* fix(kap-server): reattach the transcript fan-out after a session reload
When the engine session closed or archived, TranscriptService dropped
the live store together with its ops listener set, but the
broadcaster's SessionState kept its transcriptStream — so
ensureTranscriptStream returned early for a later subscribe on the
resumed session, delivering a fresh reset but never the live
transcript.ops. The stream is now pinned to its TranscriptStore
instance and the fan-out re-registers whenever a rebuilt store shows
up. Adds a regression test that drops the service entry mid-stream and
asserts ops keep flowing after resubscribe (fails without the fix).
* fix(transcript): map legacy background_task origins in cold rebuilds
Legacy/v1 sessions persist background-task notifications with
origin.kind === 'background_task' (the live mapper already handles that
spelling), but the cold grouping only mapped 'task' — after a restart
those turns fell through to { kind: 'other' } and lost their taskId, so
the transcript could no longer associate the notification turn with its
background task. Both spellings now share the task-origin branch.
* fix(kap-server): project no-taskId shell failures into the transcript
A foreground `!` command that failed before onForegroundTaskStart ran
(Bash validation/spawn/registerTask errors) published shell.output /
shell.completed with taskId undefined, and the projector's guard
dropped them — the live transcript lost the stderr and the terminal
state of a command that did run. Shell events now resolve their task as
the id learned at shell.started, else the event's own taskId, else a
synthetic per-command id (shell-<commandId>), so early failures land
like any other shell task.
* refactor: drop the /api/v2 RPC surface and the klient http transport
- kap-server: remove the /api/v2 REST routes and /api/v2/ws socket (registerRpcRoutes renamed to serviceDispatcherRoutes; transport/ws/{eventMap,registerWs,wsClient,wsConnection,wsProtocol} deleted). /api/v1/debug/* is now the only RPC surface — a reflection dispatcher over the entire scoped DI registry with no whitelist — and /api/v1/ws the only WS endpoint
- klient: drop the http transport (transports/http/*, transports/ws/wsSocket.ts) and the kap-server devDependency; transports reduce to the ipc|memory subpath entries, and the dual/v2 e2e suites go with them
- kimi-inspect: target /api/v1/debug only with no fallback, replace the Service-event push channel (wsChannel/wsSocket) with on-demand fetch plus 15 s polling, and show a blocking "Debug surface unavailable" screen on connection failure
- transcript: add global attachment/interaction/todo entities (model, ops, wire schema) and project them from engine events in kap-server's coreEventMap
* fix(kimi-inspect): drop the unused TranscriptTodo import
|