zidane/ agent
The agent core, createAgent(), with manual compaction, run summaries, cache telemetry and read-state helpers.
import { … } from 'zidane/agent'src/agent-entry.ts
Use zidane/agent for the agent loop without the eval, headless and UI surfaces of the root zidane entry. It holds createAgent(), the Agent and AgentOptions types, hook maps, the run-summary collector, prompt-cache drift logging and the per-session read state. An agent can also compact its own conversation with agent.compact(), report when its prompt cache is estimated to expire with agent.cacheExpiresAt(), and estimate what a model, thinking, system or tools change would re-read with agent.cacheImpact(). Every symbol is also exported from zidane. See Agents for the guide.
import { createAgent, createRunSummaryCollector } from 'zidane/agent'
import { basic } from 'zidane/presets'
import { anthropic } from 'zidane/providers/anthropic'
const agent = createAgent({ ...basic, provider: anthropic() })
const collector = createRunSummaryCollector()
const uninstall = collector.install(agent.hooks)
await agent.run({ prompt: 'Name the files in this directory.' })
uninstall()
console.log(collector.latest()?.totals.output)
console.log(agent.cacheImpact({ model: 'claude-opus-5' }).tokens, agent.cacheExpiresAt())
const result = await agent.compact({ keepTurns: 2 })
console.log(result.compacted)Functions#
createAgentFunction#
Creates an agent: a provider, tools, hooks and optional session behind one run() call.
Setup is synchronous. MCP servers and skills connect on the first run() or warmup(),
or in the background when eager is set. Throws when provider is an empty array or
a multi-provider list has blank or duplicate provider names.
function createAgent(rawOptions: AgentOptions): AgentParameters
| Name | Type | Description |
|---|---|---|
| rawOptions | AgentOptions | Agent options. Only |
Example
~~~ts
import { createAgent } from 'zidane'
import { anthropic } from 'zidane/providers/anthropic'
const agent = createAgent({
provider: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }),
system: 'You are a helpful assistant.',
})
const stats = await agent.run({ prompt: 'Say hello.' })
console.log(`Done in ${stats.turns} turns`)
await agent.destroy()
~~~createRunSummaryCollectorFunction#
Build a run-summary collector. State is created fresh inside each
install() call, so a single collector instance can be installed
across multiple agents without attribution cross-talk. latest()
returns the most-recent summary across any install — install
per-agent collectors if you need separate post-run snapshots.
function createRunSummaryCollector(
options?: RunSummaryCollectorOptions,
): RunSummaryCollectorParameters
| Name | Type | Default | Description |
|---|---|---|---|
| options? | RunSummaryCollectorOptions | {} | Collector options. |
Returns
Example
const collector = createRunSummaryCollector({
onSummary: s => console.log(JSON.stringify(s)),
})
const uninstall = collector.install(agent.hooks)
try { await agent.run({ prompt }) }
finally { uninstall() }diffCacheDimensionsFunction#
Compare two snapshots and return the list of dimensions whose bytes
differ. The details map carries a short human-readable description per
changed dimension (e.g. system: 1024 → 1031 bytes).
Pure / no I/O. Use this in tests to assert the diff logic; the logger itself just formats and emits the result.
function diffCacheDimensions(
prev: CacheDimensionSnapshot,
curr: CacheDimensionSnapshot,
): CacheDimensionDiffParameters
| Name | Type | Description |
|---|---|---|
| prev | CacheDimensionSnapshot | Snapshot of the previous turn. |
| curr | CacheDimensionSnapshot | Snapshot of the current turn. |
Returns
fnv1a32Function#
FNV-1a-style 32-bit hash over UTF-16 code units. Pure and allocation-free. Cryptographic strength is irrelevant here — we only need change detection, and a 32-bit collision is unlikely for the few dimensions tracked.
Feeds the FULL char code into the mix (same as read-state's
hashContent) — masking to the low byte would collapse code points that
share a low byte, so a non-ASCII system-prompt change could hash
identically and silently hide a cache-dimension drift.
Exported for tests; callers shouldn't typically import directly.
function fnv1a32(s: string): numberParameters
| Name | Type | Description |
|---|---|---|
| s | string | String to hash. |
Returns
numberhashMessagesFunction#
Hash each message independently, preserving order.
Per-message rather than cumulative so the diff can tell an APPEND (healthy)
from a REWRITE (a cache break) by comparing positionally — see
CacheDimensionSnapshot.messageHashes.
Folds field-by-field into a rolling FNV rather than JSON.stringify-ing the
whole list at once: on a long run the transcript is hundreds of KB, and
building one giant string every turn just to throw it away is the kind of
overhead that gets telemetry ripped back out.
Exported so hosts can stamp the same hashes onto their own spans.
function hashMessages(messages: readonly { content: unknown; role: string }[]): number[]Parameters
| Name | Type | Description |
|---|---|---|
| messages | readonly { content: unknown; role: string }[] | Messages in transcript order. |
Returns
number[]installCacheBreakLoggerFunction#
Install the cache-break logger on an agent's hook bus.
Subscribes to turn:before (snapshot) + turn:after (compare with prior
snapshot, log breaks), plus session:start + agent:start to pick up the
session's baseline (unless sessionBaseline: false). Returns an
uninstall fn that detaches every listener. Idempotent w.r.t. agent
lifecycle — installs/uninstalls cleanly across runs.
Recommended setup:
const uninstall = installCacheBreakLogger(agent.hooks, {
log: line => logger.debug(line),
verbose: !!process.env.ZIDANE_DEBUG?.includes('cache'),
})
// ...
uninstall()
Performance: hashing runs on each turn:before. The tools array and each
message block are serialized once per object (memoized), so a turn costs
the system prompt plus the blocks that are new since the last one.
Disabled callers pay nothing (don't install).
function installCacheBreakLogger(
hooks: Hookable<AgentHooks>,
options?: CacheBreakLoggerOptions,
): () => voidParameters
| Name | Type | Default | Description |
|---|---|---|---|
| hooks | Hookable<AgentHooks> | Agent hook bus to install the logger on. | |
| options? | CacheBreakLoggerOptions | {} | Logger options. All fields are optional. |
Returns
() => voidsnapshotCacheDimensionsFunction#
Build a CacheDimensionSnapshot from a turn:before payload.
Pure — exported so hosts can compute snapshots outside the logger (e.g. stamping them onto custom telemetry spans). The hash is deterministic on byte-identical inputs.
function snapshotCacheDimensions(
options: StreamOptions,
sections?: CachePromptSections,
): CacheDimensionSnapshotParameters
| Name | Type | Description |
|---|---|---|
| options | StreamOptions | Stream options of the outgoing request. |
| sections? | CachePromptSections | Texts of the sections inside |
Returns
Interfaces#
AgentInterface#
Agent instance returned by createAgent. Runs prompts, queues steering messages and exposes live state.
interface AgentProperties
| Name | Type | Description |
|---|---|---|
| [asyncDispose] | () => Promise<void> | TC39 explicit-resource-management Requires Node 20+ (or any runtime that ships the TC39
explicit-resource-management proposal). Older runtimes simply ignore
the symbol; nothing breaks. Idempotent because |
| abort | () => void | Aborts the current run: the run signal fires, so an in-flight provider stream and tool calls unwind, and |
| activateSkill | (name: string) => Promise<void> | Explicitly activate a skill by name. Fires |
| activeSkillsreadonly | readonly ActiveSkill[] | Snapshot of currently active skills. |
| cacheExpiresAt | () => number | null | When the provider's prompt cache for this conversation is estimated to
expire, in epoch ms: the time this agent sent its last request plus the
cache lifetime that request used ( |
| cacheImpact | (change: AgentCacheChange) => AgentCacheImpact | Estimate what |
| cancelTool | (callId: string, reason?: string) => boolean | Cancel a single in-flight tool call by id without aborting the rest of
the run. The matching call's Returns The optional |
| compact | (options?: AgentCompactOptions) => Promise<AgentCompactResult> | Summarize the session's older turns now, the way auto-compaction does
inside a run: same summary, restoration of recently read files, hooks
( Needs an idle agent: rejects while a run (or another compaction) is in
progress, and |
| deactivateSkill | (name: string) => Promise<void> | Deactivate a skill by name. Fires |
| decide | AgentDecide | Make a typed decision request (predicate probability, choice, score)
against the agent's decision provider — see |
| destroy | () => Promise<void> | Destroy the execution context and clean up resources.
Idempotent — safe to call from both a |
| ensureExecution | () => Promise<ExecutionHandle> | Ensure the execution handle exists (spawning it if this is called
before the first run), firing the one-time |
| executionreadonly | ExecutionContext | Execution context that decides where tools run. |
| followUp | (prompt: string | PromptPart[], options?: QueuedMessageOptions) => boolean | Queue a message delivered after the run would otherwise end (the model
returned a final answer). Same abort contract as |
| getContextBreakdown | (opts?: ContextBreakdownOptions) => Promise<ContextBreakdown | null> | Categorized context-window usage — system prompt, rules, skills, MCP
tools/instructions, searchable-tools catalog, tool definitions, conversation, deferred
buckets, autocompact buffer, and free space. Computed on demand (the host
opens a panel / popover). Available even before the first run: when no run
snapshot exists yet it assembles a skills-only pre-run view (no MCP connect),
so it never returns Per-category counts are heuristic estimates reconciled so the live
categories sum to the real last-turn total; when the provider exposes
The host supplies |
| getContextSegments | (opts?: { rulesBlock?: string; rulesFiles?: readonly ContextSegmentFile[]; systemPath?: string }) => Promise<ContextSegment[] | null> | The TEXT of every chunk in the session's default context, each tagged with
where it came from — the sibling of Reads the live run snapshot, falling back to the same pre-run assembly
Instruction files (AGENTS.md / CLAUDE.md) are HOST-supplied via
|
| handlereadonly | ExecutionHandle | null | Live execution handle, or |
| hasPendingTaskNotificationsreadonly | boolean |
This is the host-side "auto-wake" signal: when it flips true while
the agent is idle, a drain-only |
| hooks | AgentHookBus | Hook bus for registering handlers on agent lifecycle events. |
| isRunningreadonly | boolean |
|
| killBackgroundTask | (taskId: string) => Promise<boolean> | Host-side kill for a background task spawned via
Returns
Distinct from the model-facing |
| mcpConnectionreadonly | McpConnection | null | The live MCP connection — bound lazily by the first |
| mcpServersreadonly | readonly McpServerConfig[] | The TRANSFORMED MCP config list ( |
| metareadonly | Readonly<Record<string, unknown>> | Frozen view of the underlying |
| reset | () => Promise<void> | Clear the agent's in-memory state (turns, queues, skill activations).
Fires |
| resume | (options?: DurableResumeOptions) => Promise<AgentStats | null> | Continue the session's run that a process death left unfinished, from
its saved turns and the durable runtime's step records: finished steps
answer from their records, a pending Resolves The resumed run keeps its id and record, and runs with the options its
record kept ( Rejects without a checkpoint-mode |
| run | (options: AgentRunOptions) => Promise<AgentStats> | Runs the agent loop until the model stops, a limit is hit or the run is aborted. Rejects when a run is already in progress.
With |
| sessionreadonly | Session | null | Session bound to the agent, or |
| steer | (prompt: string | PromptPart[], options?: QueuedMessageOptions) => boolean | Queue a mid-run nudge injected as a user turn at the next tool/turn
boundary. Returns Hosts emulating Stop-hook semantics (queueing a follow-up on
|
| suspend | (options?: { graceMs?: number }) => Promise<void> | Stops the current run so |
| toolStatereadonly | AgentToolState | Read-only view of this agent's committed durable tool state. See
|
| turnsreadonly | SessionTurn[] | Conversation turns held in memory by the agent. |
| waitForIdle | () => Promise<void> | Resolves when the current run has finished. Resolves immediately when the agent is idle. |
| warmup | () => Promise<void> | Pre-connect MCP servers without running a turn. Idempotent and concurrency-safe:
Use from host code that wants to hide MCP bootstrap latency behind other startup work (UI init, auth, etc.). Safe to call multiple times and from multiple callers concurrently. |
AgentHooksInterface#
Event map for the agent hook bus. Each key is a hook name and each value is the handler signature. Register handlers with agent.hooks.hook(name, fn) or the hooks option of createAgent.
interface AgentHooksProperties
| Name | Type | Description |
|---|---|---|
| agent:abort | (ctx: { reason?: string; runId?: string }) => void | The run was aborted — fires exactly once per aborted run, on every abort
exit: a between-turn cancel (the loop's Does NOT fire on graceful non-cancel terminations that still finalize the
session run as
|
| agent:done | (ctx: AgentStats) => void | Run finished — fires on all exit paths (completion, maxTurns, abort, error). The |
| agent:start | (ctx: { agentName?: string; depth: number; parentRunId?: string; providerName?: string; runId: string; startedAt: number; tracingContext?: Readonly<Record<string, string>> }) => void | Fires once per
|
| artifact:recorded | (ctx: { artifact: SessionArtifact; callId: string; name: string; runId: string; turnId: string }) => void | Fires once per artifact the session recorded from a tool result's
reserved |
| background:adopt | (ctx: { command: string; cwd: string; outputPath: string; pid: number; startedAt: number; taskId: string }) => void | A still-running background task was handed to THIS agent by its host —
the agent was built on a host-owned handle that outlived the previous
agent (a rebuild after a model swap, settings change, MCP login…; see
|
| background:exit | (ctx: { command: string; durationMs: number; exitCode: number; outputPath: string; signal?: Signals; status: 'exited' | 'killed'; taskId: string }) => void | Fires exactly once per task when the child process terminates —
natural exit, host-issued kill, or |
| background:reassign | (ctx: { childId?: string; command: string; fromHandleId: string; outputPath: string; pid: number; startedAt: number; taskId: string; toHandleId: string }) => void | Ownership of a background task was transferred from one execution
handle to another — typically the spawn tool reassigning a
subagent's still-running tasks up to the parent's handle so they
outlive the subagent's Hosts that listed the task under the source handle should update
their view to reflect the new owner. The TUI's Fires on the new owner's hook bus (the parent's, in the spawn case). The old owner is typically already torn down — wiring the event the other way would deliver it to a destroyed hookable. |
| background:reconcile | (ctx: { exits: TaskExitInfo[] }) => void | Fires at the start of an This is the journalable seam for durable-execution hosts: the hook
runs inside the Mutable: handlers may reorder, filter, or substitute entries. The post-hook array is what the model sees. |
| background:stall | (ctx: { bytesWritten: number; command: string; outputPath: string; stalledForMs: number; taskId: string }) => void | The stall watchdog ( |
| background:start | (ctx: { command: string; cwd: string; outputPath: string; pid: number; startedAt: number; taskId: string }) => void | Fires when a background task is spawned via
|
| budget:exceeded | (ctx: { budget: number; bytes: number; turn: number; turnId: string }) => void | Fires when a turn's total tool-output bytes exceed |
| cache:break | (info: CacheBreakInfo) => void | Promise<void> | Fires when a turn read back materially less cached prefix than the
previous turn established — a prompt-cache break. Emitted by
The payload names the request dimensions that changed ( |
| cache:drift | (info: CacheDriftInfo) => void | Promise<void> | Fires at Drift + healthy Delivery is fire-and-forget: |
| child:artifact:recorded | (ctx: { artifact: SessionArtifact; callId: string; childId: string; depth: number; name: string; runId: string; turnId: string }) => void | Bubbled from a subagent's |
| child:background:exit | (ctx: { childId: string; command: string; depth: number; durationMs: number; exitCode: number; outputPath: string; signal?: Signals; status: 'exited' | 'killed'; taskId: string }) => void | Bubbled from a subagent's |
| child:background:reassign | (ctx: { childId: string; command: string; depth: number; fromHandleId: string; outputPath: string; pid: number; startedAt: number; taskId: string; toHandleId: string }) => void | Bubbled from a subagent's |
| child:background:start | (ctx: { childId: string; command: string; cwd: string; depth: number; outputPath: string; pid: number; startedAt: number; taskId: string }) => void | Bubbled from a subagent's |
| child:mcp:tool:gate | (ctx: McpToolHookContext & { block: boolean; childId: string; depth: number; reason: string; result?: string | ToolResultContent[] }) => void | Bubbled |
| child:repeat-guard:exceeded | (ctx: { action: 'steer' | 'block' | 'abort'; childId: string; count: number; depth: number; threshold: number; tool: string; turnId: string }) => void | Bubbled sibling of |
| child:spawn:before | (ctx: ChildSpawnHookContext) => void | Nested spawn lifecycle — a SUBAGENT's |
| child:spawn:complete | (ctx: ChildRunStats & ChildSpawnIdentity) => void | Bubbled |
| child:spawn:error | (ctx: Omit<SpawnHookContext, 'tracingContext'> & ChildSpawnIdentity & { error: Error }) => void | Bubbled |
| child:stream:end | (ctx: StreamHookContext & { childId: string; depth: number; text: string }) => void | Bubbled |
| child:stream:error | (ctx: StreamHookContext & { childId: string; depth: number; err: unknown }) => void | Bubbled |
| child:stream:retry | (ctx: StreamHookContext & { attempt: number; childId: string; delayMs: number; depth: number; discardedText?: true; discardedThinking?: true; err: unknown; maxAttempts: number; nextAttempt: number; requestId?: string; statusCode?: number }) => void | Bubbled |
| child:stream:server_tool_result | (ctx: StreamHookContext & { childId: string; content: unknown; depth: number; toolName: string; toolUseId: string }) => void | Bubbled |
| child:stream:server_tool_use | (ctx: StreamHookContext & { childId: string; depth: number; id: string; input: Record<string, unknown>; name: string }) => void | Bubbled |
| child:stream:text | (ctx: StreamHookContext & { childId: string; delta: string; depth: number; text: string }) => void | Bubbled |
| child:stream:thinking | (ctx: StreamHookContext & { childId: string; delta: string; depth: number; thinking: string }) => void | Bubbled |
| child:tool:after | (ctx: ToolHookContext & { childId: string; coercions?: readonly string[]; depth: number; isError?: boolean; outcome?: ToolOutcome; outputBytes: number; result: string | ToolResultContent[]; runToolCounts: Readonly<Record<string, number>> }) => void | Bubbled |
| child:tool:before | (ctx: ToolHookContext & { childId: string; coercions?: readonly string[]; depth: number; priorContent?: string; runToolCounts: Readonly<Record<string, number>> }) => void | Bubbled |
| child:tool:cancelled | (ctx: ToolHookContext & { childId: string; depth: number; reason: string; runToolCounts: Readonly<Record<string, number>> }) => void | Bubbled sibling of |
| child:tool:dispatched | (ctx: ToolHookContext & { childId: string; depth: number; outcome: 'execute' | 'gate-substitute' | 'gate-block' | 'unknown' | 'invalid-input'; reason?: string; runToolCounts: Readonly<Record<string, number>> }) => void | Bubbled sibling of |
| child:tool:error | (ctx: ToolHookContext & { childId: string; depth: number; error: Error }) => void | Bubbled |
| child:tool:gate | (ctx: ToolHookContext & ToolGateResumeFields & { block: boolean; childId: string; depth: number; metadata: ToolResultMetadata; reason: string; result?: string | ToolResultContent[]; runToolCounts: Readonly<Record<string, number>> }) => void | Gate-style child events. Unlike the other Use these to gate subagent tool calls (native + MCP) from the parent
without registering listeners on every child agent. The parent's own
|
| child:tool:progress | (ctx: ToolHookContext & { childId: string; depth: number; progress: ToolProgress }) => void | Bubbled |
| child:tool:transform | (ctx: ToolHookContext & { childId: string; coercions?: readonly string[]; depth: number; isError: boolean; metadata: ToolResultMetadata; outputBytes: number; result: string | ToolResultContent[] }) => void | Same-ref bubble of a subagent's |
| child:turn:after | (ctx: { aborted?: boolean; childId: string; depth: number; message: SessionTurn; toolCounts: { run: Readonly<Record<string, number>>; turn: Readonly<Record<string, number>> }; turn: number; turnId: string; usage: TurnUsage }) => void | Bubbled |
| child:validation:reject | (ctx: ToolHookContext & { childId: string; depth: number; reason: string; schema: Record<string, unknown> }) => void | Bubbled sibling of |
| compact:end | (ctx: CompactEndHookContext) => void | Fires exactly once after every |
| compact:progress | (ctx: CompactProgressHookContext) => void | Fires (throttled) while the compaction summary streams — carries a
rough cumulative output-token estimate ( |
| compact:restore | (ctx: { add: (call: { input: Record<string, unknown>; name: string; output: string }) => void; session?: Session }) => void | Promise<void> | Fires while a compaction builds the turns it re-attaches after the
summary. |
| compact:start | (ctx: CompactStartHookContext) => void | Fires when loop-native auto-compaction ( Use it to surface a "Compacting…" banner / footer state. Observational
— handlers cannot cancel the compaction. Always paired with exactly one
|
| content-ref:resolve | (ctx: { block: ResolveContentRefBlock; childId?: string; data?: string; depth?: number }) => void | Resolves persisted content refs into provider-bound payload bytes. Fires only when wire assembly sees a block carrying |
| context:overflow | (ctx: StreamHookContext & { attempt: number; err: unknown }) => void | Fires when the loop catches a Runs independently of
Observational — handlers cannot cancel the recovery. |
| context:transform | (ctx: { messages: SessionMessage[] }) => void | Fires per request with the session messages about to be sent. Mutate |
| decision:end | (ctx: DecisionEndHookContext) => void | Fires once per decision, success or failure, after |
| decision:start | (ctx: DecisionStartHookContext) => void | Fires when |
| execution:ready | (ctx: { handle: ExecutionHandle; spawned: boolean }) => void | Fires exactly once per agent, the first time the execution handle is bound
— right after the lazy The seam durable hosts use to record the minted handle id (e.g. a sandbox
id) for re-attach on replay/resume — journal it here, then re-attach via
|
| history:repair | (ctx: HistoryRepairHookContext) => void | Fires after a provider stream settles when the loop scrubbed
provider-rejected reasoning state from canonical history (e.g.
Anthropic signature-400 poisoning). In-memory turns and the bound
session are already repaired; hosts mirroring turns into an external
durable store must heal it using |
| mcp:auth:error | (ctx: { error: Error; name: string }) => void | Interactive login failed (browser error, abort, exchange failure). |
| mcp:auth:required | (ctx: { name: string; reason: 'no-tokens' | 'auto-promoted'; transport: 'sse' | 'stdio' | 'streamable-http' }) => void | Fires when a server's bootstrap was skipped because it requires OAuth
login and no tokens are stored. The host (TUI) surfaces this as a
"needs-auth" status so the user can opt in to interactive login via
|
| mcp:auth:success | (ctx: { name: string }) => void | Interactive login completed and tokens were persisted. |
| mcp:auth:url | (ctx: { name: string; url: string }) => void | Fires during an INTERACTIVE OAuth login flow — when the SDK wants the
user agent to navigate to the authorization URL. Hosts open a browser
and surface the URL in the TUI (in case the browser auto-open fails).
Distinct from |
| mcp:bootstrap:end | (ctx: { durationMs: number; name: string; transport: string } & ({ cached?: boolean; lazy?: boolean; ok: true; toolCount: number; warnings?: string[] } | { error: Error; ok: false })) => void | Fires at the end of a per-server bootstrap attempt.
|
| mcp:bootstrap:settled | (ctx: { results: readonly ({ durationMs: number; name: string; transport: string } & ({ lazy?: boolean; ok: true; toolCount: number } | { error?: Error; ok: false; skipped?: boolean }))[] }) => void | Fires exactly ONCE per bootstrap batch, after every server's attempt
has settled. |
| mcp:bootstrap:start | (ctx: { name: string; transport: string }) => void | Fires at the start of a per-server bootstrap attempt, before any network I/O.
Pairs with |
| mcp:close | (ctx: { name: string }) => void | Fires when an MCP server connection is closed. |
| mcp:connect | (ctx: { lazy?: boolean; name: string; tools: string[]; transport: string }) => void | Fires once a server's client is live (transport connected, SDK handshake
complete). For servers bootstrapped eagerly, fires right after bootstrap;
for servers with |
| mcp:error | (ctx: { error: Error; name: string }) => void | Fires when an MCP server fails to connect or errors fatally. |
| mcp:tool:after | (ctx: McpToolHookContext & { outputBytes: number; result: string | ToolResultContent[] }) => void | Fires after an MCP tool call returns, with the result. |
| mcp:tool:before | (ctx: McpToolHookContext) => void | Fires before an MCP tool call is sent to the server. |
| mcp:tool:error | (ctx: McpToolHookContext & { error: Error }) => void | Fires when an MCP tool call throws. |
| mcp:tool:gate | (ctx: McpToolHookContext & { block: boolean; reason: string; result?: string | ToolResultContent[] }) => void | MCP-side counterpart of Fires INSIDE the MCP wrapper's |
| mcp:tool:transform | (ctx: McpToolHookContext & { outputBytes: number; result: string | ToolResultContent[] }) => void | Fires before the MCP tool result is returned. Mutate |
| mcp:tools:filter | (ctx: { server: string; tools: { description?: string | null; inputSchema?: unknown; name: string }[]; transport: 'sse' | 'stdio' | 'streamable-http' }) => void | Fires once per server after Composes with config-side filters: config drops tools the host's static policy excludes; this hook is the runtime escape hatch for per-user, per- environment, or capability-driven decisions that the config can't express. Items are upstream tool descriptors (NOT yet namespaced as |
| mcp:tools:list | (ctx: { name: string; tools: readonly McpToolSchema[]; transport: 'sse' | 'stdio' | 'streamable-http' }) => void | Fires once per successful bootstrap with the raw, pre-filter tool
schemas the server advertised via
Read-only — handlers must NOT mutate Does NOT fire when the bootstrap fails. Does NOT fire on lazy-connect
servers' deferred first-call (the schemas were already known from
|
| mcp:warn | (ctx: { message: string; name: string }) => void | Diagnostic line from a server. For fatal failures hook |
| model:fallback | (ctx: StreamHookContext & { discardedThinking?: true; err: Error; from: { model: string; provider: string }; targetIndex: number; to: { model: string; provider: string } }) => void | Fires when a stream fails terminally on the active provider+model target
and the loop advances to the next entry of the run's fallback chain
( Fallback is run-sticky: after this fires, the rest of the run stays on
Observational — handlers cannot cancel the fallback. To disable fallback, configure a single provider+model target. Attribution note: |
| oauth:refresh | (ctx: OAuthRefreshHookContext) => void | Fires when the provider refreshes its OAuth credentials during a run. |
| output | (ctx: { output: Record<string, unknown>; schema: Record<string, unknown> }) => void | Fires with the structured output the model produced and the schema it was validated against. |
| pairing:repair | (ctx: PairingRepair & { turnId?: string }) => void | Fires once per repair performed by the pre-send pairing pass
( Observational — handlers can mirror to Sentry/PostHog/etc. for
postmortems on new corruption sources but cannot suppress the repair.
The companion
|
| provider:diagnostic | (ctx: ProviderDiagnostic & { runId: string; turnId: string }) => void | Fires when the provider reports a diagnostic event for a turn. |
| provider:input-transformations | (ctx: InputTransformationsHookContext) => void | Fires when the provider dropped replayed thinking blocks server-side
before sampling (Anthropic preserved-thinking binding, reported via
|
| repeat-guard:exceeded | (ctx: { action: 'steer' | 'block' | 'abort'; count: number; threshold: number; tool: string; turnId: string }) => void | Fires when the consecutive-identical |
| replay:done | (ctx: Record<string, never>) => void | Durable-execution replay finished — the invocation transitioned from journal replay to live execution (the first non-journaled provider stream or tool body just started). The framework core never fires this: it is emitted by durable-runtime
adapters (see |
| resume:mismatch | (ctx: { reason: string; resumeTurn: number; runId: string; sessionId: string; storedTurns: readonly number[] }) => void |
|
| run:beforeEnd | (ctx: { followUp: (message: string) => void; runId: string; session?: Session; turn: number }) => void | Promise<void> | Fires once per would-be final turn: the model answered with no tool call
and nothing is queued. A handler that calls |
| session:end | (ctx: SessionHookContext & { runId: string; session?: Session; status: SessionEndStatus; turnRange: [number, number] }) => void | Fires when a run ends, with its status and the range of turn indexes it produced. |
| session:meta | (ctx: SessionHookContext & { key: string; value: unknown }) => void | Fires when a session metadata key is set. |
| session:save | (ctx: SessionHookContext) => void | Fires when the session is saved. |
| session:start | (ctx: SessionHookContext & { prompt: string; runId: string }) => void | Fires when a run starts on a session, with the run id and prompt label. |
| session:turns | (ctx: SessionHookContext & { count: number; turns: SessionTurn[] }) => void | Fires when new turns are flushed to the session, with the turns and the resulting turn count. |
| skills:activate | (ctx: { skill: SkillConfig; via: ActivationVia }) => void | Fires when a skill is activated. |
| skills:catalog | (ctx: { catalog: string; skills: SkillConfig[] }) => void | Fires with the rendered skills catalog text and the skills it lists. |
| skills:deactivate | (ctx: { reason: DeactivationReason; skill: SkillConfig }) => void | Fires when a skill is deactivated. |
| skills:resolve | (ctx: { skills: SkillConfig[] }) => void | Fires once skills have been resolved, with the resolved skill list. |
| spawn:before | (ctx: SpawnHookContext) => void | Fires when a spawned subagent is about to start. |
| spawn:complete | (ctx: ChildRunStats) => void | Fires when a spawned subagent finishes, with its run stats. |
| spawn:error | (ctx: SpawnHookContext & { error: Error }) => void | Fires when a spawned subagent fails. |
| spawn:queued | (ctx: SpawnQueuedContext) => void | A top-level |
| steer:inject | (ctx: { content: readonly PromptPart[]; id?: string; text: string }) => void | Fires when the loop adds a queued message to the conversation as a user
turn: an |
| stream:end | (ctx: StreamHookContext & { text: string }) => void | Fires when the model's text stream for the turn ends, with the full text. |
| stream:error | (ctx: StreamHookContext & { err: unknown; requestId?: string; statusCode?: number }) => void | Fires when the provider's stream rejects BEFORE
Best-effort convenience fields extracted from common SDK shapes
(Anthropic, OpenAI, OpenRouter, Cerebras) when present on
Both absent when the SDK doesn't expose them (network/early rejects,
stream-internal protocol errors). Walk Does NOT fire on user-initiated aborts; those land on |
| stream:progress | (ctx: StreamProgressHookContext) => void | Fires (throttled, ~4/s) while the parent turn streams and once more,
unthrottled, right before |
| stream:retry | (ctx: StreamHookContext & { attempt: number; delayMs: number; discardedText?: true; discardedThinking?: true; err: unknown; maxAttempts: number; nextAttempt: number; requestId?: string; statusCode?: number }) => void | Fires when the loop catches a Use this hook to surface "Anthropic overloaded — retrying in Ns" in the TUI, count retries in observability, or short-circuit further retry logic from an outer harness.
Observational — handlers cannot cancel the retry. To disable
provider-classified retries, set |
| stream:server_tool_result | (ctx: StreamHookContext & { content: unknown; toolName: string; toolUseId: string }) => void | Pairs with |
| stream:server_tool_use | (ctx: StreamHookContext & { id: string; input: Record<string, unknown>; name: string }) => void | Fires mid-stream when a provider that executes a tool server-side
(currently: Anthropic's Shape is intentionally generic so future server tools reuse the same
hook. Observational — handlers cannot block or substitute, since the call has already executed server-side by the time we see it. |
| stream:start | (ctx: StreamHookContext & { startedAt: number }) => void | Fires immediately before the loop calls
|
| stream:text | (ctx: StreamHookContext & { delta: string; text: string }) => void | Fires for each text delta of the model response. |
| stream:thinking | (ctx: StreamHookContext & { delta: string; thinking: string }) => void | Fires for each thinking delta. |
| system:before | (ctx: { system: string }) => void | Fires once per run, before the first turn, only when |
| system:transform | (ctx: { messages: readonly SessionMessage[]; session?: Session; system: string; turn: number; turnId: string }) => void | Fires per request, after Anything written here must be byte-stable across turnsThe system prompt sits ABOVE the whole conversation in the provider's cache
prefix — Anthropic hashes tools → system → messages, cumulatively — so a
one-byte change to The classic way to get this wrong is a live tally: interpolating something
like Constant guidance strings, feature flags and run-stable facts are fine here.
Live counters, timestamps, elapsed times, per-turn indices and anything
derived from a growing tally are NOT — not even in the dynamic half below
Genuinely per-turn state belongs in the transcript, persisted as immutable
history so it cannot shift under the prefix on a later turn. A wire-only
append to the newest message does NOT work either: that message re-renders
without the append next turn and the prefix diverges there instead. Install
|
| tool-batch:resumed | (ctx: { done: readonly string[]; resuming: readonly string[]; runId: string; turn: number; turnId: string }) => void | Fires when a durable run resumes a tool batch a previous process left
unfinished, before any call of it runs again. Checkpoint runtimes only: in replay mode the loop cannot tell which calls are done before it issues them, and the hook would fire on every replay. |
| tool-budget:exceeded | (ctx: { count: number; max: number; mode: 'steer' | 'block'; tool: string; turnId: string }) => void | Fires when a per-tool budget configured via
|
| tool-results:after | (ctx: { message: SessionTurn; results: readonly ToolResult[]; turn: number; turnId: string }) => void | Fires after a tool-results user turn is pushed onto the conversation,
before any byte-budget enforcement or follow-up steering. Symmetric
with Why it exists: persistence layers must write tool_use/tool_result turn pairs together — if only the assistant turn (carrying tool_use blocks) is durable, a process death between turns leaves the DB with an orphan tool_use that Anthropic rejects on resume. Subscribe here to persist the tool-results turn before the loop continues. |
| tool-state:commit-failed | (ctx: { callId: string; error: Error; handled?: boolean; ops: readonly ToolStateOp[]; runId: string; toolName: string }) => void | Fires when a settled tool call's durable state ( The call itself is unaffected: the tool body already ran and its result is
already on its way to the model, so failing the run over a bookkeeping
write would be worse than losing it. But losing it silently is worse
still — a dropped one-shot flag re-arms the exact bug durable tool state
exists to prevent — so this is the seam for alerting on it. Without a handler (or with one that leaves |
| tool:after | (ctx: ToolHookContext & { coercions?: readonly string[]; isError: boolean; outcome?: ToolOutcome; outputBytes: number; result: string | ToolResultContent[]; runToolCounts: Readonly<Record<string, number>> }) => void | Fires after a tool body produced a result (the execute path) or a
Does NOT fire on gate-block / unknown-tool / validation-reject
paths: the loop synthesizes their This narrow contract is deliberate: tracing spans opened in
|
| tool:before | (ctx: ToolHookContext & { coercions?: readonly string[]; priorContent?: string; runToolCounts: Readonly<Record<string, number>>; unknown?: boolean }) => void | Fires before a tool body runs. Mutate |
| tool:cancelled | (ctx: ToolHookContext & { reason: string; runToolCounts: Readonly<Record<string, number>> }) => void | Fires when a single in-flight tool call is cancelled mid-flight via
Semantics:
|
| tool:dispatched | (ctx: ToolHookContext & { outcome: 'execute' | 'gate-substitute' | 'gate-block' | 'unknown' | 'invalid-input'; reason?: string; runToolCounts: Readonly<Record<string, number>> }) => void | Symmetric notification fired ONCE per call the model dispatched,
regardless of the path the call ultimately took through the loop.
Every call that produces a tool_result closes with exactly one
terminal event, so pair
Distinct from Strict ordering:
|
| tool:error | (ctx: ToolHookContext & { error: Error; result?: string | ToolResultContent[] }) => void | Fires when a tool throws during execution. Mutate The post-hook value flows through |
| tool:gate | (ctx: ToolHookContext & ToolGateResumeFields & { block: boolean; metadata: ToolResultMetadata; reason: string; result?: string | ToolResultContent[]; runToolCounts: Readonly<Record<string, number>> }) => void | Fires before validation,
If multiple handlers along the chain set both
Concurrent fleets see the same pre-batch snapshot on every member's
Gate a tool call before it runs. Handlers share ONE mutable Precedence across the resolved state:
Set Resumed calls ( |
| tool:progress | (ctx: ToolHookContext & { progress: ToolProgress }) => void | Live output of a running tool ( |
| tool:transform | (ctx: ToolHookContext & { coercions?: readonly string[]; isError: boolean; metadata: ToolResultMetadata; outputBytes: number; result: string | ToolResultContent[] }) => void | Fires after a tool produces output and before the result goes back to the model. Mutate |
| tool:unknown | (ctx: ToolHookContext & { result?: string | ToolResultContent[]; suppressError: boolean }) => void | Fires before the generic "Unknown tool" error when the model invokes a tool
that isn't registered (hallucinated names, dropped MCP servers, dangling
aliases). Mutate Fires for any unknown tool name — including hallucinated MCP-style names
( |
| tracing:redact | (ctx: { kind: 'system' | 'prompt' | 'tool-input' | 'tool-result' | 'mcp-tool-input' | 'mcp-tool-result' | 'stream-text' | 'stream-thinking' | 'decision-answers'; meta?: Readonly<Record<string, unknown>>; redacted: string; value: string }) => void | Privacy seam invoked by observability adapters (tracing, metrics, logging, persistence sinks) before attaching potentially-sensitive payloads as span attributes / log lines / external storage. Handlers mutate
Synchronous-only by design: observability stays on the hot path. Heavy
scrubbing (regex over MB of output) belongs in |
| turn:after | (ctx: { aborted?: boolean; cumulativeUsage: Readonly<{ cacheCreation: number; cacheRead: number; cost?: number; input: number; output: number; turns: number }>; message: SessionTurn; toolCounts: { run: Readonly<Record<string, number>>; turn: Readonly<Record<string, number>> }; turn: number; turnId: string; usage: TurnUsage }) => void | Fires after each assistant turn (before its tool-result follow-up
dispatches; the loop iterates back to a fresh
Both fields are frozen snapshots; mutate-safe.
Covers the streaming-abort path (cancel landed while the turn was
streaming) and any abort that landed before this fired. NOTE: a
|
| turn:before | (ctx: { options: StreamOptions; turn: number; turnId: string }) => void | Fires immediately before each turn's |
| usage | (ctx: { totalIn: number; totalOut: number; turn: number; turnId: string; usage: TurnUsage }) => void | Fires after each turn with that turn's token usage and the run's cumulative input and output tokens. |
| validation:coerce | (ctx: ToolHookContext & { coercions: readonly string[]; schema: Record<string, unknown> }) => void | Fires when
|
| validation:reject | (ctx: ToolHookContext & { reason: string; schema: Record<string, unknown> }) => void | Fires when |
AgentOptionsInterface#
Options for createAgent. Only provider is required.
interface AgentOptionsProperties
| Name | Type | Description |
|---|---|---|
| approveToolCall? | ToolCallApprover | Resolves a |
| artifacts? | boolean | Session artifacts master switch (docs/ARTIFACTS.md). |
| behavior? | AgentBehavior | Agent-level behavior defaults (overridden by run-level behavior) |
| behaviorTransforms? | readonly ExtensionOrderedContribution<ExtensionBehaviorTransform>[] | Behavior transforms contributed by extensions, applied before each run. |
| clock? | AgentClock | Time + UUID source for all journaled metadata (turn ids, Override at the run level via Ignored on a replay-mode durable runtime ( |
| contextBreakdownSections? | readonly ContextBreakdownContributor[] | Extension-owned context panel contributors. Hosts that already spread
|
| decisions? | false | AgentDecisionsOptions | Decisions configuration for
|
| durable? | DurableRuntime | DurableOptions | Durable runtime every model call, tool call and loop decision goes
through, so a run can resume after a process death ( |
| eager? | boolean | Pre-connect MCP servers in the background as soon as Useful when MCP latency is the dominant cost of a cold start: callers that
construct the agent early (e.g. at process init) can hide the bootstrap
behind other setup work. If bootstrap fails while a Also starts skills resolution when skills are configured. No-op when there
are no MCP servers and no skills. Default: |
| execution? | ExecutionContext | Execution context: where tools run. Defaults to in-process. |
| extensionRuntime? | AgentExtensionRuntime | The extension runtime's registries ( |
| extensions? | ExtensionRegistry | Pre-built Build it first (async) via
Config is NOT folded: |
| extensionScope? | ExtensionCallScope | For a subagent: the session and spawning run its interceptor inputs
name ( |
| handle? | ExecutionHandle | (() => Promise<ExecutionHandle>) | Pre-bound execution handle. When set, the agent uses it verbatim and does
NOT call The durable-execution use case: a host that needs the spawn to be a
recorded, replay-stable side effect drives For host-observation without driving spawn yourself, see the
A function form resolves the host-owned handle lazily, at the moment the
agent would otherwise spawn (first run / |
| hooks? | Partial<{ 'agent:abort': ((ctx: { reason?: string; runId?: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { reason?: string; runId?: string } & DecideHookContext, ...rest: []) => void)[]; 'agent:done': ((ctx: AgentStats & DecideHookContext, ...rest: []) => void) | ((ctx: AgentStats & DecideHookContext, ...rest: []) => void)[]; 'agent:start': ((ctx: { agentName?: string; depth: number; parentRunId?: string; providerName?: string; runId: string; startedAt: number; tracingContext?: Readonly<Record<string, string>> } & DecideHookContext, ...rest: []) => void) | ((ctx: { agentName?: string; depth: number; parentRunId?: string; providerName?: string; runId: string; startedAt: number; tracingContext?: Readonly<Record<..., ...>> } & DecideHookContext, ...rest: []) => void)[]; 'artifact:recorded': ((ctx: { artifact: SessionArtifact; callId: string; name: string; runId: string; turnId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { artifact: SessionArtifact; callId: string; name: string; runId: string; turnId: string } & DecideHookContext, ...rest: []) => void)[]; 'background:adopt': ((ctx: { command: string; cwd: string; outputPath: string; pid: number; startedAt: number; taskId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { command: string; cwd: string; outputPath: string; pid: number; startedAt: number; taskId: string } & DecideHookContext, ...rest: []) => void)[]; 'background:exit': ((ctx: { command: string; durationMs: number; exitCode: number; outputPath: string; signal?: Signals; status: 'exited' | 'killed'; taskId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { command: string; durationMs: number; exitCode: number; outputPath: string; signal?: Signals; status: 'exited' | 'killed'; taskId: string } & DecideHookContext, ...rest: []) => void)[]; 'background:reassign': ((ctx: { childId?: string; command: string; fromHandleId: string; outputPath: string; pid: number; startedAt: number; taskId: string; toHandleId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { childId?: string; command: string; fromHandleId: string; outputPath: string; pid: number; startedAt: number; taskId: string; toHandleId: string } & DecideHookContext, ...rest: []) => void)[]; 'background:reconcile': ((ctx: { exits: TaskExitInfo[] } & DecideHookContext, ...rest: []) => void) | ((ctx: { exits: TaskExitInfo[] } & DecideHookContext, ...rest: []) => void)[]; 'background:stall': ((ctx: { bytesWritten: number; command: string; outputPath: string; stalledForMs: number; taskId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { bytesWritten: number; command: string; outputPath: string; stalledForMs: number; taskId: string } & DecideHookContext, ...rest: []) => void)[]; 'background:start': ((ctx: { command: string; cwd: string; outputPath: string; pid: number; startedAt: number; taskId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { command: string; cwd: string; outputPath: string; pid: number; startedAt: number; taskId: string } & DecideHookContext, ...rest: []) => void)[]; 'budget:exceeded': ((ctx: { budget: number; bytes: number; turn: number; turnId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { budget: number; bytes: number; turn: number; turnId: string } & DecideHookContext, ...rest: []) => void)[]; 'cache:break': ((ctx: CacheBreakInfo & DecideHookContext, ...rest: []) => void | Promise<void>) | ((ctx: CacheBreakInfo & DecideHookContext, ...rest: []) => void | Promise<void>)[]; 'cache:drift': ((ctx: CacheDriftInfo & DecideHookContext, ...rest: []) => void | Promise<void>) | ((ctx: CacheDriftInfo & DecideHookContext, ...rest: []) => void | Promise<void>)[]; 'child:artifact:recorded': ((ctx: { artifact: SessionArtifact; callId: string; childId: string; depth: number; name: string; runId: string; turnId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { artifact: SessionArtifact; callId: string; childId: string; depth: number; name: string; runId: string; turnId: string } & DecideHookContext, ...rest: []) => void)[]; 'child:background:exit': ((ctx: { childId: string; command: string; depth: number; durationMs: number; exitCode: number; outputPath: string; signal?: Signals; status: 'exited' | 'killed'; taskId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { childId: string; command: string; depth: number; durationMs: number; exitCode: number; outputPath: string; signal?: Signals; status: 'exited' | 'killed'; taskId: string } & DecideHookContext, ...rest: []) => void)[]; 'child:background:reassign': ((ctx: { childId: string; command: string; depth: number; fromHandleId: string; outputPath: string; pid: number; startedAt: number; taskId: string; toHandleId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { childId: string; command: string; depth: number; fromHandleId: string; outputPath: string; pid: number; startedAt: number; taskId: string; toHandleId: string } & DecideHookContext, ...rest: []) => void)[]; 'child:background:start': ((ctx: { childId: string; command: string; cwd: string; depth: number; outputPath: string; pid: number; startedAt: number; taskId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { childId: string; command: string; cwd: string; depth: number; outputPath: string; pid: number; startedAt: number; taskId: string } & DecideHookContext, ...rest: []) => void)[]; 'child:mcp:tool:gate': ((ctx: McpToolHookContext & { block: boolean; childId: string; depth: number; reason: string; result?: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void) | ((ctx: McpToolHookContext & { block: boolean; childId: string; depth: number; reason: string; result?: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void)[]; 'child:repeat-guard:exceeded': ((ctx: { action: 'steer' | 'block' | 'abort'; childId: string; count: number; depth: number; threshold: number; tool: string; turnId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { action: 'steer' | 'block' | 'abort'; childId: string; count: number; depth: number; threshold: number; tool: string; turnId: string } & DecideHookContext, ...rest: []) => void)[]; 'child:spawn:before': ((ctx: Omit<SpawnHookContext, 'tracingContext'> & ChildSpawnIdentity & DecideHookContext, ...rest: []) => void) | ((ctx: Omit<SpawnHookContext, 'tracingContext'> & ChildSpawnIdentity & DecideHookContext, ...rest: []) => void)[]; 'child:spawn:complete': ((ctx: ChildRunStats & ChildSpawnIdentity & DecideHookContext, ...rest: []) => void) | ((ctx: ChildRunStats & ChildSpawnIdentity & DecideHookContext, ...rest: []) => void)[]; 'child:spawn:error': ((ctx: Omit<SpawnHookContext, 'tracingContext'> & ChildSpawnIdentity & { error: Error } & DecideHookContext, ...rest: []) => void) | ((ctx: Omit<SpawnHookContext, 'tracingContext'> & ChildSpawnIdentity & { error: Error } & DecideHookContext, ...rest: []) => void)[]; 'child:stream:end': ((ctx: StreamHookContext & { childId: string; depth: number; text: string } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { childId: string; depth: number; text: string } & DecideHookContext, ...rest: []) => void)[]; 'child:stream:error': ((ctx: StreamHookContext & { childId: string; depth: number; err: unknown } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { childId: string; depth: number; err: unknown } & DecideHookContext, ...rest: []) => void)[]; 'child:stream:retry': ((ctx: StreamHookContext & { attempt: number; childId: string; delayMs: number; depth: number; discardedText?: true; discardedThinking?: true; err: unknown; maxAttempts: number; nextAttempt: number; requestId?: string; statusCode?: number } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { attempt: number; childId: string; delayMs: number; depth: number; discardedText?: true; discardedThinking?: true; err: unknown; maxAttempts: number; nextAttempt: number; requestId?: string; statusCode?: number } & DecideHookContext, ...rest: []) => void)[]; 'child:stream:server_tool_result': ((ctx: StreamHookContext & { childId: string; content: unknown; depth: number; toolName: string; toolUseId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { childId: string; content: unknown; depth: number; toolName: string; toolUseId: string } & DecideHookContext, ...rest: []) => void)[]; 'child:stream:server_tool_use': ((ctx: StreamHookContext & { childId: string; depth: number; id: string; input: Record<string, unknown>; name: string } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { childId: string; depth: number; id: string; input: Record<string, unknown>; name: string } & DecideHookContext, ...rest: []) => void)[]; 'child:stream:text': ((ctx: StreamHookContext & { childId: string; delta: string; depth: number; text: string } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { childId: string; delta: string; depth: number; text: string } & DecideHookContext, ...rest: []) => void)[]; 'child:stream:thinking': ((ctx: StreamHookContext & { childId: string; delta: string; depth: number; thinking: string } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { childId: string; delta: string; depth: number; thinking: string } & DecideHookContext, ...rest: []) => void)[]; 'child:tool:after': ((ctx: ToolHookContext & { childId: string; coercions?: readonly string[]; depth: number; isError?: boolean; outcome?: ToolOutcome; outputBytes: number; result: string | ToolResultContent[]; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { childId: string; coercions?: readonly string[]; depth: number; isError?: boolean; outcome?: ToolOutcome; outputBytes: number; result: string | ToolResultContent[]; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void)[]; 'child:tool:before': ((ctx: ToolHookContext & { childId: string; coercions?: readonly string[]; depth: number; priorContent?: string; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { childId: string; coercions?: readonly string[]; depth: number; priorContent?: string; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void)[]; 'child:tool:cancelled': ((ctx: ToolHookContext & { childId: string; depth: number; reason: string; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { childId: string; depth: number; reason: string; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void)[]; 'child:tool:dispatched': ((ctx: ToolHookContext & { childId: string; depth: number; outcome: 'execute' | 'gate-substitute' | 'gate-block' | 'unknown' | 'invalid-input'; reason?: string; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { childId: string; depth: number; outcome: 'execute' | 'gate-substitute' | 'gate-block' | 'unknown' | 'invalid-input'; reason?: string; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void)[]; 'child:tool:error': ((ctx: ToolHookContext & { childId: string; depth: number; error: Error } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { childId: string; depth: number; error: Error } & DecideHookContext, ...rest: []) => void)[]; 'child:tool:gate': ((ctx: ToolHookContext & ToolGateResumeFields & { block: boolean; childId: string; depth: number; metadata: ToolResultMetadata; reason: string; result?: string | ToolResultContent[]; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & ToolGateResumeFields & { block: boolean; childId: string; depth: number; metadata: ToolResultMetadata; reason: string; result?: string | ToolResultContent[]; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void)[]; 'child:tool:progress': ((ctx: ToolHookContext & { childId: string; depth: number; progress: ToolProgress } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { childId: string; depth: number; progress: ToolProgress } & DecideHookContext, ...rest: []) => void)[]; 'child:tool:transform': ((ctx: ToolHookContext & { childId: string; coercions?: readonly string[]; depth: number; isError: boolean; metadata: ToolResultMetadata; outputBytes: number; result: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { childId: string; coercions?: readonly string[]; depth: number; isError: boolean; metadata: ToolResultMetadata; outputBytes: number; result: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void)[]; 'child:turn:after': ((ctx: { aborted?: boolean; childId: string; depth: number; message: SessionTurn; toolCounts: { run: Readonly<Record<string, number>>; turn: Readonly<Record<string, number>> }; turn: number; turnId: string; usage: TurnUsage } & DecideHookContext, ...rest: []) => void) | ((ctx: { aborted?: boolean; childId: string; depth: number; message: SessionTurn; toolCounts: { run: Readonly<Record<..., ...>>; turn: Readonly<Record<..., ...>> }; turn: number; turnId: string; usage: TurnUsage } & DecideHookContext, ...rest: []) => void)[]; 'child:validation:reject': ((ctx: ToolHookContext & { childId: string; depth: number; reason: string; schema: Record<string, unknown> } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { childId: string; depth: number; reason: string; schema: Record<string, unknown> } & DecideHookContext, ...rest: []) => void)[]; 'compact:end': ((ctx: (CompactHookContext & { droppedCount: number; durationMs: number; effectiveTokens: number; replacedCount: number; restoredFiles: number; restoredSkills: number; status: 'success'; summaryModel: string } & DecideHookContext) | (CompactHookContext & { durationMs: number; error: unknown; status: 'error' } & DecideHookContext), ...rest: []) => void) | ((ctx: (CompactHookContext & { droppedCount: number; durationMs: number; effectiveTokens: number; replacedCount: number; restoredFiles: number; restoredSkills: number; status: 'success'; summaryModel: string } & DecideHookContext) | (CompactHookContext & { durationMs: number; error: unknown; status: 'error' } & DecideHookContext), ...rest: []) => void)[]; 'compact:progress': ((ctx: CompactProgressHookContext & DecideHookContext, ...rest: []) => void) | ((ctx: CompactProgressHookContext & DecideHookContext, ...rest: []) => void)[]; 'compact:restore': ((ctx: { add: (call: { input: Record<string, unknown>; name: string; output: string }) => void; session?: Session } & DecideHookContext, ...rest: []) => void | Promise<void>) | ((ctx: { add: (call: { input: Record<..., ...>; name: string; output: string }) => void; session?: Session } & DecideHookContext, ...rest: []) => void | Promise<void>)[]; 'compact:start': ((ctx: CompactStartHookContext & DecideHookContext, ...rest: []) => void) | ((ctx: CompactStartHookContext & DecideHookContext, ...rest: []) => void)[]; 'content-ref:resolve': ((ctx: { block: ResolveContentRefBlock; childId?: string; data?: string; depth?: number } & DecideHookContext, ...rest: []) => void) | ((ctx: { block: ResolveContentRefBlock; childId?: string; data?: string; depth?: number } & DecideHookContext, ...rest: []) => void)[]; 'context:overflow': ((ctx: StreamHookContext & { attempt: number; err: unknown } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { attempt: number; err: unknown } & DecideHookContext, ...rest: []) => void)[]; 'context:transform': ((ctx: { messages: SessionMessage[] } & DecideHookContext, ...rest: []) => void) | ((ctx: { messages: SessionMessage[] } & DecideHookContext, ...rest: []) => void)[]; 'decision:end': ((ctx: DecisionEndHookContext) => void) | ((ctx: DecisionEndHookContext) => void)[]; 'decision:start': ((ctx: DecisionStartHookContext) => void) | ((ctx: DecisionStartHookContext) => void)[]; 'execution:ready': ((ctx: { handle: ExecutionHandle; spawned: boolean } & DecideHookContext, ...rest: []) => void) | ((ctx: { handle: ExecutionHandle; spawned: boolean } & DecideHookContext, ...rest: []) => void)[]; 'history:repair': ((ctx: HistoryRepairHookContext & DecideHookContext, ...rest: []) => void) | ((ctx: HistoryRepairHookContext & DecideHookContext, ...rest: []) => void)[]; 'mcp:auth:error': ((ctx: { error: Error; name: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { error: Error; name: string } & DecideHookContext, ...rest: []) => void)[]; 'mcp:auth:required': ((ctx: { name: string; reason: 'no-tokens' | 'auto-promoted'; transport: 'sse' | 'stdio' | 'streamable-http' } & DecideHookContext, ...rest: []) => void) | ((ctx: { name: string; reason: 'no-tokens' | 'auto-promoted'; transport: 'sse' | 'stdio' | 'streamable-http' } & DecideHookContext, ...rest: []) => void)[]; 'mcp:auth:success': ((ctx: { name: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { name: string } & DecideHookContext, ...rest: []) => void)[]; 'mcp:auth:url': ((ctx: { name: string; url: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { name: string; url: string } & DecideHookContext, ...rest: []) => void)[]; 'mcp:bootstrap:end': ((ctx: ({ durationMs: number; name: string; transport: string } & { cached?: boolean; lazy?: boolean; ok: true; toolCount: number; warnings?: string[] } & DecideHookContext) | ({ durationMs: number; name: string; transport: string } & { error: Error; ok: false } & DecideHookContext), ...rest: []) => void) | ((ctx: ({ durationMs: number; name: string; transport: string } & { cached?: boolean; lazy?: boolean; ok: true; toolCount: number; warnings?: ...[] } & DecideHookContext) | ({ durationMs: number; name: string; transport: string } & { error: Error; ok: false } & DecideHookContext), ...rest: []) => void)[]; 'mcp:bootstrap:settled': ((ctx: { results: readonly ({ durationMs: number; name: string; transport: string } & ({ lazy?: ...; ok: ...; toolCount: ... } | { error?: ...; ok: ...; skipped?: ... }))[] } & DecideHookContext, ...rest: []) => void) | ((ctx: { results: readonly ({ durationMs: ...; name: ...; transport: ... } & (... | ...))[] } & DecideHookContext, ...rest: []) => void)[]; 'mcp:bootstrap:start': ((ctx: { name: string; transport: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { name: string; transport: string } & DecideHookContext, ...rest: []) => void)[]; 'mcp:close': ((ctx: { name: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { name: string } & DecideHookContext, ...rest: []) => void)[]; 'mcp:connect': ((ctx: { lazy?: boolean; name: string; tools: string[]; transport: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { lazy?: boolean; name: string; tools: string[]; transport: string } & DecideHookContext, ...rest: []) => void)[]; 'mcp:error': ((ctx: { error: Error; name: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { error: Error; name: string } & DecideHookContext, ...rest: []) => void)[]; 'mcp:tool:after': ((ctx: McpToolHookContext & { outputBytes: number; result: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void) | ((ctx: McpToolHookContext & { outputBytes: number; result: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void)[]; 'mcp:tool:before': ((ctx: McpToolHookContext & DecideHookContext, ...rest: []) => void) | ((ctx: McpToolHookContext & DecideHookContext, ...rest: []) => void)[]; 'mcp:tool:error': ((ctx: McpToolHookContext & { error: Error } & DecideHookContext, ...rest: []) => void) | ((ctx: McpToolHookContext & { error: Error } & DecideHookContext, ...rest: []) => void)[]; 'mcp:tool:gate': ((ctx: McpToolHookContext & { block: boolean; reason: string; result?: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void) | ((ctx: McpToolHookContext & { block: boolean; reason: string; result?: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void)[]; 'mcp:tool:transform': ((ctx: McpToolHookContext & { outputBytes: number; result: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void) | ((ctx: McpToolHookContext & { outputBytes: number; result: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void)[]; 'mcp:tools:filter': ((ctx: { server: string; tools: { description?: string | null; inputSchema?: unknown; name: string }[]; transport: 'sse' | 'stdio' | 'streamable-http' } & DecideHookContext, ...rest: []) => void) | ((ctx: { server: string; tools: { description?: ... | ... | ...; inputSchema?: unknown; name: string }[]; transport: 'sse' | 'stdio' | 'streamable-http' } & DecideHookContext, ...rest: []) => void)[]; 'mcp:tools:list': ((ctx: { name: string; tools: readonly McpToolSchema[]; transport: 'sse' | 'stdio' | 'streamable-http' } & DecideHookContext, ...rest: []) => void) | ((ctx: { name: string; tools: readonly McpToolSchema[]; transport: 'sse' | 'stdio' | 'streamable-http' } & DecideHookContext, ...rest: []) => void)[]; 'mcp:warn': ((ctx: { message: string; name: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { message: string; name: string } & DecideHookContext, ...rest: []) => void)[]; 'model:fallback': ((ctx: StreamHookContext & { discardedThinking?: true; err: Error; from: { model: string; provider: string }; targetIndex: number; to: { model: string; provider: string } } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { discardedThinking?: true; err: Error; from: { model: string; provider: string }; targetIndex: number; to: { model: string; provider: string } } & DecideHookContext, ...rest: []) => void)[]; 'oauth:refresh': ((ctx: OAuthRefreshHookContext & DecideHookContext, ...rest: []) => void) | ((ctx: OAuthRefreshHookContext & DecideHookContext, ...rest: []) => void)[]; output: ((ctx: { output: Record<string, unknown>; schema: Record<string, unknown> } & DecideHookContext, ...rest: []) => void) | ((ctx: { output: Record<string, unknown>; schema: Record<string, unknown> } & DecideHookContext, ...rest: []) => void)[]; 'pairing:repair': ((ctx: PairingRepair & { turnId?: string } & DecideHookContext, ...rest: []) => void) | ((ctx: PairingRepair & { turnId?: string } & DecideHookContext, ...rest: []) => void)[]; 'provider:diagnostic': ((ctx: ProviderDiagnostic & { runId: string; turnId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: ProviderDiagnostic & { runId: string; turnId: string } & DecideHookContext, ...rest: []) => void)[]; 'provider:input-transformations': ((ctx: InputTransformationsHookContext & DecideHookContext, ...rest: []) => void) | ((ctx: InputTransformationsHookContext & DecideHookContext, ...rest: []) => void)[]; 'repeat-guard:exceeded': ((ctx: { action: 'steer' | 'block' | 'abort'; count: number; threshold: number; tool: string; turnId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { action: 'steer' | 'block' | 'abort'; count: number; threshold: number; tool: string; turnId: string } & DecideHookContext, ...rest: []) => void)[]; 'replay:done': ((ctx: Record<string, never> & DecideHookContext, ...rest: []) => void) | ((ctx: Record<string, never> & DecideHookContext, ...rest: []) => void)[]; 'resume:mismatch': ((ctx: { reason: string; resumeTurn: number; runId: string; sessionId: string; storedTurns: readonly number[] } & DecideHookContext, ...rest: []) => void) | ((ctx: { reason: string; resumeTurn: number; runId: string; sessionId: string; storedTurns: readonly number[] } & DecideHookContext, ...rest: []) => void)[]; 'run:beforeEnd': ((ctx: { followUp: (message: string) => void; runId: string; session?: Session; turn: number } & DecideHookContext, ...rest: []) => void | Promise<void>) | ((ctx: { followUp: (message: string) => void; runId: string; session?: Session; turn: number } & DecideHookContext, ...rest: []) => void | Promise<void>)[]; 'session:end': ((ctx: SessionHookContext & { runId: string; session?: Session; status: SessionEndStatus; turnRange: [number, number] } & DecideHookContext, ...rest: []) => void) | ((ctx: SessionHookContext & { runId: string; session?: Session; status: SessionEndStatus; turnRange: [number, number] } & DecideHookContext, ...rest: []) => void)[]; 'session:meta': ((ctx: SessionHookContext & { key: string; value: unknown } & DecideHookContext, ...rest: []) => void) | ((ctx: SessionHookContext & { key: string; value: unknown } & DecideHookContext, ...rest: []) => void)[]; 'session:save': ((ctx: SessionHookContext & DecideHookContext, ...rest: []) => void) | ((ctx: SessionHookContext & DecideHookContext, ...rest: []) => void)[]; 'session:start': ((ctx: SessionHookContext & { prompt: string; runId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: SessionHookContext & { prompt: string; runId: string } & DecideHookContext, ...rest: []) => void)[]; 'session:turns': ((ctx: SessionHookContext & { count: number; turns: SessionTurn[] } & DecideHookContext, ...rest: []) => void) | ((ctx: SessionHookContext & { count: number; turns: SessionTurn[] } & DecideHookContext, ...rest: []) => void)[]; 'skills:activate': ((ctx: { skill: SkillConfig; via: ActivationVia } & DecideHookContext, ...rest: []) => void) | ((ctx: { skill: SkillConfig; via: ActivationVia } & DecideHookContext, ...rest: []) => void)[]; 'skills:catalog': ((ctx: { catalog: string; skills: SkillConfig[] } & DecideHookContext, ...rest: []) => void) | ((ctx: { catalog: string; skills: SkillConfig[] } & DecideHookContext, ...rest: []) => void)[]; 'skills:deactivate': ((ctx: { reason: DeactivationReason; skill: SkillConfig } & DecideHookContext, ...rest: []) => void) | ((ctx: { reason: DeactivationReason; skill: SkillConfig } & DecideHookContext, ...rest: []) => void)[]; 'skills:resolve': ((ctx: { skills: SkillConfig[] } & DecideHookContext, ...rest: []) => void) | ((ctx: { skills: SkillConfig[] } & DecideHookContext, ...rest: []) => void)[]; 'spawn:before': ((ctx: SpawnHookContext & DecideHookContext, ...rest: []) => void) | ((ctx: SpawnHookContext & DecideHookContext, ...rest: []) => void)[]; 'spawn:complete': ((ctx: ChildRunStats & DecideHookContext, ...rest: []) => void) | ((ctx: ChildRunStats & DecideHookContext, ...rest: []) => void)[]; 'spawn:error': ((ctx: SpawnHookContext & { error: Error } & DecideHookContext, ...rest: []) => void) | ((ctx: SpawnHookContext & { error: Error } & DecideHookContext, ...rest: []) => void)[]; 'spawn:queued': ((ctx: SpawnQueuedContext & DecideHookContext, ...rest: []) => void) | ((ctx: SpawnQueuedContext & DecideHookContext, ...rest: []) => void)[]; 'steer:inject': ((ctx: { content: readonly PromptPart[]; id?: string; text: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { content: readonly PromptPart[]; id?: string; text: string } & DecideHookContext, ...rest: []) => void)[]; 'stream:end': ((ctx: StreamHookContext & { text: string } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { text: string } & DecideHookContext, ...rest: []) => void)[]; 'stream:error': ((ctx: StreamHookContext & { err: unknown; requestId?: string; statusCode?: number } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { err: unknown; requestId?: string; statusCode?: number } & DecideHookContext, ...rest: []) => void)[]; 'stream:progress': ((ctx: StreamProgressHookContext & DecideHookContext, ...rest: []) => void) | ((ctx: StreamProgressHookContext & DecideHookContext, ...rest: []) => void)[]; 'stream:retry': ((ctx: StreamHookContext & { attempt: number; delayMs: number; discardedText?: true; discardedThinking?: true; err: unknown; maxAttempts: number; nextAttempt: number; requestId?: string; statusCode?: number } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { attempt: number; delayMs: number; discardedText?: true; discardedThinking?: true; err: unknown; maxAttempts: number; nextAttempt: number; requestId?: string; statusCode?: number } & DecideHookContext, ...rest: []) => void)[]; 'stream:server_tool_result': ((ctx: StreamHookContext & { content: unknown; toolName: string; toolUseId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { content: unknown; toolName: string; toolUseId: string } & DecideHookContext, ...rest: []) => void)[]; 'stream:server_tool_use': ((ctx: StreamHookContext & { id: string; input: Record<string, unknown>; name: string } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { id: string; input: Record<string, unknown>; name: string } & DecideHookContext, ...rest: []) => void)[]; 'stream:start': ((ctx: StreamHookContext & { startedAt: number } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { startedAt: number } & DecideHookContext, ...rest: []) => void)[]; 'stream:text': ((ctx: StreamHookContext & { delta: string; text: string } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { delta: string; text: string } & DecideHookContext, ...rest: []) => void)[]; 'stream:thinking': ((ctx: StreamHookContext & { delta: string; thinking: string } & DecideHookContext, ...rest: []) => void) | ((ctx: StreamHookContext & { delta: string; thinking: string } & DecideHookContext, ...rest: []) => void)[]; 'system:before': ((ctx: { system: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { system: string } & DecideHookContext, ...rest: []) => void)[]; 'system:transform': ((ctx: { messages: readonly SessionMessage[]; session?: Session; system: string; turn: number; turnId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { messages: readonly SessionMessage[]; session?: Session; system: string; turn: number; turnId: string } & DecideHookContext, ...rest: []) => void)[]; 'tool-batch:resumed': ((ctx: { done: readonly string[]; resuming: readonly string[]; runId: string; turn: number; turnId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { done: readonly string[]; resuming: readonly string[]; runId: string; turn: number; turnId: string } & DecideHookContext, ...rest: []) => void)[]; 'tool-budget:exceeded': ((ctx: { count: number; max: number; mode: 'steer' | 'block'; tool: string; turnId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { count: number; max: number; mode: 'steer' | 'block'; tool: string; turnId: string } & DecideHookContext, ...rest: []) => void)[]; 'tool-results:after': ((ctx: { message: SessionTurn; results: readonly ToolResult[]; turn: number; turnId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { message: SessionTurn; results: readonly ToolResult[]; turn: number; turnId: string } & DecideHookContext, ...rest: []) => void)[]; 'tool-state:commit-failed': ((ctx: { callId: string; error: Error; handled?: boolean; ops: readonly ToolStateOp[]; runId: string; toolName: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { callId: string; error: Error; handled?: boolean; ops: readonly ToolStateOp[]; runId: string; toolName: string } & DecideHookContext, ...rest: []) => void)[]; 'tool:after': ((ctx: ToolHookContext & { coercions?: readonly string[]; isError: boolean; outcome?: ToolOutcome; outputBytes: number; result: string | ToolResultContent[]; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { coercions?: readonly string[]; isError: boolean; outcome?: ToolOutcome; outputBytes: number; result: string | ToolResultContent[]; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void)[]; 'tool:before': ((ctx: ToolHookContext & { coercions?: readonly string[]; priorContent?: string; runToolCounts: Readonly<Record<string, number>>; unknown?: boolean } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { coercions?: readonly string[]; priorContent?: string; runToolCounts: Readonly<Record<string, number>>; unknown?: boolean } & DecideHookContext, ...rest: []) => void)[]; 'tool:cancelled': ((ctx: ToolHookContext & { reason: string; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { reason: string; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void)[]; 'tool:dispatched': ((ctx: ToolHookContext & { outcome: 'execute' | 'gate-substitute' | 'gate-block' | 'unknown' | 'invalid-input'; reason?: string; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { outcome: 'execute' | 'gate-substitute' | 'gate-block' | 'unknown' | 'invalid-input'; reason?: string; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void)[]; 'tool:error': ((ctx: ToolHookContext & { error: Error; result?: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { error: Error; result?: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void)[]; 'tool:gate': ((ctx: ToolHookContext & ToolGateResumeFields & { block: boolean; metadata: ToolResultMetadata; reason: string; result?: string | ToolResultContent[]; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & ToolGateResumeFields & { block: boolean; metadata: ToolResultMetadata; reason: string; result?: string | ToolResultContent[]; runToolCounts: Readonly<Record<string, number>> } & DecideHookContext, ...rest: []) => void)[]; 'tool:progress': ((ctx: ToolHookContext & { progress: ToolProgress } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { progress: ToolProgress } & DecideHookContext, ...rest: []) => void)[]; 'tool:transform': ((ctx: ToolHookContext & { coercions?: readonly string[]; isError: boolean; metadata: ToolResultMetadata; outputBytes: number; result: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { coercions?: readonly string[]; isError: boolean; metadata: ToolResultMetadata; outputBytes: number; result: string | ToolResultContent[] } & DecideHookContext, ...rest: []) => void)[]; 'tool:unknown': ((ctx: ToolHookContext & { result?: string | ToolResultContent[]; suppressError: boolean } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { result?: string | ToolResultContent[]; suppressError: boolean } & DecideHookContext, ...rest: []) => void)[]; 'tracing:redact': ((ctx: { kind: 'system' | 'prompt' | 'tool-input' | 'tool-result' | 'mcp-tool-input' | 'mcp-tool-result' | 'stream-text' | 'stream-thinking' | 'decision-answers'; meta?: Readonly<Record<string, unknown>>; redacted: string; value: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { kind: 'system' | 'prompt' | 'tool-input' | 'tool-result' | 'mcp-tool-input' | 'mcp-tool-result' | 'stream-text' | 'stream-thinking' | 'decision-answers'; meta?: Readonly<Record<..., ...>>; redacted: string; value: string } & DecideHookContext, ...rest: []) => void)[]; 'turn:after': ((ctx: { aborted?: boolean; cumulativeUsage: Readonly<{ cacheCreation: number; cacheRead: number; cost?: number; input: number; output: number; turns: number }>; message: SessionTurn; toolCounts: { run: Readonly<Record<string, number>>; turn: Readonly<Record<string, number>> }; turn: number; turnId: string; usage: TurnUsage } & DecideHookContext, ...rest: []) => void) | ((ctx: { aborted?: boolean; cumulativeUsage: Readonly<{ cacheCreation: number; cacheRead: number; cost?: number; input: number; output: number; turns: number }>; message: SessionTurn; toolCounts: { run: Readonly<Record<..., ...>>; turn: Readonly<Record<..., ...>> }; turn: number; turnId: string; usage: TurnUsage } & DecideHookContext, ...rest: []) => void)[]; 'turn:before': ((ctx: { options: StreamOptions; turn: number; turnId: string } & DecideHookContext, ...rest: []) => void) | ((ctx: { options: StreamOptions; turn: number; turnId: string } & DecideHookContext, ...rest: []) => void)[]; usage: ((ctx: { totalIn: number; totalOut: number; turn: number; turnId: string; usage: TurnUsage } & DecideHookContext, ...rest: []) => void) | ((ctx: { totalIn: number; totalOut: number; turn: number; turnId: string; usage: TurnUsage } & DecideHookContext, ...rest: []) => void)[]; 'validation:coerce': ((ctx: ToolHookContext & { coercions: readonly string[]; schema: Record<string, unknown> } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { coercions: readonly string[]; schema: Record<string, unknown> } & DecideHookContext, ...rest: []) => void)[]; 'validation:reject': ((ctx: ToolHookContext & { reason: string; schema: Record<string, unknown> } & DecideHookContext, ...rest: []) => void) | ((ctx: ToolHookContext & { reason: string; schema: Record<string, unknown> } & DecideHookContext, ...rest: []) => void)[] }> | Agent-lifetime hook registrations. Registered once when Handlers may be a single function or an array — the array form is what
For per-run handlers (auto-detached at run end) use
|
| mcpConfigTransforms? | readonly ExtensionOrderedContribution<ExtensionMcpConfigTransform>[] | MCP config transforms contributed by extensions. |
| mcpConnector? | McpConnector | Override how Invoked lazily on the first |
| mcpServers? | McpServerConfig[] | MCP servers to connect and expose as tools |
| mcpToolWrap? | McpToolWrap | Wrap every discovered MCP tool right after bootstrap, before it's
registered into the agent's tool map — see Not needed for durable execution: a replay-mode runtime records MCP tool
calls as steps like native ones. Discovery, |
| name? | string | Display name for the agent (used in traces/logs). |
| provider | Provider | Provider[] | LLM provider(s) driving the agent. A single |
| readState? | ReadStateMap | Explicit read-state map for |
| runTransforms? | readonly ExtensionOrderedContribution<ExtensionRunTransform>[] | Run option transforms contributed by extensions, applied before each run. |
| session? | Session | Session for identity, turn persistence, and run tracking. With
|
| sessionId? | string | Id of the session to run in, loaded from |
| skillPolicies? | readonly ExtensionOrderedContribution<ExtensionSkillPolicy>[] | Skill activation policies contributed by extensions. |
| skills? | SkillsConfig | Skills configuration |
| system? | string | Default system prompt injected when no system is provided at run time. |
| toolAliases? | Record<string, string> | Map canonical tool names to LLM-facing (aliased) names. Aliasing is LLM-boundary-only: the alias is what the provider's tool spec
carries and what the model calls the tool; the canonical name is what lives in
|
| toolCatalogTransforms? | readonly ExtensionOrderedContribution<ExtensionToolCatalogTransform>[] | Tool catalog transforms contributed by extensions. |
| toolDescriptions? | ToolDescriptions | Override the LLM-facing description of any tool — native, host-provided,
or MCP-discovered — keyed by canonical name ( A string replaces the description; a function receives the current text
(after every framework rewrite — e.g. Applied once per run, last, so it reaches every surface that reads
|
| toolKill? | { defaultMessage?: string } | Tune
The registry also handles the pre-registration race: a |
| toolResultMetadata? | readonly ExtensionOrderedContribution<ExtensionToolResultMetadataRegistration>[] | Tool result metadata registrations contributed by extensions. |
| tools? | Record<string, ToolDef> | Tool definitions available to the agent. Defaults to no tools. |
| toolState? | ToolStateStore | Durable backing for Runtime-agnostic on purpose: with The snapshot is loaded once per agent, before the first tool dispatch; writes are staged per call and committed when the call settles. NOT inherited by subagents. A child you build yourself can be handed the
same store, but |
CacheBreakInfoInterface#
Structured payload of the 'cache:break' agent hook, emitted by
installCacheBreakLogger whenever a turn reads back materially less
prefix than the previous turn established (see expectedReadAtLeast /
expectedReadRatio on CacheBreakLoggerOptions).
changed names the request dimensions that differ from the previous turn —
the culprit when the host rewrote something. An EMPTY changed is itself a
signal: the request was byte-stable AT THIS WIRE and the provider still
failed to serve the prefix. Three suspects, in the order to check them:
- TTL expiry — a long pause since the previous turn (compare wall-clock gaps; the default window is ~5 minutes).
- Provider-side eviction — no pause, no pattern; typically isolated.
- Downstream transformation churn — on architectures where a gateway CASTS
this request into another provider's format (Anthropic → OpenAI
Responses, Converse, …), these dimensions only cover the PRE-cast bytes.
A transform that isn't prefix-monotonic (unstable id mapping, per-request
injections, config-derived fields like reasoning effort) breaks the
vendor's cache while reading as byte-stable here. Recurring
empty-
changedbreaks on a cast route point at the transform, not at this agent — instrument the gateway's OUTPUT with the same positional hashing (hashMessages) to name the churn.
interface CacheBreakInfoProperties
| Name | Type | Description |
|---|---|---|
| cacheCreation | number | Tokens written to the cache on the turn that broke. |
| cacheRead | number | Tokens read from the cache on the turn that broke. |
| changed | CacheDimensionName[] | Request dimensions that changed since the previous turn; empty ⇒ suspect TTL/eviction. |
| details | Partial<Record<CacheDimensionName, string>> | Human-readable per-dimension diffs, keyed by the entries of |
| expectedAtLeast | number | The floor that tripped: |
| model | string | Model id of the request that broke — pair with a provider key to price the break. |
| rebilledTokens | number | Approximate tokens re-billed by this break: the prefix the previous turn
had cached minus what this turn actually read back. That portion was
re-written at the cache-write rate (~1.25x input) instead of read at the
cache-read rate (~0.1x), so the extra spend is roughly
|
| systemSections? | CacheSystemSection[] | The parts of the system prompt that changed, when |
| turn | number | 1-based turn index as counted by the detector — monotonic across the
INSTALLER'S lifetime, i.e. every |
| turnId | string | Id of the turn that broke the cache. |
CacheBreakLoggerOptionsInterface#
Options for installCacheBreakLogger.
interface CacheBreakLoggerOptionsProperties
| Name | Type | Default | Description |
|---|---|---|---|
| expectedReadAtLeast? | number | Absolute floor for "this turn missed cache". The logger reports when
Defaults to
| |
| expectedReadRatio? | number | Detect PARTIAL-prefix breaks by comparing this turn's Defaults to | |
| log? | (line: string) => void | Sink for log lines. Defaults to NOT every line is a dimension diff: unless | |
| onEmitError? | (error: unknown) => void | Sink for errors thrown by | |
| sessionBaseline? | boolean | true | Share the comparison baseline with other agents bound to the same session.
When on, the last request of a top-level run on a session is kept per
session id at process level, and a newly built agent compares its first
request against it, so a cache break caused by rebuilding the agent is
reported instead of starting from a blank slate. Subagent runs never
share. Set |
| verbose? | boolean | When |
CacheDimensionDiffInterface#
Result of diffCacheDimensions.
interface CacheDimensionDiffProperties
| Name | Type | Description |
|---|---|---|
| changed | readonly CacheDimensionName[] | Dimension names whose hashes differ between the two snapshots. |
| details | Readonly<Partial<Record<CacheDimensionName, string>>> | Per-dimension details — only filled for changed dimensions. |
| messagePrefixDivergedAt? | number | Message index where the prefixes diverged, when |
| systemSections? | readonly CacheSystemSection[] | The parts of |
CacheDimensionSnapshotInterface#
Snapshot of per-request dimensions that contribute to the provider-side
prompt cache key. Hashed at turn:before and stashed for diffing on the
next request.
Hashes are FNV-1a 32-bit (cheap, no allocations on the hot path,
collision rate good enough for diff detection — we're not signing
anything). For verbose output the helper also captures byte lengths so
system: 1024 → 1031 reads like a real diff line.
interface CacheDimensionSnapshotProperties
| Name | Type | Description |
|---|---|---|
| effortHint | boolean | Whether the level-derived effort hint is emitted — |
| messageCount | number | Number of messages in the request. |
| messageHashes | readonly number[] | Per-message content hashes, in order. Compared POSITIONALLY against the previous turn so that appending messages — what every healthy turn does — is not mistaken for a rewrite. A single cumulative hash cannot make that distinction: it changes on plain growth too, and would name this dimension on literally every break, drowning the real culprit. Tracked because the expensive real-world breaks live here, not in the
request scaffold: compaction, stale-read elision, media age-out and history
trimming all rewrite the transcript mid-conversation, and none of them touch
|
| model | string | Model id. |
| systemBytes | number | Byte length of |
| systemHash | number | Hash of |
| systemSections? | Readonly<Partial<Record<CacheSystemSection, number>>> | Hash of each part of |
| taskBudget | number | Whole-task thinking budget (Anthropic adaptive-only) — |
| thinking | string | Thinking level — |
| thinkingBudget | number | Exact thinking budget — |
| toolsCount | number | Number of tools in the array. |
| toolsHash | number | Hash of the JSON-serialized |
CacheDriftInfoInterface#
Structured payload of the 'cache:drift' agent hook, emitted by
installCacheBreakLogger at turn:before whenever the outgoing
request DIVERGES from the previous completed turn's — any tracked dimension,
including an in-place rewrite of the message prefix.
Drift is the PREDICTIVE counterpart of 'cache:break': it fires from the
request bytes alone, before (and regardless of whether) the provider
confirms a miss. That makes it the signal that works everywhere the
confirmed-cost path cannot see — providers that report no cache usage, and
format-casting gateways where this wire is the last observable point before
the cast. A turn that drifts here and then reports a healthy cacheRead
means the provider absorbed the change (e.g. only the tail moved); a turn
that drifts AND breaks gives the break its attribution for free.
Intentional rewrites drift too, by design — that is the attribution story
for planned invalidations: tail compaction and media age-out cut on
chunk-aligned indices (divergedAt a multiple of the compaction chunk),
stale-read stubbing rewrites a read's own position, host history surgery
lands anywhere. details.messagePrefix carries the same hint in prose.
interface CacheDriftInfoProperties
| Name | Type | Description |
|---|---|---|
| changed | CacheDimensionName[] | Dimensions that differ from the previous completed turn's request. |
| details | Partial<Record<CacheDimensionName, string>> | Human-readable per-dimension diffs, keyed by the entries of |
| divergedAt? | number | Message index of an in-place prefix rewrite — see |
| messageCount | number | Number of messages in the outgoing request. |
| model | string | Model id of the OUTGOING request (post-fallback, pre-provider). |
| previousMessageCount | number | Number of messages in the previous completed turn's request. |
| systemSections? | CacheSystemSection[] | The parts of the system prompt that changed, when |
| turn | number | 1-based turn index, installer-lifetime monotonic — see |
| turnId | string | Id of the turn the outgoing request belongs to. |
CachePromptSectionsInterface#
Texts of the system prompt sections a snapshot can name: the sections the
SDK appended and the host's rules block. Each must be a substring of the
request's system to count; anything else is ignored.
interface CachePromptSectionsProperties
| Name | Type | Description |
|---|---|---|
| mcpInstructions? | string | The MCP server instructions the SDK appended. |
| rules? | string | The rules block the run passed as |
| skillsCatalog? | string | The skills catalog the SDK appended. |
| toolsCatalog? | string | The searchable tools catalog the SDK appended. |
McpToolMetaInterface#
Identity of a discovered MCP tool, passed to McpToolWrap.
interface McpToolMetaProperties
| Name | Type | Description |
|---|---|---|
| server | string | Server name from |
| toolName | string | The server-side tool name, un-namespaced. |
RunSummaryInterface#
Postmortem snapshot of one agent.run(). Strictly serializable — every
field round-trips through JSON.stringify / JSON.parse without loss
so a log aggregator can ingest it as-is.
interface RunSummaryProperties
| Name | Type | Description |
|---|---|---|
| agentName? | string | Name of the agent, when set. |
| blocks | RunSummaryBlock[] | Tool calls that did not run. |
| budgetEvents | RunSummaryBudget[] | Budget events recorded during the run. |
| byModel | RunSummaryByModel[] | Totals broken down by model. |
| children? | RunSummary[] | Postmortem snapshots of child runs that bubbled their stats up via
|
| depth | number | Subagent depth. 0 is the top-level agent. |
| durationMs | number | Wall-clock duration of the run. |
| endedAt | number | Run end, in Unix milliseconds. |
| errors | RunSummaryError[] | Errors recorded during the run. |
| pairingRepairs | Record<string, number> | Counts of pairing repairs, keyed by repair mode. |
| parentRunId? | string | Id of the parent run, for a subagent run. |
| repeatGuardEvents | RunSummaryRepeatGuard[] | Repeat-guard escalations. An |
| runId? | string | Id of the run. |
| startedAt | number | Run start, in Unix milliseconds. |
| status | 'completed' | 'aborted' | How the run ended. |
| streamRetries | RunSummaryRetry[] | Provider-stream retries the loop absorbed. Empty on healthy runs; on
incident runs the summed |
| totals | RunSummaryTokens | Token and cost totals, including child runs. |
| turns | number | Number of turns of the run's own loop. |
| validationRejects | RunSummaryValidation[] | Tool calls rejected by input validation. |
RunSummaryBlockInterface#
One tool call that did not run: blocked by a gate, unknown, or rejected for invalid input.
interface RunSummaryBlockProperties
| Name | Type | Description |
|---|---|---|
| callId | string | Id of the tool call. |
| outcome | 'gate-block' | 'unknown' | 'invalid-input' | Why the call did not run. |
| reason? | string | Reason given by the gate, when there was one. |
| toolName | string | Name of the tool the model called. |
RunSummaryBudgetInterface#
One output-size or tool-count budget event.
interface RunSummaryBudgetProperties
| Name | Type | Description |
|---|---|---|
| kind | 'bytes' | 'tool-count' | Budget type: a byte budget or a per-tool call budget. |
| limit | number | Configured budget. |
| mode? | 'steer' | 'block' |
|
| observed | number | Value that crossed the budget: bytes or call count. |
| toolName? | string | Tool name (for |
| turnId? | string | Id of the turn the event happened in. |
RunSummaryByModelInterface#
Token and cost totals for one model within a run.
interface RunSummaryByModelProperties
| Name | Type | Description |
|---|---|---|
| cacheCreation | number | Tokens written to the prompt cache. |
| cacheRead | number | Tokens read from the prompt cache. |
| cost | number | Cost in USD. |
| input | number | Input tokens, excluding cache tokens. |
| modelId | string | Model id, or |
| output | number | Output tokens. |
| turns | number | Number of turns served by the model. |
RunSummaryCollectorInterface#
Collector that builds a RunSummary from agent hooks. Created by createRunSummaryCollector.
interface RunSummaryCollectorProperties
| Name | Type | Description |
|---|---|---|
| install | (hooks: Hookable<AgentHooks>) => () => void | Install the collector's hook handlers. Returns an uninstall fn. |
| latest | () => RunSummary | undefined | Most-recent summary; |
RunSummaryCollectorOptionsInterface#
Options for createRunSummaryCollector.
interface RunSummaryCollectorOptionsProperties
| Name | Type | Description |
|---|---|---|
| onSummary? | (summary: RunSummary) => void | Called with the assembled |
RunSummaryErrorInterface#
One error recorded during a run.
interface RunSummaryErrorProperties
| Name | Type | Description |
|---|---|---|
| callId? | string | Id of the tool call, for tool errors. |
| childId? | string | Spawn id of the child, for spawn errors. |
| errorType? | string | Error class name, when known. |
| kind | 'spawn' | 'tool' | 'mcp' | 'stream' | 'mcp-tool' | Where the error came from: a provider stream, a tool, an MCP tool call, an MCP server, or a spawn. |
| message | string | Error message. |
| requestId? | string | Provider request id, when the provider reported one. |
| server? | string | MCP server name, for MCP errors. |
| statusCode? | number | HTTP status code, for provider errors. |
| toolName? | string | Name of the tool, for tool errors. |
| turnId? | string | Id of the turn the error happened in, when known. |
RunSummaryRepeatGuardInterface#
One repeat-guard escalation.
interface RunSummaryRepeatGuardProperties
| Name | Type | Description |
|---|---|---|
| action | 'steer' | 'block' | 'abort' | What the guard did: blocked the call, steered the model, or aborted the run. |
| count | number | Observed repeat count that tripped the guard. |
| threshold | number | Configured repeat threshold. |
| toolName | string | Name of the repeated tool. |
| turnId? | string | Id of the turn the event happened in. |
RunSummaryTokensInterface#
Token and cost totals of a run.
interface RunSummaryTokensProperties
| Name | Type | Description |
|---|---|---|
| cacheCreation | number | Tokens written to the prompt cache. |
| cacheRead | number | Tokens read from the prompt cache. |
| cost? | number | Cost in USD. Absent when no price was known. |
| input | number | Input tokens, excluding cache tokens. |
| output | number | Output tokens. |
| ttftMs? | number | First observable byte from the provider, ms from run start. |
RunSummaryValidationInterface#
One tool call rejected by input validation.
interface RunSummaryValidationProperties
| Name | Type | Description |
|---|---|---|
| callId | string | Id of the tool call. |
| reason | string | Validation failure message. |
| toolName | string | Name of the tool the model called. |
Types#
AgentHookMapType#
Strongly-typed hook registration map accepted on AgentOptions.hooks
(and therefore on any Preset). Each entry is a single handler or an
array of handlers — arrays are what composePresets produces when
multiple presets register handlers for the same event.
Handlers registered here live for the lifetime of the agent. They fire
across every run(), mirroring a manual agent.hooks.hook(event, fn) call
made right after createAgent returns. For per-run handlers (auto-detached
at run end) use AgentRunOptions.hooks instead.
type AgentHookMap = Partial<{ [K in keyof AgentHooks]: AgentHookHandlers[K] | AgentHookHandlers[K][] }>CacheDimensionNameType#
Cache-affecting dimension names.
type CacheDimensionName =
| 'system'
| 'tools'
| 'model'
| 'thinking'
| 'thinkingBudget'
| 'taskBudget'
| 'effortHint'
| 'messagePrefix'CacheSystemSectionType#
A part of the system prompt that a system change can be traced to.
skillsCatalog: the skills catalog the SDK appends.toolsCatalog: the searchable tools catalog the SDK appends.mcpInstructions: the MCP server instructions the SDK appends.rules: the rules block the run passed ascontextSections.rulesBlock.hostStatic: the rest of the prompt aboveSYSTEM_PROMPT_BOUNDARY, or the whole rest of the prompt when it has no boundary.hostDynamic: the rest of the prompt belowSYSTEM_PROMPT_BOUNDARY.
type CacheSystemSection =
| 'skillsCatalog'
| 'toolsCatalog'
| 'mcpInstructions'
| 'rules'
| 'hostStatic'
| 'hostDynamic'McpConnectorType#
Custom MCP connector — the seam that owns "connect mcpServers, discover
their tools, hand them back". Supply one via AgentOptions.mcpConnector
to inject a custom transport / auth provider (e.g. OAuth via
connectMcpServers(configs, undefined, hooks, { buildAuthProvider })).
To re-shape discovered tools, use the separate
AgentOptions.mcpToolWrap hook: it composes with whatever
connector is in play.
The agent passes its own hooks so a connector that delegates to
connectMcpServers can forward them and keep every mcp:* event
firing. A connector is responsible for whatever the default path does that
it cares about (config normalization, namespacing, per-server
instructions) — delegating to connectMcpServers preserves all of it.
The agent always passes extras.execution — a LAZY binding whose
getHandle() spawns/resolves the execution handle only when a
scope: 'context' stdio server actually builds its transport. Forward it
untouched to connectMcpServers (as { execution }) or those servers
fail bootstrap; a connector that never builds such a transport (e.g. one
reusing a live parent connection) simply never triggers the spawn. There
is no ordering guarantee beyond that — do not assume a handle exists
before getHandle() resolves. Declaring only (configs, hooks) stays
valid for host-only setups.
type McpConnector = (configs: McpServerConfig[], hooks: Hookable<AgentHooks>, extras?: { execution?: McpExecutionBinding }) => Promise<McpConnection>McpToolWrapType#
Wrap each discovered MCP tool right after bootstrap, before it's registered
into the agent's tool map — see AgentOptions.mcpToolWrap. The MCP
counterpart to wrapping native tools yourself before createAgent.
type McpToolWrap = (tool: ToolDef, meta: McpToolMeta) => ToolDefRe-exports#
These names are documented on the entry point that owns them.