/** * Builder for the `plugin.senpi.strategies` payload — the topology view of the live telemetry * stream: which strategies exist, whether each is up, how healthy it is, and which scanners it * declares. * * Pure. Every fact arrives as an input, nothing is read from the registry or a runtime handle here, * so the shape on the wire is testable without booting a runtime. * * Two properties this module exists to hold: * - `status` distinguishes a runtime that is down from one that is up but has no live entry * scanners. The second reads as healthy unless it is named, and it is not. * - The raw scanner `config` never reaches the wire. It is an unbounded passthrough record and it * has been through `resolveEnvInObject`, so it may hold resolved secrets. Only the declared, * typed fields below are copied, each by name. */ import type { ComponentHealth } from "../health/types.js"; import type { RuntimeScanner } from "../runtime/runtime-schema.js"; /** Payload schema version. Bump when a field changes meaning, not when one is added. */ export declare const STRATEGY_TOPOLOGY_VERSION = 1; /** * `stopped` — no runtime handle. * `running_no_entry_scanners` — up, but its declared external scanners never wired, so it produces * no entry signals. * `running` — up and wired. */ export type StrategyStatus = "running" | "stopped" | "running_no_entry_scanners"; /** Membership test satisfied by both a `Map` and a `Set` — the two shapes the plugin holds. */ export interface IdMembership { has(id: string): boolean; } /** Engine registration facts for one scanner. Absent when the runtime has no live handle. */ export interface TopologyScannerRegistration { enabled: boolean; scheduleMode: "interval" | "external"; intervalSeconds: number | null; configVersion: number | null; } export interface TopologyScannerSource { /** * The parsed YAML scanner entry. Only `name`, `type`, `interval_seconds` and * `signal_data_schema` are ever read from it — `config`, `advanced`, `inputs` and any * passthrough key stay off the wire. */ entry: RuntimeScanner; registration?: TopologyScannerRegistration; } export interface TopologyStrategySource { runtimeId: string; /** The config wallet as written. Normalized to the wire form by the builder — see below. */ address: string; name: string; scanners: TopologyScannerSource[]; } /** The slice of `RuntimeHealthStatus` the topology reads. `startedAt` is ISO, as health emits it. */ export interface RuntimeHealthLike { health: ComponentHealth; startedAt?: string; } export interface BuildStrategyTopologyInput { /** The buffer's epoch ULID. A change means the plugin restarted. */ epoch: string; strategies: TopologyStrategySource[]; /** Runtimes holding a live handle. Everything else is stopped. */ runningRuntimeIds: IdMembership; /** Runtimes that are up but whose entry scanners never wired. */ unwiredRuntimeIds: IdMembership; /** Per-runtime health lookup, keyed on `runtimeId` the way `senpi.getHealthStatus` keys. */ readHealth?: (runtimeId: string) => RuntimeHealthLike | undefined; /** Injectable clock for deterministic tests. */ now?: () => number; } export interface StrategyTopologyScanner { /** The engine id — the YAML `name:` field. Unique within a runtime, not across runtimes. */ scannerId: string; scheduleMode: "interval" | "external"; enabled: boolean; intervalSeconds: number | null; configVersion: number | null; signalDataSchema: Record | null; } export interface StrategyTopologyEntry { runtimeId: string; address: string; name: string; status: StrategyStatus; health: ComponentHealth; /** Epoch ms, converted from health's ISO stamp; null when health is unavailable. */ startedAt: number | null; scanners: StrategyTopologyScanner[]; } export interface StrategyTopologyPayload { v: number; epoch: string; generatedAt: number; strategies: StrategyTopologyEntry[]; } /** Builds one `plugin.senpi.strategies` payload from an already-gathered set of facts. */ export declare function buildStrategyTopology(input: BuildStrategyTopologyInput): StrategyTopologyPayload; //# sourceMappingURL=strategy-topology.d.ts.map