{"version":3,"file":"agent.mjs","names":[],"sources":["../../../../../../../ai/src/agent/agent.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type {\n  AgentContract,\n  AgentEventHandler,\n  AgentEventMap,\n  AgentExecuteOptions,\n  AgentResult,\n  BaseReport,\n  CapturedMessage,\n  CompleteEvent,\n  FinishReason,\n  LLMTrip,\n  Message,\n  MiddlewareExecuteContext,\n  MiddlewareState,\n  MiddlewareToolContext,\n  MiddlewareTripContext,\n  ModelResponse,\n  ModelToolCallRequest,\n  StreamContract,\n  StreamEventBody,\n  StreamingToolGuardConfig,\n  ToolCall,\n  ToolContext,\n  ToolEventMeta,\n  Usage,\n  UsageEvent,\n  WithoutIdentity,\n} from \"../contracts\";\nimport {\n  AgentCancelledError,\n  AgentExecutionError,\n  AgentMaxTripsError,\n  AIError,\n  SchemaValidationError,\n} from \"../errors\";\nimport type { AgentContract as AgentContractType } from \"../contracts/agent/agent.contract\";\nimport type { AgentResumeOptions } from \"../contracts/agent/agent-options.type\";\nimport type { AgentSnapshot, AgentSnapshotStatus } from \"../contracts/agent/agent-snapshot.type\";\nimport type { EvalOptions, EvalReport } from \"../contracts/agent/eval.type\";\nimport { runEval } from \"../eval/eval-runner\";\nimport { runPipeline } from \"../middleware\";\nimport { notifyObservers } from \"../observe/resolve-observers\";\nimport { skills } from \"../skills\";\nimport type { SkillsContract } from \"../skills/contracts/skills.contract\";\nimport { normalizeAgentTools } from \"../tool/executable-as-tool\";\nimport type { ToolContract, ToolInvokeResult } from \"../tool/tool\";\nimport {\n  captureChildReport,\n  computeCost,\n  extractJsonLenient,\n  extractJsonPayload,\n  generateRunId,\n  mergeUsage,\n  safeJsonParse,\n  stampReportLineage,\n} from \"../utils\";\nimport type { AgentConfig } from \"./agent-config.type\";\nimport { JUDGE_DEFAULT_REPAIR_ATTEMPTS, type JudgeConfig } from \"./judge-config.type\";\nimport { buildAgentInputMessages } from \"./agent-input-builder\";\nimport { logAgentEvent } from \"./agent-log-event\";\nimport { createAgentStream, type StreamController } from \"./agent-stream\";\nimport { agentEventToStreamEvent } from \"./agent-to-stream-event\";\nimport { JsonStreamGuard } from \"./json-stream-guard\";\nimport { computeAgentSignature } from \"./signature\";\nimport {\n  deleteAgentSnapshot,\n  loadAgentSnapshotForResume,\n  persistAgentSnapshot,\n} from \"./snapshot\";\n\nconst LOG_MODULE = \"ai.agent\";\n\n/**\n * Internal post-normalization view of an `AgentConfig`. The public\n * `tools` field accepts both built `ToolContract`s and raw executables\n * (`AgentToolEntry[]`); by the time the runtime sees the config every\n * entry has been adapted to a `ToolContract`, so `Execution` works\n * against this narrowed shape and never has to re-discriminate.\n */\ntype ResolvedAgentConfig<TOutput> = Omit<AgentConfig<TOutput>, \"tools\" | \"skills\"> & {\n  tools?: ToolContract<unknown, unknown>[];\n  /**\n   * The skills library resolved once at factory time from the public\n   * `skills` option (a {@link SkillsContract} or a raw `SkillsConfig`).\n   * `undefined` when the agent has no skills attached — the execute path\n   * then behaves byte-for-byte as today.\n   */\n  skillsLib?: SkillsContract;\n  /**\n   * Structural drift fingerprint computed once at factory time from the\n   * agent's identity-defining fields (model + provider + sorted tool\n   * names + maxTrips + output + version). Stamped on every durable\n   * snapshot and compared on `resume()`. Always present so the resume\n   * path never re-derives it.\n   */\n  signature: string;\n};\n\n/**\n * Duck-type a value as a {@link SkillsContract} (vs a raw `SkillsConfig`).\n * A contract exposes the agent-facing methods; a config is a plain spec.\n * Checking `catalogPrompt` is sufficient to discriminate the two shapes.\n */\nfunction isSkillsContract(value: unknown): value is SkillsContract {\n  return (\n    typeof value === \"object\" &&\n    value !== null &&\n    typeof (value as SkillsContract).catalogPrompt === \"function\"\n  );\n}\n\n/**\n * Detect abort-flavored errors surfaced by SDK HTTP layers — the\n * DOM `AbortError`, axios `ERR_CANCELED`, node-fetch's own\n * `AbortError`. Used to classify them as cancellation rather than\n * generic agent-execution failures.\n */\nfunction isAbortLike(err: unknown): boolean {\n  if (!err || typeof err !== \"object\") return false;\n\n  const e = err as { name?: unknown; code?: unknown };\n\n  return e.name === \"AbortError\" || e.code === \"ERR_CANCELED\" || e.code === \"ABORT_ERR\";\n}\n\n/**\n * Readable synthetic name for agents constructed without an explicit\n * `name`. Format: `anon_<provider>_<model>[_<tool1>+<tool2>+...]` —\n * deterministic (same config → same name across restarts) and\n * human-readable in logs / workflow snapshots.\n *\n * Keeps drift detection honest for the `ai.agent({ model })`\n * one-liner without punishing it with a hashed id nobody can read.\n */\nfunction synthesizeAgentName<T>(config: AgentConfig<T>): string {\n  const provider = (config.model as unknown as { provider?: string })?.provider ?? \"unknown\";\n  const model = config.model?.name ?? \"unknown\";\n  const tools = (config.tools ?? [])\n    .map((tool) => tool.name)\n    .sort()\n    .join(\"+\");\n\n  const base = `anon_${sanitize(provider)}_${sanitize(model)}`;\n  return tools ? `${base}_${sanitize(tools, { keepPlus: true })}` : base;\n}\n\nfunction sanitize(value: string, opts: { keepPlus?: boolean } = {}): string {\n  const allowed = opts.keepPlus ? /[^a-zA-Z0-9._+-]/g : /[^a-zA-Z0-9._-]/g;\n  return value.replace(allowed, \"-\");\n}\n\n/**\n * Authoring-time check on the middleware array. Throws an\n * `AgentExecutionError` with `context: { authoring: true }` the\n * moment an invalid entry is found — the agent factory surface is\n * where config bugs should surface, not ten trips into a run.\n *\n * Validates:\n * - Every entry is a non-null object with a non-empty string `name`.\n * - No two entries share the same `name` (would silently collide on\n *   `ctx.state` keys and produce impossible-to-debug behavior).\n *\n * Does NOT validate that hook maps contain callable functions —\n * that would catch late-binding bugs but also reject legitimate\n * patterns like `before` being conditionally `undefined`. Runtime\n * dispatch handles missing hooks safely.\n */\nfunction validateMiddleware(middleware: ReadonlyArray<unknown> | undefined): void {\n  if (!middleware || middleware.length === 0) {\n    return;\n  }\n\n  const seen = new Set<string>();\n\n  for (let index = 0; index < middleware.length; index++) {\n    const entry = middleware[index];\n\n    if (!entry || typeof entry !== \"object\") {\n      throw new AgentExecutionError(\n        `middleware[${index}] must be an object; received ${entry === null ? \"null\" : typeof entry}`,\n        { context: { authoring: true, index } },\n      );\n    }\n\n    const name = (entry as { name?: unknown }).name;\n\n    if (typeof name !== \"string\" || name.length === 0) {\n      throw new AgentExecutionError(`middleware[${index}] must have a non-empty string \"name\"`, {\n        context: { authoring: true, index },\n      });\n    }\n\n    if (seen.has(name)) {\n      throw new AgentExecutionError(\n        `duplicate middleware name \"${name}\" — each middleware needs a unique name so ctx.state keys do not collide`,\n        { context: { authoring: true, index, name } },\n      );\n    }\n\n    seen.add(name);\n  }\n}\n\n/**\n * Normalize the public `judge` flag (`boolean | JudgeConfig | undefined`)\n * into a resolved {@link JudgeConfig} or `undefined` when the preset is\n * off. `true` ⇒ all defaults (`{}`); a partial config fills missing fields\n * from the defaults; `false` / absent ⇒ `undefined` (judge mode off).\n */\nfunction resolveJudgeConfig(judge: boolean | JudgeConfig | undefined): JudgeConfig | undefined {\n  if (!judge) {\n    return undefined;\n  }\n\n  const base = judge === true ? {} : judge;\n\n  return {\n    repairAttempts: base.repairAttempts ?? JUDGE_DEFAULT_REPAIR_ATTEMPTS,\n  };\n}\n\n/**\n * Creates an executable AI agent from the given configuration.\n *\n * The agent runs a bounded trip loop: each trip calls the model, dispatches\n * any requested tool calls, then loops until the model stops or `maxTrips`\n * is reached. Each `execute()` / `stream()` call spawns a fresh internal\n * `Execution` instance — the factory itself holds no state across calls.\n *\n * `execute()` never throws — any error is attached to the returned result\n * under `result.error`. `stream()` surfaces errors both on the terminal\n * `error` stream event and via the `stream.result` promise.\n *\n * @example\n * const myAgent = agent({\n *   model: openai.model({ name: \"gpt-4o\" }),\n *   systemPrompt: \"You are a helpful assistant.\",\n *   tools: [searchTool],\n * });\n *\n * const result = await myAgent.execute(\"What is the capital of Egypt?\");\n *\n * @example\n * const stream = myAgent.stream(\"Write a haiku about Cairo.\");\n *\n * for await (const event of stream) {\n *   if (event.type === \"streaming\") process.stdout.write(event.delta);\n * }\n *\n * const result = await stream.result;\n */\nexport function agent<TOutput = unknown>(config: AgentConfig<TOutput>): AgentContract<TOutput> {\n  // Authoring-time validation of the middleware array. Rejects two\n  // classes of bug that would otherwise surface as opaque failures\n  // mid-execution: (a) entries that aren't proper middleware\n  // objects (null, undefined, missing `name`), and (b) two\n  // middlewares sharing the same `name`, which would silently\n  // collide on `ctx.state` keys. Fail fast, fail loud — per the\n  // authoring-time rules in `domains/ai/conventions/errors.md`.\n  validateMiddleware(config.middleware);\n\n  // Resolve the agent's identity. Explicit `name` wins; otherwise we\n  // synthesize a DETERMINISTIC fingerprint from the config's\n  // identity-defining fields (model + provider + tool names). Same\n  // config across process restarts produces the same synthetic name,\n  // so workflow signature drift detection stays honest for agents\n  // composed into workflows without explicit names.\n  const isAnonymous = !config.name || typeof config.name !== \"string\";\n\n  // Auto-adapt any raw executable (agent/workflow/supervisor) dropped\n  // into `tools: []` into a `ToolContract` before the name is\n  // synthesized — `synthesizeAgentName` reads `config.tools[].name`,\n  // so the fingerprint must see the normalized entries. Built\n  // `ToolContract`s (from `.asTool()` / `ai.tool()`) pass through\n  // untouched, so this is a no-op for the existing surface.\n  const tools = normalizeAgentTools(config.tools);\n  const name = isAnonymous\n    ? synthesizeAgentName({ ...config, tools })\n    : (config.name as string);\n\n  // Resolve the `skills` option to a `SkillsContract` ONCE, here, so every\n  // `execute()` / `stream()` call reuses the same library (and its\n  // `review`-gated saveSkill exposure). A raw `SkillsConfig` is handed to\n  // `skills()`; an already-built contract passes through. Absent ⇒ no skills.\n  const skillsLib = config.skills\n    ? isSkillsContract(config.skills)\n      ? config.skills\n      : skills(config.skills)\n    : undefined;\n\n  // Drift fingerprint for durable resume — computed once over the\n  // resolved identity (model + provider + sorted tool names + maxTrips +\n  // output + version). Cheap FNV-1a; stamped on every snapshot and\n  // compared on `resume()`. Computed unconditionally (whether or not\n  // `durable` is set) so the value is stable and the resume path is free.\n  const signature = computeAgentSignature({\n    name: isAnonymous ? undefined : name,\n    version: config.version,\n    model: { name: config.model?.name, provider: config.model?.provider },\n    tools,\n    maxTrips: config.maxTrips,\n    output: config.output,\n  });\n\n  const resolvedConfig: ResolvedAgentConfig<TOutput> = {\n    ...config,\n    name,\n    tools,\n    skillsLib,\n    signature,\n  };\n\n  // Instance-level handlers registered via `.on()`. Stored here\n  // (factory-scope) so every `execute()` / `stream()` call on this\n  // agent sees the same set. Each event name gets its own Set so\n  // `off()` can remove a specific handler without disturbing others.\n  const instanceHandlers = new Map<\n    keyof AgentEventMap,\n    Set<AgentEventHandler<keyof AgentEventMap>>\n  >();\n\n  function on<K extends keyof AgentEventMap>(event: K, handler: AgentEventHandler<K>): () => void {\n    const existing = instanceHandlers.get(event);\n    const bucket = existing ?? new Set<AgentEventHandler<keyof AgentEventMap>>();\n\n    if (!existing) {\n      instanceHandlers.set(event, bucket);\n    }\n\n    bucket.add(handler as AgentEventHandler<keyof AgentEventMap>);\n\n    return () => off(event, handler);\n  }\n\n  function off<K extends keyof AgentEventMap>(event: K, handler: AgentEventHandler<K>): void {\n    const bucket = instanceHandlers.get(event);\n\n    if (!bucket) {\n      return;\n    }\n\n    bucket.delete(handler as AgentEventHandler<keyof AgentEventMap>);\n\n    if (bucket.size === 0) {\n      instanceHandlers.delete(event);\n    }\n  }\n\n  const agentContract: AgentContractType<TOutput> = {\n    name,\n    isAnonymous,\n    description: config.description,\n    signature,\n    async execute(\n      input: string,\n      options?: AgentExecuteOptions<TOutput>,\n    ): Promise<AgentResult<TOutput>> {\n      return new Execution<TOutput>(\n        resolvedConfig,\n        input,\n        options,\n        undefined,\n        instanceHandlers,\n      ).run();\n    },\n\n    stream(\n      input: string,\n      options?: AgentExecuteOptions<TOutput>,\n    ): StreamContract<AgentResult<TOutput>> {\n      const { controller, stream } = createAgentStream<AgentResult<TOutput>>();\n\n      const execution = new Execution<TOutput>(\n        resolvedConfig,\n        input,\n        options,\n        controller,\n        instanceHandlers,\n      );\n\n      void execution.run();\n\n      return stream;\n    },\n\n    async resume(\n      runId: string,\n      options?: AgentResumeOptions<TOutput>,\n    ): Promise<AgentResult<TOutput>> {\n      // Load the persisted snapshot and run the drift check (throws\n      // AgentDriftError on a structural mismatch unless `{ force: true }`).\n      const snapshot = await loadAgentSnapshotForResume({\n        durable: resolvedConfig.durable,\n        agentName: name,\n        signature,\n        runId,\n        options: options as AgentResumeOptions<unknown> | undefined,\n      });\n\n      // A completed / cancelled / failed snapshot already settled — there\n      // is nothing left to run. Rebuild the final result from the stored\n      // state and short-circuit so resume is idempotent (mirrors the\n      // supervisor \"resume is a no-op and returns the final state\").\n      // `running` is the only status the trip loop re-enters.\n      const execution = new Execution<TOutput>(\n        resolvedConfig,\n        snapshot.input,\n        { ...options, runId } as AgentExecuteOptions<TOutput>,\n        undefined,\n        instanceHandlers,\n        snapshot,\n      );\n\n      return execution.run();\n    },\n\n    on,\n    off,\n\n    eval<TEval = TOutput>(options: EvalOptions<TEval>): Promise<EvalReport<TEval>> {\n      return runEval<TEval>(agentContract as unknown as AgentContract<TEval>, options);\n    },\n  };\n\n  return agentContract;\n}\n\n/**\n * Config for the `ai.agent.judge(...)` helper — every `AgentConfig` field\n * except `judge` itself (the helper sets it). Callers tune resilience by\n * passing a {@link JudgeConfig} as the second argument instead.\n */\nexport type JudgeAgentConfig<TOutput = unknown> = Omit<AgentConfig<TOutput>, \"judge\">;\n\n/**\n * Build a judge-safe agent — sugar for `agent({ ...config, judge })`.\n *\n * Use this for LLM-as-judge graders and verdict classifiers running on\n * models that may emit corrupted structured output (e.g. the Amazon Nova\n * family). The returned agent parses verdicts leniently (tolerates fenced\n * ` ```json ` blocks + surrounding prose), auto-enables a couple of repair\n * re-asks, and never throws on a parse miss — surfacing `result.error` with\n * `result.data` left undefined so a flaky judge degrades gracefully.\n *\n * See {@link AgentConfig.judge} for the full behavior + the resilience-over-\n * strictness trade-off.\n *\n * @param config - Any agent config (model, system prompt, output schema, …).\n * @param judge - Optional fine-tuning ({@link JudgeConfig}); defaults to `true`.\n *\n * @example\n * const grader = ai.agent.judge({\n *   model: nova.model({ name: \"amazon.nova-pro-v1:0\" }),\n *   systemPrompt: \"Grade the answer. Respond with JSON only.\",\n *   output: verdictSchema,\n * });\n *\n * const result = await grader.execute(prompt);\n * if (result.error) {\n *   // graceful default — the judge couldn't produce a clean verdict\n * }\n */\nfunction judgeAgent<TOutput = unknown>(\n  config: JudgeAgentConfig<TOutput>,\n  judge: JudgeConfig | boolean = true,\n): AgentContract<TOutput> {\n  return agent<TOutput>({ ...config, judge });\n}\n\n// Attach the judge helper to the `agent` factory so it surfaces as\n// `ai.agent.judge(...)` (the `Ai` namespace exposes `agent` as\n// `typeof agent`, which now carries this property). Done as a typed\n// property assignment rather than `Object.assign` so the generic\n// signature is preserved for callers.\nagent.judge = judgeAgent;\n\n/**\n * Name → handler-set map shared between the `agent()` factory and its\n * per-call `Execution`. Each `.on()` registration mutates this map;\n * every `Execution` reads from the same reference so additions and\n * removals take effect mid-flight.\n */\ntype InstanceHandlerMap = Map<keyof AgentEventMap, Set<AgentEventHandler<keyof AgentEventMap>>>;\n\n/**\n * Per-call driver that owns the full lifecycle of a single\n * `agent.execute()` or `agent.stream()` invocation.\n *\n * **Role.** An `Execution` is the short-lived state container and phase\n * orchestrator for one agent run. The public `agent()` factory stays purely\n * functional — all mutable bookkeeping (trips, tool calls, usage totals,\n * message history, terminal error, parsed output) lives here so each call\n * gets a fresh, isolated instance.\n *\n * **Responsibility.**\n * - Owns: building the initial message list, driving the bounded trip loop,\n *   dispatching tool calls safely, parsing the final output against the\n *   caller's schema, emitting lifecycle events (to both the user handler\n *   and, in stream mode, the `StreamController`), and producing the\n *   `AgentResult`.\n * - Does NOT own: how the model produces responses (delegated to\n *   `ModelContract.complete` / `ModelContract.stream`), how tools execute\n *   (delegated to `ToolContract.invoke`), the async-queue plumbing for\n *   streaming (delegated to `createAgentStream`), or any cross-call state\n *   (factory-level concerns live in `agent()`).\n *\n * Streaming mode is opt-in via the fourth constructor argument: pass a\n * `StreamController` and every event is mirrored into it while model calls\n * are driven via `model.stream()` instead of `model.complete()`. The public\n * contract of `execute()` says it never throws — `Execution` enforces that\n * by funneling every unexpected error into `this.error` and returning a\n * well-formed result regardless of what went wrong.\n *\n * Not exported — consumers interact only with the `agent()` factory (see\n * §4.2 of code-style.md — \"per-call execution state across phases\").\n *\n * @example\n * // Non-streaming — inside agent.execute():\n * const result = await new Execution(config, input, options).run();\n *\n * @example\n * // Streaming — inside agent.stream():\n * const { controller, stream } = createAgentStream();\n * void new Execution(config, input, options, controller).run();\n * return stream;\n */\nclass Execution<TOutput> {\n  private readonly trips: LLMTrip[] = [];\n  private readonly toolCalls: ToolCall[] = [];\n  private readonly usage: Usage = { input: 0, output: 0, total: 0 };\n  private readonly messages: Message[] = [];\n  /** Resolved system-prompt text sent to the model, captured for the report. */\n  private systemPrompt?: string;\n  /**\n   * Registry name of the named `SystemPromptContract` this run resolved, when\n   * the prompt carried a `meta.name`. Stamped onto the report so observers can\n   * attribute the run to a specific registered prompt. Absent for raw-string,\n   * anonymous-contract, or absent prompts.\n   */\n  private promptName?: string;\n  /** Registry version label paired with {@link Execution.promptName}. */\n  private promptVersion?: string;\n  private readonly maxTrips: number;\n  private readonly startedAt: Date;\n  private readonly start = performance.now();\n  /**\n   * Stable run id. A caller-supplied `options.runId` wins (load-bearing\n   * for durable resume — the snapshot key must stay constant across the\n   * crash); otherwise a fresh id is generated. When `resumeFrom` is set\n   * its `runId` is authoritative so the resumed run writes back to the\n   * same key.\n   */\n  private readonly runId: string;\n  private readonly logger: Logger = log;\n  /**\n   * Event names whose handler already threw once this run — so the\n   * isolate-but-surface warning for a broken handler fires at most once\n   * per event type, never spamming the log on a hot event (token\n   * deltas, tool calls). See {@link surfaceHandlerError} (C5).\n   */\n  private readonly warnedHandlerEvents = new Set<string>();\n  private readonly middleware: ReadonlyArray<\n    NonNullable<AgentConfig<TOutput>[\"middleware\"]>[number]\n  >;\n  private readonly middlewareState: MiddlewareState = new Map();\n  /**\n   * The agent's own tools plus this run's skill tools (`loadSkill`, and\n   * `saveSkill` when a review gate is configured). Built once per execution\n   * because `loadSkillTool` closes over a per-run counter enforcing\n   * `maxLoadsPerRun` — one tool instance per run = one budget per run. When\n   * no skills library is attached this is just `config.tools`.\n   */\n  private readonly effectiveTools: ToolContract<unknown, unknown>[];\n\n  private error?: AIError;\n  private data?: TOutput;\n  private responseSchema?: Record<string, unknown>;\n  /**\n   * Resolved judge-safe preset for this run, or `undefined` when the\n   * `judge` flag is off. When set, output parsing is lenient (tolerates\n   * fenced blocks + surrounding prose) and repair auto-defaults to the\n   * configured attempt count.\n   */\n  private readonly judgeConfig?: JudgeConfig;\n\n  public constructor(\n    private readonly config: ResolvedAgentConfig<TOutput>,\n    private readonly input: string,\n    private readonly options?: AgentExecuteOptions<TOutput>,\n    private readonly streamController?: StreamController<AgentResult<TOutput>>,\n    private readonly instanceHandlers?: InstanceHandlerMap,\n    private readonly resumeFrom?: AgentSnapshot,\n  ) {\n    this.maxTrips = config.maxTrips ?? 10;\n    this.middleware = config.middleware ?? [];\n    this.judgeConfig = resolveJudgeConfig(config.judge);\n\n    // Resolve the run id: a resumed run reuses the snapshot's key so it\n    // writes back to the same record; otherwise a caller-supplied\n    // `options.runId` wins (durable callers pass a stable key), else a\n    // fresh id is generated. `startedAt` likewise restores from the\n    // snapshot so the resumed report spans the whole run, not just the tail.\n    this.runId = resumeFrom?.runId ?? options?.runId ?? generateRunId(\"agent\");\n    this.startedAt = resumeFrom ? new Date(resumeFrom.startedAt) : new Date();\n\n    // Seed the accumulators from the snapshot on resume — re-hydrate the\n    // assembled conversation, the completed trips, the dispatched tool\n    // records, the running usage, and the resolved prompt/schema metadata.\n    // Pushing directly into `this.trips` (rather than re-running `runTrip`)\n    // is what keeps a resume from re-emitting completed trips' lifecycle\n    // events or re-invoking their tools — the loop later starts at\n    // `this.trips.length`. When `resumeFrom` is absent every accumulator\n    // stays empty, so the non-durable path is byte-for-byte unchanged.\n    if (resumeFrom) {\n      this.messages.push(...resumeFrom.messages);\n      this.trips.push(...resumeFrom.trips);\n      this.toolCalls.push(...resumeFrom.toolCalls);\n      mergeUsage(this.usage, resumeFrom.usage);\n      this.systemPrompt = resumeFrom.systemPrompt;\n      this.responseSchema = resumeFrom.responseSchema;\n      this.promptName = resumeFrom.promptName;\n      this.promptVersion = resumeFrom.promptVersion;\n    }\n\n    // Build this run's skill tools once with this run's id so the\n    // per-run `maxLoadsPerRun` counter (closed over inside `loadSkillTool`)\n    // is scoped to exactly this execution. `tools(runId)` already returns\n    // `loadSkill` always and `saveSkill` only when a review gate is wired,\n    // so no special-casing is needed here. `normalizeAgentTools` is a\n    // passthrough for already-built `ToolContract`s — called for uniformity.\n    const skillTools = config.skillsLib\n      ? normalizeAgentTools(config.skillsLib.tools(this.runId)) ?? []\n      : [];\n\n    this.effectiveTools = [...(config.tools ?? []), ...skillTools];\n  }\n\n  /**\n   * Base middleware context shared by every level. `state` is the\n   * single mutable bag threaded through `execute`, `trip`, and `tool`\n   * hooks for the lifetime of this execution — fresh per `execute()`\n   * call, never reused across runs.\n   */\n  private buildExecuteContext(): MiddlewareExecuteContext {\n    return {\n      agent: {\n        name: this.config.name ?? this.config.model.name,\n        isAnonymous: !this.config.name,\n      },\n      model: {\n        name: this.config.model.name,\n        provider: this.config.model.provider,\n      },\n      input: this.input,\n      options: this.options as AgentExecuteOptions<unknown> | undefined,\n      state: this.middlewareState,\n      signal: this.options?.signal,\n    };\n  }\n\n  /**\n   * Entry point for a single agent execution. Wraps the real work\n   * (`runCore`) in the `execute`-level middleware pipeline, then\n   * emits the terminal `agent.completed` / `agent.error` events and\n   * closes the stream (if any) with the post-pipeline result — so\n   * middleware that short-circuits or transforms the final result\n   * still produces a well-formed public outcome.\n   *\n   * Must never throw: any error that escapes the pipeline is\n   * converted into an `AgentResult` with `error` populated before\n   * returning, preserving the `agent.execute()` public contract.\n   */\n  public async run(): Promise<AgentResult<TOutput>> {\n    const context = this.buildExecuteContext();\n\n    let result: AgentResult<TOutput>;\n\n    try {\n      result = (await runPipeline(\n        this.middleware,\n        \"execute\",\n        context,\n        () => this.runCore(),\n        this.logger,\n      )) as AgentResult<TOutput>;\n    } catch (thrown) {\n      this.error = this.toAIError(thrown);\n      result = this.buildResult();\n    }\n\n    if (result.error) {\n      this.emit(\"agent.error\", { error: result.error });\n    }\n\n    this.emit(\"agent.completed\", { result });\n\n    // Fire the `onComplete` hook with a flat payload (runId +\n    // durationMs pre-extracted) for audit-log consumers. Awaited but\n    // errors swallowed so consumer bugs cannot crash the agent or\n    // interfere with the result returned to the caller.\n    await this.fireCompleteHook(result);\n\n    // Route the finished report to any resolved observers (F1/F3).\n    // Gated by `config.observe` + the global observe-all flag; a no-op\n    // when nothing resolves. Observer errors are swallowed inside\n    // `notifyObservers`, so they never break the run — mirroring the\n    // onUsage / onComplete hook policy.\n    await notifyObservers(this.config.observe, result.report);\n\n    // Auto-nest into the enclosing orchestration run when this agent\n    // executed inside a supervisor/orchestrator/team intent callback\n    // (an ambient `RunFrame` is installed). Captures this report onto\n    // the callback's `children[]` and relinks its lineage — so an\n    // `agent.execute(...)` called directly inside a `run()` callback\n    // shows up nested with its tools, instead of being lost as a\n    // separate top-level execution. No-op for standalone runs.\n    captureChildReport(result.report);\n\n    this.streamController?.end(result);\n\n    return result;\n  }\n\n  /**\n   * Inner body wrapped by the `execute`-level pipeline. Drives the\n   * full lifecycle — build messages → emit starting → run trip loop\n   * → parse output → build result. Catches any unexpected throw and\n   * funnels it into `this.error` so the returned result is always\n   * well-formed; `execute`-level `after` hooks receive the result,\n   * with `error` populated when things went wrong.\n   */\n  private async runCore(): Promise<AgentResult<TOutput>> {\n    // Completed-run short-circuit. A resume of a snapshot whose run\n    // already COMPLETED re-runs nothing — the stored trips ARE the\n    // result. Rebuild the final result from the re-hydrated accumulators\n    // and return, so resume is idempotent (mirrors the supervisor\n    // \"resume is a no-op and returns the final state\"). A `failed` or\n    // `cancelled` snapshot is intentionally NOT short-circuited — those\n    // are exactly the runs a caller resumes to retry the remaining work\n    // after fixing the cause, so they re-enter the trip loop below.\n    if (this.resumeFrom && this.resumeFrom.status === \"completed\") {\n      return this.rebuildResumedResult(this.resumeFrom);\n    }\n\n    try {\n      // On resume the conversation is already hydrated from the snapshot,\n      // so skip the (re)build of the initial messages AND the\n      // `agent.starting` emit — those belong to the original run. A fresh\n      // run (resumeFrom absent) takes the normal path unchanged.\n      if (!this.resumeFrom) {\n        await this.buildInitialMessages();\n\n        this.emit(\"agent.starting\", { input: this.input });\n      }\n\n      await this.runTripLoop();\n\n      const parseOutcome = await this.parseOutput();\n\n      if (parseOutcome === \"failed\" && this.resolveRepairAttempts() > 0) {\n        await this.runRepairLoop();\n      }\n    } catch (thrown) {\n      this.error = this.toAIError(thrown);\n    }\n\n    // Terminal checkpoint — persist the final state so a completed-run\n    // resume short-circuits to the stored result, then optionally drop\n    // the snapshot when `deleteOnComplete` is set and the run succeeded.\n    // No-op when `durable` is absent.\n    await this.checkpoint(this.resolveSnapshotStatus());\n\n    if (!this.error && this.config.durable?.deleteOnComplete) {\n      const outcome = await deleteAgentSnapshot({\n        durable: this.config.durable,\n        runId: this.runId,\n      });\n\n      if (!outcome.ok) {\n        this.logger.warn(LOG_MODULE, \"snapshot.delete.failed\", \"durable snapshot delete failed\", {\n          runId: this.runId,\n          error: outcome.error instanceof Error ? outcome.error.message : String(outcome.error),\n        });\n      }\n    }\n\n    return this.buildResult();\n  }\n\n  /**\n   * Resolve the system prompt (string or `SystemPromptContract`), merge\n   * placeholders from config + execute options, inject a structured-output\n   * instruction when the caller wants typed output but the model can't\n   * enforce it natively, prepend any conversation history, and append the\n   * user input. Produces the initial `messages` array the first trip sends\n   * to the model. Runs exactly once per execution.\n   */\n  private async buildInitialMessages(): Promise<void> {\n    const { messages, responseSchema, systemPrompt, promptName, promptVersion } =\n      await buildAgentInputMessages({\n        config: this.config,\n        input: this.input,\n        options: this.options,\n      });\n    this.messages.push(...messages);\n    this.responseSchema = responseSchema;\n    this.systemPrompt = systemPrompt;\n    this.promptName = promptName;\n    this.promptVersion = promptVersion;\n\n    await this.injectSkills();\n  }\n\n  /**\n   * Prepend the skills library's contribution to the system prompt — the\n   * always-injected metadata catalog first, then (only under `inject`) the\n   * preloaded skill bodies, then the developer's resolved system prompt.\n   * Never replaces the developer prompt.\n   *\n   * No-op when no skills library is attached. `catalogPrompt` returns `\"\"`\n   * when nothing is in scope and `preload` returns `[]` when `inject` is\n   * omitted (the default), so the prepend is a no-op in those cases too.\n   *\n   * Awaited inside `buildInitialMessages`, which runs inside `runCore`'s\n   * try/catch — a source/embedder failure funnels into `this.error` like\n   * any other build failure, no new error handling needed.\n   */\n  private async injectSkills(): Promise<void> {\n    const lib = this.config.skillsLib;\n\n    if (!lib) {\n      return;\n    }\n\n    const catalogBlock = await lib.catalogPrompt(this.input);\n    const preloaded = await lib.preload(this.input);\n\n    const blocks: string[] = [];\n\n    if (catalogBlock) {\n      blocks.push(catalogBlock);\n    }\n\n    for (const record of preloaded) {\n      if (record.body) {\n        blocks.push(record.body);\n      }\n    }\n\n    if (blocks.length === 0) {\n      return;\n    }\n\n    const prefix = blocks.join(\"\\n\\n\");\n\n    // Merge in front of the developer's resolved system prompt (captured in\n    // `this.systemPrompt` and mirrored as the leading `role: \"system\"`\n    // message). When the agent had no system prompt, the skills prefix\n    // becomes the system message.\n    const merged = this.systemPrompt ? `${prefix}\\n\\n${this.systemPrompt}` : prefix;\n\n    this.systemPrompt = merged;\n\n    const firstMessage = this.messages[0];\n\n    if (firstMessage?.role === \"system\") {\n      firstMessage.content = merged;\n    } else {\n      this.messages.unshift({ role: \"system\", content: merged });\n    }\n  }\n\n  /**\n   * Drive sequential trips up to `maxTrips`. Each trip may stop the loop\n   * naturally (model returned a non-tool-call finish), abort it (model\n   * threw), or continue it (model requested tools). When the loop exits\n   * after the cap without a natural stop, records a \"Max trips exceeded\"\n   * error so the caller can distinguish runaway tool loops from a real result.\n   */\n  private async runTripLoop(): Promise<void> {\n    // Start at the resumed offset, not 0. On a fresh run `this.trips`\n    // is empty so this is `0` and the loop behaves exactly as before; on\n    // a resume the already-settled trips are skipped entirely — their\n    // model calls and tool dispatches are never re-issued.\n    for (let tripIndex = this.trips.length; tripIndex < this.maxTrips; tripIndex++) {\n      if (this.options?.signal?.aborted) {\n        this.error = this.makeCancelledError();\n        return;\n      }\n\n      const tripInput = tripIndex === 0 ? this.input : \"[tool results]\";\n      const outcome = await this.runTrip(tripIndex, tripInput);\n\n      if (outcome === \"error\" || outcome === \"stop\") {\n        return;\n      }\n    }\n\n    const lastTrip = this.trips[this.trips.length - 1];\n\n    if (lastTrip?.finishReason === \"tool_calls\") {\n      this.error = new AgentMaxTripsError(\"Max trips exceeded\", {\n        maxTrips: this.maxTrips,\n      });\n    }\n  }\n\n  /**\n   * Execute one round-trip to the model. Aggregates usage into the running\n   * total, dispatches any requested tool calls, appends the assistant +\n   * tool-result messages for the next trip, and records an `LLMTrip`.\n   * Returns an outcome that tells `runTripLoop` whether to continue, stop,\n   * or abort.\n   */\n  private async runTrip(\n    tripIndex: number,\n    tripInput: string,\n  ): Promise<\"continue\" | \"stop\" | \"error\"> {\n    this.emit(\"agent.trip.started\", { tripIndex, input: tripInput });\n\n    const tripStartedAt = new Date();\n    const tripStart = performance.now();\n\n    let response: ModelResponse;\n\n    try {\n      response = await this.runTripThroughPipeline(tripIndex);\n    } catch (thrown) {\n      this.error = this.toAIError(thrown);\n\n      const failedTrip: LLMTrip = {\n        index: tripIndex,\n        input: tripInput,\n        output: \"\",\n        finishReason: \"error\",\n        startedAt: tripStartedAt.toISOString(),\n        endedAt: new Date().toISOString(),\n        duration: performance.now() - tripStart,\n        usage: { input: 0, output: 0, total: 0 },\n        error: this.error,\n      };\n\n      this.trips.push(failedTrip);\n\n      this.emit(\"agent.trip.completed\", { trip: failedTrip });\n      this.emit(\"agent.error\", { error: this.error });\n\n      // Persist the failed trip too, so a resume sees it in the ledger\n      // and the terminal checkpoint records the run as `failed`. The\n      // trip's model call already threw — there is no tool side effect to\n      // double-count here. No-op when `durable` is absent.\n      await this.checkpoint(\"failed\");\n\n      return \"error\";\n    }\n\n    // Attach per-trip cost breakdown using the model's pricing table\n    // (when configured). Done at the framework boundary so stored trip\n    // records carry historical cost — Panoptic and other archive\n    // consumers never re-derive against today's pricing, and the\n    // input/output/cached split stays queryable without joining to a\n    // pricing table at all.\n    if (response.usage.cost === undefined) {\n      response.usage.cost = computeCost(response.usage, this.config.model.pricing);\n    }\n\n    // Roll the trip into the agent total via the shared all-channel merge\n    // (was missing reasoningTokens / cacheWriteTokens). `response.usage.cost`\n    // is computed just above, so the cost lane merges identically.\n    mergeUsage(this.usage, response.usage);\n\n    // Fire the `onUsage` hook with a flat, pre-packaged payload so\n    // cost-ledger code receives stable identity (runId, model+provider)\n    // without joining from elsewhere. Awaited but errors swallowed.\n    await this.fireUsageHook(tripIndex, response.usage);\n\n    const isToolCallTrip =\n      response.finishReason === \"tool_calls\" &&\n      response.toolCalls !== undefined &&\n      response.toolCalls.length > 0;\n\n    const tripToolCalls: ToolCall[] = [];\n\n    if (isToolCallTrip) {\n      this.messages.push({\n        role: \"assistant\",\n        content: response.content,\n        toolCalls: response.toolCalls,\n      });\n\n      for (const toolCallRequest of response.toolCalls!) {\n        const record = await this.dispatchToolCall(toolCallRequest, tripIndex);\n\n        tripToolCalls.push(record);\n      }\n    }\n\n    const trip: LLMTrip = {\n      index: tripIndex,\n      input: tripInput,\n      output: response.content,\n      finishReason: response.finishReason,\n      startedAt: tripStartedAt.toISOString(),\n      endedAt: new Date().toISOString(),\n      duration: performance.now() - tripStart,\n      usage: response.usage,\n      toolCalls: tripToolCalls.length > 0 ? tripToolCalls : undefined,\n    };\n\n    this.trips.push(trip);\n\n    this.emit(\"agent.trip.completed\", { trip });\n\n    // Per-trip durable checkpoint. Sits AFTER the trip push + the\n    // `agent.trip.completed` emit and AFTER every tool this trip\n    // requested has been dispatched (the block above) — the only point\n    // where `messages`, `trips`, `toolCalls`, and `usage` are mutually\n    // consistent. Swallow-and-log: a failed checkpoint never aborts the\n    // run, it only loses resume-ability from here. No-op when `durable`\n    // is absent.\n    await this.checkpoint(\"running\");\n\n    if (!isToolCallTrip) {\n      return \"stop\";\n    }\n\n    // Terminate the trip loop when EVERY tool call this trip is\n    // `mode: \"silent\"`. Silent tools don't feed their result back\n    // to the model — the prose the model streamed alongside the\n    // tool call IS the final reply. The \"all\" rule is load-bearing:\n    // if any feedback tool was called too, its result still needs\n    // to round-trip, so we must continue.\n    //\n    // Composite (`asTool`-wrapped) tools never set `mode: \"silent\"`\n    // in v1 — silent-composite mechanics are deferred per plan\n    // 2026-05-07-silent-tools.md (Q4). They behave as feedback.\n    const allSilent = response.toolCalls!.every((request) => {\n      const registered = this.effectiveTools.find((tool) => tool.name === request.name);\n      return registered?.mode === \"silent\";\n    });\n\n    return allSilent ? \"stop\" : \"continue\";\n  }\n\n  /**\n   * Route `getModelResponse` through the `trip`-level middleware\n   * pipeline. `trip.before` hooks can short-circuit the trip by\n   * returning a synthetic `ModelResponse` (semantic cache hit).\n   * `trip.after` hooks can transform the response before the trip\n   * record is built or any tool calls are dispatched. `trip.onError`\n   * hooks can recover from provider failures (fallback chain).\n   */\n  private async runTripThroughPipeline(tripIndex: number): Promise<ModelResponse> {\n    const context: MiddlewareTripContext = {\n      ...this.buildExecuteContext(),\n      tripIndex,\n      messages: this.messages,\n    };\n\n    return (await runPipeline(\n      this.middleware,\n      \"trip\",\n      context,\n      () => this.getModelResponse(tripIndex),\n      this.logger,\n    )) as ModelResponse;\n  }\n\n  /**\n   * Produce the `ModelResponse` for the current trip. In non-streaming\n   * mode, delegates straight to `model.complete()`. In streaming mode,\n   * drains `model.stream()` while emitting `streaming` events per delta\n   * and accumulates the chunks into the same `ModelResponse` shape, so the\n   * rest of the trip pipeline (tool dispatch, trip record, usage\n   * aggregation) stays identical between the two modes.\n   */\n  private async getModelResponse(tripIndex: number): Promise<ModelResponse> {\n    const callOptions = {\n      ...this.config.modelOptions,\n      tools: this.effectiveTools,\n      ...(this.responseSchema ? { responseSchema: this.responseSchema } : {}),\n      ...(this.options?.signal ? { signal: this.options.signal } : {}),\n    };\n\n    if (!this.streamController) {\n      return this.config.model.complete(this.messages, callOptions);\n    }\n\n    let content = \"\";\n    let finishReason: FinishReason = \"stop\";\n    let usage: Usage = { input: 0, output: 0, total: 0 };\n    const toolCalls: ModelToolCallRequest[] = [];\n    const recoveredCalls: ModelToolCallRequest[] = [];\n\n    const guardConfig = this.resolveStreamingToolGuard();\n    const guard = guardConfig\n      ? new JsonStreamGuard({\n          tools: this.effectiveTools as ReadonlyArray<ToolContract<unknown, unknown>>,\n          maxBufferBytes: guardConfig.maxBufferBytes,\n          onSafeDelta: (delta) => {\n            content += delta;\n\n            this.emit(\"agent.trip.streaming\", { delta, tripIndex });\n          },\n          onRecoveredCall: (request) => {\n            recoveredCalls.push(request);\n          },\n        })\n      : undefined;\n\n    for await (const chunk of this.config.model.stream(this.messages, callOptions)) {\n      // Mid-stream abort — break out cleanly instead of continuing to\n      // consume the iterator. The underlying fetch is already\n      // cancelled via `signal` forwarded in callOptions; this covers\n      // adapters that don't honor signal natively and keeps mock\n      // models consistent under cancellation tests.\n      if (this.options?.signal?.aborted) {\n        throw this.makeCancelledError();\n      }\n\n      if (chunk.type === \"delta\") {\n        if (guard) {\n          await guard.feed(chunk.content);\n        } else {\n          content += chunk.content;\n\n          this.emit(\"agent.trip.streaming\", { delta: chunk.content, tripIndex });\n        }\n\n        continue;\n      }\n\n      if (chunk.type === \"tool-call\") {\n        toolCalls.push({\n          id: chunk.id,\n          name: chunk.name,\n          input: chunk.input,\n          ...(chunk.providerMetadata ? { providerMetadata: chunk.providerMetadata } : {}),\n        });\n\n        continue;\n      }\n\n      finishReason = chunk.finishReason;\n      usage = chunk.usage;\n    }\n\n    if (guard) {\n      await guard.finalize();\n    }\n\n    // Dedupe synthesized calls against real ones the provider streamed\n    // structurally — the model occasionally emits BOTH channels for\n    // the same call (real tool-call chunk + narrated JSON envelope).\n    // Real wins; the synthesized duplicate is dropped so dispatch\n    // doesn't run twice. See plan 2026-05-22 §Q5.\n    const dedupedRecovered = recoveredCalls.filter(\n      (recovered) => !isDuplicateToolCall(recovered, toolCalls),\n    );\n\n    const mergedToolCalls = [...toolCalls, ...dedupedRecovered];\n\n    // When the guard recovered any calls but the model reported a\n    // natural `\"stop\"`, override to `\"tool_calls\"` so the agent's\n    // dispatch loop (`runTrip` → `isToolCallTrip`) actually fires.\n    // Without this the guard silently suppresses the leaked JSON but\n    // never dispatches the real action — chips never render.\n    const resolvedFinishReason: FinishReason =\n      dedupedRecovered.length > 0 && finishReason === \"stop\" ? \"tool_calls\" : finishReason;\n\n    return {\n      content,\n      finishReason: resolvedFinishReason,\n      usage,\n      toolCalls: mergedToolCalls.length > 0 ? mergedToolCalls : undefined,\n    };\n  }\n\n  /**\n   * Resolve the effective `streamingToolGuard` for this trip.\n   * Per-call options win over the agent-level config when the key is\n   * explicitly present on options (including the explicit `undefined`\n   * \"disable for this call\" form). Returns `undefined` when no guard\n   * should run.\n   */\n  private resolveStreamingToolGuard(): StreamingToolGuardConfig | undefined {\n    if (\n      this.options !== undefined &&\n      Object.prototype.hasOwnProperty.call(this.options, \"streamingToolGuard\")\n    ) {\n      return this.options.streamingToolGuard;\n    }\n\n    return this.config.streamingToolGuard;\n  }\n\n  /**\n   * Dispatch a single tool call requested by the model. Looks up the tool\n   * by name, invokes it via the safe `ToolContract.invoke` entry, pushes a\n   * matching tool-result message into `this.messages` so the next trip can\n   * see it, and emits the right lifecycle event (`tool-called` on success,\n   * `tool-calling-failed` when the tool is unregistered or invoke returned\n   * an error). Never throws — always returns a `ToolCall` record.\n   */\n  private async dispatchToolCall(\n    toolCallRequest: ModelToolCallRequest,\n    tripIndex: number,\n  ): Promise<ToolCall> {\n    const registeredTool = this.effectiveTools.find((tool) => tool.name === toolCallRequest.name);\n\n    if (!registeredTool) {\n      const error = new AgentExecutionError(`Tool not registered: ${toolCallRequest.name}`, {\n        context: { toolName: toolCallRequest.name, tripIndex },\n      });\n\n      const nowIso = new Date().toISOString();\n\n      const record: ToolCall = {\n        runId: generateRunId(\"tool\"),\n        rootRunId: this.runId,\n        name: toolCallRequest.name,\n        type: \"tool\",\n        status: \"failed\",\n        startedAt: nowIso,\n        endedAt: nowIso,\n        duration: 0,\n        usage: { input: 0, output: 0, total: 0 },\n        children: [],\n        tripIndex,\n        input: toolCallRequest.input,\n        error,\n        ...(toolCallRequest.recoveredFrom ? { recoveredFrom: toolCallRequest.recoveredFrom } : {}),\n      };\n\n      this.toolCalls.push(record);\n\n      this.messages.push({\n        role: \"tool\",\n        toolCallId: toolCallRequest.id,\n        content: JSON.stringify({ error: error.message }),\n      });\n\n      // Stub meta — there's no real tool to describe. Carries the\n      // requested name for log correlation and an explanatory\n      // description so consumers don't see an empty string.\n      this.emit(\"agent.tool.failed\", {\n        tool: {\n          name: toolCallRequest.name,\n          description: \"(unregistered tool — no description available)\",\n        },\n        input: toolCallRequest.input,\n        error,\n        tripIndex,\n      });\n\n      return record;\n    }\n\n    // Build the lightweight event meta once. Resolves `action` to a\n    // string here so consumers receive plain data rather than having\n    // to re-evaluate a callback on every event.\n    const toolMeta: ToolEventMeta = {\n      name: registeredTool.name,\n      description: registeredTool.description,\n      action: resolveToolAction(registeredTool, toolCallRequest.input),\n    };\n\n    this.emit(\"agent.tool.calling\", {\n      tool: toolMeta,\n      input: toolCallRequest.input,\n      tripIndex,\n    });\n\n    const toolContext: MiddlewareToolContext = {\n      ...this.buildExecuteContext(),\n      tripIndex,\n      messages: this.messages,\n      tool: {\n        name: registeredTool.name,\n        description: registeredTool.description,\n        mode: registeredTool.mode,\n      },\n      request: toolCallRequest,\n    };\n\n    // Thread the run's cancellation signal into the ctx handed to the\n    // tool's `invoke`, so composite tools (asTool-wrapped agent/workflow/\n    // supervisor) abort their nested run when the outer agent is cancelled\n    // (C2). The caller's `toolCtx` (artifacts bag, etc.) is preserved — we\n    // only add/override `signal`. With no signal configured we pass\n    // `toolCtx` through unchanged so behavior stays byte-identical.\n    const runSignal = this.options?.signal;\n    const dispatchToolCtx: ToolContext | undefined = runSignal\n      ? {\n          artifacts: this.options?.toolCtx?.artifacts ?? {},\n          ...this.options?.toolCtx,\n          signal: runSignal,\n        }\n      : this.options?.toolCtx;\n\n    let invokeResult: ToolInvokeResult<unknown>;\n\n    try {\n      invokeResult = (await runPipeline(\n        this.middleware,\n        \"tool\",\n        toolContext,\n        () => registeredTool.invoke(toolCallRequest.input, dispatchToolCtx),\n        this.logger,\n      )) as ToolInvokeResult<unknown>;\n    } catch (thrown) {\n      // A `tool`-level middleware hook threw. The real invoke never\n      // throws (it funnels errors into `result.error`), so only a\n      // middleware abort or a bug reaches this branch. Synthesize a\n      // failed-invoke record so the tool-call trace stays consistent.\n      const error = this.toAIError(thrown);\n      const nowIso = new Date().toISOString();\n      const emptyUsage: Usage = { input: 0, output: 0, total: 0 };\n\n      const failedRunId = generateRunId(\"tool\");\n      invokeResult = {\n        error,\n        usage: emptyUsage,\n        report: {\n          runId: failedRunId,\n          rootRunId: failedRunId,\n          name: registeredTool.name,\n          version: registeredTool.version,\n          type: \"tool\",\n          status: \"failed\",\n          startedAt: nowIso,\n          endedAt: nowIso,\n          duration: 0,\n          usage: emptyUsage,\n          children: [],\n        },\n      };\n    }\n\n    // The agent-level ToolCall record merges the tool's own invocation\n    // report with agent-side enrichments (tripIndex, input, output,\n    // error). When the underlying tool was an `asTool`-wrapped\n    // composite, its inner report becomes the sole child of this\n    // ToolCall — preserving the full nested tree while keeping this\n    // node's own `type` as `\"tool\"` (from the agent's POV it *was* a\n    // tool dispatch).\n    const innerReport = invokeResult.report;\n    const isComposite = innerReport.type !== \"tool\";\n\n    const record: ToolCall = {\n      runId: innerReport.runId,\n      rootRunId: this.runId,\n      name: toolCallRequest.name,\n      version: registeredTool.version,\n      type: \"tool\",\n      status: innerReport.status,\n      startedAt: innerReport.startedAt,\n      endedAt: innerReport.endedAt,\n      duration: innerReport.duration,\n      usage: invokeResult.usage,\n      children: isComposite ? [innerReport] : innerReport.children,\n      tripIndex,\n      input: toolCallRequest.input,\n      output: invokeResult.data,\n      error: invokeResult.error,\n      ...(toolCallRequest.recoveredFrom ? { recoveredFrom: toolCallRequest.recoveredFrom } : {}),\n    };\n\n    this.toolCalls.push(record);\n\n    // Roll child usage into the agent's accumulator. Leaf tools\n    // contribute zero; `asTool`-wrapped composites contribute the\n    // full cost of the inner agent/workflow/supervisor run.\n    // All-channel merge so an asTool-wrapped composite that used prompt-cache\n    // or reasoning tokens carries those counts into the parent total too.\n    mergeUsage(this.usage, invokeResult.usage);\n\n    this.messages.push({\n      role: \"tool\",\n      toolCallId: toolCallRequest.id,\n      content: invokeResult.error\n        ? JSON.stringify({ error: invokeResult.error.message })\n        : JSON.stringify(invokeResult.data ?? null),\n    });\n\n    if (invokeResult.error) {\n      this.emit(\"agent.tool.failed\", {\n        tool: toolMeta,\n        input: toolCallRequest.input,\n        error: invokeResult.error,\n        tripIndex,\n      });\n    } else {\n      this.emit(\"agent.tool.called\", { ...record, tool: toolMeta });\n    }\n\n    return record;\n  }\n\n  /**\n   * Parse the final trip output against the user-supplied schema (if any).\n   * Failures populate `this.error` but never throw. Returns an outcome the\n   * caller uses to decide whether self-repair is worth attempting:\n   *\n   * - `\"skipped\"` — no schema, or a prior trip-level error already set\n   *   `this.error` (model crash, max trips). Not repairable; the failure\n   *   isn't a parse problem the model can fix by re-asking.\n   * - `\"failed\"` — schema present, output text either failed JSON.parse\n   *   or failed `~standard.validate`. Repairable via `runRepairLoop`.\n   * - `\"success\"` — parsed and validated; `this.data` populated.\n   *\n   * Under the judge-safe preset (`judge: true`) the JSON extraction is\n   * lenient — it tolerates fenced ` ```json ` blocks plus leading /\n   * trailing prose by slicing the first balanced object / array out of the\n   * response. Never throws regardless of preset: a parse / validation miss\n   * sets `this.error` and returns `\"failed\"`, leaving `this.data`\n   * undefined for the graceful-default path.\n   */\n  private async parseOutput(): Promise<\"success\" | \"failed\" | \"skipped\"> {\n    const schema = this.options?.output ?? this.config.output;\n\n    if (!schema || this.error) {\n      return \"skipped\";\n    }\n\n    const finalTrip = this.trips[this.trips.length - 1];\n    const text = finalTrip?.output ?? \"\";\n\n    if (!text) {\n      return \"skipped\";\n    }\n\n    // Under the judge-safe preset, parse leniently: tolerate fenced blocks\n    // AND surrounding prose by slicing the first balanced JSON object /\n    // array out of the response. Normal agents keep the strict\n    // `extractJsonPayload` (fence-only) so genuine malformations still fail\n    // loudly rather than being papered over.\n    const payload = this.judgeConfig ? extractJsonLenient(text) : extractJsonPayload(text);\n    const sentinel = Symbol(\"parse-failed\");\n    const parsed = safeJsonParse<unknown>(payload, sentinel);\n\n    if (parsed === sentinel) {\n      this.error = new SchemaValidationError(\"Failed to parse model output as JSON\", {\n        context: { text },\n      });\n      return \"failed\";\n    }\n\n    const validation = await (schema as StandardSchemaV1<TOutput>)[\"~standard\"].validate(parsed);\n\n    if (validation.issues) {\n      const summary = validation.issues.map((issue) => issue.message).join(\"; \");\n      this.error = new SchemaValidationError(summary, {\n        issues: validation.issues,\n      });\n      return \"failed\";\n    }\n\n    this.data = validation.value;\n    return \"success\";\n  }\n\n  /**\n   * Resolve how many repair re-asks this run should perform after a parse\n   * failure. Per-call `options.repair` wins when explicitly set (preserving\n   * the existing surface). Otherwise the judge-safe preset supplies its\n   * default attempt count — so `judge: true` enables repair without the\n   * caller also having to pass `repair`. Returns `0` when neither applies,\n   * which leaves the historical \"no repair unless asked\" behavior intact.\n   */\n  private resolveRepairAttempts(): number {\n    if (this.options?.repair) {\n      return this.options.repair.maxAttempts ?? 1;\n    }\n\n    if (this.judgeConfig) {\n      return this.judgeConfig.repairAttempts ?? JUDGE_DEFAULT_REPAIR_ATTEMPTS;\n    }\n\n    return 0;\n  }\n\n  /**\n   * Opt-in self-repair loop for `output` schema failures. Triggered only\n   * when repair attempts remain (`resolveRepairAttempts() > 0`) and\n   * `parseOutput()` returned `\"failed\"`.\n   *\n   * Each attempt:\n   * 1. Pushes the bad assistant response into `this.messages` (so the\n   *    model can see what it just produced).\n   * 2. Pushes a corrective user message naming the validation/parse error.\n   * 3. Runs another trip — counted against the same `maxTrips` cap as\n   *    normal trips so a stuck model can't loop forever.\n   * 4. Re-parses. Stops on success, on a trip-level error, or when\n   *    either `maxAttempts` or `maxTrips` is exhausted.\n   *\n   * Resets `this.error` and `this.data` before each attempt so the final\n   * outcome (success or last failure) is what surfaces to the caller.\n   */\n  private async runRepairLoop(): Promise<void> {\n    const maxAttempts = this.resolveRepairAttempts();\n\n    for (let attempt = 0; attempt < maxAttempts; attempt++) {\n      if (this.trips.length >= this.maxTrips) {\n        return;\n      }\n\n      const lastTrip = this.trips[this.trips.length - 1];\n      const badResponse = lastTrip?.output ?? \"\";\n      const failureReason = this.error?.message ?? \"unknown validation failure\";\n\n      this.error = undefined;\n      this.data = undefined;\n\n      this.messages.push({ role: \"assistant\", content: badResponse });\n\n      this.messages.push({\n        role: \"user\",\n        content: [\n          `Your previous response failed validation: ${failureReason}.`,\n          \"Respond again with valid JSON only — no prose, no markdown fences, no commentary.\",\n        ].join(\" \"),\n      });\n\n      const tripIndex = this.trips.length;\n\n      this.logger.warn(LOG_MODULE, \"repair.attempting\", \"retrying after validation failure\", {\n        attempt: attempt + 1,\n        maxAttempts,\n        reason: failureReason,\n      });\n\n      const outcome = await this.runTrip(tripIndex, \"[repair attempt]\");\n\n      if (outcome === \"error\") {\n        return;\n      }\n\n      const parseOutcome = await this.parseOutput();\n\n      if (parseOutcome === \"success\") {\n        return;\n      }\n    }\n  }\n\n  /**\n   * Build the final `AgentResult` snapshot from accumulated state\n   * (trips, tool calls, data/error, usage, timing).\n   *\n   * Pure — no side effects. `run()` owns terminal event emission and\n   * stream closure so the post-pipeline result (possibly transformed\n   * or short-circuited by an `execute`-level middleware) is what\n   * flows out to consumers and listeners.\n   *\n   * Trips, tool calls, status, and timing live under `report` so the\n   * root stays focused on the four things callers reach for most:\n   * `data`, `text`, `usage`, `error`.\n   */\n  private buildResult(): AgentResult<TOutput> {\n    const finalTrip = this.trips[this.trips.length - 1];\n    const endedAt = new Date();\n\n    const agentName = this.config.name ?? this.config.model.name;\n    const status: BaseReport[\"status\"] = this.error\n      ? this.error instanceof AgentCancelledError\n        ? \"cancelled\"\n        : \"failed\"\n      : \"completed\";\n\n    const report = {\n      runId: this.runId,\n      rootRunId: this.runId,\n      name: agentName,\n      version: this.config.version,\n      type: \"agent\" as const,\n      status,\n      // Stamp the terminal error onto the report so the observe path — which\n      // sees only the report, never the result envelope — surfaces WHY a\n      // failed/cancelled run ended. Spread conditionally so a completed run\n      // stays byte-for-byte as before.\n      ...(this.error ? { error: this.error } : {}),\n      startedAt: this.startedAt.toISOString(),\n      endedAt: endedAt.toISOString(),\n      duration: performance.now() - this.start,\n      usage: this.usage,\n      children: this.toolCalls,\n      model: {\n        name: this.config.model.name,\n        provider: this.config.model.provider,\n      },\n      trips: this.trips,\n      systemPrompt: this.systemPrompt,\n      // Prompt-version linkage. When the agent resolved a *named* prompt (one\n      // registered in `ai.prompts`), stamp its `name` / `version` so observers\n      // (e.g. Panoptic) can group/filter runs by the exact prompt version that\n      // produced them. Spread conditionally so unnamed / raw-string prompts\n      // leave the report byte-for-byte as before.\n      ...(this.promptName\n        ? { promptName: this.promptName, promptVersion: this.promptVersion }\n        : {}),\n      // Opt-in full-history capture (F2). When `captureMessages` is set,\n      // normalize the real assembled turn array (assistant turns with\n      // toolCalls + tool-result turns) onto the report. Off ⇒ field\n      // absent, so the report is byte-for-byte as before.\n      ...(this.config.captureMessages\n        ? { messages: this.captureMessages() }\n        : {}),\n    };\n\n    // Stamp lineage on the assembled tree exactly once per run.\n    // Rewrites any inner self-roots from composite children to this\n    // run's id, stamps `reportSchemaVersion` on the root, and\n    // propagates `sessionId` to every node.\n    stampReportLineage(report, {\n      rootRunId: this.runId,\n      sessionId: this.options?.sessionId,\n    });\n\n    return {\n      type: \"agent\",\n      data: this.data,\n      text: finalTrip?.output,\n      report,\n      usage: this.usage,\n      error: this.error,\n    };\n  }\n\n  /**\n   * Map the run's terminal outcome to the persisted snapshot status.\n   * A cancelled error reads as `\"cancelled\"`, any other error as\n   * `\"failed\"`, otherwise `\"completed\"`. Mirrors the report-status\n   * mapping in {@link buildResult}.\n   */\n  private resolveSnapshotStatus(): AgentSnapshotStatus {\n    if (!this.error) {\n      return \"completed\";\n    }\n\n    return this.error instanceof AgentCancelledError ? \"cancelled\" : \"failed\";\n  }\n\n  /**\n   * Build and persist an {@link AgentSnapshot} from the current\n   * accumulators. The per-trip and terminal checkpoints both route\n   * through here. Reuses {@link captureMessages} to normalize the live\n   * `Message[]` into JSON-safe form so the snapshot round-trips through\n   * any store backend.\n   *\n   * No-op (returns immediately) when `durable` is absent — the common\n   * non-durable path stays free. A failed persist is logged and\n   * swallowed (never aborts the run), matching the supervisor / workflow\n   * checkpoint policy.\n   */\n  private async checkpoint(status: AgentSnapshotStatus): Promise<void> {\n    if (!this.config.durable) {\n      return;\n    }\n\n    const outcome = await persistAgentSnapshot({\n      durable: this.config.durable,\n      runId: this.runId,\n      agentName: this.config.name ?? this.config.model.name,\n      signature: this.config.signature,\n      version: this.config.version,\n      input: this.input,\n      systemPrompt: this.systemPrompt,\n      responseSchema: this.responseSchema,\n      promptName: this.promptName,\n      promptVersion: this.promptVersion,\n      messages: this.captureMessages() as unknown as Message[],\n      trips: this.trips,\n      toolCalls: this.toolCalls,\n      usage: this.usage,\n      status,\n      startedAt: this.startedAt.toISOString(),\n    });\n\n    if (!outcome.ok) {\n      this.logger.warn(LOG_MODULE, \"snapshot.persist.failed\", \"durable snapshot persist failed\", {\n        runId: this.runId,\n        status,\n        error: outcome.error instanceof Error ? outcome.error.message : String(outcome.error),\n      });\n    }\n  }\n\n  /**\n   * Rebuild the final {@link AgentResult} from a COMPLETED snapshot\n   * WITHOUT re-running anything. Used by the completed-run resume\n   * short-circuit: the persisted trips / tool calls / usage are the\n   * authoritative outcome, so a resume of a settled run re-returns that\n   * outcome idempotently. Re-derives `this.data` from the final trip\n   * output against the schema (cheap, no model call) so the rebuilt\n   * result carries the same structured payload the original produced.\n   *\n   * Only reached for a `completed` snapshot — `failed` / `cancelled`\n   * snapshots re-enter the trip loop to retry the remaining work instead.\n   */\n  private async rebuildResumedResult(_snapshot: AgentSnapshot): Promise<AgentResult<TOutput>> {\n    await this.parseOutput();\n\n    return this.buildResult();\n  }\n\n  /**\n   * Normalize the accumulated runtime `Message[]` into the JSON-safe\n   * {@link CapturedMessage}[] persisted on `AgentReport.messages` (F2).\n   * Flattens `ContentPart[]` content to a string, and forwards\n   * `toolCalls` (assistant turns) / `toolCallId` (tool-result turns)\n   * only when present so the captured shape stays lean. Called only when\n   * `captureMessages` is enabled.\n   */\n  private captureMessages(): CapturedMessage[] {\n    return this.messages.map((message) => {\n      const captured: CapturedMessage = {\n        role: message.role,\n        content:\n          typeof message.content === \"string\"\n            ? message.content\n            : JSON.stringify(message.content),\n      };\n\n      if (message.toolCalls !== undefined) {\n        captured.toolCalls = message.toolCalls;\n      }\n\n      if (message.toolCallId !== undefined) {\n        captured.toolCallId = message.toolCallId;\n      }\n\n      return captured;\n    });\n  }\n\n  /**\n   * Normalize any thrown value into an `AIError`. `AIError` instances\n   * pass through untouched; provider-adapter SDK errors are caught by\n   * the adapter and already arrive typed, so this branch mainly\n   * handles runtime crashes (TypeError, ReferenceError) inside\n   * model.complete / model.stream and non-Error values (`throw \"bad\"`).\n   */\n  private toAIError(thrown: unknown): AIError {\n    if (thrown instanceof AIError) {\n      return thrown;\n    }\n\n    // Classify abort-flavored errors (DOMException \"AbortError\",\n    // node-fetch's `FetchError` with name \"AbortError\", `ERR_CANCELED`\n    // from the OpenAI SDK's axios-ish layer) as cancelled instead of\n    // a generic exec failure so callers can route retries correctly.\n    if (isAbortLike(thrown)) {\n      return this.makeCancelledError();\n    }\n\n    const message = thrown instanceof Error ? thrown.message : String(thrown);\n\n    return new AgentExecutionError(message, { cause: thrown });\n  }\n\n  /**\n   * Build the typed cancelled error that both the trip-loop guard\n   * and the mid-stream guard emit. Captures the abort reason when\n   * one was supplied to `controller.abort(reason)` so logs and\n   * telemetry can see what cancelled the run.\n   */\n  private makeCancelledError(): AgentCancelledError {\n    const reason = this.options?.signal?.reason;\n    const reasonText = reason === undefined ? \"\" : String(reason);\n\n    return new AgentCancelledError(\"agent execution cancelled\", {\n      cause: reason,\n      cancelledAt: new Date().toISOString(),\n      reason: reasonText,\n    });\n  }\n\n  /**\n   * Fire a single event through all three subscription tiers in order\n   * — factory → instance → per-call — and mirror it into the\n   * `StreamController` when streaming is active. A throwing user\n   * handler must never crash the agent, so every dispatch is wrapped\n   * in `safeCall`. Stream events are converted from the internal\n   * `AgentEventMap` payload to the public `StreamEvent` shape because\n   * some of them differ (e.g. the tool-called payload vs stream\n   * event).\n   */\n  private emit<K extends keyof AgentEventMap>(\n    event: K,\n    payload: WithoutIdentity<AgentEventMap[K]>,\n  ): void {\n    // Inject run identity once, here, so every subscription tier and\n    // the stream see it. `rootRunId === runId` for a standalone run;\n    // nested propagation lands in a follow-up.\n    const fullPayload = {\n      ...payload,\n      runId: this.runId,\n      rootRunId: this.runId,\n    } as AgentEventMap[K];\n\n    this.logEvent(event, fullPayload);\n\n    const onError = (error: unknown) => this.surfaceHandlerError(event, error);\n\n    const factoryHandler = this.config.on?.[event] as AgentEventHandler<K> | undefined;\n\n    if (factoryHandler) {\n      safeCall(factoryHandler, fullPayload, onError);\n    }\n\n    const bucket = this.instanceHandlers?.get(event);\n\n    if (bucket) {\n      for (const handler of bucket) {\n        safeCall(handler as AgentEventHandler<K>, fullPayload, onError);\n      }\n    }\n\n    const perCallHandler = this.options?.on?.[event] as AgentEventHandler<K> | undefined;\n\n    if (perCallHandler) {\n      safeCall(perCallHandler, fullPayload, onError);\n    }\n\n    if (this.streamController) {\n      const body = this.toStreamEvent(event, fullPayload);\n\n      if (body) {\n        this.streamController.push({\n          runId: this.runId,\n          rootRunId: this.runId,\n          ...body,\n        });\n      }\n    }\n  }\n\n  /**\n   * Surface an isolated event-handler failure (C5). A throwing user\n   * handler never crashes the agent — that isolation is preserved — but\n   * total silence is the wrong default: a broken `on` handler would\n   * otherwise disappear from production with no signal. Routed to the\n   * structured logger (matching the `onUsage` / `onComplete` policy) and\n   * warned at most once per event type so a hot event can't spam the log.\n   */\n  private surfaceHandlerError(event: keyof AgentEventMap, error: unknown): void {\n    if (this.warnedHandlerEvents.has(event as string)) return;\n    this.warnedHandlerEvents.add(event as string);\n\n    this.logger.warn(LOG_MODULE, \"event.handler.error\", \"an event handler threw and was isolated\", {\n      runId: this.runId,\n      event: event as string,\n      error: error instanceof Error ? error.message : String(error),\n    });\n  }\n\n  /**\n   * Emit a structured log line for a lifecycle event. The action\n   * string mirrors the event name with the `agent.` prefix stripped\n   * (`agent.trip.started` → `trip.started`) so log grep filters and\n   * event handlers read the same vocabulary. Level mapping follows\n   * the convention documented on `@warlock.js/logger`'s `Logger`.\n   */\n  private logEvent<K extends keyof AgentEventMap>(event: K, payload: AgentEventMap[K]): void {\n    const agentName = this.config.name || this.config.model.name;\n    logAgentEvent(\n      this.logger,\n      {\n        module: `${LOG_MODULE}.${agentName}`,\n        maxTrips: this.maxTrips,\n        modelName: this.config.model.name,\n        totalUsage: this.usage,\n        totalDurationMs: performance.now() - this.start,\n        trips: this.trips,\n        toolCalls: this.toolCalls,\n      },\n      event,\n      payload,\n    );\n  }\n\n  private toStreamEvent<K extends keyof AgentEventMap>(\n    event: K,\n    payload: AgentEventMap[K],\n  ): StreamEventBody | undefined {\n    return agentEventToStreamEvent(event, payload);\n  }\n\n  /**\n   * Invoke the `onUsage` hook (when configured) with a flat payload\n   * carrying stable identity. Awaits the handler so async ledger\n   * writes complete before the next trip starts; swallows any throw\n   * so consumer bugs cannot crash the agent. Sync handlers wrapped\n   * via `Promise.resolve()` so the await is safe in either case.\n   */\n  private async fireUsageHook(tripIndex: number, tripUsage: Usage): Promise<void> {\n    const handler = this.config.onUsage;\n    if (!handler) return;\n\n    const event: UsageEvent = {\n      runId: this.runId,\n      tripIndex,\n      model: {\n        name: this.config.model.name,\n        provider: this.config.model.provider,\n      },\n      usage: { ...tripUsage },\n      timestamp: new Date().toISOString(),\n    };\n\n    try {\n      await Promise.resolve(handler(event));\n    } catch (err) {\n      this.logger.warn(LOG_MODULE, \"onUsage.hook.error\", \"onUsage handler threw\", {\n        runId: this.runId,\n        tripIndex,\n        error: err instanceof Error ? err.message : String(err),\n      });\n    }\n  }\n\n  /**\n   * Invoke the `onComplete` hook (when configured) once at the end\n   * of every run. Receives the full `AgentResult` plus pre-extracted\n   * `runId` and `durationMs`. Same swallow-and-log error policy as\n   * `fireUsageHook`.\n   */\n  private async fireCompleteHook(result: AgentResult<TOutput>): Promise<void> {\n    const handler = this.config.onComplete;\n    if (!handler) return;\n\n    const event: CompleteEvent<TOutput> = {\n      result,\n      runId: this.runId,\n      durationMs: performance.now() - this.start,\n    };\n\n    try {\n      await Promise.resolve(handler(event));\n    } catch (err) {\n      this.logger.warn(LOG_MODULE, \"onComplete.hook.error\", \"onComplete handler threw\", {\n        runId: this.runId,\n        error: err instanceof Error ? err.message : String(err),\n      });\n    }\n  }\n}\n\n/**\n * Decide whether a guard-synthesized tool call duplicates a real one\n * the provider already streamed structurally. Match key is\n * `name + key-sorted JSON of input` so identical calls (regardless of\n * argument key order) collapse, but two legitimate calls to the same\n * tool with different inputs still both dispatch.\n */\nfunction isDuplicateToolCall(\n  recovered: ModelToolCallRequest,\n  realCalls: ReadonlyArray<ModelToolCallRequest>,\n): boolean {\n  const recoveredKey = `${recovered.name}|${stableStringify(recovered.input)}`;\n\n  for (const real of realCalls) {\n    const realKey = `${real.name}|${stableStringify(real.input)}`;\n\n    if (realKey === recoveredKey) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\n/**\n * `JSON.stringify` variant that sorts object keys at every nesting\n * level so structurally-equal inputs serialize to identical strings.\n * Used only for dedupe-key comparison; never surfaces to consumers.\n */\nfunction stableStringify(value: unknown): string {\n  return JSON.stringify(value, (_key, val) => {\n    if (val !== null && typeof val === \"object\" && !Array.isArray(val)) {\n      const source = val as Record<string, unknown>;\n      const sorted: Record<string, unknown> = {};\n\n      for (const key of Object.keys(source).sort()) {\n        sorted[key] = source[key];\n      }\n\n      return sorted;\n    }\n\n    return val;\n  });\n}\n\n/**\n * Invoke a user-supplied event handler without letting exceptions\n * escape the agent. A throw is isolated (it never crashes the agent)\n * but no longer silent: the optional `onError` surfaces it — the agent\n * routes it to its structured logger, matching the swallow-and-log\n * policy of the `onUsage` / `onComplete` hooks (C5).\n */\nfunction safeCall<T>(\n  handler: (payload: T) => void,\n  payload: T,\n  onError?: (error: unknown) => void,\n): void {\n  try {\n    handler(payload);\n  } catch (error) {\n    onError?.(error);\n  }\n}\n\n/**\n * Resolve a tool's `action` declaration into a plain string for\n * inclusion in `ToolEventMeta`. Static strings pass through;\n * function-shaped actions are invoked with the model's raw,\n * pre-validation input (before `execute`'s schema validation runs).\n *\n * Defensive: if the user's callback throws, swallow and return\n * `undefined` rather than crashing the agent — UI strings are not\n * worth aborting an LLM dispatch over.\n */\nfunction resolveToolAction(\n  tool: ToolContract<unknown, unknown>,\n  input: unknown,\n): string | undefined {\n  if (tool.action === undefined) return undefined;\n  if (typeof tool.action === \"string\") return tool.action;\n  try {\n    return tool.action(input);\n  } catch {\n    return undefined;\n  }\n}\n"],"mappings":"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"}