import type { SubagentSteerHandle } from "@sema-agent/core"; /** * C2 (core 1.219) + [ref] (core 1.225) β€” the SERVICE-side registry for * STEERING and RESUMING a Task SUB-AGENT (a sync delegation spawned by the `Agent` tool), the sibling of * {@link import("./workflow-agent-steer.js").WorkflowAgentRegistry} for workflow agents. * * core emits a {@link SubagentSteerHandle} to the deployment's `RunInternals.onSubagentSpawn` sink the * moment a SYNC child spawns (the handle NEVER reaches the model). The service registers it under the * PARENT RUN's taskId so `POST /v1/runs/:runId/subagents/:target/{steer,resume}` can route into it. * Delivery is replica-local (the handle is an in-memory bridge, like `steerableRuns`); a cross-replica * steer/resume is an honest 409, never a silent drop. * * πŸ”΄ LIFECYCLE ([ref] item β‘’ β€” the "不改必踩" change): handles are NO LONGER evicted on child settle. * `resume` is only legal AFTER the child settled (core rejects `steering.still_running` before), so a * settle-evicted registry would make resume structurally unreachable. Instead: * - one handle PER parentToolCallId, NEWEST WINS (core D2: each resume re-emits a FRESH handle under the * same id whose `settled` tracks the revived run β€” `Map.set` replace is exactly that contract); * - handles live until the OWNING LEG ends (each run leg collects its `register()` unregister thunks and * runs them in its finally β€” identity-guarded, so a suspendβ†’fast-resume sibling leg's fresh handles are * never clobbered by the old leg's teardown, mirroring the `steerableRuns` identity guard); * - a per-run cap (oldest-evicted) bounds the map for a delegation-heavy parent (handles now outlive * settle, so an unbounded list would grow for the whole run). * After the parent run ends core disposes the retain ledger anyway (abort in-flight resumes + release all), * so a post-teardown resume would only ever 409 β€” the eviction just keeps the map honest. * * Addressing: `target` matches `parentToolCallId` FIRST (spawn-time identity, unique per delegation), then * falls back to `agentName` (display name; two children of the same agent type collide β†’ the route 409s * rather than steering/resuming an arbitrary sibling). Steer-in redaction is shared with the workflow route * (`redactSteerIn`) β€” one owner of the trust gate. πŸ” `handle.childSessionId` is a continuation * CAPABILITY: control-plane only β€” no route response may include it. * * SCOPE (core r1-M3): only SYNC delegations emit a handle β€” `run_in_background` children have neither steer * nor resume (poll/stop via TaskOutput/TaskStop; durable bg revival is a separate core item). */ /** Per-run handle cap β€” handles now live until the leg ends (not settle), so bound the map. Oldest-evicted; * 256 mirrors the ToolDetachHub bound (a run with >256 live-or-resumable delegations is pathological). */ export declare const SUBAGENT_HANDLES_PER_RUN_CAP = 256; export declare class SubagentSteerRegistry { /** parent run taskId β†’ (parentToolCallId β†’ NEWEST handle). Map iteration order = insertion = age. */ private readonly byRun; /** Register a child handle under its parent run. SAME parentToolCallId replaces (core D2: a resume * re-emits a fresh handle for the revived run β€” newest wins). Returns an IDENTITY-GUARDED unregister * thunk: it removes the entry only while it still holds THIS handle (a fresh sibling-leg registration * under the same id survives the old leg's teardown). Call it at LEG END, not on settle ([ref] β‘’). */ register(runId: string, handle: SubagentSteerHandle): () => void; /** Resolve `runId` + `target` to its handle on this replica. Exact `parentToolCallId` match wins (unique β€” * newest-per-id by construction β†’ count is 0|1); otherwise match by `agentName` (count may be >1 β†’ * ambiguous, `handle` undefined β†’ the route 409s). count=0 β†’ no such child here. */ resolve(runId: string, target: string): { handle?: SubagentSteerHandle; count: number; }; /** Total registered handles (test/observability). */ get size(): number; } //# sourceMappingURL=subagent-steer.d.ts.map