/** * Darwin — Execution Trace Capture (v0.5 / A1) * * Pure, transport-agnostic capturer. Knows nothing about Anthropic SDK, * Claude CLI, OpenAI, or any specific runtime — the runtime feeds events, * the capturer aggregates into an ExecutionTrace. * * Three event types map to the three industry-standard span types * (Braintrust + Langfuse + Strands SDK + OTEL GenAI 2026): * - recordToolUse / recordToolResult → Tool spans * - recordTextBlock → assistant prose counter (NOT thinking-block) * - recordError → Turn-level errors * - addTokens → aggregated LLM token usage * * Pairing rule: each `recordToolUse(id, ...)` MUST be paired with a later * `recordToolResult(id, ...)` to compute durationMs. Unpaired uses get * `durationMs > 0` (start-to-finalize) and `outcome = 'error'` at * finalize-time with errorClass = 'unpaired_call' so we never silently * drop hanging tools. * * Privacy + size: args are passed through as-is — caller's responsibility * to truncate sensitive values. resultSummary is truncated to 2000 chars * at recordToolResult-time. OTEL note: `gen_ai.tool.call.arguments` is * Opt-In in the GenAI spec; Darwin's "always-capture args" stance is * acceptable for internal use but MUST be documented when traces touch * customer data (V2 will add a Redaction-Layer). * * Backwards-compat: capturer is opt-in. Existing code paths that don't * instantiate it produce DarwinExperiment.trajectory === undefined and * the rest of the system behaves identically to pre-A1. */ import type { ExecutionTrace, TraceTokenUsage } from '../types.js'; export interface TraceCapture { /** * Record the start of a tool call. `id` is the SDK's correlation id * (Anthropic SDK `tool_use.id`, OpenAI `tool_call.id`, etc.) — used to * pair the matching recordToolResult AND persisted on the captured * TraceToolCall as `id` for OTEL `gen_ai.tool.call.id` mapping. */ recordToolUse(id: string, tool: string, args?: Record): void; /** * Record the result of a previously-started tool call. If `id` was never * passed to recordToolUse, the result is silently dropped (defensive — * some SDKs emit tool_result for system tools without tool_use). */ recordToolResult(id: string, outcome: 'success' | 'error', opts?: { resultSummary?: string; errorClass?: string; errorMessage?: string; retryCount?: number; }): void; /** Mark the start of a new agent turn (1-indexed). Default starts at turn 1. */ startTurn(): void; /** * Increment the text-block counter — one per substantial assistant * text emission (>50 chars typically, but the caller decides the * threshold). Renamed from `recordReasoning` after R1 review for * accuracy: this counts ANY assistant prose, NOT only pre-action * "thinking" blocks. Use `addTokens` for cost-of-reasoning attribution. */ recordTextBlock(): void; /** Record a turn-level error (e.g. spawn failure, parse error, child crash). */ recordError(class_: string, message: string): void; /** * Accumulate LLM token usage into the trajectory aggregate. Call once * per LLM round-trip with the usage object the SDK returned. Missing * fields are treated as zero for summation. Existing aggregate fields * are preserved (additive merge). */ addTokens(usage: TraceTokenUsage): void; /** * Build the final ExecutionTrace. * Unpaired tool calls (recordToolUse without recordToolResult) get * marked as outcome='error', errorClass='unpaired_call' so silent * SDK hangs remain visible. */ finalize(): ExecutionTrace; } export interface TraceCaptureOptions { /** * Wallclock provider. Defaults to `Date.now`. Override for tests so * durations are deterministic. */ now?: () => number; /** * Predicate that classifies a tool name as an MCP-server call (vs a * built-in like Read/Bash). Default heuristic: starts with 'mcp__'. * Used to populate `mcpInvocations` aggregate. */ isMcpTool?: (toolName: string) => boolean; } /** * Create a fresh trace capturer. * * Usage: * ```ts * const trace = createTraceCapture(); * trace.startTurn(); * trace.recordToolUse('call_1', 'mcp__nex__search', { query: 'foo' }); * trace.recordToolResult('call_1', 'success', { resultSummary: '3 hits' }); * trace.recordTextBlock(); * trace.addTokens({ inputTokens: 1200, outputTokens: 340 }); * const trajectory = trace.finalize(); * ``` */ export declare function createTraceCapture(opts?: TraceCaptureOptions): TraceCapture; //# sourceMappingURL=trace-capture.d.ts.map