zidane 5.6.11 → 5.6.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -2
- package/dist/{agent-C9AKTU_V.d.ts → agent-ClkpElCZ.d.ts} +540 -55
- package/dist/agent-ClkpElCZ.d.ts.map +1 -0
- package/dist/chat.d.ts +47 -17
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +3 -3
- package/dist/{index-6f4T7Gc0.d.ts → index-CTDMMdIy.d.ts} +348 -3
- package/dist/index-CTDMMdIy.d.ts.map +1 -0
- package/dist/{index-DPN7TcXK.d.ts → index-v3Tzobqr.d.ts} +2 -2
- package/dist/{index-DPN7TcXK.d.ts.map → index-v3Tzobqr.d.ts.map} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +169 -8
- package/dist/index.js.map +1 -1
- package/dist/{login-BindcfKi.js → login-DS3sf6b5.js} +4 -4
- package/dist/{login-BindcfKi.js.map → login-DS3sf6b5.js.map} +1 -1
- package/dist/{mcp-0jRkIV0g.js → mcp-DGeB7-3D.js} +13 -2
- package/dist/mcp-DGeB7-3D.js.map +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-BfmXLDT4.js → messages-Dym8S_YH.js} +303 -8
- package/dist/messages-Dym8S_YH.js.map +1 -0
- package/dist/{presets-CmzMeWg2.js → presets-CZXS_87d.js} +2 -2
- package/dist/{presets-CmzMeWg2.js.map → presets-CZXS_87d.js.map} +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/{providers-C_ahnRBS.js → providers-beXyD9W9.js} +137 -21
- package/dist/providers-beXyD9W9.js.map +1 -0
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +3 -3
- package/dist/restate.d.ts +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-PUzXZlG6.js → session-BRIsmBSY.js} +5 -2
- package/dist/session-BRIsmBSY.js.map +1 -0
- package/dist/session.d.ts +2 -2
- package/dist/session.js +3 -3
- package/dist/skills.d.ts +2 -2
- package/dist/{tools-CxOfTt3R.js → tools-DE9pR_NG.js} +515 -116
- package/dist/tools-DE9pR_NG.js.map +1 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-DDCHSDdX.d.ts → transcript-anchors-D0TR6djV.d.ts} +4 -4
- package/dist/transcript-anchors-D0TR6djV.d.ts.map +1 -0
- package/dist/tui.d.ts +2 -2
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +12 -8
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-CxE8BBau.js → turn-operations-6Yls2HuG.js} +907 -42
- package/dist/turn-operations-6Yls2HuG.js.map +1 -0
- package/dist/types-oKPBdCmL.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/docs/ARCHITECTURE.md +101 -20
- package/docs/CHAT.md +27 -5
- package/docs/RESTATE.md +1 -1
- package/docs/SKILL.md +39 -3
- package/package.json +5 -2
- package/dist/agent-C9AKTU_V.d.ts.map +0 -1
- package/dist/index-6f4T7Gc0.d.ts.map +0 -1
- package/dist/mcp-0jRkIV0g.js.map +0 -1
- package/dist/messages-BfmXLDT4.js.map +0 -1
- package/dist/providers-C_ahnRBS.js.map +0 -1
- package/dist/session-PUzXZlG6.js.map +0 -1
- package/dist/tools-CxOfTt3R.js.map +0 -1
- package/dist/transcript-anchors-DDCHSDdX.d.ts.map +0 -1
- package/dist/turn-operations-CxE8BBau.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["prefixed"],"sources":["../src/logger.ts","../src/metrics.ts","../src/run-summary.ts","../src/tracing.ts","../src/zod.ts"],"sourcesContent":["/**\n * Structured logging primitive with auto-correlation.\n *\n * Every consumer of the harness used to roll its own logger; the TUI has\n * one, downstream apps have theirs, observability backends each want a\n * slightly different shape. This module ships the smallest reasonable\n * shared surface:\n *\n * - {@link Logger} — minimal level-tagged interface (`debug` / `info` /\n * `warn` / `error`) plus a `with(...)` method that returns a child\n * logger carrying baseline attributes (`runId`, `turnId`, `callId`,\n * `childId`, `depth`, anything you want).\n * - {@link createLogger} — builds a Logger from a {@link LogSink}.\n * - {@link consoleSink} / {@link jsonSink} — built-in sinks that cover\n * the common cases (human-readable to a terminal vs. one JSON object\n * per line to a log aggregator).\n * - {@link createLoggingHooks} — installs Logger-attached hook handlers\n * on an agent so every lifecycle event lands on the sink with the\n * right correlation ids already stamped in.\n *\n * Hosts that already own a logger (pino, winston, structlog binding,\n * platform-native) plug it in via a custom {@link LogSink} — the helper\n * itself does no I/O.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\n\n// ---------------------------------------------------------------------------\n// Core types\n// ---------------------------------------------------------------------------\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\nexport interface LogRecord {\n level: LogLevel\n /** Unix ms — set by `Logger` at emit time. */\n timestamp: number\n /** Free-form message. Sinks render this as the human-facing line. */\n message: string\n /** Structured fields. Correlation ids land here automatically. */\n attrs: Record<string, unknown>\n}\n\nexport interface LogSink {\n emit: (record: LogRecord) => void\n}\n\nexport interface Logger {\n debug: (message: string, attrs?: Record<string, unknown>) => void\n info: (message: string, attrs?: Record<string, unknown>) => void\n warn: (message: string, attrs?: Record<string, unknown>) => void\n error: (message: string, attrs?: Record<string, unknown>) => void\n /**\n * Returns a child logger that prepends the given attributes onto every\n * subsequent emit. Equivalent to `pino.child` / `winston.child`. The\n * parent and child share the same sink — children are zero-cost.\n */\n with: (extra: Record<string, unknown>) => Logger\n /**\n * Inspectable baseline attributes — handy for tests and for hook\n * handlers that want to clone-with-extra without recursing.\n */\n readonly baseAttributes: Readonly<Record<string, unknown>>\n}\n\n// ---------------------------------------------------------------------------\n// createLogger\n// ---------------------------------------------------------------------------\n\n/**\n * Build a Logger from a sink. Stateless and cheap; create one per agent\n * (or per app) and use `.with()` to attach correlation ids per-call.\n */\nexport function createLogger(\n sink: LogSink,\n baseAttributes: Readonly<Record<string, unknown>> = {},\n): Logger {\n function emit(level: LogLevel, message: string, attrs?: Record<string, unknown>): void {\n try {\n sink.emit({\n level,\n timestamp: Date.now(),\n message,\n attrs: attrs ? { ...baseAttributes, ...attrs } : { ...baseAttributes },\n })\n }\n catch {\n // Sinks should never crash the run.\n }\n }\n\n return {\n debug: (m, a) => emit('debug', m, a),\n info: (m, a) => emit('info', m, a),\n warn: (m, a) => emit('warn', m, a),\n error: (m, a) => emit('error', m, a),\n with: extra => createLogger(sink, { ...baseAttributes, ...extra }),\n baseAttributes,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Built-in sinks\n// ---------------------------------------------------------------------------\n\nexport interface ConsoleSinkOptions {\n /**\n * Minimum level to emit. Defaults to `'info'` — `debug` is dropped so\n * the harness's lifecycle logging is not noisy by default. Set to\n * `'debug'` to see every event.\n */\n minLevel?: LogLevel\n /** Custom output stream. Defaults to `process.stderr` so logs don't pollute stdout. */\n stream?: { write: (chunk: string) => void }\n}\n\nconst LEVEL_ORDER: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n}\n\n/**\n * Human-readable terminal sink. Renders each record as\n * `<ISO timestamp> <LEVEL> <message> <attrs as kv pairs>`.\n *\n * Honors `process.stderr` by default so log lines don't interleave with\n * the agent's stdout-bound output (chat responses, JSON results).\n */\nexport function consoleSink(options: ConsoleSinkOptions = {}): LogSink {\n const min = LEVEL_ORDER[options.minLevel ?? 'info']\n const stream = options.stream ?? process.stderr\n return {\n emit(record) {\n if (LEVEL_ORDER[record.level] < min)\n return\n const ts = new Date(record.timestamp).toISOString()\n const kv = Object.entries(record.attrs)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => `${k}=${typeof v === 'string' ? v : JSON.stringify(v)}`)\n .join(' ')\n stream.write(`${ts} ${record.level.toUpperCase().padEnd(5)} ${record.message}${kv ? ` ${kv}` : ''}\\n`)\n },\n }\n}\n\n/**\n * One-JSON-object-per-line sink. Suitable for piping into log aggregators\n * (Datadog Agent, Fluent Bit, Loki, Vector) that expect JSONL.\n */\nexport function jsonSink(options: ConsoleSinkOptions = {}): LogSink {\n const min = LEVEL_ORDER[options.minLevel ?? 'info']\n const stream = options.stream ?? process.stderr\n return {\n emit(record) {\n if (LEVEL_ORDER[record.level] < min)\n return\n try {\n stream.write(`${JSON.stringify(record)}\\n`)\n }\n catch {\n // Non-serializable attr (cycle, BigInt, etc.). Skip rather than throw.\n }\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// createLoggingHooks\n// ---------------------------------------------------------------------------\n\nexport interface LoggingHooksOptions {\n logger: Logger\n /**\n * Minimum interesting level for harness-emitted lines. Default `'info'`.\n * Set to `'debug'` to see every tool dispatch / stream event. Set to\n * `'warn'` to mute the chatty ones and only see failures + budgets.\n */\n level?: LogLevel\n /**\n * When true (default), lifecycle events (`agent:start`, `turn:before`,\n * `tool:before`, `mcp:bootstrap:start`) emit at `debug` level so they\n * stay quiet by default. Set false to mute them entirely regardless of\n * the configured minimum level — useful when piping into a tracer\n * that already captures lifecycle.\n */\n includeLifecycle?: boolean\n}\n\nexport interface LoggingHookSet {\n install: (hooks: Hookable<AgentHooks>) => () => void\n}\n\n/**\n * Install a bundle of hook handlers that emit a structured line per\n * relevant lifecycle event, automatically attaching correlation ids\n * (`runId`, `turnId`, `callId`, `childId`, `depth`, `agentName`).\n *\n * @example\n * ```ts\n * const logger = createLogger(consoleSink({ minLevel: 'debug' }), { service: 'tui' })\n * const lh = createLoggingHooks({ logger })\n * const uninstall = lh.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n */\nexport function createLoggingHooks(options: LoggingHooksOptions): LoggingHookSet {\n const root = options.logger\n const includeLifecycle = options.includeLifecycle ?? true\n const minLevel = LEVEL_ORDER[options.level ?? 'info']\n\n /**\n * Wrap a Logger so emissions below `minLevel` are dropped before they\n * hit the sink. Lets us gate harness chatter without forcing every\n * `LogSink` to re-implement level filtering. Preserves `.with()`\n * composition (children inherit the filter).\n */\n function gateLevel(logger: Logger): Logger {\n const skip = (level: LogLevel): boolean => LEVEL_ORDER[level] < minLevel\n const wrap = (l: Logger): Logger => ({\n debug: (m, a) => {\n if (!skip('debug'))\n l.debug(m, a)\n },\n info: (m, a) => {\n if (!skip('info'))\n l.info(m, a)\n },\n warn: (m, a) => {\n if (!skip('warn'))\n l.warn(m, a)\n },\n error: (m, a) => {\n if (!skip('error'))\n l.error(m, a)\n },\n with: extra => wrap(l.with(extra)),\n baseAttributes: l.baseAttributes,\n })\n return wrap(logger)\n }\n\n return {\n install(hooks: Hookable<AgentHooks>): () => void {\n const unregisters: Array<() => void> = []\n // Per-install loggers, refreshed on `agent:start` / `turn:before`.\n // Scoped INSIDE install() so concurrent installs across different\n // agents don't share state — each call to `install()` gets its own\n // attribution state.\n const gatedRoot = gateLevel(root)\n let runLogger = gatedRoot\n let turnLogger = gatedRoot\n\n // ---- Agent lifecycle --------------------------------------------\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n runLogger = gatedRoot.with({\n runId: ctx.runId,\n ...(ctx.parentRunId ? { parentRunId: ctx.parentRunId } : {}),\n depth: ctx.depth,\n ...(ctx.agentName ? { agentName: ctx.agentName } : {}),\n })\n turnLogger = runLogger\n if (includeLifecycle)\n runLogger.debug('agent run started')\n }))\n\n unregisters.push(hooks.hook('agent:done', (stats) => {\n runLogger.info('agent run completed', {\n turns: stats.turns,\n totalIn: stats.totalIn,\n totalOut: stats.totalOut,\n ...(typeof stats.cost === 'number' ? { cost: stats.cost } : {}),\n elapsedMs: stats.elapsed,\n ...(typeof stats.timeTillFirstTokenMs === 'number' ? { ttftMs: stats.timeTillFirstTokenMs } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n runLogger.warn('agent run aborted')\n }))\n\n // ---- Turn / stream ---------------------------------------------\n\n unregisters.push(hooks.hook('turn:before', (ctx) => {\n turnLogger = runLogger.with({ turnId: ctx.turnId, turn: ctx.turn })\n if (includeLifecycle)\n turnLogger.debug('turn started')\n }))\n\n unregisters.push(hooks.hook('turn:after', (ctx) => {\n turnLogger.debug('turn ended', {\n inputTokens: ctx.usage.input,\n outputTokens: ctx.usage.output,\n ...(ctx.usage.finishReason ? { finishReason: ctx.usage.finishReason } : {}),\n ...(ctx.usage.modelId ? { modelId: ctx.usage.modelId } : {}),\n ...(typeof ctx.usage.timeToFirstTokenMs === 'number' ? { ttftMs: ctx.usage.timeToFirstTokenMs } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n turnLogger.error('stream error', {\n message: ctx.err instanceof Error ? ctx.err.message : String(ctx.err),\n ...(ctx.statusCode !== undefined ? { statusCode: ctx.statusCode } : {}),\n ...(ctx.requestId !== undefined ? { requestId: ctx.requestId } : {}),\n })\n }))\n\n // ---- Tool calls -------------------------------------------------\n\n unregisters.push(hooks.hook('tool:before', (ctx) => {\n if (!includeLifecycle)\n return\n turnLogger.debug('tool started', {\n toolName: ctx.name,\n displayName: ctx.displayName,\n callId: ctx.callId,\n })\n }))\n\n unregisters.push(hooks.hook('tool:after', (ctx) => {\n if (!includeLifecycle)\n return\n turnLogger.debug('tool ended', {\n toolName: ctx.name,\n callId: ctx.callId,\n outputBytes: ctx.outputBytes,\n })\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n turnLogger.error('tool error', {\n toolName: ctx.name,\n callId: ctx.callId,\n message: ctx.error.message,\n })\n }))\n\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n // Successful + gate-substitute paths are routine — only log at\n // debug level. The three \"something refused / went wrong\" paths\n // (gate-block, unknown tool, invalid input) land at warn so they\n // surface in default-config dashboards without forcing debug.\n const isAnomaly = ctx.outcome === 'gate-block'\n || ctx.outcome === 'unknown'\n || ctx.outcome === 'invalid-input'\n if (!isAnomaly && !includeLifecycle)\n return\n const lvl: LogLevel = isAnomaly ? 'warn' : 'debug'\n turnLogger[lvl]('tool dispatched', {\n toolName: ctx.name,\n callId: ctx.callId,\n outcome: ctx.outcome,\n ...(ctx.reason ? { reason: ctx.reason } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n turnLogger.warn('tool input rejected', {\n toolName: ctx.name,\n callId: ctx.callId,\n reason: ctx.reason,\n })\n }))\n\n // ---- Budgets ----------------------------------------------------\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n turnLogger.warn('byte budget exceeded', {\n bytes: ctx.bytes,\n budget: ctx.budget,\n })\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n turnLogger.warn('tool budget exceeded', {\n toolName: ctx.tool,\n count: ctx.count,\n max: ctx.max,\n mode: ctx.mode,\n })\n }))\n\n // ---- MCP --------------------------------------------------------\n\n unregisters.push(hooks.hook('mcp:bootstrap:end', (ctx) => {\n if (ctx.ok) {\n if (includeLifecycle) {\n runLogger.debug('mcp bootstrap ok', {\n server: ctx.name,\n transport: ctx.transport,\n durationMs: ctx.durationMs,\n toolCount: ctx.toolCount,\n ...(ctx.lazy ? { lazy: true } : {}),\n ...(ctx.cached ? { cached: true } : {}),\n })\n }\n }\n else {\n runLogger.warn('mcp bootstrap failed', {\n server: ctx.name,\n transport: ctx.transport,\n durationMs: ctx.durationMs,\n message: ctx.error.message,\n })\n }\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n runLogger.error('mcp error', {\n server: ctx.name,\n message: ctx.error.message,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:auth:required', (ctx) => {\n runLogger.warn('mcp auth required', {\n server: ctx.name,\n transport: ctx.transport,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n turnLogger.error('mcp tool error', {\n server: ctx.server,\n tool: ctx.displayName,\n callId: ctx.callId,\n message: ctx.error.message,\n })\n }))\n\n // ---- Spawn ------------------------------------------------------\n\n unregisters.push(hooks.hook('spawn:before', (ctx) => {\n if (!includeLifecycle)\n return\n runLogger.debug('spawn started', {\n childId: ctx.id,\n depth: ctx.depth,\n })\n }))\n\n unregisters.push(hooks.hook('spawn:complete', (ctx) => {\n runLogger.info('spawn completed', {\n childId: ctx.id,\n ...(ctx.depth ? { depth: ctx.depth } : {}),\n status: ctx.status ?? 'completed',\n turns: ctx.stats.turns,\n totalIn: ctx.stats.totalIn,\n totalOut: ctx.stats.totalOut,\n ...(typeof ctx.stats.cost === 'number' ? { cost: ctx.stats.cost } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('spawn:error', (ctx) => {\n runLogger.error('spawn error', {\n childId: ctx.id,\n ...(ctx.depth ? { depth: ctx.depth } : {}),\n message: ctx.error.message,\n })\n }))\n\n // -----------------------------------------------------------------\n // Disposal\n // -----------------------------------------------------------------\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n }\n },\n }\n}\n","/**\n * Metrics helper — wraps agent lifecycle hooks in caller-provided meters.\n *\n * Symmetric with {@link createTracingHooks} from `./tracing`. Where the\n * tracer answers \"what happened in this run?\" by producing spans, the\n * metrics helper answers \"how is the fleet behaving?\" by emitting\n * counters / histograms / up-down counters. Both are tracer-agnostic —\n * the caller supplies a `Meter` (OTel-API-shaped) and the helper plugs\n * the right hook on each metric.\n *\n * Instrument families:\n *\n * ### Histograms (distribution metrics)\n * - `gen_ai.client.operation.duration` (ms) — wall-clock per turn (`turn:before` → `turn:after`).\n * - `gen_ai.client.token.usage` — `input` / `output` token counts per turn (tagged by `gen_ai.token.type`).\n * - `gen_ai.client.time_to_first_token` (ms) — per-turn TTFT from `TurnUsage.timeToFirstTokenMs`.\n * - `gen_ai.tool.duration` (ms) — native tool execution time (`tool:before` → `tool:after`).\n * - `gen_ai.tool.output_bytes` — `tool:after.outputBytes`.\n * - `gen_ai.mcp.tool.duration` (ms) — MCP tool execution time.\n * - `gen_ai.mcp.bootstrap.duration` (ms) — MCP server bootstrap (already measured by harness).\n *\n * ### Counters (monotonic)\n * - `gen_ai.agent.runs` — `agent:start`.\n * - `gen_ai.agent.runs.completed` — `agent:done`.\n * - `gen_ai.agent.aborts` — `agent:abort`.\n * - `gen_ai.tool.calls` — `tool:dispatched` (tagged by `outcome`).\n * - `gen_ai.tool.errors` — `tool:error`.\n * - `gen_ai.mcp.tool.errors` — `mcp:tool:error`.\n * - `gen_ai.stream.errors` — `stream:error` (tagged by `status_code`).\n * - `gen_ai.validation.rejects` — `validation:reject`.\n * - `gen_ai.validation.coercions` — `validation:coerce`.\n * - `gen_ai.gate.blocks` — `tool:dispatched` outcome `gate-block`.\n * - `gen_ai.budget.exceeded` — `budget:exceeded`.\n * - `gen_ai.tool_budget.exceeded` — `tool-budget:exceeded` (tagged by `mode`).\n * - `gen_ai.pairing.repairs` — `pairing:repair` (tagged by `mode`).\n * - `gen_ai.oauth.refreshes` — `oauth:refresh`.\n * - `gen_ai.mcp.errors` — `mcp:error`.\n * - `gen_ai.mcp.auth.required` — `mcp:auth:required`.\n * - `gen_ai.cost_usd` — accumulated cost (added on `turn:after`).\n *\n * ### Up-down counter (gauge-shaped)\n * - `gen_ai.agent.runs.active` — incremented on `agent:start`, decremented on `agent:done`.\n *\n * The `Meter` shape mirrors `@opentelemetry/api`'s `Meter` exactly — passing\n * `metrics.getMeter('zidane')` works directly. Hosts using a different\n * metrics backend (StatsD, Prometheus client, custom in-memory aggregator)\n * implement the same three factory methods and the helper plugs in\n * unchanged.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\n\n// ---------------------------------------------------------------------------\n// OTel-shaped Meter interface\n// ---------------------------------------------------------------------------\n\nexport type MetricAttributes = Record<string, string | number | boolean | undefined>\n\nexport interface Counter {\n add: (value: number, attributes?: MetricAttributes) => void\n}\n\nexport interface Histogram {\n record: (value: number, attributes?: MetricAttributes) => void\n}\n\nexport interface UpDownCounter {\n add: (value: number, attributes?: MetricAttributes) => void\n}\n\nexport interface InstrumentOptions {\n description?: string\n unit?: string\n}\n\n/**\n * Minimal Meter interface — structurally identical to OTel's `Meter`.\n * Hosts passing `metrics.getMeter(name)` (from `@opentelemetry/api`)\n * satisfy this without adaptation.\n */\nexport interface Meter {\n createCounter: (name: string, options?: InstrumentOptions) => Counter\n createHistogram: (name: string, options?: InstrumentOptions) => Histogram\n createUpDownCounter: (name: string, options?: InstrumentOptions) => UpDownCounter\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface MetricsHooksOptions {\n meter: Meter\n /**\n * Optional prefix prepended to every instrument name. Default: no prefix\n * (instrument names follow OTel Gen AI semantic conventions verbatim,\n * which is the most-portable shape). Set to e.g. `'zidane.'` to\n * namespace inside a shared meter registry.\n */\n namespace?: string\n /**\n * Optional baseline attributes applied to every measurement. Typical\n * use: `{ service: 'tui', env: 'prod' }`. Per-event attributes win on\n * key collision.\n */\n baseAttributes?: MetricAttributes\n /**\n * Error sink for meter failures. The helper still swallows the throw\n * so a broken backend can't crash a run; this callback surfaces the\n * failure for ops dashboards.\n */\n onError?: (kind: string, err: unknown) => void\n}\n\nexport interface MetricsHookSet {\n install: (hooks: Hookable<AgentHooks>) => () => void\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction prefixed(prefix: string | undefined, name: string): string {\n return prefix ? `${prefix}${name}` : name\n}\n\n/**\n * Drop `undefined` entries before handing attributes to a metrics\n * backend. OTel's `@opentelemetry/api` rejects `undefined` attribute\n * values (and Prometheus / StatsD adapters silently mis-label them); the\n * helper's own API surface allows `undefined` for ergonomic call sites\n * (`{...(cond ? { foo } : {})}`-style spreads), so we strip here at the\n * boundary.\n */\nfunction mergeAttrs(\n base: MetricAttributes | undefined,\n extra: MetricAttributes,\n): Record<string, string | number | boolean> {\n const out: Record<string, string | number | boolean> = {}\n if (base) {\n for (const [k, v] of Object.entries(base)) {\n if (v !== undefined)\n out[k] = v\n }\n }\n for (const [k, v] of Object.entries(extra)) {\n if (v !== undefined)\n out[k] = v\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// createMetricsHooks\n// ---------------------------------------------------------------------------\n\n/**\n * Build a set of metrics hook handlers that can be installed on an agent.\n *\n * @example OpenTelemetry\n * ```ts\n * import { metrics } from '@opentelemetry/api'\n * const meter = metrics.getMeter('zidane')\n * const m = createMetricsHooks({ meter, baseAttributes: { service: 'tui' } })\n * const uninstall = m.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n */\nexport function createMetricsHooks(options: MetricsHooksOptions): MetricsHookSet {\n const ns = options.namespace\n const base = options.baseAttributes\n const onError = options.onError ?? (() => {})\n\n const make = {\n counter: (name: string, opts?: InstrumentOptions) =>\n safeFactory(() => options.meter.createCounter(prefixed(ns, name), opts), name, onError),\n histogram: (name: string, opts?: InstrumentOptions) =>\n safeFactory(() => options.meter.createHistogram(prefixed(ns, name), opts), name, onError),\n upDown: (name: string, opts?: InstrumentOptions) =>\n safeFactory(() => options.meter.createUpDownCounter(prefixed(ns, name), opts), name, onError),\n }\n\n // Instruments — created once at install time.\n const turnDuration = make.histogram('gen_ai.client.operation.duration', { unit: 'ms', description: 'Per-turn LLM operation duration.' })\n const tokenUsage = make.histogram('gen_ai.client.token.usage', { unit: 'tokens', description: 'Per-turn token usage (tagged by gen_ai.token.type).' })\n const ttft = make.histogram('gen_ai.client.time_to_first_token', { unit: 'ms', description: 'Per-turn time to first token.' })\n const toolDuration = make.histogram('gen_ai.tool.duration', { unit: 'ms', description: 'Native tool execution wall clock.' })\n const toolOutput = make.histogram('gen_ai.tool.output_bytes', { unit: 'By', description: 'Native tool output bytes.' })\n const mcpToolDuration = make.histogram('gen_ai.mcp.tool.duration', { unit: 'ms', description: 'MCP tool execution wall clock.' })\n const mcpBootstrapDuration = make.histogram('gen_ai.mcp.bootstrap.duration', { unit: 'ms', description: 'MCP server bootstrap duration.' })\n\n const runsStarted = make.counter('gen_ai.agent.runs', { description: 'Runs started.' })\n const runsCompleted = make.counter('gen_ai.agent.runs.completed', { description: 'Runs completed.' })\n const aborts = make.counter('gen_ai.agent.aborts', { description: 'Run abort events.' })\n const toolCalls = make.counter('gen_ai.tool.calls', { description: 'Tool dispatches (tagged by outcome).' })\n const toolErrors = make.counter('gen_ai.tool.errors', { description: 'Native tool errors.' })\n const mcpToolErrors = make.counter('gen_ai.mcp.tool.errors', { description: 'MCP tool errors.' })\n const streamErrors = make.counter('gen_ai.stream.errors', { description: 'Provider stream errors.' })\n const validationRejects = make.counter('gen_ai.validation.rejects', { description: 'Tool input validation rejects.' })\n const validationCoercions = make.counter('gen_ai.validation.coercions', { description: 'Tool input auto-coercions.' })\n const gateBlocks = make.counter('gen_ai.gate.blocks', { description: 'Tool-gate refusals.' })\n const budgetExceeded = make.counter('gen_ai.budget.exceeded', { description: 'Per-turn byte budget exceedances.' })\n const toolBudgetExceeded = make.counter('gen_ai.tool_budget.exceeded', { description: 'Per-tool dispatch budget exceedances.' })\n const pairingRepairs = make.counter('gen_ai.pairing.repairs', { description: 'Tool-pairing repair counts.' })\n const oauthRefreshes = make.counter('gen_ai.oauth.refreshes', { description: 'Provider OAuth credential refreshes.' })\n const mcpErrors = make.counter('gen_ai.mcp.errors', { description: 'MCP server errors.' })\n const mcpAuthRequired = make.counter('gen_ai.mcp.auth.required', { description: 'MCP bootstrap blocked on missing auth.' })\n const costMeter = make.counter('gen_ai.cost_usd', { unit: 'USD', description: 'Cumulative cost across turns.' })\n\n const runsActive = make.upDown('gen_ai.agent.runs.active', { description: 'In-flight agent runs.' })\n\n return {\n install(hooks: Hookable<AgentHooks>): () => void {\n // Per-instrument book-keeping. Each map tracks instrument start times\n // keyed by the identifier that distinguishes concurrent in-flight\n // instances of that kind.\n const turnStart = new Map<string, number>() // turnId -> startedAt\n const toolStart = new Map<string, number>() // callId -> startedAt\n const mcpStart = new Map<string, number>() // callId -> startedAt\n\n const unregisters: Array<() => void> = []\n\n const record = <T extends { record: (v: number, a?: MetricAttributes) => void }>(\n instrument: T | undefined,\n value: number,\n attrs: MetricAttributes,\n kind: string,\n ): void => {\n if (!instrument)\n return\n try {\n instrument.record(value, mergeAttrs(base, attrs))\n }\n catch (err) {\n try {\n onError(kind, err)\n }\n catch { /* ignore */ }\n }\n }\n\n const add = <T extends { add: (v: number, a?: MetricAttributes) => void }>(\n instrument: T | undefined,\n value: number,\n attrs: MetricAttributes,\n kind: string,\n ): void => {\n if (!instrument)\n return\n try {\n instrument.add(value, mergeAttrs(base, attrs))\n }\n catch (err) {\n try {\n onError(kind, err)\n }\n catch { /* ignore */ }\n }\n }\n\n // ---- Agent lifecycle --------------------------------------------\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n const attrs: MetricAttributes = {\n 'gen_ai.agent.depth': ctx.depth,\n ...(ctx.agentName ? { 'gen_ai.agent.name': ctx.agentName } : {}),\n }\n add(runsStarted, 1, attrs, 'runs')\n add(runsActive, 1, attrs, 'runs.active')\n }))\n\n unregisters.push(hooks.hook('agent:done', () => {\n add(runsCompleted, 1, {}, 'runs.completed')\n add(runsActive, -1, {}, 'runs.active')\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n add(aborts, 1, {}, 'aborts')\n }))\n\n // ---- Turn / stream metrics -------------------------------------\n\n unregisters.push(hooks.hook('turn:before', (ctx) => {\n turnStart.set(ctx.turnId, Date.now())\n }))\n\n unregisters.push(hooks.hook('turn:after', (ctx) => {\n const started = turnStart.get(ctx.turnId)\n turnStart.delete(ctx.turnId)\n const tagsBase: MetricAttributes = {\n 'gen_ai.operation.name': 'chat',\n ...(ctx.usage.modelId ? { 'gen_ai.response.model': ctx.usage.modelId } : {}),\n ...(ctx.usage.finishReason ? { 'gen_ai.response.finish_reason': ctx.usage.finishReason } : {}),\n }\n if (typeof started === 'number')\n record(turnDuration, Date.now() - started, tagsBase, 'turn.duration')\n if (typeof ctx.usage.timeToFirstTokenMs === 'number')\n record(ttft, ctx.usage.timeToFirstTokenMs, tagsBase, 'ttft')\n\n record(tokenUsage, ctx.usage.input, { ...tagsBase, 'gen_ai.token.type': 'input' }, 'token.input')\n record(tokenUsage, ctx.usage.output, { ...tagsBase, 'gen_ai.token.type': 'output' }, 'token.output')\n if (typeof ctx.usage.cacheRead === 'number' && ctx.usage.cacheRead > 0)\n record(tokenUsage, ctx.usage.cacheRead, { ...tagsBase, 'gen_ai.token.type': 'cache_read' }, 'token.cache_read')\n if (typeof ctx.usage.cacheCreation === 'number' && ctx.usage.cacheCreation > 0)\n record(tokenUsage, ctx.usage.cacheCreation, { ...tagsBase, 'gen_ai.token.type': 'cache_creation' }, 'token.cache_creation')\n\n if (typeof ctx.usage.cost === 'number' && ctx.usage.cost > 0)\n add(costMeter, ctx.usage.cost, tagsBase, 'cost')\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n add(streamErrors, 1, {\n 'http.response.status_code': ctx.statusCode,\n 'error.type': ctx.err instanceof Error ? ctx.err.name : 'unknown',\n }, 'stream.error')\n }))\n\n // ---- Native tool metrics ---------------------------------------\n\n unregisters.push(hooks.hook('tool:before', (ctx) => {\n toolStart.set(ctx.callId, Date.now())\n }))\n\n unregisters.push(hooks.hook('tool:after', (ctx) => {\n const started = toolStart.get(ctx.callId)\n toolStart.delete(ctx.callId)\n const tags: MetricAttributes = { 'gen_ai.tool.name': ctx.name }\n if (typeof started === 'number')\n record(toolDuration, Date.now() - started, tags, 'tool.duration')\n record(toolOutput, ctx.outputBytes, tags, 'tool.output_bytes')\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n toolStart.delete(ctx.callId)\n add(toolErrors, 1, {\n 'gen_ai.tool.name': ctx.name,\n 'error.type': ctx.error.name,\n }, 'tool.errors')\n }))\n\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n add(toolCalls, 1, {\n 'gen_ai.tool.name': ctx.name,\n 'outcome': ctx.outcome,\n }, 'tool.calls')\n if (ctx.outcome === 'gate-block') {\n add(gateBlocks, 1, {\n 'gen_ai.tool.name': ctx.name,\n ...(ctx.reason ? { reason: ctx.reason } : {}),\n }, 'gate.blocks')\n }\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n add(validationRejects, 1, { 'gen_ai.tool.name': ctx.name }, 'validation.rejects')\n }))\n\n unregisters.push(hooks.hook('validation:coerce', (ctx) => {\n add(validationCoercions, 1, {\n 'gen_ai.tool.name': ctx.name,\n 'coercions': ctx.coercions.length,\n }, 'validation.coercions')\n }))\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n add(budgetExceeded, 1, {\n bytes: ctx.bytes,\n budget: ctx.budget,\n }, 'budget.exceeded')\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n add(toolBudgetExceeded, 1, {\n 'gen_ai.tool.name': ctx.tool,\n 'mode': ctx.mode,\n 'count': ctx.count,\n 'max': ctx.max,\n }, 'tool_budget.exceeded')\n }))\n\n unregisters.push(hooks.hook('pairing:repair', (ctx) => {\n add(pairingRepairs, 1, { mode: ctx.mode }, 'pairing.repairs')\n }))\n\n unregisters.push(hooks.hook('oauth:refresh', (ctx) => {\n add(oauthRefreshes, 1, { provider: ctx.provider, source: ctx.source }, 'oauth.refreshes')\n }))\n\n // ---- MCP metrics -----------------------------------------------\n\n unregisters.push(hooks.hook('mcp:tool:before', (ctx) => {\n mcpStart.set(ctx.callId, Date.now())\n }))\n\n unregisters.push(hooks.hook('mcp:tool:after', (ctx) => {\n const started = mcpStart.get(ctx.callId)\n mcpStart.delete(ctx.callId)\n const tags: MetricAttributes = {\n 'gen_ai.mcp.server': ctx.server,\n 'gen_ai.tool.name': ctx.displayName,\n }\n if (typeof started === 'number')\n record(mcpToolDuration, Date.now() - started, tags, 'mcp.tool.duration')\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n mcpStart.delete(ctx.callId)\n add(mcpToolErrors, 1, {\n 'gen_ai.mcp.server': ctx.server,\n 'gen_ai.tool.name': ctx.displayName,\n 'error.type': ctx.error.name,\n }, 'mcp.tool.errors')\n }))\n\n unregisters.push(hooks.hook('mcp:bootstrap:end', (ctx) => {\n record(mcpBootstrapDuration, ctx.durationMs, {\n 'gen_ai.mcp.server': ctx.name,\n 'ok': ctx.ok,\n }, 'mcp.bootstrap.duration')\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n add(mcpErrors, 1, {\n 'gen_ai.mcp.server': ctx.name,\n 'error.type': ctx.error.name,\n }, 'mcp.errors')\n }))\n\n unregisters.push(hooks.hook('mcp:auth:required', (ctx) => {\n add(mcpAuthRequired, 1, {\n 'gen_ai.mcp.server': ctx.name,\n 'transport': ctx.transport,\n 'reason': ctx.reason,\n }, 'mcp.auth.required')\n }))\n\n // -----------------------------------------------------------------\n // Disposal\n // -----------------------------------------------------------------\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n turnStart.clear()\n toolStart.clear()\n mcpStart.clear()\n }\n },\n }\n}\n\n/**\n * Defensive factory wrapper — returns undefined when meter creation\n * throws. Lets a half-broken meter (one unsupported instrument) still\n * give partial metrics rather than collapsing the whole install.\n */\nfunction safeFactory<T>(\n factory: () => T,\n name: string,\n onError: (kind: string, err: unknown) => void,\n): T | undefined {\n try {\n return factory()\n }\n catch (err) {\n try {\n onError(`createInstrument:${name}`, err)\n }\n catch { /* ignore */ }\n return undefined\n }\n}\n","/**\n * Run summary collector — one JSON postmortem per `agent.run()`.\n *\n * `AgentStats` is great for billing and per-turn accounting but doesn't\n * carry a record of the *interesting* events that happened during a run\n * (errors, gate blocks, validation rejects, budget hits, MCP failures).\n * Reconstructing that from a hook stream after the fact is exactly the\n * kind of plumbing every consumer ends up re-implementing.\n *\n * This module ships:\n *\n * - {@link RunSummary} — a serializable shape that bundles totals,\n * per-model breakdown, and all the per-run incident lists.\n * - {@link createRunSummaryCollector} — installs hook listeners that\n * build up a {@link RunSummary} during the run and surfaces it on\n * `agent:done`. Calls an optional `onSummary` callback so the host can\n * forward to a log aggregator / DB / postmortem dashboard without\n * touching `agent.run()`'s return value.\n *\n * The collector is purely additive — it doesn't touch the agent loop,\n * doesn't change return shapes, and doesn't suppress any other hook\n * handlers. Drop it in alongside tracing / metrics / logging or use it\n * standalone when you only need the summary.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { AgentStats } from './types'\nimport { statsByModel } from './stats'\n\n// ---------------------------------------------------------------------------\n// Public shape\n// ---------------------------------------------------------------------------\n\nexport interface RunSummaryTokens {\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost?: number\n /** First observable byte from the provider, ms from run start. */\n ttftMs?: number\n}\n\nexport interface RunSummaryByModel {\n modelId: string\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost: number\n turns: number\n}\n\nexport interface RunSummaryError {\n kind: 'stream' | 'tool' | 'mcp-tool' | 'mcp' | 'spawn'\n message: string\n errorType?: string\n turnId?: string\n callId?: string\n server?: string\n toolName?: string\n childId?: string\n statusCode?: number\n requestId?: string\n}\n\nexport interface RunSummaryBlock {\n callId: string\n toolName: string\n outcome: 'gate-block' | 'unknown' | 'invalid-input'\n reason?: string\n}\n\nexport interface RunSummaryValidation {\n callId: string\n toolName: string\n reason: string\n}\n\nexport interface RunSummaryBudget {\n kind: 'bytes' | 'tool-count'\n /** Tool name (for `'tool-count'`); absent for byte budgets. */\n toolName?: string\n /** `mode` for `'tool-count'`; absent for byte budgets. */\n mode?: 'steer' | 'block'\n observed: number\n limit: number\n turnId?: string\n}\n\n/**\n * Postmortem snapshot of one `agent.run()`. Strictly serializable — every\n * field round-trips through `JSON.stringify` / `JSON.parse` without loss\n * so a log aggregator can ingest it as-is.\n */\nexport interface RunSummary {\n runId?: string\n parentRunId?: string\n depth: number\n agentName?: string\n startedAt: number\n endedAt: number\n durationMs: number\n status: 'completed' | 'aborted'\n turns: number\n totals: RunSummaryTokens\n byModel: RunSummaryByModel[]\n errors: RunSummaryError[]\n blocks: RunSummaryBlock[]\n validationRejects: RunSummaryValidation[]\n budgetEvents: RunSummaryBudget[]\n /** Counts of pairing repairs, keyed by repair mode. */\n pairingRepairs: Record<string, number>\n /**\n * Postmortem snapshots of child runs that bubbled their stats up via\n * `spawn:complete`. Only present when the run actually spawned.\n */\n children?: RunSummary[]\n}\n\n// ---------------------------------------------------------------------------\n// Collector\n// ---------------------------------------------------------------------------\n\nexport interface RunSummaryCollectorOptions {\n /**\n * Called with the assembled {@link RunSummary} on every `agent:done`.\n * Synchronous — heavy I/O should be deferred (e.g. via `setImmediate`).\n */\n onSummary?: (summary: RunSummary) => void\n}\n\nexport interface RunSummaryCollector {\n /** Install the collector's hook handlers. Returns an uninstall fn. */\n install: (hooks: Hookable<AgentHooks>) => () => void\n /** Most-recent summary; `undefined` until the first `agent:done` fires. */\n latest: () => RunSummary | undefined\n}\n\n/**\n * Build a run-summary collector. State is created fresh inside each\n * `install()` call, so a single collector instance can be installed\n * across multiple agents without attribution cross-talk. `latest()`\n * returns the most-recent summary across **any** install — install\n * per-agent collectors if you need separate post-run snapshots.\n *\n * @example\n * ```ts\n * const collector = createRunSummaryCollector({\n * onSummary: s => console.log(JSON.stringify(s)),\n * })\n * const uninstall = collector.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n */\nexport function createRunSummaryCollector(\n options: RunSummaryCollectorOptions = {},\n): RunSummaryCollector {\n let last: RunSummary | undefined\n\n return {\n latest: () => last,\n install(hooks: Hookable<AgentHooks>): () => void {\n // Per-run in-progress accumulators. The hookable bus serializes\n // agent.run lifecycles per agent — we reset between runs on\n // `agent:start` and consume on `agent:done`.\n let runId: string | undefined\n let parentRunId: string | undefined\n let depth = 0\n let agentName: string | undefined\n let startedAt = Date.now()\n let aborted = false\n const errors: RunSummaryError[] = []\n const blocks: RunSummaryBlock[] = []\n const validationRejects: RunSummaryValidation[] = []\n const budgetEvents: RunSummaryBudget[] = []\n const pairingRepairs: Record<string, number> = {}\n const children: RunSummary[] = []\n\n function resetForNewRun(): void {\n aborted = false\n errors.length = 0\n blocks.length = 0\n validationRejects.length = 0\n budgetEvents.length = 0\n for (const k of Object.keys(pairingRepairs))\n delete pairingRepairs[k]\n children.length = 0\n }\n\n const unregisters: Array<() => void> = []\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n resetForNewRun()\n runId = ctx.runId\n parentRunId = ctx.parentRunId\n depth = ctx.depth\n agentName = ctx.agentName\n startedAt = ctx.startedAt\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n aborted = true\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n const msg = ctx.err instanceof Error ? ctx.err.message : String(ctx.err)\n const errorType = ctx.err instanceof Error ? ctx.err.name : 'unknown'\n errors.push({\n kind: 'stream',\n message: msg,\n errorType,\n turnId: ctx.turnId,\n ...(ctx.statusCode !== undefined ? { statusCode: ctx.statusCode } : {}),\n ...(ctx.requestId !== undefined ? { requestId: ctx.requestId } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n errors.push({\n kind: 'tool',\n message: ctx.error.message,\n errorType: ctx.error.name,\n turnId: ctx.turnId,\n callId: ctx.callId,\n toolName: ctx.name,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n errors.push({\n kind: 'mcp-tool',\n message: ctx.error.message,\n errorType: ctx.error.name,\n turnId: ctx.turnId,\n callId: ctx.callId,\n server: ctx.server,\n toolName: ctx.displayName,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n errors.push({\n kind: 'mcp',\n message: ctx.error.message,\n errorType: ctx.error.name,\n server: ctx.name,\n })\n }))\n\n unregisters.push(hooks.hook('spawn:error', (ctx) => {\n errors.push({\n kind: 'spawn',\n message: ctx.error.message,\n errorType: ctx.error.name,\n childId: ctx.id,\n })\n }))\n\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n if (ctx.outcome === 'gate-block' || ctx.outcome === 'unknown' || ctx.outcome === 'invalid-input') {\n blocks.push({\n callId: ctx.callId,\n toolName: ctx.name,\n outcome: ctx.outcome,\n ...(ctx.reason ? { reason: ctx.reason } : {}),\n })\n }\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n validationRejects.push({\n callId: ctx.callId,\n toolName: ctx.name,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n budgetEvents.push({\n kind: 'bytes',\n observed: ctx.bytes,\n limit: ctx.budget,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n budgetEvents.push({\n kind: 'tool-count',\n toolName: ctx.tool,\n mode: ctx.mode,\n observed: ctx.count,\n limit: ctx.max,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('pairing:repair', (ctx) => {\n pairingRepairs[ctx.mode] = (pairingRepairs[ctx.mode] ?? 0) + 1\n }))\n\n unregisters.push(hooks.hook('agent:done', (stats) => {\n const endedAt = Date.now()\n\n // Build per-model rollup via the existing `statsByModel` helper\n // so we stay in lockstep with how the rest of the harness\n // attributes cost and tokens by model id.\n const byModel: RunSummaryByModel[] = []\n for (const [modelId, usage] of statsByModel(stats)) {\n byModel.push({\n modelId,\n input: usage.input,\n output: usage.output,\n cacheRead: usage.cacheRead,\n cacheCreation: usage.cacheCreation,\n cost: usage.cost,\n turns: usage.turns,\n })\n }\n\n // Flatten child stats into nested summaries. We don't have the\n // child's full event lists here (those landed on the child's\n // own hooks), so the nested entry is a minimal totals-only\n // record — enough for a flat per-run audit trail; consumers\n // wanting full per-child event lists install a collector on\n // each subagent.\n for (const c of stats.children ?? []) {\n children.push(minimalSummaryFromStats(c.stats, {\n depth: c.depth ?? depth + 1,\n status: c.status === 'aborted' ? 'aborted' : 'completed',\n }))\n }\n\n const summary: RunSummary = {\n ...(runId ? { runId } : {}),\n ...(parentRunId ? { parentRunId } : {}),\n depth,\n ...(agentName ? { agentName } : {}),\n startedAt,\n endedAt,\n durationMs: endedAt - startedAt,\n status: aborted ? 'aborted' : 'completed',\n turns: stats.turns,\n totals: buildTotals(stats),\n byModel,\n errors: errors.slice(),\n blocks: blocks.slice(),\n validationRejects: validationRejects.slice(),\n budgetEvents: budgetEvents.slice(),\n pairingRepairs: { ...pairingRepairs },\n ...(children.length > 0 ? { children: children.slice() } : {}),\n }\n\n last = summary\n try {\n options.onSummary?.(summary)\n }\n catch {\n // Sink errors are not the collector's concern.\n }\n }))\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n }\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction buildTotals(stats: AgentStats): RunSummaryTokens {\n return {\n input: stats.totalIn,\n output: stats.totalOut,\n cacheRead: stats.totalCacheRead,\n cacheCreation: stats.totalCacheCreation,\n ...(typeof stats.cost === 'number' ? { cost: stats.cost } : {}),\n ...(typeof stats.timeTillFirstTokenMs === 'number' ? { ttftMs: stats.timeTillFirstTokenMs } : {}),\n }\n}\n\nfunction minimalSummaryFromStats(\n stats: AgentStats,\n meta: { depth: number, status: 'completed' | 'aborted' },\n): RunSummary {\n const byModel: RunSummaryByModel[] = []\n for (const [modelId, usage] of statsByModel(stats)) {\n byModel.push({\n modelId,\n input: usage.input,\n output: usage.output,\n cacheRead: usage.cacheRead,\n cacheCreation: usage.cacheCreation,\n cost: usage.cost,\n turns: usage.turns,\n })\n }\n const children: RunSummary[] = []\n for (const c of stats.children ?? []) {\n children.push(minimalSummaryFromStats(c.stats, {\n depth: c.depth ?? meta.depth + 1,\n status: c.status === 'aborted' ? 'aborted' : 'completed',\n }))\n }\n return {\n depth: meta.depth,\n startedAt: 0,\n endedAt: 0,\n durationMs: stats.elapsed,\n status: meta.status,\n turns: stats.turns,\n totals: buildTotals(stats),\n byModel,\n errors: [],\n blocks: [],\n validationRejects: [],\n budgetEvents: [],\n pairingRepairs: {},\n ...(children.length > 0 ? { children } : {}),\n }\n}\n","/**\n * Tracing helper — wraps agent lifecycle hooks in caller-provided spans.\n *\n * Zidane is hooks-native; rather than introducing a plugin abstraction just for\n * observability, we ship a small helper that turns an arbitrary `startSpan`\n * function into a bundle of hook handlers. Works with Sentry, OpenTelemetry,\n * Datadog — any tracer that can hand back an object with an `end()` method.\n *\n * Attribute conventions follow the Sentry AI Agents spec (which itself\n * tracks OpenTelemetry Gen AI semconv v1.36, with a few divergences). The\n * `conventions` option lets callers pick `'sentry'` (default — Sentry's\n * exact keys), `'otel'` (vendor-neutral OTel 1.36), or `'both'` (emit\n * every variant for maximum portability).\n *\n * Span coverage:\n *\n * - `invoke_agent <name?>` — root span, opens on `agent:start`, closes on `agent:done`. Sentry op `gen_ai.invoke_agent`.\n * - `chat <model?>` — per-turn LLM call, opens on `turn:before`, closes on `turn:after`. Sentry op `gen_ai.chat`.\n * - `execute_tool <name>` — native tool execution, opens on `tool:before`, closes on `tool:after` / `tool:error`. Sentry op `gen_ai.execute_tool`.\n * - `execute_tool <server>.<name>` — MCP tool variant, same op. Tagged with `gen_ai.tool.type: 'extension'`.\n * - `handoff to <task>` — subagent handoff (parent→child), opens on `spawn:before`, closes on `spawn:complete` / `spawn:error`. Sentry op `gen_ai.handoff`.\n * Also injects parent trace context into the spawn carrier when `propagator` is configured.\n * - `mcp.bootstrap <server>` — server bootstrap, opens on `mcp:bootstrap:start`, closes on `mcp:bootstrap:end`. Zidane-specific (no Sentry op equivalent).\n *\n * Span events (not spans — recorded on the active span via `addEvent`):\n *\n * - `gen_ai.gate.block` — a `tool:gate` listener refused a call.\n * - `gen_ai.validation.reject` — tool input failed schema validation.\n * - `gen_ai.validation.coerce` — tool input was auto-coerced to satisfy the schema.\n * - `gen_ai.budget.exceeded` — byte budget exceeded (`budget:exceeded`).\n * - `gen_ai.tool_budget.exceeded` — per-tool dispatch cap exceeded.\n * - `gen_ai.pairing.repair` — pre-send pairing repair fired.\n * - `gen_ai.stream.error` — provider stream errored (carries `statusCode` / `requestId`).\n * - `gen_ai.mcp.auth.required` — MCP server bootstrap blocked on missing OAuth tokens.\n * - `gen_ai.oauth.refresh` — provider OAuth credentials refreshed.\n *\n * Attribute naming follows the Sentry AI Agents conventions\n * (https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents/) by\n * default — which closely track OTel Gen AI semconv v1.36 with a few\n * intentional differences (`usage.input_tokens.cached` instead of\n * `usage.cache_read_input_tokens`, `cost.total_tokens` instead of\n * `usage.cost_usd`, seconds instead of ms for `time_to_first_token`).\n * Set `conventions: 'otel'` to emit only the vendor-neutral OTel keys, or\n * `'both'` to emit everything for maximum portability. Legacy attribute\n * names (`inputTokens`, `toolName`, `displayName`, …) are also emitted by\n * default for back-compat; set `legacyAttributes: false` to drop them.\n *\n * On `agent:done` or `uninstall()` it closes any spans still open (defensive —\n * normally all tool/turn spans have matching end hooks, but an abort\n * mid-execution can leave dangling ones).\n *\n * Returns an `install(hooks)` helper that registers every handler at once and\n * yields an `uninstall()` function for cleanup. Safe across multiple runs.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { SessionMessage } from './types'\n\n/** Minimal span shape — any tracer that provides these methods is compatible. */\nexport interface Span {\n /** Close the span. Called exactly once per span. */\n end: () => void\n /** Optional: attach additional attributes after the span is started (ignored if unsupported). */\n setAttributes?: (attrs: Record<string, unknown>) => void\n /**\n * Optional: record a structured event on the span. Used for non-span\n * occurrences (gate blocks, validation rejects, budget hits, pairing\n * repairs). Maps to OTel's `Span.addEvent` and Sentry's `addBreadcrumb`.\n * Tracers without an event surface should treat this as a no-op (the\n * helper falls back to `setAttributes` with an `event.*` key prefix).\n */\n addEvent?: (name: string, attrs?: Record<string, unknown>) => void\n}\n\n/**\n * Function that opens a span. Caller-provided so we stay tracer-agnostic.\n *\n * `parentContext` carries opaque trace metadata propagated from another\n * agent (typically a W3C `{ traceparent, tracestate }` carrier received on\n * `agent:start.tracingContext` after a parent agent's tracer injected it on\n * `spawn:before`). Implementations that integrate with OTel should extract\n * the carrier into a `Context` and use it as the parent when opening the\n * span; implementations that don't care about cross-agent linkage can\n * ignore the third argument entirely.\n */\nexport type StartSpan = (\n name: string,\n attrs?: Record<string, unknown>,\n parentContext?: Readonly<Record<string, string>>,\n) => Span\n\nexport type TracingConventions = 'sentry' | 'otel' | 'both'\n\nexport interface TracingHooksOptions {\n /** Tracer seam. Receives a span name + attributes; must return a `Span`. */\n startSpan: StartSpan\n /**\n * Optional attribute namespace. Prepended to every span name with a `/`\n * separator (e.g. `\"agent\"` → `agent/chat <model>`, `agent/execute_tool Bash`).\n *\n * Empty / undefined = no prefix.\n */\n namespace?: string\n /**\n * Which Gen-AI attribute conventions to emit.\n *\n * - `'sentry'` (default) — Sentry AI Agents conventions. Most common\n * choice — Sentry, Datadog AI, Honeycomb derived dashboards and\n * Langfuse all consume these.\n * - `'otel'` — vendor-neutral OpenTelemetry Gen AI semconv v1.36.\n * Diverges from Sentry on cache-token keys, cost keys, and TTFT unit.\n * - `'both'` — emit every variant. Doubles the per-span attribute\n * payload; pick when shipping to multiple backends with different\n * ingestion conventions.\n */\n conventions?: TracingConventions\n /**\n * Capture the actual prompt / completion text on spans\n * (`gen_ai.input.messages`, `gen_ai.output.messages`,\n * `gen_ai.system_instructions`, `gen_ai.tool.definitions`,\n * `gen_ai.tool.call.arguments`, `gen_ai.tool.call.result`).\n *\n * Defaults to `true` — the Sentry AI Agents dashboard requires these\n * fields to render the conversation viewer. Set `false` to drop them\n * for privacy / cost reasons; everything still goes through `redact`\n * when enabled, so most consumers want it on with a redactor wired up\n * rather than off entirely.\n */\n captureMessageContent?: boolean\n /**\n * Emit legacy attribute keys (`inputTokens`, `outputTokens`, `toolName`,\n * `displayName`, `finishReason`, `modelId`) alongside the new\n * `gen_ai.*` semconv keys. Defaults to `true` so existing dashboards\n * keep working. Set `false` once you've migrated all consumers off the\n * legacy names.\n */\n legacyAttributes?: boolean\n /**\n * Error sink for tracer failures. Replaces the historic silent-swallow.\n * Called when `Span.end()`, `setAttributes()`, `addEvent()`, or\n * `startSpan()` throws — so degraded tracers stay visible. The helper\n * still does not let tracer errors propagate into the agent loop.\n *\n * `kind` identifies which step failed (`'startSpan'`, `'end'`,\n * `'setAttributes'`, `'addEvent'`, `'redact'`, `'getActiveTraceContext'`).\n * Default: no-op (silent).\n */\n onError?: (kind: string, err: unknown) => void\n /**\n * Optional callback the helper invokes on `spawn:before` (after opening\n * the spawn span) to read the active trace context. Whatever it returns\n * is written into `ctx.tracingContext` and forwarded to the child's\n * `agent.run({ tracingContext })`. Typical use:\n *\n * ```ts\n * getActiveTraceContext: () => {\n * const carrier: Record<string, string> = {}\n * propagation.inject(context.active(), carrier)\n * return carrier\n * }\n * ```\n *\n * Returning `undefined` or an empty object leaves the carrier empty\n * and the child runs with no parent context. The helper's own\n * `propagator` is preferred over this when both are set (the\n * propagator runs inside the helper; this callback is the escape\n * hatch for hosts wanting full control).\n */\n getActiveTraceContext?: () => Readonly<Record<string, string>> | undefined\n /**\n * Optional in-process span redactor. When set, every potentially\n * sensitive string (tool input, tool result, system prompt) flows\n * through this fn before landing on a span attribute / event. Returning\n * the input unchanged is a no-op.\n *\n * Composes with the harness-wide `tracing:redact` hook: if a host has\n * already registered a hook handler that mutates `ctx.redacted`, the\n * tracer reuses that pipeline; setting `redact` here registers an extra\n * step that runs only for the tracer's attributes. The hook is the\n * canonical path; `redact` is the convenience surface for tracer-only\n * setups.\n */\n redact?: (kind: string, value: string, meta?: Readonly<Record<string, unknown>>) => string\n}\n\n/** Value returned by {@link createTracingHooks} — install entrypoint. */\nexport interface TracingHookSet {\n /**\n * Attach every hook handler to the given agent hooks instance.\n *\n * @returns an `uninstall` function that detaches every handler and closes any\n * still-open spans. Safe to call multiple times.\n */\n install: (hooks: Hookable<AgentHooks>) => () => void\n}\n\n// ---------------------------------------------------------------------------\n// Gen AI semantic-convention attribute keys\n// ---------------------------------------------------------------------------\n\n/**\n * Stable keys we emit unconditionally — present in BOTH Sentry and OTel\n * conventions with identical semantics, so no `conventions` branching is\n * needed at the call site.\n */\nconst GEN_AI = {\n system: 'gen_ai.system',\n operationName: 'gen_ai.operation.name',\n requestModel: 'gen_ai.request.model',\n responseModel: 'gen_ai.response.model',\n responseFinishReasons: 'gen_ai.response.finish_reasons',\n responseId: 'gen_ai.response.id',\n responseStreaming: 'gen_ai.response.streaming',\n responseTokensPerSecond: 'gen_ai.response.tokens_per_second',\n // Sentry uses seconds; OTel uses ms. We emit one or both based on `conventions`.\n responseTimeToFirstTokenSeconds: 'gen_ai.response.time_to_first_token',\n responseTimeToFirstTokenMs: 'gen_ai.client.time_to_first_token',\n // Usage — Sentry expects input_tokens to INCLUDE cached, with subkeys for cache slices.\n usageInputTokens: 'gen_ai.usage.input_tokens',\n usageOutputTokens: 'gen_ai.usage.output_tokens',\n usageTotalTokens: 'gen_ai.usage.total_tokens',\n // Sentry-specific cache keys.\n usageInputTokensCached: 'gen_ai.usage.input_tokens.cached',\n usageInputTokensCacheWrite: 'gen_ai.usage.input_tokens.cache_write',\n usageOutputTokensReasoning: 'gen_ai.usage.output_tokens.reasoning',\n // OTel cache keys (older / vendor-neutral).\n usageCacheReadInputTokens: 'gen_ai.usage.cache_read_input_tokens',\n usageCacheCreationInputTokens: 'gen_ai.usage.cache_creation_input_tokens',\n usageReasoningTokens: 'gen_ai.usage.reasoning_tokens',\n // Cost — Sentry splits, OTel rolls up.\n costTotalTokens: 'gen_ai.cost.total_tokens',\n costInputTokens: 'gen_ai.cost.input_tokens',\n costOutputTokens: 'gen_ai.cost.output_tokens',\n usageCostUsd: 'gen_ai.usage.cost_usd',\n // Tool — Sentry uses `tool.call.arguments` / `tool.call.result`; OTel uses `tool.input` / `tool.output` (deprecated in Sentry).\n toolName: 'gen_ai.tool.name',\n toolDescription: 'gen_ai.tool.description',\n toolType: 'gen_ai.tool.type',\n toolCallId: 'gen_ai.tool.call.id',\n toolCallArguments: 'gen_ai.tool.call.arguments',\n toolCallResult: 'gen_ai.tool.call.result',\n toolMessage: 'gen_ai.tool.message',\n toolInputDeprecated: 'gen_ai.tool.input',\n toolOutputDeprecated: 'gen_ai.tool.output',\n // Request configuration.\n requestMaxTokens: 'gen_ai.request.max_tokens',\n requestTemperature: 'gen_ai.request.temperature',\n requestTopP: 'gen_ai.request.top_p',\n requestTopK: 'gen_ai.request.top_k',\n requestSeed: 'gen_ai.request.seed',\n requestFrequencyPenalty: 'gen_ai.request.frequency_penalty',\n requestPresencePenalty: 'gen_ai.request.presence_penalty',\n // Message content surfaces (Sentry conversation viewer).\n inputMessages: 'gen_ai.input.messages',\n outputMessages: 'gen_ai.output.messages',\n systemInstructions: 'gen_ai.system_instructions',\n toolDefinitions: 'gen_ai.tool.definitions',\n // Agent identity.\n agentName: 'gen_ai.agent.name',\n agentRunId: 'gen_ai.agent.run.id',\n agentParentRunId: 'gen_ai.agent.parent_run.id',\n agentDepth: 'gen_ai.agent.depth',\n pipelineName: 'gen_ai.pipeline.name',\n turn: 'gen_ai.agent.turn',\n // MCP-specific (zidane extension; no Sentry op equivalent).\n mcpServer: 'gen_ai.mcp.server',\n mcpToolCount: 'gen_ai.mcp.tool_count',\n} as const\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Compose namespace prefix once. */\nfunction prefixed(namespace: string | undefined, name: string): string {\n return namespace ? `${namespace}/${name}` : name\n}\n\n/** Drop undefined entries — tracers vary on how they render `undefined`. */\nfunction compact(attrs: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(attrs)) {\n if (v !== undefined)\n out[k] = v\n }\n return out\n}\n\n/**\n * Convert a zidane `SessionMessage` (or `StreamOptions.messages` entry)\n * into Sentry's `{role, parts: [{type, content}]}` shape, JSON-stringify\n * the result. Returns `undefined` on serialization failure so the caller\n * skips the attribute rather than poisoning the span.\n *\n * Per Sentry footnote [7], binary blobs in image content are replaced\n * with `\"[Blob substitute]\"`. Tool calls become `{type: 'tool_call'}`\n * parts; tool results become `{type: 'tool_result'}` parts.\n */\ninterface SentryMessagePart {\n type: 'text' | 'tool_call' | 'tool_result' | 'reasoning'\n content?: string\n name?: string\n arguments?: string\n result?: string\n call_id?: string\n}\ninterface SentryMessage {\n role: 'user' | 'assistant' | 'system' | 'tool'\n parts: SentryMessagePart[]\n}\n\n/**\n * Wide-shape block record. `SessionContentBlock` is a discriminated\n * union — accepting the wide record lets us peek at the discriminator\n * without re-importing the entire union at this layer.\n */\ninterface AnyBlock { type: string, [k: string]: unknown }\n\nfunction blockToParts(block: AnyBlock): SentryMessagePart[] {\n switch (block.type) {\n case 'text':\n return [{ type: 'text', content: typeof block.text === 'string' ? block.text : '' }]\n case 'image':\n return [{ type: 'text', content: '[Blob substitute]' }]\n case 'tool_call':\n return [{\n type: 'tool_call',\n ...(typeof block.name === 'string' ? { name: block.name } : {}),\n ...(typeof block.id === 'string' ? { call_id: block.id } : {}),\n arguments: safeJson(block.input),\n }]\n case 'tool_result': {\n const output = block.output\n const text = typeof output === 'string'\n ? output\n : Array.isArray(output)\n ? (output as Array<{ type: string, text?: string }>)\n .map(b => b.type === 'image' ? '[Blob substitute]' : (b.text ?? ''))\n .join('\\n')\n : ''\n return [{\n type: 'tool_result',\n ...(typeof block.callId === 'string' ? { call_id: block.callId } : {}),\n content: text,\n }]\n }\n case 'thinking':\n return [{ type: 'reasoning', content: typeof block.text === 'string' ? block.text : '' }]\n default:\n return []\n }\n}\n\nfunction messageToSentry(msg: { role: 'user' | 'assistant', content: readonly AnyBlock[] }): SentryMessage {\n const parts: SentryMessagePart[] = []\n for (const block of msg.content)\n parts.push(...blockToParts(block))\n return { role: msg.role, parts }\n}\n\nfunction safeJson(value: unknown): string {\n try {\n return JSON.stringify(value ?? null)\n }\n catch {\n return '\"[unserializable]\"'\n }\n}\n\n/**\n * Stringify an array of `SessionMessage`s into Sentry's `gen_ai.input.messages`\n * format. Each entry's role + content is mapped via `messageToSentry`.\n * Returns `undefined` when the input is empty or unserializable.\n */\nfunction stringifyMessages(messages: readonly SessionMessage[]): string | undefined {\n if (messages.length === 0)\n return undefined\n try {\n const sentry = messages.map(m => messageToSentry({\n role: m.role,\n content: m.content as readonly AnyBlock[],\n }))\n return JSON.stringify(sentry)\n }\n catch {\n return undefined\n }\n}\n\n/**\n * Stringify a `StreamOptions.tools` array into Sentry's\n * `gen_ai.tool.definitions` format: `[{name, description?, parameters?}, …]`.\n *\n * The input is `unknown[]` because `StreamOptions.tools` carries the\n * provider-formatted shape (Anthropic / OpenAI / OpenAI-compat all\n * differ). We probe the three names that overlap (`name`, `description`,\n * `inputSchema` / `parameters`) and degrade gracefully otherwise.\n */\nfunction stringifyToolDefs(tools: readonly unknown[] | undefined): string | undefined {\n if (!tools || tools.length === 0)\n return undefined\n try {\n return JSON.stringify(tools.map((raw) => {\n if (!raw || typeof raw !== 'object')\n return { name: 'anonymous' }\n const t = raw as Record<string, unknown>\n const name = typeof t.name === 'string' ? t.name : 'anonymous'\n const description = typeof t.description === 'string' ? t.description : undefined\n // OpenAI's wire format calls it `parameters`; Anthropic / ToolSpec\n // call it `input_schema` / `inputSchema`. Probe all three.\n const parameters = t.parameters ?? t.input_schema ?? t.inputSchema\n return {\n name,\n ...(description ? { description } : {}),\n ...(parameters !== undefined ? { parameters } : {}),\n }\n }))\n }\n catch {\n return undefined\n }\n}\n\n/**\n * Build the usage attribute set per the configured conventions. Sentry\n * expects `input_tokens` to include cached, with `input_tokens.cached`\n * + `input_tokens.cache_write` subkeys; OTel splits cache into\n * separate top-level keys. `'both'` emits everything.\n */\nfunction buildUsageAttrs(\n usage: {\n input: number\n output: number\n cacheRead?: number\n cacheCreation?: number\n thinking?: number\n cost?: number\n modelId?: string\n finishReason?: string\n timeToFirstTokenMs?: number\n },\n conventions: TracingConventions,\n): Record<string, unknown> {\n const cacheRead = usage.cacheRead ?? 0\n const cacheWrite = usage.cacheCreation ?? 0\n // Per Sentry: `input_tokens` includes cached. zidane reports `input` as\n // the uncached new-input slice, so the headline number is the sum.\n const inputInclCache = usage.input + cacheRead + cacheWrite\n const totalTokens = inputInclCache + usage.output\n const wantSentry = conventions === 'sentry' || conventions === 'both'\n const wantOtel = conventions === 'otel' || conventions === 'both'\n\n const out: Record<string, unknown> = {\n [GEN_AI.responseModel]: usage.modelId,\n [GEN_AI.responseFinishReasons]: usage.finishReason ? [usage.finishReason] : undefined,\n [GEN_AI.usageOutputTokens]: usage.output,\n [GEN_AI.usageTotalTokens]: totalTokens,\n }\n\n if (wantSentry) {\n out[GEN_AI.usageInputTokens] = inputInclCache\n if (cacheRead > 0)\n out[GEN_AI.usageInputTokensCached] = cacheRead\n if (cacheWrite > 0)\n out[GEN_AI.usageInputTokensCacheWrite] = cacheWrite\n if (typeof usage.thinking === 'number' && usage.thinking > 0)\n out[GEN_AI.usageOutputTokensReasoning] = usage.thinking\n if (typeof usage.cost === 'number')\n out[GEN_AI.costTotalTokens] = usage.cost\n if (typeof usage.timeToFirstTokenMs === 'number')\n out[GEN_AI.responseTimeToFirstTokenSeconds] = usage.timeToFirstTokenMs / 1000\n }\n if (wantOtel) {\n // OTel: input_tokens is the uncached slice; cache is separate.\n if (!wantSentry)\n out[GEN_AI.usageInputTokens] = usage.input\n if (cacheRead > 0)\n out[GEN_AI.usageCacheReadInputTokens] = cacheRead\n if (cacheWrite > 0)\n out[GEN_AI.usageCacheCreationInputTokens] = cacheWrite\n if (typeof usage.thinking === 'number' && usage.thinking > 0)\n out[GEN_AI.usageReasoningTokens] = usage.thinking\n if (typeof usage.cost === 'number')\n out[GEN_AI.usageCostUsd] = usage.cost\n if (typeof usage.timeToFirstTokenMs === 'number')\n out[GEN_AI.responseTimeToFirstTokenMs] = usage.timeToFirstTokenMs\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a set of tracing hook handlers that can be installed on an agent.\n *\n * @example Sentry\n * ```ts\n * const tracing = createTracingHooks({\n * startSpan: (name, attrs) => Sentry.startInactiveSpan({ name, attributes: attrs }),\n * })\n * const uninstall = tracing.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n *\n * @example OpenTelemetry with parent-context propagation\n * ```ts\n * import { trace, context, propagation } from '@opentelemetry/api'\n *\n * const tracer = trace.getTracer('zidane')\n * const tracing = createTracingHooks({\n * startSpan: (name, attrs, parentCtx) => {\n * const ctx = parentCtx ? propagation.extract(context.active(), parentCtx) : context.active()\n * return tracer.startSpan(name, { attributes: attrs }, ctx)\n * },\n * getActiveTraceContext: () => {\n * const carrier: Record<string, string> = {}\n * propagation.inject(context.active(), carrier)\n * return carrier\n * },\n * })\n * ```\n */\nexport function createTracingHooks(options: TracingHooksOptions): TracingHookSet {\n const namespace = options.namespace\n const legacy = options.legacyAttributes ?? true\n const conventions = options.conventions ?? 'sentry'\n const wantSentry = conventions === 'sentry' || conventions === 'both'\n const captureContent = options.captureMessageContent ?? true\n const onError = options.onError ?? (() => {})\n\n return {\n install(hooks: Hookable<AgentHooks>): () => void {\n // Span book-keeping. Each map keys by the identifier that's unique\n // across concurrent in-flight spans of that kind.\n const runSpans = new Map<string, Span>() // keyed by runId\n const turnSpans = new Map<string, Span>() // keyed by turnId\n const toolSpans = new Map<string, Span>() // keyed by callId\n const mcpSpans = new Map<string, Span>() // keyed by callId\n const spawnSpans = new Map<string, Span>() // keyed by spawn id\n const bootstrapSpans = new Map<string, Span>() // keyed by server name\n // Provider system identifier (`anthropic`, `openai`, …) captured at\n // `agent:start`. Stamped onto every chat / tool span belonging to\n // this run so Sentry's per-vendor breakdowns work. Reset on\n // `agent:done`.\n let activeSystem: string | undefined\n // Active turn book-keeping for tokens-per-second derivation.\n const turnTrack = new Map<string, { startedAt: number }>()\n // Active run span ref — used so non-span events (gate blocks,\n // budget hits, validation rejects, policy/pairing repairs) can\n // attach via `addEvent` on the in-flight run span. The run span is\n // the natural parent for cross-cutting policy events: it outlives\n // turn spans (which close before tool dispatch starts in the\n // current loop), and tool spans (which haven't opened on a\n // gate-block path).\n let activeRunSpan: Span | undefined\n\n function safeStartSpan(\n name: string,\n attrs: Record<string, unknown>,\n parentContext?: Readonly<Record<string, string>>,\n ): Span | undefined {\n try {\n return options.startSpan(prefixed(namespace, name), compact(attrs), parentContext)\n }\n catch (err) {\n try {\n onError('startSpan', err)\n }\n catch { /* ignore */ }\n return undefined\n }\n }\n\n function safeSetAttrs(span: Span | undefined, attrs: Record<string, unknown>): void {\n if (!span?.setAttributes)\n return\n try {\n span.setAttributes(compact(attrs))\n }\n catch (err) {\n try {\n onError('setAttributes', err)\n }\n catch { /* ignore */ }\n }\n }\n\n function safeEnd(map: Map<string, Span>, key: string): void {\n const span = map.get(key)\n if (!span)\n return\n try {\n span.end()\n }\n catch (err) {\n try {\n onError('end', err)\n }\n catch { /* ignore */ }\n }\n map.delete(key)\n }\n\n function endAll(map: Map<string, Span>): void {\n for (const [, span] of map) {\n try {\n span.end()\n }\n catch (err) {\n try {\n onError('end', err)\n }\n catch { /* ignore */ }\n }\n }\n map.clear()\n }\n\n function safeAddEvent(span: Span | undefined, name: string, attrs: Record<string, unknown>): void {\n if (!span)\n return\n if (typeof span.addEvent === 'function') {\n try {\n span.addEvent(name, compact(attrs))\n }\n catch (err) {\n try {\n onError('addEvent', err)\n }\n catch { /* ignore */ }\n }\n return\n }\n // Tracer doesn't support events — fall back to a single\n // `event.<name>.*` attribute so the data lands somewhere.\n safeSetAttrs(span, Object.fromEntries(\n Object.entries(compact(attrs)).map(([k, v]) => [`event.${name}.${k}`, v]),\n ))\n }\n\n function redact(kind: string, value: string, meta?: Readonly<Record<string, unknown>>): string {\n if (typeof options.redact !== 'function')\n return value\n try {\n return options.redact(kind, value, meta)\n }\n catch (err) {\n try {\n onError('redact', err)\n }\n catch { /* ignore */ }\n return value\n }\n }\n\n const unregisters: Array<() => void> = []\n\n // -----------------------------------------------------------------\n // agent.run (root)\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n activeSystem = ctx.providerName\n // Empty-string names produce `invoke_agent ` (trailing space).\n // Treat empty as \"no name\" so the span name reads cleanly.\n const displayName = ctx.agentName && ctx.agentName.length > 0\n ? ctx.agentName\n : 'agent'\n const span = safeStartSpan(\n `invoke_agent ${displayName}`,\n {\n [GEN_AI.operationName]: 'invoke_agent',\n [GEN_AI.system]: activeSystem,\n [GEN_AI.agentName]: ctx.agentName,\n [GEN_AI.agentRunId]: ctx.runId,\n [GEN_AI.agentParentRunId]: ctx.parentRunId,\n [GEN_AI.agentDepth]: ctx.depth,\n // Sentry-specific span op hint — backends that key off\n // `op` (Sentry Python/JS) read this directly; OTel\n // backends ignore unknown keys, so it's harmless.\n 'sentry.op': 'gen_ai.invoke_agent',\n ...(legacy\n ? {\n runId: ctx.runId,\n parentRunId: ctx.parentRunId,\n agentName: ctx.agentName,\n depth: ctx.depth,\n }\n : {}),\n },\n ctx.tracingContext,\n )\n if (span) {\n runSpans.set(ctx.runId, span)\n activeRunSpan = span\n }\n }))\n\n unregisters.push(hooks.hook('agent:done', (stats) => {\n // `agent:done` doesn't carry a runId on its payload (it's `AgentStats`).\n // The 99% case is a single in-flight run — close every open run span\n // and stamp the totals on each. Multi-run-concurrent users plug a\n // runId into stats via their own integration; for now we close all.\n const usageAttrs = buildUsageAttrs({\n input: stats.totalIn,\n output: stats.totalOut,\n cacheRead: stats.totalCacheRead,\n cacheCreation: stats.totalCacheCreation,\n cost: stats.cost,\n timeToFirstTokenMs: stats.timeTillFirstTokenMs,\n }, conventions)\n for (const [, span] of runSpans) {\n safeSetAttrs(span, {\n ...usageAttrs,\n [GEN_AI.turn]: stats.turns,\n 'gen_ai.agent.elapsed_ms': stats.elapsed,\n ...(legacy\n ? {\n totalIn: stats.totalIn,\n totalOut: stats.totalOut,\n totalCacheRead: stats.totalCacheRead,\n totalCacheCreation: stats.totalCacheCreation,\n cost: stats.cost,\n turns: stats.turns,\n elapsed: stats.elapsed,\n }\n : {}),\n })\n }\n endAll(runSpans)\n // Defensive — close anything else still hanging open.\n endAll(turnSpans)\n endAll(toolSpans)\n endAll(mcpSpans)\n endAll(spawnSpans)\n endAll(bootstrapSpans)\n activeRunSpan = undefined\n activeSystem = undefined\n turnTrack.clear()\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n for (const [, span] of runSpans)\n safeSetAttrs(span, { 'gen_ai.agent.status': 'aborted' })\n }))\n\n // -----------------------------------------------------------------\n // Turn span — OTel Gen AI `chat` operation\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('turn:before', (ctx) => {\n const requestedModel = ctx.options.model\n // Built-in Sentry/OTel attrs that don't depend on content capture.\n const baseAttrs: Record<string, unknown> = {\n [GEN_AI.operationName]: 'chat',\n [GEN_AI.system]: activeSystem,\n [GEN_AI.requestModel]: requestedModel,\n [GEN_AI.turn]: ctx.turn,\n [GEN_AI.responseStreaming]: true,\n [GEN_AI.requestMaxTokens]: ctx.options.maxTokens,\n 'sentry.op': 'gen_ai.chat',\n 'gen_ai.agent.turn_id': ctx.turnId,\n ...(legacy ? { turnId: ctx.turnId, turn: ctx.turn } : {}),\n }\n // Optionally surface conversation content — prompts, system\n // instructions, available tools. Each piece flows through the\n // redact seam so hosts can scrub secrets without losing the\n // structural attribute. Skipping content keeps the span cheap\n // and privacy-safe.\n if (captureContent) {\n const systemText = ctx.options.system\n if (systemText) {\n baseAttrs[GEN_AI.systemInstructions] = redact('system', systemText, {\n turnId: ctx.turnId,\n turn: ctx.turn,\n })\n }\n const inputMsgs = stringifyMessages(ctx.options.messages)\n if (inputMsgs) {\n baseAttrs[GEN_AI.inputMessages] = redact('prompt', inputMsgs, {\n turnId: ctx.turnId,\n turn: ctx.turn,\n })\n }\n const toolDefs = stringifyToolDefs(ctx.options.tools)\n if (toolDefs)\n baseAttrs[GEN_AI.toolDefinitions] = toolDefs\n }\n const span = safeStartSpan(\n `chat${requestedModel ? ` ${requestedModel}` : ''}`,\n baseAttrs,\n )\n if (span) {\n turnSpans.set(ctx.turnId, span)\n turnTrack.set(ctx.turnId, { startedAt: Date.now() })\n }\n }))\n\n unregisters.push(hooks.hook('stream:start', (ctx) => {\n safeAddEvent(turnSpans.get(ctx.turnId), 'gen_ai.stream.start', {\n startedAt: ctx.startedAt,\n })\n }))\n\n unregisters.push(hooks.hook('turn:after', (ctx) => {\n const span = turnSpans.get(ctx.turnId)\n const tracked = turnTrack.get(ctx.turnId)\n turnTrack.delete(ctx.turnId)\n // Tokens-per-second derivation. Use wall-clock duration since the\n // turn span opened, fall back to TTFT-adjusted slice when present.\n let tokensPerSecond: number | undefined\n if (tracked && ctx.usage.output > 0) {\n const elapsedMs = Date.now() - tracked.startedAt\n if (elapsedMs > 0)\n tokensPerSecond = (ctx.usage.output / elapsedMs) * 1000\n }\n const usageAttrs = buildUsageAttrs(ctx.usage, conventions)\n // Cumulative-through-this-turn totals. We compute `input_tokens`\n // here to mirror `gen_ai.usage.input_tokens`'s semantic on the\n // same span: it INCLUDES cached when Sentry conventions are in\n // play, matches the uncached slice when OTel-only. Without this\n // alignment the two keys (`usage.*` vs `cumulative.*`) would\n // silently differ in arithmetic.\n const cumInputTokens = wantSentry\n ? ctx.cumulativeUsage.input + ctx.cumulativeUsage.cacheRead + ctx.cumulativeUsage.cacheCreation\n : ctx.cumulativeUsage.input\n const attrs: Record<string, unknown> = {\n ...usageAttrs,\n [GEN_AI.responseTokensPerSecond]: tokensPerSecond,\n 'gen_ai.agent.cumulative.input_tokens': cumInputTokens,\n 'gen_ai.agent.cumulative.output_tokens': ctx.cumulativeUsage.output,\n 'gen_ai.agent.cumulative.total_tokens': cumInputTokens + ctx.cumulativeUsage.output,\n 'gen_ai.agent.cumulative.cache_read_input_tokens': ctx.cumulativeUsage.cacheRead,\n 'gen_ai.agent.cumulative.cache_creation_input_tokens': ctx.cumulativeUsage.cacheCreation,\n 'gen_ai.agent.cumulative.cost_usd': ctx.cumulativeUsage.cost,\n 'gen_ai.agent.cumulative.turns': ctx.cumulativeUsage.turns,\n ...(legacy\n ? {\n inputTokens: ctx.usage.input,\n outputTokens: ctx.usage.output,\n finishReason: ctx.usage.finishReason,\n modelId: ctx.usage.modelId,\n }\n : {}),\n }\n // Output messages — the assistant turn that just landed. Sentry's\n // conversation viewer keys off this; skipped when content capture\n // is off. `system` turns aren't serialized (Sentry's schema only\n // accepts user / assistant / system / tool — and we never emit\n // system role from the loop).\n if (captureContent && ctx.message && ctx.message.role !== 'system') {\n const outputMsgs = stringifyMessages([{\n role: ctx.message.role,\n content: ctx.message.content,\n }])\n if (outputMsgs)\n attrs[GEN_AI.outputMessages] = redact('stream-text', outputMsgs, { turnId: ctx.turnId, turn: ctx.turn })\n }\n safeSetAttrs(span, attrs)\n safeEnd(turnSpans, ctx.turnId)\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n const span = turnSpans.get(ctx.turnId)\n const message = ctx.err instanceof Error ? ctx.err.message : String(ctx.err)\n safeSetAttrs(span, {\n 'error.type': ctx.err instanceof Error ? ctx.err.name : 'unknown',\n 'error.message': message,\n 'http.response.status_code': ctx.statusCode,\n 'gen_ai.request.id': ctx.requestId,\n })\n safeAddEvent(span, 'gen_ai.stream.error', {\n 'error.type': ctx.err instanceof Error ? ctx.err.name : 'unknown',\n 'error.message': message,\n 'http.response.status_code': ctx.statusCode,\n 'gen_ai.request.id': ctx.requestId,\n })\n }))\n\n // -----------------------------------------------------------------\n // Native tool span — OTel Gen AI `execute_tool` operation\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('tool:before', (ctx) => {\n const span = safeStartSpan(\n `execute_tool ${ctx.displayName}`,\n {\n [GEN_AI.operationName]: 'execute_tool',\n [GEN_AI.system]: activeSystem,\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolType]: 'function',\n [GEN_AI.toolCallId]: ctx.callId,\n 'sentry.op': 'gen_ai.execute_tool',\n 'gen_ai.tool.display_name': ctx.displayName,\n 'gen_ai.agent.turn_id': ctx.turnId,\n 'gen_ai.agent.depth': ctx.depth,\n 'gen_ai.agent.run.id': ctx.runId,\n 'gen_ai.agent.parent_run.id': ctx.parentRunId,\n ...(legacy\n ? {\n toolName: ctx.name,\n displayName: ctx.displayName,\n turnId: ctx.turnId,\n callId: ctx.callId,\n }\n : {}),\n },\n )\n if (span) {\n toolSpans.set(ctx.callId, span)\n if (captureContent) {\n // Attach (possibly redacted) input as an attribute. Sentry's\n // canonical key is `gen_ai.tool.call.arguments`; OTel's\n // older key is `gen_ai.tool.input` (now deprecated in\n // Sentry). Emit one or both based on `conventions`.\n try {\n const serialized = JSON.stringify(ctx.input)\n const redacted = redact('tool-input', serialized, {\n toolName: ctx.name,\n displayName: ctx.displayName,\n callId: ctx.callId,\n turnId: ctx.turnId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallArguments] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolInputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n catch {\n // Non-serializable input (circular refs) — skip the attr;\n // the tool name and call id are still on the span.\n }\n }\n }\n }))\n\n unregisters.push(hooks.hook('tool:after', (ctx) => {\n const span = toolSpans.get(ctx.callId)\n safeSetAttrs(span, {\n 'gen_ai.tool.output_bytes': ctx.outputBytes,\n 'gen_ai.tool.coercions': ctx.coercions?.length ?? 0,\n })\n if (captureContent && typeof ctx.result === 'string') {\n const redacted = redact('tool-result', ctx.result, {\n toolName: ctx.name,\n callId: ctx.callId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallResult] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolOutputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n safeEnd(toolSpans, ctx.callId)\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n safeSetAttrs(toolSpans.get(ctx.callId), {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n safeEnd(toolSpans, ctx.callId)\n }))\n\n // Tool gate / dispatch outcomes. Listen on `tool:dispatched` (fires\n // AFTER every `tool:gate` listener has had a chance to mutate\n // `block` / `result`) so the recorded `outcome` reflects the final\n // decision rather than the as-entered state. Attaches to the\n // in-flight run span; the chat span has already closed by tool\n // dispatch time in the current loop ordering, and no tool span has\n // opened for a `gate-block` / `unknown` / `invalid-input` path.\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n if (ctx.outcome === 'gate-block') {\n safeAddEvent(activeRunSpan, 'gen_ai.gate.block', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n reason: ctx.reason,\n })\n }\n else if (ctx.outcome === 'gate-substitute') {\n safeAddEvent(activeRunSpan, 'gen_ai.gate.substitute', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n })\n }\n else if (ctx.outcome === 'unknown') {\n safeAddEvent(activeRunSpan, 'gen_ai.tool.unknown', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n })\n }\n else if (ctx.outcome === 'invalid-input') {\n safeAddEvent(activeRunSpan, 'gen_ai.tool.invalid_input', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n })\n }\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.validation.reject', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('validation:coerce', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.validation.coerce', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n coercions: ctx.coercions,\n })\n }))\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.budget.exceeded', {\n turnId: ctx.turnId,\n bytes: ctx.bytes,\n budget: ctx.budget,\n })\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.tool_budget.exceeded', {\n [GEN_AI.toolName]: ctx.tool,\n turnId: ctx.turnId,\n count: ctx.count,\n max: ctx.max,\n mode: ctx.mode,\n })\n }))\n\n unregisters.push(hooks.hook('pairing:repair', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.pairing.repair', {\n mode: ctx.mode,\n callId: ctx.callId,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('oauth:refresh', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.oauth.refresh', {\n provider: ctx.provider,\n providerId: ctx.providerId,\n source: ctx.source,\n })\n }))\n\n // -----------------------------------------------------------------\n // MCP tool span — OTel Gen AI `execute_tool` operation, MCP variant\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('mcp:tool:before', (ctx) => {\n const span = safeStartSpan(\n `execute_tool ${ctx.server}.${ctx.tool}`,\n {\n [GEN_AI.operationName]: 'execute_tool',\n [GEN_AI.system]: activeSystem,\n [GEN_AI.toolName]: ctx.displayName,\n // MCP tools are Sentry's \"extension\" tool type — backed by an\n // out-of-process server rather than a local function.\n [GEN_AI.toolType]: 'extension',\n [GEN_AI.toolCallId]: ctx.callId,\n [GEN_AI.mcpServer]: ctx.server,\n 'sentry.op': 'gen_ai.execute_tool',\n 'gen_ai.mcp.tool': ctx.tool,\n 'gen_ai.agent.turn_id': ctx.turnId,\n 'gen_ai.agent.run.id': ctx.runId,\n ...(legacy\n ? {\n server: ctx.server,\n tool: ctx.tool,\n displayName: ctx.displayName,\n turnId: ctx.turnId,\n callId: ctx.callId,\n }\n : {}),\n },\n )\n if (span) {\n mcpSpans.set(ctx.callId, span)\n if (captureContent) {\n try {\n const redacted = redact('mcp-tool-input', JSON.stringify(ctx.input), {\n server: ctx.server,\n tool: ctx.tool,\n callId: ctx.callId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallArguments] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolInputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n catch { /* ignore */ }\n }\n }\n }))\n\n unregisters.push(hooks.hook('mcp:tool:after', (ctx) => {\n const span = mcpSpans.get(ctx.callId)\n safeSetAttrs(span, { 'gen_ai.tool.output_bytes': ctx.outputBytes })\n if (captureContent && typeof ctx.result === 'string') {\n const redacted = redact('mcp-tool-result', ctx.result, {\n server: ctx.server,\n tool: ctx.tool,\n callId: ctx.callId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallResult] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolOutputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n safeEnd(mcpSpans, ctx.callId)\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n safeSetAttrs(mcpSpans.get(ctx.callId), {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n safeEnd(mcpSpans, ctx.callId)\n }))\n\n // -----------------------------------------------------------------\n // MCP server bootstrap span\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('mcp:bootstrap:start', (ctx) => {\n const span = safeStartSpan(\n `mcp.bootstrap ${ctx.name}`,\n {\n [GEN_AI.operationName]: 'mcp.bootstrap',\n [GEN_AI.system]: activeSystem,\n [GEN_AI.mcpServer]: ctx.name,\n 'gen_ai.mcp.transport': ctx.transport,\n },\n )\n if (span)\n bootstrapSpans.set(ctx.name, span)\n }))\n\n unregisters.push(hooks.hook('mcp:bootstrap:end', (ctx) => {\n const span = bootstrapSpans.get(ctx.name)\n const ok = ctx.ok\n safeSetAttrs(span, {\n 'gen_ai.mcp.bootstrap.duration_ms': ctx.durationMs,\n 'gen_ai.mcp.bootstrap.ok': ok,\n ...(ok\n ? {\n [GEN_AI.mcpToolCount]: ctx.toolCount,\n 'gen_ai.mcp.lazy': ctx.lazy,\n 'gen_ai.mcp.cached': ctx.cached,\n }\n : {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n }),\n })\n safeEnd(bootstrapSpans, ctx.name)\n }))\n\n unregisters.push(hooks.hook('mcp:auth:required', (ctx) => {\n const span = bootstrapSpans.get(ctx.name)\n safeAddEvent(span ?? activeRunSpan, 'gen_ai.mcp.auth.required', {\n server: ctx.name,\n transport: ctx.transport,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.mcp.error', {\n 'server': ctx.name,\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n }))\n\n // -----------------------------------------------------------------\n // Spawn span — parent-side. Also injects trace context for the child.\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('spawn:before', (ctx) => {\n // Sentry: \"handoff from {from_agent} to {to_agent}\". We don't\n // always know the child agent's name at spawn time — fall back\n // to the truncated task as the destination identifier.\n const toAgent = ctx.task.length > 60 ? `${ctx.task.slice(0, 57)}…` : ctx.task\n const span = safeStartSpan(\n `handoff to ${toAgent}`,\n {\n [GEN_AI.operationName]: 'handoff',\n [GEN_AI.system]: activeSystem,\n 'sentry.op': 'gen_ai.handoff',\n 'gen_ai.agent.spawn.id': ctx.id,\n 'gen_ai.agent.spawn.task': ctx.task,\n 'gen_ai.agent.depth': ctx.depth,\n },\n )\n if (span)\n spawnSpans.set(ctx.id, span)\n\n // Inject parent trace context for the child agent. The spawn tool\n // passes whatever we write into `ctx.tracingContext` to the child's\n // `agent.run({ tracingContext })` — see `tools/spawn.ts`.\n if (typeof options.getActiveTraceContext === 'function' && ctx.tracingContext) {\n let carrier: Readonly<Record<string, string>> | undefined\n try {\n carrier = options.getActiveTraceContext()\n }\n catch (err) {\n try {\n onError('getActiveTraceContext', err)\n }\n catch { /* ignore */ }\n }\n if (carrier) {\n for (const [k, v] of Object.entries(carrier))\n ctx.tracingContext[k] = v\n }\n }\n }))\n\n unregisters.push(hooks.hook('spawn:complete', (ctx) => {\n const span = spawnSpans.get(ctx.id)\n const childUsage = buildUsageAttrs({\n input: ctx.stats.totalIn,\n output: ctx.stats.totalOut,\n cacheRead: ctx.stats.totalCacheRead,\n cacheCreation: ctx.stats.totalCacheCreation,\n cost: ctx.stats.cost,\n }, conventions)\n safeSetAttrs(span, {\n 'gen_ai.agent.spawn.status': ctx.status ?? 'completed',\n 'gen_ai.agent.spawn.depth': ctx.depth,\n ...childUsage,\n })\n safeEnd(spawnSpans, ctx.id)\n }))\n\n unregisters.push(hooks.hook('spawn:error', (ctx) => {\n safeSetAttrs(spawnSpans.get(ctx.id), {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n safeEnd(spawnSpans, ctx.id)\n }))\n\n // -----------------------------------------------------------------\n // Disposal\n // -----------------------------------------------------------------\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n endAll(runSpans)\n endAll(turnSpans)\n endAll(toolSpans)\n endAll(mcpSpans)\n endAll(spawnSpans)\n endAll(bootstrapSpans)\n activeRunSpan = undefined\n }\n },\n }\n}\n\n/**\n * OpenTelemetry Gen AI semantic-convention attribute keys used by the\n * built-in tracer. Exported so consumers integrating with raw OTel SDKs\n * (instead of going through `createTracingHooks`) can stay aligned with\n * the names the harness itself emits.\n */\nexport const GEN_AI_ATTRIBUTES = GEN_AI\n","/**\n * Zod v4 integration helper.\n *\n * Normalizes the output of z.toJsonSchema() for use as ToolSpec.inputSchema.\n * Zod is an optional peer dependency — consumers call z.toJsonSchema() themselves.\n *\n * Usage:\n * import { z } from 'zod'\n * import { zodToJsonSchema } from 'zidane'\n * const schema = zodToJsonSchema(z.toJsonSchema(z.object({ name: z.string() })))\n */\n\n/**\n * Normalize a JSON Schema (e.g. from zod v4's z.toJsonSchema()) for use\n * as a ToolSpec.inputSchema.\n *\n * Strips the $schema key that some providers reject.\n */\nexport function zodToJsonSchema(jsonSchema: Record<string, unknown>): Record<string, unknown> {\n const { $schema, ...rest } = jsonSchema\n return rest\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["prefixed"],"sources":["../src/cache-telemetry.ts","../src/logger.ts","../src/metrics.ts","../src/run-summary.ts","../src/tracing.ts","../src/zod.ts"],"sourcesContent":["/**\n * Per-dimension cache-break detection.\n *\n * The agent surfaces `cacheRead` / `cacheCreation` in {@link TurnUsage}, but\n * doesn't answer the load-bearing diagnostic question: *which* dimension\n * busted the cache? When `cacheCreation > 0` on turn N+1 with no obvious\n * reason, you want to know whether the system prompt, the tool list, the\n * thinking config, or the model id changed — without re-reading the loop\n * source.\n *\n * This module hashes each cache-affecting dimension at `turn:before`, then\n * on `turn:after` checks whether the latest turn read its share of the\n * cached prefix. If not, it diffs the previous turn's dimension hashes\n * against the current ones and logs the responsible dimension(s).\n *\n * Wire contract:\n *\n * - Install via {@link installCacheBreakLogger} on the agent's hook bus.\n * Returns an uninstall fn. Idempotent — calling install twice on the\n * same bus leaves two listeners; callers manage lifecycle.\n * - Output sink defaults to `console.error`; pass `log` to redirect into a\n * {@link Logger}, span attribute writer, or test buffer.\n * - Heuristic only: a missing cache read on turn 1 is expected (nothing\n * cached yet); the logger only reports from turn 2 onward.\n * - Pure helper {@link diffCacheDimensions} is exported for tests so the\n * diff logic can be exercised without standing up the loop.\n *\n * The dimensions tracked are the inputs the provider treats as part of the\n * cache key — `system`, `tools`, `model`, thinking config. Tools are hashed\n * after `provider.formatTools`, so MCP catalogues showing up mid-run also\n * register as a break. Messages are NOT tracked: the conversation grows by\n * design, and the last-message breakpoint is meant to advance every turn.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { StreamOptions } from './providers'\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Snapshot of per-request dimensions that contribute to the provider-side\n * prompt cache key. Hashed at `turn:before` and stashed for diffing on the\n * next request.\n *\n * Hashes are FNV-1a 32-bit (cheap, no allocations on the hot path,\n * collision rate good enough for diff detection — we're not signing\n * anything). For verbose output the helper also captures byte lengths so\n * `system: 1024 → 1031` reads like a real diff line.\n */\nexport interface CacheDimensionSnapshot {\n /** Hash of `options.system` (with boundary marker preserved). */\n systemHash: number\n /** Byte length of `options.system`. */\n systemBytes: number\n /** Hash of the JSON-serialized `options.tools` array. */\n toolsHash: number\n /** Number of tools in the array. */\n toolsCount: number\n /** Model id. */\n model: string\n /** Thinking level — `'off'` when absent. */\n thinking: string\n /** Exact thinking budget — `0` when absent. */\n thinkingBudget: number\n}\n\n/** Result of {@link diffCacheDimensions}. */\nexport interface CacheDimensionDiff {\n /** Dimension names whose hashes differ between the two snapshots. */\n changed: readonly CacheDimensionName[]\n /** Per-dimension details — only filled for changed dimensions. */\n details: Readonly<Partial<Record<CacheDimensionName, string>>>\n}\n\n/** Cache-affecting dimension names. */\nexport type CacheDimensionName\n = | 'system'\n | 'tools'\n | 'model'\n | 'thinking'\n | 'thinkingBudget'\n\n// ---------------------------------------------------------------------------\n// Configuration\n// ---------------------------------------------------------------------------\n\nexport interface CacheBreakLoggerOptions {\n /**\n * Sink for log lines. Defaults to `console.error`. Use a {@link Logger} or\n * a test buffer here when you want the output to land somewhere other than\n * stderr.\n */\n log?: (line: string) => void\n /**\n * When `true`, emit a log line on EVERY turn (not just cache breaks) with\n * the per-dimension hashes. Useful for offline correlation; off by default\n * to keep the logger zero-noise on happy paths.\n */\n verbose?: boolean\n /**\n * Threshold for \"this turn missed cache\". The logger reports when\n * `cacheRead < expectedReadAtLeast` AND the dimensions differ from the\n * previous turn. Defaults to `1` — any cache read at all means the\n * provider matched some prefix, so a strict \"did we get any cache?\" check\n * is the right signal. Raise to detect partial-prefix breaks (e.g. the\n * tools cache hit but the system prompt didn't, which lowers `cacheRead`\n * without zeroing it).\n */\n expectedReadAtLeast?: number\n}\n\n// ---------------------------------------------------------------------------\n// Hashing\n// ---------------------------------------------------------------------------\n\nconst FNV_OFFSET = 0x811C9DC5\nconst FNV_PRIME = 0x01000193\n\n/**\n * FNV-1a 32-bit hash of a UTF-8 string. Pure, allocation-free, ~10 ns per\n * KB. Cryptographic strength is irrelevant here — we only need byte-change\n * detection, and FNV-1a has effectively zero collision risk for the small\n * number of dimensions tracked.\n *\n * Exported for tests; callers shouldn't typically import directly.\n */\nexport function fnv1a32(s: string): number {\n let h = FNV_OFFSET\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i) & 0xFF\n // Multiply by FNV_PRIME mod 2^32, using `Math.imul` to keep within\n // 32-bit signed range (avoid >>> 0 ping-pong on every iteration).\n h = Math.imul(h, FNV_PRIME)\n }\n // Map signed → unsigned 32-bit so the printed hex is stable.\n return h >>> 0\n}\n\n/**\n * Build a {@link CacheDimensionSnapshot} from a `turn:before` payload.\n *\n * Pure — exported so hosts can compute snapshots outside the logger (e.g.\n * stamping them onto custom telemetry spans). The hash is deterministic on\n * byte-identical inputs.\n */\nexport function snapshotCacheDimensions(options: StreamOptions): CacheDimensionSnapshot {\n const toolsJson = JSON.stringify(options.tools ?? [])\n return {\n systemHash: fnv1a32(options.system),\n systemBytes: options.system.length,\n toolsHash: fnv1a32(toolsJson),\n toolsCount: Array.isArray(options.tools) ? options.tools.length : 0,\n model: options.model,\n thinking: options.thinking ?? 'off',\n thinkingBudget: options.thinkingBudget ?? 0,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Diffing\n// ---------------------------------------------------------------------------\n\n/**\n * Compare two snapshots and return the list of dimensions whose bytes\n * differ. The `details` map carries a short human-readable description per\n * changed dimension (e.g. `system: 1024 → 1031 bytes`).\n *\n * Pure / no I/O. Use this in tests to assert the diff logic; the logger\n * itself just formats and emits the result.\n */\nexport function diffCacheDimensions(\n prev: CacheDimensionSnapshot,\n curr: CacheDimensionSnapshot,\n): CacheDimensionDiff {\n const changed: CacheDimensionName[] = []\n const details: Partial<Record<CacheDimensionName, string>> = {}\n\n if (prev.systemHash !== curr.systemHash) {\n changed.push('system')\n details.system = `${prev.systemBytes} → ${curr.systemBytes} bytes (hash ${hex(prev.systemHash)} → ${hex(curr.systemHash)})`\n }\n if (prev.toolsHash !== curr.toolsHash) {\n changed.push('tools')\n details.tools = `${prev.toolsCount} → ${curr.toolsCount} tools (hash ${hex(prev.toolsHash)} → ${hex(curr.toolsHash)})`\n }\n if (prev.model !== curr.model) {\n changed.push('model')\n details.model = `${prev.model} → ${curr.model}`\n }\n if (prev.thinking !== curr.thinking) {\n changed.push('thinking')\n details.thinking = `${prev.thinking} → ${curr.thinking}`\n }\n if (prev.thinkingBudget !== curr.thinkingBudget) {\n changed.push('thinkingBudget')\n details.thinkingBudget = `${prev.thinkingBudget} → ${curr.thinkingBudget}`\n }\n\n return { changed, details }\n}\n\nfunction hex(n: number): string {\n return `0x${n.toString(16).padStart(8, '0')}`\n}\n\n// ---------------------------------------------------------------------------\n// Hook installer\n// ---------------------------------------------------------------------------\n\n/**\n * Install the cache-break logger on an agent's hook bus.\n *\n * Subscribes to `turn:before` (snapshot) + `turn:after` (compare with prior\n * snapshot, log breaks). Returns an `uninstall` fn that detaches both\n * listeners. Idempotent w.r.t. agent lifecycle — installs/uninstalls cleanly\n * across runs.\n *\n * Recommended setup:\n *\n * ```ts\n * const uninstall = installCacheBreakLogger(agent.hooks, {\n * log: line => logger.debug(line),\n * verbose: !!process.env.ZIDANE_DEBUG?.includes('cache'),\n * })\n * // ...\n * uninstall()\n * ```\n *\n * Performance: hashing runs on each `turn:before` and is bounded by the\n * input size (system + tools serialization). Disabled callers pay nothing\n * (don't install).\n */\nexport function installCacheBreakLogger(\n hooks: Hookable<AgentHooks>,\n options: CacheBreakLoggerOptions = {},\n): () => void {\n const log = options.log ?? ((line: string) => console.error(line))\n const verbose = options.verbose === true\n const expectedReadAtLeast = options.expectedReadAtLeast ?? 1\n\n // Per-bus latest snapshot. Keyed implicitly by the bus instance —\n // installing on a fresh bus starts from empty. Across runs on the same\n // bus, the snapshot persists, which is what we want: the first turn of\n // run N+1 should hit cache if it shares a prefix with the last turn of\n // run N. The detector reports correctly across that boundary.\n let prev: CacheDimensionSnapshot | undefined\n let turnIndex = 0\n let pending: { snapshot: CacheDimensionSnapshot, turnIndex: number, turnId: string } | undefined\n\n function onTurnBefore(ctx: { turn: number, turnId: string, options: StreamOptions }): void {\n const curr = snapshotCacheDimensions(ctx.options)\n turnIndex += 1\n\n if (verbose) {\n log(formatVerboseLine({\n turn: turnIndex,\n turnId: ctx.turnId,\n snapshot: curr,\n }))\n }\n\n // Stash for the matching `turn:after`. We can't compute the diff here —\n // the cache-read result lands on `turn:after.usage`.\n pending = { snapshot: curr, turnIndex, turnId: ctx.turnId }\n }\n\n function onTurnAfter(ctx: { turn: number, turnId: string, usage: { cacheRead?: number, cacheCreation?: number } }): void {\n if (!pending || pending.turnId !== ctx.turnId)\n return\n const { snapshot: curr, turnIndex: tIdx } = pending\n pending = undefined\n\n // Skip break detection on providers that don't report `cacheRead` —\n // treating undefined as 0 would log a false break every turn on Codex,\n // Cerebras, or any host without a cache_read field. We can only diagnose\n // breaks when the provider tells us how much cache was actually used.\n if (prev && tIdx > 1 && typeof ctx.usage.cacheRead === 'number') {\n const cacheRead = ctx.usage.cacheRead\n if (cacheRead < expectedReadAtLeast) {\n const diff = diffCacheDimensions(prev, curr)\n if (diff.changed.length > 0) {\n log(formatBreakLine({\n turn: tIdx,\n turnId: ctx.turnId,\n cacheRead,\n cacheCreation: ctx.usage.cacheCreation ?? 0,\n diff,\n }))\n }\n }\n }\n\n prev = curr\n }\n\n // Hookable's `.hook()` returns its own unregister fn — keep both so\n // `uninstall()` is symmetric with the install path and survives a\n // future hookable rename of `removeHook`.\n const unregisterBefore = hooks.hook('turn:before', onTurnBefore)\n const unregisterAfter = hooks.hook('turn:after', onTurnAfter)\n\n return () => {\n unregisterBefore()\n unregisterAfter()\n }\n}\n\n// ---------------------------------------------------------------------------\n// Formatting\n// ---------------------------------------------------------------------------\n\nfunction formatBreakLine(input: {\n turn: number\n turnId: string\n cacheRead: number\n cacheCreation: number\n diff: CacheDimensionDiff\n}): string {\n const names = input.diff.changed.join(', ')\n const details = input.diff.changed\n .map(d => ` ${d}: ${input.diff.details[d]}`)\n .join('\\n')\n return [\n `[zidane:cache-break] turn=${input.turn} turnId=${input.turnId} cacheRead=${input.cacheRead} cacheCreation=${input.cacheCreation}`,\n ` changed: ${names}`,\n details,\n ].join('\\n')\n}\n\nfunction formatVerboseLine(input: {\n turn: number\n turnId: string\n snapshot: CacheDimensionSnapshot\n}): string {\n const s = input.snapshot\n return `[zidane:cache] turn=${input.turn} turnId=${input.turnId} system=${hex(s.systemHash)}(${s.systemBytes}b) tools=${hex(s.toolsHash)}(${s.toolsCount}) model=${s.model} thinking=${s.thinking}/${s.thinkingBudget}`\n}\n","/**\n * Structured logging primitive with auto-correlation.\n *\n * Every consumer of the harness used to roll its own logger; the TUI has\n * one, downstream apps have theirs, observability backends each want a\n * slightly different shape. This module ships the smallest reasonable\n * shared surface:\n *\n * - {@link Logger} — minimal level-tagged interface (`debug` / `info` /\n * `warn` / `error`) plus a `with(...)` method that returns a child\n * logger carrying baseline attributes (`runId`, `turnId`, `callId`,\n * `childId`, `depth`, anything you want).\n * - {@link createLogger} — builds a Logger from a {@link LogSink}.\n * - {@link consoleSink} / {@link jsonSink} — built-in sinks that cover\n * the common cases (human-readable to a terminal vs. one JSON object\n * per line to a log aggregator).\n * - {@link createLoggingHooks} — installs Logger-attached hook handlers\n * on an agent so every lifecycle event lands on the sink with the\n * right correlation ids already stamped in.\n *\n * Hosts that already own a logger (pino, winston, structlog binding,\n * platform-native) plug it in via a custom {@link LogSink} — the helper\n * itself does no I/O.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\n\n// ---------------------------------------------------------------------------\n// Core types\n// ---------------------------------------------------------------------------\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\nexport interface LogRecord {\n level: LogLevel\n /** Unix ms — set by `Logger` at emit time. */\n timestamp: number\n /** Free-form message. Sinks render this as the human-facing line. */\n message: string\n /** Structured fields. Correlation ids land here automatically. */\n attrs: Record<string, unknown>\n}\n\nexport interface LogSink {\n emit: (record: LogRecord) => void\n}\n\nexport interface Logger {\n debug: (message: string, attrs?: Record<string, unknown>) => void\n info: (message: string, attrs?: Record<string, unknown>) => void\n warn: (message: string, attrs?: Record<string, unknown>) => void\n error: (message: string, attrs?: Record<string, unknown>) => void\n /**\n * Returns a child logger that prepends the given attributes onto every\n * subsequent emit. Equivalent to `pino.child` / `winston.child`. The\n * parent and child share the same sink — children are zero-cost.\n */\n with: (extra: Record<string, unknown>) => Logger\n /**\n * Inspectable baseline attributes — handy for tests and for hook\n * handlers that want to clone-with-extra without recursing.\n */\n readonly baseAttributes: Readonly<Record<string, unknown>>\n}\n\n// ---------------------------------------------------------------------------\n// createLogger\n// ---------------------------------------------------------------------------\n\n/**\n * Build a Logger from a sink. Stateless and cheap; create one per agent\n * (or per app) and use `.with()` to attach correlation ids per-call.\n */\nexport function createLogger(\n sink: LogSink,\n baseAttributes: Readonly<Record<string, unknown>> = {},\n): Logger {\n function emit(level: LogLevel, message: string, attrs?: Record<string, unknown>): void {\n try {\n sink.emit({\n level,\n timestamp: Date.now(),\n message,\n attrs: attrs ? { ...baseAttributes, ...attrs } : { ...baseAttributes },\n })\n }\n catch {\n // Sinks should never crash the run.\n }\n }\n\n return {\n debug: (m, a) => emit('debug', m, a),\n info: (m, a) => emit('info', m, a),\n warn: (m, a) => emit('warn', m, a),\n error: (m, a) => emit('error', m, a),\n with: extra => createLogger(sink, { ...baseAttributes, ...extra }),\n baseAttributes,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Built-in sinks\n// ---------------------------------------------------------------------------\n\nexport interface ConsoleSinkOptions {\n /**\n * Minimum level to emit. Defaults to `'info'` — `debug` is dropped so\n * the harness's lifecycle logging is not noisy by default. Set to\n * `'debug'` to see every event.\n */\n minLevel?: LogLevel\n /** Custom output stream. Defaults to `process.stderr` so logs don't pollute stdout. */\n stream?: { write: (chunk: string) => void }\n}\n\nconst LEVEL_ORDER: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n}\n\n/**\n * Human-readable terminal sink. Renders each record as\n * `<ISO timestamp> <LEVEL> <message> <attrs as kv pairs>`.\n *\n * Honors `process.stderr` by default so log lines don't interleave with\n * the agent's stdout-bound output (chat responses, JSON results).\n */\nexport function consoleSink(options: ConsoleSinkOptions = {}): LogSink {\n const min = LEVEL_ORDER[options.minLevel ?? 'info']\n const stream = options.stream ?? process.stderr\n return {\n emit(record) {\n if (LEVEL_ORDER[record.level] < min)\n return\n const ts = new Date(record.timestamp).toISOString()\n const kv = Object.entries(record.attrs)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => `${k}=${typeof v === 'string' ? v : JSON.stringify(v)}`)\n .join(' ')\n stream.write(`${ts} ${record.level.toUpperCase().padEnd(5)} ${record.message}${kv ? ` ${kv}` : ''}\\n`)\n },\n }\n}\n\n/**\n * One-JSON-object-per-line sink. Suitable for piping into log aggregators\n * (Datadog Agent, Fluent Bit, Loki, Vector) that expect JSONL.\n */\nexport function jsonSink(options: ConsoleSinkOptions = {}): LogSink {\n const min = LEVEL_ORDER[options.minLevel ?? 'info']\n const stream = options.stream ?? process.stderr\n return {\n emit(record) {\n if (LEVEL_ORDER[record.level] < min)\n return\n try {\n stream.write(`${JSON.stringify(record)}\\n`)\n }\n catch {\n // Non-serializable attr (cycle, BigInt, etc.). Skip rather than throw.\n }\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// createLoggingHooks\n// ---------------------------------------------------------------------------\n\nexport interface LoggingHooksOptions {\n logger: Logger\n /**\n * Minimum interesting level for harness-emitted lines. Default `'info'`.\n * Set to `'debug'` to see every tool dispatch / stream event. Set to\n * `'warn'` to mute the chatty ones and only see failures + budgets.\n */\n level?: LogLevel\n /**\n * When true (default), lifecycle events (`agent:start`, `turn:before`,\n * `tool:before`, `mcp:bootstrap:start`) emit at `debug` level so they\n * stay quiet by default. Set false to mute them entirely regardless of\n * the configured minimum level — useful when piping into a tracer\n * that already captures lifecycle.\n */\n includeLifecycle?: boolean\n}\n\nexport interface LoggingHookSet {\n install: (hooks: Hookable<AgentHooks>) => () => void\n}\n\n/**\n * Install a bundle of hook handlers that emit a structured line per\n * relevant lifecycle event, automatically attaching correlation ids\n * (`runId`, `turnId`, `callId`, `childId`, `depth`, `agentName`).\n *\n * @example\n * ```ts\n * const logger = createLogger(consoleSink({ minLevel: 'debug' }), { service: 'tui' })\n * const lh = createLoggingHooks({ logger })\n * const uninstall = lh.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n */\nexport function createLoggingHooks(options: LoggingHooksOptions): LoggingHookSet {\n const root = options.logger\n const includeLifecycle = options.includeLifecycle ?? true\n const minLevel = LEVEL_ORDER[options.level ?? 'info']\n\n /**\n * Wrap a Logger so emissions below `minLevel` are dropped before they\n * hit the sink. Lets us gate harness chatter without forcing every\n * `LogSink` to re-implement level filtering. Preserves `.with()`\n * composition (children inherit the filter).\n */\n function gateLevel(logger: Logger): Logger {\n const skip = (level: LogLevel): boolean => LEVEL_ORDER[level] < minLevel\n const wrap = (l: Logger): Logger => ({\n debug: (m, a) => {\n if (!skip('debug'))\n l.debug(m, a)\n },\n info: (m, a) => {\n if (!skip('info'))\n l.info(m, a)\n },\n warn: (m, a) => {\n if (!skip('warn'))\n l.warn(m, a)\n },\n error: (m, a) => {\n if (!skip('error'))\n l.error(m, a)\n },\n with: extra => wrap(l.with(extra)),\n baseAttributes: l.baseAttributes,\n })\n return wrap(logger)\n }\n\n return {\n install(hooks: Hookable<AgentHooks>): () => void {\n const unregisters: Array<() => void> = []\n // Per-install loggers, refreshed on `agent:start` / `turn:before`.\n // Scoped INSIDE install() so concurrent installs across different\n // agents don't share state — each call to `install()` gets its own\n // attribution state.\n const gatedRoot = gateLevel(root)\n let runLogger = gatedRoot\n let turnLogger = gatedRoot\n\n // ---- Agent lifecycle --------------------------------------------\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n runLogger = gatedRoot.with({\n runId: ctx.runId,\n ...(ctx.parentRunId ? { parentRunId: ctx.parentRunId } : {}),\n depth: ctx.depth,\n ...(ctx.agentName ? { agentName: ctx.agentName } : {}),\n })\n turnLogger = runLogger\n if (includeLifecycle)\n runLogger.debug('agent run started')\n }))\n\n unregisters.push(hooks.hook('agent:done', (stats) => {\n runLogger.info('agent run completed', {\n turns: stats.turns,\n totalIn: stats.totalIn,\n totalOut: stats.totalOut,\n ...(typeof stats.cost === 'number' ? { cost: stats.cost } : {}),\n elapsedMs: stats.elapsed,\n ...(typeof stats.timeTillFirstTokenMs === 'number' ? { ttftMs: stats.timeTillFirstTokenMs } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n runLogger.warn('agent run aborted')\n }))\n\n // ---- Turn / stream ---------------------------------------------\n\n unregisters.push(hooks.hook('turn:before', (ctx) => {\n turnLogger = runLogger.with({ turnId: ctx.turnId, turn: ctx.turn })\n if (includeLifecycle)\n turnLogger.debug('turn started')\n }))\n\n unregisters.push(hooks.hook('turn:after', (ctx) => {\n turnLogger.debug('turn ended', {\n inputTokens: ctx.usage.input,\n outputTokens: ctx.usage.output,\n ...(ctx.usage.finishReason ? { finishReason: ctx.usage.finishReason } : {}),\n ...(ctx.usage.modelId ? { modelId: ctx.usage.modelId } : {}),\n ...(typeof ctx.usage.timeToFirstTokenMs === 'number' ? { ttftMs: ctx.usage.timeToFirstTokenMs } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n turnLogger.error('stream error', {\n message: ctx.err instanceof Error ? ctx.err.message : String(ctx.err),\n ...(ctx.statusCode !== undefined ? { statusCode: ctx.statusCode } : {}),\n ...(ctx.requestId !== undefined ? { requestId: ctx.requestId } : {}),\n })\n }))\n\n // ---- Tool calls -------------------------------------------------\n\n unregisters.push(hooks.hook('tool:before', (ctx) => {\n if (!includeLifecycle)\n return\n turnLogger.debug('tool started', {\n toolName: ctx.name,\n displayName: ctx.displayName,\n callId: ctx.callId,\n })\n }))\n\n unregisters.push(hooks.hook('tool:after', (ctx) => {\n if (!includeLifecycle)\n return\n turnLogger.debug('tool ended', {\n toolName: ctx.name,\n callId: ctx.callId,\n outputBytes: ctx.outputBytes,\n })\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n turnLogger.error('tool error', {\n toolName: ctx.name,\n callId: ctx.callId,\n message: ctx.error.message,\n })\n }))\n\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n // Successful + gate-substitute paths are routine — only log at\n // debug level. The three \"something refused / went wrong\" paths\n // (gate-block, unknown tool, invalid input) land at warn so they\n // surface in default-config dashboards without forcing debug.\n const isAnomaly = ctx.outcome === 'gate-block'\n || ctx.outcome === 'unknown'\n || ctx.outcome === 'invalid-input'\n if (!isAnomaly && !includeLifecycle)\n return\n const lvl: LogLevel = isAnomaly ? 'warn' : 'debug'\n turnLogger[lvl]('tool dispatched', {\n toolName: ctx.name,\n callId: ctx.callId,\n outcome: ctx.outcome,\n ...(ctx.reason ? { reason: ctx.reason } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n turnLogger.warn('tool input rejected', {\n toolName: ctx.name,\n callId: ctx.callId,\n reason: ctx.reason,\n })\n }))\n\n // ---- Budgets ----------------------------------------------------\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n turnLogger.warn('byte budget exceeded', {\n bytes: ctx.bytes,\n budget: ctx.budget,\n })\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n turnLogger.warn('tool budget exceeded', {\n toolName: ctx.tool,\n count: ctx.count,\n max: ctx.max,\n mode: ctx.mode,\n })\n }))\n\n // ---- MCP --------------------------------------------------------\n\n unregisters.push(hooks.hook('mcp:bootstrap:end', (ctx) => {\n if (ctx.ok) {\n if (includeLifecycle) {\n runLogger.debug('mcp bootstrap ok', {\n server: ctx.name,\n transport: ctx.transport,\n durationMs: ctx.durationMs,\n toolCount: ctx.toolCount,\n ...(ctx.lazy ? { lazy: true } : {}),\n ...(ctx.cached ? { cached: true } : {}),\n })\n }\n }\n else {\n runLogger.warn('mcp bootstrap failed', {\n server: ctx.name,\n transport: ctx.transport,\n durationMs: ctx.durationMs,\n message: ctx.error.message,\n })\n }\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n runLogger.error('mcp error', {\n server: ctx.name,\n message: ctx.error.message,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:auth:required', (ctx) => {\n runLogger.warn('mcp auth required', {\n server: ctx.name,\n transport: ctx.transport,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n turnLogger.error('mcp tool error', {\n server: ctx.server,\n tool: ctx.displayName,\n callId: ctx.callId,\n message: ctx.error.message,\n })\n }))\n\n // ---- Spawn ------------------------------------------------------\n\n unregisters.push(hooks.hook('spawn:before', (ctx) => {\n if (!includeLifecycle)\n return\n runLogger.debug('spawn started', {\n childId: ctx.id,\n depth: ctx.depth,\n })\n }))\n\n unregisters.push(hooks.hook('spawn:complete', (ctx) => {\n runLogger.info('spawn completed', {\n childId: ctx.id,\n ...(ctx.depth ? { depth: ctx.depth } : {}),\n status: ctx.status ?? 'completed',\n turns: ctx.stats.turns,\n totalIn: ctx.stats.totalIn,\n totalOut: ctx.stats.totalOut,\n ...(typeof ctx.stats.cost === 'number' ? { cost: ctx.stats.cost } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('spawn:error', (ctx) => {\n runLogger.error('spawn error', {\n childId: ctx.id,\n ...(ctx.depth ? { depth: ctx.depth } : {}),\n message: ctx.error.message,\n })\n }))\n\n // -----------------------------------------------------------------\n // Disposal\n // -----------------------------------------------------------------\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n }\n },\n }\n}\n","/**\n * Metrics helper — wraps agent lifecycle hooks in caller-provided meters.\n *\n * Symmetric with {@link createTracingHooks} from `./tracing`. Where the\n * tracer answers \"what happened in this run?\" by producing spans, the\n * metrics helper answers \"how is the fleet behaving?\" by emitting\n * counters / histograms / up-down counters. Both are tracer-agnostic —\n * the caller supplies a `Meter` (OTel-API-shaped) and the helper plugs\n * the right hook on each metric.\n *\n * Instrument families:\n *\n * ### Histograms (distribution metrics)\n * - `gen_ai.client.operation.duration` (ms) — wall-clock per turn (`turn:before` → `turn:after`).\n * - `gen_ai.client.token.usage` — `input` / `output` token counts per turn (tagged by `gen_ai.token.type`).\n * - `gen_ai.client.time_to_first_token` (ms) — per-turn TTFT from `TurnUsage.timeToFirstTokenMs`.\n * - `gen_ai.tool.duration` (ms) — native tool execution time (`tool:before` → `tool:after`).\n * - `gen_ai.tool.output_bytes` — `tool:after.outputBytes`.\n * - `gen_ai.mcp.tool.duration` (ms) — MCP tool execution time.\n * - `gen_ai.mcp.bootstrap.duration` (ms) — MCP server bootstrap (already measured by harness).\n *\n * ### Counters (monotonic)\n * - `gen_ai.agent.runs` — `agent:start`.\n * - `gen_ai.agent.runs.completed` — `agent:done`.\n * - `gen_ai.agent.aborts` — `agent:abort`.\n * - `gen_ai.tool.calls` — `tool:dispatched` (tagged by `outcome`).\n * - `gen_ai.tool.errors` — `tool:error`.\n * - `gen_ai.mcp.tool.errors` — `mcp:tool:error`.\n * - `gen_ai.stream.errors` — `stream:error` (tagged by `status_code`).\n * - `gen_ai.validation.rejects` — `validation:reject`.\n * - `gen_ai.validation.coercions` — `validation:coerce`.\n * - `gen_ai.gate.blocks` — `tool:dispatched` outcome `gate-block`.\n * - `gen_ai.budget.exceeded` — `budget:exceeded`.\n * - `gen_ai.tool_budget.exceeded` — `tool-budget:exceeded` (tagged by `mode`).\n * - `gen_ai.pairing.repairs` — `pairing:repair` (tagged by `mode`).\n * - `gen_ai.oauth.refreshes` — `oauth:refresh`.\n * - `gen_ai.mcp.errors` — `mcp:error`.\n * - `gen_ai.mcp.auth.required` — `mcp:auth:required`.\n * - `gen_ai.cost_usd` — accumulated cost (added on `turn:after`).\n *\n * ### Up-down counter (gauge-shaped)\n * - `gen_ai.agent.runs.active` — incremented on `agent:start`, decremented on `agent:done`.\n *\n * The `Meter` shape mirrors `@opentelemetry/api`'s `Meter` exactly — passing\n * `metrics.getMeter('zidane')` works directly. Hosts using a different\n * metrics backend (StatsD, Prometheus client, custom in-memory aggregator)\n * implement the same three factory methods and the helper plugs in\n * unchanged.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\n\n// ---------------------------------------------------------------------------\n// OTel-shaped Meter interface\n// ---------------------------------------------------------------------------\n\nexport type MetricAttributes = Record<string, string | number | boolean | undefined>\n\nexport interface Counter {\n add: (value: number, attributes?: MetricAttributes) => void\n}\n\nexport interface Histogram {\n record: (value: number, attributes?: MetricAttributes) => void\n}\n\nexport interface UpDownCounter {\n add: (value: number, attributes?: MetricAttributes) => void\n}\n\nexport interface InstrumentOptions {\n description?: string\n unit?: string\n}\n\n/**\n * Minimal Meter interface — structurally identical to OTel's `Meter`.\n * Hosts passing `metrics.getMeter(name)` (from `@opentelemetry/api`)\n * satisfy this without adaptation.\n */\nexport interface Meter {\n createCounter: (name: string, options?: InstrumentOptions) => Counter\n createHistogram: (name: string, options?: InstrumentOptions) => Histogram\n createUpDownCounter: (name: string, options?: InstrumentOptions) => UpDownCounter\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface MetricsHooksOptions {\n meter: Meter\n /**\n * Optional prefix prepended to every instrument name. Default: no prefix\n * (instrument names follow OTel Gen AI semantic conventions verbatim,\n * which is the most-portable shape). Set to e.g. `'zidane.'` to\n * namespace inside a shared meter registry.\n */\n namespace?: string\n /**\n * Optional baseline attributes applied to every measurement. Typical\n * use: `{ service: 'tui', env: 'prod' }`. Per-event attributes win on\n * key collision.\n */\n baseAttributes?: MetricAttributes\n /**\n * Error sink for meter failures. The helper still swallows the throw\n * so a broken backend can't crash a run; this callback surfaces the\n * failure for ops dashboards.\n */\n onError?: (kind: string, err: unknown) => void\n}\n\nexport interface MetricsHookSet {\n install: (hooks: Hookable<AgentHooks>) => () => void\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction prefixed(prefix: string | undefined, name: string): string {\n return prefix ? `${prefix}${name}` : name\n}\n\n/**\n * Drop `undefined` entries before handing attributes to a metrics\n * backend. OTel's `@opentelemetry/api` rejects `undefined` attribute\n * values (and Prometheus / StatsD adapters silently mis-label them); the\n * helper's own API surface allows `undefined` for ergonomic call sites\n * (`{...(cond ? { foo } : {})}`-style spreads), so we strip here at the\n * boundary.\n */\nfunction mergeAttrs(\n base: MetricAttributes | undefined,\n extra: MetricAttributes,\n): Record<string, string | number | boolean> {\n const out: Record<string, string | number | boolean> = {}\n if (base) {\n for (const [k, v] of Object.entries(base)) {\n if (v !== undefined)\n out[k] = v\n }\n }\n for (const [k, v] of Object.entries(extra)) {\n if (v !== undefined)\n out[k] = v\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// createMetricsHooks\n// ---------------------------------------------------------------------------\n\n/**\n * Build a set of metrics hook handlers that can be installed on an agent.\n *\n * @example OpenTelemetry\n * ```ts\n * import { metrics } from '@opentelemetry/api'\n * const meter = metrics.getMeter('zidane')\n * const m = createMetricsHooks({ meter, baseAttributes: { service: 'tui' } })\n * const uninstall = m.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n */\nexport function createMetricsHooks(options: MetricsHooksOptions): MetricsHookSet {\n const ns = options.namespace\n const base = options.baseAttributes\n const onError = options.onError ?? (() => {})\n\n const make = {\n counter: (name: string, opts?: InstrumentOptions) =>\n safeFactory(() => options.meter.createCounter(prefixed(ns, name), opts), name, onError),\n histogram: (name: string, opts?: InstrumentOptions) =>\n safeFactory(() => options.meter.createHistogram(prefixed(ns, name), opts), name, onError),\n upDown: (name: string, opts?: InstrumentOptions) =>\n safeFactory(() => options.meter.createUpDownCounter(prefixed(ns, name), opts), name, onError),\n }\n\n // Instruments — created once at install time.\n const turnDuration = make.histogram('gen_ai.client.operation.duration', { unit: 'ms', description: 'Per-turn LLM operation duration.' })\n const tokenUsage = make.histogram('gen_ai.client.token.usage', { unit: 'tokens', description: 'Per-turn token usage (tagged by gen_ai.token.type).' })\n const ttft = make.histogram('gen_ai.client.time_to_first_token', { unit: 'ms', description: 'Per-turn time to first token.' })\n const toolDuration = make.histogram('gen_ai.tool.duration', { unit: 'ms', description: 'Native tool execution wall clock.' })\n const toolOutput = make.histogram('gen_ai.tool.output_bytes', { unit: 'By', description: 'Native tool output bytes.' })\n const mcpToolDuration = make.histogram('gen_ai.mcp.tool.duration', { unit: 'ms', description: 'MCP tool execution wall clock.' })\n const mcpBootstrapDuration = make.histogram('gen_ai.mcp.bootstrap.duration', { unit: 'ms', description: 'MCP server bootstrap duration.' })\n\n const runsStarted = make.counter('gen_ai.agent.runs', { description: 'Runs started.' })\n const runsCompleted = make.counter('gen_ai.agent.runs.completed', { description: 'Runs completed.' })\n const aborts = make.counter('gen_ai.agent.aborts', { description: 'Run abort events.' })\n const toolCalls = make.counter('gen_ai.tool.calls', { description: 'Tool dispatches (tagged by outcome).' })\n const toolErrors = make.counter('gen_ai.tool.errors', { description: 'Native tool errors.' })\n const mcpToolErrors = make.counter('gen_ai.mcp.tool.errors', { description: 'MCP tool errors.' })\n const streamErrors = make.counter('gen_ai.stream.errors', { description: 'Provider stream errors.' })\n const validationRejects = make.counter('gen_ai.validation.rejects', { description: 'Tool input validation rejects.' })\n const validationCoercions = make.counter('gen_ai.validation.coercions', { description: 'Tool input auto-coercions.' })\n const gateBlocks = make.counter('gen_ai.gate.blocks', { description: 'Tool-gate refusals.' })\n const budgetExceeded = make.counter('gen_ai.budget.exceeded', { description: 'Per-turn byte budget exceedances.' })\n const toolBudgetExceeded = make.counter('gen_ai.tool_budget.exceeded', { description: 'Per-tool dispatch budget exceedances.' })\n const pairingRepairs = make.counter('gen_ai.pairing.repairs', { description: 'Tool-pairing repair counts.' })\n const oauthRefreshes = make.counter('gen_ai.oauth.refreshes', { description: 'Provider OAuth credential refreshes.' })\n const mcpErrors = make.counter('gen_ai.mcp.errors', { description: 'MCP server errors.' })\n const mcpAuthRequired = make.counter('gen_ai.mcp.auth.required', { description: 'MCP bootstrap blocked on missing auth.' })\n const costMeter = make.counter('gen_ai.cost_usd', { unit: 'USD', description: 'Cumulative cost across turns.' })\n\n const runsActive = make.upDown('gen_ai.agent.runs.active', { description: 'In-flight agent runs.' })\n\n return {\n install(hooks: Hookable<AgentHooks>): () => void {\n // Per-instrument book-keeping. Each map tracks instrument start times\n // keyed by the identifier that distinguishes concurrent in-flight\n // instances of that kind.\n const turnStart = new Map<string, number>() // turnId -> startedAt\n const toolStart = new Map<string, number>() // callId -> startedAt\n const mcpStart = new Map<string, number>() // callId -> startedAt\n\n const unregisters: Array<() => void> = []\n\n const record = <T extends { record: (v: number, a?: MetricAttributes) => void }>(\n instrument: T | undefined,\n value: number,\n attrs: MetricAttributes,\n kind: string,\n ): void => {\n if (!instrument)\n return\n try {\n instrument.record(value, mergeAttrs(base, attrs))\n }\n catch (err) {\n try {\n onError(kind, err)\n }\n catch { /* ignore */ }\n }\n }\n\n const add = <T extends { add: (v: number, a?: MetricAttributes) => void }>(\n instrument: T | undefined,\n value: number,\n attrs: MetricAttributes,\n kind: string,\n ): void => {\n if (!instrument)\n return\n try {\n instrument.add(value, mergeAttrs(base, attrs))\n }\n catch (err) {\n try {\n onError(kind, err)\n }\n catch { /* ignore */ }\n }\n }\n\n // ---- Agent lifecycle --------------------------------------------\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n const attrs: MetricAttributes = {\n 'gen_ai.agent.depth': ctx.depth,\n ...(ctx.agentName ? { 'gen_ai.agent.name': ctx.agentName } : {}),\n }\n add(runsStarted, 1, attrs, 'runs')\n add(runsActive, 1, attrs, 'runs.active')\n }))\n\n unregisters.push(hooks.hook('agent:done', () => {\n add(runsCompleted, 1, {}, 'runs.completed')\n add(runsActive, -1, {}, 'runs.active')\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n add(aborts, 1, {}, 'aborts')\n }))\n\n // ---- Turn / stream metrics -------------------------------------\n\n unregisters.push(hooks.hook('turn:before', (ctx) => {\n turnStart.set(ctx.turnId, Date.now())\n }))\n\n unregisters.push(hooks.hook('turn:after', (ctx) => {\n const started = turnStart.get(ctx.turnId)\n turnStart.delete(ctx.turnId)\n const tagsBase: MetricAttributes = {\n 'gen_ai.operation.name': 'chat',\n ...(ctx.usage.modelId ? { 'gen_ai.response.model': ctx.usage.modelId } : {}),\n ...(ctx.usage.finishReason ? { 'gen_ai.response.finish_reason': ctx.usage.finishReason } : {}),\n }\n if (typeof started === 'number')\n record(turnDuration, Date.now() - started, tagsBase, 'turn.duration')\n if (typeof ctx.usage.timeToFirstTokenMs === 'number')\n record(ttft, ctx.usage.timeToFirstTokenMs, tagsBase, 'ttft')\n\n record(tokenUsage, ctx.usage.input, { ...tagsBase, 'gen_ai.token.type': 'input' }, 'token.input')\n record(tokenUsage, ctx.usage.output, { ...tagsBase, 'gen_ai.token.type': 'output' }, 'token.output')\n if (typeof ctx.usage.cacheRead === 'number' && ctx.usage.cacheRead > 0)\n record(tokenUsage, ctx.usage.cacheRead, { ...tagsBase, 'gen_ai.token.type': 'cache_read' }, 'token.cache_read')\n if (typeof ctx.usage.cacheCreation === 'number' && ctx.usage.cacheCreation > 0)\n record(tokenUsage, ctx.usage.cacheCreation, { ...tagsBase, 'gen_ai.token.type': 'cache_creation' }, 'token.cache_creation')\n\n if (typeof ctx.usage.cost === 'number' && ctx.usage.cost > 0)\n add(costMeter, ctx.usage.cost, tagsBase, 'cost')\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n add(streamErrors, 1, {\n 'http.response.status_code': ctx.statusCode,\n 'error.type': ctx.err instanceof Error ? ctx.err.name : 'unknown',\n }, 'stream.error')\n }))\n\n // ---- Native tool metrics ---------------------------------------\n\n unregisters.push(hooks.hook('tool:before', (ctx) => {\n toolStart.set(ctx.callId, Date.now())\n }))\n\n unregisters.push(hooks.hook('tool:after', (ctx) => {\n const started = toolStart.get(ctx.callId)\n toolStart.delete(ctx.callId)\n const tags: MetricAttributes = { 'gen_ai.tool.name': ctx.name }\n if (typeof started === 'number')\n record(toolDuration, Date.now() - started, tags, 'tool.duration')\n record(toolOutput, ctx.outputBytes, tags, 'tool.output_bytes')\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n toolStart.delete(ctx.callId)\n add(toolErrors, 1, {\n 'gen_ai.tool.name': ctx.name,\n 'error.type': ctx.error.name,\n }, 'tool.errors')\n }))\n\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n add(toolCalls, 1, {\n 'gen_ai.tool.name': ctx.name,\n 'outcome': ctx.outcome,\n }, 'tool.calls')\n if (ctx.outcome === 'gate-block') {\n add(gateBlocks, 1, {\n 'gen_ai.tool.name': ctx.name,\n ...(ctx.reason ? { reason: ctx.reason } : {}),\n }, 'gate.blocks')\n }\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n add(validationRejects, 1, { 'gen_ai.tool.name': ctx.name }, 'validation.rejects')\n }))\n\n unregisters.push(hooks.hook('validation:coerce', (ctx) => {\n add(validationCoercions, 1, {\n 'gen_ai.tool.name': ctx.name,\n 'coercions': ctx.coercions.length,\n }, 'validation.coercions')\n }))\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n add(budgetExceeded, 1, {\n bytes: ctx.bytes,\n budget: ctx.budget,\n }, 'budget.exceeded')\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n add(toolBudgetExceeded, 1, {\n 'gen_ai.tool.name': ctx.tool,\n 'mode': ctx.mode,\n 'count': ctx.count,\n 'max': ctx.max,\n }, 'tool_budget.exceeded')\n }))\n\n unregisters.push(hooks.hook('pairing:repair', (ctx) => {\n add(pairingRepairs, 1, { mode: ctx.mode }, 'pairing.repairs')\n }))\n\n unregisters.push(hooks.hook('oauth:refresh', (ctx) => {\n add(oauthRefreshes, 1, { provider: ctx.provider, source: ctx.source }, 'oauth.refreshes')\n }))\n\n // ---- MCP metrics -----------------------------------------------\n\n unregisters.push(hooks.hook('mcp:tool:before', (ctx) => {\n mcpStart.set(ctx.callId, Date.now())\n }))\n\n unregisters.push(hooks.hook('mcp:tool:after', (ctx) => {\n const started = mcpStart.get(ctx.callId)\n mcpStart.delete(ctx.callId)\n const tags: MetricAttributes = {\n 'gen_ai.mcp.server': ctx.server,\n 'gen_ai.tool.name': ctx.displayName,\n }\n if (typeof started === 'number')\n record(mcpToolDuration, Date.now() - started, tags, 'mcp.tool.duration')\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n mcpStart.delete(ctx.callId)\n add(mcpToolErrors, 1, {\n 'gen_ai.mcp.server': ctx.server,\n 'gen_ai.tool.name': ctx.displayName,\n 'error.type': ctx.error.name,\n }, 'mcp.tool.errors')\n }))\n\n unregisters.push(hooks.hook('mcp:bootstrap:end', (ctx) => {\n record(mcpBootstrapDuration, ctx.durationMs, {\n 'gen_ai.mcp.server': ctx.name,\n 'ok': ctx.ok,\n }, 'mcp.bootstrap.duration')\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n add(mcpErrors, 1, {\n 'gen_ai.mcp.server': ctx.name,\n 'error.type': ctx.error.name,\n }, 'mcp.errors')\n }))\n\n unregisters.push(hooks.hook('mcp:auth:required', (ctx) => {\n add(mcpAuthRequired, 1, {\n 'gen_ai.mcp.server': ctx.name,\n 'transport': ctx.transport,\n 'reason': ctx.reason,\n }, 'mcp.auth.required')\n }))\n\n // -----------------------------------------------------------------\n // Disposal\n // -----------------------------------------------------------------\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n turnStart.clear()\n toolStart.clear()\n mcpStart.clear()\n }\n },\n }\n}\n\n/**\n * Defensive factory wrapper — returns undefined when meter creation\n * throws. Lets a half-broken meter (one unsupported instrument) still\n * give partial metrics rather than collapsing the whole install.\n */\nfunction safeFactory<T>(\n factory: () => T,\n name: string,\n onError: (kind: string, err: unknown) => void,\n): T | undefined {\n try {\n return factory()\n }\n catch (err) {\n try {\n onError(`createInstrument:${name}`, err)\n }\n catch { /* ignore */ }\n return undefined\n }\n}\n","/**\n * Run summary collector — one JSON postmortem per `agent.run()`.\n *\n * `AgentStats` is great for billing and per-turn accounting but doesn't\n * carry a record of the *interesting* events that happened during a run\n * (errors, gate blocks, validation rejects, budget hits, MCP failures).\n * Reconstructing that from a hook stream after the fact is exactly the\n * kind of plumbing every consumer ends up re-implementing.\n *\n * This module ships:\n *\n * - {@link RunSummary} — a serializable shape that bundles totals,\n * per-model breakdown, and all the per-run incident lists.\n * - {@link createRunSummaryCollector} — installs hook listeners that\n * build up a {@link RunSummary} during the run and surfaces it on\n * `agent:done`. Calls an optional `onSummary` callback so the host can\n * forward to a log aggregator / DB / postmortem dashboard without\n * touching `agent.run()`'s return value.\n *\n * The collector is purely additive — it doesn't touch the agent loop,\n * doesn't change return shapes, and doesn't suppress any other hook\n * handlers. Drop it in alongside tracing / metrics / logging or use it\n * standalone when you only need the summary.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { AgentStats } from './types'\nimport { statsByModel } from './stats'\n\n// ---------------------------------------------------------------------------\n// Public shape\n// ---------------------------------------------------------------------------\n\nexport interface RunSummaryTokens {\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost?: number\n /** First observable byte from the provider, ms from run start. */\n ttftMs?: number\n}\n\nexport interface RunSummaryByModel {\n modelId: string\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost: number\n turns: number\n}\n\nexport interface RunSummaryError {\n kind: 'stream' | 'tool' | 'mcp-tool' | 'mcp' | 'spawn'\n message: string\n errorType?: string\n turnId?: string\n callId?: string\n server?: string\n toolName?: string\n childId?: string\n statusCode?: number\n requestId?: string\n}\n\nexport interface RunSummaryBlock {\n callId: string\n toolName: string\n outcome: 'gate-block' | 'unknown' | 'invalid-input'\n reason?: string\n}\n\nexport interface RunSummaryValidation {\n callId: string\n toolName: string\n reason: string\n}\n\nexport interface RunSummaryBudget {\n kind: 'bytes' | 'tool-count'\n /** Tool name (for `'tool-count'`); absent for byte budgets. */\n toolName?: string\n /** `mode` for `'tool-count'`; absent for byte budgets. */\n mode?: 'steer' | 'block'\n observed: number\n limit: number\n turnId?: string\n}\n\n/**\n * Postmortem snapshot of one `agent.run()`. Strictly serializable — every\n * field round-trips through `JSON.stringify` / `JSON.parse` without loss\n * so a log aggregator can ingest it as-is.\n */\nexport interface RunSummary {\n runId?: string\n parentRunId?: string\n depth: number\n agentName?: string\n startedAt: number\n endedAt: number\n durationMs: number\n status: 'completed' | 'aborted'\n turns: number\n totals: RunSummaryTokens\n byModel: RunSummaryByModel[]\n errors: RunSummaryError[]\n blocks: RunSummaryBlock[]\n validationRejects: RunSummaryValidation[]\n budgetEvents: RunSummaryBudget[]\n /** Counts of pairing repairs, keyed by repair mode. */\n pairingRepairs: Record<string, number>\n /**\n * Postmortem snapshots of child runs that bubbled their stats up via\n * `spawn:complete`. Only present when the run actually spawned.\n */\n children?: RunSummary[]\n}\n\n// ---------------------------------------------------------------------------\n// Collector\n// ---------------------------------------------------------------------------\n\nexport interface RunSummaryCollectorOptions {\n /**\n * Called with the assembled {@link RunSummary} on every `agent:done`.\n * Synchronous — heavy I/O should be deferred (e.g. via `setImmediate`).\n */\n onSummary?: (summary: RunSummary) => void\n}\n\nexport interface RunSummaryCollector {\n /** Install the collector's hook handlers. Returns an uninstall fn. */\n install: (hooks: Hookable<AgentHooks>) => () => void\n /** Most-recent summary; `undefined` until the first `agent:done` fires. */\n latest: () => RunSummary | undefined\n}\n\n/**\n * Build a run-summary collector. State is created fresh inside each\n * `install()` call, so a single collector instance can be installed\n * across multiple agents without attribution cross-talk. `latest()`\n * returns the most-recent summary across **any** install — install\n * per-agent collectors if you need separate post-run snapshots.\n *\n * @example\n * ```ts\n * const collector = createRunSummaryCollector({\n * onSummary: s => console.log(JSON.stringify(s)),\n * })\n * const uninstall = collector.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n */\nexport function createRunSummaryCollector(\n options: RunSummaryCollectorOptions = {},\n): RunSummaryCollector {\n let last: RunSummary | undefined\n\n return {\n latest: () => last,\n install(hooks: Hookable<AgentHooks>): () => void {\n // Per-run in-progress accumulators. The hookable bus serializes\n // agent.run lifecycles per agent — we reset between runs on\n // `agent:start` and consume on `agent:done`.\n let runId: string | undefined\n let parentRunId: string | undefined\n let depth = 0\n let agentName: string | undefined\n let startedAt = Date.now()\n let aborted = false\n const errors: RunSummaryError[] = []\n const blocks: RunSummaryBlock[] = []\n const validationRejects: RunSummaryValidation[] = []\n const budgetEvents: RunSummaryBudget[] = []\n const pairingRepairs: Record<string, number> = {}\n const children: RunSummary[] = []\n\n function resetForNewRun(): void {\n aborted = false\n errors.length = 0\n blocks.length = 0\n validationRejects.length = 0\n budgetEvents.length = 0\n for (const k of Object.keys(pairingRepairs))\n delete pairingRepairs[k]\n children.length = 0\n }\n\n const unregisters: Array<() => void> = []\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n resetForNewRun()\n runId = ctx.runId\n parentRunId = ctx.parentRunId\n depth = ctx.depth\n agentName = ctx.agentName\n startedAt = ctx.startedAt\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n aborted = true\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n const msg = ctx.err instanceof Error ? ctx.err.message : String(ctx.err)\n const errorType = ctx.err instanceof Error ? ctx.err.name : 'unknown'\n errors.push({\n kind: 'stream',\n message: msg,\n errorType,\n turnId: ctx.turnId,\n ...(ctx.statusCode !== undefined ? { statusCode: ctx.statusCode } : {}),\n ...(ctx.requestId !== undefined ? { requestId: ctx.requestId } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n errors.push({\n kind: 'tool',\n message: ctx.error.message,\n errorType: ctx.error.name,\n turnId: ctx.turnId,\n callId: ctx.callId,\n toolName: ctx.name,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n errors.push({\n kind: 'mcp-tool',\n message: ctx.error.message,\n errorType: ctx.error.name,\n turnId: ctx.turnId,\n callId: ctx.callId,\n server: ctx.server,\n toolName: ctx.displayName,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n errors.push({\n kind: 'mcp',\n message: ctx.error.message,\n errorType: ctx.error.name,\n server: ctx.name,\n })\n }))\n\n unregisters.push(hooks.hook('spawn:error', (ctx) => {\n errors.push({\n kind: 'spawn',\n message: ctx.error.message,\n errorType: ctx.error.name,\n childId: ctx.id,\n })\n }))\n\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n if (ctx.outcome === 'gate-block' || ctx.outcome === 'unknown' || ctx.outcome === 'invalid-input') {\n blocks.push({\n callId: ctx.callId,\n toolName: ctx.name,\n outcome: ctx.outcome,\n ...(ctx.reason ? { reason: ctx.reason } : {}),\n })\n }\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n validationRejects.push({\n callId: ctx.callId,\n toolName: ctx.name,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n budgetEvents.push({\n kind: 'bytes',\n observed: ctx.bytes,\n limit: ctx.budget,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n budgetEvents.push({\n kind: 'tool-count',\n toolName: ctx.tool,\n mode: ctx.mode,\n observed: ctx.count,\n limit: ctx.max,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('pairing:repair', (ctx) => {\n pairingRepairs[ctx.mode] = (pairingRepairs[ctx.mode] ?? 0) + 1\n }))\n\n unregisters.push(hooks.hook('agent:done', (stats) => {\n const endedAt = Date.now()\n\n // Build per-model rollup via the existing `statsByModel` helper\n // so we stay in lockstep with how the rest of the harness\n // attributes cost and tokens by model id.\n const byModel: RunSummaryByModel[] = []\n for (const [modelId, usage] of statsByModel(stats)) {\n byModel.push({\n modelId,\n input: usage.input,\n output: usage.output,\n cacheRead: usage.cacheRead,\n cacheCreation: usage.cacheCreation,\n cost: usage.cost,\n turns: usage.turns,\n })\n }\n\n // Flatten child stats into nested summaries. We don't have the\n // child's full event lists here (those landed on the child's\n // own hooks), so the nested entry is a minimal totals-only\n // record — enough for a flat per-run audit trail; consumers\n // wanting full per-child event lists install a collector on\n // each subagent.\n for (const c of stats.children ?? []) {\n children.push(minimalSummaryFromStats(c.stats, {\n depth: c.depth ?? depth + 1,\n status: c.status === 'aborted' ? 'aborted' : 'completed',\n }))\n }\n\n const summary: RunSummary = {\n ...(runId ? { runId } : {}),\n ...(parentRunId ? { parentRunId } : {}),\n depth,\n ...(agentName ? { agentName } : {}),\n startedAt,\n endedAt,\n durationMs: endedAt - startedAt,\n status: aborted ? 'aborted' : 'completed',\n turns: stats.turns,\n totals: buildTotals(stats),\n byModel,\n errors: errors.slice(),\n blocks: blocks.slice(),\n validationRejects: validationRejects.slice(),\n budgetEvents: budgetEvents.slice(),\n pairingRepairs: { ...pairingRepairs },\n ...(children.length > 0 ? { children: children.slice() } : {}),\n }\n\n last = summary\n try {\n options.onSummary?.(summary)\n }\n catch {\n // Sink errors are not the collector's concern.\n }\n }))\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n }\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction buildTotals(stats: AgentStats): RunSummaryTokens {\n return {\n input: stats.totalIn,\n output: stats.totalOut,\n cacheRead: stats.totalCacheRead,\n cacheCreation: stats.totalCacheCreation,\n ...(typeof stats.cost === 'number' ? { cost: stats.cost } : {}),\n ...(typeof stats.timeTillFirstTokenMs === 'number' ? { ttftMs: stats.timeTillFirstTokenMs } : {}),\n }\n}\n\nfunction minimalSummaryFromStats(\n stats: AgentStats,\n meta: { depth: number, status: 'completed' | 'aborted' },\n): RunSummary {\n const byModel: RunSummaryByModel[] = []\n for (const [modelId, usage] of statsByModel(stats)) {\n byModel.push({\n modelId,\n input: usage.input,\n output: usage.output,\n cacheRead: usage.cacheRead,\n cacheCreation: usage.cacheCreation,\n cost: usage.cost,\n turns: usage.turns,\n })\n }\n const children: RunSummary[] = []\n for (const c of stats.children ?? []) {\n children.push(minimalSummaryFromStats(c.stats, {\n depth: c.depth ?? meta.depth + 1,\n status: c.status === 'aborted' ? 'aborted' : 'completed',\n }))\n }\n return {\n depth: meta.depth,\n startedAt: 0,\n endedAt: 0,\n durationMs: stats.elapsed,\n status: meta.status,\n turns: stats.turns,\n totals: buildTotals(stats),\n byModel,\n errors: [],\n blocks: [],\n validationRejects: [],\n budgetEvents: [],\n pairingRepairs: {},\n ...(children.length > 0 ? { children } : {}),\n }\n}\n","/**\n * Tracing helper — wraps agent lifecycle hooks in caller-provided spans.\n *\n * Zidane is hooks-native; rather than introducing a plugin abstraction just for\n * observability, we ship a small helper that turns an arbitrary `startSpan`\n * function into a bundle of hook handlers. Works with Sentry, OpenTelemetry,\n * Datadog — any tracer that can hand back an object with an `end()` method.\n *\n * Attribute conventions follow the Sentry AI Agents spec (which itself\n * tracks OpenTelemetry Gen AI semconv v1.36, with a few divergences). The\n * `conventions` option lets callers pick `'sentry'` (default — Sentry's\n * exact keys), `'otel'` (vendor-neutral OTel 1.36), or `'both'` (emit\n * every variant for maximum portability).\n *\n * Span coverage:\n *\n * - `invoke_agent <name?>` — root span, opens on `agent:start`, closes on `agent:done`. Sentry op `gen_ai.invoke_agent`.\n * - `chat <model?>` — per-turn LLM call, opens on `turn:before`, closes on `turn:after`. Sentry op `gen_ai.chat`.\n * - `execute_tool <name>` — native tool execution, opens on `tool:before`, closes on `tool:after` / `tool:error`. Sentry op `gen_ai.execute_tool`.\n * - `execute_tool <server>.<name>` — MCP tool variant, same op. Tagged with `gen_ai.tool.type: 'extension'`.\n * - `handoff to <task>` — subagent handoff (parent→child), opens on `spawn:before`, closes on `spawn:complete` / `spawn:error`. Sentry op `gen_ai.handoff`.\n * Also injects parent trace context into the spawn carrier when `propagator` is configured.\n * - `mcp.bootstrap <server>` — server bootstrap, opens on `mcp:bootstrap:start`, closes on `mcp:bootstrap:end`. Zidane-specific (no Sentry op equivalent).\n *\n * Span events (not spans — recorded on the active span via `addEvent`):\n *\n * - `gen_ai.gate.block` — a `tool:gate` listener refused a call.\n * - `gen_ai.validation.reject` — tool input failed schema validation.\n * - `gen_ai.validation.coerce` — tool input was auto-coerced to satisfy the schema.\n * - `gen_ai.budget.exceeded` — byte budget exceeded (`budget:exceeded`).\n * - `gen_ai.tool_budget.exceeded` — per-tool dispatch cap exceeded.\n * - `gen_ai.pairing.repair` — pre-send pairing repair fired.\n * - `gen_ai.stream.error` — provider stream errored (carries `statusCode` / `requestId`).\n * - `gen_ai.mcp.auth.required` — MCP server bootstrap blocked on missing OAuth tokens.\n * - `gen_ai.oauth.refresh` — provider OAuth credentials refreshed.\n *\n * Attribute naming follows the Sentry AI Agents conventions\n * (https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents/) by\n * default — which closely track OTel Gen AI semconv v1.36 with a few\n * intentional differences (`usage.input_tokens.cached` instead of\n * `usage.cache_read_input_tokens`, `cost.total_tokens` instead of\n * `usage.cost_usd`, seconds instead of ms for `time_to_first_token`).\n * Set `conventions: 'otel'` to emit only the vendor-neutral OTel keys, or\n * `'both'` to emit everything for maximum portability. Legacy attribute\n * names (`inputTokens`, `toolName`, `displayName`, …) are also emitted by\n * default for back-compat; set `legacyAttributes: false` to drop them.\n *\n * On `agent:done` or `uninstall()` it closes any spans still open (defensive —\n * normally all tool/turn spans have matching end hooks, but an abort\n * mid-execution can leave dangling ones).\n *\n * Returns an `install(hooks)` helper that registers every handler at once and\n * yields an `uninstall()` function for cleanup. Safe across multiple runs.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { SessionMessage } from './types'\n\n/** Minimal span shape — any tracer that provides these methods is compatible. */\nexport interface Span {\n /** Close the span. Called exactly once per span. */\n end: () => void\n /** Optional: attach additional attributes after the span is started (ignored if unsupported). */\n setAttributes?: (attrs: Record<string, unknown>) => void\n /**\n * Optional: record a structured event on the span. Used for non-span\n * occurrences (gate blocks, validation rejects, budget hits, pairing\n * repairs). Maps to OTel's `Span.addEvent` and Sentry's `addBreadcrumb`.\n * Tracers without an event surface should treat this as a no-op (the\n * helper falls back to `setAttributes` with an `event.*` key prefix).\n */\n addEvent?: (name: string, attrs?: Record<string, unknown>) => void\n}\n\n/**\n * Function that opens a span. Caller-provided so we stay tracer-agnostic.\n *\n * `parentContext` carries opaque trace metadata propagated from another\n * agent (typically a W3C `{ traceparent, tracestate }` carrier received on\n * `agent:start.tracingContext` after a parent agent's tracer injected it on\n * `spawn:before`). Implementations that integrate with OTel should extract\n * the carrier into a `Context` and use it as the parent when opening the\n * span; implementations that don't care about cross-agent linkage can\n * ignore the third argument entirely.\n */\nexport type StartSpan = (\n name: string,\n attrs?: Record<string, unknown>,\n parentContext?: Readonly<Record<string, string>>,\n) => Span\n\nexport type TracingConventions = 'sentry' | 'otel' | 'both'\n\nexport interface TracingHooksOptions {\n /** Tracer seam. Receives a span name + attributes; must return a `Span`. */\n startSpan: StartSpan\n /**\n * Optional attribute namespace. Prepended to every span name with a `/`\n * separator (e.g. `\"agent\"` → `agent/chat <model>`, `agent/execute_tool Bash`).\n *\n * Empty / undefined = no prefix.\n */\n namespace?: string\n /**\n * Which Gen-AI attribute conventions to emit.\n *\n * - `'sentry'` (default) — Sentry AI Agents conventions. Most common\n * choice — Sentry, Datadog AI, Honeycomb derived dashboards and\n * Langfuse all consume these.\n * - `'otel'` — vendor-neutral OpenTelemetry Gen AI semconv v1.36.\n * Diverges from Sentry on cache-token keys, cost keys, and TTFT unit.\n * - `'both'` — emit every variant. Doubles the per-span attribute\n * payload; pick when shipping to multiple backends with different\n * ingestion conventions.\n */\n conventions?: TracingConventions\n /**\n * Capture the actual prompt / completion text on spans\n * (`gen_ai.input.messages`, `gen_ai.output.messages`,\n * `gen_ai.system_instructions`, `gen_ai.tool.definitions`,\n * `gen_ai.tool.call.arguments`, `gen_ai.tool.call.result`).\n *\n * Defaults to `true` — the Sentry AI Agents dashboard requires these\n * fields to render the conversation viewer. Set `false` to drop them\n * for privacy / cost reasons; everything still goes through `redact`\n * when enabled, so most consumers want it on with a redactor wired up\n * rather than off entirely.\n */\n captureMessageContent?: boolean\n /**\n * Emit legacy attribute keys (`inputTokens`, `outputTokens`, `toolName`,\n * `displayName`, `finishReason`, `modelId`) alongside the new\n * `gen_ai.*` semconv keys. Defaults to `true` so existing dashboards\n * keep working. Set `false` once you've migrated all consumers off the\n * legacy names.\n */\n legacyAttributes?: boolean\n /**\n * Error sink for tracer failures. Replaces the historic silent-swallow.\n * Called when `Span.end()`, `setAttributes()`, `addEvent()`, or\n * `startSpan()` throws — so degraded tracers stay visible. The helper\n * still does not let tracer errors propagate into the agent loop.\n *\n * `kind` identifies which step failed (`'startSpan'`, `'end'`,\n * `'setAttributes'`, `'addEvent'`, `'redact'`, `'getActiveTraceContext'`).\n * Default: no-op (silent).\n */\n onError?: (kind: string, err: unknown) => void\n /**\n * Optional callback the helper invokes on `spawn:before` (after opening\n * the spawn span) to read the active trace context. Whatever it returns\n * is written into `ctx.tracingContext` and forwarded to the child's\n * `agent.run({ tracingContext })`. Typical use:\n *\n * ```ts\n * getActiveTraceContext: () => {\n * const carrier: Record<string, string> = {}\n * propagation.inject(context.active(), carrier)\n * return carrier\n * }\n * ```\n *\n * Returning `undefined` or an empty object leaves the carrier empty\n * and the child runs with no parent context. The helper's own\n * `propagator` is preferred over this when both are set (the\n * propagator runs inside the helper; this callback is the escape\n * hatch for hosts wanting full control).\n */\n getActiveTraceContext?: () => Readonly<Record<string, string>> | undefined\n /**\n * Optional in-process span redactor. When set, every potentially\n * sensitive string (tool input, tool result, system prompt) flows\n * through this fn before landing on a span attribute / event. Returning\n * the input unchanged is a no-op.\n *\n * Composes with the harness-wide `tracing:redact` hook: if a host has\n * already registered a hook handler that mutates `ctx.redacted`, the\n * tracer reuses that pipeline; setting `redact` here registers an extra\n * step that runs only for the tracer's attributes. The hook is the\n * canonical path; `redact` is the convenience surface for tracer-only\n * setups.\n */\n redact?: (kind: string, value: string, meta?: Readonly<Record<string, unknown>>) => string\n}\n\n/** Value returned by {@link createTracingHooks} — install entrypoint. */\nexport interface TracingHookSet {\n /**\n * Attach every hook handler to the given agent hooks instance.\n *\n * @returns an `uninstall` function that detaches every handler and closes any\n * still-open spans. Safe to call multiple times.\n */\n install: (hooks: Hookable<AgentHooks>) => () => void\n}\n\n// ---------------------------------------------------------------------------\n// Gen AI semantic-convention attribute keys\n// ---------------------------------------------------------------------------\n\n/**\n * Stable keys we emit unconditionally — present in BOTH Sentry and OTel\n * conventions with identical semantics, so no `conventions` branching is\n * needed at the call site.\n */\nconst GEN_AI = {\n system: 'gen_ai.system',\n operationName: 'gen_ai.operation.name',\n requestModel: 'gen_ai.request.model',\n responseModel: 'gen_ai.response.model',\n responseFinishReasons: 'gen_ai.response.finish_reasons',\n responseId: 'gen_ai.response.id',\n responseStreaming: 'gen_ai.response.streaming',\n responseTokensPerSecond: 'gen_ai.response.tokens_per_second',\n // Sentry uses seconds; OTel uses ms. We emit one or both based on `conventions`.\n responseTimeToFirstTokenSeconds: 'gen_ai.response.time_to_first_token',\n responseTimeToFirstTokenMs: 'gen_ai.client.time_to_first_token',\n // Usage — Sentry expects input_tokens to INCLUDE cached, with subkeys for cache slices.\n usageInputTokens: 'gen_ai.usage.input_tokens',\n usageOutputTokens: 'gen_ai.usage.output_tokens',\n usageTotalTokens: 'gen_ai.usage.total_tokens',\n // Sentry-specific cache keys.\n usageInputTokensCached: 'gen_ai.usage.input_tokens.cached',\n usageInputTokensCacheWrite: 'gen_ai.usage.input_tokens.cache_write',\n usageOutputTokensReasoning: 'gen_ai.usage.output_tokens.reasoning',\n // OTel cache keys (older / vendor-neutral).\n usageCacheReadInputTokens: 'gen_ai.usage.cache_read_input_tokens',\n usageCacheCreationInputTokens: 'gen_ai.usage.cache_creation_input_tokens',\n usageReasoningTokens: 'gen_ai.usage.reasoning_tokens',\n // Cost — Sentry splits, OTel rolls up.\n costTotalTokens: 'gen_ai.cost.total_tokens',\n costInputTokens: 'gen_ai.cost.input_tokens',\n costOutputTokens: 'gen_ai.cost.output_tokens',\n usageCostUsd: 'gen_ai.usage.cost_usd',\n // Tool — Sentry uses `tool.call.arguments` / `tool.call.result`; OTel uses `tool.input` / `tool.output` (deprecated in Sentry).\n toolName: 'gen_ai.tool.name',\n toolDescription: 'gen_ai.tool.description',\n toolType: 'gen_ai.tool.type',\n toolCallId: 'gen_ai.tool.call.id',\n toolCallArguments: 'gen_ai.tool.call.arguments',\n toolCallResult: 'gen_ai.tool.call.result',\n toolMessage: 'gen_ai.tool.message',\n toolInputDeprecated: 'gen_ai.tool.input',\n toolOutputDeprecated: 'gen_ai.tool.output',\n // Request configuration.\n requestMaxTokens: 'gen_ai.request.max_tokens',\n requestTemperature: 'gen_ai.request.temperature',\n requestTopP: 'gen_ai.request.top_p',\n requestTopK: 'gen_ai.request.top_k',\n requestSeed: 'gen_ai.request.seed',\n requestFrequencyPenalty: 'gen_ai.request.frequency_penalty',\n requestPresencePenalty: 'gen_ai.request.presence_penalty',\n // Message content surfaces (Sentry conversation viewer).\n inputMessages: 'gen_ai.input.messages',\n outputMessages: 'gen_ai.output.messages',\n systemInstructions: 'gen_ai.system_instructions',\n toolDefinitions: 'gen_ai.tool.definitions',\n // Agent identity.\n agentName: 'gen_ai.agent.name',\n agentRunId: 'gen_ai.agent.run.id',\n agentParentRunId: 'gen_ai.agent.parent_run.id',\n agentDepth: 'gen_ai.agent.depth',\n pipelineName: 'gen_ai.pipeline.name',\n turn: 'gen_ai.agent.turn',\n // MCP-specific (zidane extension; no Sentry op equivalent).\n mcpServer: 'gen_ai.mcp.server',\n mcpToolCount: 'gen_ai.mcp.tool_count',\n} as const\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Compose namespace prefix once. */\nfunction prefixed(namespace: string | undefined, name: string): string {\n return namespace ? `${namespace}/${name}` : name\n}\n\n/** Drop undefined entries — tracers vary on how they render `undefined`. */\nfunction compact(attrs: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(attrs)) {\n if (v !== undefined)\n out[k] = v\n }\n return out\n}\n\n/**\n * Convert a zidane `SessionMessage` (or `StreamOptions.messages` entry)\n * into Sentry's `{role, parts: [{type, content}]}` shape, JSON-stringify\n * the result. Returns `undefined` on serialization failure so the caller\n * skips the attribute rather than poisoning the span.\n *\n * Per Sentry footnote [7], binary blobs in image content are replaced\n * with `\"[Blob substitute]\"`. Tool calls become `{type: 'tool_call'}`\n * parts; tool results become `{type: 'tool_result'}` parts.\n */\ninterface SentryMessagePart {\n type: 'text' | 'tool_call' | 'tool_result' | 'reasoning'\n content?: string\n name?: string\n arguments?: string\n result?: string\n call_id?: string\n}\ninterface SentryMessage {\n role: 'user' | 'assistant' | 'system' | 'tool'\n parts: SentryMessagePart[]\n}\n\n/**\n * Wide-shape block record. `SessionContentBlock` is a discriminated\n * union — accepting the wide record lets us peek at the discriminator\n * without re-importing the entire union at this layer.\n */\ninterface AnyBlock { type: string, [k: string]: unknown }\n\nfunction blockToParts(block: AnyBlock): SentryMessagePart[] {\n switch (block.type) {\n case 'text':\n return [{ type: 'text', content: typeof block.text === 'string' ? block.text : '' }]\n case 'image':\n return [{ type: 'text', content: '[Blob substitute]' }]\n case 'tool_call':\n return [{\n type: 'tool_call',\n ...(typeof block.name === 'string' ? { name: block.name } : {}),\n ...(typeof block.id === 'string' ? { call_id: block.id } : {}),\n arguments: safeJson(block.input),\n }]\n case 'tool_result': {\n const output = block.output\n const text = typeof output === 'string'\n ? output\n : Array.isArray(output)\n ? (output as Array<{ type: string, text?: string }>)\n .map(b => b.type === 'image' ? '[Blob substitute]' : (b.text ?? ''))\n .join('\\n')\n : ''\n return [{\n type: 'tool_result',\n ...(typeof block.callId === 'string' ? { call_id: block.callId } : {}),\n content: text,\n }]\n }\n case 'thinking':\n return [{ type: 'reasoning', content: typeof block.text === 'string' ? block.text : '' }]\n default:\n return []\n }\n}\n\nfunction messageToSentry(msg: { role: 'user' | 'assistant', content: readonly AnyBlock[] }): SentryMessage {\n const parts: SentryMessagePart[] = []\n for (const block of msg.content)\n parts.push(...blockToParts(block))\n return { role: msg.role, parts }\n}\n\nfunction safeJson(value: unknown): string {\n try {\n return JSON.stringify(value ?? null)\n }\n catch {\n return '\"[unserializable]\"'\n }\n}\n\n/**\n * Stringify an array of `SessionMessage`s into Sentry's `gen_ai.input.messages`\n * format. Each entry's role + content is mapped via `messageToSentry`.\n * Returns `undefined` when the input is empty or unserializable.\n */\nfunction stringifyMessages(messages: readonly SessionMessage[]): string | undefined {\n if (messages.length === 0)\n return undefined\n try {\n const sentry = messages.map(m => messageToSentry({\n role: m.role,\n content: m.content as readonly AnyBlock[],\n }))\n return JSON.stringify(sentry)\n }\n catch {\n return undefined\n }\n}\n\n/**\n * Stringify a `StreamOptions.tools` array into Sentry's\n * `gen_ai.tool.definitions` format: `[{name, description?, parameters?}, …]`.\n *\n * The input is `unknown[]` because `StreamOptions.tools` carries the\n * provider-formatted shape (Anthropic / OpenAI / OpenAI-compat all\n * differ). We probe the three names that overlap (`name`, `description`,\n * `inputSchema` / `parameters`) and degrade gracefully otherwise.\n */\nfunction stringifyToolDefs(tools: readonly unknown[] | undefined): string | undefined {\n if (!tools || tools.length === 0)\n return undefined\n try {\n return JSON.stringify(tools.map((raw) => {\n if (!raw || typeof raw !== 'object')\n return { name: 'anonymous' }\n const t = raw as Record<string, unknown>\n const name = typeof t.name === 'string' ? t.name : 'anonymous'\n const description = typeof t.description === 'string' ? t.description : undefined\n // OpenAI's wire format calls it `parameters`; Anthropic / ToolSpec\n // call it `input_schema` / `inputSchema`. Probe all three.\n const parameters = t.parameters ?? t.input_schema ?? t.inputSchema\n return {\n name,\n ...(description ? { description } : {}),\n ...(parameters !== undefined ? { parameters } : {}),\n }\n }))\n }\n catch {\n return undefined\n }\n}\n\n/**\n * Build the usage attribute set per the configured conventions. Sentry\n * expects `input_tokens` to include cached, with `input_tokens.cached`\n * + `input_tokens.cache_write` subkeys; OTel splits cache into\n * separate top-level keys. `'both'` emits everything.\n */\nfunction buildUsageAttrs(\n usage: {\n input: number\n output: number\n cacheRead?: number\n cacheCreation?: number\n thinking?: number\n cost?: number\n modelId?: string\n finishReason?: string\n timeToFirstTokenMs?: number\n },\n conventions: TracingConventions,\n): Record<string, unknown> {\n const cacheRead = usage.cacheRead ?? 0\n const cacheWrite = usage.cacheCreation ?? 0\n // Per Sentry: `input_tokens` includes cached. zidane reports `input` as\n // the uncached new-input slice, so the headline number is the sum.\n const inputInclCache = usage.input + cacheRead + cacheWrite\n const totalTokens = inputInclCache + usage.output\n const wantSentry = conventions === 'sentry' || conventions === 'both'\n const wantOtel = conventions === 'otel' || conventions === 'both'\n\n const out: Record<string, unknown> = {\n [GEN_AI.responseModel]: usage.modelId,\n [GEN_AI.responseFinishReasons]: usage.finishReason ? [usage.finishReason] : undefined,\n [GEN_AI.usageOutputTokens]: usage.output,\n [GEN_AI.usageTotalTokens]: totalTokens,\n }\n\n if (wantSentry) {\n out[GEN_AI.usageInputTokens] = inputInclCache\n if (cacheRead > 0)\n out[GEN_AI.usageInputTokensCached] = cacheRead\n if (cacheWrite > 0)\n out[GEN_AI.usageInputTokensCacheWrite] = cacheWrite\n if (typeof usage.thinking === 'number' && usage.thinking > 0)\n out[GEN_AI.usageOutputTokensReasoning] = usage.thinking\n if (typeof usage.cost === 'number')\n out[GEN_AI.costTotalTokens] = usage.cost\n if (typeof usage.timeToFirstTokenMs === 'number')\n out[GEN_AI.responseTimeToFirstTokenSeconds] = usage.timeToFirstTokenMs / 1000\n }\n if (wantOtel) {\n // OTel: input_tokens is the uncached slice; cache is separate.\n if (!wantSentry)\n out[GEN_AI.usageInputTokens] = usage.input\n if (cacheRead > 0)\n out[GEN_AI.usageCacheReadInputTokens] = cacheRead\n if (cacheWrite > 0)\n out[GEN_AI.usageCacheCreationInputTokens] = cacheWrite\n if (typeof usage.thinking === 'number' && usage.thinking > 0)\n out[GEN_AI.usageReasoningTokens] = usage.thinking\n if (typeof usage.cost === 'number')\n out[GEN_AI.usageCostUsd] = usage.cost\n if (typeof usage.timeToFirstTokenMs === 'number')\n out[GEN_AI.responseTimeToFirstTokenMs] = usage.timeToFirstTokenMs\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a set of tracing hook handlers that can be installed on an agent.\n *\n * @example Sentry\n * ```ts\n * const tracing = createTracingHooks({\n * startSpan: (name, attrs) => Sentry.startInactiveSpan({ name, attributes: attrs }),\n * })\n * const uninstall = tracing.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n *\n * @example OpenTelemetry with parent-context propagation\n * ```ts\n * import { trace, context, propagation } from '@opentelemetry/api'\n *\n * const tracer = trace.getTracer('zidane')\n * const tracing = createTracingHooks({\n * startSpan: (name, attrs, parentCtx) => {\n * const ctx = parentCtx ? propagation.extract(context.active(), parentCtx) : context.active()\n * return tracer.startSpan(name, { attributes: attrs }, ctx)\n * },\n * getActiveTraceContext: () => {\n * const carrier: Record<string, string> = {}\n * propagation.inject(context.active(), carrier)\n * return carrier\n * },\n * })\n * ```\n */\nexport function createTracingHooks(options: TracingHooksOptions): TracingHookSet {\n const namespace = options.namespace\n const legacy = options.legacyAttributes ?? true\n const conventions = options.conventions ?? 'sentry'\n const wantSentry = conventions === 'sentry' || conventions === 'both'\n const captureContent = options.captureMessageContent ?? true\n const onError = options.onError ?? (() => {})\n\n return {\n install(hooks: Hookable<AgentHooks>): () => void {\n // Span book-keeping. Each map keys by the identifier that's unique\n // across concurrent in-flight spans of that kind.\n const runSpans = new Map<string, Span>() // keyed by runId\n const turnSpans = new Map<string, Span>() // keyed by turnId\n const toolSpans = new Map<string, Span>() // keyed by callId\n const mcpSpans = new Map<string, Span>() // keyed by callId\n const spawnSpans = new Map<string, Span>() // keyed by spawn id\n const bootstrapSpans = new Map<string, Span>() // keyed by server name\n // Provider system identifier (`anthropic`, `openai`, …) captured at\n // `agent:start`. Stamped onto every chat / tool span belonging to\n // this run so Sentry's per-vendor breakdowns work. Reset on\n // `agent:done`.\n let activeSystem: string | undefined\n // Active turn book-keeping for tokens-per-second derivation.\n const turnTrack = new Map<string, { startedAt: number }>()\n // Active run span ref — used so non-span events (gate blocks,\n // budget hits, validation rejects, policy/pairing repairs) can\n // attach via `addEvent` on the in-flight run span. The run span is\n // the natural parent for cross-cutting policy events: it outlives\n // turn spans (which close before tool dispatch starts in the\n // current loop), and tool spans (which haven't opened on a\n // gate-block path).\n let activeRunSpan: Span | undefined\n\n function safeStartSpan(\n name: string,\n attrs: Record<string, unknown>,\n parentContext?: Readonly<Record<string, string>>,\n ): Span | undefined {\n try {\n return options.startSpan(prefixed(namespace, name), compact(attrs), parentContext)\n }\n catch (err) {\n try {\n onError('startSpan', err)\n }\n catch { /* ignore */ }\n return undefined\n }\n }\n\n function safeSetAttrs(span: Span | undefined, attrs: Record<string, unknown>): void {\n if (!span?.setAttributes)\n return\n try {\n span.setAttributes(compact(attrs))\n }\n catch (err) {\n try {\n onError('setAttributes', err)\n }\n catch { /* ignore */ }\n }\n }\n\n function safeEnd(map: Map<string, Span>, key: string): void {\n const span = map.get(key)\n if (!span)\n return\n try {\n span.end()\n }\n catch (err) {\n try {\n onError('end', err)\n }\n catch { /* ignore */ }\n }\n map.delete(key)\n }\n\n function endAll(map: Map<string, Span>): void {\n for (const [, span] of map) {\n try {\n span.end()\n }\n catch (err) {\n try {\n onError('end', err)\n }\n catch { /* ignore */ }\n }\n }\n map.clear()\n }\n\n function safeAddEvent(span: Span | undefined, name: string, attrs: Record<string, unknown>): void {\n if (!span)\n return\n if (typeof span.addEvent === 'function') {\n try {\n span.addEvent(name, compact(attrs))\n }\n catch (err) {\n try {\n onError('addEvent', err)\n }\n catch { /* ignore */ }\n }\n return\n }\n // Tracer doesn't support events — fall back to a single\n // `event.<name>.*` attribute so the data lands somewhere.\n safeSetAttrs(span, Object.fromEntries(\n Object.entries(compact(attrs)).map(([k, v]) => [`event.${name}.${k}`, v]),\n ))\n }\n\n function redact(kind: string, value: string, meta?: Readonly<Record<string, unknown>>): string {\n if (typeof options.redact !== 'function')\n return value\n try {\n return options.redact(kind, value, meta)\n }\n catch (err) {\n try {\n onError('redact', err)\n }\n catch { /* ignore */ }\n return value\n }\n }\n\n const unregisters: Array<() => void> = []\n\n // -----------------------------------------------------------------\n // agent.run (root)\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n activeSystem = ctx.providerName\n // Empty-string names produce `invoke_agent ` (trailing space).\n // Treat empty as \"no name\" so the span name reads cleanly.\n const displayName = ctx.agentName && ctx.agentName.length > 0\n ? ctx.agentName\n : 'agent'\n const span = safeStartSpan(\n `invoke_agent ${displayName}`,\n {\n [GEN_AI.operationName]: 'invoke_agent',\n [GEN_AI.system]: activeSystem,\n [GEN_AI.agentName]: ctx.agentName,\n [GEN_AI.agentRunId]: ctx.runId,\n [GEN_AI.agentParentRunId]: ctx.parentRunId,\n [GEN_AI.agentDepth]: ctx.depth,\n // Sentry-specific span op hint — backends that key off\n // `op` (Sentry Python/JS) read this directly; OTel\n // backends ignore unknown keys, so it's harmless.\n 'sentry.op': 'gen_ai.invoke_agent',\n ...(legacy\n ? {\n runId: ctx.runId,\n parentRunId: ctx.parentRunId,\n agentName: ctx.agentName,\n depth: ctx.depth,\n }\n : {}),\n },\n ctx.tracingContext,\n )\n if (span) {\n runSpans.set(ctx.runId, span)\n activeRunSpan = span\n }\n }))\n\n unregisters.push(hooks.hook('agent:done', (stats) => {\n // `agent:done` doesn't carry a runId on its payload (it's `AgentStats`).\n // The 99% case is a single in-flight run — close every open run span\n // and stamp the totals on each. Multi-run-concurrent users plug a\n // runId into stats via their own integration; for now we close all.\n const usageAttrs = buildUsageAttrs({\n input: stats.totalIn,\n output: stats.totalOut,\n cacheRead: stats.totalCacheRead,\n cacheCreation: stats.totalCacheCreation,\n cost: stats.cost,\n timeToFirstTokenMs: stats.timeTillFirstTokenMs,\n }, conventions)\n for (const [, span] of runSpans) {\n safeSetAttrs(span, {\n ...usageAttrs,\n [GEN_AI.turn]: stats.turns,\n 'gen_ai.agent.elapsed_ms': stats.elapsed,\n ...(legacy\n ? {\n totalIn: stats.totalIn,\n totalOut: stats.totalOut,\n totalCacheRead: stats.totalCacheRead,\n totalCacheCreation: stats.totalCacheCreation,\n cost: stats.cost,\n turns: stats.turns,\n elapsed: stats.elapsed,\n }\n : {}),\n })\n }\n endAll(runSpans)\n // Defensive — close anything else still hanging open.\n endAll(turnSpans)\n endAll(toolSpans)\n endAll(mcpSpans)\n endAll(spawnSpans)\n endAll(bootstrapSpans)\n activeRunSpan = undefined\n activeSystem = undefined\n turnTrack.clear()\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n for (const [, span] of runSpans)\n safeSetAttrs(span, { 'gen_ai.agent.status': 'aborted' })\n }))\n\n // -----------------------------------------------------------------\n // Turn span — OTel Gen AI `chat` operation\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('turn:before', (ctx) => {\n const requestedModel = ctx.options.model\n // Built-in Sentry/OTel attrs that don't depend on content capture.\n const baseAttrs: Record<string, unknown> = {\n [GEN_AI.operationName]: 'chat',\n [GEN_AI.system]: activeSystem,\n [GEN_AI.requestModel]: requestedModel,\n [GEN_AI.turn]: ctx.turn,\n [GEN_AI.responseStreaming]: true,\n [GEN_AI.requestMaxTokens]: ctx.options.maxTokens,\n 'sentry.op': 'gen_ai.chat',\n 'gen_ai.agent.turn_id': ctx.turnId,\n ...(legacy ? { turnId: ctx.turnId, turn: ctx.turn } : {}),\n }\n // Optionally surface conversation content — prompts, system\n // instructions, available tools. Each piece flows through the\n // redact seam so hosts can scrub secrets without losing the\n // structural attribute. Skipping content keeps the span cheap\n // and privacy-safe.\n if (captureContent) {\n const systemText = ctx.options.system\n if (systemText) {\n baseAttrs[GEN_AI.systemInstructions] = redact('system', systemText, {\n turnId: ctx.turnId,\n turn: ctx.turn,\n })\n }\n const inputMsgs = stringifyMessages(ctx.options.messages)\n if (inputMsgs) {\n baseAttrs[GEN_AI.inputMessages] = redact('prompt', inputMsgs, {\n turnId: ctx.turnId,\n turn: ctx.turn,\n })\n }\n const toolDefs = stringifyToolDefs(ctx.options.tools)\n if (toolDefs)\n baseAttrs[GEN_AI.toolDefinitions] = toolDefs\n }\n const span = safeStartSpan(\n `chat${requestedModel ? ` ${requestedModel}` : ''}`,\n baseAttrs,\n )\n if (span) {\n turnSpans.set(ctx.turnId, span)\n turnTrack.set(ctx.turnId, { startedAt: Date.now() })\n }\n }))\n\n unregisters.push(hooks.hook('stream:start', (ctx) => {\n safeAddEvent(turnSpans.get(ctx.turnId), 'gen_ai.stream.start', {\n startedAt: ctx.startedAt,\n })\n }))\n\n unregisters.push(hooks.hook('turn:after', (ctx) => {\n const span = turnSpans.get(ctx.turnId)\n const tracked = turnTrack.get(ctx.turnId)\n turnTrack.delete(ctx.turnId)\n // Tokens-per-second derivation. Use wall-clock duration since the\n // turn span opened, fall back to TTFT-adjusted slice when present.\n let tokensPerSecond: number | undefined\n if (tracked && ctx.usage.output > 0) {\n const elapsedMs = Date.now() - tracked.startedAt\n if (elapsedMs > 0)\n tokensPerSecond = (ctx.usage.output / elapsedMs) * 1000\n }\n const usageAttrs = buildUsageAttrs(ctx.usage, conventions)\n // Cumulative-through-this-turn totals. We compute `input_tokens`\n // here to mirror `gen_ai.usage.input_tokens`'s semantic on the\n // same span: it INCLUDES cached when Sentry conventions are in\n // play, matches the uncached slice when OTel-only. Without this\n // alignment the two keys (`usage.*` vs `cumulative.*`) would\n // silently differ in arithmetic.\n const cumInputTokens = wantSentry\n ? ctx.cumulativeUsage.input + ctx.cumulativeUsage.cacheRead + ctx.cumulativeUsage.cacheCreation\n : ctx.cumulativeUsage.input\n const attrs: Record<string, unknown> = {\n ...usageAttrs,\n [GEN_AI.responseTokensPerSecond]: tokensPerSecond,\n 'gen_ai.agent.cumulative.input_tokens': cumInputTokens,\n 'gen_ai.agent.cumulative.output_tokens': ctx.cumulativeUsage.output,\n 'gen_ai.agent.cumulative.total_tokens': cumInputTokens + ctx.cumulativeUsage.output,\n 'gen_ai.agent.cumulative.cache_read_input_tokens': ctx.cumulativeUsage.cacheRead,\n 'gen_ai.agent.cumulative.cache_creation_input_tokens': ctx.cumulativeUsage.cacheCreation,\n 'gen_ai.agent.cumulative.cost_usd': ctx.cumulativeUsage.cost,\n 'gen_ai.agent.cumulative.turns': ctx.cumulativeUsage.turns,\n ...(legacy\n ? {\n inputTokens: ctx.usage.input,\n outputTokens: ctx.usage.output,\n finishReason: ctx.usage.finishReason,\n modelId: ctx.usage.modelId,\n }\n : {}),\n }\n // Output messages — the assistant turn that just landed. Sentry's\n // conversation viewer keys off this; skipped when content capture\n // is off. `system` turns aren't serialized (Sentry's schema only\n // accepts user / assistant / system / tool — and we never emit\n // system role from the loop).\n if (captureContent && ctx.message && ctx.message.role !== 'system') {\n const outputMsgs = stringifyMessages([{\n role: ctx.message.role,\n content: ctx.message.content,\n }])\n if (outputMsgs)\n attrs[GEN_AI.outputMessages] = redact('stream-text', outputMsgs, { turnId: ctx.turnId, turn: ctx.turn })\n }\n safeSetAttrs(span, attrs)\n safeEnd(turnSpans, ctx.turnId)\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n const span = turnSpans.get(ctx.turnId)\n const message = ctx.err instanceof Error ? ctx.err.message : String(ctx.err)\n safeSetAttrs(span, {\n 'error.type': ctx.err instanceof Error ? ctx.err.name : 'unknown',\n 'error.message': message,\n 'http.response.status_code': ctx.statusCode,\n 'gen_ai.request.id': ctx.requestId,\n })\n safeAddEvent(span, 'gen_ai.stream.error', {\n 'error.type': ctx.err instanceof Error ? ctx.err.name : 'unknown',\n 'error.message': message,\n 'http.response.status_code': ctx.statusCode,\n 'gen_ai.request.id': ctx.requestId,\n })\n }))\n\n // -----------------------------------------------------------------\n // Native tool span — OTel Gen AI `execute_tool` operation\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('tool:before', (ctx) => {\n const span = safeStartSpan(\n `execute_tool ${ctx.displayName}`,\n {\n [GEN_AI.operationName]: 'execute_tool',\n [GEN_AI.system]: activeSystem,\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolType]: 'function',\n [GEN_AI.toolCallId]: ctx.callId,\n 'sentry.op': 'gen_ai.execute_tool',\n 'gen_ai.tool.display_name': ctx.displayName,\n 'gen_ai.agent.turn_id': ctx.turnId,\n 'gen_ai.agent.depth': ctx.depth,\n 'gen_ai.agent.run.id': ctx.runId,\n 'gen_ai.agent.parent_run.id': ctx.parentRunId,\n ...(legacy\n ? {\n toolName: ctx.name,\n displayName: ctx.displayName,\n turnId: ctx.turnId,\n callId: ctx.callId,\n }\n : {}),\n },\n )\n if (span) {\n toolSpans.set(ctx.callId, span)\n if (captureContent) {\n // Attach (possibly redacted) input as an attribute. Sentry's\n // canonical key is `gen_ai.tool.call.arguments`; OTel's\n // older key is `gen_ai.tool.input` (now deprecated in\n // Sentry). Emit one or both based on `conventions`.\n try {\n const serialized = JSON.stringify(ctx.input)\n const redacted = redact('tool-input', serialized, {\n toolName: ctx.name,\n displayName: ctx.displayName,\n callId: ctx.callId,\n turnId: ctx.turnId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallArguments] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolInputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n catch {\n // Non-serializable input (circular refs) — skip the attr;\n // the tool name and call id are still on the span.\n }\n }\n }\n }))\n\n unregisters.push(hooks.hook('tool:after', (ctx) => {\n const span = toolSpans.get(ctx.callId)\n safeSetAttrs(span, {\n 'gen_ai.tool.output_bytes': ctx.outputBytes,\n 'gen_ai.tool.coercions': ctx.coercions?.length ?? 0,\n })\n if (captureContent && typeof ctx.result === 'string') {\n const redacted = redact('tool-result', ctx.result, {\n toolName: ctx.name,\n callId: ctx.callId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallResult] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolOutputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n safeEnd(toolSpans, ctx.callId)\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n safeSetAttrs(toolSpans.get(ctx.callId), {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n safeEnd(toolSpans, ctx.callId)\n }))\n\n // Tool gate / dispatch outcomes. Listen on `tool:dispatched` (fires\n // AFTER every `tool:gate` listener has had a chance to mutate\n // `block` / `result`) so the recorded `outcome` reflects the final\n // decision rather than the as-entered state. Attaches to the\n // in-flight run span; the chat span has already closed by tool\n // dispatch time in the current loop ordering, and no tool span has\n // opened for a `gate-block` / `unknown` / `invalid-input` path.\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n if (ctx.outcome === 'gate-block') {\n safeAddEvent(activeRunSpan, 'gen_ai.gate.block', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n reason: ctx.reason,\n })\n }\n else if (ctx.outcome === 'gate-substitute') {\n safeAddEvent(activeRunSpan, 'gen_ai.gate.substitute', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n })\n }\n else if (ctx.outcome === 'unknown') {\n safeAddEvent(activeRunSpan, 'gen_ai.tool.unknown', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n })\n }\n else if (ctx.outcome === 'invalid-input') {\n safeAddEvent(activeRunSpan, 'gen_ai.tool.invalid_input', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n })\n }\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.validation.reject', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('validation:coerce', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.validation.coerce', {\n [GEN_AI.toolName]: ctx.name,\n [GEN_AI.toolCallId]: ctx.callId,\n coercions: ctx.coercions,\n })\n }))\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.budget.exceeded', {\n turnId: ctx.turnId,\n bytes: ctx.bytes,\n budget: ctx.budget,\n })\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.tool_budget.exceeded', {\n [GEN_AI.toolName]: ctx.tool,\n turnId: ctx.turnId,\n count: ctx.count,\n max: ctx.max,\n mode: ctx.mode,\n })\n }))\n\n unregisters.push(hooks.hook('pairing:repair', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.pairing.repair', {\n mode: ctx.mode,\n callId: ctx.callId,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('oauth:refresh', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.oauth.refresh', {\n provider: ctx.provider,\n providerId: ctx.providerId,\n source: ctx.source,\n })\n }))\n\n // -----------------------------------------------------------------\n // MCP tool span — OTel Gen AI `execute_tool` operation, MCP variant\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('mcp:tool:before', (ctx) => {\n const span = safeStartSpan(\n `execute_tool ${ctx.server}.${ctx.tool}`,\n {\n [GEN_AI.operationName]: 'execute_tool',\n [GEN_AI.system]: activeSystem,\n [GEN_AI.toolName]: ctx.displayName,\n // MCP tools are Sentry's \"extension\" tool type — backed by an\n // out-of-process server rather than a local function.\n [GEN_AI.toolType]: 'extension',\n [GEN_AI.toolCallId]: ctx.callId,\n [GEN_AI.mcpServer]: ctx.server,\n 'sentry.op': 'gen_ai.execute_tool',\n 'gen_ai.mcp.tool': ctx.tool,\n 'gen_ai.agent.turn_id': ctx.turnId,\n 'gen_ai.agent.run.id': ctx.runId,\n ...(legacy\n ? {\n server: ctx.server,\n tool: ctx.tool,\n displayName: ctx.displayName,\n turnId: ctx.turnId,\n callId: ctx.callId,\n }\n : {}),\n },\n )\n if (span) {\n mcpSpans.set(ctx.callId, span)\n if (captureContent) {\n try {\n const redacted = redact('mcp-tool-input', JSON.stringify(ctx.input), {\n server: ctx.server,\n tool: ctx.tool,\n callId: ctx.callId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallArguments] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolInputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n catch { /* ignore */ }\n }\n }\n }))\n\n unregisters.push(hooks.hook('mcp:tool:after', (ctx) => {\n const span = mcpSpans.get(ctx.callId)\n safeSetAttrs(span, { 'gen_ai.tool.output_bytes': ctx.outputBytes })\n if (captureContent && typeof ctx.result === 'string') {\n const redacted = redact('mcp-tool-result', ctx.result, {\n server: ctx.server,\n tool: ctx.tool,\n callId: ctx.callId,\n })\n const attrs: Record<string, unknown> = {}\n if (wantSentry)\n attrs[GEN_AI.toolCallResult] = redacted\n if (conventions === 'otel' || conventions === 'both')\n attrs[GEN_AI.toolOutputDeprecated] = redacted\n safeSetAttrs(span, attrs)\n }\n safeEnd(mcpSpans, ctx.callId)\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n safeSetAttrs(mcpSpans.get(ctx.callId), {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n safeEnd(mcpSpans, ctx.callId)\n }))\n\n // -----------------------------------------------------------------\n // MCP server bootstrap span\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('mcp:bootstrap:start', (ctx) => {\n const span = safeStartSpan(\n `mcp.bootstrap ${ctx.name}`,\n {\n [GEN_AI.operationName]: 'mcp.bootstrap',\n [GEN_AI.system]: activeSystem,\n [GEN_AI.mcpServer]: ctx.name,\n 'gen_ai.mcp.transport': ctx.transport,\n },\n )\n if (span)\n bootstrapSpans.set(ctx.name, span)\n }))\n\n unregisters.push(hooks.hook('mcp:bootstrap:end', (ctx) => {\n const span = bootstrapSpans.get(ctx.name)\n const ok = ctx.ok\n safeSetAttrs(span, {\n 'gen_ai.mcp.bootstrap.duration_ms': ctx.durationMs,\n 'gen_ai.mcp.bootstrap.ok': ok,\n ...(ok\n ? {\n [GEN_AI.mcpToolCount]: ctx.toolCount,\n 'gen_ai.mcp.lazy': ctx.lazy,\n 'gen_ai.mcp.cached': ctx.cached,\n }\n : {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n }),\n })\n safeEnd(bootstrapSpans, ctx.name)\n }))\n\n unregisters.push(hooks.hook('mcp:auth:required', (ctx) => {\n const span = bootstrapSpans.get(ctx.name)\n safeAddEvent(span ?? activeRunSpan, 'gen_ai.mcp.auth.required', {\n server: ctx.name,\n transport: ctx.transport,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n safeAddEvent(activeRunSpan, 'gen_ai.mcp.error', {\n 'server': ctx.name,\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n }))\n\n // -----------------------------------------------------------------\n // Spawn span — parent-side. Also injects trace context for the child.\n // -----------------------------------------------------------------\n\n unregisters.push(hooks.hook('spawn:before', (ctx) => {\n // Sentry: \"handoff from {from_agent} to {to_agent}\". We don't\n // always know the child agent's name at spawn time — fall back\n // to the truncated task as the destination identifier.\n const toAgent = ctx.task.length > 60 ? `${ctx.task.slice(0, 57)}…` : ctx.task\n const span = safeStartSpan(\n `handoff to ${toAgent}`,\n {\n [GEN_AI.operationName]: 'handoff',\n [GEN_AI.system]: activeSystem,\n 'sentry.op': 'gen_ai.handoff',\n 'gen_ai.agent.spawn.id': ctx.id,\n 'gen_ai.agent.spawn.task': ctx.task,\n 'gen_ai.agent.depth': ctx.depth,\n },\n )\n if (span)\n spawnSpans.set(ctx.id, span)\n\n // Inject parent trace context for the child agent. The spawn tool\n // passes whatever we write into `ctx.tracingContext` to the child's\n // `agent.run({ tracingContext })` — see `tools/spawn.ts`.\n if (typeof options.getActiveTraceContext === 'function' && ctx.tracingContext) {\n let carrier: Readonly<Record<string, string>> | undefined\n try {\n carrier = options.getActiveTraceContext()\n }\n catch (err) {\n try {\n onError('getActiveTraceContext', err)\n }\n catch { /* ignore */ }\n }\n if (carrier) {\n for (const [k, v] of Object.entries(carrier))\n ctx.tracingContext[k] = v\n }\n }\n }))\n\n unregisters.push(hooks.hook('spawn:complete', (ctx) => {\n const span = spawnSpans.get(ctx.id)\n const childUsage = buildUsageAttrs({\n input: ctx.stats.totalIn,\n output: ctx.stats.totalOut,\n cacheRead: ctx.stats.totalCacheRead,\n cacheCreation: ctx.stats.totalCacheCreation,\n cost: ctx.stats.cost,\n }, conventions)\n safeSetAttrs(span, {\n 'gen_ai.agent.spawn.status': ctx.status ?? 'completed',\n 'gen_ai.agent.spawn.depth': ctx.depth,\n ...childUsage,\n })\n safeEnd(spawnSpans, ctx.id)\n }))\n\n unregisters.push(hooks.hook('spawn:error', (ctx) => {\n safeSetAttrs(spawnSpans.get(ctx.id), {\n 'error.type': ctx.error.name,\n 'error.message': ctx.error.message,\n })\n safeEnd(spawnSpans, ctx.id)\n }))\n\n // -----------------------------------------------------------------\n // Disposal\n // -----------------------------------------------------------------\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n endAll(runSpans)\n endAll(turnSpans)\n endAll(toolSpans)\n endAll(mcpSpans)\n endAll(spawnSpans)\n endAll(bootstrapSpans)\n activeRunSpan = undefined\n }\n },\n }\n}\n\n/**\n * OpenTelemetry Gen AI semantic-convention attribute keys used by the\n * built-in tracer. Exported so consumers integrating with raw OTel SDKs\n * (instead of going through `createTracingHooks`) can stay aligned with\n * the names the harness itself emits.\n */\nexport const GEN_AI_ATTRIBUTES = GEN_AI\n","/**\n * Zod v4 integration helper.\n *\n * Normalizes the output of z.toJsonSchema() for use as ToolSpec.inputSchema.\n * Zod is an optional peer dependency — consumers call z.toJsonSchema() themselves.\n *\n * Usage:\n * import { z } from 'zod'\n * import { zodToJsonSchema } from 'zidane'\n * const schema = zodToJsonSchema(z.toJsonSchema(z.object({ name: z.string() })))\n */\n\n/**\n * Normalize a JSON Schema (e.g. from zod v4's z.toJsonSchema()) for use\n * as a ToolSpec.inputSchema.\n *\n * Strips the $schema key that some providers reject.\n */\nexport function zodToJsonSchema(jsonSchema: Record<string, unknown>): Record<string, unknown> {\n const { $schema, ...rest } = jsonSchema\n return rest\n}\n"],"mappings":"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"}
|