/** * `SdkTracer` — observer-style helper for consumers who want OTel spans driven * off the `TaskEvent` stream rather than (or in addition to) the per-public-API * spans the `QodoClient` opens internally. * * Two use cases: * * 1. **`client.receive()` taps.** A consumer reading the raw escape-hatch * stream wants spans for events the per-method wiring doesn't cover — * e.g. inbound `tool.request` envelopes observed without a registered * handler. Pass each event through `tracer.onEvent(env)` and the tracer * manages the span lifecycle. * 2. **Stream wrapping.** `tracer.wrapStream(stream)` yields every event * unchanged after recording it, so a `for await` loop gets traced * transparently. * * Span boundaries the observer opens: * * - `qar.client.task.observed` — opened on the first `task.delta` for a * previously-unseen `task_id`; closed on the matching `task.done` / * `error`. Distinct name from the internal `qar.client.task.start` so * consumers using both surfaces see one span per role rather than two. * - `qar.client.tool.request` — opened on inbound `tool.request`; closed * when the tracer next observes the matching `task.delta`/`task.done` * after the request (best effort — consumer-driven `tool.response` * doesn't surface as a `TaskEvent`, so we close on the next * forward-progress event). * * The 1.x `SdkTracer` shape (`onEvent` + `wrapStream` + `flush`) is preserved * intact — consumers upgrading don't change imports. */ import type { TaskEvent } from '../qar/index.js'; import type { OTelAPI } from './types.js'; export interface SdkTracerOptions { /** Tracer name reported to the OTel SDK (default: `'@qodo/sdk'`). */ readonly tracerName?: string; /** Tracer version (default: SDK package version). */ readonly tracerVersion?: string; /** Extra attributes stamped on every root span. */ readonly rootAttributes?: { readonly [key: string]: string | number | boolean; }; } /** * Stateful observer. Tracks in-flight spans by `task_id` and `tool_call_id`. * * The class shape (constructor, `onEvent`, `wrapStream`, `flush`) is the 1.x * surface preserved across the SDK 2.0 refactor — consumers upgrading don't * need to change imports. */ export declare class SdkTracer { private readonly otel; private readonly tracer; private readonly rootAttributes; /** Active task spans keyed by `task_id`. */ private readonly taskSpans; /** Active tool spans keyed by `${task_id ?? 'no-task'}:${tool_call_id}`. */ private readonly toolSpans; constructor(otel: OTelAPI, options?: SdkTracerOptions); /** * Observe a single `TaskEvent` and update tracing state. Idempotent + safe * to call from multiple subscribers (each consumer should construct its own * `SdkTracer` to avoid sharing the in-flight maps). */ onEvent(event: TaskEvent): void; /** * Wrap a `TaskEvent` async iterable, recording each event for tracing and * yielding it unchanged. Closes orphan spans on iterator exhaustion. */ wrapStream(stream: AsyncIterable): AsyncGenerator; /** * Force-close every in-flight span. Call on process exit / abort. The body * is fully implemented because flushing is structural — no QAR-side wiring * needed. */ flush(): void; private openTaskSpan; private openToolSpan; /** * Close any tool spans for `taskId` that have outlived a forward-progress * envelope without a matching `tool.response` ever showing up on the * inbound stream. Best-effort — `tool.response` is outbound, so the * observer never sees it directly. */ private closeOrphanedToolSpansFor; private closeTaskSpan; } /** * Factory that returns an `SdkTracer`. Preserves the 1.x ergonomics so * consumers upgrading don't have to change imports. */ export declare function createSdkTracer(otel: OTelAPI, options?: SdkTracerOptions): SdkTracer; //# sourceMappingURL=SdkTracer.d.ts.map