Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
eb8cecb
refactor(agent-core-v2): extract toolApproval domain from permissionGate
sailist Jul 21, 2026
ffbe67f
feat(kimi-inspect): add App Services view for app-scope service refle…
sailist Jul 21, 2026
133786e
refactor(agent-core-v2): restructure workspace domains
sailist Jul 21, 2026
abdbe0f
feat(transcript): add op-batch sequencing and point-to-point catch-up
sailist Jul 21, 2026
80a512a
feat(transcript): persist plan revisions and task/interaction facts, …
sailist Jul 21, 2026
dabdaaf
fix(agent-core-v2): anchor external PreToolUse hooks before the permi…
sailist Jul 21, 2026
cfec4e8
refactor(agent-core-v2): replace ordered onBeforeExecuteTool hook wit…
sailist Jul 22, 2026
752d731
refactor(agent-core-v2): unify veto payload on ExecutableToolResult
sailist Jul 22, 2026
e4977b1
fix(agent-core-v2): pull up IAgentPermissionGate in agent activation
sailist Jul 22, 2026
ee00331
refactor(agent-core-v2): fold systemReminder domain into contextMemor…
sailist Jul 22, 2026
efd4327
refactor(klient): merge providers/models/catalog into global.kosong f…
sailist Jul 22, 2026
5fc019a
feat(transcript,kimi-inspect): add tag field to text frames and impro…
sailist Jul 22, 2026
916a734
refactor(transcript): rename wire/ directory to contract/
sailist Jul 22, 2026
ba63004
refactor(transcript): rename wire/ directory to contract/
sailist Jul 22, 2026
16b0978
Revert "refactor(agent-core-v2): fold systemReminder domain into cont…
sailist Jul 22, 2026
f399a06
feat(transcript): add wire-equivalent detail, dedupe session events
sailist Jul 22, 2026
5f65cb9
fix(klient): resolve lint errors in ipc channel stream and e2e matrix…
sailist Jul 22, 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
2 changes: 1 addition & 1 deletion .agents/skills/agent-core-dev/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ End-to-end procedures that span the stages. Reach for these before reading the s
- Topic: [Config](config.md) — the section-registry model, App vs Session split, owning a config section, the TOML format, and the env overlay.
- Topic: [Errors](errors.md) — co-located `XxxError`, the central code registry, wire serialization, boundary translation.
- Topic: [Flags](flags.md) — `registerFlagDefinition`, `IFlagService.enabled(id)`, the `[experimental]` config section, resolution precedence.
- Topic: [Permission](permission.md) — composable chain-of-responsibility kernel, policy registry + composer, `modes`/`agentTypes` metadata, `resolveExecution`/`accesses`.
- Topic: [Permission](permission.md) — risk-only chain-of-responsibility kernel, harness constraints and product reviews as domain `onBeforeExecuteTool` veto listeners (`veto` / `allow` / `pass` / cold `waitUntil` factories), shared `toolApproval` round-trip, policy registry + composer, `modes`/`agentTypes` metadata, `resolveExecution`/`accesses`.
- Topic: [Telemetry](telemetry.md) — emitting events via `ITelemetryService`, context propagation, and appender destinations (`ConsoleAppender` / `CloudAppender`).
- [Stage 4 — Test](test.md): resolve the system under test by interface, pick `TestInstantiationService` vs `createScopedTestHost`, shared stubs, service groups, teardown.
- [Stage 5 — Verify & submit](verify.md): `lint:domain`, `typecheck`, `test`, and the pre-submit checklist.
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/agent-core-dev/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ domain: `sessionLifecycle` (owning scope: App)
├─ hostEnvironment @App direct — gates scope creation on the probe
├─ sessionIndex @App direct — persisted read model for cold resumes
├─ storage @App direct — atomic docs + append logs
├─ workspaceRegistry @App direct — resolves a session's workspace
├─ workspace @App direct — resolves a session's workspace
└─ event @App direct — broadcasts session-level facts (e.g. archived)
```

Expand Down
4 changes: 2 additions & 2 deletions .agents/skills/agent-core-dev/domain-boundaries.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Before introducing `I{Domain}EntityService`, classify the persistence model:
| **Append-log / event-sourced** | The authoritative record is "what happened" | `wireRecord`, `contextMemory`, `goal`, `plan`, `permission` transitions |
| **Blob / key-value** | Large or content-addressed bytes | media offload, blob store |
| **Indexed query / read model** | Derived, queryable view | `sessionIndex`, future `IQueryStore` projections |
| **Registry / catalog** | Global or scoped known items | `workspaceRegistry`, `toolRegistry` |
| **Registry / catalog** | Global or scoped known items | `workspace`, `toolRegistry` |
| **Ephemeral runtime state** | No durable entity | active turn handle, pending interactions, terminal handles |

See [persistence.md](persistence.md) for the `Store → Storage → backend` rules. A domain EntityService is a business facade over those stores; it is not a replacement for the store layer.
Expand Down Expand Up @@ -101,7 +101,7 @@ The `session` domain owns only Session-level identity, metadata, lifecycle comma
| Background tasks | `background` |
| Cron tasks | `cron` |
| Pending approvals / questions | `interaction` / `approval` / `question` |
| Workspace | `workspaceRegistry` |
| Workspace | `workspace` |
| Provider / config | `provider` / `config` |

Entity-service conclusion for `session`:
Expand Down
10 changes: 5 additions & 5 deletions .agents/skills/agent-core-dev/edge-exposure.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ Read = `GET`, write = `POST`. `sid` = `session_id`, `aid` = `agent_id`.
| `sessions` | `list` | ISessionIndex.list | GET |
| `sessions` | `get` | ISessionIndex.get | GET |
| `sessions` | `countActive` | ISessionIndex.countActive | GET |
| `workspaces` | `list` | IWorkspaceRegistry.list | GET |
| `workspaces` | `get` | IWorkspaceRegistry.get | GET |
| `workspaces` | `createOrTouch` | IWorkspaceRegistry.createOrTouch | POST |
| `workspaces` | `update` | IWorkspaceRegistry.update | POST |
| `workspaces` | `delete` | IWorkspaceRegistry.delete | POST |
| `workspaces` | `list` | IWorkspaceService.list | GET |
| `workspaces` | `get` | IWorkspaceService.get | GET |
| `workspaces` | `createOrTouch` | IWorkspaceService.createOrTouch | POST |
| `workspaces` | `update` | IWorkspaceService.update | POST |
| `workspaces` | `delete` | IWorkspaceService.delete | POST |
| `config` | `get` / `getAll` / `inspect` | IConfigService.* | GET |
| `config` | `set` / `replace` / `reload` | IConfigService.* | POST |
| `providers` | `list` / `get` | IProviderService.* | GET |
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/agent-core-dev/orient.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ So a Session-scoped service is not "L1" — e.g. `session` is Session-scoped but
|---|---|---|
| L0 | base infrastructure | `_base`, `errors`, `llmProtocol` |
| L1 | bridges & low-level capabilities | `log`, `telemetry`, `event`, `environment`, `bootstrap`, `storage` |
| L2 | data & cross-cutting capabilities | `records`, `wireRecord`, `config`, `provider`, `auth`, `workspaceRegistry` |
| L2 | data & cross-cutting capabilities | `records`, `wireRecord`, `config`, `provider`, `auth`, `workspace` |
| L3 | registries & capabilities | `tool`, `toolRegistry`, `permission*`, `flag`, `skill`, `plugin` |
| L4 | agent behaviour | `turn`, `loop`, `prompt`, `profile`, `contextMemory`, `goal`, `plan`, `swarm` |
| L5 | async lifecycle | `background`, `mcp`, `cron`, `agentTool` |
Expand Down
55 changes: 31 additions & 24 deletions .agents/skills/agent-core-dev/permission.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ The target design for the agent-core permission system. Read this when touching

> **The permission system should be a composable, registrable chain of responsibility (a microkernel).** The kernel only runs the chain in order, first hit wins; concrete permission dimensions (policies) are contributed by their owning Domain Services through a registry; tools only declare standardized resource access (`accesses`) in `resolveExecution`, and generic dimensions consume that metadata.
>
> **The chain adjudicates risk only.** A policy node answers "how dangerous is this call, and may the user override that judgment?" — its `ask`/`deny` outcomes are always user-overridable. **Harness constraints are not permissions**: a mechanism that limits the agent for its own correctness (plan-mode write guard, AgentSwarm batch exclusivity, btw side-question fork, goal budget rejection) produces a hard deny with no ask channel and no per-call user exemption. Those live in their owning domains as `onBeforeExecuteTool` veto listeners that call `event.veto(...)` (precedent: `goalService.ts`'s budget/stale rejection). Product reviews (plan review, goal-start review) are likewise not permissions: the owning domain intercepts its tool with a cold `event.waitUntil(factory)` and drives the shared `IAgentToolApprovalService` round-trip itself, so the review only starts once no other listener vetoed the call.
>
> **Do not introduce Casbin** — the hard part here is *decision behavior* (continuations, side effects, RPC, state machines), not "match + scalar decision".

## 1. Problem definition
Expand Down Expand Up @@ -56,7 +58,7 @@ Casbin = single Strategy + data-driven. This design = multiple Strategies + chai

1. **The chain encodes "permission dimensions", not "tools".** Adding a tool does not lengthen the chain; only adding a dimension adds a node.
2. **Two contribution paths:** high-frequency trivial specifics go through the **data path** (rules); low-frequency new dimensions with behavior go through the **code path** (policies).
3. **Domain self-registration:** a domain that owns a dimension (plan/goal/swarm) registers its policy in DI, mirroring v2's existing "domain self-registers tools".
3. **Guard/review off-chain, risk on-chain:** harness constraints and product reviews ship with their owning domain as `onBeforeExecuteTool` veto listeners (§5.4); risk dimensions contributed by a domain self-register as chain policies in DI, mirroring v2's "domain self-registers tools".
4. **Tools declare resources; generic dimensions consume them:** bash/write/read only declare `accesses`; file/security dimensions judge centrally.

### 5.2 Core abstractions
Expand Down Expand Up @@ -106,23 +108,23 @@ Key points:

Most growth goes through the data path — node count is bounded by "kinds of behavior"; rule count grows with specifics (rule matching is a cheap Set/glob).

### 5.4 Domain self-registration
### 5.4 Domain dimensions: guard/review via the executor veto event, policy registration for risk

Mirrors v2's "domain registers tools in its constructor". `PlanService` self-registers its dimensions:
**Harness constraints and product reviews no longer live on the chain.** A domain that owns one registers an `onBeforeExecuteTool` veto listener and adjudicates through the event:

```ts
// src/plan/planService.ts
constructor(@IPermissionPolicyRegistry registry: IPermissionPolicyRegistry) {
registry.register({ name: 'plan-mode-guard-deny', phase: 'guard',
factory: a => new PlanModeGuardDenyPolicy(a.get(IPlanService)) });
registry.register({ name: 'plan-mode-tool-approve', phase: 'mode',
factory: a => new PlanModeToolApprovePolicy(a.get(IPlanService)) });
registry.register({ name: 'exit-plan-mode-review-ask', phase: 'user-ask',
factory: a => new ExitPlanModeReviewAskPolicy(a.get(IPlanService), a.get(IPermissionModeService)) });
// src/plan/planService.ts — constructor
constructor(@IAgentToolExecutorService executor, ...) {
executor.onBeforeExecuteTool((event) => this.guardToolExecution(event));
}
```

A complex domain may register a single **composite** node externally and run a small internal chain, hiding its internal order from the global chain.
- The veto event carries no id and no ordering contract. Listeners answer with `event.veto(result)` (first one wins, ends adjudication), `event.allow()` (final pass, ends everything including the permission gate's own listener), `event.pass(metadata)` (pass with an `executionMetadata` trace, ends nothing), or `event.waitUntil(factory)` (defer to a cold factory).
- **Guard** (hard deny): call `event.veto(denyToolExecution(toolApproval.formatDenyMessage(...)))`. An immediate veto suppresses every pending `waitUntil` factory, so a deny can never be preceded by someone else's approval prompt.
- **Review** (product approval): intercept the tool with `event.waitUntil(() => ...requestToolApproval(event, ask, origin))`. The factory is cold — the executor only invokes it after every listener ran without a veto or an allow, so the review's Interaction starts only once the call is otherwise clear to proceed; abstain (no statement) for every case you do not review so user rules still apply.
- **Plain allow**: do NOT `allow()` casually — prefer putting the tool in `default-tool-approve`'s whitelist so user deny/ask rules keep their precedence; reserve `allow()` for cases like the plan-file write guard that must bypass even the permission chain.

**Risk dimensions contributed by a domain still go through the chain** (the registry path below): a domain whose state changes the *risk* verdict registers its policy via `IPermissionPolicyRegistry`, mirroring v2's "domain self-registers tools". A complex domain may register a single **composite** node externally and run a small internal chain, hiding its internal order from the global chain.

### 5.5 Tools declare resources at runtime (`resolveExecution` / `accesses`)

Expand Down Expand Up @@ -170,37 +172,42 @@ Each new resource kind can pair with a generic dimension that consumes it; tools

### 5.6 Dimension ownership

| Dimension | Owner (who registers) | Type |
| Dimension | Owner | Type |
|---|---|---|
| external hook veto | `externalHooks` domain | generic |
| tool-batch exclusivity | `swarm` domain | domain-specific (ships with the AgentSwarm tool) |
| runtime-mode posture | `permissionMode` domain | generic |
| plan-mode constraints | `plan` domain | domain-specific |
| goal-start approval | `goal` domain | domain-specific |
| tool-batch exclusivity | `swarm` domain — `onBeforeExecuteTool` veto listener | harness constraint (off-chain) |
| plan-mode write guard | `plan` domain — `onBeforeExecuteTool` veto listener | harness constraint (off-chain) |
| plan review | `plan` domain — same listener's `waitUntil` + `toolApproval` | product review (off-chain) |
| goal-start review | `goal` domain — veto listener's `waitUntil` + `toolApproval` | product review (off-chain) |
| goal budget / stale rejection | `goal` domain — `onBeforeExecuteTool` veto listener | harness constraint (off-chain) |
| btw tool disablement | `btw` domain — veto listener on the fork | harness constraint (off-chain) |
| runtime-mode posture (auto/yolo) | `permissionMode` domain (chain nodes, pending the level×routing split) | generic |
| static config rules | `permissionRules` domain | generic (data path) |
| session approval memory | `permissionRules` domain | generic |
| sensitive / special paths | generic "file-access/security" dimension | generic (consumes `accesses`) |
| tool intrinsic risk | core permission | generic (consumes tool declarations) |
| tool intrinsic risk | core permission (`default-tool-approve`) | generic (consumes tool declarations) |
| workspace write trust | generic "file-access/security" dimension | generic (consumes `accesses`) |
| fallback | core permission | generic |
| approval round-trip | `toolApproval` domain — shared by gate asks and domain reviews | infrastructure |

Pattern: **specific dimensions ship with their owning domain + tool; generic dimensions register centrally and apply across tools via the declared `accesses`.**
Pattern: **harness constraints and reviews ship with their owning domain as `onBeforeExecuteTool` veto listeners; risk dimensions ship as chain policies (self-registered once the registry lands); generic dimensions register centrally and apply across tools via the declared `accesses`.**

## 6. Evolution path

Incremental, not big-bang:

1. **Registry + Composer (zero behavior change).** Replace the 19 hardcoded `new`s in v2 `PermissionPolicyService` with reads from `IPermissionPolicyRegistry`; register existing policies as-is. Immediately gain multi-agent/mode selectable chains and an external registration entry.
2. **Declarative modes.** Lift the mode guards in `YoloModeApprove` / `AutoModeApprove` into `modes` metadata.
3. **Sink domain dimensions.** Move registration of plan/goal/swarm policies into their owning domain service constructors.
1. ~~**Sink domain dimensions.**~~ **Done** — plan guard/review, goal-start review, swarm batch exclusivity, and btw deny-all moved out of the chain into their owning domains as `onBeforeExecuteTool` veto listeners (immediate `veto` / `allow` / `pass` statements plus cold `waitUntil` factories for approval round-trips); the shared approval round-trip was extracted to `IAgentToolApprovalService`; `registerPolicy` was removed (btw was its only production user). The chain now holds 12 risk-adjudication nodes only.
2. **Level × routing split.** Separate "risk level" (read-only / read-write / yolo posture — what `yolo-mode-approve` really is) from "interaction routing" (what `auto-mode-approve` / `auto-mode-ask-user-question-deny` really are: route permission asks and reviews without the user). The routing layer lands on the `session/approval` broker; the three remaining mode policies leave the chain here.
3. **Registry + Composer.** Replace the hardcoded `new`s in `PermissionPolicyService` with reads from `IPermissionPolicyRegistry`; lift mode guards into `modes` metadata. Chain shape becomes selectable per `(agent, mode)` and externally extensible.
4. **(On demand) extend resource types.** When non-file resources (network/DB/shell) need structural dimensions, extend the `ToolResourceAccess` union.
5. **(On demand) swap the matching kernel for Casbin.** Only when external rules genuinely need RBAC/ABAC semantics, swap the data-path rule-matching kernel for Casbin. Not before.

## Red lines (this topic)

- Do not introduce Casbin — decisions are behavior bundles, not scalar effects.
- The chain adjudicates risk only. A node whose deny/ask the user cannot per-call exempt is a harness constraint: implement it as an `onBeforeExecuteTool` veto listener in the owning domain (`event.veto(...)` / `event.allow()`), never as a chain policy.
- Product reviews (plan/goal) are not permissions either: the owning domain intercepts its tool with a cold `event.waitUntil(factory)` and drives `IAgentToolApprovalService` itself; the gate only handles chain asks.
- The chain encodes dimensions, not tools: a new tool must not lengthen the chain.
- New specifics go through the data path (rules); only new behavior goes through the code path (a policy node).
- A domain that owns a dimension self-registers its policy in DI; do not centralize domain policies in core.
- New specifics go through the data path (rules); only new risk behavior goes through the code path (a policy node).
- Tools only declare `accesses`; generic dimensions consume them. kaos is the execution environment, not the permission abstraction.
- Use `factory` (Agent-scope instantiation), not `instance`, for registered policies.
2 changes: 1 addition & 1 deletion .agents/skills/agent-core-dev/server-align.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Self-check: "would a released v1 client get a byte-identical envelope from `pack

Resolve the v2 Service that will back the route. Two cases:

**Case A — the v2 native Service already matches the v1 contract.** Use it directly. Most data/command Services (`IConfigService`, `IWorkspaceRegistry`, `IApprovalService`, `IQuestionService`, `IFileStore`, …) land here: the route is a thin adapter that resolves the scope, calls the method, and wraps the result. Examples: `routes/config.ts`, `routes/messages.ts`, `routes/questions.ts`, `routes/files.ts`.
**Case A — the v2 native Service already matches the v1 contract.** Use it directly. Most data/command Services (`IConfigService`, `IWorkspaceService`, `IApprovalService`, `IQuestionService`, `IFileStore`, …) land here: the route is a thin adapter that resolves the scope, calls the method, and wraps the result. Examples: `routes/config.ts`, `routes/messages.ts`, `routes/questions.ts`, `routes/files.ts`.

**Case B — the v1 contract needs behavior that would distort the v2 domain.** Introduce a **`*LegacyService`** — an L7 edge adapter that implements the v1 contract **on top of** the v2 native Service, leaving the native Service untouched. The v2 native Service keeps serving `/api/v2`; the LegacyService serves `/api/v1`.

Expand Down
5 changes: 5 additions & 0 deletions .changeset/plan-revision-blobs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Record plan content as versioned facts: each plan review submission offloads the document to a versioned per-agent plan directory and journals a reference record, so the transcript surfaces plan revisions (marker + badge with review path) and rebuilds them after restarts.
5 changes: 5 additions & 0 deletions .changeset/slim-reset-baseline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Stop embedding historical turns in the transcript WS baseline reset; it now carries only global state and the stream watermark, and clients page history through the REST transcript API.
5 changes: 5 additions & 0 deletions .changeset/transcript-event-dedup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": minor
---

Extend the transcript protocol with step usage and timing, streamed tool input and progress, subagent outcomes, agent status, and the prompt queue; WebSocket connections subscribed to the transcript protocol no longer receive the equivalent legacy session events.
5 changes: 5 additions & 0 deletions .changeset/transcript-user-messages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": minor
---

Add a session API endpoint that returns all turn-opening user messages of a session, grouped per agent. Query `GET /api/v1/sessions/{session_id}/transcript/user-messages` (optionally with `?agent_id=` for a single agent) to fetch them.
5 changes: 5 additions & 0 deletions .changeset/wire-facts-cold-fold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Persist task lifecycle and interaction records in session wire journals, and rebuild tasks, interactions, todos, and goal/plan meta when loading a cold transcript, so transcript state survives server restarts (older sessions are unaffected).
Loading
Loading