/** * A structured log that telemetry reads. It is a LOG on purpose — not an event — and it must stay one. * * Do NOT "promote" a call site here to `logger.event()`, and do not delete or downgrade one as noise: * a telemetry consumer parses these records by their field names. * * Why a log and not an event: `logger.event()` is unconditional (the trade narrative may never go * dark) and every event also reaches `appendDomainEvent`, which feeds the per-strategy on-disk ring * and the live-telemetry publisher's bounded in-memory buffer. These facts are per-tick and * high-frequency, so catalogued they would evict real decision records from that buffer. Routed * through the plain log path they stay level-gated — silenceable by configuration, no code change — * and nothing taps them, so nothing is evicted. * * The level is fixed at `info`: the default minimum level, so these records are present in a stock * deployment, while `SENPI_LOG_LEVEL=warn` still silences them. `debug` would be off by default and * telemetry would go dark without anyone noticing. */ import type { Logger } from "./logger.js"; /** A value a consumer can parse on sight: no shape to walk, no encoding to agree on. */ export type TelemetryScalar = string | number | boolean; /** Named structured fields — scalars only. A consumer reads keys, never a formatted sentence. */ export type TelemetryLogFields = Record; /** Named structured fields, widened by one shape: a list whose every element is a scalar. */ export type TelemetryListFields = Record; /** * Keep the values a consumer can actually parse: strings, finite numbers, booleans. Everything else — * objects, arrays, `null`/`undefined`, `NaN`/`Infinity`, functions — is dropped and counted. * * Exported because the tick frame's state projection has to apply the SAME rule to an author's own * state row. Sharing the implementation is what makes "sanitised exactly as the required log does" * true rather than intended. */ export declare function sanitizeFields(fields: unknown): { attributes: TelemetryLogFields; dropped: number; }; /** * `sanitizeFields`, widened by exactly one shape: a list whose every element is a scalar. * * A SECOND entry point rather than a flag on the first, because the two rules belong to different * consumers and the narrow one is a contract two call sites depend on. The required log's consumer * reads flat named fields, and the tick frame's state projection promises to carry what that log * would have written; neither may gain lists as a side effect of a caller that wants them. * * A single non-scalar element refuses the WHOLE list rather than filtering it down. Filtering would * let an author's unbounded nested value decide how much of itself gets rendered, and scalars-only * is the rule that keeps a nested payload off the wire. The kept list is a new array, so the * caller's own can still be mutated without changing what was recorded. */ export declare function sanitizeFieldsWithScalarLists(fields: unknown): { attributes: TelemetryListFields; dropped: number; }; /** * Write one telemetry-required log. `name` is a stable machine identifier and becomes the whole * body — the facts live in `fields`, never interpolated into a sentence. The wrapper's own keys are * applied last, so a caller field can never shadow them. Never throws into the caller. */ export declare function logRequiredByTelemetry(logger: Logger, name: string, fields: TelemetryLogFields): void; //# sourceMappingURL=telemetry-required-log.d.ts.map