/**
 * OBSERVE.1 — trace timeline reader.
 *
 * Reads the existing DURABLE.1 + DURABLE.4 tables (`checkpoints` +
 * `run_manifests` + `terminal_markers`) and renders them as a typed
 * timeline. There is NO separate trace table — the checkpoint log IS
 * the trace data. Every query in this module hits those three tables
 * directly; nothing is duplicated, nothing is denormalized.
 *
 * Status inference (4 paths):
 *
 *   completed    — terminal_markers row exists AND no errored checkpoint
 *   errored      — terminal_markers row exists AND ≥1 errored checkpoint
 *   in_flight    — no terminal_markers row AND lastCompletedAtMs within
 *                  RESUME_WINDOW_MS (60s, same as Resumer)
 *   interrupted  — no terminal_markers row AND lastCompletedAtMs is older
 *                  than RESUME_WINDOW_MS (the Resumer would skip these)
 *
 * OpenTelemetry export uses derived IDs:
 *
 *   trace_id  =  sha256(runId).slice(0, 32)              // 16 bytes / 32 hex
 *   span_id   =  sha256(runId + ':' + stepIdx).slice(0, 16)  // 8 bytes / 16 hex
 *
 * Previews are truncated to 200 user-visible code points (UTF-8-safe via
 * `Array.from`) and then passed through `stripSecrets` from
 * `deliver_only.ts` — same regex pattern as the SCHED.2 deliver-only
 * webhook path. Inputs only carry a content-addressed hash in the
 * checkpoint, so `inputsPreview` is derived from the hash; outputs hold
 * the canonical-JSON-revived value so `outputsPreview` is a JSON snippet.
 *
 * Tail polling defaults to 1000ms with a 100ms floor — libsql is
 * resilient but hammering at 10ms doesn't reflect anything `tail`-shaped
 * a user actually needs.
 *
 * Imports from: @libsql/client, node:crypto, ../deliver_only.js,
 *               ../durable/canonical_json.js, ./trace_types.js.
 * Imported by: OBSERVE.2 CLI, downstream consumers via barrel.
 */
import type { Client } from '@libsql/client';
import type { TraceEvent, TraceListEntry, TraceStatus, TraceTimeline } from './trace_types.js';
export interface TailOpts {
    /** Earliest `completed_at_ms` to surface. Defaults to `Date.now()`. */
    sinceMs?: number;
    /** Optional pack filter. */
    packId?: string;
    /** Polling interval in ms; floored at 100ms; default 1000ms. */
    intervalMs?: number;
    /** AbortSignal to stop the polling loop cleanly. */
    signal?: AbortSignal;
}
export interface ListRecentOpts {
    limit?: number;
    sinceMs?: number;
    packId?: string;
    skill?: string;
    status?: TraceStatus;
}
export declare class TraceReader {
    private readonly db;
    private readonly nowMs;
    constructor(db: Client, nowMs?: () => number);
    /**
     * Materialize a full timeline for `runId`. Joins one row from
     * `run_manifests` with its ordered checkpoint rows, then derives status
     * from the presence of a `terminal_markers` row + the age of the last
     * completed step.
     *
     * Returns `null` when the manifest is missing — even if checkpoint rows
     * exist, an orphan run can't be reconstructed without manifest identity
     * (matches the Resumer's `manifest_missing` audit reason).
     */
    getTimeline(runId: string): Promise<TraceTimeline | null>;
    /**
     * Recent runs, filterable by pack/skill/status/window. Uses
     * `idx_run_manifests_started_at` for the time-range scan, which keeps
     * the query sub-100ms at 1M-row scale (validated via EXPLAIN — the
     * index is hit before the LEFT JOIN against terminal_markers).
     *
     * Status filter is applied AFTER the SQL pass since `interrupted` vs
     * `in_flight` is a time-based heuristic, not a column.
     */
    listRecent(opts?: ListRecentOpts): Promise<TraceListEntry[]>;
    /**
     * Polling tail. Returns an `AsyncIterable<TraceEvent>` that yields every
     * checkpoint row whose `completed_at_ms` is `> cursor`, where `cursor`
     * starts at `opts.sinceMs` and advances after every batch.
     *
     * Termination: caller passes an `AbortSignal`. When aborted, the
     * generator returns cleanly (no thrown error, no leaked timer).
     *
     * libsql has no CDC; polling at 1s is fine for human-facing tail use
     * cases. Higher-frequency consumers (CI dashboards) can drop to the
     * 100ms floor.
     */
    tail(opts?: TailOpts): Promise<AsyncIterable<TraceEvent>>;
    /**
     * Serialize a timeline as JSON (pretty) or OTEL (subset of the OTLP
     * trace JSON format — vendor-importable into AgentOps / LangSmith /
     * Jaeger). Returns `''` for a nonexistent runId so callers don't have
     * to disambiguate `null` vs serialization failure.
     */
    export(runId: string, format: 'json' | 'otel'): Promise<string>;
}
//# sourceMappingURL=trace_reader.d.ts.map