@xneog/dsh-tool-subagent-control 0.1.0 → 0.1.2-rc.1

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.
package/README.i18n.yaml CHANGED
@@ -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 packages/subagent/tool-subagent-control/README.md
5
- README.md: 91f8d23ac092049e5315418070cdaae025054860
6
- README.zh.md: 66d4b5a446fc9e412c5fec7bccf27bdefd1f5f77
5
+ README.md: e7940477fe14965f922e53b59e063b375c21b621
6
+ README.zh.md: 77037e24f98c1a1e35e6e3921a0ef603e0cb3d25
package/README.md CHANGED
@@ -1,22 +1,114 @@
1
+ ---
2
+ description: "Global send_message, interrupt_agent, and list_agents tools for users and maintainers composing or debugging continuable-child control."
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @xneog/dsh-tool-subagent-control
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- The optional, globally named `send_message`, `interrupt_agent`, and `list_agents` tools are thin adapters over `ctx.subagents`. Provider-bound `@xneog/dsh-tool-subagent` instances register distinct delegation tools per transport; this separately loaded package registers shared control tools once, so multiple delegation tools never register duplicate global controls. The root plugin registers `send_message` and `interrupt_agent` and requires only `subagents`; the separately loadable `./list-agents` plugin registers `list_agents` and declares `subagents` plus `agents` as load-time dependencies. Its catalog reads additionally require the session store and projection registry at call time, but no query service. A deployment can keep the root tools while omitting the list tool. No tool's presence determines whether a delegation tool starts continuable work. These tools own only the parent-to-child direction; the independently installed [`@xneog/dsh-tool-subagent-report`](../tool-subagent-report/README.md) owns the child-to-parent direction.
10
+ ## Summary
11
+
12
+ `dsh-tool-subagent-control` adds the global control tools for continuable children: `send_message` steers between a direct parent and child, `interrupt_agent` stops a child's current turn while keeping its inbox and descendants intact, and `list_agents` (from the separately loadable `list-agents` plugin) lists continuable children by durable id and label. Parents and continuable children inherit the same `send_message` definition and ordering, so model communication adds no child-only tool schema. No tool's presence decides whether a delegation tool starts continuable work.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ Mount this package in any composition with continuable children the model should message, interrupt, or list. The root plugin needs only the subagent service; the list tool is a separate plugin a deployment can omit.
29
+
30
+ ### Minimal configuration
31
+
32
+ Load the subagent service, a backend, the delegation tool, and this package. Adding the separate list plugin exposes all three tools:
33
+
34
+ ```yaml
35
+ - name: '@xneog/dsh-subagent'
36
+ - name: '@xneog/dsh-subagent-spawn-in-process'
37
+ - name: '@xneog/dsh-tool-subagent'
38
+ config:
39
+ provider: spawn
40
+ backgroundMode: continuable
41
+ - name: '@xneog/dsh-tool-subagent-control'
42
+ - name: '@xneog/dsh-tool-subagent-control/list-agents'
43
+ ```
44
+
45
+ This package takes no configuration: the root plugin provides `send_message` and `interrupt_agent`, and the list plugin provides `list_agents`.
46
+
47
+ ### send_message
48
+
49
+ Sends a message to an Agent named by `agent_id`: any exact live Agent may target its direct continuable child, while a resident continuable child may also target its direct parent. A working target receives the message at its nearest step boundary through Steer; an idle target starts a turn, and a cold direct child resumes through the continuation lifecycle. The call returns only acceptance (the accepted message's stable `messageId`), never a reply. A failure — an unsupported target, unavailable parent, unknown child, descriptor-less child that cannot be resumed, or rejected admission — states the message was not delivered.
50
+
51
+ ### interrupt_agent
52
+
53
+ Stops only the target's current turn: queued messages stay parked until a later `send_message`, descendants keep running, and the child stays available for follow-ups. The call returns when the stop request is accepted, not when the target is quiet; interrupting an already-finished agent is an accepted no-op, and self, sibling, stale, and non-ancestor callers get errored results.
54
+
55
+ ### list_agents
56
+
57
+ Lists the continuable children below the calling agent: `children` (default) shows direct children, `descendants` walks the whole tree in stable pre-order, annotating each entry with its durable direct-parent session id and depth. Status comes from the live Agent registry — `running`, `idle`, or `ready`. One-shot children are intentionally absent because they cannot accept `send_message`, and unreadable candidates appear as diagnostics.
58
+
59
+ -----
60
+
61
+ <a id="understand-the-implementation"></a>
62
+ ## Understand the implementation
63
+
64
+ <details>
65
+ <summary>Implementation internals — click to expand</summary>
6
66
 
7
- The tool performs no lifecycle routing residency and cold resume belong to the subagent service. It passes `exec.agent` as the exact live parent that authorizes delivery and records every message source as `{ kind: 'coordinator', senderSessionId: parent.id }`, which the service retains but never treats as authority. Every message becomes the subagent's next FIFO turn through `Agent.followup()`: if the child is still working, the message waits until its current turn finishes, so it cannot redirect work already underway. The tool forwards its execution signal, which owns admission only until inbox acceptance; once the child accepts the message the accepted turn cannot be cancelled through this tool. This call returns no child reply — its transcript by that id is the source of what it did — and a child with `report` sends content on its own initiative as a separate parent message. A delivery failure becomes an errored tool result stating the message was not delivered.
67
+ This section explains what the tools delegate to the subagent service; the observable behavior is covered in [Use this package](#use-this-package).
8
68
 
9
- `interrupt_agent(agent_id)` passes `exec.agent` as the exact live ancestor authority for `ctx.subagents.interrupt()`: the target may be a direct child or a deeper descendant, and the service — never this tool — verifies the caller against the target Activation's recorded lineage. Only the target's current turn stops (`keepInbox`): queued messages stay parked until a later `send_message`, published descendants keep running, and the child stays available for follow-ups. The call returns as soon as the stop request is accepted, without waiting for target quiescence; an absent or already-settled target is an accepted no-op, while self, sibling, stale, and non-ancestor callers become errored results.
69
+ ### Design concept
10
70
 
11
- `list_agents` takes one optional `scope` argument, derives the root id from the calling agent, and projects the service catalog to continuable children without a cursor. The default `children` scope reads `ctx.subagents.listChildren()`; `descendants` reads `ctx.subagents.listDescendants()`, whose one-corpus walk crosses ordinary sessions and one-shot children and renders surviving rows in stable pre-order with `parent=<id> depth=<n>`. The `parent` annotation is the durable direct-parent session id and may name an ordinary session omitted from the output. For the calling agent, only depth-1 child entries are `send_message` candidates; deeper child entries are `interrupt_agent` candidates only. Status comes from the live Agent registry: `running` (active driver), `idle` (resident between turns, possibly waiting on agents it started), or `ready` (storage only and resumable rather than terminal). The service result also contains one-shot session-backed subagents for consumers such as a UI, but those entries are omitted from this model tool because they cannot accept `send_message`. Diagnostics remain visible, with positions in the descendants scope. Durable identity and mode come from each child's descriptor, while delivery-time authority and Activation ownership checks remain the service's.
71
+ Thin adapters over `ctx.subagents.sendMessage()`, `interrupt()`, and the list projections; the tools perform no lifecycle routing. Residency, cold resume, and authorization belong to the service, and the tools pass the exact live calling agent (`exec.agent`) as both sender and authority.
12
72
 
73
+ ### Delivery and signal ownership
74
+
75
+ The tool forwards its execution signal, which owns admission only until inbox acceptance. Once the target accepts a message, it cannot be cancelled through this tool. Every message is framed as `Agent <sender-id> sent a message:` and recorded with `{ kind: 'agent-message', form: 'relay', senderSessionId: sender.id }`; the service derives that attribution and never treats it as authority.
76
+
77
+ ### Listing projection
78
+
79
+ `list_agents` derives the root id from the calling agent, reads the service catalog without a cursor, refines each candidate's status through the live Agent registry, and omits one-shot children because they cannot accept `send_message`. Diagnostics keep their positions in the descendants scope and never expose descriptor contents.
80
+
81
+ ### Source map
82
+
83
+ | File | Role |
84
+ |---|---|
85
+ | [`src/index.ts`](src/index.ts) | `send_message` and `interrupt_agent` registration |
86
+ | [`src/list-agents.ts`](src/list-agents.ts) | `list_agents` registration: scopes, status refinement, projection |
87
+ | — | No runtime invariant companion is published; this model-facing adapter has no independent lifecycle stream; delivery and activation relations are owned by the subagent service it calls. |
88
+
89
+ </details>
90
+
91
+ -----
92
+
93
+ <a id="further-exploration"></a>
94
+ ## Further Exploration
95
+
96
+ Read these pages when the package-level contract is not enough; they move from the tool schemas to the continuation service behind them.
97
+
98
+ - [Subagent subsystem](../../../docs/subsystems/subagent.md) — continuable children, activations, inbox, interrupt, and follow-up authority.
99
+ - [dsh-tool-subagent](../tool-subagent/README.md) — the delegation tool that starts continuable children.
100
+ - [Generated tool catalog](../../../docs/tool-catalog.md#xneogdsh-tool-subagent-control) — the three tool schemas.
101
+
102
+ -----
103
+
104
+ <a id="model-experience"></a>
13
105
  ## Model Experience
14
106
 
15
107
  ### Tool schema
16
108
 
17
109
  #### What the model sees
18
110
 
19
- The generated [schemas](../../../docs/tool-catalog.md#deepseek-aidsh-tool-subagent-control): `send_message` takes `subagent_id` and `message`, describing that the message becomes the subagent's next turn, that this call returns no answer from the subagent, and that a failure means the message was not delivered; `interrupt_agent` takes `agent_id`, describing that only the current turn stops, queued messages park, descendants keep running, and acceptance precedes the actual stop; `list_agents` takes the optional `scope` enum.
111
+ The generated [schemas](../../../docs/tool-catalog.md#xneogdsh-tool-subagent-control): `send_message` takes `agent_id` and `message`; `interrupt_agent` takes `agent_id`; `list_agents` takes the optional `scope` enum.
20
112
 
21
113
  #### Token effect
22
114
 
@@ -44,11 +136,11 @@ Append-only; each result follows the reusable request prefix.
44
136
 
45
137
  #### What the model sees
46
138
 
47
- `message queued as the next turn for subagent <subagent_id>` on acceptance; the canonical output carries the accepted `messageId`. A failure — an unauthorized or unknown child, a descriptor-less child that cannot be resumed, or admission rejected — is an errored result whose message states the message was not delivered.
139
+ `message delivered to agent <agent_id>` on acceptance; the canonical output carries the accepted `messageId`. A failure — a non-adjacent target, unavailable parent, unknown child, descriptor-less child that cannot be resumed, or admission rejected — is an errored result whose message states the message was not delivered.
48
140
 
49
141
  #### Token effect
50
142
 
51
- One short acknowledgement per call; the child's response never returns through this call. A separately granted `report` may append selected content to parent history.
143
+ One short acknowledgement per call; the target's response never returns through this call. A child uses the same tool with its initial task's parent id to append selected content to parent history.
52
144
 
53
145
  #### KV Cache effect
54
146
 
@@ -58,7 +150,7 @@ Append-only; newly visible content follows the reusable request prefix and does
58
150
 
59
151
  #### What the model sees
60
152
 
61
- One line per continuable child in stable catalog order: `<id> [<status>] — <label>` (`running` = active driver, `idle` = resident between turns, `ready` = storage only; resumable rather than terminal, not a result waiting to be collected — a direct child in that state can be resumed by `send_message`), plus `<id> [diagnostic: <reason>]` for a candidate that could not be read (`corrupt`, `unsupported`, or `unavailable`). The `descendants` scope inserts ` parent=<id> depth=<n>` before the label dash on every line, in pre-order. One-shot children are intentionally absent; `(no subagents)` means no continuable child or diagnostic survived the projection. Diagnostics never expose descriptor contents.
153
+ One line per continuable child in stable catalog order: `<id> [<status>] — <label>` (`running` = active driver, `idle` = resident between turns, `ready` = storage only, resumable rather than terminal), plus `<id> [diagnostic: <reason>]` for a candidate that could not be read. The `descendants` scope inserts ` parent=<id> depth=<n>` before the label dash on every line, in pre-order. One-shot children are intentionally absent; `(no subagents)` means no continuable child or diagnostic survived the projection.
62
154
 
63
155
  #### Token effect
64
156
 
@@ -70,7 +162,22 @@ Append-only; each result follows the reusable request prefix.
70
162
 
71
163
  ## Known Limitations and Deferred Work
72
164
 
73
- - **A queued message has no independent result** — acceptance returns only its inbox `messageId`; the child's work lands in the durable child Session and is never collected through this tool. A child granted `report` may send selected content back separately, but that message is not this call's result.
74
- - **No steering of the current turn** — every message opens a later FIFO turn, so a message sent while the child is working runs only after its current turn finishes and cannot redirect it.
165
+ <a id="known-limitations-and-deferred-work"></a>
166
+
167
+
168
+ These limits define what the control tools cannot observe or steer; they are current package constraints.
169
+
170
+ - **A delivered message has no independent result** — acceptance returns only its inbox `messageId`; later target work lands in that target's durable Session and is never collected through this tool. A reply is another explicitly addressed `send_message`, not this call's result.
171
+ - **Only supported adjacent Agents can communicate** — every sender may target a direct continuable child, only a sender with a resident continuable Activation may target its direct parent, and that parent must remain live; siblings and deeper descendants are not message targets, and only direct-child delivery supports cold activation.
75
172
  - **Listing is a snapshot, not a delivery promise** — it may race publication, disposal, or a later message, and another process may activate a child this process reports as `ready`; cross-process accuracy requires a shared lease. `interrupt_agent` performs the authoritative live-lineage check itself, so discovery staleness cannot grant authority.
76
173
  - **No pagination or deletion** — the complete stably ordered set is returned, and persisted children remain listed for as long as their sessions remain in persistence; a service-level bound or delete operation is a later product decision.
174
+
175
+ <a id="dev-note"></a>
176
+ ### Dev Note
177
+
178
+ <details>
179
+ <summary>Working context for maintainers — click to expand</summary>
180
+
181
+ None.
182
+
183
+ </details>
package/README.zh.md CHANGED
@@ -1,22 +1,114 @@
1
+ ---
2
+ description: "全局 send_message、interrupt_agent 与 list_agents 工具,供用户与维护者组合或排查可继续子级的控制。"
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @xneog/dsh-tool-subagent-control
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- 可选的全局具名 `send_message`、`interrupt_agent` 与 `list_agents` 工具是 `ctx.subagents` 之上的轻量适配器。绑定提供方的 `@xneog/dsh-tool-subagent` 实例会为每种传输注册不同的委派工具;这个单独加载的包只注册一次共享控制工具,因此多个委派工具绝不会重复注册全局控制工具。根插件注册 `send_message` 与 `interrupt_agent`,且只要求 `subagents`;可单独加载的 `./list-agents` 插件注册 `list_agents`,并将 `subagents` 与 `agents` 声明为加载时依赖。其目录读取在调用时还要求会话存储与投影注册表,但不要求任何查询服务。部署可保留根插件工具并省略列表工具。是否加载这些工具不会决定委派工具是否启动可继续工作。这些工具只负责父到子的方向;单独安装的 [`@xneog/dsh-tool-subagent-report`](../tool-subagent-report/README.md) 负责子到父的方向。
10
+ ## 概述
11
+
12
+ `dsh-tool-subagent-control` 为可继续子级添加全局控制工具:`send_message` 在直接 parent 与 child 之间 steer,`interrupt_agent` 停止 child 当前轮次但保留其 inbox 与后代,`list_agents`(来自可单独加载的 `list-agents` 插件)按持久化 id 与标签列出可继续 child。parent 与可继续 child 继承相同的 `send_message` 定义和顺序,因此模型通信不会增加 child 专属工具 schema。是否加载这些工具不会决定委派工具是否启动可继续工作。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 在模型需要对可继续子级发消息、中断或列出的任何组合中挂载本包。根插件只需要 subagent 服务;列表工具是独立插件,部署方可以省略。
29
+
30
+ ### 最小配置
31
+
32
+ 先加载 subagent 服务、一个后端、委派工具与本包。加上独立的列表插件即可公开全部三个工具:
33
+
34
+ ```yaml
35
+ - name: '@xneog/dsh-subagent'
36
+ - name: '@xneog/dsh-subagent-spawn-in-process'
37
+ - name: '@xneog/dsh-tool-subagent'
38
+ config:
39
+ provider: spawn
40
+ backgroundMode: continuable
41
+ - name: '@xneog/dsh-tool-subagent-control'
42
+ - name: '@xneog/dsh-tool-subagent-control/list-agents'
43
+ ```
44
+
45
+ 本包不接收任何配置:根插件提供 `send_message` 与 `interrupt_agent`,列表插件提供 `list_agents`。
46
+
47
+ ### send_message
48
+
49
+ 向 `agent_id` 指定的 Agent 发送消息:任何确切在线 Agent 都可以指定自己的直接可继续 child,而驻留的可继续 child 还可以指定自己的直接 parent。正在工作的目标通过 Steer 在最近的 step 边界接收消息;空闲目标会启动一个轮次,冷状态的直接 child 会通过继续执行生命周期恢复。调用只返回接受结果(被接受消息的稳定 `messageId`),绝不返回回复。失败——不受支持的目标、不可用的 parent、未知 child、缺少描述符而无法恢复的 child,或准入被拒——会明确说明消息未送达。
50
+
51
+ ### interrupt_agent
52
+
53
+ 只停止目标当前轮次:已排队消息保持暂停直到之后的 `send_message`,后代继续运行,子级仍可接受后续消息。调用在停止请求被接受后立即返回,不等待目标完全停稳;中断已结束的 agent 是被接受的 no-op,而 self、sibling、陈旧与非 ancestor 调用方会收到出错结果。
54
+
55
+ ### list_agents
56
+
57
+ 列出调用 agent 下方的可继续子级:`children`(默认)只显示直接子级,`descendants` 按稳定 pre-order 遍历整棵树,并为每个条目标注其持久化直接父级会话 id 与深度。状态来自在线 Agent 注册表——`running`、`idle` 或 `ready`。一次性子级因无法接受 `send_message` 而被有意排除,无法读取的候选项以 diagnostic 呈现。
58
+
59
+ -----
60
+
61
+ <a id="understand-the-implementation"></a>
62
+ ## 理解实现
63
+
64
+ <details>
65
+ <summary>实现细节——点击展开</summary>
6
66
 
7
- 本工具不执行生命周期路由:驻留与冷恢复归 subagent 服务所有。它将 `exec.agent` 作为授权投递的确切在线父级传入,并把每条消息的来源记录为 `{ kind: 'coordinator', senderSessionId: parent.id }`;服务会保留该来源,但绝不将其视为权限。每条消息都会通过 `Agent.followup()` 成为 subagent 的下一个 FIFO 轮次:如果子 agent(智能体)仍在工作,该消息会等待其当前轮次结束,因此无法重定向已经在进行的工作。本工具会转发其执行信号,该信号只在 inbox 接受之前掌管准入;一旦子 agent 接受消息,已接受的轮次便无法再通过本工具取消。本次调用不会返回子 agent 的回复;通过该 id 查看其 transcript(文本记录),才是了解它完成了哪些工作的真源。拥有 `report` 的子 agent 会自行把内容作为一条单独的父级消息发回。投递失败会变为出错的工具结果,并明确说明消息未送达。
67
+ 本节解释工具把什么委托给 subagent 服务;可观察行为已在[使用本包](#use-this-package)中说明。
8
68
 
9
- `interrupt_agent(agent_id)` 将 `exec.agent` 作为 `ctx.subagents.interrupt()` 的确切在线 ancestor 授权传入:目标可以是直接 child 或更深的后代,由服务——而不是本工具——依据目标 Activation 记录的 lineage 校验调用方。只有目标的当前轮次会停止(`keepInbox`):已排队的消息保持暂停直到之后的 `send_message`,已发布的后代继续运行,child 也仍可接受后续消息。调用在停止请求被接受后立即返回,不等待目标完全停稳;目标不存在或已结算是被接受的 no-op,而 self、sibling、陈旧与非 ancestor 调用方会成为出错结果。
69
+ ### 设计理念
10
70
 
11
- `list_agents` 接受一个可选的 `scope` 参数,会从调用它的 agent 推导根 id,并且不使用 cursor,将服务目录投影为可继续 child。默认的 `children` scope 读取 `ctx.subagents.listChildren()`;`descendants` 读取 `ctx.subagents.listDescendants()`,其单份语料的遍历会穿过普通会话与一次性 child,并按稳定 pre-order 以 `parent=<id> depth=<n>` 渲染保留下来的条目。`parent` 注释是持久化直接 parent 会话 id,可能指向输出中省略的普通会话。对于调用本工具的 agent,只有 depth-1 child 条目可作为 `send_message` 候选;更深的 child 条目只能作为 `interrupt_agent` 候选。状态来自在线 Agent 注册表:`running`(driver 活跃)、`idle`(驻留但处于轮次之间,可能在等待它启动的 agent)或 `ready`(仅存于存储,表示可恢复而非终态)。服务结果还包含由会话支撑的一次性 subagent,以供 UI 等消费方使用;但这些条目无法接受 `send_message`,因此会从这个模型工具中排除。diagnostic 仍然可见,并在 descendants scope 中带有位置。持久化身份和模式来自每个子 agent 的描述符,消息送达时的鉴权和 Activation 所有权检查仍归服务负责。
71
+ `ctx.subagents.sendMessage()`、`interrupt()` 与列表投影之上的轻量适配器;工具不执行任何生命周期路由。驻留、冷恢复与授权归服务所有,工具把确切在线的调用 Agent(`exec.agent`)同时作为 sender 与权限凭据传入。
12
72
 
73
+ ### 投递与信号所有权
74
+
75
+ 工具转发其执行信号,该信号只在 inbox 接受之前掌管准入。目标一旦接受消息,该消息便无法再通过本工具取消。每条消息都以 `Agent <sender-id> sent a message:` 作为前缀,并记录 `{ kind: 'agent-message', form: 'relay', senderSessionId: sender.id }`;该来源信息由服务推导,且绝不被视为权限。
76
+
77
+ ### 列表投影
78
+
79
+ `list_agents` 从调用 agent 推导根 id,不使用 cursor 读取服务目录,通过在线 Agent 注册表细化每个候选的状态,并省略无法接受 `send_message` 的一次性子级。diagnostic 在 descendants scope 中保留其位置,且绝不暴露描述符内容。
80
+
81
+ ### 源码地图
82
+
83
+ | 文件 | 职责 |
84
+ |---|---|
85
+ | [`src/index.ts`](src/index.ts) | `send_message` 与 `interrupt_agent` 注册 |
86
+ | [`src/list-agents.ts`](src/list-agents.ts) | `list_agents` 注册:作用域、状态细化、投影 |
87
+ | — | 不发布运行时不变式伴生入口;这个模型侧 adapter 没有独立 lifecycle stream;delivery 与 activation 关系由 subagent service 负责。 |
88
+
89
+ </details>
90
+
91
+ -----
92
+
93
+ <a id="further-exploration"></a>
94
+ ## 进一步探索
95
+
96
+ 当包级约定不够用时阅读以下页面;它们从工具 schema 进入其背后的继续执行服务。
97
+
98
+ - [Subagent 子系统](../../../docs/subsystems/subagent.zh.md)——可继续子级、Activation、inbox、中断与后续消息权限。
99
+ - [dsh-tool-subagent](../tool-subagent/README.zh.md)——启动可继续子级的委派工具。
100
+ - [生成工具目录](../../../docs/tool-catalog.zh.md#xneogdsh-tool-subagent-control)——三个工具的 schema。
101
+
102
+ -----
103
+
104
+ <a id="model-experience"></a>
13
105
  ## 模型体验
14
106
 
15
107
  ### 工具 schema
16
108
 
17
- #### 模型看到的内容
109
+ #### 模型看到什么
18
110
 
19
- 已生成的 [schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-subagent-control):`send_message` 包含 `subagent_id` `message`,说明消息会成为 subagent 的下一个轮次、本次调用不会返回 subagent 的回答,以及失败即表示消息未送达;`interrupt_agent` 包含 `agent_id`,说明只有当前轮次会停止、已排队消息保持暂停、后代继续运行,以及接受先于实际停止;`list_agents` 包含可选的 `scope` 枚举。
111
+ 已生成的 [schema](../../../docs/tool-catalog.zh.md#xneogdsh-tool-subagent-control):`send_message` 接受 `agent_id` `message`;`interrupt_agent` 接受 `agent_id`;`list_agents` 接受可选的 `scope` 枚举。
20
112
 
21
113
  #### Token 影响
22
114
 
@@ -28,13 +120,13 @@
28
120
 
29
121
  ### 中断结果
30
122
 
31
- #### 模型看到的内容
123
+ #### 模型看到什么
32
124
 
33
125
  接受时返回 `interrupt requested for agent <agent_id>`。未授权的调用方——self、sibling、陈旧或非 ancestor——会成为指明拒绝原因的出错结果;目标不存在或已结算仍渲染接受行。
34
126
 
35
127
  #### Token 影响
36
128
 
37
- 每次调用产生一条简短确认消息;被中断轮次的中止只在 child 自己的 transcript 中可见。
129
+ 每次调用产生一条简短确认消息;被中断轮次的中止只在子级自己的 transcript 中可见。
38
130
 
39
131
  #### KV Cache 影响
40
132
 
@@ -42,13 +134,13 @@
42
134
 
43
135
  ### 投递结果
44
136
 
45
- #### 模型看到的内容
137
+ #### 模型看到什么
46
138
 
47
- 接受时返回 `message queued as the next turn for subagent <subagent_id>`;规范输出携带被接受的 `messageId`。失败,包括未授权或未知的子 agent、缺少描述符而无法恢复的子 agent,或准入被拒绝,都会成为出错的结果,其消息说明该消息未送达。
139
+ 接受时返回 `message delivered to agent <agent_id>`;规范输出携带被接受的 `messageId`。失败——非相邻目标、不可用的 parent、未知 child、缺少描述符而无法恢复的 child,或准入被拒——会成为出错的结果,其消息说明该消息未送达。
48
140
 
49
141
  #### Token 影响
50
142
 
51
- 每次调用产生一条简短确认消息;子 agent 的响应绝不会通过本次调用返回。单独授予的 `report` 可以把选定内容追加到父级历史中。
143
+ 每次调用产生一条简短确认消息;目标的响应绝不会通过本次调用返回。child 使用同一个工具和初始任务中的 parent id,把选定内容追加到 parent 历史中。
52
144
 
53
145
  #### KV Cache 影响
54
146
 
@@ -56,21 +148,36 @@
56
148
 
57
149
  ### 列表结果
58
150
 
59
- #### 模型看到的内容
151
+ #### 模型看到什么
60
152
 
61
- 按稳定目录顺序,每个可继续 child 占一行:渲染为 `<id> [<status>] — <label>`(`running` 表示 driver 活跃,`idle` 表示驻留但处于轮次之间,`ready` 表示仅存于存储;可恢复而非终态,也不表示有结果等待收集——处于该状态的直接 child 可通过 `send_message` 恢复),另为无法读取的候选项渲染 `<id> [diagnostic: <reason>]`(`corrupt`、`unsupported` 或 `unavailable`)。`descendants` scope 会在每行 label 破折号之前插入 ` parent=<id> depth=<n>`,按 pre-order 排列。一次性 child 会被有意排除;`(no subagents)` 表示投影后没有留下可继续 child 或 diagnostic。诊断信息绝不会暴露描述符内容。
153
+ 按稳定目录顺序,每个可继续子级占一行:`<id> [<status>] — <label>`(`running` 表示 driver 活跃,`idle` 表示驻留但处于轮次之间,`ready` 表示仅存于存储,可恢复而非终态),另为无法读取的候选项渲染 `<id> [diagnostic: <reason>]`。`descendants` scope 会在每行 label 破折号之前按 pre-order 插入 ` parent=<id> depth=<n>`。一次性子级会被有意排除;`(no subagents)` 表示投影后没有留下可继续子级或 diagnostic
62
154
 
63
155
  #### Token 影响
64
156
 
65
- 随所列可继续 child 数量线性增长——`descendants` scope 下为整棵树;没有 cursor 或上限,因此长期存活且有许多持久化 child 的 parent 每次调用都会承担完整列表成本。
157
+ 随所列可继续子级数量线性增长——`descendants` scope 下为整棵树;没有 cursor 或上限,因此长期存活且有许多持久化子级的父级每次调用都会承担完整列表成本。
66
158
 
67
159
  #### KV Cache 影响
68
160
 
69
161
  仅追加;每个结果都位于可复用请求前缀之后。
70
162
 
71
- ## 已知限制与暂缓事项
163
+ ## 已知限制与延期工作
164
+
165
+ <a id="known-limitations-and-deferred-work"></a>
166
+
167
+
168
+ 这些限制说明控制工具无法观察或引导什么;它们是当前包约束。
169
+
170
+ - **已投递消息没有独立结果**——接受时只返回其 inbox `messageId`;目标后续工作会落入该目标的持久化会话,绝不会通过本工具收集。回复是另一条显式指定地址的 `send_message`,而非本次调用的结果。
171
+ - **只有受支持的相邻 Agent 可以通信**——每个 sender 都可以指定直接可继续 child,只有具备驻留可继续 Activation 的 sender 可以指定自己的直接 parent,且该 parent 必须仍在线;sibling 与更深的后代不能作为消息目标,只有直接 child 投递支持冷激活。
172
+ - **列表是快照,而非投递承诺**——它可能与发布、dispose(资源释放)或后续消息发生竞态,另一个进程也可能激活当前进程报告为 `ready` 的子级;跨进程准确性需要共享租约。`interrupt_agent` 自己执行权威的在线 lineage 检查,因此过期的发现结果不会授予权限。
173
+ - **没有分页或删除**——系统返回完整且稳定排序的集合;只要子级会话仍在持久化存储中,它就会继续出现在列表中,服务级上限或删除操作留待后续产品决策。
174
+
175
+ <a id="dev-note"></a>
176
+ ### 开发备注
177
+
178
+ <details>
179
+ <summary>维护者的工作上下文——点击展开</summary>
180
+
181
+ 无。
72
182
 
73
- - **已排队的消息没有独立结果**:接受时只返回其 inbox `messageId`;subagent 的工作会落入持久化子 agent 会话,绝不会通过本工具收集。获得 `report` 的子 agent 可以单独发回选定内容,但该消息不是本次调用的结果。
74
- - **不对当前轮次进行 steering(中途引导)**:每条消息都会开启后续 FIFO 轮次,因此在子 agent 工作时发送的消息只会在其当前轮次结束后运行,无法将其重定向。
75
- - **列表是快照,而非投递承诺**:它可能与发布、dispose(资源释放)或后续消息发生竞态,另一个进程也可能激活当前进程报告为 `ready` 的 child;跨进程准确性需要共享租约。`interrupt_agent` 自己执行权威的在线 lineage 检查,因此过期的发现结果不会授予权限。
76
- - **没有分页或删除**:系统返回完整且稳定排序的集合;只要 child 会话仍在持久化存储中,它就会继续出现在列表中,服务级上限或删除操作留待后续产品决策。
183
+ </details>
package/lib/index.js CHANGED
@@ -1,9 +1,10 @@
1
+ import { brandString } from "@xneog/dsh-brand";
1
2
  import { defineTool } from "@xneog/dsh-tools";
2
- import { SessionId } from "@xneog/dsh-session";
3
+ import { markAdjacentAgentSendMessageTool } from "@xneog/dsh-subagent/internal";
3
4
  //#region lib/types/index.js
4
5
  /**
5
6
  * The globally named `send_message` and `interrupt_agent` tools: thin
6
- * model-facing adapters over `ctx.subagents.followup()` and
7
+ * model-facing adapters over `ctx.subagents.sendMessage()` and
7
8
  * `ctx.subagents.interrupt()`. They perform no lifecycle routing of their own —
8
9
  * residency, cold resume, and interrupt authorization belong to the subagent
9
10
  * service — and they live apart from the provider-bound
@@ -18,19 +19,19 @@ const inject = ["tools", "subagents"];
18
19
  * @param ctx - context carrying the tool registry and subagent service.
19
20
  */
20
21
  function apply(ctx) {
21
- ctx.tools.register(defineTool({
22
+ ctx.tools.register(markAdjacentAgentSendMessageTool(defineTool({
22
23
  name: "send_message",
23
- description: "Send a message to a background subagent by its subagent id, continuing the same conversation. It becomes the subagent's next turn: if it is still working, the message waits until its current turn finishes, so it cannot redirect work already underway. This call returns no answer from the subagent — only confirmation that the message was delivered — so use it to give it more work. A failure means the message was NOT delivered.",
24
+ description: "Send a message to a direct continuable child by its agent id. If you are a resident continuable child, you may also target your direct parent. If the target is still working, the message steers its nearest step; if it is idle, the message starts a turn. This call returns no answer from the agent — only confirmation that the message was delivered. A failure means the message was NOT delivered.",
24
25
  parameters: {
25
- subagent_id: {
26
+ agent_id: {
26
27
  type: "string",
27
28
  required: true,
28
- description: "The subagent id returned when the background subagent was started."
29
+ description: "The agent id of your direct continuable child, or your direct parent when you are a resident continuable child."
29
30
  },
30
31
  message: {
31
32
  type: "string",
32
33
  required: true,
33
- description: "The message to deliver to the subagent."
34
+ description: "The message to deliver to the agent."
34
35
  }
35
36
  },
36
37
  output: {
@@ -44,26 +45,19 @@ function apply(ctx) {
44
45
  },
45
46
  render: (args, _value) => [{
46
47
  type: "text",
47
- text: `message queued as the next turn for subagent ${args.subagent_id}`
48
+ text: `message delivered to agent ${args.agent_id}`
48
49
  }]
49
50
  },
50
51
  async execute(args, exec) {
51
- const parent = exec.agent;
52
- if (!parent) throw new Error("send_message requires a calling agent (exec.agent was undefined)");
52
+ const sender = exec.agent;
53
+ if (!sender) throw new Error("send_message requires a calling agent (exec.agent was undefined)");
53
54
  const message = [{
54
55
  type: "text",
55
56
  text: args.message
56
57
  }];
57
- return { messageId: await ctx.subagents.followup(parent, SessionId(args.subagent_id), message, {
58
- source: {
59
- kind: "coordinator",
60
- form: "relay",
61
- senderSessionId: parent.id
62
- },
63
- signal: exec.signal
64
- }) };
58
+ return { messageId: await ctx.subagents.sendMessage(sender, brandString(args.agent_id), message, { signal: exec.signal }) };
65
59
  }
66
- }));
60
+ })));
67
61
  ctx.tools.register(defineTool({
68
62
  name: "interrupt_agent",
69
63
  description: "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
@@ -89,7 +83,7 @@ function apply(ctx) {
89
83
  execute(args, exec) {
90
84
  const caller = exec.agent;
91
85
  if (!caller) throw new Error("interrupt_agent requires a calling agent (exec.agent was undefined)");
92
- ctx.subagents.interrupt(SessionId(args.agent_id), {
86
+ ctx.subagents.interrupt(brandString(args.agent_id), {
93
87
  kind: "ancestor",
94
88
  agent: caller
95
89
  });
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The globally named `send_message` and `interrupt_agent` tools: thin
3
- * model-facing adapters over `ctx.subagents.followup()` and
3
+ * model-facing adapters over `ctx.subagents.sendMessage()` and
4
4
  * `ctx.subagents.interrupt()`. They perform no lifecycle routing of their own —
5
5
  * residency, cold resume, and interrupt authorization belong to the subagent
6
6
  * service — and they live apart from the provider-bound
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The globally named `send_message` and `interrupt_agent` tools: thin
3
- * model-facing adapters over `ctx.subagents.followup()` and
3
+ * model-facing adapters over `ctx.subagents.sendMessage()` and
4
4
  * `ctx.subagents.interrupt()`. They perform no lifecycle routing of their own —
5
5
  * residency, cold resume, and interrupt authorization belong to the subagent
6
6
  * service — and they live apart from the provider-bound
@@ -8,8 +8,9 @@
8
8
  * one control API.
9
9
  * @module @xneog/dsh-tool-subagent-control
10
10
  */
11
+ import { brandString } from '@xneog/dsh-brand';
11
12
  import { defineTool } from '@xneog/dsh-tools';
12
- import { SessionId } from '@xneog/dsh-session';
13
+ import { markAdjacentAgentSendMessageTool } from '@xneog/dsh-subagent/internal';
13
14
  export const name = 'tool-subagent-control';
14
15
  export const inject = ['tools', 'subagents'];
15
16
  /**
@@ -17,23 +18,22 @@ export const inject = ['tools', 'subagents'];
17
18
  * @param ctx - context carrying the tool registry and subagent service.
18
19
  */
19
20
  export function apply(ctx) {
20
- ctx.tools.register(defineTool({
21
+ ctx.tools.register(markAdjacentAgentSendMessageTool(defineTool({
21
22
  name: 'send_message',
22
- description: 'Send a message to a background subagent by its subagent id, continuing the same conversation. It '
23
- + 'becomes the subagent\'s next turn: if it is still working, the message waits until its current turn '
24
- + 'finishes, so it cannot redirect work already underway. This call returns no answer from the '
25
- + 'subagent — only confirmation that the message was delivered so use it to give it more work. A '
26
- + 'failure means the message was NOT delivered.',
23
+ description: 'Send a message to a direct continuable child by its agent id. If you are a resident continuable child, '
24
+ + 'you may also target your direct parent. If the target is still working, the message steers its nearest step; '
25
+ + 'if it is idle, the message starts a turn. This call returns no answer from the agent — only confirmation '
26
+ + 'that the message was delivered. A failure means the message was NOT delivered.',
27
27
  parameters: {
28
- subagent_id: {
28
+ agent_id: {
29
29
  type: 'string',
30
30
  required: true,
31
- description: 'The subagent id returned when the background subagent was started.',
31
+ description: 'The agent id of your direct continuable child, or your direct parent when you are a resident continuable child.',
32
32
  },
33
33
  message: {
34
34
  type: 'string',
35
35
  required: true,
36
- description: 'The message to deliver to the subagent.',
36
+ description: 'The message to deliver to the agent.',
37
37
  },
38
38
  },
39
39
  output: {
@@ -46,23 +46,19 @@ export function apply(ctx) {
46
46
  },
47
47
  render: (args, _value) => [{
48
48
  type: 'text',
49
- text: `message queued as the next turn for subagent ${args.subagent_id}`,
49
+ text: `message delivered to agent ${args.agent_id}`,
50
50
  }],
51
51
  },
52
52
  async execute(args, exec) {
53
- const parent = exec.agent;
54
- if (!parent) {
55
- // Parent authority requires an exact live calling agent.
53
+ const sender = exec.agent;
54
+ if (!sender) {
56
55
  throw new Error('send_message requires a calling agent (exec.agent was undefined)');
57
56
  }
58
57
  const message = [{ type: 'text', text: args.message }];
59
- const messageId = await ctx.subagents.followup(parent, SessionId(args.subagent_id), message, {
60
- source: { kind: 'coordinator', form: 'relay', senderSessionId: parent.id },
61
- signal: exec.signal,
62
- });
58
+ const messageId = await ctx.subagents.sendMessage(sender, brandString(args.agent_id), message, { signal: exec.signal });
63
59
  return { messageId };
64
60
  },
65
- }));
61
+ })));
66
62
  ctx.tools.register(defineTool({
67
63
  name: 'interrupt_agent',
68
64
  description: 'Request cancellation of a background agent\'s current turn by its agent id. The target may be your '
@@ -99,7 +95,7 @@ export function apply(ctx) {
99
95
  }
100
96
  // The service authorizes the exact live caller against the target's
101
97
  // recorded lineage; the tool adds no authority of its own.
102
- ctx.subagents.interrupt(SessionId(args.agent_id), { kind: 'ancestor', agent: caller });
98
+ ctx.subagents.interrupt(brandString(args.agent_id), { kind: 'ancestor', agent: caller });
103
99
  return Promise.resolve({ accepted: true });
104
100
  },
105
101
  }));
@@ -7,7 +7,7 @@
7
7
  * @module @xneog/dsh-tool-subagent-control/list-agents
8
8
  */
9
9
  import { defineTool } from '@xneog/dsh-tools';
10
- import { assertNever } from '@xneog/dsh-llm';
10
+ import { assertNever } from '@xneog/dsh-util-values';
11
11
  export const name = 'tool-subagent-list-agents';
12
12
  export const inject = ['tools', 'subagents', 'agents'];
13
13
  /** Resolve the optional model request into an internal required-scope spec. */
@@ -56,8 +56,9 @@ export function apply(ctx) {
56
56
  + 'you started, not to poll for completion — you are told when one finishes. Status comes from the live '
57
57
  + 'registry: running means the agent is working right now, idle means it is loaded but between turns '
58
58
  + '(it may be waiting on agents it started), and ready means it exists only in storage — resumable, not '
59
- + 'terminal, and not a result waiting to be collected; a `send_message` starts a new turn on the same '
60
- + 'conversation, and a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery '
59
+ + 'terminal, and not a result waiting to be collected; a `send_message` steers a running child at its nearest '
60
+ + 'step boundary or starts a turn for an idle or ready child, and a direct child remains a `send_message` '
61
+ + 'candidate in every status. The snapshot is not a delivery '
61
62
  + 'promise — `send_message` performs the authoritative check and may still fail. Children that could '
62
63
  + 'not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` '
63
64
  + 'walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent '
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xneog/dsh-tool-subagent-control",
3
3
  "description": "Globally named send_message, interrupt_agent, and list_agents tools over ctx.subagents continuations",
4
- "version": "0.1.0",
4
+ "version": "0.1.2-rc.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -18,10 +18,6 @@
18
18
  "types": "./lib/types/index.d.ts",
19
19
  "default": "./lib/index.js"
20
20
  },
21
- "./invariant": {
22
- "types": "./lib/types/invariant.d.ts",
23
- "default": "./lib/invariant.js"
24
- },
25
21
  "./list-agents": {
26
22
  "types": "./lib/types/list-agents.d.ts",
27
23
  "default": "./lib/types/list-agents.js"
@@ -31,32 +27,35 @@
31
27
  },
32
28
  "files": [
33
29
  "lib/index.js",
34
- "lib/invariant.js",
35
30
  "lib/types/**/*.js",
36
31
  "lib/types/**/*.d.ts"
37
32
  ],
38
33
  "license": "MIT",
39
34
  "peerDependencies": {
40
- "@xneog/dsh-invariants": "0.1.0",
41
- "@xneog/dsh-llm": "0.1.0",
42
- "@xneog/dsh-session": "0.1.0",
43
- "@xneog/dsh-subagent": "0.1.0",
44
- "@xneog/dsh-tools": "0.1.0",
45
- "@xneog/cordis": "0.1.0"
35
+ "@xneog/dsh-llm": "^0.1.2-rc.1",
36
+ "@xneog/dsh-subagent": "^0.1.2-rc.1",
37
+ "@xneog/cordis": "^4.0.2",
38
+ "@xneog/dsh-tools": "^0.1.2-rc.1",
39
+ "@xneog/dsh-session": "^0.1.2-rc.1"
46
40
  },
47
41
  "devDependencies": {
48
- "@xneog/dsh-agent": "0.1.0",
49
- "@xneog/dsh-agent-loop": "0.1.0",
50
- "@xneog/dsh-agent-loop-testkit": "0.1.0",
51
- "@xneog/dsh-invariants": "0.1.0",
52
- "@xneog/dsh-llm": "0.1.0",
53
- "@xneog/dsh-session": "0.1.0",
54
- "@xneog/dsh-session-persistence": "0.1.0",
55
- "@xneog/dsh-session-persistence-jsonl": "0.1.0",
56
- "@xneog/dsh-session-projection": "0.1.0",
57
- "@xneog/dsh-subagent": "0.1.0",
58
- "@xneog/dsh-subagent-spawn-in-process": "0.1.0",
59
- "@xneog/dsh-tools": "0.1.0",
60
- "@xneog/cordis": "0.1.0"
42
+ "@xneog/cordis": "^4.0.2",
43
+ "@xneog/dsh-agent": "^0.1.2-rc.1",
44
+ "@xneog/dsh-session": "^0.1.2-rc.1",
45
+ "@xneog/dsh-agent-loop-testkit": "^0.1.2-rc.1",
46
+ "@xneog/dsh-session-persistence": "^0.1.2-rc.1",
47
+ "@xneog/dsh-session-persistence-jsonl": "^0.1.2-rc.1",
48
+ "@xneog/dsh-llm": "^0.1.2-rc.1",
49
+ "@xneog/dsh-session-projection": "^0.1.2-rc.1",
50
+ "@xneog/dsh-session-query": "^0.1.2-rc.1",
51
+ "@xneog/dsh-agent-loop": "^0.1.2-rc.1",
52
+ "@xneog/dsh-subagent": "^0.1.2-rc.1",
53
+ "@xneog/dsh-subagent-fork-in-process": "^0.1.2-rc.1",
54
+ "@xneog/dsh-subagent-spawn-in-process": "^0.1.2-rc.1",
55
+ "@xneog/dsh-tools": "^0.1.2-rc.1"
56
+ },
57
+ "dependencies": {
58
+ "@xneog/dsh-brand": "^0.1.2-rc.1",
59
+ "@xneog/dsh-util-values": "^0.1.2-rc.1"
61
60
  }
62
61
  }
package/lib/invariant.js DELETED
@@ -1,23 +0,0 @@
1
- //#region lib/types/invariant.js
2
- /**
3
- * Package-owned invariant companion for `@xneog/dsh-tool-subagent-control`.
4
- * @module @xneog/dsh-tool-subagent-control/invariant
5
- */
6
- const PACKAGE_NAME = "@xneog/dsh-tool-subagent-control";
7
- /** Cordis companion plugin name. */
8
- const name = "tool-subagent-control-invariant";
9
- /** Service required before the companion can reserve package ownership. */
10
- const inject = ["invariants"];
11
- /**
12
- * No runtime invariant: this model-facing adapter has no independent lifecycle stream; delivery
13
- * and activation relations are owned by the subagent service it calls.
14
- */
15
- const install = () => {};
16
- /**
17
- * Register this package's invariant companion.
18
- * @param ctx - Cordis context carrying the invariant service.
19
- * @returns the installed registration's disposer after setup succeeds.
20
- */
21
- const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
22
- //#endregion
23
- export { apply, inject, name };
@@ -1,16 +0,0 @@
1
- /**
2
- * Package-owned invariant companion for `@xneog/dsh-tool-subagent-control`.
3
- * @module @xneog/dsh-tool-subagent-control/invariant
4
- */
5
- import type { Context } from '@xneog/cordis';
6
- /** Cordis companion plugin name. */
7
- export declare const name = "tool-subagent-control-invariant";
8
- /** Service required before the companion can reserve package ownership. */
9
- export declare const inject: string[];
10
- /**
11
- * Register this package's invariant companion.
12
- * @param ctx - Cordis context carrying the invariant service.
13
- * @returns the installed registration's disposer after setup succeeds.
14
- */
15
- export declare const apply: (ctx: Context) => Promise<() => void>;
16
- //# sourceMappingURL=invariant.d.ts.map
@@ -1,22 +0,0 @@
1
- /**
2
- * Package-owned invariant companion for `@xneog/dsh-tool-subagent-control`.
3
- * @module @xneog/dsh-tool-subagent-control/invariant
4
- */
5
- const PACKAGE_NAME = '@xneog/dsh-tool-subagent-control';
6
- /** Cordis companion plugin name. */
7
- export const name = 'tool-subagent-control-invariant';
8
- /** Service required before the companion can reserve package ownership. */
9
- export const inject = ['invariants'];
10
- /**
11
- * No runtime invariant: this model-facing adapter has no independent lifecycle stream; delivery
12
- * and activation relations are owned by the subagent service it calls.
13
- */
14
- const install = () => { };
15
- /**
16
- * Register this package's invariant companion.
17
- * @param ctx - Cordis context carrying the invariant service.
18
- * @returns the installed registration's disposer after setup succeeds.
19
- */
20
- export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
21
- /* jscpd:ignore-end */
22
- //# sourceMappingURL=invariant.js.map