refactor(session): migrate host state reads to projections

This commit is contained in:
_Kerman 2026-08-19 13:57:58 +08:00
parent 4c421ec882
commit 1a72ae202a
337 changed files with 3186 additions and 1736 deletions

View file

@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-07-session-projection-mandatory-seam.md
2026-08-07-session-projection-mandatory-seam.md: 6348ae9afdb1689cd4885e658651d1e5a1f44a4b
2026-08-07-session-projection-mandatory-seam.zh.md: 95f3489275f67fc18a030345e120e2cb8c88c191

View file

@ -0,0 +1,32 @@
# Agent Note: Session projections as a required seam
Status: implemented
English | [中文](2026-08-07-session-projection-mandatory-seam.zh.md)
## Problem
An optional projection registry lets contributors and readers activate without the service, silently dropping host state, client read models, or subagent catalog fields. Batch-only reads also materialize every client view when a consumer needs one host value.
## Decision
This decision builds on the split between host projection state and client views in [Session projection state and client views](2026-08-19-session-projection-state-and-client-views.md).
`sessionProjections` is a required injection for every plugin that contributes or reads a projection unit. Official compositions mount the registry before those plugins. `ApiProxyService` follows the same rule; the lower-level `createApiProxy` factory remains tolerant for isolated tests and diagnostics.
The registry provides `stateOf(session, key)` for one typed host state and keeps `snapshot()` for batch carriers. Client views contain only consumed fields; host readers use `stateOf` for richer state.
`onChanged` publishes client-visible value changes only. Unit registration and removal remain effect-scoped registry lifecycle; they do not create a second Host event stream or client tombstone protocol. A later authoritative history or list baseline reflects the active key set.
## Alternatives considered
- **Keep the registry optional.** This preserves more partial compositions but makes missing read models indistinguishable from valid absence. Rejected because official profiles already mount the registry and configuration errors should fail at load.
- **Use `snapshot()` for every read.** This keeps one method but computes unrelated wire views and encourages consumers to depend on batch transport data for host logic. Rejected in favor of typed single-key state reads.
- **Send full host values to clients.** This avoids separate view types but exposes provenance and policy knobs that no client consumes. Rejected in favor of explicit cropped views.
- **Broadcast registry additions and removals across Host and mux streams.** The streams have no shared ordering, so clients need tombstones, buffered frames, and baseline retries to reconcile them. Rejected because plugin-key churn does not justify a second synchronization protocol.
## Consequences
- Missing projection composition fails during plugin activation.
- Host consumers avoid repeated whole-registry snapshots and log scans.
- Wire payloads exclude host-only fields and per-key watermark wrappers; ordinary baselines communicate the active key set.

View file

@ -0,0 +1,32 @@
# Agent Note: 会话投影作为必需 seam
Status: implemented
[English](2026-08-07-session-projection-mandatory-seam.md) | 中文
## 问题
可选的投影注册表会让贡献方和读取方在缺少该服务时仍然激活,并静默丢失 host 状态、客户端读模型或 subagent 目录字段。只有批量读取也会在消费方只需要一个 host 值时物化每个客户端 view。
## 决策
本决策建立在[会话投影的 host 状态与客户端视图](2026-08-19-session-projection-state-and-client-views.md)所定义的拆分之上。
每个贡献或读取投影单元的插件都把 `sessionProjections` 作为必需注入。正式组合在这些插件之前挂载注册表。`ApiProxyService` 遵循同一规则;较低层的 `createApiProxy` factory 对隔离测试和诊断保持容错。
注册表提供 `stateOf(session, key)` 来读取一个类型化 host 状态,并为批量 carrier 保留 `snapshot()`。客户端 view 只包含消费方使用的字段;host 读取方通过 `stateOf` 取得更丰富的状态。
`onChanged` 只发布客户端可见值的变化。单元注册和移除仍是绑定 effect 的注册表生命周期;它们不创建第二条 Host 事件流或客户端 tombstone 协议。后续权威 history 或 list 基线会反映活跃 key 集。
## 考虑过的替代方案
- **让注册表保持可选。** 这会保留更多不完整组合,但缺失读模型将无法与合法缺席区分。之所以否决,是因为正式 profile 已挂载注册表,且配置错误应在加载时失败。
- **每次读取都使用 `snapshot()`。** 这只保留一个方法,但会计算无关 wire view,并鼓励消费方让 host 逻辑依赖批量传输数据。改用类型化单 key 状态读取。
- **向客户端发送完整 host 值。** 这避免单独的 view 类型,但会暴露客户端不消费的来源信息和策略旋钮。改用显式裁剪的 view。
- **跨 Host 和 mux stream 广播注册表新增和移除。** 两条 stream 没有共享顺序,客户端因此需要 tombstone、缓冲帧和基线重试来协调。插件 key 变化不值得引入第二套同步协议。
## 后果
- 缺少投影组合会在插件激活期间失败。
- host 消费方避免重复的全注册表快照和日志扫描。
- 协议负载排除 host 内部字段和逐 key 水位包装;普通基线会传达活跃 key 集。

View file

@ -1,5 +1,4 @@
import { fileURLToPath } from 'node:url'
import { Context } from '@deepseek-ai/cordis'
import { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent'
import { CallId } from '@deepseek-ai/dsh-llm'
import { boot, loadOverlayPatches } from '@deepseek-ai/dsh-app-boot'
@ -20,7 +19,7 @@ try {
const agentId = SessionId('dsh-badge-snapshot')
const session = ctx.sessions.create(agentId, { meta: { cwd: process.cwd() } })
const agent: Agent = {
ctx: new Context(),
ctx,
id: agentId,
options: {},
session,

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/architecture.md
architecture.md: 2a51992a4f99d1827259c0a2ca2daa309f0ddd95
architecture.zh.md: d1629a088ed8d2fe61a7e20ab2ab1fd3709abd50
architecture.md: 3ad9dec77f59af84594057e74602a1cc6c31dd18
architecture.zh.md: e3e2128a71fd9b3abe915e9af2bfe1322f450661

View file

@ -95,6 +95,8 @@ The session log is the source of the context the model sees. `deriveMessages()`
**Model-visible means logged.** Anything that reaches a model request must be reconstructable from the log, and a runtime invariant asserts it. This is why a new model-visible input requires a new session event: extend `SessionEventMap` and render from the log.
**Projection seam.** `dsh-session-projection` owns `ctx.sessionProjections`: registered units fold committed events incrementally, host consumers read one typed state with `stateOf()`, and carriers batch cropped client views with `snapshot()`. Projection contributors and readers require this service, so an incomplete composition fails during activation. The agent loop registers shared `turnBoundary` state instead of making each consumer scan the log ([decision](../.agents/notes/implemented/architecture/2026-08-07-session-projection-mandatory-seam.md)).
## Capability seams
A **seam** is a swappable capability with three roles: a **Service Definition** declaring the interface, a **Service Provider** implementing it, and a **Consumer** using it, commonly a model-facing tool. A package may combine roles, but one role alone is not a seam; adding a capability means designing all three ([capability graph](capability-seams.md)).

View file

@ -99,6 +99,8 @@ turn/end
**模型可见即已记录。** 抵达模型请求的一切都必须能从日志重建,并由一项运行时不变量断言这一点。因此,新增一项模型可见输入就需要新增一个会话事件:扩展 `SessionEventMap` 并从日志渲染。
**投影 seam。** `dsh-session-projection` 提供 `ctx.sessionProjections`:已注册单元增量折叠已提交事件,host 消费方通过 `stateOf()` 读取单个类型化状态,载体通过 `snapshot()` 批量取得裁剪后的客户端视图。投影贡献方和读取方都要求该服务,因此不完整的组合会在激活时失败。agent loop 注册共享的 `turnBoundary` 状态,避免每个消费方各自扫描日志([决策](../.agents/notes/implemented/architecture/2026-08-07-session-projection-mandatory-seam.md))。
## 能力 seam
一个 **seam** 是一项可替换能力,包含三种角色:声明接口的 **Service Definition**、实现它的 **Service Provider**,以及使用它的 **Consumer**(通常是面向模型的工具)。一个包可以合并承担多个角色,但单一角色本身不是 seam;添加一项能力意味着把三者一并设计([能力图](capability-seams.md))。

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/config-catalog.md
config-catalog.md: 3f15ddbe310caf96a5c84b52628f9ba44f6e3637
config-catalog.zh.md: 403db954334506e8023c6ed5f5dbad74eec58c6e
config-catalog.md: 1a3699d4c2f8d0611811ce8532892ca59b29d466
config-catalog.zh.md: 6535c09adeb80d59639d47121dd8bd33e367f4bb

View file

@ -106,6 +106,8 @@ Source: [`packages/core/agent-default-model/src/index.ts:41`](../packages/core/a
## `@deepseek-ai/dsh-agent-instructions`
Requires: `sessionProjections`
```ts config-catalog
/** User-facing workspace instruction loader configuration. */
export interface Config {
@ -136,7 +138,7 @@ Source: [`packages/context/agent-instructions/src/config.ts:18`](../packages/con
## `@deepseek-ai/dsh-agent-loop`
Requires: `agents` · `sessions` · `llm` · `tools` · `systemPrompt`
Requires: `agents` · `sessions` · `llm` · `tools` · `systemPrompt` · `sessionProjections`
```ts config-catalog
/** Agent-loop plugin configuration. */
@ -162,7 +164,7 @@ export interface Config {
Depends on: [`AgentOptions`](subsystems/core.md) · [`SessionId`](subsystems/core.md)
Source: [`packages/core/agent-loop/src/index.ts:255`](../packages/core/agent-loop/src/index.ts)
Source: [`packages/core/agent-loop/src/index.ts:314`](../packages/core/agent-loop/src/index.ts)
<a id="deepseek-aidsh-agent-presets"></a>
@ -292,7 +294,7 @@ export interface GoalConfig {
Depends on: [`AgentLoopConfig`](#deepseek-aidsh-agent-loop) · [`GoalDomainConfig`](#deepseek-aidsh-goal) · [`InvariantConfig`](#deepseek-aidsh-invariants) · [`JobsConfig`](#deepseek-aidsh-jobs-local) · [`SessionTitleConfig`](#deepseek-aidsh-session-title) · [`SkillFileSystem`](../packages/skill/skill-filesystem/src/index.ts) · [`SkillRegistryConfig`](#deepseek-aidsh-skill) · [`SystemPromptConfig`](#deepseek-aidsh-system-prompt) · [`toolBash`](../packages/shell/tool-bash/src/index.ts) · [`toolGoal`](../packages/goal/tool-goal/src/index.ts) · [`toolJobs`](../packages/jobs/tool-jobs/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools) · [`toolSkill`](../packages/skill/tool-skill/src/index.ts) · [`workspaceContext`](../packages/context/agent-instructions/src/index.ts)
Source: [`packages/examples/agent-spine-demo/src/index.ts:92`](../packages/examples/agent-spine-demo/src/index.ts)
Source: [`packages/examples/agent-spine-demo/src/index.ts:93`](../packages/examples/agent-spine-demo/src/index.ts)
<a id="deepseek-aidsh-agent-tool-presentation"></a>
@ -630,7 +632,7 @@ Source: [`packages/fs/fs-sandbox/src/index.ts:49`](../packages/fs/fs-sandbox/src
## `@deepseek-ai/dsh-goal`
Requires: `agents`
Requires: `agents` · `sessionProjections`
```ts config-catalog
/** Deployment defaults for goal creation. */
@ -640,7 +642,7 @@ export interface Config {
}
```
Source: [`packages/goal/goal/src/index.ts:116`](../packages/goal/goal/src/index.ts)
Source: [`packages/goal/goal/src/index.ts:115`](../packages/goal/goal/src/index.ts)
<a id="deepseek-aidsh-headless"></a>
@ -662,7 +664,7 @@ Source: [`packages/bundle/headless/src/index.ts:31`](../packages/bundle/headless
## `@deepseek-ai/dsh-hooks-claude-code`
Requires: `shell`
Requires: `shell` · `sessionProjections`
```ts config-catalog
/** Plugin config: where the CC hook config lives + substitution roots. */
@ -694,13 +696,13 @@ export interface Config {
}
```
Source: [`packages/hooks/hooks-claude-code/src/index.ts:45`](../packages/hooks/hooks-claude-code/src/index.ts)
Source: [`packages/hooks/hooks-claude-code/src/index.ts:46`](../packages/hooks/hooks-claude-code/src/index.ts)
<a id="deepseek-aidsh-hooks-codex"></a>
## `@deepseek-ai/dsh-hooks-codex`
Requires: `shell`
Requires: `shell` · `sessionProjections`
```ts config-catalog
/** Plugin config: where the Codex hooks.json lives + the model name for payloads. */
@ -721,13 +723,13 @@ export interface Config {
}
```
Source: [`packages/hooks/hooks-codex/src/index.ts:44`](../packages/hooks/hooks-codex/src/index.ts)
Source: [`packages/hooks/hooks-codex/src/index.ts:45`](../packages/hooks/hooks-codex/src/index.ts)
<a id="deepseek-aidsh-host-apiproxy"></a>
## `@deepseek-ai/dsh-host-apiproxy`
Requires: `agentDefaultModel` · `agents` · `attachments` · `directoryPicker` · `llm` · `sessions` · `subagents` · `sessionQuery` · `tools` · `userQuestions` · `workspaceRegistry`
Requires: `agentDefaultModel` · `agents` · `attachments` · `directoryPicker` · `llm` · `sessions` · `sessionProjections` · `subagents` · `sessionQuery` · `tools` · `userQuestions` · `workspaceRegistry`
```ts config-catalog
/** Gateway plugin configuration. */
@ -1162,14 +1164,14 @@ Source: [`packages/test-support/llm-replay/src/index.ts:776`](../packages/test-s
## `@deepseek-ai/dsh-llm-retry`
Requires: `agents`
Requires: `agents` · `sessionProjections`
```ts config-catalog
/** This policy executor has no config; providers own `retryPolicy`. */
export type Config = Readonly<Record<string, never>>
```
Source: [`packages/llm/llm-retry/src/index.ts:24`](../packages/llm/llm-retry/src/index.ts)
Source: [`packages/llm/llm-retry/src/index.ts:25`](../packages/llm/llm-retry/src/index.ts)
<a id="deepseek-aidsh-lsp-stdio"></a>
@ -1306,7 +1308,7 @@ Source: [`packages/feedback/message-feedback/src/index.ts:49`](../packages/feedb
## `@deepseek-ai/dsh-permission-presets`
Requires: `shell` · `approval` · `sessions`
Requires: `shell` · `approval` · `sessions` · `sessionProjections`
```ts config-catalog
/** The {@link PermissionPresetService} config: preset table and composition default. */
@ -1339,7 +1341,7 @@ export interface PresetSpec {
Depends on: [`ApprovalPolicy`](subsystems/approval.md) · [`SandboxMode`](subsystems/sandbox.md)
Source: [`packages/interaction/permission-presets/src/index.ts:156`](../packages/interaction/permission-presets/src/index.ts)
Source: [`packages/interaction/permission-presets/src/index.ts:116`](../packages/interaction/permission-presets/src/index.ts)
<a id="deepseek-aidsh-persona"></a>
@ -1369,7 +1371,7 @@ Source: [`packages/preset/persona/src/index.ts:34`](../packages/preset/persona/s
## `@deepseek-ai/dsh-plan-mode`
Requires: `tools` · `systemPrompt`
Requires: `tools` · `systemPrompt` · `sessionProjections`
```ts config-catalog
/** Deployment-owned plan guidance. */
@ -1379,7 +1381,7 @@ export interface PlanModeConfig {
}
```
Source: [`packages/plan/plan-mode/src/index.ts:70`](../packages/plan/plan-mode/src/index.ts)
Source: [`packages/plan/plan-mode/src/index.ts:65`](../packages/plan/plan-mode/src/index.ts)
<a id="deepseek-aidsh-pwsh-local"></a>
@ -1506,6 +1508,8 @@ Source: [`packages/sandbox/sandbox-local/src/index.ts:44`](../packages/sandbox/s
## `@deepseek-ai/dsh-sandbox-policy`
Requires: `sessionProjections`
```ts config-catalog
/**
* Plugin config: the deployment's sandbox default. All optional — `Config`
@ -1527,7 +1531,7 @@ export interface Config {
Depends on: [`SandboxMode`](subsystems/sandbox.md)
Source: [`packages/sandbox/sandbox-policy/src/index.ts:67`](../packages/sandbox/sandbox-policy/src/index.ts)
Source: [`packages/sandbox/sandbox-policy/src/index.ts:68`](../packages/sandbox/sandbox-policy/src/index.ts)
<a id="deepseek-aidsh-sdk-jsonrpc-server"></a>
@ -1664,7 +1668,7 @@ Source: [`packages/session/session-projection-cache/src/index.ts:42`](../package
## `@deepseek-ai/dsh-session-query-sqlite`
Requires: `sessions`
Requires: `sessions` · `sessionProjections`
```ts config-catalog
/** Combined session-query configuration backed by SQLite full-text search. */
@ -1776,7 +1780,7 @@ Source: [`packages/session/session-telemetry-otel/src/index.ts:91`](../packages/
## `@deepseek-ai/dsh-session-title`
Requires: `sessions`
Requires: `sessions` · `sessionProjections`
```ts config-catalog
/** Required deterministic fallback and accepted-title limits. */
@ -1790,7 +1794,7 @@ export interface Config {
}
```
Source: [`packages/session/session-title/src/index.ts:79`](../packages/session/session-title/src/index.ts)
Source: [`packages/session/session-title/src/index.ts:54`](../packages/session/session-title/src/index.ts)
<a id="deepseek-aidsh-session-title-all-prompts-llm"></a>
@ -2303,7 +2307,7 @@ Source: [`packages/experimental/team/src/types.ts:125`](../packages/experimental
## `@deepseek-ai/dsh-terminal-bash`
Requires: `terminals` · `sandboxPolicy` · `subprocess`
Requires: `terminals` · `sandboxPolicy` · `sessionProjections` · `subprocess`
```ts config-catalog
/** Public plugin configuration. */
@ -2348,7 +2352,7 @@ Source: [`packages/terminal/terminal-bash/src/config.ts:6`](../packages/terminal
## `@deepseek-ai/dsh-time-context`
Requires: `agents`
Requires: `agents` · `sessionProjections`
```ts config-catalog
/** Request-preparation clock formatting and append scheduling. Invalid values fail plugin load. */
@ -2360,13 +2364,13 @@ export interface Config {
}
```
Source: [`packages/context/time-context/src/index.ts:27`](../packages/context/time-context/src/index.ts)
Source: [`packages/context/time-context/src/index.ts:50`](../packages/context/time-context/src/index.ts)
<a id="deepseek-aidsh-tmux-context"></a>
## `@deepseek-ai/dsh-tmux-context`
Requires: `agents`
Requires: `agents` · `sessionProjections`
```ts config-catalog
/** Per-turn tmux-location scheduling. Invalid values fail plugin load. */
@ -2376,12 +2380,14 @@ export interface Config {
}
```
Source: [`packages/context/tmux-context/src/index.ts:34`](../packages/context/tmux-context/src/index.ts)
Source: [`packages/context/tmux-context/src/index.ts:36`](../packages/context/tmux-context/src/index.ts)
<a id="deepseek-aidsh-token-meter"></a>
## `@deepseek-ai/dsh-token-meter`
Requires: `sessionProjections`
```ts config-catalog
/** Token-meter plugin configuration; the fixed estimator has no settings. */
export type TokenMeterConfig = Record<string, never>
@ -2488,7 +2494,7 @@ Source: [`packages/fs/tool-fs-search/src/index.ts:73`](../packages/fs/tool-fs-se
## `@deepseek-ai/dsh-tool-goal`
Requires: `agents` · `goals` · `tools` · `systemPrompt`
Requires: `agents` · `goals` · `tools` · `systemPrompt` · `sessionProjections`
```ts config-catalog
/** Model policy and hard lower bounds for goal-state updates. */
@ -2596,7 +2602,7 @@ Source: [`packages/workflow/tool-ralph/src/index.ts:23`](../packages/workflow/to
## `@deepseek-ai/dsh-tool-session-query`
Requires: `tools` · `systemPrompt` · `sessionQuery`
Requires: `tools` · `systemPrompt` · `sessionQuery` · `sessionProjections`
```ts config-catalog
/** Deployment-owned search count and timeout bounds. */
@ -2614,7 +2620,7 @@ Source: [`packages/session-query/tool-session-query/src/index.ts:29`](../package
## `@deepseek-ai/dsh-tool-skill`
Requires: `agents` · `tools` · `skills`
Requires: `agents` · `tools` · `skills` · `sessionProjections`
```ts config-catalog
/** Model-facing skill catalog configuration. */
@ -2624,7 +2630,7 @@ export interface Config {
}
```
Source: [`packages/skill/tool-skill/src/index.ts:61`](../packages/skill/tool-skill/src/index.ts)
Source: [`packages/skill/tool-skill/src/index.ts:63`](../packages/skill/tool-skill/src/index.ts)
<a id="deepseek-aidsh-tool-str-replace-editor"></a>
@ -2771,7 +2777,7 @@ Source: [`packages/terminal/tool-terminal/src/index.ts:35`](../packages/terminal
## `@deepseek-ai/dsh-tool-todo`
Requires: `tools`
Requires: `tools` · `sessionProjections`
```ts config-catalog
/** Model-facing todo tool configuration. */
@ -2787,7 +2793,7 @@ export interface Config {
}
```
Source: [`packages/todo/tool-todo/src/index.ts:29`](../packages/todo/tool-todo/src/index.ts)
Source: [`packages/todo/tool-todo/src/index.ts:28`](../packages/todo/tool-todo/src/index.ts)
<a id="deepseek-aidsh-tool-web"></a>
@ -2891,6 +2897,8 @@ Source: [`packages/typert/loader/src/index.ts:47`](../packages/typert/loader/src
## `@deepseek-ai/dsh-user-approval`
Requires: `sessionProjections`
```ts config-catalog
/** Plugin config. All optional — `static Config` supplies the defaults. */
export interface Config {
@ -2916,7 +2924,7 @@ export interface Config {
export type ApprovalPolicy = 'ask' | 'never'
```
Source: [`packages/interaction/user-approval/src/index.ts:177`](../packages/interaction/user-approval/src/index.ts)
Source: [`packages/interaction/user-approval/src/index.ts:175`](../packages/interaction/user-approval/src/index.ts)
<a id="deepseek-aidsh-web"></a>
@ -3158,7 +3166,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
- `@deepseek-ai/dsh-session-stats` — requires `sessionProjections` ([`packages/session/session-stats/src/index.ts`](../packages/session/session-stats/src/index.ts))
- `@deepseek-ai/dsh-skill-badge` — requires `skills` ([`packages/skill/skill-badge/src/index.ts`](../packages/skill/skill-badge/src/index.ts))
- `@deepseek-ai/dsh-storage` ([`packages/storage/storage/src/index.ts`](../packages/storage/storage/src/index.ts))
- `@deepseek-ai/dsh-subagent` ([`packages/subagent/subagent/src/index.ts`](../packages/subagent/subagent/src/index.ts))
- `@deepseek-ai/dsh-subagent` — requires `sessionProjections` ([`packages/subagent/subagent/src/index.ts`](../packages/subagent/subagent/src/index.ts))
- `@deepseek-ai/dsh-subprocess-local` ([`packages/subprocess/subprocess-local/src/index.ts`](../packages/subprocess/subprocess-local/src/index.ts))
- `@deepseek-ai/dsh-terminal` ([`packages/terminal/terminal/src/index.ts`](../packages/terminal/terminal/src/index.ts))
- `@deepseek-ai/dsh-tool-ask-user` — requires `tools` · `userQuestions` ([`packages/interaction/tool-ask-user/src/index.ts`](../packages/interaction/tool-ask-user/src/index.ts))

View file

@ -108,6 +108,8 @@ export interface Config {
## `@deepseek-ai/dsh-agent-instructions`
需要:`sessionProjections`
```ts config-catalog
/** User-facing workspace instruction loader configuration. */
export interface Config {
@ -138,7 +140,7 @@ export interface Config {
## `@deepseek-ai/dsh-agent-loop`
需要:`agents` · `sessions` · `llm` · `tools` · `systemPrompt`
需要:`agents` · `sessions` · `llm` · `tools` · `systemPrompt` · `sessionProjections`
```ts config-catalog
/** Agent-loop plugin configuration. */
@ -164,7 +166,7 @@ export interface Config {
依赖:[`AgentOptions`](subsystems/core.md) · [`SessionId`](subsystems/core.md)
来源:[`packages/core/agent-loop/src/index.ts:255`](../packages/core/agent-loop/src/index.ts)
来源:[`packages/core/agent-loop/src/index.ts:314`](../packages/core/agent-loop/src/index.ts)
<a id="deepseek-aidsh-agent-presets"></a>
@ -294,7 +296,7 @@ export interface GoalConfig {
依赖:[`AgentLoopConfig`](#deepseek-aidsh-agent-loop) · [`GoalDomainConfig`](#deepseek-aidsh-goal) · [`InvariantConfig`](#deepseek-aidsh-invariants) · [`JobsConfig`](#deepseek-aidsh-jobs-local) · [`SessionTitleConfig`](#deepseek-aidsh-session-title) · [`SkillFileSystem`](../packages/skill/skill-filesystem/src/index.ts) · [`SkillRegistryConfig`](#deepseek-aidsh-skill) · [`SystemPromptConfig`](#deepseek-aidsh-system-prompt) · [`toolBash`](../packages/shell/tool-bash/src/index.ts) · [`toolGoal`](../packages/goal/tool-goal/src/index.ts) · [`toolJobs`](../packages/jobs/tool-jobs/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools) · [`toolSkill`](../packages/skill/tool-skill/src/index.ts) · [`workspaceContext`](../packages/context/agent-instructions/src/index.ts)
来源:[`packages/examples/agent-spine-demo/src/index.ts:92`](../packages/examples/agent-spine-demo/src/index.ts)
来源:[`packages/examples/agent-spine-demo/src/index.ts:93`](../packages/examples/agent-spine-demo/src/index.ts)
<a id="deepseek-aidsh-agent-tool-presentation"></a>
@ -420,7 +422,7 @@ export interface ConnectionConfig {
## `@deepseek-ai/dsh-client-hmr`
需要:`clientModuleHost` · `webServer`
需要:`clientModules` · `webServer`
```ts config-catalog
/** Plugin config, validated by the same-named schemastery schema. */
@ -632,7 +634,7 @@ export type Config = LocalConfig
## `@deepseek-ai/dsh-goal`
需要:`agents`
需要:`agents` · `sessionProjections`
```ts config-catalog
/** Deployment defaults for goal creation. */
@ -642,7 +644,7 @@ export interface Config {
}
```
来源:[`packages/goal/goal/src/index.ts:116`](../packages/goal/goal/src/index.ts)
来源:[`packages/goal/goal/src/index.ts:115`](../packages/goal/goal/src/index.ts)
<a id="deepseek-aidsh-headless"></a>
@ -664,7 +666,7 @@ export interface Config {
## `@deepseek-ai/dsh-hooks-claude-code`
需要:`bash`
需要:`shell` · `sessionProjections`
```ts config-catalog
/** Plugin config: where the CC hook config lives + substitution roots. */
@ -696,13 +698,13 @@ export interface Config {
}
```
来源:[`packages/hooks/hooks-claude-code/src/index.ts:45`](../packages/hooks/hooks-claude-code/src/index.ts)
来源:[`packages/hooks/hooks-claude-code/src/index.ts:46`](../packages/hooks/hooks-claude-code/src/index.ts)
<a id="deepseek-aidsh-hooks-codex"></a>
## `@deepseek-ai/dsh-hooks-codex`
需要:`bash`
需要:`shell` · `sessionProjections`
```ts config-catalog
/** Plugin config: where the Codex hooks.json lives + the model name for payloads. */
@ -723,13 +725,13 @@ export interface Config {
}
```
来源:[`packages/hooks/hooks-codex/src/index.ts:44`](../packages/hooks/hooks-codex/src/index.ts)
来源:[`packages/hooks/hooks-codex/src/index.ts:45`](../packages/hooks/hooks-codex/src/index.ts)
<a id="deepseek-aidsh-host-apiproxy"></a>
## `@deepseek-ai/dsh-host-apiproxy`
需要:`agentDefaultModel` · `agents` · `attachments` · `directoryPicker` · `llm` · `sessions` · `subagents` · `sessionQuery` · `tools` · `userInteraction` · `workspace`
需要:`agentDefaultModel` · `agents` · `attachments` · `directoryPicker` · `llm` · `sessions` · `sessionProjections` · `subagents` · `sessionQuery` · `tools` · `userQuestions` · `workspaceRegistry`
```ts config-catalog
/** Gateway plugin configuration. */
@ -1164,14 +1166,14 @@ export interface ReplayModelConfig {
## `@deepseek-ai/dsh-llm-retry`
需要:`agents`
需要:`agents` · `sessionProjections`
```ts config-catalog
/** This policy executor has no config; providers own `retryPolicy`. */
export type Config = Readonly<Record<string, never>>
```
来源:[`packages/llm/llm-retry/src/index.ts:24`](../packages/llm/llm-retry/src/index.ts)
来源:[`packages/llm/llm-retry/src/index.ts:25`](../packages/llm/llm-retry/src/index.ts)
<a id="deepseek-aidsh-lsp-stdio"></a>
@ -1308,7 +1310,7 @@ export interface Config {
## `@deepseek-ai/dsh-permission-presets`
需要:`bash` · `approval` · `sessions`
需要:`shell` · `approval` · `sessions` · `sessionProjections`
```ts config-catalog
/** The {@link PermissionPresetService} config: preset table and composition default. */
@ -1341,7 +1343,7 @@ export interface PresetSpec {
依赖:[`ApprovalPolicy`](subsystems/approval.md) · [`SandboxMode`](subsystems/sandbox.md)
来源:[`packages/interaction/permission-presets/src/index.ts:156`](../packages/interaction/permission-presets/src/index.ts)
来源:[`packages/interaction/permission-presets/src/index.ts:116`](../packages/interaction/permission-presets/src/index.ts)
<a id="deepseek-aidsh-persona"></a>
@ -1371,7 +1373,7 @@ export interface Config {
## `@deepseek-ai/dsh-plan-mode`
需要:`tools` · `systemPrompt`
需要:`tools` · `systemPrompt` · `sessionProjections`
```ts config-catalog
/** Deployment-owned plan guidance. */
@ -1381,7 +1383,7 @@ export interface PlanModeConfig {
}
```
来源:[`packages/plan/plan-mode/src/index.ts:70`](../packages/plan/plan-mode/src/index.ts)
来源:[`packages/plan/plan-mode/src/index.ts:65`](../packages/plan/plan-mode/src/index.ts)
<a id="deepseek-aidsh-pwsh-local"></a>
@ -1508,6 +1510,8 @@ export interface Config {
## `@deepseek-ai/dsh-sandbox-policy`
需要:`sessionProjections`
```ts config-catalog
/**
* Plugin config: the deployment's sandbox default. All optional — `Config`
@ -1529,7 +1533,7 @@ export interface Config {
依赖:[`SandboxMode`](subsystems/sandbox.md)
来源:[`packages/sandbox/sandbox-policy/src/index.ts:67`](../packages/sandbox/sandbox-policy/src/index.ts)
来源:[`packages/sandbox/sandbox-policy/src/index.ts:68`](../packages/sandbox/sandbox-policy/src/index.ts)
<a id="deepseek-aidsh-sdk-jsonrpc-server"></a>
@ -1553,7 +1557,7 @@ export interface JsonRpcConfig {
依赖:`Readable`(`node:stream`)· `Writable`(`node:stream`)
来源:[`packages/sdk/server/src/index.ts:29`](../packages/sdk/server/src/index.ts)
来源:[`packages/sdk/server/src/index.ts:25`](../packages/sdk/server/src/index.ts)
<a id="deepseek-aidsh-session-persistence-jsonl"></a>
@ -1666,7 +1670,7 @@ export interface Config {
## `@deepseek-ai/dsh-session-query-sqlite`
需要:`sessions`
需要:`sessions` · `sessionProjections`
```ts config-catalog
/** Combined session-query configuration backed by SQLite full-text search. */
@ -1778,7 +1782,7 @@ export enum SessionTelemetryMode {
## `@deepseek-ai/dsh-session-title`
需要:`sessions`
需要:`sessions` · `sessionProjections`
```ts config-catalog
/** Required deterministic fallback and accepted-title limits. */
@ -1792,7 +1796,7 @@ export interface Config {
}
```
来源:[`packages/session/session-title/src/index.ts:79`](../packages/session/session-title/src/index.ts)
来源:[`packages/session/session-title/src/index.ts:54`](../packages/session/session-title/src/index.ts)
<a id="deepseek-aidsh-session-title-all-prompts-llm"></a>
@ -2306,7 +2310,7 @@ export interface Config {
## `@deepseek-ai/dsh-terminal-bash`
需要:`pty` · `sandboxPolicy` · `subprocess`
需要:`terminals` · `sandboxPolicy` · `sessionProjections` · `subprocess`
```ts config-catalog
/** Public plugin configuration. */
@ -2351,7 +2355,7 @@ export interface Config {
## `@deepseek-ai/dsh-time-context`
需要:`agents`
需要:`agents` · `sessionProjections`
```ts config-catalog
/** Request-preparation clock formatting and append scheduling. Invalid values fail plugin load. */
@ -2363,13 +2367,13 @@ export interface Config {
}
```
来源:[`packages/context/time-context/src/index.ts:27`](../packages/context/time-context/src/index.ts)
来源:[`packages/context/time-context/src/index.ts:50`](../packages/context/time-context/src/index.ts)
<a id="deepseek-aidsh-tmux-context"></a>
## `@deepseek-ai/dsh-tmux-context`
需要:`agents`
需要:`agents` · `sessionProjections`
```ts config-catalog
/** Per-turn tmux-location scheduling. Invalid values fail plugin load. */
@ -2379,12 +2383,14 @@ export interface Config {
}
```
来源:[`packages/context/tmux-context/src/index.ts:34`](../packages/context/tmux-context/src/index.ts)
来源:[`packages/context/tmux-context/src/index.ts:36`](../packages/context/tmux-context/src/index.ts)
<a id="deepseek-aidsh-token-meter"></a>
## `@deepseek-ai/dsh-token-meter`
需要:`sessionProjections`
```ts config-catalog
/** Token-meter plugin configuration; the fixed estimator has no settings. */
export type TokenMeterConfig = Record<string, never>
@ -2396,7 +2402,7 @@ export type TokenMeterConfig = Record<string, never>
## `@deepseek-ai/dsh-tool-bash`
需要:`tools` · `bash` · `systemPrompt` · `bashEnv`
需要:`tools` · `shell` · `systemPrompt` · `shellEnv`
```ts config-catalog
/** Configuration for the bash tool. */
@ -2412,7 +2418,7 @@ export interface Config {
## `@deepseek-ai/dsh-tool-bash-persistent`
需要:`tools` · `pty`
需要:`tools` · `terminals`
```ts config-catalog
/** Configuration for the persistent Bash tool. */
@ -2491,7 +2497,7 @@ export interface Config {
## `@deepseek-ai/dsh-tool-goal`
需要:`agents` · `goals` · `tools` · `systemPrompt`
需要:`agents` · `goals` · `tools` · `systemPrompt` · `sessionProjections`
```ts config-catalog
/** Model policy and hard lower bounds for goal-state updates. */
@ -2507,7 +2513,7 @@ export interface Config {
## `@deepseek-ai/dsh-tool-jobs`
需要:`tools` · `tasks` · `systemPrompt`
需要:`tools` · `jobs` · `systemPrompt`
```ts config-catalog
/** Configures bounded `job_output` waits and completion-notice delivery. */
@ -2561,7 +2567,7 @@ export interface Config {
## `@deepseek-ai/dsh-tool-pwsh`
需要:`tools` · `bash` · `systemPrompt` · `bashEnv`
需要:`tools` · `shell` · `systemPrompt` · `shellEnv`
```ts config-catalog
/** Configuration for the pwsh tool. */
@ -2577,7 +2583,7 @@ export interface Config {
## `@deepseek-ai/dsh-tool-ralph`
需要:`tools` · `workflows` · `subagents` · `systemPrompt`
需要:`tools` · `workflowEngine` · `subagents` · `systemPrompt`
```ts config-catalog
/** Deployment policy for the fixed Ralph workflow. */
@ -2599,7 +2605,7 @@ export interface Config {
## `@deepseek-ai/dsh-tool-session-query`
需要:`tools` · `systemPrompt` · `sessionQuery`
需要:`tools` · `systemPrompt` · `sessionQuery` · `sessionProjections`
```ts config-catalog
/** Deployment-owned search count and timeout bounds. */
@ -2617,7 +2623,7 @@ export interface Config {
## `@deepseek-ai/dsh-tool-skill`
需要:`agents` · `tools` · `skills`
需要:`agents` · `tools` · `skills` · `sessionProjections`
```ts config-catalog
/** Model-facing skill catalog configuration. */
@ -2627,7 +2633,7 @@ export interface Config {
}
```
来源:[`packages/skill/tool-skill/src/index.ts:61`](../packages/skill/tool-skill/src/index.ts)
来源:[`packages/skill/tool-skill/src/index.ts:63`](../packages/skill/tool-skill/src/index.ts)
<a id="deepseek-aidsh-tool-str-replace-editor"></a>
@ -2757,7 +2763,7 @@ export interface Config {
## `@deepseek-ai/dsh-tool-terminal`
需要:`pty` · `tools` · `systemPrompt`
需要:`terminals` · `tools` · `systemPrompt`
```ts config-catalog
/** Model-facing terminal tool configuration. */
@ -2775,7 +2781,7 @@ export interface Config {
## `@deepseek-ai/dsh-tool-todo`
需要:`tools`
需要:`tools` · `sessionProjections`
```ts config-catalog
/** Model-facing todo tool configuration. */
@ -2791,7 +2797,7 @@ export interface Config {
}
```
来源:[`packages/todo/tool-todo/src/index.ts:29`](../packages/todo/tool-todo/src/index.ts)
来源:[`packages/todo/tool-todo/src/index.ts:28`](../packages/todo/tool-todo/src/index.ts)
<a id="deepseek-aidsh-tool-web"></a>
@ -2825,7 +2831,7 @@ export interface Config {
## `@deepseek-ai/dsh-tool-workflow`
需要:`tools` · `workflows` · `systemPrompt`
需要:`tools` · `workflowEngine` · `systemPrompt`
```ts config-catalog
/** Config: the model-facing tool name plus result rendering caps. */
@ -2895,6 +2901,8 @@ export interface Config {
## `@deepseek-ai/dsh-user-approval`
需要:`sessionProjections`
```ts config-catalog
/** Plugin config. All optional — `static Config` supplies the defaults. */
export interface Config {
@ -2920,7 +2928,7 @@ export interface Config {
export type ApprovalPolicy = 'ask' | 'never'
```
来源:[`packages/interaction/user-approval/src/index.ts:177`](../packages/interaction/user-approval/src/index.ts)
来源:[`packages/interaction/user-approval/src/index.ts:175`](../packages/interaction/user-approval/src/index.ts)
<a id="deepseek-aidsh-web"></a>

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/event-producer-consumer.md
event-producer-consumer.md: c906ee6329fac66e7391c266213dd150dd5b8e09
event-producer-consumer.zh.md: 77bf401b7215bd263c0d84f04e0eabe6b28b7915
event-producer-consumer.md: 699c84c9f84c486bdb090aec07cca63349e67f75
event-producer-consumer.zh.md: 977d1909ec0370e81e90344ae0acef17dea5d842

View file

@ -7,7 +7,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| Event | Mode | Declared in | Dispatchers | Listeners |
| --- | --- | --- | --- | --- |
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:183`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - |
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:242`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - |
| `agent-preset/selected` | `emit` | [`packages/preset/agent-presets/src/types.ts:13`](../packages/preset/agent-presets/src/types.ts) | [`agent-presets`](../packages/preset/agent-presets) (`emit`) | `apiproxy` |
| `agent/created` | `emit` | [`packages/core/agent/src/runtime-types.ts:159`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-presets`](../packages/preset/agent-presets), [`goal-round-driver`](../packages/goal/goal-round-driver), [`schedule`](../packages/schedule/schedule), [`tool-team`](../packages/experimental/tool-team) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:168`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-team`](../packages/experimental/tool-team) |
@ -21,7 +21,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| `agent/session-start` | `emit` | [`packages/core/agent/src/runtime-types.ts:217`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emitAgentEvent`) | [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`team`](../packages/experimental/team) |
| `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:178`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`agent`](../packages/core/agent), `apiproxy`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`schedule`](../packages/schedule/schedule), `server`, [`team`](../packages/experimental/team) |
| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:278`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `approval/request` | `waterfall` | [`packages/interaction/user-approval/src/index.ts:30`](../packages/interaction/user-approval/src/index.ts) | [`user-approval`](../packages/interaction/user-approval) (`waterfall`) | [`acp`](../packages/acp/acp), `apiproxy` |
| `approval/request` | `waterfall` | [`packages/interaction/user-approval/src/index.ts:32`](../packages/interaction/user-approval/src/index.ts) | [`user-approval`](../packages/interaction/user-approval) (`waterfall`) | [`acp`](../packages/acp/acp), `apiproxy` |
| `commands/change` | `emit` | [`packages/interaction/commands/src/types.ts:80`](../packages/interaction/commands/src/types.ts) | [`commands`](../packages/interaction/commands) (`events.dispatch`) | `apiproxy` |
| `cordis/dynamic-package` | `emit` | [`packages/extensions/cordis-host-runner/src/types.ts:379`](../packages/extensions/cordis-host-runner/src/types.ts) | [`cordis-host-runner`](../packages/extensions/cordis-host-runner) (`emit`) | `apiproxy` |
| `cordis/dynamic-retract` | `emit` | [`packages/extensions/cordis-host-runner/src/types.ts:385`](../packages/extensions/cordis-host-runner/src/types.ts) | [`cordis-host-runner`](../packages/extensions/cordis-host-runner) (`emit`) | `apiproxy` |

View file

@ -9,7 +9,7 @@
| 事件 | 模式 | 声明位置 | 派发方 | 监听方 |
| --- | --- | --- | --- | --- |
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:183`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - |
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:242`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - |
| `agent-preset/selected` | `emit` | [`packages/preset/agent-presets/src/types.ts:13`](../packages/preset/agent-presets/src/types.ts) | [`agent-presets`](../packages/preset/agent-presets) (`emit`) | `apiproxy` |
| `agent/created` | `emit` | [`packages/core/agent/src/runtime-types.ts:159`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-presets`](../packages/preset/agent-presets), [`goal-round-driver`](../packages/goal/goal-round-driver), [`schedule`](../packages/schedule/schedule), [`tool-team`](../packages/experimental/tool-team) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:168`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-team`](../packages/experimental/tool-team) |
@ -23,7 +23,7 @@
| `agent/session-start` | `emit` | [`packages/core/agent/src/runtime-types.ts:217`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emitAgentEvent`) | [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`team`](../packages/experimental/team) |
| `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:178`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`agent`](../packages/core/agent), `apiproxy`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`schedule`](../packages/schedule/schedule), `server`, [`team`](../packages/experimental/team) |
| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:278`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `approval/request` | `waterfall` | [`packages/interaction/user-approval/src/index.ts:30`](../packages/interaction/user-approval/src/index.ts) | [`user-approval`](../packages/interaction/user-approval) (`waterfall`) | [`acp`](../packages/acp/acp), `apiproxy` |
| `approval/request` | `waterfall` | [`packages/interaction/user-approval/src/index.ts:32`](../packages/interaction/user-approval/src/index.ts) | [`user-approval`](../packages/interaction/user-approval) (`waterfall`) | [`acp`](../packages/acp/acp), `apiproxy` |
| `commands/change` | `emit` | [`packages/interaction/commands/src/types.ts:80`](../packages/interaction/commands/src/types.ts) | [`commands`](../packages/interaction/commands) (`events.dispatch`) | `apiproxy` |
| `cordis/dynamic-package` | `emit` | [`packages/extensions/cordis-host-runner/src/types.ts:379`](../packages/extensions/cordis-host-runner/src/types.ts) | [`cordis-host-runner`](../packages/extensions/cordis-host-runner) (`emit`) | `apiproxy` |
| `cordis/dynamic-retract` | `emit` | [`packages/extensions/cordis-host-runner/src/types.ts:385`](../packages/extensions/cordis-host-runner/src/types.ts) | [`cordis-host-runner`](../packages/extensions/cordis-host-runner) (`emit`) | `apiproxy` |

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/module-graph.md
module-graph.md: 0bd5f80534ba65e0d483bd04228cdac321e082bc
module-graph.zh.md: 6239520b7d819eb14c2b859afa44a0c987545200
module-graph.md: 8f9be27dbc41d9caf88f54ce9003dee598da8958
module-graph.zh.md: 1054c845b4ecfb3cadda57572f09ead8ad4563fd

View file

@ -436,12 +436,6 @@ flowchart TD
pkg_lsp --> pkg_brand
pkg_lsp --> pkg_invariants
pkg_lsp --> pkg_llm
pkg_agent --> pkg_invariants
pkg_agent --> pkg_llm
pkg_agent --> pkg_scope
pkg_agent --> pkg_session
pkg_agent --> pkg_system_prompt
pkg_agent --> pkg_typert_protocol
pkg_skill_badge --> pkg_invariants
pkg_skill_badge --> pkg_skill
pkg_web_fetch_http --> pkg_invariants
@ -478,40 +472,19 @@ flowchart TD
pkg_session_projection --> pkg_session
pkg_acp_snapshot --> pkg_invariants
pkg_acp_snapshot --> pkg_session
pkg_llm_retry --> pkg_agent
pkg_llm_retry --> pkg_brand
pkg_llm_retry --> pkg_invariants
pkg_llm_retry --> pkg_llm
pkg_llm_retry --> pkg_session
pkg_llm_retry --> pkg_timeout
pkg_agent_default_model --> pkg_agent
pkg_agent_default_model --> pkg_invariants
pkg_agent_default_model --> pkg_llm
pkg_agent_default_model --> pkg_settings
pkg_goal --> pkg_agent
pkg_goal --> pkg_brand
pkg_goal --> pkg_invariants
pkg_goal --> pkg_llm
pkg_goal --> pkg_scope
pkg_goal --> pkg_session
pkg_goal --> pkg_session_projection
pkg_goal --> pkg_typert_protocol
pkg_agent --> pkg_invariants
pkg_agent --> pkg_llm
pkg_agent --> pkg_scope
pkg_agent --> pkg_session
pkg_agent --> pkg_session_projection
pkg_agent --> pkg_system_prompt
pkg_agent --> pkg_typert_protocol
pkg_fs --> pkg_brand
pkg_fs --> pkg_invariants
pkg_fs --> pkg_llm
pkg_fs --> pkg_sandbox
pkg_web_search_deepseek --> pkg_agent
pkg_web_search_deepseek --> pkg_credentials
pkg_web_search_deepseek --> pkg_invariants
pkg_web_search_deepseek --> pkg_launch_environment
pkg_web_search_deepseek --> pkg_session
pkg_web_search_deepseek --> pkg_settings
pkg_web_search_deepseek --> pkg_web
pkg_spill_local --> pkg_invariants
pkg_spill_local --> pkg_spill
pkg_time_context --> pkg_agent
pkg_time_context --> pkg_invariants
pkg_time_context --> pkg_session
pkg_message_feedback --> pkg_brand
pkg_message_feedback --> pkg_invariants
pkg_message_feedback --> pkg_llm
@ -519,45 +492,10 @@ flowchart TD
pkg_message_feedback --> pkg_session_persistence
pkg_message_feedback --> pkg_storage_domain
pkg_message_feedback --> pkg_typert_protocol
pkg_commands --> pkg_agent
pkg_commands --> pkg_attachment
pkg_commands --> pkg_brand
pkg_commands --> pkg_invariants
pkg_commands --> pkg_llm
pkg_commands --> pkg_scope
pkg_commands --> pkg_session
pkg_commands --> pkg_typert_protocol
pkg_user_approval --> pkg_agent
pkg_user_approval --> pkg_brand
pkg_user_approval --> pkg_invariants
pkg_user_approval --> pkg_llm
pkg_user_approval --> pkg_scope
pkg_user_approval --> pkg_session
pkg_user_approval --> pkg_system_prompt
pkg_user_questions --> pkg_agent
pkg_user_questions --> pkg_invariants
pkg_user_questions --> pkg_llm
pkg_jobs --> pkg_agent
pkg_jobs --> pkg_brand
pkg_jobs --> pkg_invariants
pkg_jobs --> pkg_session
pkg_agent_presets --> pkg_agent
pkg_agent_presets --> pkg_atomic_write
pkg_agent_presets --> pkg_home_paths
pkg_agent_presets --> pkg_invariants
pkg_agent_presets --> pkg_scope
pkg_agent_presets --> pkg_session
pkg_agent_presets --> pkg_settings
pkg_agent_presets --> pkg_system_prompt
pkg_sandbox_local --> pkg_invariants
pkg_sandbox_local --> pkg_llm
pkg_sandbox_local --> pkg_sandbox
pkg_sandbox_local --> pkg_session
pkg_sandbox_policy --> pkg_agent
pkg_sandbox_policy --> pkg_invariants
pkg_sandbox_policy --> pkg_sandbox
pkg_sandbox_policy --> pkg_session
pkg_sandbox_policy --> pkg_system_prompt
pkg_session_persistence_jsonl --> pkg_invariants
pkg_session_persistence_jsonl --> pkg_session
pkg_session_persistence_jsonl --> pkg_session_persistence
@ -573,18 +511,128 @@ flowchart TD
pkg_session_stats --> pkg_llm
pkg_session_stats --> pkg_session
pkg_session_stats --> pkg_session_projection
pkg_shell --> pkg_invariants
pkg_shell --> pkg_sandbox
pkg_shell --> pkg_settings
pkg_shell --> pkg_subprocess
pkg_workspace --> pkg_brand
pkg_workspace --> pkg_invariants
pkg_workspace --> pkg_session
pkg_workspace --> pkg_session_persistence
pkg_workspace --> pkg_storage
pkg_workspace --> pkg_storage_domain
pkg_llm_retry --> pkg_agent
pkg_llm_retry --> pkg_brand
pkg_llm_retry --> pkg_invariants
pkg_llm_retry --> pkg_llm
pkg_llm_retry --> pkg_session
pkg_llm_retry --> pkg_session_projection
pkg_llm_retry --> pkg_timeout
pkg_agent_default_model --> pkg_agent
pkg_agent_default_model --> pkg_invariants
pkg_agent_default_model --> pkg_llm
pkg_agent_default_model --> pkg_settings
pkg_goal --> pkg_agent
pkg_goal --> pkg_brand
pkg_goal --> pkg_invariants
pkg_goal --> pkg_llm
pkg_goal --> pkg_scope
pkg_goal --> pkg_session
pkg_goal --> pkg_session_projection
pkg_goal --> pkg_typert_protocol
pkg_fs_local --> pkg_fs
pkg_fs_local --> pkg_invariants
pkg_fs_observation_policy --> pkg_fs
pkg_fs_observation_policy --> pkg_invariants
pkg_skill_filesystem --> pkg_fs
pkg_skill_filesystem --> pkg_home_paths
pkg_skill_filesystem --> pkg_invariants
pkg_skill_filesystem --> pkg_skill
pkg_web_search_deepseek --> pkg_agent
pkg_web_search_deepseek --> pkg_credentials
pkg_web_search_deepseek --> pkg_invariants
pkg_web_search_deepseek --> pkg_launch_environment
pkg_web_search_deepseek --> pkg_session
pkg_web_search_deepseek --> pkg_settings
pkg_web_search_deepseek --> pkg_web
pkg_hook_protocol --> pkg_invariants
pkg_hook_protocol --> pkg_session
pkg_hook_protocol --> pkg_shell
pkg_time_context --> pkg_agent
pkg_time_context --> pkg_invariants
pkg_time_context --> pkg_session
pkg_time_context --> pkg_session_projection
pkg_tmux_context --> pkg_agent
pkg_tmux_context --> pkg_invariants
pkg_tmux_context --> pkg_session
pkg_tmux_context --> pkg_session_projection
pkg_tmux_context --> pkg_shell
pkg_fs_e2b --> pkg_e2b
pkg_fs_e2b --> pkg_fs
pkg_fs_e2b --> pkg_invariants
pkg_commands --> pkg_agent
pkg_commands --> pkg_attachment
pkg_commands --> pkg_brand
pkg_commands --> pkg_invariants
pkg_commands --> pkg_llm
pkg_commands --> pkg_scope
pkg_commands --> pkg_session
pkg_commands --> pkg_typert_protocol
pkg_user_approval --> pkg_agent
pkg_user_approval --> pkg_brand
pkg_user_approval --> pkg_invariants
pkg_user_approval --> pkg_llm
pkg_user_approval --> pkg_scope
pkg_user_approval --> pkg_session
pkg_user_approval --> pkg_session_projection
pkg_user_approval --> pkg_system_prompt
pkg_user_questions --> pkg_agent
pkg_user_questions --> pkg_invariants
pkg_user_questions --> pkg_llm
pkg_jobs --> pkg_agent
pkg_jobs --> pkg_brand
pkg_jobs --> pkg_invariants
pkg_jobs --> pkg_session
pkg_lsp_stdio --> pkg_brand
pkg_lsp_stdio --> pkg_fs
pkg_lsp_stdio --> pkg_invariants
pkg_lsp_stdio --> pkg_llm
pkg_lsp_stdio --> pkg_lsp
pkg_lsp_stdio --> pkg_subprocess
pkg_lsp_stdio --> pkg_timeout
pkg_agent_presets --> pkg_agent
pkg_agent_presets --> pkg_atomic_write
pkg_agent_presets --> pkg_home_paths
pkg_agent_presets --> pkg_invariants
pkg_agent_presets --> pkg_scope
pkg_agent_presets --> pkg_session
pkg_agent_presets --> pkg_settings
pkg_agent_presets --> pkg_system_prompt
pkg_sandbox_policy --> pkg_agent
pkg_sandbox_policy --> pkg_invariants
pkg_sandbox_policy --> pkg_sandbox
pkg_sandbox_policy --> pkg_session
pkg_sandbox_policy --> pkg_session_projection
pkg_sandbox_policy --> pkg_system_prompt
pkg_session_telemetry --> pkg_agent
pkg_session_telemetry --> pkg_invariants
pkg_session_telemetry --> pkg_session
pkg_session_title --> pkg_agent
pkg_session_title --> pkg_brand
pkg_session_title --> pkg_invariants
pkg_session_title --> pkg_llm
pkg_session_title --> pkg_session
pkg_session_title --> pkg_session_projection
pkg_shell --> pkg_invariants
pkg_shell --> pkg_sandbox
pkg_shell --> pkg_settings
pkg_shell --> pkg_subprocess
pkg_bash_local --> pkg_invariants
pkg_bash_local --> pkg_settings
pkg_bash_local --> pkg_shell
pkg_bash_local --> pkg_subprocess
pkg_bash_local --> pkg_timeout
pkg_pwsh_local --> pkg_invariants
pkg_pwsh_local --> pkg_settings
pkg_pwsh_local --> pkg_shell
pkg_pwsh_local --> pkg_subprocess
pkg_pwsh_local --> pkg_timeout
pkg_terminal --> pkg_agent
pkg_terminal --> pkg_brand
pkg_terminal --> pkg_invariants
@ -597,12 +645,6 @@ flowchart TD
pkg_workflow --> pkg_invariants
pkg_workflow --> pkg_llm
pkg_workflow --> pkg_session
pkg_workspace --> pkg_brand
pkg_workspace --> pkg_invariants
pkg_workspace --> pkg_session
pkg_workspace --> pkg_session_persistence
pkg_workspace --> pkg_storage
pkg_workspace --> pkg_storage_domain
pkg_tools --> pkg_agent
pkg_tools --> pkg_code_runtime
pkg_tools --> pkg_invariants
@ -620,17 +662,11 @@ flowchart TD
pkg_goal_round_driver --> pkg_invariants
pkg_goal_round_driver --> pkg_llm
pkg_goal_round_driver --> pkg_session
pkg_fs_local --> pkg_fs
pkg_fs_local --> pkg_invariants
pkg_fs_observation_policy --> pkg_fs
pkg_fs_observation_policy --> pkg_invariants
pkg_skill_filesystem --> pkg_fs
pkg_skill_filesystem --> pkg_home_paths
pkg_skill_filesystem --> pkg_invariants
pkg_skill_filesystem --> pkg_skill
pkg_hook_protocol --> pkg_invariants
pkg_hook_protocol --> pkg_session
pkg_hook_protocol --> pkg_shell
pkg_fs_sandbox --> pkg_fs
pkg_fs_sandbox --> pkg_fs_local
pkg_fs_sandbox --> pkg_invariants
pkg_fs_sandbox --> pkg_sandbox
pkg_fs_sandbox --> pkg_sandbox_policy
pkg_session_query --> pkg_brand
pkg_session_query --> pkg_invariants
pkg_session_query --> pkg_llm
@ -653,13 +689,6 @@ flowchart TD
pkg_compaction --> pkg_invariants
pkg_compaction --> pkg_llm
pkg_compaction --> pkg_session
pkg_tmux_context --> pkg_agent
pkg_tmux_context --> pkg_invariants
pkg_tmux_context --> pkg_session
pkg_tmux_context --> pkg_shell
pkg_fs_e2b --> pkg_e2b
pkg_fs_e2b --> pkg_fs
pkg_fs_e2b --> pkg_invariants
pkg_command_feedback --> pkg_anonymous_user_id
pkg_command_feedback --> pkg_commands
pkg_command_feedback --> pkg_invariants
@ -679,33 +708,27 @@ flowchart TD
pkg_jobs_local --> pkg_jobs
pkg_jobs_local --> pkg_scope
pkg_jobs_local --> pkg_timeout
pkg_lsp_stdio --> pkg_brand
pkg_lsp_stdio --> pkg_fs
pkg_lsp_stdio --> pkg_invariants
pkg_lsp_stdio --> pkg_llm
pkg_lsp_stdio --> pkg_lsp
pkg_lsp_stdio --> pkg_subprocess
pkg_lsp_stdio --> pkg_timeout
pkg_session_title_llm --> pkg_invariants
pkg_session_title_llm --> pkg_llm
pkg_session_title_llm --> pkg_session
pkg_session_title_llm --> pkg_session_title
pkg_session_title_llm --> pkg_timeout
pkg_bash_local --> pkg_invariants
pkg_bash_local --> pkg_settings
pkg_bash_local --> pkg_shell
pkg_bash_local --> pkg_subprocess
pkg_bash_local --> pkg_timeout
pkg_pwsh_local --> pkg_invariants
pkg_pwsh_local --> pkg_settings
pkg_pwsh_local --> pkg_shell
pkg_pwsh_local --> pkg_subprocess
pkg_pwsh_local --> pkg_timeout
pkg_bash_sandbox --> pkg_bash_local
pkg_bash_sandbox --> pkg_invariants
pkg_bash_sandbox --> pkg_sandbox
pkg_bash_sandbox --> pkg_sandbox_policy
pkg_bash_sandbox --> pkg_shell
pkg_pwsh_sandbox --> pkg_invariants
pkg_pwsh_sandbox --> pkg_pwsh_local
pkg_pwsh_sandbox --> pkg_sandbox
pkg_pwsh_sandbox --> pkg_sandbox_policy
pkg_pwsh_sandbox --> pkg_shell
pkg_terminal_bash --> pkg_agent
pkg_terminal_bash --> pkg_invariants
pkg_terminal_bash --> pkg_sandbox
pkg_terminal_bash --> pkg_sandbox_policy
pkg_terminal_bash --> pkg_session
pkg_terminal_bash --> pkg_session_projection
pkg_terminal_bash --> pkg_subprocess
pkg_terminal_bash --> pkg_terminal
pkg_token_meter --> pkg_compaction
@ -719,6 +742,7 @@ flowchart TD
pkg_agent_loop --> pkg_scope
pkg_agent_loop --> pkg_session
pkg_agent_loop --> pkg_session_persistence
pkg_agent_loop --> pkg_session_projection
pkg_agent_loop --> pkg_settings
pkg_agent_loop --> pkg_system_prompt
pkg_agent_loop --> pkg_tools
@ -729,13 +753,9 @@ flowchart TD
pkg_tool_goal --> pkg_invariants
pkg_tool_goal --> pkg_llm
pkg_tool_goal --> pkg_session
pkg_tool_goal --> pkg_session_projection
pkg_tool_goal --> pkg_system_prompt
pkg_tool_goal --> pkg_tools
pkg_fs_sandbox --> pkg_fs
pkg_fs_sandbox --> pkg_fs_local
pkg_fs_sandbox --> pkg_invariants
pkg_fs_sandbox --> pkg_sandbox
pkg_fs_sandbox --> pkg_sandbox_policy
pkg_tool_fs --> pkg_attachment
pkg_tool_fs --> pkg_fs
pkg_tool_fs --> pkg_invariants
@ -763,6 +783,7 @@ flowchart TD
pkg_tool_skill --> pkg_agent
pkg_tool_skill --> pkg_invariants
pkg_tool_skill --> pkg_llm
pkg_tool_skill --> pkg_session_projection
pkg_tool_skill --> pkg_skill
pkg_tool_skill --> pkg_tools
pkg_subagent --> pkg_agent
@ -811,14 +832,17 @@ flowchart TD
pkg_hooks_codex --> pkg_llm
pkg_hooks_codex --> pkg_session
pkg_hooks_codex --> pkg_session_persistence
pkg_hooks_codex --> pkg_session_projection
pkg_hooks_codex --> pkg_tools
pkg_session_query_sqlite --> pkg_invariants
pkg_session_query_sqlite --> pkg_session
pkg_session_query_sqlite --> pkg_session_persistence
pkg_session_query_sqlite --> pkg_session_query
pkg_tool_session_query --> pkg_agent
pkg_tool_session_query --> pkg_invariants
pkg_tool_session_query --> pkg_llm
pkg_tool_session_query --> pkg_session
pkg_tool_session_query --> pkg_session_projection
pkg_tool_session_query --> pkg_session_query
pkg_tool_session_query --> pkg_system_prompt
pkg_tool_session_query --> pkg_timeout
@ -832,6 +856,7 @@ flowchart TD
pkg_agent_instructions --> pkg_invariants
pkg_agent_instructions --> pkg_llm
pkg_agent_instructions --> pkg_session
pkg_agent_instructions --> pkg_session_projection
pkg_agent_instructions --> pkg_tools
pkg_session_reference --> pkg_agent
pkg_session_reference --> pkg_compaction
@ -907,16 +932,6 @@ flowchart TD
pkg_session_title_first_prompt_llm --> pkg_session
pkg_session_title_first_prompt_llm --> pkg_session_title
pkg_session_title_first_prompt_llm --> pkg_session_title_llm
pkg_bash_sandbox --> pkg_bash_local
pkg_bash_sandbox --> pkg_invariants
pkg_bash_sandbox --> pkg_sandbox
pkg_bash_sandbox --> pkg_sandbox_policy
pkg_bash_sandbox --> pkg_shell
pkg_pwsh_sandbox --> pkg_invariants
pkg_pwsh_sandbox --> pkg_pwsh_local
pkg_pwsh_sandbox --> pkg_sandbox
pkg_pwsh_sandbox --> pkg_sandbox_policy
pkg_pwsh_sandbox --> pkg_shell
pkg_shell_env --> pkg_home_paths
pkg_shell_env --> pkg_invariants
pkg_shell_env --> pkg_session_persistence
@ -1001,6 +1016,7 @@ flowchart TD
pkg_hooks_claude_code --> pkg_llm
pkg_hooks_claude_code --> pkg_session
pkg_hooks_claude_code --> pkg_session_persistence
pkg_hooks_claude_code --> pkg_session_projection
pkg_hooks_claude_code --> pkg_subagent
pkg_hooks_claude_code --> pkg_tools
pkg_web_app --> pkg_invariants
@ -1026,6 +1042,7 @@ flowchart TD
pkg_tool_cordis --> pkg_session
pkg_tool_cordis --> pkg_system_prompt
pkg_tool_cordis --> pkg_tools
pkg_host_apiproxy --> pkg_agent_loop
pkg_host_apiproxy --> pkg_agent_presets
pkg_host_apiproxy --> pkg_cordis_host_runner
pkg_host_apiproxy --> pkg_invariants
@ -1470,7 +1487,6 @@ flowchart TD
| [`skill`](../packages/skill/skill) | `skill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
| [`web`](../packages/web/web) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`lsp`](../packages/lsp/lsp) | `lsp` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`agent`](../packages/core/agent) | `core` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`typert-protocol`](../packages/typert/protocol) |
| [`skill-badge`](../packages/skill/skill-badge) | `skill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`skill`](../packages/skill/skill) |
| [`web-fetch-http`](../packages/web/web-fetch-http) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`timeout`](../packages/util/timeout), [`web`](../packages/web/web) |
| [`web-search-exa`](../packages/web/web-search-exa) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`web`](../packages/web/web) |
@ -1483,72 +1499,75 @@ flowchart TD
| [`session-persistence`](../packages/session/session-persistence) | `session` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
| [`session-projection`](../packages/session/session-projection) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`acp-snapshot`](../packages/test-support/acp-snapshot) | `test-support` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`llm-retry`](../packages/llm/llm-retry) | `llm` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
| [`agent-default-model`](../packages/core/agent-default-model) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`settings`](../packages/settings/settings) |
| [`goal`](../packages/goal/goal) | `goal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`typert-protocol`](../packages/typert/protocol) |
| [`agent`](../packages/core/agent) | `core` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`typert-protocol`](../packages/typert/protocol) |
| [`fs`](../packages/fs/fs) | `fs` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
| [`web-search-deepseek`](../packages/web/web-search-deepseek) | `web` | [`agent`](../packages/core/agent), [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`web`](../packages/web/web) |
| [`spill-local`](../packages/spill/spill-local) | `spill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`spill`](../packages/spill/spill) |
| [`time-context`](../packages/context/time-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`message-feedback`](../packages/feedback/message-feedback) | `feedback` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`storage-domain`](../packages/storage/storage-domain), [`typert-protocol`](../packages/typert/protocol) |
| [`commands`](../packages/interaction/commands) | `interaction` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`user-approval`](../packages/interaction/user-approval) | `interaction` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`user-questions`](../packages/interaction/user-questions) | `interaction` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`jobs`](../packages/jobs/jobs) | `jobs` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`agent-presets`](../packages/preset/agent-presets) | `preset` | [`agent`](../packages/core/agent), [`atomic-write`](../packages/util/atomic-write), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt) |
| [`sandbox-local`](../packages/sandbox/sandbox-local) | `sandbox` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session) |
| [`sandbox-policy`](../packages/sandbox/sandbox-policy) | `sandbox` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
| [`session-persistence-sqlite`](../packages/session/session-persistence-sqlite) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
| [`session-projection-cache`](../packages/session/session-projection-cache) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`storage-domain`](../packages/storage/storage-domain) |
| [`session-stats`](../packages/session/session-stats) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
| [`session-telemetry`](../packages/session/session-telemetry) | `session` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`session-title`](../packages/session/session-title) | `session` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
| [`shell`](../packages/shell/shell) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`settings`](../packages/settings/settings), [`subprocess`](../packages/subprocess/subprocess) |
| [`terminal`](../packages/terminal/terminal) | `terminal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`loader-smoke`](../packages/test-support/loader-smoke) | `test-support` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`workflow`](../packages/workflow/workflow) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`workspace`](../packages/workspace/workspace) | `workspace` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`storage`](../packages/storage/storage), [`storage-domain`](../packages/storage/storage-domain) |
| [`tools`](../packages/core/tools) | `core` | [`agent`](../packages/core/agent), [`code-runtime`](../packages/code-runtime/code-runtime), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`user-approval`](../packages/interaction/user-approval) |
| [`command-goal`](../packages/goal/command-goal) | `goal` | [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`goal-round-driver`](../packages/goal/goal-round-driver) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`llm-retry`](../packages/llm/llm-retry) | `llm` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`timeout`](../packages/util/timeout) |
| [`agent-default-model`](../packages/core/agent-default-model) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`settings`](../packages/settings/settings) |
| [`goal`](../packages/goal/goal) | `goal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`typert-protocol`](../packages/typert/protocol) |
| [`fs-local`](../packages/fs/fs-local) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`fs-observation-policy`](../packages/fs/fs-observation-policy) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`skill-filesystem`](../packages/skill/skill-filesystem) | `skill` | [`fs`](../packages/fs/fs), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`skill`](../packages/skill/skill) |
| [`web-search-deepseek`](../packages/web/web-search-deepseek) | `web` | [`agent`](../packages/core/agent), [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`web`](../packages/web/web) |
| [`hook-protocol`](../packages/hooks/hook-protocol) | `hooks` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`shell`](../packages/shell/shell) |
| [`time-context`](../packages/context/time-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
| [`tmux-context`](../packages/context/tmux-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`shell`](../packages/shell/shell) |
| [`fs-e2b`](../packages/e2b/fs-e2b) | `e2b` | [`e2b`](../packages/e2b/e2b), [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`commands`](../packages/interaction/commands) | `interaction` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`user-approval`](../packages/interaction/user-approval) | `interaction` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt) |
| [`user-questions`](../packages/interaction/user-questions) | `interaction` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`jobs`](../packages/jobs/jobs) | `jobs` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`lsp-stdio`](../packages/lsp/lsp-stdio) | `lsp` | [`brand`](../packages/util/brand), [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`lsp`](../packages/lsp/lsp), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`agent-presets`](../packages/preset/agent-presets) | `preset` | [`agent`](../packages/core/agent), [`atomic-write`](../packages/util/atomic-write), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt) |
| [`sandbox-policy`](../packages/sandbox/sandbox-policy) | `sandbox` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt) |
| [`session-telemetry`](../packages/session/session-telemetry) | `session` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`session-title`](../packages/session/session-title) | `session` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
| [`bash-local`](../packages/shell/bash-local) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings), [`shell`](../packages/shell/shell), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`pwsh-local`](../packages/shell/pwsh-local) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings), [`shell`](../packages/shell/shell), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`terminal`](../packages/terminal/terminal) | `terminal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`loader-smoke`](../packages/test-support/loader-smoke) | `test-support` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`workflow`](../packages/workflow/workflow) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`tools`](../packages/core/tools) | `core` | [`agent`](../packages/core/agent), [`code-runtime`](../packages/code-runtime/code-runtime), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`user-approval`](../packages/interaction/user-approval) |
| [`command-goal`](../packages/goal/command-goal) | `goal` | [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`goal-round-driver`](../packages/goal/goal-round-driver) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`fs-sandbox`](../packages/fs/fs-sandbox) | `fs` | [`fs`](../packages/fs/fs), [`fs-local`](../packages/fs/fs-local), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
| [`session-query`](../packages/session-query/session-query) | `session-query` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-title`](../packages/session/session-title) |
| [`acp`](../packages/acp/acp) | `acp` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`user-approval`](../packages/interaction/user-approval) |
| [`headless`](../packages/bundle/headless) | `bundle` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`compaction`](../packages/compaction/compaction) | `compaction` | [`brand`](../packages/util/brand), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`tmux-context`](../packages/context/tmux-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`shell`](../packages/shell/shell) |
| [`fs-e2b`](../packages/e2b/fs-e2b) | `e2b` | [`e2b`](../packages/e2b/e2b), [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`command-feedback`](../packages/feedback/command-feedback) | `feedback` | [`anonymous-user-id`](../packages/identity/anonymous-user-id), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-telemetry`](../packages/session/session-telemetry) |
| [`permission-presets`](../packages/interaction/permission-presets) | `interaction` | [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`settings`](../packages/settings/settings), [`shell`](../packages/shell/shell), [`user-approval`](../packages/interaction/user-approval) |
| [`jobs-local`](../packages/jobs/jobs-local) | `jobs` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`scope`](../packages/core/scope), [`timeout`](../packages/util/timeout) |
| [`lsp-stdio`](../packages/lsp/lsp-stdio) | `lsp` | [`brand`](../packages/util/brand), [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`lsp`](../packages/lsp/lsp), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`session-title-llm`](../packages/session/session-title-llm) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`timeout`](../packages/util/timeout) |
| [`bash-local`](../packages/shell/bash-local) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings), [`shell`](../packages/shell/shell), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`pwsh-local`](../packages/shell/pwsh-local) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings), [`shell`](../packages/shell/shell), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`terminal-bash`](../packages/terminal/terminal-bash) | `terminal` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess), [`terminal`](../packages/terminal/terminal) |
| [`bash-sandbox`](../packages/shell/bash-sandbox) | `shell` | [`bash-local`](../packages/shell/bash-local), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) |
| [`pwsh-sandbox`](../packages/shell/pwsh-sandbox) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`pwsh-local`](../packages/shell/pwsh-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) |
| [`terminal-bash`](../packages/terminal/terminal-bash) | `terminal` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`subprocess`](../packages/subprocess/subprocess), [`terminal`](../packages/terminal/terminal) |
| [`token-meter`](../packages/llm/token-meter) | `llm` | [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`agent-tool-presentation`](../packages/core/agent-tool-presentation) | `core` | [`invariants`](../packages/runtime-diagnostics/invariants), [`tools`](../packages/core/tools) |
| [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`fs-sandbox`](../packages/fs/fs-sandbox) | `fs` | [`fs`](../packages/fs/fs), [`fs-local`](../packages/fs/fs-local), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
| [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`attachment`](../packages/attachment/attachment), [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| [`tool-fs-search`](../packages/fs/tool-fs-search) | `fs` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`tools`](../packages/core/tools) |
| [`tool-skill`](../packages/skill/tool-skill) | `skill` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`skill`](../packages/skill/skill), [`tools`](../packages/core/tools) |
| [`tool-skill`](../packages/skill/tool-skill) | `skill` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session-projection`](../packages/session/session-projection), [`skill`](../packages/skill/skill), [`tools`](../packages/core/tools) |
| [`subagent`](../packages/subagent/subagent) | `subagent` | [`agent`](../packages/core/agent), [`agent-presets`](../packages/preset/agent-presets), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| [`tool-web`](../packages/web/tool-web) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`web`](../packages/web/web) |
| [`spill-policy`](../packages/spill/spill-policy) | `spill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`tools`](../packages/core/tools) |
| [`tool-todo`](../packages/todo/tool-todo) | `todo` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`tools`](../packages/core/tools) |
| [`plan-mode`](../packages/plan/plan-mode) | `plan` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-questions`](../packages/interaction/user-questions) |
| [`hooks-codex`](../packages/hooks/hooks-codex) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`tools`](../packages/core/tools) |
| [`hooks-codex`](../packages/hooks/hooks-codex) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`tools`](../packages/core/tools) |
| [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | `session-query` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query) |
| [`tool-session-query`](../packages/session-query/tool-session-query) | `session-query` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`tool-session-query`](../packages/session-query/tool-session-query) | `session-query` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`command-compact`](../packages/compaction/command-compact) | `compaction` | [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`agent-instructions`](../packages/context/agent-instructions) | `context` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`agent-instructions`](../packages/context/agent-instructions) | `context` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`tools`](../packages/core/tools) |
| [`session-reference`](../packages/context/session-reference) | `context` | [`agent`](../packages/core/agent), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query) |
| [`cordis-host-runner`](../packages/extensions/cordis-host-runner) | `extensions` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol) |
| [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder) | `guard` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`tools`](../packages/core/tools) |
@ -1562,8 +1581,6 @@ flowchart TD
| [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | `session` | [`anonymous-user-id`](../packages/identity/anonymous-user-id), [`command-feedback`](../packages/feedback/command-feedback), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-telemetry`](../packages/session/session-telemetry) |
| [`session-title-all-prompts-llm`](../packages/session/session-title-all-prompts-llm) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`session-title-llm`](../packages/session/session-title-llm) |
| [`session-title-first-prompt-llm`](../packages/session/session-title-first-prompt-llm) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`session-title-llm`](../packages/session/session-title-llm) |
| [`bash-sandbox`](../packages/shell/bash-sandbox) | `shell` | [`bash-local`](../packages/shell/bash-local), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) |
| [`pwsh-sandbox`](../packages/shell/pwsh-sandbox) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`pwsh-local`](../packages/shell/pwsh-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) |
| [`shell-env`](../packages/shell/shell-env) | `shell` | [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`session-persistence`](../packages/session/session-persistence), [`shell`](../packages/shell/shell), [`tools`](../packages/core/tools) |
| [`tool-bash-persistent`](../packages/shell/tool-bash-persistent) | `shell` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`terminal`](../packages/terminal/terminal), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`tool-terminal`](../packages/terminal/tool-terminal) | `terminal` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`system-prompt`](../packages/core/system-prompt), [`terminal`](../packages/terminal/terminal), [`tools`](../packages/core/tools) |
@ -1577,12 +1594,12 @@ flowchart TD
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-subagent-control`](../packages/subagent/tool-subagent-control) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`tool-subagent-report`](../packages/subagent/tool-subagent-report) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`hooks-claude-code`](../packages/hooks/hooks-claude-code) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`hooks-claude-code`](../packages/hooks/hooks-claude-code) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`web-app`](../packages/bundle/web-app) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt) |
| [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | `compaction` | [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
| [`team`](../packages/experimental/team) | `experimental` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`subagent`](../packages/subagent/subagent) |
| [`tool-cordis`](../packages/extensions/tool-cordis) | `extensions` | [`agent`](../packages/core/agent), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`host-apiproxy`](../packages/host/apiproxy) | `host` | [`agent-presets`](../packages/preset/agent-presets), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`host-apiproxy`](../packages/host/apiproxy) | `host` | [`agent-loop`](../packages/core/agent-loop), [`agent-presets`](../packages/preset/agent-presets), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`sdk-protocol`](../packages/sdk/protocol) | `sdk` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`tool-bash`](../packages/shell/tool-bash) | `shell` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| [`tool-pwsh`](../packages/shell/tool-pwsh) | `shell` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |

View file

@ -438,12 +438,6 @@ flowchart TD
pkg_lsp --> pkg_brand
pkg_lsp --> pkg_invariants
pkg_lsp --> pkg_llm
pkg_agent --> pkg_invariants
pkg_agent --> pkg_llm
pkg_agent --> pkg_scope
pkg_agent --> pkg_session
pkg_agent --> pkg_system_prompt
pkg_agent --> pkg_typert_protocol
pkg_skill_badge --> pkg_invariants
pkg_skill_badge --> pkg_skill
pkg_web_fetch_http --> pkg_invariants
@ -480,40 +474,19 @@ flowchart TD
pkg_session_projection --> pkg_session
pkg_acp_snapshot --> pkg_invariants
pkg_acp_snapshot --> pkg_session
pkg_llm_retry --> pkg_agent
pkg_llm_retry --> pkg_brand
pkg_llm_retry --> pkg_invariants
pkg_llm_retry --> pkg_llm
pkg_llm_retry --> pkg_session
pkg_llm_retry --> pkg_timeout
pkg_agent_default_model --> pkg_agent
pkg_agent_default_model --> pkg_invariants
pkg_agent_default_model --> pkg_llm
pkg_agent_default_model --> pkg_settings
pkg_goal --> pkg_agent
pkg_goal --> pkg_brand
pkg_goal --> pkg_invariants
pkg_goal --> pkg_llm
pkg_goal --> pkg_scope
pkg_goal --> pkg_session
pkg_goal --> pkg_session_projection
pkg_goal --> pkg_typert_protocol
pkg_agent --> pkg_invariants
pkg_agent --> pkg_llm
pkg_agent --> pkg_scope
pkg_agent --> pkg_session
pkg_agent --> pkg_session_projection
pkg_agent --> pkg_system_prompt
pkg_agent --> pkg_typert_protocol
pkg_fs --> pkg_brand
pkg_fs --> pkg_invariants
pkg_fs --> pkg_llm
pkg_fs --> pkg_sandbox
pkg_web_search_deepseek --> pkg_agent
pkg_web_search_deepseek --> pkg_credentials
pkg_web_search_deepseek --> pkg_invariants
pkg_web_search_deepseek --> pkg_launch_environment
pkg_web_search_deepseek --> pkg_session
pkg_web_search_deepseek --> pkg_settings
pkg_web_search_deepseek --> pkg_web
pkg_spill_local --> pkg_invariants
pkg_spill_local --> pkg_spill
pkg_time_context --> pkg_agent
pkg_time_context --> pkg_invariants
pkg_time_context --> pkg_session
pkg_message_feedback --> pkg_brand
pkg_message_feedback --> pkg_invariants
pkg_message_feedback --> pkg_llm
@ -521,45 +494,10 @@ flowchart TD
pkg_message_feedback --> pkg_session_persistence
pkg_message_feedback --> pkg_storage_domain
pkg_message_feedback --> pkg_typert_protocol
pkg_commands --> pkg_agent
pkg_commands --> pkg_attachment
pkg_commands --> pkg_brand
pkg_commands --> pkg_invariants
pkg_commands --> pkg_llm
pkg_commands --> pkg_scope
pkg_commands --> pkg_session
pkg_commands --> pkg_typert_protocol
pkg_user_approval --> pkg_agent
pkg_user_approval --> pkg_brand
pkg_user_approval --> pkg_invariants
pkg_user_approval --> pkg_llm
pkg_user_approval --> pkg_scope
pkg_user_approval --> pkg_session
pkg_user_approval --> pkg_system_prompt
pkg_user_questions --> pkg_agent
pkg_user_questions --> pkg_invariants
pkg_user_questions --> pkg_llm
pkg_jobs --> pkg_agent
pkg_jobs --> pkg_brand
pkg_jobs --> pkg_invariants
pkg_jobs --> pkg_session
pkg_agent_presets --> pkg_agent
pkg_agent_presets --> pkg_atomic_write
pkg_agent_presets --> pkg_home_paths
pkg_agent_presets --> pkg_invariants
pkg_agent_presets --> pkg_scope
pkg_agent_presets --> pkg_session
pkg_agent_presets --> pkg_settings
pkg_agent_presets --> pkg_system_prompt
pkg_sandbox_local --> pkg_invariants
pkg_sandbox_local --> pkg_llm
pkg_sandbox_local --> pkg_sandbox
pkg_sandbox_local --> pkg_session
pkg_sandbox_policy --> pkg_agent
pkg_sandbox_policy --> pkg_invariants
pkg_sandbox_policy --> pkg_sandbox
pkg_sandbox_policy --> pkg_session
pkg_sandbox_policy --> pkg_system_prompt
pkg_session_persistence_jsonl --> pkg_invariants
pkg_session_persistence_jsonl --> pkg_session
pkg_session_persistence_jsonl --> pkg_session_persistence
@ -575,18 +513,128 @@ flowchart TD
pkg_session_stats --> pkg_llm
pkg_session_stats --> pkg_session
pkg_session_stats --> pkg_session_projection
pkg_shell --> pkg_invariants
pkg_shell --> pkg_sandbox
pkg_shell --> pkg_settings
pkg_shell --> pkg_subprocess
pkg_workspace --> pkg_brand
pkg_workspace --> pkg_invariants
pkg_workspace --> pkg_session
pkg_workspace --> pkg_session_persistence
pkg_workspace --> pkg_storage
pkg_workspace --> pkg_storage_domain
pkg_llm_retry --> pkg_agent
pkg_llm_retry --> pkg_brand
pkg_llm_retry --> pkg_invariants
pkg_llm_retry --> pkg_llm
pkg_llm_retry --> pkg_session
pkg_llm_retry --> pkg_session_projection
pkg_llm_retry --> pkg_timeout
pkg_agent_default_model --> pkg_agent
pkg_agent_default_model --> pkg_invariants
pkg_agent_default_model --> pkg_llm
pkg_agent_default_model --> pkg_settings
pkg_goal --> pkg_agent
pkg_goal --> pkg_brand
pkg_goal --> pkg_invariants
pkg_goal --> pkg_llm
pkg_goal --> pkg_scope
pkg_goal --> pkg_session
pkg_goal --> pkg_session_projection
pkg_goal --> pkg_typert_protocol
pkg_fs_local --> pkg_fs
pkg_fs_local --> pkg_invariants
pkg_fs_observation_policy --> pkg_fs
pkg_fs_observation_policy --> pkg_invariants
pkg_skill_filesystem --> pkg_fs
pkg_skill_filesystem --> pkg_home_paths
pkg_skill_filesystem --> pkg_invariants
pkg_skill_filesystem --> pkg_skill
pkg_web_search_deepseek --> pkg_agent
pkg_web_search_deepseek --> pkg_credentials
pkg_web_search_deepseek --> pkg_invariants
pkg_web_search_deepseek --> pkg_launch_environment
pkg_web_search_deepseek --> pkg_session
pkg_web_search_deepseek --> pkg_settings
pkg_web_search_deepseek --> pkg_web
pkg_hook_protocol --> pkg_invariants
pkg_hook_protocol --> pkg_session
pkg_hook_protocol --> pkg_shell
pkg_time_context --> pkg_agent
pkg_time_context --> pkg_invariants
pkg_time_context --> pkg_session
pkg_time_context --> pkg_session_projection
pkg_tmux_context --> pkg_agent
pkg_tmux_context --> pkg_invariants
pkg_tmux_context --> pkg_session
pkg_tmux_context --> pkg_session_projection
pkg_tmux_context --> pkg_shell
pkg_fs_e2b --> pkg_e2b
pkg_fs_e2b --> pkg_fs
pkg_fs_e2b --> pkg_invariants
pkg_commands --> pkg_agent
pkg_commands --> pkg_attachment
pkg_commands --> pkg_brand
pkg_commands --> pkg_invariants
pkg_commands --> pkg_llm
pkg_commands --> pkg_scope
pkg_commands --> pkg_session
pkg_commands --> pkg_typert_protocol
pkg_user_approval --> pkg_agent
pkg_user_approval --> pkg_brand
pkg_user_approval --> pkg_invariants
pkg_user_approval --> pkg_llm
pkg_user_approval --> pkg_scope
pkg_user_approval --> pkg_session
pkg_user_approval --> pkg_session_projection
pkg_user_approval --> pkg_system_prompt
pkg_user_questions --> pkg_agent
pkg_user_questions --> pkg_invariants
pkg_user_questions --> pkg_llm
pkg_jobs --> pkg_agent
pkg_jobs --> pkg_brand
pkg_jobs --> pkg_invariants
pkg_jobs --> pkg_session
pkg_lsp_stdio --> pkg_brand
pkg_lsp_stdio --> pkg_fs
pkg_lsp_stdio --> pkg_invariants
pkg_lsp_stdio --> pkg_llm
pkg_lsp_stdio --> pkg_lsp
pkg_lsp_stdio --> pkg_subprocess
pkg_lsp_stdio --> pkg_timeout
pkg_agent_presets --> pkg_agent
pkg_agent_presets --> pkg_atomic_write
pkg_agent_presets --> pkg_home_paths
pkg_agent_presets --> pkg_invariants
pkg_agent_presets --> pkg_scope
pkg_agent_presets --> pkg_session
pkg_agent_presets --> pkg_settings
pkg_agent_presets --> pkg_system_prompt
pkg_sandbox_policy --> pkg_agent
pkg_sandbox_policy --> pkg_invariants
pkg_sandbox_policy --> pkg_sandbox
pkg_sandbox_policy --> pkg_session
pkg_sandbox_policy --> pkg_session_projection
pkg_sandbox_policy --> pkg_system_prompt
pkg_session_telemetry --> pkg_agent
pkg_session_telemetry --> pkg_invariants
pkg_session_telemetry --> pkg_session
pkg_session_title --> pkg_agent
pkg_session_title --> pkg_brand
pkg_session_title --> pkg_invariants
pkg_session_title --> pkg_llm
pkg_session_title --> pkg_session
pkg_session_title --> pkg_session_projection
pkg_shell --> pkg_invariants
pkg_shell --> pkg_sandbox
pkg_shell --> pkg_settings
pkg_shell --> pkg_subprocess
pkg_bash_local --> pkg_invariants
pkg_bash_local --> pkg_settings
pkg_bash_local --> pkg_shell
pkg_bash_local --> pkg_subprocess
pkg_bash_local --> pkg_timeout
pkg_pwsh_local --> pkg_invariants
pkg_pwsh_local --> pkg_settings
pkg_pwsh_local --> pkg_shell
pkg_pwsh_local --> pkg_subprocess
pkg_pwsh_local --> pkg_timeout
pkg_terminal --> pkg_agent
pkg_terminal --> pkg_brand
pkg_terminal --> pkg_invariants
@ -599,12 +647,6 @@ flowchart TD
pkg_workflow --> pkg_invariants
pkg_workflow --> pkg_llm
pkg_workflow --> pkg_session
pkg_workspace --> pkg_brand
pkg_workspace --> pkg_invariants
pkg_workspace --> pkg_session
pkg_workspace --> pkg_session_persistence
pkg_workspace --> pkg_storage
pkg_workspace --> pkg_storage_domain
pkg_tools --> pkg_agent
pkg_tools --> pkg_code_runtime
pkg_tools --> pkg_invariants
@ -622,17 +664,11 @@ flowchart TD
pkg_goal_round_driver --> pkg_invariants
pkg_goal_round_driver --> pkg_llm
pkg_goal_round_driver --> pkg_session
pkg_fs_local --> pkg_fs
pkg_fs_local --> pkg_invariants
pkg_fs_observation_policy --> pkg_fs
pkg_fs_observation_policy --> pkg_invariants
pkg_skill_filesystem --> pkg_fs
pkg_skill_filesystem --> pkg_home_paths
pkg_skill_filesystem --> pkg_invariants
pkg_skill_filesystem --> pkg_skill
pkg_hook_protocol --> pkg_invariants
pkg_hook_protocol --> pkg_session
pkg_hook_protocol --> pkg_shell
pkg_fs_sandbox --> pkg_fs
pkg_fs_sandbox --> pkg_fs_local
pkg_fs_sandbox --> pkg_invariants
pkg_fs_sandbox --> pkg_sandbox
pkg_fs_sandbox --> pkg_sandbox_policy
pkg_session_query --> pkg_brand
pkg_session_query --> pkg_invariants
pkg_session_query --> pkg_llm
@ -655,13 +691,6 @@ flowchart TD
pkg_compaction --> pkg_invariants
pkg_compaction --> pkg_llm
pkg_compaction --> pkg_session
pkg_tmux_context --> pkg_agent
pkg_tmux_context --> pkg_invariants
pkg_tmux_context --> pkg_session
pkg_tmux_context --> pkg_shell
pkg_fs_e2b --> pkg_e2b
pkg_fs_e2b --> pkg_fs
pkg_fs_e2b --> pkg_invariants
pkg_command_feedback --> pkg_anonymous_user_id
pkg_command_feedback --> pkg_commands
pkg_command_feedback --> pkg_invariants
@ -681,33 +710,27 @@ flowchart TD
pkg_jobs_local --> pkg_jobs
pkg_jobs_local --> pkg_scope
pkg_jobs_local --> pkg_timeout
pkg_lsp_stdio --> pkg_brand
pkg_lsp_stdio --> pkg_fs
pkg_lsp_stdio --> pkg_invariants
pkg_lsp_stdio --> pkg_llm
pkg_lsp_stdio --> pkg_lsp
pkg_lsp_stdio --> pkg_subprocess
pkg_lsp_stdio --> pkg_timeout
pkg_session_title_llm --> pkg_invariants
pkg_session_title_llm --> pkg_llm
pkg_session_title_llm --> pkg_session
pkg_session_title_llm --> pkg_session_title
pkg_session_title_llm --> pkg_timeout
pkg_bash_local --> pkg_invariants
pkg_bash_local --> pkg_settings
pkg_bash_local --> pkg_shell
pkg_bash_local --> pkg_subprocess
pkg_bash_local --> pkg_timeout
pkg_pwsh_local --> pkg_invariants
pkg_pwsh_local --> pkg_settings
pkg_pwsh_local --> pkg_shell
pkg_pwsh_local --> pkg_subprocess
pkg_pwsh_local --> pkg_timeout
pkg_bash_sandbox --> pkg_bash_local
pkg_bash_sandbox --> pkg_invariants
pkg_bash_sandbox --> pkg_sandbox
pkg_bash_sandbox --> pkg_sandbox_policy
pkg_bash_sandbox --> pkg_shell
pkg_pwsh_sandbox --> pkg_invariants
pkg_pwsh_sandbox --> pkg_pwsh_local
pkg_pwsh_sandbox --> pkg_sandbox
pkg_pwsh_sandbox --> pkg_sandbox_policy
pkg_pwsh_sandbox --> pkg_shell
pkg_terminal_bash --> pkg_agent
pkg_terminal_bash --> pkg_invariants
pkg_terminal_bash --> pkg_sandbox
pkg_terminal_bash --> pkg_sandbox_policy
pkg_terminal_bash --> pkg_session
pkg_terminal_bash --> pkg_session_projection
pkg_terminal_bash --> pkg_subprocess
pkg_terminal_bash --> pkg_terminal
pkg_token_meter --> pkg_compaction
@ -721,6 +744,7 @@ flowchart TD
pkg_agent_loop --> pkg_scope
pkg_agent_loop --> pkg_session
pkg_agent_loop --> pkg_session_persistence
pkg_agent_loop --> pkg_session_projection
pkg_agent_loop --> pkg_settings
pkg_agent_loop --> pkg_system_prompt
pkg_agent_loop --> pkg_tools
@ -731,13 +755,9 @@ flowchart TD
pkg_tool_goal --> pkg_invariants
pkg_tool_goal --> pkg_llm
pkg_tool_goal --> pkg_session
pkg_tool_goal --> pkg_session_projection
pkg_tool_goal --> pkg_system_prompt
pkg_tool_goal --> pkg_tools
pkg_fs_sandbox --> pkg_fs
pkg_fs_sandbox --> pkg_fs_local
pkg_fs_sandbox --> pkg_invariants
pkg_fs_sandbox --> pkg_sandbox
pkg_fs_sandbox --> pkg_sandbox_policy
pkg_tool_fs --> pkg_attachment
pkg_tool_fs --> pkg_fs
pkg_tool_fs --> pkg_invariants
@ -765,6 +785,7 @@ flowchart TD
pkg_tool_skill --> pkg_agent
pkg_tool_skill --> pkg_invariants
pkg_tool_skill --> pkg_llm
pkg_tool_skill --> pkg_session_projection
pkg_tool_skill --> pkg_skill
pkg_tool_skill --> pkg_tools
pkg_subagent --> pkg_agent
@ -813,14 +834,17 @@ flowchart TD
pkg_hooks_codex --> pkg_llm
pkg_hooks_codex --> pkg_session
pkg_hooks_codex --> pkg_session_persistence
pkg_hooks_codex --> pkg_session_projection
pkg_hooks_codex --> pkg_tools
pkg_session_query_sqlite --> pkg_invariants
pkg_session_query_sqlite --> pkg_session
pkg_session_query_sqlite --> pkg_session_persistence
pkg_session_query_sqlite --> pkg_session_query
pkg_tool_session_query --> pkg_agent
pkg_tool_session_query --> pkg_invariants
pkg_tool_session_query --> pkg_llm
pkg_tool_session_query --> pkg_session
pkg_tool_session_query --> pkg_session_projection
pkg_tool_session_query --> pkg_session_query
pkg_tool_session_query --> pkg_system_prompt
pkg_tool_session_query --> pkg_timeout
@ -834,6 +858,7 @@ flowchart TD
pkg_agent_instructions --> pkg_invariants
pkg_agent_instructions --> pkg_llm
pkg_agent_instructions --> pkg_session
pkg_agent_instructions --> pkg_session_projection
pkg_agent_instructions --> pkg_tools
pkg_session_reference --> pkg_agent
pkg_session_reference --> pkg_compaction
@ -909,16 +934,6 @@ flowchart TD
pkg_session_title_first_prompt_llm --> pkg_session
pkg_session_title_first_prompt_llm --> pkg_session_title
pkg_session_title_first_prompt_llm --> pkg_session_title_llm
pkg_bash_sandbox --> pkg_bash_local
pkg_bash_sandbox --> pkg_invariants
pkg_bash_sandbox --> pkg_sandbox
pkg_bash_sandbox --> pkg_sandbox_policy
pkg_bash_sandbox --> pkg_shell
pkg_pwsh_sandbox --> pkg_invariants
pkg_pwsh_sandbox --> pkg_pwsh_local
pkg_pwsh_sandbox --> pkg_sandbox
pkg_pwsh_sandbox --> pkg_sandbox_policy
pkg_pwsh_sandbox --> pkg_shell
pkg_shell_env --> pkg_home_paths
pkg_shell_env --> pkg_invariants
pkg_shell_env --> pkg_session_persistence
@ -1003,6 +1018,7 @@ flowchart TD
pkg_hooks_claude_code --> pkg_llm
pkg_hooks_claude_code --> pkg_session
pkg_hooks_claude_code --> pkg_session_persistence
pkg_hooks_claude_code --> pkg_session_projection
pkg_hooks_claude_code --> pkg_subagent
pkg_hooks_claude_code --> pkg_tools
pkg_web_app --> pkg_invariants
@ -1028,6 +1044,7 @@ flowchart TD
pkg_tool_cordis --> pkg_session
pkg_tool_cordis --> pkg_system_prompt
pkg_tool_cordis --> pkg_tools
pkg_host_apiproxy --> pkg_agent_loop
pkg_host_apiproxy --> pkg_agent_presets
pkg_host_apiproxy --> pkg_cordis_host_runner
pkg_host_apiproxy --> pkg_invariants
@ -1472,7 +1489,6 @@ flowchart TD
| [`skill`](../packages/skill/skill) | `skill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
| [`web`](../packages/web/web) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`lsp`](../packages/lsp/lsp) | `lsp` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`agent`](../packages/core/agent) | `core` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`typert-protocol`](../packages/typert/protocol) |
| [`skill-badge`](../packages/skill/skill-badge) | `skill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`skill`](../packages/skill/skill) |
| [`web-fetch-http`](../packages/web/web-fetch-http) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`timeout`](../packages/util/timeout), [`web`](../packages/web/web) |
| [`web-search-exa`](../packages/web/web-search-exa) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`web`](../packages/web/web) |
@ -1485,72 +1501,75 @@ flowchart TD
| [`session-persistence`](../packages/session/session-persistence) | `session` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
| [`session-projection`](../packages/session/session-projection) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`acp-snapshot`](../packages/test-support/acp-snapshot) | `test-support` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`llm-retry`](../packages/llm/llm-retry) | `llm` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
| [`agent-default-model`](../packages/core/agent-default-model) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`settings`](../packages/settings/settings) |
| [`goal`](../packages/goal/goal) | `goal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`typert-protocol`](../packages/typert/protocol) |
| [`agent`](../packages/core/agent) | `core` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`typert-protocol`](../packages/typert/protocol) |
| [`fs`](../packages/fs/fs) | `fs` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
| [`web-search-deepseek`](../packages/web/web-search-deepseek) | `web` | [`agent`](../packages/core/agent), [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`web`](../packages/web/web) |
| [`spill-local`](../packages/spill/spill-local) | `spill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`spill`](../packages/spill/spill) |
| [`time-context`](../packages/context/time-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`message-feedback`](../packages/feedback/message-feedback) | `feedback` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`storage-domain`](../packages/storage/storage-domain), [`typert-protocol`](../packages/typert/protocol) |
| [`commands`](../packages/interaction/commands) | `interaction` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`user-approval`](../packages/interaction/user-approval) | `interaction` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`user-questions`](../packages/interaction/user-questions) | `interaction` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`jobs`](../packages/jobs/jobs) | `jobs` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`agent-presets`](../packages/preset/agent-presets) | `preset` | [`agent`](../packages/core/agent), [`atomic-write`](../packages/util/atomic-write), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt) |
| [`sandbox-local`](../packages/sandbox/sandbox-local) | `sandbox` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session) |
| [`sandbox-policy`](../packages/sandbox/sandbox-policy) | `sandbox` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
| [`session-persistence-sqlite`](../packages/session/session-persistence-sqlite) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
| [`session-projection-cache`](../packages/session/session-projection-cache) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`storage-domain`](../packages/storage/storage-domain) |
| [`session-stats`](../packages/session/session-stats) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
| [`session-telemetry`](../packages/session/session-telemetry) | `session` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`session-title`](../packages/session/session-title) | `session` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
| [`shell`](../packages/shell/shell) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`settings`](../packages/settings/settings), [`subprocess`](../packages/subprocess/subprocess) |
| [`terminal`](../packages/terminal/terminal) | `terminal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`loader-smoke`](../packages/test-support/loader-smoke) | `test-support` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`workflow`](../packages/workflow/workflow) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`workspace`](../packages/workspace/workspace) | `workspace` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`storage`](../packages/storage/storage), [`storage-domain`](../packages/storage/storage-domain) |
| [`tools`](../packages/core/tools) | `core` | [`agent`](../packages/core/agent), [`code-runtime`](../packages/code-runtime/code-runtime), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`user-approval`](../packages/interaction/user-approval) |
| [`command-goal`](../packages/goal/command-goal) | `goal` | [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`goal-round-driver`](../packages/goal/goal-round-driver) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`llm-retry`](../packages/llm/llm-retry) | `llm` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`timeout`](../packages/util/timeout) |
| [`agent-default-model`](../packages/core/agent-default-model) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`settings`](../packages/settings/settings) |
| [`goal`](../packages/goal/goal) | `goal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`typert-protocol`](../packages/typert/protocol) |
| [`fs-local`](../packages/fs/fs-local) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`fs-observation-policy`](../packages/fs/fs-observation-policy) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`skill-filesystem`](../packages/skill/skill-filesystem) | `skill` | [`fs`](../packages/fs/fs), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`skill`](../packages/skill/skill) |
| [`web-search-deepseek`](../packages/web/web-search-deepseek) | `web` | [`agent`](../packages/core/agent), [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`web`](../packages/web/web) |
| [`hook-protocol`](../packages/hooks/hook-protocol) | `hooks` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`shell`](../packages/shell/shell) |
| [`time-context`](../packages/context/time-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
| [`tmux-context`](../packages/context/tmux-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`shell`](../packages/shell/shell) |
| [`fs-e2b`](../packages/e2b/fs-e2b) | `e2b` | [`e2b`](../packages/e2b/e2b), [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`commands`](../packages/interaction/commands) | `interaction` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`user-approval`](../packages/interaction/user-approval) | `interaction` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt) |
| [`user-questions`](../packages/interaction/user-questions) | `interaction` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`jobs`](../packages/jobs/jobs) | `jobs` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`lsp-stdio`](../packages/lsp/lsp-stdio) | `lsp` | [`brand`](../packages/util/brand), [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`lsp`](../packages/lsp/lsp), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`agent-presets`](../packages/preset/agent-presets) | `preset` | [`agent`](../packages/core/agent), [`atomic-write`](../packages/util/atomic-write), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt) |
| [`sandbox-policy`](../packages/sandbox/sandbox-policy) | `sandbox` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt) |
| [`session-telemetry`](../packages/session/session-telemetry) | `session` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`session-title`](../packages/session/session-title) | `session` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
| [`bash-local`](../packages/shell/bash-local) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings), [`shell`](../packages/shell/shell), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`pwsh-local`](../packages/shell/pwsh-local) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings), [`shell`](../packages/shell/shell), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`terminal`](../packages/terminal/terminal) | `terminal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`loader-smoke`](../packages/test-support/loader-smoke) | `test-support` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`workflow`](../packages/workflow/workflow) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`tools`](../packages/core/tools) | `core` | [`agent`](../packages/core/agent), [`code-runtime`](../packages/code-runtime/code-runtime), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`user-approval`](../packages/interaction/user-approval) |
| [`command-goal`](../packages/goal/command-goal) | `goal` | [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`goal-round-driver`](../packages/goal/goal-round-driver) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`fs-sandbox`](../packages/fs/fs-sandbox) | `fs` | [`fs`](../packages/fs/fs), [`fs-local`](../packages/fs/fs-local), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
| [`session-query`](../packages/session-query/session-query) | `session-query` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-title`](../packages/session/session-title) |
| [`acp`](../packages/acp/acp) | `acp` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`user-approval`](../packages/interaction/user-approval) |
| [`headless`](../packages/bundle/headless) | `bundle` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`compaction`](../packages/compaction/compaction) | `compaction` | [`brand`](../packages/util/brand), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`tmux-context`](../packages/context/tmux-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`shell`](../packages/shell/shell) |
| [`fs-e2b`](../packages/e2b/fs-e2b) | `e2b` | [`e2b`](../packages/e2b/e2b), [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`command-feedback`](../packages/feedback/command-feedback) | `feedback` | [`anonymous-user-id`](../packages/identity/anonymous-user-id), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-telemetry`](../packages/session/session-telemetry) |
| [`permission-presets`](../packages/interaction/permission-presets) | `interaction` | [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`settings`](../packages/settings/settings), [`shell`](../packages/shell/shell), [`user-approval`](../packages/interaction/user-approval) |
| [`jobs-local`](../packages/jobs/jobs-local) | `jobs` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`scope`](../packages/core/scope), [`timeout`](../packages/util/timeout) |
| [`lsp-stdio`](../packages/lsp/lsp-stdio) | `lsp` | [`brand`](../packages/util/brand), [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`lsp`](../packages/lsp/lsp), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`session-title-llm`](../packages/session/session-title-llm) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`timeout`](../packages/util/timeout) |
| [`bash-local`](../packages/shell/bash-local) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings), [`shell`](../packages/shell/shell), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`pwsh-local`](../packages/shell/pwsh-local) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings), [`shell`](../packages/shell/shell), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`terminal-bash`](../packages/terminal/terminal-bash) | `terminal` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess), [`terminal`](../packages/terminal/terminal) |
| [`bash-sandbox`](../packages/shell/bash-sandbox) | `shell` | [`bash-local`](../packages/shell/bash-local), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) |
| [`pwsh-sandbox`](../packages/shell/pwsh-sandbox) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`pwsh-local`](../packages/shell/pwsh-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) |
| [`terminal-bash`](../packages/terminal/terminal-bash) | `terminal` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`subprocess`](../packages/subprocess/subprocess), [`terminal`](../packages/terminal/terminal) |
| [`token-meter`](../packages/llm/token-meter) | `llm` | [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`settings`](../packages/settings/settings), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`agent-tool-presentation`](../packages/core/agent-tool-presentation) | `core` | [`invariants`](../packages/runtime-diagnostics/invariants), [`tools`](../packages/core/tools) |
| [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`fs-sandbox`](../packages/fs/fs-sandbox) | `fs` | [`fs`](../packages/fs/fs), [`fs-local`](../packages/fs/fs-local), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
| [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`attachment`](../packages/attachment/attachment), [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| [`tool-fs-search`](../packages/fs/tool-fs-search) | `fs` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`tools`](../packages/core/tools) |
| [`tool-skill`](../packages/skill/tool-skill) | `skill` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`skill`](../packages/skill/skill), [`tools`](../packages/core/tools) |
| [`tool-skill`](../packages/skill/tool-skill) | `skill` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session-projection`](../packages/session/session-projection), [`skill`](../packages/skill/skill), [`tools`](../packages/core/tools) |
| [`subagent`](../packages/subagent/subagent) | `subagent` | [`agent`](../packages/core/agent), [`agent-presets`](../packages/preset/agent-presets), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| [`tool-web`](../packages/web/tool-web) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`web`](../packages/web/web) |
| [`spill-policy`](../packages/spill/spill-policy) | `spill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`tools`](../packages/core/tools) |
| [`tool-todo`](../packages/todo/tool-todo) | `todo` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`tools`](../packages/core/tools) |
| [`plan-mode`](../packages/plan/plan-mode) | `plan` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-questions`](../packages/interaction/user-questions) |
| [`hooks-codex`](../packages/hooks/hooks-codex) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`tools`](../packages/core/tools) |
| [`hooks-codex`](../packages/hooks/hooks-codex) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`tools`](../packages/core/tools) |
| [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | `session-query` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query) |
| [`tool-session-query`](../packages/session-query/tool-session-query) | `session-query` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`tool-session-query`](../packages/session-query/tool-session-query) | `session-query` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`command-compact`](../packages/compaction/command-compact) | `compaction` | [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`agent-instructions`](../packages/context/agent-instructions) | `context` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`agent-instructions`](../packages/context/agent-instructions) | `context` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`tools`](../packages/core/tools) |
| [`session-reference`](../packages/context/session-reference) | `context` | [`agent`](../packages/core/agent), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query) |
| [`cordis-host-runner`](../packages/extensions/cordis-host-runner) | `extensions` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol) |
| [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder) | `guard` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`tools`](../packages/core/tools) |
@ -1564,8 +1583,6 @@ flowchart TD
| [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | `session` | [`anonymous-user-id`](../packages/identity/anonymous-user-id), [`command-feedback`](../packages/feedback/command-feedback), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-telemetry`](../packages/session/session-telemetry) |
| [`session-title-all-prompts-llm`](../packages/session/session-title-all-prompts-llm) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`session-title-llm`](../packages/session/session-title-llm) |
| [`session-title-first-prompt-llm`](../packages/session/session-title-first-prompt-llm) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`session-title-llm`](../packages/session/session-title-llm) |
| [`bash-sandbox`](../packages/shell/bash-sandbox) | `shell` | [`bash-local`](../packages/shell/bash-local), [`invariants`](../packages/runtime-diagnostics/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) |
| [`pwsh-sandbox`](../packages/shell/pwsh-sandbox) | `shell` | [`invariants`](../packages/runtime-diagnostics/invariants), [`pwsh-local`](../packages/shell/pwsh-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) |
| [`shell-env`](../packages/shell/shell-env) | `shell` | [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`session-persistence`](../packages/session/session-persistence), [`shell`](../packages/shell/shell), [`tools`](../packages/core/tools) |
| [`tool-bash-persistent`](../packages/shell/tool-bash-persistent) | `shell` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`terminal`](../packages/terminal/terminal), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`tool-terminal`](../packages/terminal/tool-terminal) | `terminal` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`system-prompt`](../packages/core/system-prompt), [`terminal`](../packages/terminal/terminal), [`tools`](../packages/core/tools) |
@ -1579,12 +1596,12 @@ flowchart TD
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-subagent-control`](../packages/subagent/tool-subagent-control) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`tool-subagent-report`](../packages/subagent/tool-subagent-report) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`hooks-claude-code`](../packages/hooks/hooks-claude-code) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`hooks-claude-code`](../packages/hooks/hooks-claude-code) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`web-app`](../packages/bundle/web-app) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt) |
| [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | `compaction` | [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
| [`team`](../packages/experimental/team) | `experimental` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`subagent`](../packages/subagent/subagent) |
| [`tool-cordis`](../packages/extensions/tool-cordis) | `extensions` | [`agent`](../packages/core/agent), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`host-apiproxy`](../packages/host/apiproxy) | `host` | [`agent-presets`](../packages/preset/agent-presets), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`host-apiproxy`](../packages/host/apiproxy) | `host` | [`agent-loop`](../packages/core/agent-loop), [`agent-presets`](../packages/preset/agent-presets), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`sdk-protocol`](../packages/sdk/protocol) | `sdk` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`tool-bash`](../packages/shell/tool-bash) | `shell` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| [`tool-pwsh`](../packages/shell/tool-pwsh) | `shell` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/persistence-catalog.md
persistence-catalog.md: c79c1bbf8f8fe9c3ed677f1cee160472b22a3584
persistence-catalog.zh.md: 23e2c15810d97998b49cfee1d1e96204c30661f9
persistence-catalog.md: db96226b648afecd65e74f50e14a82d036f7de74
persistence-catalog.zh.md: 0a247b4c4c5bf7eedbf8b7b8120bac3828090bc6

View file

@ -115,7 +115,7 @@ Sources: [`packages/core/session/src/types.ts:336`](../packages/core/session/src
}
```
Source: [`packages/core/agent/src/types.ts:19`](../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:33`](../packages/core/agent/src/types.ts)
### `agent-preset/*`
@ -160,7 +160,7 @@ Source: [`packages/preset/agent-presets/src/session.ts:26`](../packages/preset/a
Types: [CallId](subsystems/core.md)
Source: [`packages/interaction/user-approval/src/index.ts:44`](../packages/interaction/user-approval/src/index.ts)
Source: [`packages/interaction/user-approval/src/index.ts:73`](../packages/interaction/user-approval/src/index.ts)
<a id="approvaldecided--log-only"></a>
@ -178,7 +178,7 @@ Source: [`packages/interaction/user-approval/src/index.ts:44`](../packages/inter
}
```
Source: [`packages/interaction/user-approval/src/index.ts:55`](../packages/interaction/user-approval/src/index.ts)
Source: [`packages/interaction/user-approval/src/index.ts:84`](../packages/interaction/user-approval/src/index.ts)
<a id="approvalpolicy--log-only"></a>
@ -189,7 +189,8 @@ Source: [`packages/interaction/user-approval/src/index.ts:55`](../packages/inter
* The session's approval policy was switched — log-only, durable,
* replayable, never in the model transcript (the model learns the policy
* from the runtime-context snapshot and live switch notices). The LAST
* such event is the session's override ({@link effectiveApprovalPolicy}).
* such event is the session's override (folded by the approvalPolicy
* projection unit).
* `source: 'delegation'` marks an override seeded into a child; an absent
* source is a runtime switch.
*/
@ -200,7 +201,7 @@ Source: [`packages/interaction/user-approval/src/index.ts:55`](../packages/inter
}
```
Source: [`packages/interaction/user-approval/src/index.ts:67`](../packages/interaction/user-approval/src/index.ts)
Source: [`packages/interaction/user-approval/src/index.ts:97`](../packages/interaction/user-approval/src/index.ts)
### `assistant/*`
@ -504,13 +505,13 @@ Source: [`packages/llm/llm-retry/src/types.ts:11`](../packages/llm/llm-retry/src
/**
* Records the selected preset as durable, log-only user intent. The knob
* events follow in the same turn and control execution; this event stays
* out of the model transcript and lets {@link effectivePermissionPreset}
* out of the model transcript and lets the permission projection unit
* preserve a selection when bundles match.
*/
'permission/preset': { preset: string }
```
Source: [`packages/interaction/permission-presets/src/index.ts:50`](../packages/interaction/permission-presets/src/index.ts)
Source: [`packages/interaction/permission-presets/src/index.ts:46`](../packages/interaction/permission-presets/src/index.ts)
### `plan/*`
@ -522,12 +523,12 @@ Source: [`packages/interaction/permission-presets/src/index.ts:50`](../packages/
/**
* Whether plan mode is in force from this point on: log-only, non-surface,
* whole-value replace. The last `plan/mode` wins; a log with none folds to
* inactive through {@link foldPlanMode}.
* inactive through the projection unit's fold.
*/
'plan/mode': { active: boolean }
```
Source: [`packages/plan/plan-mode/src/index.ts:53`](../packages/plan/plan-mode/src/index.ts)
Source: [`packages/plan/plan-mode/src/index.ts:48`](../packages/plan/plan-mode/src/index.ts)
### `request/*`
@ -570,7 +571,7 @@ Source: [`packages/core/session/src/types.ts:304`](../packages/core/session/src/
* The session's sandbox mode was switched — log-only (like `approval/*`;
* NOT a surface event, carries no `surfaceOp`): durable and replayable,
* never in the model transcript. The LAST such event is the session's
* override ({@link effectiveSandboxMode}). `source: 'delegation'` marks
* override (folded by the sandboxMode projection unit). `source: 'delegation'` marks
* an override seeded into a child; an absent source is a runtime switch.
*/
'sandbox/mode': {
@ -648,7 +649,7 @@ Source: [`packages/core/session/src/types.ts:332`](../packages/core/session/src/
Types: [SessionTitleEventData](subsystems/session-title.md)
Source: [`packages/session/session-title/src/index.ts:100`](../packages/session/session-title/src/index.ts)
Source: [`packages/session/session-title/src/index.ts:75`](../packages/session/session-title/src/index.ts)
<a id="sessiontitle-llm-request--log-only"></a>

View file

@ -117,7 +117,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
}
```
来源:[`packages/core/agent/src/types.ts:19`](../packages/core/agent/src/types.ts)
来源:[`packages/core/agent/src/types.ts:33`](../packages/core/agent/src/types.ts)
### `agent-preset/*`
@ -162,7 +162,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
类型:[CallId](subsystems/core.md)
来源:[`packages/interaction/user-approval/src/index.ts:44`](../packages/interaction/user-approval/src/index.ts)
来源:[`packages/interaction/user-approval/src/index.ts:73`](../packages/interaction/user-approval/src/index.ts)
<a id="approvaldecided--log-only"></a>
@ -180,7 +180,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
}
```
来源:[`packages/interaction/user-approval/src/index.ts:55`](../packages/interaction/user-approval/src/index.ts)
来源:[`packages/interaction/user-approval/src/index.ts:84`](../packages/interaction/user-approval/src/index.ts)
<a id="approvalpolicy--log-only"></a>
@ -191,7 +191,8 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
* The session's approval policy was switched — log-only, durable,
* replayable, never in the model transcript (the model learns the policy
* from the runtime-context snapshot and live switch notices). The LAST
* such event is the session's override ({@link effectiveApprovalPolicy}).
* such event is the session's override (folded by the approvalPolicy
* projection unit).
* `source: 'delegation'` marks an override seeded into a child; an absent
* source is a runtime switch.
*/
@ -202,7 +203,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
}
```
来源:[`packages/interaction/user-approval/src/index.ts:67`](../packages/interaction/user-approval/src/index.ts)
来源:[`packages/interaction/user-approval/src/index.ts:97`](../packages/interaction/user-approval/src/index.ts)
### `assistant/*`
@ -506,13 +507,13 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
/**
* Records the selected preset as durable, log-only user intent. The knob
* events follow in the same turn and control execution; this event stays
* out of the model transcript and lets {@link effectivePermissionPreset}
* out of the model transcript and lets the permission projection unit
* preserve a selection when bundles match.
*/
'permission/preset': { preset: string }
```
来源:[`packages/interaction/permission-presets/src/index.ts:50`](../packages/interaction/permission-presets/src/index.ts)
来源:[`packages/interaction/permission-presets/src/index.ts:46`](../packages/interaction/permission-presets/src/index.ts)
### `plan/*`
@ -524,12 +525,12 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
/**
* Whether plan mode is in force from this point on: log-only, non-surface,
* whole-value replace. The last `plan/mode` wins; a log with none folds to
* inactive through {@link foldPlanMode}.
* inactive through the projection unit's fold.
*/
'plan/mode': { active: boolean }
```
来源:[`packages/plan/plan-mode/src/index.ts:53`](../packages/plan/plan-mode/src/index.ts)
来源:[`packages/plan/plan-mode/src/index.ts:48`](../packages/plan/plan-mode/src/index.ts)
### `request/*`
@ -572,7 +573,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
* The session's sandbox mode was switched — log-only (like `approval/*`;
* NOT a surface event, carries no `surfaceOp`): durable and replayable,
* never in the model transcript. The LAST such event is the session's
* override ({@link effectiveSandboxMode}). `source: 'delegation'` marks
* override (folded by the sandboxMode projection unit). `source: 'delegation'` marks
* an override seeded into a child; an absent source is a runtime switch.
*/
'sandbox/mode': {
@ -650,7 +651,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
类型:[SessionTitleEventData](subsystems/session-title.md)
来源:[`packages/session/session-title/src/index.ts:100`](../packages/session/session-title/src/index.ts)
来源:[`packages/session/session-title/src/index.ts:75`](../packages/session/session-title/src/index.ts)
<a id="sessiontitle-llm-request--log-only"></a>

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/approval.md
approval.md: 8e0ae22f5ed4c68f460ddfd929cf481d24015a27
approval.zh.md: f4a6371f9ac962db4d0cbce41399a47e4e444dbd
approval.md: ee8ab1cfca1fba6868b5f4931bab3dd9118a7a9b
approval.zh.md: f84d6a1bcd44e565a5cce85fbeb2a1194a2acade

View file

@ -132,7 +132,7 @@ setPolicy(agent: Agent, policy: ApprovalPolicy): void
async request(req: ApprovalRequest): Promise<ApprovalOutcome>
/**
* Read the session override without applying the configured default.
* Read the projected session override without applying the configured default.
* @param session - session whose log supplies the override.
* @returns the last logged policy, or `undefined` without one.
*/
@ -141,7 +141,7 @@ overrideOf(session: Session): ApprovalPolicy | undefined
Types: [Agent](core.md) · [Session](session.md)
Source: [`packages/interaction/user-approval/src/index.ts:192`](../../packages/interaction/user-approval/src/index.ts)
Source: [`packages/interaction/user-approval/src/index.ts:190`](../../packages/interaction/user-approval/src/index.ts)
<a id="approval-events"></a>
@ -166,5 +166,5 @@ Ask composed answerers for one decision. Return an outcome to claim the request
Types: [Scoped](scope.md)
Source: [`packages/interaction/user-approval/src/index.ts:30`](../../packages/interaction/user-approval/src/index.ts)
Source: [`packages/interaction/user-approval/src/index.ts:32`](../../packages/interaction/user-approval/src/index.ts)
<!-- END GENERATED cordis-surface -->

View file

@ -132,7 +132,7 @@ setPolicy(agent: Agent, policy: ApprovalPolicy): void
async request(req: ApprovalRequest): Promise<ApprovalOutcome>
/**
* Read the session override without applying the configured default.
* Read the projected session override without applying the configured default.
* @param session - session whose log supplies the override.
* @returns the last logged policy, or `undefined` without one.
*/
@ -141,7 +141,7 @@ overrideOf(session: Session): ApprovalPolicy | undefined
Types: [Agent](core.md) · [Session](session.md)
Source: [`packages/interaction/user-approval/src/index.ts:192`](../../packages/interaction/user-approval/src/index.ts)
Source: [`packages/interaction/user-approval/src/index.ts:190`](../../packages/interaction/user-approval/src/index.ts)
<a id="approval-events"></a>
@ -166,5 +166,5 @@ Ask composed answerers for one decision. Return an outcome to claim the request
Types: [Scoped](scope.md)
Source: [`packages/interaction/user-approval/src/index.ts:30`](../../packages/interaction/user-approval/src/index.ts)
Source: [`packages/interaction/user-approval/src/index.ts:32`](../../packages/interaction/user-approval/src/index.ts)
<!-- END GENERATED cordis-surface -->

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/core.md
core.md: d14ad52e57572d5b0b110a0b16ff734e3499bdf5
core.zh.md: 9ace28731f2532f571b66f2e7f6a3ea4a2c4e345
core.md: 1f53a6f6831f4d969464c9ff83fd64a96b5cadd9
core.zh.md: 083b88c136efec54bb45db3d736d7b131df9b048

View file

@ -377,7 +377,7 @@ async resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandl
Types: [SessionHeader](persistence.md)
Source: [`packages/core/agent-loop/src/index.ts:296`](../../packages/core/agent-loop/src/index.ts)
Source: [`packages/core/agent-loop/src/index.ts:355`](../../packages/core/agent-loop/src/index.ts)
<a id="ctxagentpresets--agentpresets"></a>
@ -720,7 +720,7 @@ list(): Agent[]
roots(): Agent[]
```
Source: [`packages/core/agent/src/index.ts:256`](../../packages/core/agent/src/index.ts)
Source: [`packages/core/agent/src/index.ts:257`](../../packages/core/agent/src/index.ts)
<a id="agent-events"></a>
@ -1043,7 +1043,7 @@ A declarative agent entry failed before it could publish a live agent. Consumers
'agent-loop/config-start-failed'(payload: { sessionId: SessionId; error: unknown }): void
```
Source: [`packages/core/agent-loop/src/index.ts:183`](../../packages/core/agent-loop/src/index.ts)
Source: [`packages/core/agent-loop/src/index.ts:242`](../../packages/core/agent-loop/src/index.ts)
<a id="agent-preset-events"></a>

View file

@ -385,7 +385,7 @@ async resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandl
Types: [SessionHeader](persistence.md)
Source: [`packages/core/agent-loop/src/index.ts:296`](../../packages/core/agent-loop/src/index.ts)
Source: [`packages/core/agent-loop/src/index.ts:355`](../../packages/core/agent-loop/src/index.ts)
<a id="ctxagentpresets--agentpresets"></a>
@ -728,7 +728,7 @@ list(): Agent[]
roots(): Agent[]
```
Source: [`packages/core/agent/src/index.ts:256`](../../packages/core/agent/src/index.ts)
Source: [`packages/core/agent/src/index.ts:257`](../../packages/core/agent/src/index.ts)
<a id="agent-events"></a>
@ -1051,7 +1051,7 @@ A declarative agent entry failed before it could publish a live agent. Consumers
'agent-loop/config-start-failed'(payload: { sessionId: SessionId; error: unknown }): void
```
Source: [`packages/core/agent-loop/src/index.ts:183`](../../packages/core/agent-loop/src/index.ts)
Source: [`packages/core/agent-loop/src/index.ts:242`](../../packages/core/agent-loop/src/index.ts)
<a id="agent-preset-events"></a>

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/goal.md
goal.md: 676b2f49d00681ac7f05b894cda0157ebba4cfcd
goal.zh.md: 1d46bc2c9ba1f3b35026ff132d323e46ccd3bc19
goal.md: e5c8d77e7a897b097e7801f65024612d233319a9
goal.zh.md: a4c8f26173a1ed4b991beaa02de7894258f50b6d

View file

@ -247,7 +247,7 @@ block(agent: Agent, ref: GoalRef, reason: GoalBlockReason): GoalView
Types: [Agent](core.md)
Source: [`packages/goal/goal/src/index.ts:183`](../../packages/goal/goal/src/index.ts)
Source: [`packages/goal/goal/src/index.ts:182`](../../packages/goal/goal/src/index.ts)
<a id="goal-events"></a>

View file

@ -247,7 +247,7 @@ block(agent: Agent, ref: GoalRef, reason: GoalBlockReason): GoalView
Types: [Agent](core.md)
Source: [`packages/goal/goal/src/index.ts:183`](../../packages/goal/goal/src/index.ts)
Source: [`packages/goal/goal/src/index.ts:182`](../../packages/goal/goal/src/index.ts)
<a id="goal-events"></a>

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/permission-presets.md
permission-presets.md: 9fc0fe8d0c347cc8a60b20df04542963aac03bb1
permission-presets.zh.md: 3ae37c6c0cf3c4ef77be41968f446fb4f2199bd9
permission-presets.md: 55f74f86478b96807678a9e0040a45fdd209503f
permission-presets.zh.md: 3e8877d28556eedca9218bd87f85e5bd0768b741

View file

@ -45,7 +45,7 @@ The service requires a confining `ctx.shell` executor and `ctx.approval`, and mi
## Current preset and the derived `custom`
`current(events)` derives the effective preset from the knobs, not from its own event alone: it folds the session's effective sandbox mode (falling back to the executor's configured mode) and effective approval policy (falling back to the approval service config, then `ask`), prefers a still-matching recorded selection, then the first matching table entry in declaration order, and otherwise returns `CUSTOM_PRESET` (`'custom'`). `custom` is derived-only: clients may display it as the current value, but it is never a switch target or an event payload.
`current(session)` derives the effective preset from the required `permissions` projection state, not from its own event alone. The unit folds the session's effective sandbox mode (falling back to the executor's configured mode), effective approval policy (falling back to the approval service config, then `ask`), and recorded selection. The service prefers a still-matching selection, then the first matching table entry in declaration order, and otherwise returns `CUSTOM_PRESET` (`'custom'`). `custom` is derived-only: clients may display it as the current value, but it is never a switch target or an event payload.
`names` lists the switchable presets in table declaration order; `optionOf(name)` builds the option a client renders for a table key (label falls back to the key) or for `custom`, and throws for any other name.
@ -65,7 +65,7 @@ interface PresetOption {
`set(session, name)` resolves the preset (unknown names throw), appends a log-only `permission/preset` event unless `name` is already the effective preset, then writes each knob through its own setter — `setSandboxMode` from [dsh-sandbox-policy](../../packages/sandbox/sandbox-policy) and `setApprovalPolicy` from [dsh-user-approval](../../packages/interaction/user-approval) — only when that knob's effective value changes. The selection event precedes the knob events in the same turn, and re-selecting the effective preset appends nothing at all.
`permission/preset` is durable, log-only user intent: it stays out of the model transcript (the knob events own the model-visible consequences through their consumers), and it exists so `current()` can preserve WHICH preset the user chose when two presets share a bundle; `effectivePermissionPreset(events)` folds the last one, and replay needs no catch-up state. The complete event declaration is in the [persistence log event catalog](../persistence-catalog.md); the method signatures are in the generated [service catalog](#ctxpermissionpresets--permissionpresetservice).
`permission/preset` is durable, log-only user intent: it stays out of the model transcript (the knob events own the model-visible consequences through their consumers), and it exists so `current()` can preserve WHICH preset the user chose when two presets share a bundle. The `permissions` projection folds that selection with both knob events; replay needs no catch-up state. The complete event declaration is in the [persistence log event catalog](../persistence-catalog.md); the method signatures are in the generated [service catalog](#ctxpermissionpresets--permissionpresetservice).
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
@ -86,10 +86,10 @@ Owns the deployment's permission presets and their write path. Requires a confin
* Resolve the preset matching the effective knob values. A still-matching
* last selection wins shared-bundle ties; otherwise the first table match
* wins, or {@link CUSTOM_PRESET} when no entry matches.
* @param events - the session's events in log order.
* @param session - the session whose knob state is read.
* @returns the effective preset name, or `custom` when nothing matches.
*/
current(events: readonly SessionEvent[]): string
current(session: Session): string
/**
* Build the whole select value for one folded knob state: every table
@ -125,7 +125,7 @@ optionOf(name: string): PresetOption
set(session: Session, name: string): void
```
Types: [Session](session.md) · [SessionEvent](session.md)
Types: [Session](session.md)
Source: [`packages/interaction/permission-presets/src/index.ts:175`](../../packages/interaction/permission-presets/src/index.ts)
Source: [`packages/interaction/permission-presets/src/index.ts:135`](../../packages/interaction/permission-presets/src/index.ts)
<!-- END GENERATED cordis-surface -->

View file

@ -45,7 +45,7 @@ interface Config {
## 当前预设与派生的 `custom`
`current(events)` 从 knob 派生实际生效的预设,而不是只看自身事件:它折叠会话的生效沙箱模式(回退到执行器配置的模式)与生效审批策略(先回退到审批服务配置,再回退到 `ask`),优先取仍然匹配的已记录选择,其次取声明顺序中第一个匹配的表项,否则返回 `CUSTOM_PRESET`(`'custom'`)。`custom` 只是派生值:客户端可以把它显示为当前值,但它绝不是切换目标,也绝不出现在事件 payload 中。
`current(session)` 从必需的 `permissions` 投影状态派生实际生效的预设,而不是只看自身事件。该单元折叠会话的生效沙箱模式(回退到执行器配置的模式)、生效审批策略(先回退到审批服务配置,再回退到 `ask`)和已记录选择。服务优先取仍然匹配的选择,其次取声明顺序中第一个匹配的表项,否则返回 `CUSTOM_PRESET`(`'custom'`)。`custom` 只是派生值:客户端可以把它显示为当前值,但它绝不是切换目标,也绝不出现在事件 payload 中。
`names` 按预设表声明顺序列出可切换的预设;`optionOf(name)` 为某个表键(label 回退为该键)或 `custom` 构建客户端渲染的选项,传入其他任何名称都会抛出异常。
@ -65,7 +65,7 @@ interface PresetOption {
`set(session, name)` 解析预设(未知名称抛出异常),在 `name` 尚不是生效预设时追加一条仅记日志的 `permission/preset` 事件,然后通过各旋钮自己的 setter([dsh-sandbox-policy](../../packages/sandbox/sandbox-policy) 的 `setSandboxMode` 与 [dsh-user-approval](../../packages/interaction/user-approval) 的 `setApprovalPolicy`)写入,且仅当该 knob的生效值发生变化时才写。同一轮次内,选择事件先于旋钮事件出现;重新选择当前生效的预设则什么都不追加。
`permission/preset` 是持久、仅记日志的用户意图:它不进入模型 transcript(文本记录),模型可见的后果由 knob 事件经各自消费方承担;它存在是为了在两个预设共享同一个旋钮组合时,让 `current()` 仍能保住用户选择的究竟是哪一个预设;`effectivePermissionPreset(events)` 折叠最后一条,回放不需要任何追赶状态。完整事件声明见[持久化日志事件目录](../persistence-catalog.md);方法签名见生成的[服务目录](#ctxpermissionpresets--permissionpresetservice)。
`permission/preset` 是持久、仅记日志的用户意图:它不进入模型 transcript(文本记录),模型可见的后果由 knob 事件经各自消费方承担;它存在是为了在两个预设共享同一个旋钮组合时,让 `current()` 仍能保住用户选择的究竟是哪一个预设。`permissions` 投影把该选择与两个 knob 事件一同折叠,回放不需要任何追赶状态。完整事件声明见[持久化日志事件目录](../persistence-catalog.md);方法签名见生成的[服务目录](#ctxpermissionpresets--permissionpresetservice)。
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
@ -86,10 +86,10 @@ Owns the deployment's permission presets and their write path. Requires a confin
* Resolve the preset matching the effective knob values. A still-matching
* last selection wins shared-bundle ties; otherwise the first table match
* wins, or {@link CUSTOM_PRESET} when no entry matches.
* @param events - the session's events in log order.
* @param session - the session whose knob state is read.
* @returns the effective preset name, or `custom` when nothing matches.
*/
current(events: readonly SessionEvent[]): string
current(session: Session): string
/**
* Build the whole select value for one folded knob state: every table
@ -125,7 +125,7 @@ optionOf(name: string): PresetOption
set(session: Session, name: string): void
```
Types: [Session](session.md) · [SessionEvent](session.md)
Types: [Session](session.md)
Source: [`packages/interaction/permission-presets/src/index.ts:175`](../../packages/interaction/permission-presets/src/index.ts)
Source: [`packages/interaction/permission-presets/src/index.ts:135`](../../packages/interaction/permission-presets/src/index.ts)
<!-- END GENERATED cordis-surface -->

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/plan.md
plan.md: 11abe27f2bfa0f2d32bd9d37600792ba915d63fb
plan.zh.md: 47fa160beb4651850a44b325edb6700b116855a4
plan.md: e8e84bc92792198ca56f0abb7e208ff33f5c0ab6
plan.zh.md: 1a848085342a2bceeda6515fde529bf2d7f5d5e1

View file

@ -8,7 +8,7 @@ Source: [`packages/plan/plan-mode/src/index.ts`](../../packages/plan/plan-mode/s
## Logged state and recovery
`plan/mode` (`{ active: boolean }`) is a log-only, whole-value-replace [session event](session.md): durable and replayable, never in the model transcript. `foldPlanMode(events, end?)` returns the last logged value in the prefix, or `false` when there is none — the state in force is always a pure fold of the session log, so resume, fork, and compaction recover it with no live mirror, and UIs observe committed flips through `session/event`. The complete event declaration is in the [persistence log event catalog](../persistence-catalog.md).
`plan/mode` (`{ active: boolean }`) is a log-only, whole-value-replace [session event](session.md): durable and replayable, never in the model transcript. The required `plan` session-projection unit folds committed mode, command settlement, and the mode recorded at the latest request header. `ctx.planMode` reads that host state through `stateOf()`, while clients receive only `{ active, pending }`; resume, fork, and compaction recover both from the log. The complete event declaration is in the [persistence log event catalog](../persistence-catalog.md).
## Pending selections and the pre-step append
@ -50,7 +50,7 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp
### `ctx.planMode` — `PlanModeController`
`ctx.planMode`: owns logged plan state, applies and narrates selected state at step start, the `plan:policy` section, the `/plan` command, and the stable exit tool. UIs observe committed flips through `session/event`; there is no live mirror.
`ctx.planMode`: owns logged plan state, applies and narrates selected state at step start, the `plan:policy` section, the `/plan` command, and the stable exit tool. Client carriers expose the projection's cropped `{ active, pending }` view.
```ts cordis-catalog
/**
@ -83,5 +83,5 @@ set(agent: Agent, active: boolean): 'committed' | 'queued' | 'cancelled' | 'noop
Types: [Agent](core.md)
Source: [`packages/plan/plan-mode/src/index.ts:199`](../../packages/plan/plan-mode/src/index.ts)
Source: [`packages/plan/plan-mode/src/index.ts:169`](../../packages/plan/plan-mode/src/index.ts)
<!-- END GENERATED cordis-surface -->

View file

@ -8,7 +8,7 @@
## 已记录状态与恢复
`plan/mode`(`{ active: boolean }`)是仅记日志、整值替换的[会话事件](session.md):持久且可回放,绝不进入模型 transcript(文本记录)。`foldPlanMode(events, end?)` 返回前缀中最后一条已记录值,没有时返回 `false`:生效状态始终是会话日志的纯折叠,因此恢复、fork 与压缩(compaction)无需实时镜像即可将其复原,UI 通过 `session/event` 观察已提交的切换。完整事件声明见[持久化日志事件目录](../persistence-catalog.md)。
`plan/mode`(`{ active: boolean }`)是仅记日志、整值替换的[会话事件](session.md):持久且可回放,绝不进入模型 transcript(文本记录)。必需的 `plan` 会话投影单元折叠已提交模式、命令结算结果和最近一次请求头记录的模式。`ctx.planMode` 通过 `stateOf()` 读取该 host 状态,而客户端只接收 `{ active, pending }`;恢复、fork 与压缩(compaction)都能从日志恢复两者。完整事件声明见[持久化日志事件目录](../persistence-catalog.md)。
## 待生效选择与 pre-step 追加
@ -50,7 +50,7 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp
### `ctx.planMode` — `PlanModeController`
`ctx.planMode`: owns logged plan state, applies and narrates selected state at step start, the `plan:policy` section, the `/plan` command, and the stable exit tool. UIs observe committed flips through `session/event`; there is no live mirror.
`ctx.planMode`: owns logged plan state, applies and narrates selected state at step start, the `plan:policy` section, the `/plan` command, and the stable exit tool. Client carriers expose the projection's cropped `{ active, pending }` view.
```ts cordis-catalog
/**
@ -83,5 +83,5 @@ set(agent: Agent, active: boolean): 'committed' | 'queued' | 'cancelled' | 'noop
Types: [Agent](core.md)
Source: [`packages/plan/plan-mode/src/index.ts:199`](../../packages/plan/plan-mode/src/index.ts)
Source: [`packages/plan/plan-mode/src/index.ts:169`](../../packages/plan/plan-mode/src/index.ts)
<!-- END GENERATED cordis-surface -->

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/sandbox.md
sandbox.md: 19f1807cf4fc7fa5e7ae0843daeee818a7e2bdde
sandbox.zh.md: 78ee0f162d1179abea723465bfd462ef9b9e255e
sandbox.md: bb35f3d655844e65fd30f1016fa31aec55ed8bc1
sandbox.zh.md: bf8567944ec5789371002d376eb5181d4770381b

View file

@ -190,7 +190,7 @@ Source: [`packages/sandbox/sandbox/src/index.ts:158`](../../packages/sandbox/san
### `ctx.sandboxPolicy` — `SandboxPolicyService`
The sandbox-policy service (`ctx.sandboxPolicy`). Owns the deployment default mode, fallback workspace root, and current request-time policy section. Tool layers call resolve for each execution so a session's mode log and immutable cwd travel together to every enforcing capability.
The sandbox policy seam: the deployment default mode and workspace-write root, with per-session overrides folded from the log.
```ts cordis-catalog
/**
@ -214,5 +214,5 @@ overrideOf(session: Session): SandboxMode | undefined
Types: [Session](session.md)
Source: [`packages/sandbox/sandbox-policy/src/index.ts:91`](../../packages/sandbox/sandbox-policy/src/index.ts)
Source: [`packages/sandbox/sandbox-policy/src/index.ts:108`](../../packages/sandbox/sandbox-policy/src/index.ts)
<!-- END GENERATED cordis-surface -->

View file

@ -190,7 +190,7 @@ Source: [`packages/sandbox/sandbox/src/index.ts:158`](../../packages/sandbox/san
### `ctx.sandboxPolicy` — `SandboxPolicyService`
The sandbox-policy service (`ctx.sandboxPolicy`). Owns the deployment default mode, fallback workspace root, and current request-time policy section. Tool layers call resolve for each execution so a session's mode log and immutable cwd travel together to every enforcing capability.
The sandbox policy seam: the deployment default mode and workspace-write root, with per-session overrides folded from the log.
```ts cordis-catalog
/**
@ -214,5 +214,5 @@ overrideOf(session: Session): SandboxMode | undefined
Types: [Session](session.md)
Source: [`packages/sandbox/sandbox-policy/src/index.ts:91`](../../packages/sandbox/sandbox-policy/src/index.ts)
Source: [`packages/sandbox/sandbox-policy/src/index.ts:108`](../../packages/sandbox/sandbox-policy/src/index.ts)
<!-- END GENERATED cordis-surface -->

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/session-projection.md
session-projection.md: c0e75ddad59cd764025269fef1f79f07472b74fe
session-projection.zh.md: 24c96b6055a1f89ee256253c3005e9ca100a4f90
session-projection.md: 3a354c081e38d23eb5ba337f3d4973788f1fe124
session-projection.zh.md: 378b22faff90d15b3e34dc31112597a64a3a7b44

View file

@ -2,7 +2,7 @@
English | [中文](session-projection.zh.md)
The session-projection seam — a [capability seam](../capability-seams.md) through which domain host plugins serve whole current values of log-derived per-session state to client carriers: the Service Definition and registry ([dsh-session-projection](../../packages/session/session-projection), `ctx.sessionProjections`), domain contributors (each registering one pure unit), and carriers ([dsh-host-apiproxy](../../packages/host/apiproxy)'s history tail page and `session/projection` push frame). It is one optional capability, not part of the agent-loop spine. The framework drives, the domain computes: the registry subscribes to `session/event` once and folds every committed event through every unit; domains hold no subscriptions and clients never fold domain events — they receive finished values. Design authority: the [session-projection RFC](../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md); drive/cache/feed contracts: the [package README](../../packages/session/session-projection/README.md).
The session-projection [capability seam](../capability-seams.md) serves log-derived per-session state to host consumers and client carriers. Its registry ([dsh-session-projection](../../packages/session/session-projection), `ctx.sessionProjections`) drives pure domain units; [dsh-host-apiproxy](../../packages/host/apiproxy) carries selected values on history/list baselines and `session/projection` frames. Plugins that contribute or read units require the registry. Design authority: the [session-projection RFC](../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md); drive/cache/feed contracts: the [package README](../../packages/session/session-projection/README.md).
Source: [`packages/session/session-projection/src/index.ts`](../../packages/session/session-projection/src/index.ts)
@ -100,7 +100,7 @@ type ProjectionChangeListener = (
## The registry: `ctx.sessionProjections`
`SessionProjectionRegistry` ([signatures](#ctxsessionprojections--sessionprojectionregistry)) owns the drive: one `session/event` subscription, eager `apply` over every registered unit, and per-session per-unit watermark cells. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Registration is an effect whose disposer rides the calling fiber: an unloaded domain plugin's key (with its cached cells) disappears from subsequent drives and snapshots, and clients read that as capability absence; duplicate keys throw. Domain plugins register under `ctx.inject(['sessionProjections'], …)` so headless assemblies without the registry stay unaffected.
`SessionProjectionRegistry` ([signatures](#ctxsessionprojections--sessionprojectionregistry)) owns the drive: one `session/event` subscription, eager `apply` over every registered unit, and per-session per-unit watermark cells. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Registration is an effect whose disposer rides the calling fiber: an unloaded domain plugin's key (with its cached cells) disappears from subsequent drives and snapshots, and clients read that as capability absence. Unit contributors and host readers require the registry, so an incomplete composition fails during activation ([decision](../../.agents/notes/implemented/architecture/2026-08-07-session-projection-mandatory-seam.md)). Registrants of the same compatible definition share the unit until the last registration is disposed.
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
@ -165,7 +165,7 @@ Source: [`packages/session/session-projection-cache/src/index.ts:71`](../../pack
### `ctx.sessionProjections` — `SessionProjectionRegistry`
`ctx.sessionProjections`: the projection unit table and its drive. The service subscribes to `session/event` once; every committed event passes every registered unit's `apply` (eager drive), and a changed state reference notifies the change feed with the schema-validated view. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Registration is an effect (disposer rides the calling fiber): an unloaded domain plugin's key disappears from snapshots and clients read it as capability absence. Domain plugins register under `ctx.inject(['sessionProjections'], …)` so headless assemblies without the registry stay unaffected. Registrants sharing a key share one unit and are counted: the same tool package mounted in N agent presets registers N times, and the key survives until the last one unloads.
`ctx.sessionProjections`: the projection unit table and its drive. The service subscribes to `session/event` once; every committed event passes every registered unit's `apply` (eager drive), and a changed state reference notifies the change feed with the schema-validated view. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Registration is an effect (disposer rides the calling fiber): an unloaded domain plugin's key disappears from snapshots and clients read it as capability absence. Unit contributors and host readers require this service, so incomplete compositions fail during activation. Registrants sharing a key share one unit and are counted: the same tool package mounted in N agent presets registers N times, and the key survives until the last one unloads.
```ts cordis-catalog
/**

View file

@ -2,7 +2,7 @@
[English](session-projection.md) | 中文
会话投影 seam 是一项[能力 seam](../capability-seams.md):领域 host 插件经由它向客户端载体供给按会话的日志派生状态的当前全量值;三方分别是 Service Definition 与注册表([dsh-session-projection](../../packages/session/session-projection),`ctx.sessionProjections`)、领域贡献方(每个领域注册一个纯单元)与载体([dsh-host-apiproxy](../../packages/host/apiproxy) 的历史尾页与 `session/projection` 推送帧)。它是一项可选能力,不属于 agent loop(智能体循环)主干。框架负责驱动,领域负责计算:注册表只订阅一次 `session/event`,并把每个已提交事件折叠进每个单元;领域不持有任何订阅,客户端也从不折叠领域事件——它们收到的是成品值。设计权威:[session-projection RFC](../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md);驱动、缓存与变更流约定:[包 README](../../packages/session/session-projection/README.md)。
会话投影[能力 seam](../capability-seams.md)向 host 消费方和客户端载体提供按会话的日志派生状态。其注册表([dsh-session-projection](../../packages/session/session-projection),`ctx.sessionProjections`)驱动纯领域单元;[dsh-host-apiproxy](../../packages/host/apiproxy)在历史/列表基线和 `session/projection` 帧中承载选定值。贡献或读取单元的插件要求该注册表。设计权威:[session-projection RFC](../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md);驱动、缓存与变更流约定:[包 README](../../packages/session/session-projection/README.md)。
源码:[`packages/session/session-projection/src/index.ts`](../../packages/session/session-projection/src/index.ts)
@ -100,7 +100,7 @@ type ProjectionChangeListener = (
## 注册表:`ctx.sessionProjections`
`SessionProjectionRegistry`([签名](#ctxsessionprojections--sessionprojectionregistry))拥有驱动权:一份 `session/event` 订阅、对每个已注册单元即时调用 `apply`,以及每会话每单元的水位线(watermark)cell。cell 惰性构建:在事件流过之后才注册的单元,或比注册表更早的会话,都在首次触达(事件或读取)时从 `init` 出发在内存日志上折叠。注册是一个 effect,其 disposer 随调用方 fiber 走:领域插件卸载后,其 key(连同缓存的 cell)从后续驱动与快照中消失,客户端将其读作能力缺失;key 重复直接 throw。领域插件在 `ctx.inject(['sessionProjections'], …)` 下注册,因此不带注册表的 headless 组装完全不受影响。
`SessionProjectionRegistry`([签名](#ctxsessionprojections--sessionprojectionregistry))拥有驱动权:一份 `session/event` 订阅、对每个已注册单元即时调用 `apply`,以及每会话每单元的水位线(watermark)cell。cell 惰性构建:在事件流过之后才注册的单元,或比注册表更早的会话,都在首次触达(事件或读取)时从 `init` 出发在内存日志上折叠。注册是一个 effect,其 disposer 随调用方 fiber 走:领域插件卸载后,其 key(连同缓存的 cell)从后续驱动与快照中消失,客户端将其读作能力缺失。单元贡献方与 host 读取方要求该注册表,因此不完整的组合会在激活时失败([决策](../../.agents/notes/implemented/architecture/2026-08-07-session-projection-mandatory-seam.md))。相同且兼容定义的多个注册者共享该单元,直到最后一项注册被 dispose。
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
@ -165,7 +165,7 @@ Source: [`packages/session/session-projection-cache/src/index.ts:71`](../../pack
### `ctx.sessionProjections` — `SessionProjectionRegistry`
`ctx.sessionProjections`: the projection unit table and its drive. The service subscribes to `session/event` once; every committed event passes every registered unit's `apply` (eager drive), and a changed state reference notifies the change feed with the schema-validated view. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Registration is an effect (disposer rides the calling fiber): an unloaded domain plugin's key disappears from snapshots and clients read it as capability absence. Domain plugins register under `ctx.inject(['sessionProjections'], …)` so headless assemblies without the registry stay unaffected. Registrants sharing a key share one unit and are counted: the same tool package mounted in N agent presets registers N times, and the key survives until the last one unloads.
`ctx.sessionProjections`: the projection unit table and its drive. The service subscribes to `session/event` once; every committed event passes every registered unit's `apply` (eager drive), and a changed state reference notifies the change feed with the schema-validated view. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Registration is an effect (disposer rides the calling fiber): an unloaded domain plugin's key disappears from snapshots and clients read it as capability absence. Unit contributors and host readers require this service, so incomplete compositions fail during activation. Registrants sharing a key share one unit and are counted: the same tool package mounted in N agent presets registers N times, and the key survives until the last one unloads.
```ts cordis-catalog
/**

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/session-title.md
session-title.md: 6952cd7289861e1f83e76ef7ae9a115a404ee671
session-title.zh.md: 37442e258325e23fe6d3b288dd8c680efd2d0ca4
session-title.md: d872086500cf477cfa88f8eb756fe9b7be5fc571
session-title.zh.md: 01aa62a24320e9c9329641603529c1ced9da4360

View file

@ -8,7 +8,7 @@ Sources: [`packages/session/session-title/src/index.ts`](../../packages/session/
## Durable title state
`SessionTitleProviderId` is recorded for provider-produced revisions. `SessionTitleEventData` lists the exact human-message seqs used for the title, while `SessionTitleSnapshot` adds the durable event envelope facts selected by `foldSessionTitle()`.
`SessionTitleProviderId` is recorded for provider-produced revisions. `SessionTitleEventData` lists the exact human-message seqs used for the title, while `SessionTitleSnapshot` adds the durable event envelope facts retained in the `title` projection state. `ctx.sessionTitle.get()` reads that state through `stateOf()`; the client view remains only the title string or `null`. `foldSessionTitle()` provides the same selection for detached logs.
```ts type-equiv
/** Identifies one session-title provider registration. */
@ -200,5 +200,5 @@ register(provider: SessionTitleProvider): () => Promise<void>
Types: [Session](session.md)
Source: [`packages/session/session-title/src/index.ts:261`](../../packages/session/session-title/src/index.ts)
Source: [`packages/session/session-title/src/index.ts:282`](../../packages/session/session-title/src/index.ts)
<!-- END GENERATED cordis-surface -->

View file

@ -8,7 +8,7 @@
## 持久标题状态
提供方生成修订时会记录 `SessionTitleProviderId`。`SessionTitleEventData` 列出生成标题时使用的精确人类消息 seq,`SessionTitleSnapshot` 则加入 `foldSessionTitle()` 选出的持久事件封装信息。
提供方生成修订时会记录 `SessionTitleProviderId`。`SessionTitleEventData` 列出生成标题时使用的精确人类消息 seq,`SessionTitleSnapshot` 则加入 `title` 投影状态保留的持久事件封装信息。`ctx.sessionTitle.get()` 通过 `stateOf()` 读取该状态;客户端视图仍只包含标题字符串或 `null`。`foldSessionTitle()` 为脱离服务的日志提供相同选择。
```ts type-equiv
/** Identifies one session-title provider registration. */
@ -200,5 +200,5 @@ register(provider: SessionTitleProvider): () => Promise<void>
Types: [Session](session.md)
Source: [`packages/session/session-title/src/index.ts:261`](../../packages/session/session-title/src/index.ts)
Source: [`packages/session/session-title/src/index.ts:282`](../../packages/session/session-title/src/index.ts)
<!-- END GENERATED cordis-surface -->

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/subagent.md
subagent.md: fee5f95d4a8810959e452653ecf9d7bcf36b7387
subagent.zh.md: 38136f27172c071af95bf3b3cce7986af499368f
subagent.md: e998c2fbea2d2e2a89798275466a4c00a205e895
subagent.zh.md: 0eaf927552b6e19ab4f7b58a47c2409da44498d3

View file

@ -286,7 +286,7 @@ A local one-shot provider appends the descriptor inside the child's initial turn
## Durable enumeration: `listChildren()`, `listDescendants()`, and their entries
`SubagentRuntime.listChildren(parentSessionId)` enumerates the parent's direct session-backed subagents from the live-preferred merge of `ctx.sessions.list()` and optional `ctx.sessionPersistence.list()` — no query service, and no Agent is loaded or resumed. Candidates are the direct children whose durable header carries `origin: 'subagent'`; the marker classifies enumeration and coarse generic-route denial but cannot establish a valid descriptor, resumability, or authorization — the projection fold owns identity, and the Activation contract owns resume. Each row's `mode`/`label` is the registered `subagent` projection unit's value, served through a three-rung ladder: the registry's watermark cache for a live child (zero log reads); the optional projection checkpoint cache for a cold one (`cachedSnapshot` — an identity passing the own-suffix seq gate is final, because an own descriptor is immutable once appended); otherwise one `persistence.inspect()` reading folded through the registry (bounded concurrency, recomputed per listing). The cache is a pure optional accelerator: absent, serving the `null` sentinel or missing the key, failing the seq gate, or faulting, it falls silently through to the authoritative refold. The fold is `subagent/descriptor` last-wins with no failure channel: the child's own descriptor overrides a fork-seeded ancestor's, and a malformed or unknown-version payload folds to a serializable `null` sentinel, treated as no value. The result is one `SubagentListEntry[]` in `createdAt`-then-id order: a served identity yields a `child` entry with `mode: 'one-shot' | 'continuable'` and `activity: 'running' | 'inactive'`; continuable entries always carry `label`, while one-shot entries carry it only when the start caller supplied presentation metadata. A settled candidate whose fold served no identity yields a `corrupt` diagnostic — missing, malformed, and unknown-version descriptors deliberately undistinguished (`unsupported` remains in the type but is never produced); a running candidate without an identity is omitted (the creation window before its descriptor lands); a failed cold inspection yields one `unavailable` diagnostic retried on the next listing, so one damaged sibling cannot hide healthy children. `hasChildren` marks a direct descendant with durable subagent origin, read from the same merged material. Activity snapshots only whether the logical record is live in `ctx.sessions`, not outcome or resumability. Absent persistence, enumeration is live-only rather than an error — a cold child cannot be resumed then either. `listChildren()` throws `SubagentError` with code `SUBAGENT_CONTROL_PROJECTIONS_UNAVAILABLE` when the `ctx.sessionProjections` registry is absent and `SUBAGENT_CONTROL_SESSION_STORE_UNAVAILABLE` when the session store is, both checked before any read so a deployment with zero children still fails deterministically; the list tool requires `ctx.subagents` and `ctx.agents` at plugin load. A service consumer such as a UI can display both modes and choose an unlabeled one-shot fallback, while the model-facing `list_agents` adapter (the separately loadable `/list-agents` plugin of [dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control)) keeps only continuable entries and refines status through the live Agent registry into its own `running`/`idle`/`ready` vocabulary, whose `ready` names a storage-only child as resumable rather than terminal. Listing does not consult the continuation manager's Activation map, Agent registry, or provider availability; `send_message` remains the authoritative delivery-time operation, and a listed running continuable child may still reject delivery as an ownership conflict. The read-path rationale lives in [the list-identity-projection Agent Note](../../.agents/notes/implemented/architecture/2026-08-06-subagent-list-identity-projection.md).
`SubagentRuntime.listChildren(parentSessionId)` enumerates the parent's direct session-backed subagents from the live-preferred merge of `ctx.sessions.list()` and optional `ctx.sessionPersistence.list()` — no query service, and no Agent is loaded or resumed. Candidates are the direct children whose durable header carries `origin: 'subagent'`; the marker classifies enumeration and coarse generic-route denial but cannot establish a valid descriptor, resumability, or authorization — the projection fold owns identity, and the Activation contract owns resume. Each row's `mode`/`label` is the registered `subagent` projection unit's value, served through a three-rung ladder: `stateOf()` for a live child (zero log reads); the optional projection checkpoint cache for a cold one (`cachedSnapshot` — an identity passing the own-suffix seq gate is final, because an own descriptor is immutable once appended); otherwise one `persistence.inspect()` reading folded through the registry (bounded concurrency, recomputed per listing). The cache is a pure optional accelerator: absent, serving the `null` sentinel or missing the key, failing the seq gate, or faulting, it falls silently through to the authoritative refold. The fold is `subagent/descriptor` last-wins with no failure channel: the child's own descriptor overrides a fork-seeded ancestor's, and a malformed or unknown-version payload folds to a serializable `null` sentinel, treated as no value. The result is one `SubagentListEntry[]` in `createdAt`-then-id order: a served identity yields a `child` entry with `mode: 'one-shot' | 'continuable'` and `activity: 'running' | 'inactive'`; continuable entries always carry `label`, while one-shot entries carry it only when the start caller supplied presentation metadata. A settled candidate whose fold served no identity yields a `corrupt` diagnostic — missing, malformed, and unknown-version descriptors deliberately undistinguished (`unsupported` remains in the type but is never produced); a running candidate without an identity is omitted (the creation window before its descriptor lands); a failed cold inspection yields one `unavailable` diagnostic retried on the next listing, so one damaged sibling cannot hide healthy children. `hasChildren` marks a direct descendant with durable subagent origin, read from the same merged material. Activity snapshots only whether the logical record is live in `ctx.sessions`, not outcome or resumability. Absent persistence, enumeration is live-only rather than an error — a cold child cannot be resumed then either. `listChildren()` requires the runtime's projection registry and throws `SubagentError` with code `SUBAGENT_CONTROL_SESSION_STORE_UNAVAILABLE` when the session store is absent, checked before any read so a deployment with zero children still fails deterministically; the list tool requires `ctx.subagents` and `ctx.agents` at plugin load. A service consumer such as a UI can display both modes and choose an unlabeled one-shot fallback, while the model-facing `list_agents` adapter (the separately loadable `/list-agents` plugin of [dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control)) keeps only continuable entries and refines status through the live Agent registry into its own `running`/`idle`/`ready` vocabulary, whose `ready` names a storage-only child as resumable rather than terminal. Listing does not consult the continuation manager's Activation map, Agent registry, or provider availability; `send_message` remains the authoritative delivery-time operation, and a listed running continuable child may still reject delivery as an ownership conflict. The read-path rationale lives in [the list-identity-projection Agent Note](../../.agents/notes/implemented/architecture/2026-08-06-subagent-list-identity-projection.md).
`SubagentRuntime.listDescendants(rootSessionId)` applies the same live-preferred corpus and projection-backed interpretation to the root's complete descendant tree in stable pre-order. Ordinary sessions and one-shot children remain traversal nodes, so continuable descendants below them are discovered; only `origin: 'subagent'` candidates produce rows. Each returned child or diagnostic adds its position from the enumerated durable header, while a cold inspection revalidates that complete lifecycle before serving identity:
@ -591,7 +591,7 @@ async drainContinuableChildren(parent: Agent, childIds: readonly SessionId[]): P
* row when the optional cache serves an own-suffix identity (its `seq`
* gate proves the value postdates the fork seed, where a child's own
* descriptor is immutable once appended), else one persistence inspection
* folded through the registry. The
* folded through the same unit. The
* projection fold is the single classification authority; per-child
* diagnostics relay a fold that served no identity or a failed inspection,
* never a list-time descriptor parse. Absent persistence, enumeration is
@ -606,8 +606,8 @@ async drainContinuableChildren(parent: Agent, childIds: readonly SessionId[]): P
* @param signal - caller-owned cancellation forwarded to persistence reads
* and observed around every read await.
* @returns children and per-child diagnostics ordered by `createdAt`, then id.
* @throws {@link SubagentError} when the projection registry or the session
* store is not mounted, or the caller cancels the listing.
* @throws {@link SubagentError} when the session store is not mounted, or
* the caller cancels the listing.
*/
listChildren(parentSessionId: SessionId, signal?: AbortSignal): Promise<SubagentListEntry[]>

View file

@ -286,7 +286,7 @@ interface ContinuableCreateSpec {
## 持久化枚举:`listChildren()`、`listDescendants()` 与其条目
`SubagentRuntime.listChildren(parentSessionId)` 从 `ctx.sessions.list()` 与可选 `ctx.sessionPersistence.list()` 的实时优先合并中枚举 parent 直接且由会话支撑的 subagent——不经查询服务,也不会加载或恢复任何 Agent。候选是持久 header 携带 `origin: 'subagent'` 的直接 child;该标记只负责枚举分类与粗粒度的通用路由拒绝,不能证明描述符有效、child 可恢复或操作已获授权——身份由投影折叠负责,恢复由 Activation 约定负责。每行的 `mode`/`label` 是已注册 `subagent` projection unit 的值,经三级阶梯供值:存活 child 由注册表水位缓存供值(零日志读取);冷 child 先读可选的投影 checkpoint 缓存(`cachedSnapshot`——过 own-suffix seq 门的身份即定值,own descriptor 一经追加不可变);否则在一次 `persistence.inspect()` 读取上经注册表折叠(有界并发,每次列表重新计算)。该缓存是纯可选加速层:服务缺席、行里是 `null` 哨兵或 key 缺席、seq 门不过、读取出错,都静默落到权威重折。折叠规则是 `subagent/descriptor` last-wins 且没有失败通道:子 agent 自己的描述符覆盖 fork seed 中祖先的描述符,格式错误或版本不认识的载荷折叠为可序列化的 `null` 哨兵,视同无值。结果是按 `createdAt`、再按 id 排序的 `SubagentListEntry[]`:取到身份即生成带有 `mode: 'one-shot' | 'continuable'` 和 `activity: 'running' | 'inactive'` 的 `child` 条目;可继续条目始终携带 `label`,一次性条目则只在启动调用方提供展示元数据时携带该字段。已定局而折叠无身份的候选生成 `corrupt` diagnostic——缺失、格式错误与版本不认识的描述符有意不再细分(`unsupported` 仍保留在类型中但从不产出);运行中而无身份的候选被省略(描述符落盘前的创建窗口);冷检查失败生成一条 `unavailable` diagnostic 并在下次列表自然重试,因此一个损坏的 sibling 不会隐藏健康 child。`hasChildren` 标记存在持久 subagent origin 的直接后代,读取自同一份合并材料。活动状态只表示逻辑记录是否在 `ctx.sessions` 中存活,而不表示结果或可恢复性。缺少持久化时,枚举退化为仅存活枚举而不是报错——此时冷 child 本就无法恢复。缺少 `ctx.sessionProjections` 注册表时,`listChildren()` 抛出携带错误码 `SUBAGENT_CONTROL_PROJECTIONS_UNAVAILABLE` 的 `SubagentError`,缺少会话存储时则抛出 `SUBAGENT_CONTROL_SESSION_STORE_UNAVAILABLE`,两者都在任何读取之前检查,因此零 child 的部署同样确定失败;列表工具在插件加载时要求 `ctx.subagents` 与 `ctx.agents`。UI 等服务消费方可以展示两种模式,并为无标签的一次性 child 选择回退展示;面向模型的 `list_agents` 适配器([dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control) 中可单独加载的 `/list-agents` 插件)则只保留可继续条目,并通过在线 Agent 注册表将状态细化为自己的 `running`/`idle`/`ready` 词汇,其中 `ready` 把仅存于存储的 child 命名为可恢复而非终态。枚举不会查询继续执行管理器的 Activation map、Agent 注册表或提供方可用性;`send_message` 仍是消息送达时的权威操作,列表中的运行中可继续 child 仍可能因所有权冲突而拒绝投递。读路径的设计理由见[列表身份投影 Agent Note](../../.agents/notes/implemented/architecture/2026-08-06-subagent-list-identity-projection.md)。
`SubagentRuntime.listChildren(parentSessionId)` 从 `ctx.sessions.list()` 与可选 `ctx.sessionPersistence.list()` 的实时优先合并中枚举 parent 直接且由会话支撑的 subagent——不经查询服务,也不会加载或恢复任何 Agent。候选是持久 header 携带 `origin: 'subagent'` 的直接 child;该标记只负责枚举分类与粗粒度的通用路由拒绝,不能证明描述符有效、child 可恢复或操作已获授权——身份由投影折叠负责,恢复由 Activation 约定负责。每行的 `mode`/`label` 是已注册 `subagent` projection unit 的值,经三级阶梯供值:存活 child 通过 `stateOf()` 供值(零日志读取);冷 child 先读可选的投影 checkpoint 缓存(`cachedSnapshot`——过 own-suffix seq 门的身份即定值,own descriptor 一经追加不可变);否则在一次 `persistence.inspect()` 读取上经注册表折叠(有界并发,每次列表重新计算)。该缓存是纯可选加速层:服务缺席、行里是 `null` 哨兵或 key 缺席、seq 门不过、读取出错,都静默落到权威重折。折叠规则是 `subagent/descriptor` last-wins 且没有失败通道:子 agent 自己的描述符覆盖 fork seed 中祖先的描述符,格式错误或版本不认识的载荷折叠为可序列化的 `null` 哨兵,视同无值。结果是按 `createdAt`、再按 id 排序的 `SubagentListEntry[]`:取到身份即生成带有 `mode: 'one-shot' | 'continuable'` 和 `activity: 'running' | 'inactive'` 的 `child` 条目;可继续条目始终携带 `label`,一次性条目则只在启动调用方提供展示元数据时携带该字段。已定局而折叠无身份的候选生成 `corrupt` diagnostic——缺失、格式错误与版本不认识的描述符有意不再细分(`unsupported` 仍保留在类型中但从不产出);运行中而无身份的候选被省略(描述符落盘前的创建窗口);冷检查失败生成一条 `unavailable` diagnostic 并在下次列表自然重试,因此一个损坏的 sibling 不会隐藏健康 child。`hasChildren` 标记存在持久 subagent origin 的直接后代,读取自同一份合并材料。活动状态只表示逻辑记录是否在 `ctx.sessions` 中存活,而不表示结果或可恢复性。缺少持久化时,枚举退化为仅存活枚举而不是报错——此时冷 child 本就无法恢复。`listChildren()` 要求 runtime 的投影注册表;缺少会话存储时抛出携带错误码 `SUBAGENT_CONTROL_SESSION_STORE_UNAVAILABLE` 的 `SubagentError`,并在任何读取之前检查,因此零 child 的部署同样确定失败;列表工具在插件加载时要求 `ctx.subagents` 与 `ctx.agents`。UI 等服务消费方可以展示两种模式,并为无标签的一次性 child 选择回退展示;面向模型的 `list_agents` 适配器([dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control) 中可单独加载的 `/list-agents` 插件)则只保留可继续条目,并通过在线 Agent 注册表将状态细化为自己的 `running`/`idle`/`ready` 词汇,其中 `ready` 把仅存于存储的 child 命名为可恢复而非终态。枚举不会查询继续执行管理器的 Activation map、Agent 注册表或提供方可用性;`send_message` 仍是消息送达时的权威操作,列表中的运行中可继续 child 仍可能因所有权冲突而拒绝投递。读路径的设计理由见[列表身份投影 Agent Note](../../.agents/notes/implemented/architecture/2026-08-06-subagent-list-identity-projection.md)。
`SubagentRuntime.listDescendants(rootSessionId)` 将同一份实时优先语料与基于投影的解释应用到根的完整后代树,并按稳定 pre-order 输出。普通会话和一次性 child 仍作为遍历节点,因此其下的可继续后代仍可发现;只有 `origin: 'subagent'` 的候选会生成条目。每个返回的 child 或 diagnostic 都从枚举所得的持久 header 附加树位置;冷检查在提供身份前还会重新校验完整生命周期:
@ -593,7 +593,7 @@ async drainContinuableChildren(parent: Agent, childIds: readonly SessionId[]): P
* row when the optional cache serves an own-suffix identity (its `seq`
* gate proves the value postdates the fork seed, where a child's own
* descriptor is immutable once appended), else one persistence inspection
* folded through the registry. The
* folded through the same unit. The
* projection fold is the single classification authority; per-child
* diagnostics relay a fold that served no identity or a failed inspection,
* never a list-time descriptor parse. Absent persistence, enumeration is
@ -608,8 +608,8 @@ async drainContinuableChildren(parent: Agent, childIds: readonly SessionId[]): P
* @param signal - caller-owned cancellation forwarded to persistence reads
* and observed around every read await.
* @returns children and per-child diagnostics ordered by `createdAt`, then id.
* @throws {@link SubagentError} when the projection registry or the session
* store is not mounted, or the caller cancels the listing.
* @throws {@link SubagentError} when the session store is not mounted, or
* the caller cancels the listing.
*/
listChildren(parentSessionId: SessionId, signal?: AbortSignal): Promise<SubagentListEntry[]>

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/token-meter.md
token-meter.md: a1a096b800d895cabe9e05b751e23f06cea67d67
token-meter.zh.md: 16e13e3d168c3cd67bdf12903045dd9b4056402f
token-meter.md: 180da591e6e123e07c5249cd88d9e59fd3e448b3
token-meter.zh.md: b8b82fcefca836796cd3d987af863afe80b0700b

View file

@ -86,5 +86,5 @@ estimateMessage(message: Message): number
Types: [EpochHeader](session.md) · [Message](llm-streaming.md) · [Session](session.md)
Source: [`packages/llm/token-meter/src/index.ts:74`](../../packages/llm/token-meter/src/index.ts)
Source: [`packages/llm/token-meter/src/index.ts:78`](../../packages/llm/token-meter/src/index.ts)
<!-- END GENERATED cordis-surface -->

View file

@ -86,5 +86,5 @@ estimateMessage(message: Message): number
Types: [EpochHeader](session.md) · [Message](llm-streaming.md) · [Session](session.md)
Source: [`packages/llm/token-meter/src/index.ts:74`](../../packages/llm/token-meter/src/index.ts)
Source: [`packages/llm/token-meter/src/index.ts:78`](../../packages/llm/token-meter/src/index.ts)
<!-- END GENERATED cordis-surface -->

View file

@ -12,6 +12,7 @@ import type { ToolExecutionResult } from '@deepseek-ai/dsh-tools'
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { LocalBashExecutor } from '@deepseek-ai/dsh-bash-local'
import * as BashEnvPlugin from '@deepseek-ai/dsh-shell-env'
import LocalSubprocessRuntime from '@deepseek-ai/dsh-subprocess-local'
@ -53,6 +54,7 @@ async function codeModeHarness(cwd: string): Promise<Context> {
const harness = new Context()
await harness.plugin(LlmRuntime)
await harness.plugin(SessionStore)
await harness.plugin(SessionProjectionRegistry)
await harness.plugin(SystemPrompt, { persona: PERSONA })
await harness.plugin(ToolRuntime, { mode: 'code' })
await harness.plugin(AgentRegistry)
@ -70,6 +72,7 @@ async function workspaceCodeModeHarness(): Promise<Context> {
const harness = new Context()
await harness.plugin(LlmRuntime)
await harness.plugin(SessionStore)
await harness.plugin(SessionProjectionRegistry)
await harness.plugin(SystemPrompt, { persona: PERSONA })
await harness.plugin(ToolRuntime, { mode: 'code' })
await harness.plugin(AgentRegistry)

View file

@ -2,6 +2,7 @@ import { Context } from '@deepseek-ai/cordis'
import type { SessionEvent } from '@deepseek-ai/dsh-session'
import type { Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
import { LocalBashExecutor } from '@deepseek-ai/dsh-bash-local'
import * as BashEnvPlugin from '@deepseek-ai/dsh-shell-env'
@ -55,6 +56,7 @@ export interface CodingHarnessOptions {
export async function codingHarness(workdir: string, options: CodingHarnessOptions = {}): Promise<Context> {
const ctx = new Context()
await ctx.plugin(SessionProjectionRegistry)
await mountAgentLoopTestDependencies(ctx, {
systemPrompt: { persona: options.persona ?? '' },
})

View file

@ -46,6 +46,10 @@
- id: session-checkpoints
name: '@deepseek-ai/dsh-session-checkpoint-policy'
# Required by plugins that register or read session projection state below.
- id: session-projection
name: '@deepseek-ai/dsh-session-projection'
- id: subagent
name: '@deepseek-ai/dsh-subagent'

View file

@ -54,6 +54,7 @@
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/dsh-user-approval": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session-projection": "workspace:^"
}
}

View file

@ -61,7 +61,11 @@ describe('ACP machine permission policy', () => {
harness = await makeBridgeHarness()
const request = await ownedRequest()
const foreign = {
session: { id: request.agent.session.id, events: [{ type: 'turn/start' }], append: () => ({}) },
session: {
id: request.agent.session.id,
events: [{ type: 'turn/start', seq: 0, time: 0, data: { turn: 1 } }],
append: () => ({}),
},
} as unknown as Agent
await expect(harness.ctx.approval.request({ agent: foreign, toolName: 'bash', callId: CallId('call') }))
.resolves.toBe('unavailable')

View file

@ -17,6 +17,7 @@ import type { ImageAttachmentLimits, ImageAttachmentRef, SaveImageAttachment, St
import { type GenerateOptions, LlmAdapter, type LlmResolvedModelInfo, type StreamChunk } from '@deepseek-ai/dsh-llm'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import * as AcpPlugin from '../src/index.ts'
import type { AcpConfig } from '../src/index.ts'
@ -184,6 +185,10 @@ export async function makeBridgeHarness(options: {
const adapter = new MockAdapter(options.script ?? [], options.imageCapable === true)
const ctx = new Context()
await mountAgentLoopTestDependencies(ctx, { systemPrompt: { persona: options.persona ?? '' } })
// The agent loop and the composed approval/permission services declare
// sessionProjections a required injection: mount the registry (and with it
// the loop's turnBoundary unit) before the loop activates.
await ctx.plugin(SessionProjectionRegistry)
if (options.attachments !== false) await ctx.plugin(MemoryAttachmentStore)
const loopFiber = await ctx.plugin(AgentLoop, { agents: [] })
ctx.llm.registerAdapter(['mock'], adapter)

View file

@ -26,6 +26,7 @@ const requiredArtifacts = [
'packages/api/gateway/lib/index.js',
'packages/typert/registry/lib/client.js',
'packages/typert/registry/lib/index.js',
'packages/session/session-projection/lib/index.js',
].every(path => existsSync(artifact(path)))
describe.skipIf(!requiredArtifacts)('Goal Remote built LIB chain', () => {
@ -42,6 +43,7 @@ describe.skipIf(!requiredArtifacts)('Goal Remote built LIB chain', () => {
registryHost: 'packages/typert/registry/lib/index.js',
remotesClient: 'packages/api/remotes/lib/client.js',
session: 'packages/core/session/lib/index.js',
sessionProjections: 'packages/session/session-projection/lib/index.js',
}).map(([key, path]) => [key, artifactUrl(path)]))
const script = `
import { createServer } from 'node:http'
@ -53,6 +55,7 @@ describe.skipIf(!requiredArtifacts)('Goal Remote built LIB chain', () => {
const connectionHost = await import(urls.connectionHost)
const { default: TypertRemoteService } = await import(urls.apiGatewayHost)
const { default: GoalService } = await import(urls.goal)
const { default: SessionProjectionRegistry } = await import(urls.sessionProjections)
const { TYPERT } = await import(urls.goalTypert)
const { default: TypertRegistry } = await import(urls.registryHost)
const { Session, SessionId } = await import(urls.session)
@ -71,6 +74,7 @@ describe.skipIf(!requiredArtifacts)('Goal Remote built LIB chain', () => {
await host.plugin(TypertRegistry)
await host.plugin(AgentRegistry)
await host.plugin(TypertRemoteService)
await host.plugin(SessionProjectionRegistry)
await host.plugin(GoalService)
host.typert.register(TYPERT)

View file

@ -65,6 +65,7 @@
"@deepseek-ai/dsh-token-meter": "workspace:^",
"@deepseek-ai/dsh-compaction-tool-result-pruner": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session-projection": "workspace:^"
}
}

View file

@ -23,6 +23,7 @@ import type {
TokenUsage,
} from '@deepseek-ai/dsh-llm'
import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import TokenMeter from '@deepseek-ai/dsh-token-meter'
import { agentEvents, type Agent, type RequestErrorAction } from '@deepseek-ai/dsh-agent'
import ToolResultPruner from '@deepseek-ai/dsh-compaction-tool-result-pruner'
@ -72,6 +73,9 @@ class RoutedContextAdapter extends LlmAdapter {
function createContext(contextWindow = 1_000): Context {
const ctx = new Context()
void new LlmRuntime(ctx)
// The registry is a required injection of TokenMeter (its three
// projection units register in the constructor); mount it synchronously.
new SessionProjectionRegistry(ctx)
void new TokenMeter(ctx)
ctx.llm.registerAdapter([MODEL, 'actual', 'unlisted-provider'], new ContextAdapter(contextWindow))
return ctx
@ -518,6 +522,7 @@ describe('pressure measurement and retention', () => {
it('re-resolves capacity after a same-model-id provider switch in one session', async () => {
const ctx = new Context()
void new LlmRuntime(ctx)
new SessionProjectionRegistry(ctx)
void new TokenMeter(ctx)
ctx.llm.registerAdapter(['large', 'small'], new RoutedContextAdapter({
large: 10_000,
@ -545,6 +550,7 @@ describe('pressure measurement and retention', () => {
it('requires capacity only for proactive pressure, not provider-confirmed overflow', async () => {
const ctx = new Context()
void new LlmRuntime(ctx)
new SessionProjectionRegistry(ctx)
void new TokenMeter(ctx)
ctx.llm.registerAdapter(['unknown-context'], new ContextAdapter(1_000))
vi.spyOn(ctx.llm, 'resolveModelInfo').mockImplementation((provider, model) => Promise.resolve({
@ -1162,6 +1168,7 @@ async function summarizerHarness(
): Promise<{ ctx: Context; adapter: ScriptedAdapter; compact: ExposedCompactionEngine }> {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionProjectionRegistry)
void new TokenMeter(ctx)
const adapter = new ScriptedAdapter(blocks, finish)
ctx.llm.registerAdapter([model], adapter)
@ -1338,6 +1345,7 @@ describe('default one-shot summarizer', () => {
it('fails clearly when no complete summarization target can be resolved', async () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionProjectionRegistry)
void new TokenMeter(ctx)
const compact = new ExposedCompactionEngine(ctx, { auto: false })
await expect(compact.runSummarize(promptInput('history'), agent(Session.create(SessionId('model-less')))))
@ -1852,6 +1860,7 @@ describe('automatic listener and loader composition', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
const meterFiber = await ctx.plugin(TokenMeter)
const compactFiber = await ctx.plugin(BasicCompactionEngine, { auto: false })
@ -1865,6 +1874,7 @@ describe('automatic listener and loader composition', () => {
it('removes its automatic listener with the plugin fiber', async () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(TokenMeter)
const fiber = await ctx.plugin(TestCompactionEngine, {
thresholdRatio: 0.5,

View file

@ -13,6 +13,7 @@ import * as SessionInvariant from '@deepseek-ai/dsh-session/invariant'
import * as AgentInvariant from '@deepseek-ai/dsh-agent/invariant'
import * as AgentLoopInvariant from '@deepseek-ai/dsh-agent-loop/invariant'
import { BasicCompactionEngine } from '@deepseek-ai/dsh-compaction-basic'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import TokenMeter from '@deepseek-ai/dsh-token-meter'
import * as LlmRetry from '@deepseek-ai/dsh-llm-retry'
import { Session, SessionId, type SessionEvent, type SurfaceEvent } from '@deepseek-ai/dsh-session'
@ -150,6 +151,9 @@ async function harness(toolSteps: number): Promise<{ ctx: Context; compact: Repr
const ctx = new Context()
await mountAgentLoopTestDependencies(ctx)
await mountInvariants(ctx)
// AgentLoop and TokenMeter both declare the registry as a required
// injection; mount it before either activates.
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(AgentLoop, { agents: [] })
await ctx.plugin(TokenMeter)
ctx.llm.registerAdapter(['mock'], new StepwiseToolAdapter(toolSteps))
@ -312,6 +316,7 @@ describe('context-overflow recovery across the real loop and compaction-basic',
const adapter = new OverflowRecoveryAdapter(delivery)
await mountAgentLoopTestDependencies(ctx)
await mountInvariants(ctx)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(AgentLoop, { agents: [] })
await ctx.plugin(TokenMeter)
ctx.llm.registerAdapter(['mock'], adapter)
@ -390,6 +395,7 @@ describe('context-overflow recovery across the real loop and compaction-basic',
const adapter = new OverflowRecoveryAdapter('thrown', true)
await mountAgentLoopTestDependencies(ctx)
await mountInvariants(ctx)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(LlmRetry)
await ctx.plugin(AgentLoop, { agents: [] })
await ctx.plugin(TokenMeter)

View file

@ -8,6 +8,7 @@ import Loader from '@deepseek-ai/cordis-plugin-loader'
import Include from '@deepseek-ai/cordis-plugin-include'
import LlmRuntime from '@deepseek-ai/dsh-llm'
import SessionStore from '@deepseek-ai/dsh-session'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import TokenMeter from '@deepseek-ai/dsh-token-meter'
import BasicCompactionEngine from '@deepseek-ai/dsh-compaction-basic'
import ToolResultPruner from '@deepseek-ai/dsh-compaction-tool-result-pruner'
@ -34,6 +35,7 @@ async function loadYaml(lines: readonly string[]): Promise<Context> {
const modules = new Map<string, unknown>([
['@deepseek-ai/dsh-llm', LlmRuntime],
['@deepseek-ai/dsh-session', SessionStore],
['@deepseek-ai/dsh-session-projection', SessionProjectionRegistry],
['@deepseek-ai/dsh-token-meter', TokenMeter],
['@deepseek-ai/dsh-compaction-tool-result-pruner', ToolResultPruner],
['@deepseek-ai/dsh-compaction-basic', BasicCompactionEngine],
@ -58,6 +60,7 @@ describe('real Loader composition', () => {
const loaded = await loadYaml([
"- name: '@deepseek-ai/dsh-llm'",
"- name: '@deepseek-ai/dsh-session'",
"- name: '@deepseek-ai/dsh-session-projection'",
"- name: '@deepseek-ai/dsh-token-meter'",
"- name: '@deepseek-ai/dsh-compaction-tool-result-pruner'",
' config:',
@ -86,6 +89,7 @@ describe('real Loader composition', () => {
it('rejects stale token-meter config after Schemastery normalization', async () => {
context = new Context()
await context.plugin(SessionProjectionRegistry)
await expect(context.plugin(TokenMeter, {
contextWindow: 4096,
} as never)).rejects.toThrow(/TokenMeterConfig: unknown key "contextWindow"/)
@ -95,6 +99,7 @@ describe('real Loader composition', () => {
context = new Context()
await context.plugin(LlmRuntime)
await context.plugin(SessionStore)
await context.plugin(SessionProjectionRegistry)
await context.plugin(TokenMeter)
await expect(context.plugin(BasicCompactionEngine, {
models: { legacy: { thresholdRatio: 0.5 } },
@ -105,6 +110,7 @@ describe('real Loader composition', () => {
context = new Context()
await context.plugin(LlmRuntime)
await context.plugin(SessionStore)
await context.plugin(SessionProjectionRegistry)
await context.plugin(TokenMeter)
await expect(context.plugin(BasicCompactionEngine, {
retainRatio: 0.2,
@ -120,6 +126,7 @@ describe('real Loader composition', () => {
context = new Context()
await context.plugin(LlmRuntime)
await context.plugin(SessionStore)
await context.plugin(SessionProjectionRegistry)
await context.plugin(TokenMeter)
await expect(context.plugin(BasicCompactionEngine, {
summarizationProvider: 'default-provider',

View file

@ -25,6 +25,7 @@ import type {
TokenUsage,
} from '@deepseek-ai/dsh-llm'
import SessionStore, { Session, SessionId, type SessionEvent } from '@deepseek-ai/dsh-session'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import LlmRuntime from '@deepseek-ai/dsh-llm'
import TokenMeter from '@deepseek-ai/dsh-token-meter'
import type { Agent } from '@deepseek-ai/dsh-agent'
@ -105,6 +106,7 @@ async function loopHarness(): Promise<LoopHarness> {
await ctx.plugin(AgentLoopInvariant)
await ctx.plugin(CompactionInvariant)
await ctx.plugin(CompactionBasicInvariant)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(AgentLoop, { agents: [] })
await ctx.plugin(TokenMeter)
const adapter = new TextAdapter()
@ -221,6 +223,7 @@ function detachedService(): { ctx: Context; compact: GatedCompactionEngine; flus
const ctx = new Context()
void new LlmRuntime(ctx)
void new SessionStore(ctx)
new SessionProjectionRegistry(ctx)
void new TokenMeter(ctx)
ctx.llm.registerAdapter([MODEL], new TextAdapter())
let flushes = 0

View file

@ -50,6 +50,7 @@
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-token-meter": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session-projection": "workspace:^"
}
}

View file

@ -6,6 +6,7 @@ import { afterEach, describe, expect, it } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import Loader from '@deepseek-ai/cordis-plugin-loader'
import Include from '@deepseek-ai/cordis-plugin-include'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import TokenMeter from '@deepseek-ai/dsh-token-meter'
import ToolResultPruner from '@deepseek-ai/dsh-compaction-tool-result-pruner'
@ -24,6 +25,7 @@ describe('compaction-tool-result-pruner real Loader composition', () => {
root = await mkdtemp(join(tmpdir(), 'dsh-compact-tool-result-prune-loader-'))
const configPath = join(root, 'cordis.yml')
await writeFile(configPath, [
"- name: '@deepseek-ai/dsh-session-projection'",
"- name: '@deepseek-ai/dsh-token-meter'",
"- name: '@deepseek-ai/dsh-compaction-tool-result-pruner'",
' config:',
@ -40,6 +42,7 @@ describe('compaction-tool-result-pruner real Loader composition', () => {
context.loader.internal = {
version: 'v2',
async import(specifier: string) {
if (specifier === '@deepseek-ai/dsh-session-projection') return SessionProjectionRegistry
if (specifier === '@deepseek-ai/dsh-token-meter') return TokenMeter
if (specifier === '@deepseek-ai/dsh-compaction-tool-result-pruner') return ToolResultPruner
throw new Error(`unexpected Loader import: ${specifier}`)
@ -61,8 +64,9 @@ describe('compaction-tool-result-pruner real Loader composition', () => {
it('rejects stale config after plugin schema normalization', async () => {
context = new Context()
// Satisfy the declared injection first: config normalization runs in the
// Satisfy the declared injections first: config normalization runs in the
// service constructor, which a pending fiber never reaches.
await context.plugin(SessionProjectionRegistry)
await context.plugin(TokenMeter)
await expect(context.plugin(ToolResultPruner, {
maxChars: 100,

View file

@ -9,6 +9,7 @@ import SessionStore, {
import type { SurfaceEvent } from '@deepseek-ai/dsh-session'
import * as SessionInvariant from '@deepseek-ai/dsh-session/invariant'
import InvariantRegistry from '@deepseek-ai/dsh-invariants'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import TokenMeter from '@deepseek-ai/dsh-token-meter'
import ToolResultPruner, {
codePointLength,
@ -29,12 +30,15 @@ function service(config: ToolResultPruneConfig = SMALL): ToolResultPruner {
const ctx = new Context()
// Service constructors self-register, so `ctx.tokenMeter` resolves for the
// shadow-price pricing without a full plugin boot.
new SessionProjectionRegistry(ctx)
void new TokenMeter(ctx)
return new ToolResultPruner(ctx, config)
}
/** Pricing oracle mirroring the service's estimator for expectations. */
const METER = new TokenMeter(new Context())
const METER_CTX = new Context()
new SessionProjectionRegistry(METER_CTX)
const METER = new TokenMeter(METER_CTX)
function appendToolStep(
session: Session,
@ -258,6 +262,7 @@ describe('ToolResultPruner session transaction', () => {
it('runs under real invariants between closed steps but not outside a turn', async () => {
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(InvariantRegistry)
await ctx.plugin(SessionInvariant)
await ctx.plugin(TokenMeter)

View file

@ -39,10 +39,12 @@
"@deepseek-ai/dsh-home-paths": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session-projection": "workspace:^"
},
"dependencies": {
"@deepseek-ai/schemastery": "workspace:^"
"@deepseek-ai/schemastery": "workspace:^",
"zod": "^4.4.3"
},
"devDependencies": {
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
@ -58,6 +60,7 @@
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/dsh-tool-fs": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session-projection": "workspace:^"
}
}

View file

@ -15,11 +15,13 @@ import type { Agent, PreStepDecision } from '@deepseek-ai/dsh-agent'
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import type { Session, UserMessage } from '@deepseek-ai/dsh-session'
import type { ToolExecution, ToolExecutionResult, ToolExecutionToken } from '@deepseek-ai/dsh-tools'
import type {} from '@deepseek-ai/dsh-session-projection'
import { Config, resolveConfig, workspaceBaselineIdentity, type ResolvedConfig } from './config.ts'
import { findProjectRoot, loadBaselineInstructionSet } from './files.ts'
import {
applyInstructionVersionUpdates,
baselineInstructionState,
createWorkspaceInstructionsProjection,
name,
reconcileInstructionContext,
workspaceContextMessage,
@ -77,7 +79,12 @@ function filePathFromExecution(exec: ToolExecution): string | undefined {
return filePath.length > 0 ? filePath : undefined
}
/** Required services (the projection registry drives the instruction fold). */
export const inject = ['sessionProjections']
export function apply(ctx: Context, config: Config): void {
ctx.sessionProjections.register(createWorkspaceInstructionsProjection())
const resolved: ResolvedConfig = resolveConfig(config)
const instructionVersions: InstructionVersionCache = new WeakMap()
const baselinePreparations = new WeakMap<Session, {

View file

@ -48,7 +48,7 @@ export interface AgentInstructionChange {
action: 'set' | 'replace' | 'remove'
scope: string
path: string
digest?: string
digest?: string | undefined
}
/** One state transition paired with the content used to render it. */

View file

@ -4,7 +4,10 @@
* @module @deepseek-ai/dsh-agent-instructions/state
*/
import { z as zod } from 'zod'
import type { Agent } from '@deepseek-ai/dsh-agent'
import type {} from '@deepseek-ai/dsh-session-projection'
import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import type { Message } from '@deepseek-ai/dsh-llm'
import type { Session, UserMessage } from '@deepseek-ai/dsh-session'
@ -133,18 +136,59 @@ function sameInstructionChange(a: AgentInstructionChange, b: AgentInstructionCha
&& a.digest === b.digest
}
const workspaceInstructionsStateSchema = zod.record(zod.string(), zod.object({
change: zod.object({
action: zod.enum(['set', 'replace', 'remove']),
scope: zod.string(),
path: zod.string(),
digest: zod.string().optional(),
}),
seq: zod.number().int().nonnegative(),
}))
/** Latest workspace-instruction change by scope. */
export type WorkspaceInstructionsState = zod.infer<typeof workspaceInstructionsStateSchema>
declare module '@deepseek-ai/dsh-session-projection/types' {
interface SessionProjectionStateMap {
/** Latest workspace-instruction change by scope. */
workspaceInstructions: WorkspaceInstructionsState
}
}
/**
* Create the workspace-instruction projection.
* @returns Workspace-instruction projection definition.
*/
export function createWorkspaceInstructionsProjection(): ProjectionDefinition<'workspaceInstructions', WorkspaceInstructionsState> {
return {
key: 'workspaceInstructions',
stateSchema: workspaceInstructionsStateSchema,
init: () => ({}),
apply: (state, event) => {
if (event.type !== 'user/message' || !isWorkspaceContextSource(event.data.source)) return state
const changes = workspaceInstructionChanges(event.data.source)
if (changes.length === 0) return state
let next = state
for (const change of changes) {
next = { ...next, [change.scope]: { change, seq: event.seq } }
}
return next
},
stateVersion: 1,
}
}
function visibleInstructionChanges(
agent: Agent,
authorityMessages: readonly UserMessage[],
): Map<string, AgentInstructionChange> {
const visibleSeqs = new Set(agent.session.surface.nodes)
const visible = new Map<string, AgentInstructionChange>()
for (const [seq, event] of agent.session.events.entries()) {
if (event.type !== 'user/message' || !isWorkspaceContextSource(event.data.source)) continue
const changes = workspaceInstructionChanges(event.data.source)
for (const change of changes) {
if (visibleSeqs.has(seq)) visible.set(change.scope, change)
}
const folded = agent.ctx.get('sessionProjections')?.stateOf(agent.session, 'workspaceInstructions')
if (folded === undefined) throw new Error('workspaceInstructions projection is not registered')
for (const [scope, record] of Object.entries(folded)) {
if (visibleSeqs.has(record.seq)) visible.set(scope, record.change)
}
for (const message of authorityMessages) {
if (!isWorkspaceContextSource(message.source)) continue

View file

@ -11,6 +11,7 @@ import ToolRuntime from '@deepseek-ai/dsh-tools'
import AgentRegistry from '@deepseek-ai/dsh-agent'
import type { Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
import * as WorkspaceContext from '@deepseek-ai/dsh-agent-instructions'
import { candidateScopeKey } from '../src/render.ts'
@ -39,6 +40,7 @@ async function harness(): Promise<{ ctx: Context; agent: Agent }> {
ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt, { persona: 'Answer the user exactly and concisely.' })
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -7,6 +7,7 @@ import Loader from '@deepseek-ai/cordis-plugin-loader'
import * as workspaceContext from '@deepseek-ai/dsh-agent-instructions'
import LlmRuntime, { createUserMessage, CallId, type Message, type StreamChunk } from '@deepseek-ai/dsh-llm'
import SessionStore, { Session, SessionId, SESSION_FORMAT_VERSION, type SessionEvent, type UserMessage } from '@deepseek-ai/dsh-session'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import AgentRegistry, { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import { FileSystem, FsTargetKey, FsVersion } from '@deepseek-ai/dsh-fs'
@ -36,6 +37,7 @@ import {
import {
applyInstructionVersionUpdates,
baselineInstructionState,
createWorkspaceInstructionsProjection,
reconcileInstructionContext,
type InstructionVersionCache,
} from '../src/state.ts'
@ -167,9 +169,16 @@ class BlockingReadFileSystem extends RecordingFileSystem {
}
}
async function pluginWorkspaceContext(ctx: Context, config: workspaceContext.Config): Promise<Awaited<ReturnType<Context['plugin']>>> {
if (ctx.get('sessionProjections') === undefined) {
await ctx.plugin(SessionProjectionRegistry)
}
return ctx.plugin(workspaceContext, config)
}
async function mountWorkspaceContext(ctx: Context, config: workspaceContext.Config): Promise<Awaited<ReturnType<Context['plugin']>>> {
await ctx.plugin(LocalFileSystem, { cwd: '/' })
return ctx.plugin(workspaceContext, config)
return pluginWorkspaceContext(ctx, config)
}
async function mountFileToolsAndWorkspaceContext(ctx: Context, config: workspaceContext.Config): Promise<Awaited<ReturnType<Context['plugin']>>> {
@ -177,14 +186,17 @@ async function mountFileToolsAndWorkspaceContext(ctx: Context, config: workspace
await ctx.plugin(ToolRuntime)
await ctx.plugin(LocalFileSystem, { cwd: '/' })
await ctx.plugin(ToolFs)
return ctx.plugin(workspaceContext, config)
return pluginWorkspaceContext(ctx, config)
}
function stubAgent(cwd?: string, seed: SessionEvent[] = []): Agent {
const id = SessionId('s1')
const session = Session.create(id, seed, cwd === undefined ? undefined : { version: SESSION_FORMAT_VERSION, id, createdAt: 0, cwd })
const ctx = new Context()
new SessionProjectionRegistry(ctx)
ctx.sessionProjections.register(createWorkspaceInstructionsProjection())
return {
ctx: new Context(),
ctx,
id: SessionId('a1'),
options: {},
session,
@ -193,7 +205,7 @@ function stubAgent(cwd?: string, seed: SessionEvent[] = []): Agent {
send: () => {},
followup: () => {},
steer: () => {},
inject: () => { throw new Error('agent-instructions must append directly to the open step') },
inject: () => { throw new Error('workspace-context must append directly to the open step') },
cancel() {},
runMaintenance: task => task(new AbortController().signal),
whenIdle: () => Promise.resolve(),
@ -223,7 +235,7 @@ async function workspaceContextOf(agent: Agent): Promise<UserMessage> {
message.source.kind === 'agent-instructions')
expect(context).toBeDefined()
return context!
})
}, { timeout: 10_000 })
}
async function syncWorkspaceContext(ctx: Context, agent: Agent): Promise<void> {
@ -622,10 +634,10 @@ describe('workspace context instruction discovery', () => {
it('labels the default DSH home as ~/.dsh when HOME points at the configured default', async () => {
const root = await tempRepo()
const home = await tempRepo()
vi.stubEnv('DSH_HOME', '')
try {
await write(join(home, '.dsh/AGENTS.md'), 'global default rule')
// A set DSH_HOME would override the homedir default and relabel the home.
vi.stubEnv('DSH_HOME', '')
vi.resetModules()
vi.doMock('node:os', () => ({ homedir: () => home }))
@ -985,26 +997,27 @@ describe('workspace context request injection', () => {
it('requires an explicit maxBytes configuration', async () => {
const ctx = new Context()
await expect(ctx.plugin(workspaceContext, {} as workspaceContext.Config)).rejects.toThrow(/maxBytes/)
await expect(pluginWorkspaceContext(ctx, {} as workspaceContext.Config)).rejects.toThrow(/maxBytes/)
})
it('mounts without requiring a filesystem provider', async () => {
const ctx = new Context()
try {
await ctx.plugin(workspaceContext, { maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { maxBytes: 65536 })
} finally {
await ctx.fiber.dispose()
}
})
it('does not declare fs as a static inject dependency', () => {
expect('inject' in workspaceContext).toBe(false)
expect(workspaceContext.inject).toEqual(['sessionProjections'])
expect(workspaceContext.inject).not.toContain('fs')
})
it('does not inject baseline context when no filesystem provider is present', async () => {
const ctx = new Context()
try {
await ctx.plugin(workspaceContext, { maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { maxBytes: 65536 })
const agent = stubAgent('/virtual/repo')
await composeBaselinePrefix(ctx, agent)
@ -1111,7 +1124,7 @@ describe('workspace context request injection', () => {
const fs = ctx.fs as RecordingFileSystem
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(root, 'AGENTS.md'), { type: 'file', content: 'repo rule' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const original = stubAgent(root)
await composeBaselinePrefix(ctx, original)
@ -1355,7 +1368,7 @@ describe('workspace context request injection', () => {
expect(inserted?.source).toMatchObject({ kind: 'agent-instructions', baseline: true })
await fiber.dispose()
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const resumed = stubAgent(root, [...original.session.events])
agentEvents(ctx, resumed).emit('agent/session-start', { source: 'resume' })
const claimed = resumed.inbox.claim('next-step', 1)
@ -1401,7 +1414,7 @@ describe('workspace context request injection', () => {
await write(join(root, 'AGENTS.md'), 'new repo rule')
await fiber.dispose()
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const resumed = stubAgent(root, [...original.session.events])
agentEvents(ctx, resumed).emit('agent/session-start', { source: 'resume' })
const staleClaim = resumed.inbox.claim('next-step', 1)
@ -1454,7 +1467,7 @@ describe('workspace context request injection', () => {
await originalCtx.fiber.dispose()
if (provideFs) await resumedCtx.plugin(LocalFileSystem, { cwd: '/' })
await resumedCtx.plugin(workspaceContext, { dshHome: home, maxBytes })
await pluginWorkspaceContext(resumedCtx, { dshHome: home, maxBytes })
const resumed = stubAgent(root, [...original.session.events])
agentEvents(resumedCtx, resumed).emit('agent/session-start', { source: 'resume' })
const claimed = resumed.inbox.claim('next-step', 1)
@ -1564,7 +1577,7 @@ describe('workspace context request injection', () => {
const decision = await agentEvents(ctx, agent).waterfall(
'agent/pre-step',
{ messages: [prompt], turn: 1, step: 1, signal: AbortSignal.timeout(1000) },
{ messages: [prompt], turn: 1, step: 1, signal: AbortSignal.timeout(10_000) },
() => Promise.resolve(downstream),
)
@ -1649,7 +1662,7 @@ describe('workspace context request injection', () => {
// Hot remount over the live session: the durable baseline remains
// visible, so the fresh mount does not append a duplicate.
await fiber.dispose()
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
await composeBaselinePrefix(ctx, agent)
expect(baselineEvents(agent)).toHaveLength(1)
@ -1679,7 +1692,7 @@ describe('workspace context request injection', () => {
await write(join(root, 'AGENTS.md'), 'repo rule')
const ctx = new Context()
await ctx.plugin(LocalFileSystem, { cwd: '/' })
const fiber = await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
const fiber = await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
await composeBaselinePrefix(ctx, agent)
const baseline = baselineEvents(agent)[0]
@ -1694,7 +1707,7 @@ describe('workspace context request injection', () => {
})
await fiber.dispose()
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
await composeBaselinePrefix(ctx, agent)
expect(baselineEvents(agent)).toHaveLength(2)
@ -1981,7 +1994,7 @@ describe('workspace context request injection', () => {
const fs = ctx.fs as RecordingFileSystem
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(root, 'AGENTS.md'), { type: 'file', content: 'ctx.fs rule' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
await composeBaselinePrefix(ctx, agent)
@ -2004,7 +2017,7 @@ describe('workspace context request injection', () => {
const fs = ctx.fs as RecordingFileSystem
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(root, 'AGENTS.md'), { type: 'file', content: 'provider-only rule' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
await composeBaselinePrefix(ctx, agent)
@ -2046,7 +2059,7 @@ describe('workspace context request injection', () => {
const fs = ctx.fs as RecordingFileSystem
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(root, 'AGENTS.md'), { type: 'file', content: 'far too large' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536, maxSourceBytes: 4 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536, maxSourceBytes: 4 })
const prefix = await composeBaselinePrefix(ctx, stubAgent(root))
@ -2071,7 +2084,7 @@ describe('workspace context request injection', () => {
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(instructionPath, { type: 'file', content: 'far too large' })
fs.omitSizes.add(instructionPath)
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536, maxSourceBytes: 4 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536, maxSourceBytes: 4 })
const prefix = await composeBaselinePrefix(ctx, stubAgent(root))
@ -2094,7 +2107,7 @@ describe('workspace context request injection', () => {
const fs = ctx.fs as BlockingReadFileSystem
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(root, 'AGENTS.md'), { type: 'file', content: 'blocked' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const controller = new AbortController()
const reason = new Error('cancel prefix')
const pending = agentEvents(ctx, stubAgent(root)).waterfall(
@ -2128,7 +2141,7 @@ describe('workspace context request injection', () => {
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(home, 'AGENTS.md'), { type: 'file', content: 'ctx global rule' })
fs.entries.set(join(root, 'CLAUDE.md'), { type: 'file', content: 'ctx claude rule' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
await composeBaselinePrefix(ctx, agent)
@ -2154,7 +2167,7 @@ describe('workspace context request injection', () => {
const fs = ctx.fs as RecordingFileSystem
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(root, 'AGENTS.md'), { type: 'directory' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
await composeBaselinePrefix(ctx, agent)
@ -2177,7 +2190,7 @@ describe('workspace context request injection', () => {
const fs = ctx.fs as RecordingFileSystem
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(root, 'AGENTS.md'), { type: 'file' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
await composeBaselinePrefix(ctx, agent)
@ -2200,7 +2213,7 @@ describe('workspace context request injection', () => {
const fs = ctx.fs as RecordingFileSystem
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.throwOnStat.add(join(root, 'AGENTS.md'))
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
await composeBaselinePrefix(ctx, agent)
@ -2222,7 +2235,7 @@ describe('workspace context request injection', () => {
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.throwOnStat.add(join(root, 'AGENTS.md'))
fs.entries.set(join(root, 'CLAUDE.md'), { type: 'file', content: 'claude sibling rule' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
await composeBaselinePrefix(ctx, agent)
@ -2247,7 +2260,7 @@ describe('workspace context request injection', () => {
const fs = ctx.fs as RecordingFileSystem
fs.throwOnStat.add(join(root, '.git'))
fs.entries.set(join(root, 'AGENTS.md'), { type: 'file', content: 'repo rule' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
await composeBaselinePrefix(ctx, agent)
@ -2297,7 +2310,7 @@ describe('workspace context request injection', () => {
await write(join(cwd, 'AGENTS.md'), 'child schema default rule')
const ctx = new Context()
await ctx.plugin(LocalFileSystem, { cwd: '/' })
await ctx.plugin(workspaceContext, { maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { maxBytes: 65536 })
const agent = stubAgent(cwd)
await composeBaselinePrefix(ctx, agent)
@ -2318,7 +2331,7 @@ describe('workspace context request injection', () => {
await write(join(root, 'AGENTS.local.md'), 'local rule')
const ctx = new Context()
await ctx.plugin(LocalFileSystem, { cwd: '/' })
await ctx.plugin(workspaceContext, { maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { maxBytes: 65536 })
const agent = stubAgent(root)
await composeBaselinePrefix(ctx, agent)
@ -2514,7 +2527,7 @@ describe('dynamic nested workspace context injection', () => {
await ctx.plugin(AgentRegistry)
await ctx.plugin(LocalFileSystem, { cwd: '/' })
await ctx.plugin(ToolFs)
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
await ctx.plugin(AgentLoop, { agents: [] })
ctx.llm.registerAdapter(['mock'], adapter)
const agent = ctx.agentLoop.create(SessionId('workspace-context-abort'), { provider: 'mock', model: 'mock' }, { cwd: root })
@ -2590,7 +2603,7 @@ describe('dynamic nested workspace context injection', () => {
const fs = ctx.fs as RecordingFileSystem
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(root, 'pkg/AGENTS.md'), { type: 'file', content: 'nested' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const controller = new AbortController()
const reason = new Error('cancel dynamic reconciliation')
controller.abort(reason)
@ -2853,7 +2866,7 @@ describe('dynamic nested workspace context injection', () => {
fs.omitSizes.add(instructionPath)
fs.entries.set(join(root, 'pkg/file.txt'), { type: 'file', content: 'hello' })
await ctx.plugin(ToolFs)
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
const first = await ctx.tools.execute({
@ -2890,7 +2903,7 @@ describe('dynamic nested workspace context injection', () => {
fs.entries.set(instructionPath, { type: 'file', content: 'same package rule', version: FsVersion('revision-1') })
fs.entries.set(join(root, 'pkg/file.txt'), { type: 'file', content: 'hello' })
await ctx.plugin(ToolFs)
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
await ctx.tools.execute({
@ -2935,7 +2948,7 @@ describe('dynamic nested workspace context injection', () => {
fs.entries.set(instructionPath, { type: 'file', content: 'shared path, separate sessions' })
fs.entries.set(join(root, 'pkg/file.txt'), { type: 'file', content: 'hello' })
await ctx.plugin(ToolFs)
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const firstAgent = stubAgent(root)
const secondAgent = stubAgent(root)
@ -3471,7 +3484,7 @@ describe('dynamic nested workspace context injection', () => {
fs.entries.set(join(root, 'pkg/AGENTS.md'), { type: 'file', content: 'provider package rule' })
fs.entries.set(join(root, 'pkg/file.txt'), { type: 'file', content: 'hello' })
await ctx.plugin(ToolFs)
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
const first = await ctx.tools.execute({
@ -3850,7 +3863,7 @@ describe('dynamic nested workspace context injection', () => {
fs.entries.set(join(root, 'pkg/deep/file.txt'), { type: 'file', content: 'hello' })
fs.throwOnRead.add(nested)
await ctx.plugin(ToolFs)
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
const result = await ctx.tools.execute({
@ -3991,7 +4004,7 @@ describe('dynamic nested workspace context injection', () => {
? { kind: 'block' as const, feedback: [{ type: 'text' as const, text: 'outer policy block' }] }
: downstream
})
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
const blocked = await ctx.tools.execute({
@ -4060,7 +4073,7 @@ describe('dynamic nested workspace context injection', () => {
? { kind: 'block' as const, feedback: [{ type: 'text' as const, text: 'outer composite block' }] }
: downstream
})
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
const blocked = await ctx.tools.execute({
@ -4084,7 +4097,7 @@ describe('dynamic nested workspace context injection', () => {
const ctx = new Context()
try {
await ctx.plugin(RecordingFileSystem)
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const fs = ctx.fs as RecordingFileSystem
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(root, 'pkg/AGENTS.md'), { type: 'file', content: 'nested package rule' })
@ -4159,7 +4172,7 @@ describe('dynamic nested workspace context injection', () => {
agent.session.append('step/start', { turn: 1, step: 1 })
agent.session.append('step/end', { turn: 1, step: 1 })
agent.session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
ctx.emit('tools/result', stubToolExecution({
signal: testToolSignal,
@ -4182,7 +4195,7 @@ describe('dynamic nested workspace context injection', () => {
const ctx = new Context()
try {
await ctx.plugin(RecordingFileSystem)
await ctx.plugin(workspaceContext, { maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { maxBytes: 65536 })
const fs = ctx.fs as RecordingFileSystem
const agent = stubAgent('/')
const plainResult = { callId: CallId('plain'), content: [], isError: false as const, value: null }
@ -4231,7 +4244,7 @@ describe('dynamic nested workspace context injection', () => {
const ctx = new Context()
try {
await ctx.plugin(RecordingFileSystem)
await ctx.plugin(workspaceContext, { maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { maxBytes: 65536 })
const fs = ctx.fs as RecordingFileSystem
const root = resolve('/')
const agent = stubAgent(root)
@ -4297,7 +4310,7 @@ describe('dynamic nested workspace context injection', () => {
fs.entries.set(instructionPath, { type: 'file', content: 'x'.repeat(1000) })
fs.entries.set(join(root, 'pkg/file.txt'), { type: 'file', content: 'hello' })
await ctx.plugin(ToolFs)
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 20 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 20 })
const agent = stubAgent(root)
const first = await ctx.tools.execute({
@ -4419,7 +4432,7 @@ describe('workspace context inbox synchronization', () => {
const fs = ctx.fs as RecordingFileSystem
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(root, 'pkg/AGENTS.md'), { type: 'file', content: 'tiny-budget rule' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 1 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 1 })
const agent = stubAgent(root)
ctx.emit('tools/result', stubToolExecution({
signal: testToolSignal,
@ -4495,7 +4508,7 @@ describe('workspace context inbox synchronization', () => {
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(root, 'a/AGENTS.md'), { type: 'file', content: 'restored A' })
fs.entries.set(join(root, 'b/AGENTS.md'), { type: 'file', content: 'restored B' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
const first = stubToolExecution({
signal: testToolSignal,
@ -4535,7 +4548,7 @@ describe('workspace context inbox synchronization', () => {
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(root, 'a/AGENTS.md'), { type: 'file', content: 'scope A' })
fs.entries.set(join(root, 'b/AGENTS.md'), { type: 'file', content: 'scope B' })
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
await pluginWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
const first = stubToolExecution({
signal: testToolSignal,

View file

@ -52,6 +52,8 @@
"@deepseek-ai/dsh-output-retention": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-session-query": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/dsh-session-title": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session-projection": "workspace:^"
}
}

View file

@ -4,7 +4,9 @@ import type { Agent } from '@deepseek-ai/dsh-agent'
import { CompactionId, compactCheckpointSource } from '@deepseek-ai/dsh-compaction'
import { createUserMessage, CallId , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import SessionQueryEngine from '@deepseek-ai/dsh-session-query'
import { titleProjectionDefinition } from '@deepseek-ai/dsh-session-title'
import SessionReferenceResolver, {
decodeSessionReferenceUri,
encodeSessionReferenceUri,
@ -35,6 +37,8 @@ class TestSessionQueryEngine extends SessionQueryEngine {
async function harness(config: Config = {}): Promise<Context> {
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
ctx.sessionProjections.register(titleProjectionDefinition)
await ctx.plugin(TestSessionQueryEngine)
await ctx.plugin(SessionReferenceResolver, config)
return ctx

View file

@ -32,13 +32,15 @@
],
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "workspace:^"
"@deepseek-ai/schemastery": "workspace:^",
"zod": "^4.4.3"
},
"peerDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session-projection": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
@ -50,6 +52,7 @@
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session-projection": "workspace:^"
}
}

View file

@ -7,9 +7,11 @@
import type { Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery'
import { z as zod } from 'zod'
import type { Agent, PreStepDecision } from '@deepseek-ai/dsh-agent'
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import type { UserMessage } from '@deepseek-ai/dsh-llm'
import type {} from '@deepseek-ai/dsh-session-projection'
import {
deriveBrowserTimeZoneContext,
renderBrowserTimeZoneContext,
@ -20,8 +22,29 @@ import { createTimestampFormatter, formatTimestamp } from './timestamp.ts'
/** Cordis plugin name used by loader diagnostics. */
export const name = 'time-context'
declare module '@deepseek-ai/dsh-session-projection/types' {
interface SessionProjectionStateMap {
/** Latest time-context readings. */
timeContext: TimeContextProjection
}
}
const timeContextStateSchema = zod.object({
/** Time of the latest model-visible event (user/assistant message, tool result), or null. */
lastMessageTime: zod.number().nullable(),
/** Time of this plugin's latest durable injection, or null. */
lastInjectionTime: zod.number().nullable(),
/** Turn of the latest injection, or null (a same-turn injection answers precedingStepContextTime). */
lastInjectionTurn: zod.number().nullable(),
/** The open turn at the latest fold (null between turns); the injection's turn is captured at append. */
currentTurn: zod.number().nullable(),
})
/** Folded time-context readings. */
export type TimeContextProjection = zod.infer<typeof timeContextStateSchema>
/** The agent registry that owns pre-step processing. */
export const inject = ['agents']
export const inject = ['agents', 'sessionProjections']
/** Request-preparation clock formatting and append scheduling. Invalid values fail plugin load. */
export interface Config {
@ -54,47 +77,6 @@ function formatDuration(elapsedMs: number): string {
return parts.join(' ')
}
/** Find the latest model-visible event, excluding this plugin's pending append. */
function precedingMessageTime(agent: Agent): number | undefined {
for (const event of [...agent.session.events].reverse()) {
switch (event.type) {
case 'user/message':
case 'assistant/message':
case 'tool/result':
return event.time
default:
// Merge-extensible session events: non-surface records are not messages.
break
}
}
return undefined
}
/** Find the preceding time-context event within the open turn. */
function precedingStepContextTime(agent: Agent, turn: number): number | undefined {
for (const event of [...agent.session.events].reverse()) {
if (event.type === 'turn/start' && event.data.turn === turn) return undefined
if (event.type === 'user/message'
&& event.data.source.kind === 'plugin'
&& event.data.source.plugin === name) {
return event.time
}
}
return undefined
}
/** Find this plugin's latest durable injection, including a shadowed surface event. */
function latestInjectionTime(agent: Agent): number | undefined {
for (const event of [...agent.session.events].reverse()) {
if (event.type === 'user/message'
&& event.data.source.kind === 'plugin'
&& event.data.source.plugin === name) {
return event.time
}
}
return undefined
}
/** Collect already-entered and proposed user messages belonging to one open turn. */
function requestMessages(agent: Agent, turn: number, proposed: readonly UserMessage[]): UserMessage[] {
const start = agent.session.events.findLastIndex(
@ -167,6 +149,37 @@ export function apply(ctx: Context, config: Config): void {
return created
}
ctx.sessionProjections.register({
key: 'timeContext',
stateVersion: 1,
stateSchema: timeContextStateSchema,
init: () => ({ lastMessageTime: null, lastInjectionTime: null, lastInjectionTurn: null, currentTurn: null }),
apply: (state, event) => {
if (event.type === 'turn/start') {
return state.currentTurn === event.data.turn ? state : { ...state, currentTurn: event.data.turn }
}
if (event.type === 'turn/end') {
return state.currentTurn === null ? state : { ...state, currentTurn: null }
}
if (event.type === 'user/message') {
const injected = event.data.source.kind === 'plugin' && event.data.source.plugin === name
const withMessage = state.lastMessageTime === event.time
? state
: { ...state, lastMessageTime: event.time }
if (!injected) return withMessage
return {
...withMessage,
lastInjectionTime: event.time,
lastInjectionTurn: state.currentTurn,
}
}
if (event.type === 'assistant/message' || event.type === 'tool/result') {
return state.lastMessageTime === event.time ? state : { ...state, lastMessageTime: event.time }
}
return state
},
})
ctx.on('agent/pre-step', async (
{ agent, turn, step, signal },
next,
@ -174,15 +187,21 @@ export function apply(ctx: Context, config: Config): void {
const decision = await next()
if (decision.kind === 'reject' || signal.aborted) return decision
const now = Date.now()
if (refreshIntervalMs !== undefined && refreshIntervalMs > 0) {
const lastInjection = latestInjectionTime(agent)
if (lastInjection !== undefined
const state = ctx.sessionProjections.stateOf(agent.session, 'timeContext')
if (state !== undefined && refreshIntervalMs !== undefined && refreshIntervalMs > 0) {
const lastInjection = state.lastInjectionTime
if (lastInjection != null
&& now >= lastInjection
&& now - lastInjection < refreshIntervalMs) return decision
}
/* v8 ignore next 2 -- time-context registers its own unit in apply, so the key is always present */
if (state === undefined) return decision
/* v8 ignore next 6 -- an injection always records a time, so the lastInjectionTime nullish fallback is unreachable */
const previous = step === 1
? precedingMessageTime(agent)
: precedingStepContextTime(agent, turn)
? state.lastMessageTime ?? undefined
: state.lastInjectionTurn === turn
? state.lastInjectionTime ?? undefined
: undefined
const messages = requestMessages(agent, turn, decision.messages)
const browser = deriveBrowserTimeZoneContext(messages)
const selectedTimeZone = browser.kind === 'resolved' ? browser.timeZone : fallbackTimeZone

View file

@ -7,6 +7,7 @@ import { Session, SessionId, type SessionEvent } from '@deepseek-ai/dsh-session'
import AgentRegistry, { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent'
import { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
import * as timeContext from '@deepseek-ai/dsh-time-context'
import type { Config } from '@deepseek-ai/dsh-time-context'
@ -30,6 +31,7 @@ afterEach(() => {
async function mount(config: Config = {}) {
const ctx = new Context()
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(AgentRegistry)
const fiber = await ctx.plugin(timeContext, config)
return { ctx, fiber }
@ -137,6 +139,7 @@ class ScriptedAdapter extends LlmAdapter {
async function loopHarness(adapter: ScriptedAdapter, config: Config = {}): Promise<Context> {
const ctx = new Context()
await mountAgentLoopTestDependencies(ctx)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(AgentLoop, { agents: [] })
await ctx.plugin(timeContext, config)
ctx.llm.registerAdapter(['mock'], adapter)
@ -373,6 +376,7 @@ describe('configuration and lifecycle', () => {
it('fails loud for an invalid explicit zone or an unavailable process zone', async () => {
const invalid = new Context()
await invalid.plugin(SessionProjectionRegistry)
await invalid.plugin(AgentRegistry)
await expect(invalid.plugin(timeContext, { timeZone: 'Not/A_Real_Zone' })).rejects.toThrow(
/invalid IANA timeZone/,
@ -382,6 +386,7 @@ describe('configuration and lifecycle', () => {
throw new RangeError('system zone unavailable')
})
const unresolved = new Context()
await unresolved.plugin(SessionProjectionRegistry)
await unresolved.plugin(AgentRegistry)
await expect(unresolved.plugin(timeContext, {})).rejects.toThrow(/failed to resolve the system time zone/)
})
@ -409,6 +414,27 @@ describe('configuration and lifecycle', () => {
})
})
describe('time-context projection fold edges', () => {
it('keeps the same turn when a repeated turn/start lands', async () => {
const { ctx } = await mount()
const session = Session.create(SessionId('same-turn'))
session.append('turn/start', { turn: 1 })
session.append('turn/start', { turn: 1 })
expect(ctx.sessionProjections.snapshot(session).values.timeContext).toMatchObject({
currentTurn: 1,
})
})
it('stays null when turn/end arrives without a turn/start', async () => {
const { ctx } = await mount()
const session = Session.create(SessionId('end-without-start'))
session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
expect(ctx.sessionProjections.snapshot(session).values.timeContext).toMatchObject({
currentTurn: null,
})
})
})
describe('real agent-loop request history', () => {
it.each([
['throws'],
@ -485,11 +511,12 @@ describe('real Loader export path', () => {
const unwrapped = loader.unwrapExports(timeContext) as Record<string, unknown>
expect(unwrapped).toBe(timeContext)
expect(unwrapped.name).toBe('time-context')
expect(unwrapped.inject).toEqual(['agents'])
expect(unwrapped.inject).toEqual(['agents', 'sessionProjections'])
expect(unwrapped.Config).toBeDefined()
expect(typeof unwrapped.apply).toBe('function')
const ctx = new Context()
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(AgentRegistry)
const plugin = loader.unwrapExports(timeContext) as Parameters<Context['plugin']>[0]
await ctx.plugin(plugin)

View file

@ -4,7 +4,9 @@
"rootDir": "src",
"outDir": "lib/types"
},
"include": ["src"],
"include": [
"src"
],
"references": [
{
"path": "../../../vendor/cosmokit"
@ -35,6 +37,9 @@
},
{
"path": "../../core/session"
},
{
"path": "../../session/session-projection"
}
]
}

View file

@ -32,14 +32,16 @@
],
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "workspace:^"
"@deepseek-ai/schemastery": "workspace:^",
"zod": "^4.4.3"
},
"peerDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-shell": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session-projection": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
@ -48,6 +50,7 @@
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session-projection": "workspace:^"
}
}

View file

@ -20,7 +20,9 @@
import type { Context, LoggerService } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery'
import type { Agent, PreStepDecision } from '@deepseek-ai/dsh-agent'
import { z as zod } from 'zod'
import type { PreStepDecision } from '@deepseek-ai/dsh-agent'
import type {} from '@deepseek-ai/dsh-session-projection'
import type { ShellExecutor, ShellRunResult } from '@deepseek-ai/dsh-shell'
import { createUserMessage } from '@deepseek-ai/dsh-llm'
@ -28,7 +30,7 @@ import { createUserMessage } from '@deepseek-ai/dsh-llm'
export const name = 'tmux-context'
/** The agent registry that owns pre-step processing. */
export const inject = ['agents']
export const inject = ['agents', 'sessionProjections']
/** Per-turn tmux-location scheduling. Invalid values fail plugin load. */
export interface Config {
@ -178,21 +180,6 @@ function renderReading(location: TmuxLocation, turn: number): string {
* schedule survives compaction and resumed processes without process-local
* cache state.
*/
function latestInjectedState(agent: Agent): { state: string; time: number } | undefined {
for (const event of [...agent.session.events].reverse()) {
if (event.type === 'user/message'
&& event.data.source.kind === 'plugin'
&& event.data.source.plugin === name) {
const [block] = event.data.content
if (block?.type !== 'text') return undefined
const newline = block.text.indexOf('\n')
const state = newline === -1 ? '' : block.text.slice(newline + 1)
return { state, time: event.time }
}
}
return undefined
}
/** Reject refresh intervals that cannot represent an exact elapsed-millisecond threshold. */
function validateRefreshInterval(refreshIntervalMs: number | undefined): void {
if (refreshIntervalMs !== undefined && (
@ -205,16 +192,47 @@ function validateRefreshInterval(refreshIntervalMs: number | undefined): void {
}
}
const tmuxContextStateSchema = zod.object({
state: zod.string(),
time: zod.number(),
}).nullable()
type TmuxContextState = zod.infer<typeof tmuxContextStateSchema>
/**
* Register a prepended pre-step listener for the lifetime of `ctx`.
* @param ctx - plugin context; the listener is disposed with it.
* @param config - durable refresh scheduling configuration.
* @throws when the refresh interval is invalid.
*/
declare module '@deepseek-ai/dsh-session-projection/types' {
interface SessionProjectionStateMap {
/** The stable state block of this plugin's latest durable injection, or null. */
tmuxContext: TmuxContextState
}
}
export function apply(ctx: Context, config: Config): void {
const refreshIntervalMs = config.refreshIntervalMs
validateRefreshInterval(refreshIntervalMs)
ctx.sessionProjections.register({
key: 'tmuxContext',
stateVersion: 1,
stateSchema: tmuxContextStateSchema,
init: () => null,
apply: (state, event) => {
if (event.type !== 'user/message'
|| event.data.source.kind !== 'plugin'
|| event.data.source.plugin !== name) return state
const [block] = event.data.content
if (block?.type !== 'text') return state
const newline = block.text.indexOf('\n')
const stableState = newline === -1 ? '' : block.text.slice(newline + 1)
return { state: stableState, time: event.time }
},
})
ctx.on('agent/pre-step', async (
{ agent, turn, step, signal },
next,
@ -223,15 +241,15 @@ export function apply(ctx: Context, config: Config): void {
if (decision.kind === 'reject' || signal.aborted || step !== 1) return decision
const bash = ctx.get('shell')
if (bash === undefined) return decision
const previous = latestInjectedState(agent)
if (refreshIntervalMs !== undefined && refreshIntervalMs > 0 && previous !== undefined) {
const previous = ctx.sessionProjections.stateOf(agent.session, 'tmuxContext') ?? null
if (refreshIntervalMs !== undefined && refreshIntervalMs > 0 && previous !== null) {
const now = Date.now()
if (now >= previous.time && now - previous.time < refreshIntervalMs) return decision
}
const location = await queryTmuxLocation(bash, ctx.logger, process.pid, signal)
if (location === undefined) return decision
const state = renderState(location)
if (previous !== undefined && previous.state === state) return decision
if (previous !== null && previous.state === state) return decision
const text = renderReading(location, turn)
return {
kind: 'enter',

View file

@ -2,6 +2,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import { Session, SessionId } from '@deepseek-ai/dsh-session'
import AgentRegistry, { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import { ShellExecutor } from '@deepseek-ai/dsh-shell'
import type { ShellExecRequest, ShellExecSpec, ShellProcess, ShellRunResult } from '@deepseek-ai/dsh-shell'
@ -82,6 +83,7 @@ async function mount(
): Promise<{ ctx: Context; bash: FakeBash | undefined }> {
const ctx = new Context()
await ctx.plugin(AgentRegistry)
await ctx.plugin(SessionProjectionRegistry)
let bash: FakeBash | undefined
if (withBash) {
await ctx.plugin(FakeBash)

View file

@ -4,7 +4,9 @@
"rootDir": "src",
"outDir": "lib/types"
},
"include": ["src"],
"include": [
"src"
],
"references": [
{
"path": "../../../vendor/cosmokit"
@ -32,6 +34,9 @@
},
{
"path": "../../core/session"
},
{
"path": "../../session/session-projection"
}
]
}

View file

@ -37,13 +37,15 @@
"@deepseek-ai/dsh-scope": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-session-persistence": "workspace:^",
"@deepseek-ai/dsh-session-projection": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"dependencies": {
"@deepseek-ai/schemastery": "workspace:^"
"@deepseek-ai/schemastery": "workspace:^",
"zod": "^4.4.3"
},
"devDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
@ -53,9 +55,10 @@
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-session-persistence": "workspace:^",
"@deepseek-ai/dsh-session-persistence-jsonl": "workspace:^",
"@deepseek-ai/dsh-session-projection": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
}
}

View file

@ -31,6 +31,7 @@ import type { EpochHeader, RequestContext, Session, SessionId, TurnEndReason, Us
import { canonicalHeader, headerEquals } from '@deepseek-ai/dsh-session'
import { joinContextSections, renderContextSections, renderPrompt } from '@deepseek-ai/dsh-system-prompt'
import type { PromptAssembly } from '@deepseek-ai/dsh-system-prompt'
import type {} from '@deepseek-ai/dsh-session-projection'
import type { Context } from '@deepseek-ai/cordis'
import { RuntimeContextProjection } from './runtime-context.ts'
import { executeToolCalls } from './tool-calls.ts'
@ -89,7 +90,8 @@ export class ReactLoopAgent implements Agent {
discarded: (message) => { this.dispatch.emit('agent/inbox/discarded', { message }) },
claimed: (message, turn) => { this.dispatch.emit('agent/inbox/claimed', { message, turn }) },
})
const lastTurn = session.events.findLast(event => event.type === 'turn/start')?.data.turn ?? 0
/* v8 ignore next -- the loop registers its own turnBoundary unit, so the key is always present */
const lastTurn = this.loopCtx.sessionProjections.stateOf(session, 'turnBoundary')?.lastTurn ?? 0
this.phase = { kind: 'idle', lastTurn }
this.scope = createScope(loopCtx, this)
this.ctx = this.scope.ctx.extend({ agent: this })

View file

@ -8,6 +8,7 @@
import { Context, FiberState, Service } from '@deepseek-ai/cordis'
import { randomUUID } from 'node:crypto'
import z from '@deepseek-ai/schemastery'
import { z as zod } from 'zod'
import { emitAgentEvent } from '@deepseek-ai/dsh-agent'
import type {
Agent,
@ -18,6 +19,7 @@ import type {
CreateAgentOptions,
ResumeAgentOptions,
SessionStartSource,
TurnBoundaryProjection,
} from '@deepseek-ai/dsh-agent'
import { errorChain } from '@deepseek-ai/dsh-llm'
import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'
@ -25,6 +27,8 @@ import { SessionId, SessionPreparation } from '@deepseek-ai/dsh-session'
import type { Session, SessionHeader } from '@deepseek-ai/dsh-session'
import type {} from '@deepseek-ai/dsh-system-prompt'
import type {} from '@deepseek-ai/dsh-tools'
import type {} from '@deepseek-ai/dsh-session-projection'
import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'
import type { SessionPersistence } from '@deepseek-ai/dsh-session-persistence'
import { ReactLoopAgent } from './agent.ts'
import { DEFAULT_MAX_PARALLEL_TOOL_CALLS } from './constants.ts'
@ -36,6 +40,61 @@ const INACTIVE_STATES: ReadonlySet<FiberState> = new Set([
FiberState.FAILED,
])
const turnBoundaryProjectionSchema: zod.ZodType<TurnBoundaryProjection> = zod.object({
openTurn: zod.number().int().nonnegative().nullable(),
openTurnStartSeq: zod.number().int().nonnegative().nullable(),
lastStepStartSeq: zod.number().int().nonnegative().nullable(),
lastStepBoundary: zod.object({
kind: zod.union([zod.literal('start'), zod.literal('end')]),
seq: zod.number().int().nonnegative(),
}).nullable(),
lastTurn: zod.number().int().nonnegative(),
})
/** Host projection of agent turn and step boundaries. */
export const turnBoundaryProjectionDefinition = {
key: 'turnBoundary',
stateVersion: 1,
stateSchema: turnBoundaryProjectionSchema,
init: () => ({
openTurn: null,
openTurnStartSeq: null,
lastStepStartSeq: null,
lastStepBoundary: null,
lastTurn: 0,
}),
apply: (state, event) => {
switch (event.type) {
case 'turn/start':
return {
...state,
openTurn: event.data.turn,
openTurnStartSeq: event.seq,
lastTurn: event.data.turn,
}
case 'turn/end':
return {
...state,
openTurn: null,
openTurnStartSeq: null,
}
case 'step/start':
return {
...state,
lastStepStartSeq: event.seq,
lastStepBoundary: { kind: 'start', seq: event.seq },
}
case 'step/end':
return {
...state,
lastStepBoundary: { kind: 'end', seq: event.seq },
}
default:
return state
}
},
} satisfies ProjectionDefinition<'turnBoundary', TurnBoundaryProjection>
/** Factory-level ownership: live agent teardowns plus config startup work. */
class FactoryOwnership {
private accepting = true
@ -294,7 +353,7 @@ function validateConfiguredAgents(agents: Config['agents']): void {
/** Concrete agent factory and driver service. */
export class AgentLoop extends Service implements AgentFactory {
static inject = ['agents', 'sessions', 'llm', 'tools', 'systemPrompt']
static inject = ['agents', 'sessions', 'llm', 'tools', 'systemPrompt', 'sessionProjections']
/** Runtime schema for declarative agents. */
static Config = z.object({
@ -318,6 +377,9 @@ export class AgentLoop extends Service implements AgentFactory {
constructor(ctx: Context, config: Config) {
super(ctx, 'agentLoop')
ctx.sessionProjections.register(turnBoundaryProjectionDefinition)
const entry: AgentLoopSettings = {
maxParallelToolCalls: resolveMaxParallelToolCalls(config.maxParallelToolCalls),
}

View file

@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
import { Context, type Fiber } from '@deepseek-ai/cordis'
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import LlmRuntime, { createUserMessage, CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
@ -21,6 +22,7 @@ async function harness(adapter: LlmAdapter): Promise<Harness> {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
const agentsFiber = await ctx.plugin(AgentRegistry)
@ -121,6 +123,7 @@ describe('AgentLoop initiator scope', () => {
const adapter = new OverlapAdapter(ctx)
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -380,6 +383,7 @@ describe('AgentLoop initiator scope', () => {
const adapter = new ReloadAdapter()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -3,6 +3,7 @@ import { describe, expect, it, vi } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import LlmRuntime from '@deepseek-ai/dsh-llm'
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
@ -13,6 +14,7 @@ async function harness(adapter: MockAdapter): Promise<Context> {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -15,6 +15,7 @@ import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRuntime, { defineContentToolFixture, TOOL_ABORTED_BEFORE_DISPATCH } from '@deepseek-ai/dsh-tools'
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { MockAdapter, textResponse, toolCallResponse } from './mock-adapter.ts'
function driverDone(agent: Agent): Promise<void> {
@ -25,6 +26,7 @@ async function harness(adapter: MockAdapter) {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -514,6 +516,7 @@ describe('Agent.cancel()', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -12,6 +12,7 @@ import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
import AgentLoop, { CONFIGURED_AGENT_IDENTITIES_KEY } from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { MockAdapter, textResponse } from './mock-adapter.ts'
const dirs: string[] = []
@ -29,6 +30,7 @@ async function makeCoreContext(): Promise<Context> {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -327,6 +329,7 @@ describe('config-driven session id', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -352,6 +355,7 @@ describe('config-driven session id', () => {
const ctx1 = new Context()
await ctx1.plugin(LlmRuntime)
await ctx1.plugin(SessionStore)
await ctx1.plugin(SessionProjectionRegistry)
await ctx1.plugin(SystemPrompt)
await ctx1.plugin(ToolRuntime)
await ctx1.plugin(AgentRegistry)
@ -371,6 +375,7 @@ describe('config-driven session id', () => {
const ctx2 = new Context()
await ctx2.plugin(LlmRuntime)
await ctx2.plugin(SessionStore)
await ctx2.plugin(SessionProjectionRegistry)
await ctx2.plugin(SystemPrompt)
await ctx2.plugin(ToolRuntime)
await ctx2.plugin(AgentRegistry)
@ -395,6 +400,7 @@ describe('config-driven session id', () => {
const ctx1 = new Context()
await ctx1.plugin(LlmRuntime)
await ctx1.plugin(SessionStore)
await ctx1.plugin(SessionProjectionRegistry)
await ctx1.plugin(SystemPrompt)
await ctx1.plugin(ToolRuntime)
await ctx1.plugin(AgentRegistry)
@ -411,6 +417,7 @@ describe('config-driven session id', () => {
const ctx2 = new Context()
await ctx2.plugin(LlmRuntime)
await ctx2.plugin(SessionStore)
await ctx2.plugin(SessionProjectionRegistry)
await ctx2.plugin(SystemPrompt)
await ctx2.plugin(ToolRuntime)
await ctx2.plugin(AgentRegistry)
@ -436,6 +443,7 @@ describe('config-driven session id', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -6,6 +6,7 @@ import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRuntime, { defineContentToolFixture, type PostToolDecision } from '@deepseek-ai/dsh-tools'
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { ReactLoopAgent } from '../src/agent.ts'
import InvariantRegistry from '@deepseek-ai/dsh-invariants'
import * as SessionInvariant from '@deepseek-ai/dsh-session/invariant'
@ -30,6 +31,7 @@ async function harness(adapter: MockAdapter) {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -525,6 +527,7 @@ describe('turn numbering continues across seeded sessions', () => {
const ctx2 = new Context()
await ctx2.plugin(LlmRuntime)
await ctx2.plugin(SessionStore)
await ctx2.plugin(SessionProjectionRegistry)
await ctx2.plugin(SystemPrompt)
await ctx2.plugin(ToolRuntime)
await ctx2.plugin(AgentRegistry)
@ -676,6 +679,7 @@ describe('turn and step boundary recovery', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -1108,6 +1112,7 @@ describe('disposal and cancellation during pre-step assembly', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -1158,6 +1163,7 @@ describe('disposal and cancellation during pre-step assembly', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -1208,6 +1214,7 @@ describe('disposal and cancellation during pre-step assembly', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -1254,6 +1261,7 @@ describe('disposal and cancellation during pre-step assembly', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -1302,6 +1310,7 @@ describe('disposal and cancellation during pre-step assembly', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -8,6 +8,7 @@ import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { MockAdapter, textResponse, toolCallResponse } from './mock-adapter.ts'
function driverDone(agent: Agent): Promise<void> {
@ -18,6 +19,7 @@ async function harness(adapter: MockAdapter) {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -16,6 +16,7 @@ import AgentRegistry, {
} from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { MockAdapter, textResponse, toolCallResponse } from './mock-adapter.ts'
/**
@ -31,6 +32,7 @@ async function harness(adapter: MockAdapter) {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -7,6 +7,7 @@ import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { MockAdapter, maxTokensResponse, textResponse, toolCallResponse } from './mock-adapter.ts'
function driverDone(agent: Agent): Promise<void> {
@ -17,6 +18,7 @@ async function harness(adapter: MockAdapter, persona = '') {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt, { persona })
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -1433,6 +1435,7 @@ describe('agent loop', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -1457,6 +1460,7 @@ describe('agent loop', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -20,6 +20,7 @@ import ToolRuntime from '@deepseek-ai/dsh-tools'
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import fc from 'fast-check'
/** A never-exhausting adapter: every model call returns the same short reply. */
@ -39,6 +40,7 @@ async function harness() {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -8,6 +8,7 @@ import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
/**
@ -41,6 +42,7 @@ async function loopHarness(): Promise<Context> {
const created = new Context()
await created.plugin(LlmRuntime)
await created.plugin(SessionStore)
await created.plugin(SessionProjectionRegistry)
await created.plugin(SystemPrompt, { persona: SYSTEM })
await created.plugin(ToolRuntime)
await created.plugin(AgentRegistry)

View file

@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import AgentRegistry from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import LlmRuntime, { createUserMessage, LlmError } from '@deepseek-ai/dsh-llm'
import type { LlmFailure, ResolvedRetryPolicy } from '@deepseek-ai/dsh-llm'
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
@ -13,6 +14,7 @@ async function harness(adapter: MockAdapter): Promise<Context> {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -15,6 +15,7 @@ import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { MockAdapter, textResponse, toolCallResponse } from './mock-adapter.ts'
async function harness(adapter: MockAdapter, persona = 'stable base') {
@ -28,6 +29,7 @@ async function harnessRoutes(
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt, { persona })
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -255,6 +257,7 @@ describe('request stability across the loop', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt, { persona: 'stable base' })
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -373,6 +376,7 @@ describe('request stability across the loop', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt, { persona: 'stable base' })
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -13,6 +13,7 @@ import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { MockAdapter, textResponse } from './mock-adapter.ts'
const dirs: string[] = []
@ -28,6 +29,7 @@ async function mountPersistentHarness(root: string, adapter: MockAdapter): Promi
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -244,6 +246,7 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
const ctx2 = new Context()
await ctx2.plugin(LlmRuntime)
await ctx2.plugin(SessionStore)
await ctx2.plugin(SessionProjectionRegistry)
await ctx2.plugin(SystemPrompt)
await ctx2.plugin(ToolRuntime)
await ctx2.plugin(AgentRegistry)
@ -272,6 +275,7 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
const ctx2 = new Context()
await ctx2.plugin(LlmRuntime)
await ctx2.plugin(SessionStore)
await ctx2.plugin(SessionProjectionRegistry)
await ctx2.plugin(SystemPrompt)
await ctx2.plugin(ToolRuntime)
await ctx2.plugin(AgentRegistry)
@ -522,6 +526,7 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -585,6 +590,7 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
const ctx2 = new Context()
await ctx2.plugin(LlmRuntime)
await ctx2.plugin(SessionStore)
await ctx2.plugin(SessionProjectionRegistry)
await ctx2.plugin(SystemPrompt)
await ctx2.plugin(ToolRuntime)
await ctx2.plugin(AgentRegistry)
@ -617,6 +623,7 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
const ctx2 = new Context()
await ctx2.plugin(LlmRuntime)
await ctx2.plugin(SessionStore)
await ctx2.plugin(SessionProjectionRegistry)
await ctx2.plugin(SystemPrompt)
await ctx2.plugin(ToolRuntime)
await ctx2.plugin(AgentRegistry)
@ -653,6 +660,7 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
const ctx2 = new Context()
await ctx2.plugin(LlmRuntime)
await ctx2.plugin(SessionStore)
await ctx2.plugin(SessionProjectionRegistry)
await ctx2.plugin(SystemPrompt)
await ctx2.plugin(ToolRuntime)
await ctx2.plugin(AgentRegistry)
@ -686,6 +694,7 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -875,6 +884,7 @@ describe('configured-start failure edges', () => {
const configured = new Context()
await configured.plugin(LlmRuntime)
await configured.plugin(SessionStore)
await configured.plugin(SessionProjectionRegistry)
await configured.plugin(SystemPrompt)
await configured.plugin(ToolRuntime)
await configured.plugin(AgentRegistry)
@ -920,6 +930,7 @@ describe('configured-start failure edges', () => {
const configured = new Context()
await configured.plugin(LlmRuntime)
await configured.plugin(SessionStore)
await configured.plugin(SessionProjectionRegistry)
await configured.plugin(SystemPrompt)
await configured.plugin(ToolRuntime)
await configured.plugin(AgentRegistry)

View file

@ -10,6 +10,7 @@ import AgentRegistry, { agentEvents, assembleContextFor } from '@deepseek-ai/dsh
import type { Agent } from '@deepseek-ai/dsh-agent'
import { scopeOf } from '@deepseek-ai/dsh-scope'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import { MockAdapter, textResponse } from './mock-adapter.ts'
@ -17,6 +18,7 @@ async function harnessWithLoop(adapter: MockAdapter = new MockAdapter([textRespo
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt, { persona: 'You are the deployment.' })
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -8,6 +8,7 @@ import SessionStore from '@deepseek-ai/dsh-session'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRuntime from '@deepseek-ai/dsh-tools'
import AgentRegistry from '@deepseek-ai/dsh-agent'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { SettingsProvider } from '@deepseek-ai/dsh-settings'
import type { SettingsNamespace } from '@deepseek-ai/dsh-settings'
import AgentLoop, { AGENT_LOOP_SETTINGS_NAMESPACE } from '@deepseek-ai/dsh-agent-loop'
@ -34,6 +35,7 @@ async function boot(): Promise<{ ctx: Context; settingsFiber: Fiber; loopFiber:
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

View file

@ -12,6 +12,7 @@ import LlmRuntime from '@deepseek-ai/dsh-llm'
import ToolRuntime, { defineContentToolFixture, TOOL_ABORTED_BEFORE_DISPATCH, TOOL_RUNTIME_SCHEDULER, type PostToolDecision, type PreToolDecision } from '@deepseek-ai/dsh-tools'
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop, { DEFAULT_MAX_PARALLEL_TOOL_CALLS } from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { MockAdapter, textResponse } from './mock-adapter.ts'
import { CodeRuntime } from '@deepseek-ai/dsh-code-runtime'
import type { CodeRunRequest, CodeRunResult } from '@deepseek-ai/dsh-code-runtime'
@ -20,6 +21,7 @@ async function harness(adapter: MockAdapter, maxParallelToolCalls?: number) {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt, { persona: '' })
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -267,17 +269,24 @@ describe('tool-call scheduler: rolling pool honors maxParallelToolCalls', () =>
await expect(harness(new MockAdapter([]), 1.5)).rejects.toThrow()
})
it('defensively rejects invalid caps when direct construction bypasses the config schema', () => {
expect(() => new AgentLoop(new Context(), { agents: [], maxParallelToolCalls: 0 }))
it('defensively rejects invalid caps when direct construction bypasses the config schema', async () => {
const ctx = new Context()
await ctx.plugin(SessionProjectionRegistry)
expect(() => new AgentLoop(ctx, { agents: [], maxParallelToolCalls: 0 }))
.toThrow('maxParallelToolCalls must be a positive integer')
expect(() => new AgentLoop(new Context(), { agents: [], maxParallelToolCalls: 1.5 }))
await ctx.fiber.dispose()
const other = new Context()
await other.plugin(SessionProjectionRegistry)
expect(() => new AgentLoop(other, { agents: [], maxParallelToolCalls: 1.5 }))
.toThrow('maxParallelToolCalls must be a positive integer')
await other.fiber.dispose()
})
it('defaults the cap when direct construction bypasses the config schema', async () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt, { persona: '' })
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -346,6 +355,7 @@ describe('tool-call scheduler: rolling pool honors maxParallelToolCalls', () =>
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt, { persona: '' })
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)
@ -704,6 +714,7 @@ describe('code-mode native-tool denial through the agent loop', () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt, { persona: '' })
await ctx.plugin(ToolRuntime, { mode: 'code' })
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- FakeCodeRuntime is an internal test helper with an opaque type shape

View file

@ -17,12 +17,14 @@ import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { MockAdapter, textResponse } from './mock-adapter.ts'
async function harness(adapter: MockAdapter, toolOrder?: SystemPromptConfig['toolOrder']) {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(SystemPrompt, { persona: 'stable base', ...toolOrder !== undefined ? { toolOrder } : {} })
await ctx.plugin(ToolRuntime)
await ctx.plugin(AgentRegistry)

Some files were not shown because too many files have changed in this diff Show more