dsh-plugin-guide 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.es.md +1 -1
  2. package/README.hi.md +1 -1
  3. package/README.md +1 -1
  4. package/README.pt.md +1 -1
  5. package/README.zh.md +1 -1
  6. package/dist/dsh-plugin-dev.js +3 -3
  7. package/guide/plugin-dev-guide.md +1 -1
  8. package/guide/quick-reference.es.md +1 -1
  9. package/guide/quick-reference.md +1 -1
  10. package/guide/quick-reference.pt.md +1 -1
  11. package/guide/quick-reference.zh-CN.md +1 -1
  12. package/package.json +3 -3
  13. package/references/official-docs/AGENTS.md +1 -1
  14. package/references/official-docs/SNAPSHOT.md +3 -3
  15. package/references/official-docs/docs/architecture.i18n.yaml +2 -2
  16. package/references/official-docs/docs/architecture.md +2 -1
  17. package/references/official-docs/docs/architecture.zh.md +2 -1
  18. package/references/official-docs/docs/capability-seams.i18n.yaml +2 -2
  19. package/references/official-docs/docs/capability-seams.md +3 -1
  20. package/references/official-docs/docs/capability-seams.zh.md +3 -1
  21. package/references/official-docs/docs/config-catalog.i18n.yaml +2 -2
  22. package/references/official-docs/docs/config-catalog.md +96 -54
  23. package/references/official-docs/docs/config-catalog.zh.md +93 -51
  24. package/references/official-docs/docs/cookbook/adding-a-package.i18n.yaml +2 -2
  25. package/references/official-docs/docs/cookbook/adding-a-package.md +1 -1
  26. package/references/official-docs/docs/cookbook/adding-a-package.zh.md +1 -1
  27. package/references/official-docs/docs/event-producer-consumer.i18n.yaml +2 -2
  28. package/references/official-docs/docs/event-producer-consumer.md +17 -17
  29. package/references/official-docs/docs/event-producer-consumer.zh.md +17 -17
  30. package/references/official-docs/docs/module-graph.i18n.yaml +2 -2
  31. package/references/official-docs/docs/module-graph.md +193 -361
  32. package/references/official-docs/docs/module-graph.zh.md +193 -361
  33. package/references/official-docs/docs/persistence-catalog.i18n.yaml +2 -2
  34. package/references/official-docs/docs/persistence-catalog.md +41 -41
  35. package/references/official-docs/docs/persistence-catalog.zh.md +40 -40
  36. package/references/official-docs/docs/subsystems/agent-team.i18n.yaml +2 -2
  37. package/references/official-docs/docs/subsystems/agent-team.md +4 -3
  38. package/references/official-docs/docs/subsystems/agent-team.zh.md +4 -3
  39. package/references/official-docs/docs/subsystems/code-runtime.i18n.yaml +2 -2
  40. package/references/official-docs/docs/subsystems/code-runtime.md +7 -3
  41. package/references/official-docs/docs/subsystems/code-runtime.zh.md +7 -3
  42. package/references/official-docs/docs/subsystems/commands.i18n.yaml +2 -2
  43. package/references/official-docs/docs/subsystems/commands.md +1 -1
  44. package/references/official-docs/docs/subsystems/commands.zh.md +1 -1
  45. package/references/official-docs/docs/subsystems/compaction.i18n.yaml +2 -2
  46. package/references/official-docs/docs/subsystems/compaction.md +9 -9
  47. package/references/official-docs/docs/subsystems/compaction.zh.md +9 -9
  48. package/references/official-docs/docs/subsystems/core.i18n.yaml +2 -2
  49. package/references/official-docs/docs/subsystems/core.md +4 -3
  50. package/references/official-docs/docs/subsystems/core.zh.md +4 -3
  51. package/references/official-docs/docs/subsystems/feedback.i18n.yaml +2 -2
  52. package/references/official-docs/docs/subsystems/feedback.md +2 -2
  53. package/references/official-docs/docs/subsystems/feedback.zh.md +2 -2
  54. package/references/official-docs/docs/subsystems/persistence.i18n.yaml +2 -2
  55. package/references/official-docs/docs/subsystems/persistence.md +179 -187
  56. package/references/official-docs/docs/subsystems/persistence.zh.md +179 -187
  57. package/references/official-docs/docs/subsystems/schedule.i18n.yaml +2 -2
  58. package/references/official-docs/docs/subsystems/schedule.md +1 -1
  59. package/references/official-docs/docs/subsystems/schedule.zh.md +1 -1
  60. package/references/official-docs/docs/subsystems/session-projection.i18n.yaml +2 -2
  61. package/references/official-docs/docs/subsystems/session-projection.md +21 -17
  62. package/references/official-docs/docs/subsystems/session-projection.zh.md +21 -17
  63. package/references/official-docs/docs/subsystems/session-query.i18n.yaml +2 -2
  64. package/references/official-docs/docs/subsystems/session-query.md +19 -13
  65. package/references/official-docs/docs/subsystems/session-query.zh.md +19 -13
  66. package/references/official-docs/docs/subsystems/session-title.i18n.yaml +2 -2
  67. package/references/official-docs/docs/subsystems/session-title.md +5 -5
  68. package/references/official-docs/docs/subsystems/session-title.zh.md +5 -5
  69. package/references/official-docs/docs/subsystems/session.i18n.yaml +2 -2
  70. package/references/official-docs/docs/subsystems/session.md +94 -34
  71. package/references/official-docs/docs/subsystems/session.zh.md +94 -34
  72. package/references/official-docs/docs/subsystems/slots.i18n.yaml +2 -2
  73. package/references/official-docs/docs/subsystems/slots.md +1 -0
  74. package/references/official-docs/docs/subsystems/slots.zh.md +1 -0
  75. package/references/official-docs/docs/subsystems/storage.i18n.yaml +2 -2
  76. package/references/official-docs/docs/subsystems/storage.md +28 -5
  77. package/references/official-docs/docs/subsystems/storage.zh.md +28 -5
  78. package/references/official-docs/docs/subsystems/subagent.i18n.yaml +2 -2
  79. package/references/official-docs/docs/subsystems/subagent.md +32 -88
  80. package/references/official-docs/docs/subsystems/subagent.zh.md +31 -87
  81. package/references/official-docs/docs/subsystems/token-meter.i18n.yaml +2 -2
  82. package/references/official-docs/docs/subsystems/token-meter.md +2 -2
  83. package/references/official-docs/docs/subsystems/token-meter.zh.md +2 -2
  84. package/references/official-docs/docs/tool-catalog.i18n.yaml +2 -2
  85. package/references/official-docs/docs/tool-catalog.md +9 -63
  86. package/references/official-docs/docs/tool-catalog.zh.md +8 -62
  87. package/references/official-docs/docs/web-styling.i18n.yaml +2 -2
  88. package/references/official-docs/docs/web-styling.md +3 -0
  89. package/references/official-docs/docs/web-styling.zh.md +3 -0
  90. package/references/official-docs/packages/AGENTS.md +2 -2
  91. package/templates/js/README.es.md +1 -1
  92. package/templates/js/README.hi.md +1 -1
  93. package/templates/js/README.md +1 -1
  94. package/templates/js/README.pt.md +1 -1
  95. package/templates/js/README.zh.md +1 -1
  96. package/templates/ts/README.es.md +1 -1
  97. package/templates/ts/README.hi.md +1 -1
  98. package/templates/ts/README.md +1 -1
  99. package/templates/ts/README.pt.md +1 -1
  100. package/templates/ts/README.zh.md +1 -1
@@ -44,7 +44,7 @@ interface StorageBackend {
44
44
  }
45
45
  ```
46
46
 
47
- A backend owns one medium (a file-tree root, a database file) and exposes optional operation groups; `kv` is the only shipped group. `KvFacet.open(descriptor)` opens one named unit — `KvUnitDescriptor` carries the name, format version, table names, and whether a global singleton slot exists — and returns a `KvUnit` with `loadAll`, `putRecord`, `deleteRecord`, `setGlobal`, and `close`. Unit and table names must match `UNIT_NAME_RE` (safe as a file name and as a SQL identifier segment); record keys are arbitrary strings that never reach file paths. A unit does not serialize concurrent writes — ordering belongs to the caller — but each single call is atomic on the medium and durable once resolved. A medium stamped with a different version rejects `version-mismatch`; one that cannot be parsed as the unit rejects `malformed-medium` (no migration, pre-release stance). [`backend.ts`](../../packages/storage/storage/src/backend.ts) is the normative clause-by-clause contract, and the shared conformance suite in [`tests/contract.ts`](../../packages/storage/storage/tests/contract.ts) checks every clause against each backend. The [json backend](../../packages/storage/storage-json/README.md) republishes one whole human-readable file per unit atomically; the [sqlite backend](../../packages/storage/storage-sqlite/README.md) stores one document per row in one database for frequently updated data.
47
+ A backend owns one medium (a file-tree root, a database file) and exposes optional operation groups; `kv` is the only shipped group. `KvFacet.open(descriptor)` opens one named unit — `KvUnitDescriptor` carries the name, current format version, optional compatible record versions, table names, and whether a global singleton slot exists — and returns a `KvUnit` with `loadAll`, `putRecord`, `deleteRecord`, `setGlobal`, and `close`. Unit and table names must match `UNIT_NAME_RE` (safe as a file name and as a SQL identifier segment); record keys are arbitrary strings that never reach file paths. A unit does not serialize concurrent writes — ordering belongs to the caller — but each single call is atomic on the medium and durable once resolved. A `single` medium stamped with a different version rejects `version-mismatch`; a `per-record` document stamped outside the accepted set reads as absent. A medium that cannot be parsed as the unit rejects `malformed-medium`. [`backend.ts`](../../packages/storage/storage/src/backend.ts) is the normative clause-by-clause contract, and the shared conformance suite in [`tests/contract.ts`](../../packages/storage/storage/tests/contract.ts) checks every clause against each backend. The [json backend](../../packages/storage/storage-json/README.md) republishes one whole human-readable file per unit atomically; the [sqlite backend](../../packages/storage/storage-sqlite/README.md) stores one document per row in one database for frequently updated data.
48
48
 
49
49
  ## Declaring a domain
50
50
 
@@ -55,16 +55,36 @@ A domain is declared once by its owning package as a spec object — the single
55
55
  interface DomainSpec {
56
56
  /** Domain name; must match `UNIT_NAME_RE` (doubles as the backend unit name). */
57
57
  readonly name: string
58
- /** Domain format version; a medium stamped with a different version rejects at open. */
58
+ /** Current domain format version; reads enforce it according to the selected layout. */
59
59
  readonly version: number
60
60
  /**
61
61
  * Medium layout for the backend unit: `single` (the default) stores the
62
62
  * whole unit as one document; `per-record` stores each record as its own
63
63
  * document, for units whose records are large, sparse, or individually
64
- * disposable — the projection cache — and scopes version bumps per record
65
- * (a stale record document is discarded, never migrated).
64
+ * disposable — the projection cache — and scopes version checks per record
65
+ * (an unaccepted record document is discarded, never migrated).
66
66
  */
67
67
  readonly layout?: 'single' | 'per-record'
68
+ /**
69
+ * Older domain versions whose stored records the current record schemas
70
+ * also accept (the declaring owner vouches for that, typically by
71
+ * declaring the fields older records lack as optional). `per-record` backends
72
+ * read documents stamped with a listed version instead of discarding them,
73
+ * and accept a legacy whole-unit file so stamped for the one-time
74
+ * bootstrap; writes always stamp {@link version}.
75
+ */
76
+ readonly compatibleVersions?: readonly number[]
77
+ /**
78
+ * What `open` does with a stored table record that fails its zod schema.
79
+ * Absent (the default), the whole open rejects with `invalid-record` —
80
+ * right for authoritative data. `'backup-and-skip'` is for domains whose
81
+ * records are disposable derived data: the backend moves the record's
82
+ * document aside (`KvUnit.backupRecord`), the failure is logged with
83
+ * its cause, and the open continues with the record absent. A backend
84
+ * without `backupRecord` (no per-record document to move) falls back
85
+ * to the rejecting default. The global slot always rejects.
86
+ */
87
+ readonly invalidRecords?: 'backup-and-skip'
68
88
  /** Optional global singleton slot. */
69
89
  readonly global?: DomainGlobalSpec<unknown>
70
90
  /** Table declarations keyed by table name; each name must match `UNIT_NAME_RE`. */
@@ -180,7 +200,10 @@ The mounted domain facility. Opens declared domains over routed backends; one fa
180
200
  * (`facet-unsupported`); open the unit projected from the spec (backend
181
201
  * `version-mismatch`/`malformed-medium` pass through); load and validate
182
202
  * every stored record against the spec's zod schemas (`invalid-record`
183
- * with the offending table and key); construct the domain.
203
+ * with the offending table and key unless the spec declares
204
+ * `invalidRecords: 'backup-and-skip'` and the unit can move documents aside, in
205
+ * which case the failing record is backed up, logged, and skipped);
206
+ * construct the domain.
184
207
  *
185
208
  * Lifecycle: the CALLER owns the returned handle and closes it via
186
209
  * `Domain.close()` (typically as its own `ctx.effect` disposer) — the
@@ -44,7 +44,7 @@ interface StorageBackend {
44
44
  }
45
45
  ```
46
46
 
47
- 一个后端拥有一个介质(一棵文件树的根目录、一个数据库文件),并提供可选的操作组;`kv` 是唯一已交付的操作组。`KvFacet.open(descriptor)` 打开一个具名 unit——`KvUnitDescriptor` 携带名称、格式版本、表名清单,以及是否存在全局单例 slot——并返回提供 `loadAll`、`putRecord`、`deleteRecord`、`setGlobal` 和 `close` 的 `KvUnit`。unit 名与表名必须匹配 `UNIT_NAME_RE`(既可安全用作文件名,也可安全用作 SQL 标识符片段);记录键是任意字符串,绝不进入文件路径。unit 不对并发写入做串行化——顺序由调用方负责——但每次单独调用在介质上都是原子的,且 resolve 后即已持久。介质上记录的版本与之不同时拒绝 `version-mismatch`;无法按该 unit 解析的介质拒绝 `malformed-medium`(不做迁移:预发布立场)。[`backend.ts`](../../packages/storage/storage/src/backend.ts) 是逐条款的规范性约定,[`tests/contract.ts`](../../packages/storage/storage/tests/contract.ts) 中的共享一致性套件会针对每个后端检查每项条款。[json 后端](../../packages/storage/storage-json/README.zh.md)以原子方式为每个 unit 整文件重新发布一份人类可读文件;[sqlite 后端](../../packages/storage/storage-sqlite/README.zh.md)在单个数据库中每行存储一份文档,用于频繁更新的数据。
47
+ 一个后端拥有一个介质(一棵文件树的根目录、一个数据库文件),并提供可选的操作组;`kv` 是唯一已交付的操作组。`KvFacet.open(descriptor)` 打开一个具名 unit——`KvUnitDescriptor` 携带名称、当前格式版本、可选的兼容记录版本、表名清单,以及是否存在全局单例 slot——并返回提供 `loadAll`、`putRecord`、`deleteRecord`、`setGlobal` 和 `close` 的 `KvUnit`。unit 名与表名必须匹配 `UNIT_NAME_RE`(既可安全用作文件名,也可安全用作 SQL 标识符片段);记录键是任意字符串,绝不进入文件路径。unit 不对并发写入做串行化——顺序由调用方负责——但每次单独调用在介质上都是原子的,且 resolve 后即已持久。`single` 介质上记录的版本不同时拒绝 `version-mismatch`;`per-record` 文档的版本在接受集合之外时读作不存在。无法按该 unit 解析的介质拒绝 `malformed-medium`。[`backend.ts`](../../packages/storage/storage/src/backend.ts) 是逐条款的规范性约定,[`tests/contract.ts`](../../packages/storage/storage/tests/contract.ts) 中的共享一致性套件会针对每个后端检查每项条款。[json 后端](../../packages/storage/storage-json/README.zh.md)以原子方式为每个 unit 整文件重新发布一份人类可读文件;[sqlite 后端](../../packages/storage/storage-sqlite/README.zh.md)在单个数据库中每行存储一份文档,用于频繁更新的数据。
48
48
 
49
49
  ## 声明领域
50
50
 
@@ -55,16 +55,36 @@ interface StorageBackend {
55
55
  interface DomainSpec {
56
56
  /** Domain name; must match `UNIT_NAME_RE` (doubles as the backend unit name). */
57
57
  readonly name: string
58
- /** Domain format version; a medium stamped with a different version rejects at open. */
58
+ /** Current domain format version; reads enforce it according to the selected layout. */
59
59
  readonly version: number
60
60
  /**
61
61
  * Medium layout for the backend unit: `single` (the default) stores the
62
62
  * whole unit as one document; `per-record` stores each record as its own
63
63
  * document, for units whose records are large, sparse, or individually
64
- * disposable — the projection cache — and scopes version bumps per record
65
- * (a stale record document is discarded, never migrated).
64
+ * disposable — the projection cache — and scopes version checks per record
65
+ * (an unaccepted record document is discarded, never migrated).
66
66
  */
67
67
  readonly layout?: 'single' | 'per-record'
68
+ /**
69
+ * Older domain versions whose stored records the current record schemas
70
+ * also accept (the declaring owner vouches for that, typically by
71
+ * declaring the fields older records lack as optional). `per-record` backends
72
+ * read documents stamped with a listed version instead of discarding them,
73
+ * and accept a legacy whole-unit file so stamped for the one-time
74
+ * bootstrap; writes always stamp {@link version}.
75
+ */
76
+ readonly compatibleVersions?: readonly number[]
77
+ /**
78
+ * What `open` does with a stored table record that fails its zod schema.
79
+ * Absent (the default), the whole open rejects with `invalid-record` —
80
+ * right for authoritative data. `'backup-and-skip'` is for domains whose
81
+ * records are disposable derived data: the backend moves the record's
82
+ * document aside (`KvUnit.backupRecord`), the failure is logged with
83
+ * its cause, and the open continues with the record absent. A backend
84
+ * without `backupRecord` (no per-record document to move) falls back
85
+ * to the rejecting default. The global slot always rejects.
86
+ */
87
+ readonly invalidRecords?: 'backup-and-skip'
68
88
  /** Optional global singleton slot. */
69
89
  readonly global?: DomainGlobalSpec<unknown>
70
90
  /** Table declarations keyed by table name; each name must match `UNIT_NAME_RE`. */
@@ -180,7 +200,10 @@ The mounted domain facility. Opens declared domains over routed backends; one fa
180
200
  * (`facet-unsupported`); open the unit projected from the spec (backend
181
201
  * `version-mismatch`/`malformed-medium` pass through); load and validate
182
202
  * every stored record against the spec's zod schemas (`invalid-record`
183
- * with the offending table and key); construct the domain.
203
+ * with the offending table and key unless the spec declares
204
+ * `invalidRecords: 'backup-and-skip'` and the unit can move documents aside, in
205
+ * which case the failing record is backed up, logged, and skipped);
206
+ * construct the domain.
184
207
  *
185
208
  * Lifecycle: the CALLER owns the returned handle and closes it via
186
209
  * `Domain.close()` (typically as its own `ctx.effect` disposer) — the
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write docs/subsystems/subagent.md
5
- subagent.md: 095d73f43e75add0fab68aee97a6e6c0d27dbed2
6
- subagent.zh.md: e3df4c982cec86cf1701bb807715a20e18978677
5
+ subagent.md: 1d111706ee93328e47b0cf2ed069a8fd9cdb56ec
6
+ subagent.zh.md: 5b7eca88e9b981c2e582a2af70140f4750914632
@@ -4,7 +4,7 @@ English | [中文](subagent.zh.md)
4
4
 
5
5
  The subagent seam lets an agent delegate work to a child agent. Like [bash](shell.md), it is **one optional capability**, not part of the agent loop, so its types live here rather than in [core.md](core.md). It differs from the other capability seams because **multiple provider implementations coexist** in one context, registered by name (`ctx.subagents`), while bash allows only one executor. Its registry follows the [LLM adapter registry](llm-streaming.md), not the single-service bash executor.
6
6
 
7
- Service Definition: [dsh-subagent](../../packages/subagent/subagent) (`ctx.subagents` + the vocabulary below). Service Providers are sibling packages (`dsh-subagent-spawn-in-process`, `dsh-subagent-fork-in-process`, `dsh-subagent-acp`, `dsh-subagent-codex`, `dsh-subagent-claude-code`, `dsh-subagent-dsh-sdk`); the model-facing Consumers are [dsh-tool-subagent](../../packages/subagent/tool-subagent) (per-provider delegation), [dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control) (the optional global `send_message`, `interrupt_agent`, and `list_agents` controls), and [dsh-tool-subagent-report](../../packages/subagent/tool-subagent-report) (the optional child-scoped `report` return channel). The same `ctx.subagents` service owns continuable-child orchestration through an internal activation manager and read-only child and descendant discovery straight from the session store and optional session persistence. Product-provider rationale lives in [the Codex and Claude Code Agent Note](../../.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md); common-seam rationale lives in [the subagent Agent Note](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md), [the continuable subagents Agent Note](../../.agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.md), [the report-tool Agent Note](../../.agents/notes/implemented/feature/2026-07-30-continuable-subagent-report-tool.md), [the durable catalog Agent Note](../../.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md), [the list-identity-projection Agent Note](../../.agents/notes/implemented/architecture/2026-08-06-subagent-list-identity-projection.md), and [the merged-service Agent Note](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md).
7
+ Service Definition: [dsh-subagent](../../packages/subagent/subagent) (`ctx.subagents` + the vocabulary below). Service Providers are sibling packages (`dsh-subagent-spawn-in-process`, `dsh-subagent-fork-in-process`, `dsh-subagent-acp`, `dsh-subagent-codex`, `dsh-subagent-claude-code`, `dsh-subagent-dsh-sdk`); the model-facing Consumers are [dsh-tool-subagent](../../packages/subagent/tool-subagent) (per-provider delegation) and [dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control) (the optional global `send_message`, `interrupt_agent`, and `list_agents` controls). The same `ctx.subagents` service owns continuable-child orchestration through an internal activation manager and read-only child and descendant discovery straight from the session store and optional session persistence. Product-provider rationale lives in [the Codex and Claude Code Agent Note](../../.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md); common-seam rationale lives in [the subagent Agent Note](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md), [the continuable subagents Agent Note](../../.agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.md), [the adjacent-Agent messaging Agent Note](../../.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.md), [the durable catalog Agent Note](../../.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md), [the list-identity-projection Agent Note](../../.agents/notes/implemented/architecture/2026-08-06-subagent-list-identity-projection.md), and [the merged-service Agent Note](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md).
8
8
 
9
9
  Sources: [`packages/subagent/subagent/src/types.ts`](../../packages/subagent/subagent/src/types.ts), [`packages/subagent/subagent/src/index.ts`](../../packages/subagent/subagent/src/index.ts), and [`packages/subagent/subagent/src/continuation.ts`](../../packages/subagent/subagent/src/continuation.ts)
10
10
 
@@ -133,21 +133,21 @@ persisted Session
133
133
 
134
134
  `SubagentRuntime.startContinuable()` reserves the stable child id, snapshots the versioned `subagent/descriptor` payload, asks the named provider for its detached `ContinuableCreateSpec`, creates the child Agent through a private activation-owner scope, establishes any continuable-parent ownership, and submits the initial prompt. It resolves with `{ childId, messageId }` when inbox acceptance yields the message id — without waiting for the turn to start or for the message to enter the Session log. Every failure before that acceptance rejects with neither id, disposing any created handle and rolling back the Activation and parent ownership.
135
135
 
136
- `SubagentRuntime.followup()` is the sole continuation-message operation, and routing depends only on Activation residency:
136
+ `SubagentRuntime.sendMessage()` is the sole model-authored message operation. It accepts the exact live sender plus a target id, permits only a direct parent or direct continuable child, derives sender attribution itself, and routes a direct-child target by Activation residency:
137
137
 
138
- | Activation state | `followup` |
138
+ | Target Activation state | `sendMessage` |
139
139
  |---|---|
140
- | `running` | enqueue in the same Activation |
141
- | `waiting` | wake the same Activation |
142
- | no Activation | cold-resume a new Activation |
140
+ | `running` | steer the nearest step in the same Activation |
141
+ | `waiting` | wake and steer the same Activation |
142
+ | no Activation | cold-resume a new Activation, then steer it |
143
143
 
144
144
  `running` means the Agent has an active admission or turn, or waking inbox work; `waiting` means it is quiescent but still owns at least one child Activation that has not completed disposal; `settled` means quiescent with every owned child disposed, at which point the manager disposes the [`AgentHandle`](core.md#creation-and-ownership) and removes the Activation. The manager derives these internal conditions from Agent quiescence and the owned-child set rather than maintaining a second execution state machine.
145
145
 
146
- The Agent inbox is the only queue. Every continuation message becomes one `Agent.followup()` FIFO turn, so accepted messages have one observable order and a follow-up cannot redirect a turn already underway. Successful delivery returns the accepted `MessageId`; the existing `agent/inbox/inserted`, `agent/inbox/claimed`, and `agent/inbox/discarded` events remain the message-lifecycle observations, and the continuation layer defines no subagent-specific delivery route.
146
+ The Agent inbox is the only queue. Every Agent message uses `Agent.steer()`: an idle target starts a turn, while a running target claims it at the nearest step boundary. Successful delivery returns the accepted `MessageId`; the existing `agent/inbox/inserted`, `agent/inbox/claimed`, and `agent/inbox/discarded` events remain the message-lifecycle observations, and the continuation layer defines no subagent-specific delivery route.
147
147
 
148
- Follow-up authority comes from an exact live Agent tool context. The authenticated Agent must be the durable child's direct parent recorded in `SessionHeader.parentSession`. `MessageSource` and `senderSessionId` record who supplied an admitted message but grant no authority; the optional model-facing tool uses `CoordinatorMessageSource`.
148
+ Authority comes from the exact live sender. Parent-to-child delivery requires the target's `SessionHeader.parentSession` to name the sender; child-to-parent delivery requires the sender's resident Activation to name the target. Siblings, ancestors beyond one edge, self-targets, stale Agent objects, and one-shot children are rejected. Each accepted message is framed as `Agent <sender-id> sent a message:` and records `AgentMessageSource`; provenance records the sender but grants no authority.
149
149
 
150
- For both operations the caller signal owns lookup, materialization, and admission only until inbox acceptance. Afterwards the manager owns the Activation independently: later caller cancellation neither cancels the accepted turn nor disposes the child, and the seam exposes no steering operation.
150
+ For `startContinuable()` and `sendMessage()`, the caller signal owns lookup, materialization, and admission only until inbox acceptance. Afterwards the manager owns the Activation independently: later caller cancellation neither cancels the accepted turn nor disposes the child. Human browser prompts remain a separate private Queue adapter and therefore still produce distinct FIFO turns.
151
151
 
152
152
  `SubagentRuntime.interrupt(targetSessionId, authority)` is the one public stop: it authorizes synchronously, issues `Agent.cancel(cause, { keepInbox: true })` on the live target, and returns without awaiting quiescence. The Activation, its unclaimed pending inbox work, and published descendants are untouched; work already claimed into the interrupted turn is not requeued. Once the interrupted driver is idle, a waking send resumes the parked FIFO queue. An absent target — unknown, one-shot, or already settled — and a manager-less composition are accepted no-ops. For a live target, a mismatched parent address or caller outside its live ancestry rejects with `UNAUTHORIZED`; stale ancestor objects and self-targeting ancestor requests reject before target lookup.
153
153
 
@@ -167,21 +167,19 @@ Every Activation owns its `AgentHandle` and an `ownedChildren: Set<SessionId>`;
167
167
  Final settlement awaits `ctx.sessions.flush(session)` but ignores its participation boolean because an arbitrary listener cannot prove that a persistence backend stored the state. Rejection is logged without failing the Activation, and the manager still disposes the handle and releases ownership; the persisted child state may then be missing or stale on a later resume. Manager unload invokes an internal manager-wide drain that closes admission and disposes every live forest; `drainContinuableDescendants(parents)` closes admission only below exact live host-owned Agents and disposes their continuable descendants while unrelated forests remain live. Both await already-admitted materializations in their scope, propagate cancellation top-down, release handles child-first, and await every selected branch despite individual failures. Durable child Sessions survive that process-local teardown.
168
168
 
169
169
  ```ts type-equiv
170
- /** Attribution for a model coordinator's follow-up to one of its children. */
171
- interface CoordinatorMessageSource {
172
- readonly kind: 'coordinator'
170
+ /** Durable attribution for one model-authored message between adjacent Agents. */
171
+ interface AgentMessageSource {
172
+ readonly kind: 'agent-message'
173
173
  /** A message another agent addressed to this one (`relay` context form). */
174
174
  readonly form: 'relay'
175
- /** Session id of the agent whose tool call produced the follow-up. */
175
+ /** Session id of the Agent whose tool call produced the message. */
176
176
  readonly senderSessionId: SessionId
177
177
  }
178
178
  ```
179
179
 
180
180
  ```ts type-equiv
181
- /** Options for following up with one continuable child. */
182
- interface SubagentFollowupOptions {
183
- /** Durable attribution retained on the delivered message; it grants no authority. */
184
- readonly source: MessageSource
181
+ /** Options for one model-authored message between adjacent Agents. */
182
+ interface SubagentSendMessageOptions {
185
183
  /** Caller cancellation, owning the operation only until inbox acceptance. */
186
184
  readonly signal: AbortSignal
187
185
  }
@@ -197,33 +195,13 @@ interface ContinuableStart {
197
195
  }
198
196
  ```
199
197
 
200
- An optional continuable-child setup contribution can install scope-local capabilities after base child composition and before Activation publication. The registry is ordered and transactional: a failed or revoked setup rolls back the unpublished Activation, child-scope disposal releases every installation, new registrations affect the next Activation, and registration removal revokes every resident installation immediately.
201
-
202
- `SubagentRuntime.reportFrom()` uses that extension point without adding a second queue or a result-bearing child wrapper. The exact live child Agent authorizes the call; callers cannot name a recipient. The manager derives the only recipient from the child's durable `parentSession`, requires that parent Agent to be live, frames the selected content as one `subagent-report` user message, and returns the message's stable `MessageId`. Quiet delivery uses `Agent.inject()` and does not wake the parent; next-step delivery uses `Agent.steer()`, waking an idle parent or joining a running parent's nearest step boundary. Neither mode concludes the child's turn, and no final answer reports implicitly.
203
-
204
- ```ts type-equiv
205
- /** Durable attribution for a continuable child's explicit parent report. */
206
- interface SubagentReportMessageSource {
207
- readonly kind: 'subagent-report'
208
- /** A message another agent addressed to this one (`relay` context form). */
209
- readonly form: 'relay'
210
- /** Session id of the reporting child. */
211
- readonly senderSessionId: SessionId
212
- }
213
- ```
214
-
215
- ```ts type-equiv
216
- /** Deployment scheduling policy for accepted child reports. */
217
- type SubagentReportDelivery = 'quiet' | 'next-step'
218
- ```
219
-
220
- Reporting is the child's own choice, so the manager keeps a separate account of its own: when a resident Activation settles, it delivers one notice to the child's durable direct parent describing how that epoch ended and carrying its final assistant content. That delivery is unconditional for every child whose id a caller received, happens before the ownership release that would let the parent be judged settled, and reaches a resident parent through the same waking-admission accounting as a report. A parent whose own lineage is already tearing down receives it without a wake, because waking a quiescent Agent starts a turn rather than queueing work. Its provenance is a distinct kind so a transcript never presents a runtime account as something the child wrote.
198
+ When a resident Activation settles, the manager delivers one notice to the child's durable direct parent describing how that epoch ended and carrying its final assistant content. That delivery is unconditional for every child whose id a caller received, happens before the ownership release that would let the parent be judged settled, and reaches a resident parent through the same waking-admission accounting as an Agent message. A parent whose own lineage is already tearing down receives it without a wake, because waking a quiescent Agent starts a turn rather than queueing work. Its provenance is a distinct kind so a transcript never presents a runtime account as something the child wrote.
221
199
 
222
200
  ```ts type-equiv
223
201
  /**
224
202
  * Durable attribution for the runtime's own account of a continuable child
225
203
  * settling. Deliberately a different kind from
226
- * {@link SubagentReportMessageSource}: a report is content the child chose,
204
+ * {@link AgentMessageSource}: an Agent message is content the sender chose,
227
205
  * while this message is the manager stating what became of the child, and a
228
206
  * transcript that merged them would credit the child with words it never wrote.
229
207
  */
@@ -238,17 +216,7 @@ interface SubagentSettledMessageSource {
238
216
  }
239
217
  ```
240
218
 
241
- ```ts type-equiv
242
- /** Options for one continuable child's report to its direct parent. */
243
- interface SubagentReportOptions {
244
- /** Already-resolved parent scheduling policy. */
245
- readonly delivery: SubagentReportDelivery
246
- /** Caller cancellation, owning authorization and admission until acceptance. */
247
- readonly signal: AbortSignal
248
- }
249
- ```
250
-
251
- The provider participates only in preparing the initial creation spec, where `spawn` and `fork` differ. Its returned spec carries only detached provider-specific creation inputs — today the optional parent-history seed — and no Agent, `AgentHandle`, prompt delivery, result, disposal, or resume operation. Cold resume does not dispatch through a provider at all: the manager folds the generic descriptor, calls `ctx.agents.resume()` through the same activation-owner scope, and submits the waiting turn.
219
+ The provider participates only in preparing the initial creation spec, where `spawn` and `fork` differ. Its returned spec carries only detached provider-specific creation inputs — the optional parent-history seed — and no Agent, `AgentHandle`, prompt delivery, result, disposal, or resume operation. Cold resume does not dispatch through a provider at all: the manager folds the generic descriptor, calls `ctx.agents.resume()` through the same activation-owner scope, and submits the waiting turn.
252
220
 
253
221
  ```ts type-equiv
254
222
  /**
@@ -290,11 +258,11 @@ interface ContinuableCreateSpec {
290
258
 
291
259
  The descriptor (`SubagentDescriptorData` in [descriptor.ts](../../packages/subagent/subagent/src/descriptor.ts)) is a mode-discriminated durable identity for every session-backed subagent. Both modes carry the provider name. A `one-shot` descriptor optionally carries a caller-owned display `label`; a `continuable` descriptor requires the delegation `description` as its durable creation label and additionally snapshots resolved child `agentOptions.provider`/`model`/`reasoningEffort` and optional `persona`/`toolFilter` for cold resume. It never snapshots the merge-extensible `AgentOptions` object, so an unrelated extension value cannot break continuation and a later composition input is a deliberate version change. It omits `subagentDepth` (cold resume trusts the persisted header's `delegationDepth` as the monotone floor) and `outputSchema` (one run or Activation's result contract, not durable identity).
292
260
 
293
- A local one-shot provider appends the descriptor inside the child's initial turn before its first request. The continuation manager appends the descriptor after any provider-supplied lineage and before the initial prompt is admitted; `header.seedLength` remains the fork-lineage boundary: resume-time descriptor authority reads the child's own suffix, while the list-serving identity projection folds `subagent/descriptor` last-wins so the child's own descriptor overrides a fork-seeded ancestor's. The event is log-only: no `surfaceOp`, never in model history, and retained across compaction by the append-only log. Malformed current-version descriptors are corrupt; unsupported versions cannot be classified by this runtime.
261
+ A local one-shot provider appends the descriptor inside the child's initial turn before its first request. The continuation manager appends the descriptor after any provider-supplied lineage and before the initial prompt is admitted; `Session.inheritedEventCount` remains the fork-lineage boundary: resume-time descriptor authority reads the child's own suffix, while the list-serving identity projection folds `subagent/descriptor` last-wins so the child's own descriptor overrides a fork-seeded ancestor's. A seeded cold list skips a cache hint until an authoritative observation supplies that exact cut. The event is log-only: no `surfaceOp`, never in model history, and retained across compaction by the append-only log. Malformed current-version descriptors are corrupt; unsupported versions cannot be classified by this runtime.
294
262
 
295
263
  ## Durable enumeration: `listChildren()`, `listDescendants()`, and their entries
296
264
 
297
- `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).
265
+ `SubagentRuntime.listChildren(parentSessionId)` enumerates the parent's direct session-backed subagents from the live-preferred merge of `ctx.sessions` and the session-query engine's `listSessions()` — 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 `query.observeSession()` cold observation 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).
298
266
 
299
267
  `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:
300
268
 
@@ -532,21 +500,20 @@ Named provider registry with one-shot runs, durable discovery, and continuable-c
532
500
  async startContinuable(spec: ContinuableStartSpec): Promise<ContinuableStart>
533
501
 
534
502
  /**
535
- * Deliver one later message to a continuable child as its next FIFO turn. A
536
- * resident child's Agent inbox accepts it directly (waking a `waiting`
537
- * Activation), while an absent one is cold-resumed from its persisted
538
- * Session. The Agent inbox is the only queue, so every accepted message has
539
- * one observable order.
540
- * @param parent - the exact live direct parent authorizing this delivery.
541
- * @param childId - durable child session id.
542
- * @param content - user-role content to deliver.
543
- * @param options - the message source fields and caller cancellation, which stops the
544
- * operation only before inbox acceptance.
503
+ * Steer one model-authored message to the sender's direct parent or direct
504
+ * continuable child. A running target admits it at the nearest step boundary;
505
+ * an idle target starts a turn, and an absent direct child cold-resumes from
506
+ * persistence. The service derives durable sender attribution from the exact
507
+ * live sender. Caller cancellation stops only pre-acceptance work.
508
+ * @param sender - exact live Agent authorizing and originating the message.
509
+ * @param targetId - durable direct-parent or direct-child session id.
510
+ * @param content - model-authored content to deliver.
511
+ * @param options - caller cancellation before inbox acceptance.
545
512
  * @returns the accepted message's inbox id.
546
- * @throws when continuation services are unavailable, parent authority is
547
- * rejected, or the message was not admitted.
513
+ * @throws when continuation services are unavailable, adjacency is rejected,
514
+ * or the message was not admitted.
548
515
  */
549
- async followup( parent: Agent, childId: SessionId, content: ContentBlock[], options: SubagentFollowupOptions, ): Promise<MessageId>
516
+ async sendMessage( sender: Agent, targetId: SessionId, content: ContentBlock[], options: SubagentSendMessageOptions, ): Promise<MessageId>
550
517
 
551
518
  /**
552
519
  * Interrupt one live continuable child's current turn under a human parent
@@ -565,29 +532,6 @@ async followup( parent: Agent, childId: SessionId, content: ContentBlock[], opti
565
532
  */
566
533
  interrupt(targetSessionId: SessionId, authority: SubagentInterruptAuthority): void
567
534
 
568
- /**
569
- * Deliver selected content from one live continuable child to its durable
570
- * direct parent. The child is the authority credential; callers cannot name a
571
- * recipient. Reporting does not conclude the child's turn or Activation.
572
- * @param child - exact live reporting child.
573
- * @param content - selected model-facing content.
574
- * @param options - parent scheduling and pre-acceptance cancellation.
575
- * @returns the stable identity of the parent-accepted message.
576
- * @throws when continuation services are unavailable, sender authorization
577
- * fails, or the direct parent is not live.
578
- */
579
- async reportFrom( child: Agent, content: ContentBlock[], options: SubagentReportOptions, ): Promise<MessageId>
580
-
581
- /**
582
- * Compose one deployment capability into every continuable child's
583
- * unpublished creation context on fresh creation and cold resume. Grants wait
584
- * for the next Activation; removing the contribution revokes every resident
585
- * installation immediately.
586
- * @param contribution - synchronous child-scope installer.
587
- * @returns the exact Cordis effect disposer.
588
- */
589
- registerContinuableSetup(contribution: ContinuableSetupContribution): () => void
590
-
591
535
  /**
592
536
  * Close continuable admission below exact live parent Agents, stop only their
593
537
  * visible descendant Activations synchronously, then await admitted scoped