/** * sim/trace.ts — lightweight layered span collection, ported from mirofish * `gui_use/trace` (contextvars → AsyncLocalStorage). * * Records a span tree per unit of work (one LLM call / one device op / one * processing stretch) as a flat list with parent indices — JSON-ready, order * is time order, streaming append. Accounting convention (metrics sums by * kind, do not break): llm / device / process never nest into each other; * grouping uses kind "phase" (not counted). * * Concurrency: nested collecting() reuses the outer collector and yields a * SubView that buffers only the spans appended in ITS async context — an index * slice would interleave sibling agents' spans (a real bug fixed upstream). */ import type { ModelUsage, SpanDict } from "./models.ts"; export declare class TraceCollector { spans: SpanDict[]; closed: boolean; drain(): SpanDict[]; } /** Nested-window view: buffers only spans appended within this view's context; * entries share the dict references (global i/p numbering) with the collector. */ export declare class SubView { own: SpanDict[]; get spans(): SpanDict[]; drain(): SpanDict[]; } /** * Scope a collection window around `fn`. Nesting reuses the outer collector * (spans join the same tree) and yields a SubView whose drain() returns only * this stretch's spans; the top level creates the collector and seals it on * exit (late spans from stray background tasks are refused). */ export declare function collecting(fn: (view: TraceCollector | SubView) => Promise): Promise; /** * Record one span around `fn` (exceptions mark FAIL and rethrow). No-op with * zero cost outside a window. The span dict is passed to `fn` so call sites * can backfill note/attrs (e.g. token counts after the LLM call). */ export declare function span(name: string, kind: "llm" | "device" | "process" | "phase", fn: (s: SpanDict) => Promise, opts?: { note?: string; attrs?: Record; }): Promise; /** * Graft a drained span list from another window (e.g. the executor's own tree * returned with an ExecResult) under the current parent: renumber i/p onto the * tail of the current collector; roots re-parent to the current span. t0 is * absolute epoch ms so the timeline still renders truthfully across processes. */ export declare function graft(spans: SpanDict[]): void; /** ModelUsage → span attrs token digest ({tok_in, tok_out[, tok_reasoning]}). */ export declare function llmAttrs(usage: ModelUsage | null | undefined): Record;