Fix provider diagnostics and ownership docs
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/subagent/README.md
|
||||
README.md: abe1432d3c4ea0f67ed3cdf1bb4aec5f817d17b5
|
||||
README.zh.md: 3df2b6c62dd355db2991468ad19883cd27c280cd
|
||||
README.md: 4008bbb2a69c058fc49b33fd3e7b2e2b426d1197
|
||||
README.zh.md: 1ef95a5bdfaa68205ddf428b1f3ec177e4dc39f7
|
||||
|
||||
@@ -17,6 +17,6 @@ The subagent seam: an agent delegating work to a child agent. Like the [bash](..
|
||||
| `tool-subagent-control/` | The optional, globally named `send_message` and `list_agents` tools over `ctx.subagents` | (registers on `ctx.tools`) |
|
||||
| `tool-subagent-report/` | Child-scoped `report` return channel for continuable in-process children | (registers in each child scope) |
|
||||
|
||||
The interface and continuation orchestration live at `subagent/subagent/`. One-shot provider `start` dispatch stays independent of persistence; an internal continuation manager owns each durable continuable child as one Session plus at most one process-local Activation, binding no Task, and exists only while the Agent service is present, resolving persistence per continuation operation. The in-process `subagent-spawn` / `subagent-fork` backends share the `subagent-inprocess` driver (a library with no provider of its own — both depend on it, neither on the other), and the out-of-process `subagent-acp` / `subagent-codex` / `subagent-dsh-sdk` backends spawn their children through the [`subprocess/`](../subprocess/README.md) seam (the shared credential scrub, tree-scoped teardown, and dispose ladder). Tests replace only external or nondeterministic product boundaries with package-local fixtures.
|
||||
The interface and continuation orchestration live at `subagent/subagent/`. One-shot provider `start` dispatch stays independent of persistence; an internal continuation manager owns each durable continuable child as one Session plus at most one process-local Activation, binding no Task, and exists only while the Agent service is present, resolving persistence per continuation operation. The in-process `subagent-spawn` / `subagent-fork` backends share the `subagent-inprocess` driver (a library with no provider of its own — both depend on it, neither on the other). The out-of-process `subagent-acp` / `subagent-codex` backends spawn through the [`subprocess/`](../subprocess/README.md) seam, which owns credential scrubbing, termination escalation, and whole-tree exit observation; `subagent-dsh-sdk` instead delegates process creation and teardown to the TypeScript SDK client that owns its transport, while reusing the seam's credential scrub. Tests replace only external or nondeterministic product boundaries with package-local fixtures.
|
||||
|
||||
The design rationale: [.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md), [.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md](../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md), and [.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md).
|
||||
|
||||
@@ -17,6 +17,6 @@ subagent(子 agent)seam 允许 agent(智能体)把工作委派给子 age
|
||||
| `tool-subagent-control/` | 基于 `ctx.subagents`、可选且全局名称唯一的 `send_message` 与 `list_agents` 工具 | (注册到 `ctx.tools`) |
|
||||
| `tool-subagent-report/` | 子级作用域的 `report` 返回通道,用于可继续的进程内子级 | (注册到每个子级作用域) |
|
||||
|
||||
接口和继续执行编排位于 `subagent/subagent/`。一次性提供方 `start` 分发不依赖持久化;内部继续执行管理器把每个持久化可继续子 agent 作为一个 Session 加至多一个进程内 Activation 来拥有,不绑定任何 Task,且只在 Agent 服务存在时存在,并按每项继续执行操作解析持久化。进程内 `subagent-spawn` / `subagent-fork` 后端共享 `subagent-inprocess` 驱动器(一个自身不含提供方的库:两者都依赖它,彼此不依赖),进程外 `subagent-acp` / `subagent-codex` / `subagent-dsh-sdk` 后端则经由 [`subprocess/`](../subprocess/README.md) seam spawn 其子进程(共享的凭据清除、以进程树为范围的拆卸、dispose(资源释放)阶梯)。测试只用包内 fixture(测试前置数据)替换外部或非确定性的产品边界。
|
||||
接口和继续执行编排位于 `subagent/subagent/`。一次性提供方 `start` 分发不依赖持久化;内部继续执行管理器把每个持久化可继续子 agent 作为一个 Session 加至多一个进程内 Activation 来拥有,不绑定任何 Task,且只在 Agent 服务存在时存在,并按每项继续执行操作解析持久化。进程内 `subagent-spawn` / `subagent-fork` 后端共享 `subagent-inprocess` 驱动器(一个自身不含提供方的库:两者都依赖它,彼此不依赖)。进程外 `subagent-acp` / `subagent-codex` 后端则经由 [`subprocess/`](../subprocess/README.md) seam spawn 其子进程,该 seam 拥有凭据清除、终止升级和整棵进程树的退出观测;`subagent-dsh-sdk` 则将进程创建和拆卸委托给拥有自身传输的 TypeScript SDK 客户端,同时复用该 seam 的凭据清除机制。测试只用包内 fixture(测试前置数据)替换外部或非确定性的产品边界。
|
||||
|
||||
设计理由见 [.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md)、[.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md](../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md) 和 [.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md)。
|
||||
|
||||
@@ -55,11 +55,17 @@ class CodexProvider implements SubagentProvider {
|
||||
) {}
|
||||
|
||||
start(request: ResolvedSubagentStartRequest) {
|
||||
const parentCwd = request.parent.session.header.cwd
|
||||
if (parentCwd === undefined) {
|
||||
throw new Error(
|
||||
'subagent-codex: no working directory for the child — delegate from a parent session that has one',
|
||||
)
|
||||
}
|
||||
const spec: CodexRunSpec = {
|
||||
cwd: resolveChildCwd(
|
||||
'subagent-codex',
|
||||
undefined,
|
||||
request.parent.session.header.cwd,
|
||||
parentCwd,
|
||||
),
|
||||
env: this.config.env,
|
||||
disposeGraceMs: this.config.disposeGraceMs,
|
||||
|
||||
@@ -300,6 +300,27 @@ describe('task admission and package contracts', () => {
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
|
||||
it('requires a parent session cwd without suggesting unsupported config', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SubagentService)
|
||||
await ctx.plugin(LocalSubprocessService)
|
||||
const spawn = vi.spyOn(ctx.subprocess, 'spawn')
|
||||
await ctx.plugin(codex, {})
|
||||
|
||||
await expect(ctx.subagents.start('codex', {
|
||||
prompt: [{ type: 'text', text: 'task' }],
|
||||
parent: {
|
||||
id: 'parent-without-cwd',
|
||||
session: { header: {} },
|
||||
} as unknown as Agent,
|
||||
signal: new AbortController().signal,
|
||||
})).rejects.toThrow(
|
||||
'subagent-codex: no working directory for the child — delegate from a parent session that has one',
|
||||
)
|
||||
expect(spawn).not.toHaveBeenCalled()
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
|
||||
it('keeps the namespace export shape and package-owned empty invariant', async () => {
|
||||
expect('default' in codex).toBe(false)
|
||||
expect(codex.name).toBe('subagent-codex')
|
||||
|
||||
Reference in New Issue
Block a user