Fix review findings: lifecycle containment, configurable tool name, coverage, type catalog
Address four findings from the first Codex review round: - Contain subagent/start|end listener throws (emitContainedStart/End): a thrown lifecycle listener could escape SubagentService.start() before the caller received the live run to dispose it (a leaked child), and a thrown subagent/end listener could surface as an unhandled rejection on the detached result-settle hook. Both emits now log-and-contain, mirroring the agent registry's agent/created|disposed containment. - Make the model-facing tool name configurable (Config.toolName, default subagent). The docs say to load dsh-tool-subagent once per provider to expose multiple transports, but the hardcoded name made the second load throw a duplicate-tool-name error; a distinct toolName per load is now required and documented. - Reach the per-file 100% coverage gate: tests for the subagent/end error branch, lifecycle-listener containment, every stopReasonError arm + the merge-extensible default, the multi-provider toolName path, agentOptions forwarding, and the direct-apply schema-bypass fallbacks. - Document the seam vocabulary in docs/core-data-structures/subagent.md with verbatim type-equiv blocks + manifest entries, and link it from core.md (a brand-new core/seam type the doc-sync gate cannot detect on its own).
This commit is contained in:
@@ -35,6 +35,14 @@ export const inject = ['tools', 'subagents']
|
||||
export interface Config {
|
||||
/** The `ctx.subagents` provider name to start runs on (e.g. `spawn`, `acp`). */
|
||||
provider: string
|
||||
/**
|
||||
* The model-facing tool name to register (default `subagent`). To expose more
|
||||
* than one transport, load this plugin once per provider — each load MUST set
|
||||
* a distinct `toolName` (the tool registry rejects a duplicate name), e.g.
|
||||
* `{ provider: 'spawn', toolName: 'subagent' }` and
|
||||
* `{ provider: 'acp', toolName: 'subagent_acp' }`.
|
||||
*/
|
||||
toolName?: string
|
||||
/**
|
||||
* Default per-child agent options (model, system prompt) applied to every
|
||||
* spawned child. Omitted fields fall back to the child loop's own defaults.
|
||||
@@ -44,6 +52,7 @@ export interface Config {
|
||||
|
||||
export const Config: z<Config> = z.object({
|
||||
provider: z.string().required(),
|
||||
toolName: z.string().default('subagent'),
|
||||
agentOptions: z.object({
|
||||
model: z.string(),
|
||||
systemPrompt: z.string(),
|
||||
@@ -85,7 +94,7 @@ function stopReasonError(result: SubagentResult): string | undefined {
|
||||
|
||||
export function apply(ctx: Context, config: Config): void {
|
||||
ctx.tools.register(defineTool({
|
||||
name: 'subagent',
|
||||
name: config.toolName ?? 'subagent',
|
||||
description:
|
||||
'Delegate a self-contained task to a subagent (a separate agent that works in its own context) '
|
||||
+ 'and return its final result. Use this to offload focused, independent work — research, a scoped '
|
||||
|
||||
Reference in New Issue
Block a user