/** * Local domain-event store: a per-strategy on-disk ring of the events `logger.event` emits, so the * agent can query the trade narrative locally without a collector. Additive to the OTel collector * emit — this is a second, independent sink. * * Layout: `{stateDir}/strategies/{address}/events/events.jsonl`, rotated to `events.1.jsonl` once it * passes the size cap (a 2-file ring — at most ~2× the cap on disk per strategy). Writes are * fire-and-forget and serialized per sink, so the hot `event()` path only enqueues — never blocks on * I/O — and a rotation can't interleave with an append. Failures are swallowed: the event log must * never disturb trading. */ import type { AttrValue } from "../utils/event-catalog.js"; import { type LogLevel } from "../utils/logger.js"; /** One persisted domain event: the catalogued name, severity, narrative body, call-time stamp, and the event's own capped scalar bag. Never the redact free-text slot. */ export interface StoredEvent { name: string; level: LogLevel; body: string; /** Call-time epoch ms (the same stamp the collector record carries). */ ts: number; attrs: Record; } /** Filters for {@link readEvents}; all optional. Time bounds are epoch ms, inclusive. */ export interface EventQuery { name?: string; asset?: string; level?: LogLevel; sinceMs?: number; untilMs?: number; /** Keep the last N after filtering (newest tail). */ limit?: number; } export interface EventSink { /** Enqueue one event for async, serialized, best-effort persistence. Returns immediately. */ append(event: StoredEvent): void; /** Resolve once all currently-enqueued writes have settled (for shutdown drains and tests). */ flush(): Promise; } /** * Normalize a wallet to the routing/path key. The strategy address rides telemetry lowercased and a * runtime's config address may be mixed-case, so lowercasing here keeps the registration key, the * per-event resolved address, and the on-disk path in agreement (a mismatch would silently misroute). */ export declare function normalizeStrategyAddress(address: string): string; /** * A per-strategy ring writer. Appends are serialized through one promise chain so the size check and * rotation can't race a concurrent append; the caller's `append` only links the chain (no I/O). */ export declare function createEventStore(stateDir: string, address: string, maxBytes?: number): EventSink; /** Register a runtime's event sink under its strategy wallet. Replaces any prior sink for that address. */ export declare function registerEventSink(address: string, sink: EventSink): void; /** Drain a registered sink's queued writes (best-effort) before it is unregistered on shutdown. */ export declare function flushEventSink(address: string): Promise; /** Remove a runtime's event sink on shutdown. */ export declare function unregisterEventSink(address: string): void; /** Test-only: drop all registered sinks. */ export declare function clearEventSinks(): void; /** * Tap point for `logger.event`: route one event to its strategy's local ring. Resolves the wallet from * the async-context identity (in-flow events) or the event's own `senpi.strategy.address` * (lifecycle/intake events) and enqueues a best-effort write of the body + the event's own capped * scalars. Stores every event — the redact free-text slot (never passed here) is the only thing the * local store omits. A no-op when no sink is registered or the address is unknown; never throws. */ export declare function appendDomainEvent(name: string, level: LogLevel, body: string, attributes: Record | undefined, timestampMs: number): void; /** * Read a strategy's stored events oldest-first, filtered, capped to the last `limit`. Reads the * archive then the current file (both size-bounded by the ring), so memory stays bounded. Returns * `[]` when nothing has been stored yet. */ export declare function readEvents(stateDir: string, address: string, query?: EventQuery): Promise; //# sourceMappingURL=event-store.d.ts.map