Skip to content

feat: agent-core-v2 permission/workspace refactors and transcript durability#2021

Merged
sailist merged 17 commits into
MoonshotAI:mainfrom
sailist:feat/transcript-wire-facts
Jul 22, 2026
Merged

feat: agent-core-v2 permission/workspace refactors and transcript durability#2021
sailist merged 17 commits into
MoonshotAI:mainfrom
sailist:feat/transcript-wire-facts

Conversation

@sailist

@sailist sailist commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — this PR lands a stack of engine refactors and transcript durability work developed together; the problems each part addresses are explained below.

Problem

  • The permission gate had grown into a catch-all: the approval round-trip, plan/swarm/goal harness constraints, and the static policy chain all lived in one domain, and the dynamic registerPolicy mechanism blurred domain ownership.
  • Workspace domains were split awkwardly (workspaceRegistry, workspaceLocalConfig), with id-spelling resolution and workspace-centric session queries lacking a home.
  • Transcript clients converged only via full refreshes: WS op batches carried no sequencing, so a reconnect or lost frame forced a baseline reset; interaction frames were a second, frame-level channel duplicating the interaction item.
  • Transcript state did not survive restarts: tasks, interactions, todos and goal/plan meta lived only in the live store, and plan review content was never recorded, so cold sessions rebuilt a bare turn tree.
  • kimi-inspect had no place to inspect app-scope services.

What changed

1. Extract toolApproval from the permission gate (agent-core-v2)

  • New agent-scoped toolApproval domain owns the approval round-trip: builds approval requests, drives the session/approval broker, publishes permission.approval.* events, records session approval rules, and resolves ask continuations.
  • permissionPolicy is slimmed to the static risk-adjudication chain; the dynamic registerPolicy mechanism is dropped.
  • Harness constraints move out of the policy chain into their owning domains as toolExecutor hooks ordered before: 'permission': plan-mode guard and exit-plan review (plan), swarm batch exclusivity and AgentSwarm approve (swarm), goal-start review (goal), btw deny (session/btw). The deleted policies are covered by the new hook tests (planGuard.test.ts, toolApproval.test.ts).
  • Follow-up fix: external PreToolUse hooks anchor before: 'permission' again — the extraction forces the gate to construct early (planService injects it), which had flipped the hook order and could hang a turn on a policy ask before PreToolUse could block.

2. Restructure workspace domains (agent-core-v2)

  • Rename workspaceRegistryworkspace and workspaceLocalConfigprojectLocalConfig.
  • Extract id-spelling resolution into workspaceAliases (IWorkspaceAliases) and workspace-centric session queries into workspaceSessions (IWorkspaceSessions).
  • kap-server routes, klient contracts, and related tests updated to the new services.

3. Transcript op-batch sequencing and point-to-point catch-up

  • Wire contract: per-agent monotonic batch seq watermark on transcript.reset / transcript.ops and the REST transcript response, a transcript_since subscription cursor, and a GET .../transcript/ops catch-up shape — every field optional so legacy peers fall back to loss-signal-driven refreshes.
  • kap-server assigns consecutive per-agent seqs and retains them in a bounded in-memory journal; the baseline reset is now items-empty because history always pages in over REST.
  • interactionFrame is dropped; interactions live on the interaction item in the ops stream.
  • kimi-inspect tracks the watermark, runs seq-gap/reconnect catch-up with full-refresh fallback, and gains the Transcript audit panel (AuditTrail timeline, structural diff, state tree).

4. Persist plan revisions and task/interaction facts; user-messages endpoint

  • Every ExitPlanMode review submission snapshots the plan file into blob storage (plan/<id>/v<N>.md) and journals a reference-only plan.revision record ({id, version, path, sha256, bytes}), projected live and cold as a plan.revision marker plus the plan badge (reviewPath, version).
  • task.started / task.terminated (with a bounded output tail) and interaction.request / interaction.resolved ops persist to the wire journal; foldFacts rebuilds tasks, interactions, todos and goal/plan/swarm meta on cold transcript loads (interactions left pending fold to cancelled).
  • New GET /sessions/{session_id}/transcript/user-messages returns all turn-opening prompts grouped per agent (optional agent_id), with referenced attachment metadata.

5. kimi-inspect: App Services view

  • New services top-level view on the NavRail: full-width app-scope Service panel grid; session/agent scopes stay in the Chat view's Inspector.
  • Shared ServicePanels extracted from Inspector; methodArgs helper builds per-method argument editors from channel metadata; variadic service calls supported in panels.ts.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

@changeset-bot

changeset-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5f65cb9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 21, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@5f65cb9
npx https://pkg.pr.new/@moonshot-ai/kimi-code@5f65cb9

commit: 5f65cb9

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8821919731

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +45 to +47
?.hooks.onBeforeExecuteTool.register('btw-deny-all', async (ctx) => {
ctx.decision = { block: true, reason };
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve BTW deny ordering before permission

For BTW child agents created by lifecycle.fork(), the normal agent services have already been ignited before this hook is appended, so the existing permission hook runs first. In manual/ask modes a tool call from the side-question agent can therefore open a permission approval round-trip before btw-deny-all finally blocks it; the old dynamic deny policy ran before every static policy. Register this hook ahead of permission (as the other extracted harness guards do) so BTW tools are denied immediately.

Useful? React with 👍 / 👎.

Comment on lines +574 to +576
// Second fold: tasks / interactions / todos / meta (goal, plan, swarm)
// come from the non-`context.*` records in the same journal.
return foldWireRecordFacts(records, base);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Replay folded facts into live backfills

This enriches readColdSnapshot() with interactions, todos, attachments, and meta facts, but the same snapshot is also consumed by backfillAgent() for sessions that are still live; that path converts it with snapshotToOps(), which only emits item/turn ops, tasks, and meta. If the transcript service first binds after a TodoWrite, approval/question, or media prompt, the live REST path serves from the backfilled store and silently drops those persisted global entities even though the cold path would show them. Emit interaction.upsert, todo.upsert, and attachment.upsert during snapshot backfill as well.

Useful? React with 👍 / 👎.

Comment on lines 494 to +497
this.buildTranscriptEnvelope(state, 'transcript.ops', {
agent_id: agentId,
ops: filtered,
seq,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep seq contiguous after grade filtering

The new seq is the unfiltered per-agent journal sequence, but lower transcript grades can drop whole batches here (append-only batches for block, step/frame/append batches for turn). A turn/block subscriber will then see the next delivered frame jump from N to N+k during normal filtered traffic, which violates the consecutive seq contract and makes gap-detecting clients refresh or catch up repeatedly. Either send an empty seq-bearing frame for filtered-out batches or track a recipient-visible watermark.

Useful? React with 👍 / 👎.

sailist added 17 commits July 22, 2026 19:02
- 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
…ction

- 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
- 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
- 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
…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
…ssion 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.
…h 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
- 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
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.
…y 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`
…acade

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.
…ve 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
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
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
…extMemory appendTagged"

This reverts commit 55afaa3.

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).
- 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
@sailist
sailist force-pushed the feat/transcript-wire-facts branch from 1aa3769 to 5f65cb9 Compare July 22, 2026 11:08
@sailist
sailist merged commit 64f053c into MoonshotAI:main Jul 22, 2026
14 checks passed
Yorha9e pushed a commit to Yorha9e/kimi-code that referenced this pull request Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant