/** * The producer tap for the live telemetry stream: a second {@link EventSink} that copies selected * `logger.event` records into the in-memory {@link TimelineBuffer} while the on-disk ring keeps * receiving every one of them. It sits on the serial trading path, so it resolves the scanner id, * picks a fixed set of fields, appends, and returns — no I/O, no await, no counting, no serialization. * * Two lists govern the stream, and this module owns only the first: * - ADMISSION ({@link ADMITTED_EVENT_NAMES}) — what enters the buffer. Every name the timeline * displays PLUS every name a counter is derived from, because a counting source that never enters * the buffer makes the stats read zero with nothing logged anywhere. * - PUBLICATION — what leaves on the timeline event. Applied at flush time, not here. Admission is * deliberately the wider of the two: filtering for display at append time is unrecoverable. */ import { type EventName } from "../utils/event-catalog.js"; import { type EventSink } from "./event-store.js"; import { type TimelineBuffer } from "./timeline-buffer.js"; /** * The names that may enter the buffer. Volume control for a bounded ring, so it is an allow-list of * the strategy's own narrative: what its scanners did, what each signal became, what was traded, and * why the strategy's status changed. It excludes the box-operations and agent-plane families — * `auto_update.*`, `tool.*`, `tool_approval.*`, the installer and state/mcp faults — and the * mechanical, poll-frequency records (`dsl.sl_updated`, `dsl.close_pending`, `dsl.close_attempt`, * `dsl.price_fetch_stale`), which would evict a strategy's recent window without adding a line * anyone reads. * * Typed as `EventName`, so renaming a catalog entry out from under this list is a compile error * rather than a silently empty counter. */ export declare const ADMITTED_EVENT_NAMES: ReadonlySet; /** * What the tap cost, in nanoseconds, since the last read. Two plain numbers and a maximum — no span, * no histogram, no allocation: a span per event is exactly the blocking cost the buffer exists to * avoid, and this sits on the same serial trading path the records describe. */ export interface TapCounters { /** Tap invocations, admitted or not: the admission check is cost the trading path already paid. */ calls: number; nsTotal: number; /** The longest single call. An average hides the one append that had to evict. */ nsMax: number; } /** * Take the window and zero it. The reader owns the window: a counter that is never reset reports a * lifetime total forever, which sizes nothing. Process-wide, like the buffer the tap feeds. */ export declare function readAndResetTapCounters(): TapCounters; export interface TelemetryPublisherOptions { /** Buffer to feed. Defaults to the process buffer — one per process, shared by every address. */ buffer?: TimelineBuffer; } /** * A sink that copies admitted records into the timeline buffer under `address`. One instance per * registered address; every instance writes into the same process buffer, which is what makes the * publisher process-wide and address-partitioned rather than per-runtime. */ export declare function createTelemetryPublisherSink(address: string, options?: TelemetryPublisherOptions): EventSink; /** * One sink that feeds several. `registerEventSink` keys a single sink per address, so this is how * the publisher rides alongside the on-disk ring instead of replacing it. Each branch already * swallows its own failures; the loop keeps one branch's throw from starving the rest regardless. */ export declare function createFanOutEventSink(sinks: readonly EventSink[]): EventSink; //# sourceMappingURL=telemetry-publisher.d.ts.map