/** * Manual OTel instruments, mirroring `tracing.ts`: obtained through the `@opentelemetry/api` proxy * meter, so they no-op before the SDK initialises and resolve the real meter afterwards. * * One lazy cache stands up every instrument once: a name → instrument map per kind, fed by a spec * table (description + unit). The instrument is created on first record (so it binds to the real * meter after SDK init, not the no-op meter at module load) and cached thereafter. Every record * helper is fully guarded — metrics must never throw into a caller path. * * GenAI instruments follow the GenAI semantic conventions; operational instruments mirror their * event-family names (`scanner.*`, `runtime.*`), with attributes in the shared `senpi.*` namespace. */ import { type Meter } from "@opentelemetry/api"; /** The single meter for our manual instruments. Resolves the no-op meter pre-init. */ export declare function getMeter(): Meter; /** Reset cached instruments (tests only). */ export declare function __resetMetricsForTest(): void; /** Elapsed seconds from a `Date.now()` start, clamped to non-negative. */ export declare function elapsedSeconds(startMs: number | undefined): number | undefined; export interface GenAiOperationDims { provider?: string; model?: string; } /** * Record token usage at chat finalize: one data point per token type present (input/output and, when * the model reports them, cache_read/cache_write — the signal a silent context compaction otherwise * hides). Skips a token type whose count is undefined. */ export declare function recordTokenUsage(dims: GenAiOperationDims, tokens: { input?: number; output?: number; cacheRead?: number; cacheWrite?: number; }): void; /** Record the chat operation duration (seconds); stamps `error.type` when set. */ export declare function recordOperationDuration(seconds: number | undefined, dims: GenAiOperationDims & { errorType?: string; }): void; /** * The origin-uniform tool-call metric plane: one entry point recording both the `tool.calls` counter * (every call) and the `gen_ai.execute_tool.duration` histogram (when a duration was measured), under * one set of dimensions so a single query spans every dispatch origin. `origin` is `agent`|`mcp`| * `decision`; `outcome` is `ok`|`error`|`unknown`|`denied`. The narrative event (`tool.called`) is * separate and abnormal-only — this is the count/weight half. */ export interface ToolCallDims { toolName?: string; /** Dispatch site: `agent` | `mcp` | `decision`. */ origin: string; /** `ok` | `error` | `unknown` | `denied`. */ outcome: string; /** Low-cardinality error class on an abnormal outcome (`errorTypeOf`). */ errorType?: string; /** Measured call duration in seconds; omitted when the call never ran (e.g. an invented tool). */ durationSec?: number; } export declare function recordToolCall(dims: ToolCallDims): void; /** * Terminal outcome of one `senpi deploy` job (M6): `refused` is an OUTCOME, recorded here exactly * like `live` — it never marks a span errored. `refusalCode` carries the `[E_*]`/`[INVALID_REQUEST]` * taxonomy code of the first refusal, so a refusal spike is attributable without reading reports. * * `indeterminateCode` is the separate dimension for the one `failed` that leaves a funded wallet * behind ON PURPOSE — a deploy that stopped waiting on an install it could not cancel. It is not * folded into `refusalCode` (nothing was refused, and that dimension backs the exit-code contract) * and it cannot be inferred from `outcome`, which reads `failed` for an ENOSPC just the same. The * rollback counter is silent on this path by design, so without it the population is invisible. */ export declare function recordDeploy(dims: { outcome: string; refusalCode?: string; indeterminateCode?: string; durationSec: number; }): void; /** One deploy phase (reconcile/preflight/create/install/observe), with its worst step status. */ export declare function recordDeployStep(dims: { step: string; status: string; durationSec: number; }): void; /** * One D-6 rollback attempt. `incomplete` means a wallet this job created and funded is live, * unwatched, and could not be closed — the alert-worthy signal in this whole plane. */ export declare function recordDeployRollback(dims: { result: "ok" | "incomplete"; }): void; /** * Record one MCP tool approval-gate decision. `decision` is the terminal user * choice (`allow-once` | `allow-always` | `deny` | `timeout`); `isTrade` marks a * state-changing tool vs a read gated only in `all` mode. Lets dashboards chart * allow-always adoption and deny/timeout rates per tool. */ export declare function recordApprovalDecision(dims: { toolName: string; decision: string; isTrade: boolean; }): void; /** Record the sub-agent invocation duration (seconds); stamps `error.type` when set. */ export declare function recordInvokeAgentDuration(seconds: number | undefined, dims: { agentName?: string; errorType?: string; }): void; /** Low-cardinality dimensions for the supervised-scanner health instruments. */ export interface ScannerHealthDims { /** Recipe-declared scanner name — stable across relaunch (the ephemeral scanner id is not a dim). */ scannerName?: string; /** Strategy wallet, lowercased by the caller to match the identity join key. */ strategyAddress?: string; } /** Count one supervised-scanner restart (the supervisor's `restarting` edge). */ export declare function recordScannerRestart(dims: ScannerHealthDims): void; /** * Move the `scanner.degraded` state gauge: +1 on the healthy→degraded edge, −1 on degraded→recovered. * The supervisor fires each edge once, so the net is the count of scanners currently degraded — * alertable as `> 0` without a trace or log scan. */ export declare function recordScannerDegradedDelta(delta: number, dims: ScannerHealthDims): void; /** Count one scanner run by outcome (the run status). */ export declare function recordScannerRun(dims: { scannerId?: string; outcome: string; }): void; /** Time a signal's wait in the serial queue before processing (seconds). */ export declare function recordSignalQueueWait(seconds: number | undefined, dims: { scannerId?: string; }): void; /** Count one signal-intake disposition at the HTTP boundary (accepted/rejected/expired/superseded/…). */ export declare function recordSignalIntake(dims: { scannerName?: string; disposition: string; reasonCode?: string; }): void; /** * Count one post-decision signal outcome by action type and result (accepted/rejected/blocked/error). * `actionType` is the emitting action's type, under the same attribute name the `action.execute` span * carries — every action feeds this one counter, so without it a bucket like `accepted` merges order * submissions with position-tracker lifecycle fires and is only separable by knowing which reason * codes belong to which action. Bounded: one value per registered action type. */ export declare function recordSignalOutcome(dims: { actionType: string; result: string; reasonCode?: string; }): void; /** * Count one trade decision by action type, mode and intent. `intent` is derived from the decision's * own action type, not from the action that ran it, so it does not identify the producer; `actionType` * is the emitting action, matching {@link recordSignalOutcome}. */ export declare function recordDecision(dims: { actionType: string; mode?: string; intent?: string; byLlm?: boolean; }): void; /** Count one venue order submission by outcome (placed/filled/failed). */ export declare function recordOrder(dims: { asset?: string; outcome: string; reason?: string; errorType?: string; }): void; /** Count one DSL degradation event by kind (sl_sync/price_stale/handoff). */ export declare function recordDslDegradation(dims: { failure: string; handoffFailure?: string; errorType?: string; }): void; /** Count one StateManager write that failed to persist. */ export declare function recordStatePersistFailure(): void; /** Count one clearinghouse-state fetch retry by outcome (`stale` | `unreadable` | `exhausted`). */ export declare function recordClearinghouseRetry(outcome: string): void; /** Per-runtime identity for the liveness heartbeat: wallet + the optional fleet `group` key. */ export interface RuntimeIdentityDims { /** Strategy wallet, lowercased by the caller to match the identity join key. */ strategyAddress?: string; /** Deployment/fleet key — answers "same strategy failing across many boxes". */ group?: string; } /** * Emit one liveness tick for a runtime. Called once at start and then every window: the *absence* of * ticks for a runtime (by wallet/group) is what detects a dead runtime or box — a down process can't * emit a "down" event, only the gap in this positive signal is alertable. */ export declare function recordRuntimeHeartbeat(dims: RuntimeIdentityDims): void; /** One runtime's contribution to the shared `runtime.queue_depth` gauge. */ export interface QueueDepthSource { /** Strategy wallet, lowercased by the caller to match the identity join key. */ strategyAddress?: string; /** Deployment/fleet key. */ group?: string; /** Live, sync read of the current queue depth — invoked at each metric collection. */ read: () => number; } /** * Register a runtime's queue depth as a callback on the shared `runtime.queue_depth` observable gauge, * dimensioned by wallet so N runtimes in one process report distinct series. The callback reads the live * depth at each collection. Returns a disposer the runtime must call on stop to drop its observation. */ export declare function registerQueueDepthGauge(source: QueueDepthSource): () => void; /** One runtime's contribution to the shared `dsl.active_positions` gauge. */ export interface DslActivePositionsSource { /** Strategy wallet, lowercased by the caller to match the identity join key. */ strategyAddress?: string; /** Deployment/fleet key. */ group?: string; /** Live, sync read of this runtime's active DSL positions counted by exit phase. */ read: () => { phase1: number; phase2: number; }; } /** * Register a runtime's active DSL-managed positions as a callback on the shared `dsl.active_positions` * observable gauge, split by exit phase (`senpi.dsl.phase` 1|2) and dimensioned by wallet. Both phases * are observed every collection (0 when empty) so the series stay live — a flat 0 (alive, no positions) * reads differently from an absent series (a dead runtime). The callback reads the live counts at each * collection. Returns a disposer the runtime must call on stop to drop its observation. */ export declare function registerDslActivePositionsGauge(source: DslActivePositionsSource): () => void; import type { ValidateResult } from "../validate/run.js"; import type { VerifyReason } from "../validate/proof.js"; /** One validate run and every finding it produced. Duration in milliseconds. */ export declare function recordValidateRun(result: ValidateResult, durationMs: number): void; /** One proof verification, by outcome alone. */ export declare function recordProofVerification(outcome: "ok" | VerifyReason): void; //# sourceMappingURL=metrics.d.ts.map