feat(subagent): durable child catalog and list_agents

Implements the durable-subagent-catalog RFC: SubagentControlService.listChildren()
enumerates a parent's direct continuable children from one sessionQuery trace,
validates each child's sole subagent/descriptor event (now carrying the durable
creation label), and returns one ordered SubagentListEntry[] with per-child
corrupt/unsupported/unavailable diagnostics. The list_agents tool ships as a
separately loadable plugin of dsh-tool-subagent-control requiring sessionQuery
at load; send_message stays usable without it.
This commit is contained in:
Dudu-0223
2026-07-26 02:32:34 +08:00
committed by Tianyi Cui
parent c7acc8fc6c
commit 4240c7dd7b
55 changed files with 1128 additions and 94 deletions

View File

@@ -896,6 +896,10 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
signature: 'async drainContinuableDescendants(parents: readonly Agent[]): Promise<void>',
jsDoc: '/**\n * Close continuable admission below exact live parent Agents, stop only their\n * visible descendant Activations synchronously, then await admitted scoped\n * materializations and release those forests child-first. The scoped cutoff\n * lasts until each exact parent leaves the registry; unrelated parent trees\n * remain live.\n * @param parents - exact host-owned parent Agents entering teardown.\n * @returns once every retained descendant Activation released its `AgentHandle`.\n * @throws an aggregate error after all scoped branches settle when any failed.\n */',
},
{
signature: 'async listChildren(parentSessionId: SessionId): Promise<SubagentListEntry[]>',
jsDoc: '/**\n * Enumerate one session\'s direct continuable children from the durable,\n * live-preferred corpus without loading or resuming an Agent. The lineage\n * trace supplies stable candidate order and live status; each candidate is\n * then inspected independently for exactly one supported descriptor in its\n * own suffix.\n * @param parentSessionId - parent whose direct children are listed.\n * @returns child and diagnostic entries in lineage-trace order.\n */',
},
{
signature: 'registerProvider(provider: SubagentProvider): () => void',
jsDoc: '/**\n * Register a provider under its name. Registration is effect-scoped and HMR\n * safe; removing a provider blocks new starts but does not revoke runs that\n * were already returned to their holders.\n * @param provider - the trusted provider implementation.\n * @returns the exact Cordis effect disposer.\n */',
@@ -1809,7 +1813,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
},
{
name: 'ContinuableStartSpec',
declaration: 'export interface ContinuableStartSpec {\n readonly provider: string;\n readonly request: Omit<SubagentStartRequest, \'signal\' | \'outputSchema\'>;\n readonly signal: AbortSignal;\n}',
declaration: 'export interface ContinuableStartSpec {\n readonly provider: string;\n readonly label: string;\n readonly request: Omit<SubagentStartRequest, \'signal\' | \'outputSchema\'>;\n readonly signal: AbortSignal;\n}',
},
{
name: 'CreateAgentOptions',
@@ -2691,6 +2695,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'SubagentFollowupOptions',
declaration: 'export interface SubagentFollowupOptions {\n readonly source: MessageSource;\n readonly signal: AbortSignal;\n}',
},
{
name: 'SubagentListEntry',
declaration: 'export type SubagentListEntry = {\n readonly kind: \'child\';\n readonly id: SessionId;\n readonly label: string;\n readonly status: \'running\' | \'complete\';\n} | {\n readonly kind: \'diagnostic\';\n readonly id: SessionId;\n readonly reason: \'corrupt\' | \'unsupported\' | \'unavailable\';\n};',
},
{
name: 'SubagentProvider',
declaration: 'export interface SubagentProvider {\n readonly name: string;\n readonly capabilities: SubagentCapabilities;\n readonly inheritsParentContext: boolean;\n start(request: SubagentStartRequest): Promise<SubagentRun>;\n prepareContinuable?(request: ContinuableCreateRequest): Promise<ContinuableCreateSpec>;\n}',