import type { Database as BetterSqliteDatabase } from 'better-sqlite3';
/** Canonical event kinds. `circuit-open`/`circuit-recover` come from the breaker
 *  (the primary, account-level signal); the throttle/quota/529 kinds come from
 *  RateLimitSentinel (session-scoped, secondary — tagged `source`). */
export type RateLimitEventKind = 'circuit-open' | 'circuit-recover' | 'throttle' | 'quota' | '529';
export type RateLimitEventSource = 'circuit-breaker' | 'session-sentinel';
export interface RateLimitEventInput {
    ts: number;
    kind: RateLimitEventKind;
    source: RateLimitEventSource;
    /** Stable per-event sequence within this process — together with ts+source it
     *  forms the row id, so same-ms events are distinct and restarts don't replay. */
    seq: number;
    accountKey?: string;
    sessionName?: string;
    reason?: string;
    detail?: string;
}
export interface RateLimitSummaryRow {
    /** Headline: breaker trips (the account-level signal). */
    circuitOpenCount: number;
    circuitRecoverCount: number;
    /** Session-sentinel detections, reported separately (different semantics). */
    sentinelCount: number;
    totalEvents: number;
    /** Breaker trips per hour over the window — the "rate" asked for. */
    tripsPerHour: number;
    oldestEventTs: number | null;
    newestEventTs: number | null;
    windowMs: number;
}
export interface RateLimitKindRow {
    kind: RateLimitEventKind;
    source: RateLimitEventSource;
    count: number;
    firstTs: number;
    lastTs: number;
}
export interface RateLimitEventRow {
    id: string;
    ts: number;
    kind: RateLimitEventKind;
    source: RateLimitEventSource;
    accountKey: string;
    sessionName: string | null;
    reason: string | null;
    detail: string | null;
}
/** Where a sample came from. `agent-server` = this process; `session:<id>` = a
 *  spawned session (pane PID); `aggregate` = server + all sessions combined. */
export type ResourceSampleSource = `session:${string}` | 'agent-server' | 'aggregate';
export interface ResourceSampleInput {
    ts: number;
    source: ResourceSampleSource;
    /** OS pid sampled (0/undefined for the synthetic `aggregate` row). */
    pid?: number;
    /** Instantaneous CPU percent (one core = 100). */
    cpuPercent: number;
    /** Resident set size in bytes. */
    rssBytes: number;
    /** V8 heapUsed in bytes — own process only (sessions report null). */
    heapUsedBytes?: number | null;
}
export interface ResourceSampleRow {
    ts: number;
    source: string;
    pid: number | null;
    cpuPercent: number;
    rssBytes: number;
    heapUsedBytes: number | null;
}
/** Per-source rollup over a window: current (latest) + avg + peak. */
export interface ResourceSourceSummaryRow {
    source: string;
    /** Most recent sample's values (the "right now" reading). */
    currentCpuPercent: number | null;
    currentRssBytes: number | null;
    currentHeapUsedBytes: number | null;
    avgCpuPercent: number;
    peakCpuPercent: number;
    avgRssBytes: number;
    peakRssBytes: number;
    sampleCount: number;
    newestTs: number | null;
}
export interface ResourceLedgerOptions {
    /** SQLite db path, or ':memory:' for tests. */
    dbPath: string;
    /** Test seam — inject a Database instance. */
    databaseFactory?: (dbPath: string) => BetterSqliteDatabase;
}
export declare class ResourceLedger {
    private db;
    private insertEvent;
    private insertSample;
    private closed;
    constructor(opts: ResourceLedgerOptions);
    /** Per-event identity: ts + source + seq is unique within a process; the source
     *  prefix keeps breaker and sentinel rows from ever colliding. NOT a content
     *  hash, so two genuine same-ms events stay distinct. */
    private static eventId;
    /** Record one rate-limit event. Idempotent on (source,ts,seq). Never throws —
     *  observability must not break the observed path. */
    recordRateLimitEvent(e: RateLimitEventInput): void;
    /** Count + rate of rate-limit events over the trailing window. */
    rateLimitSummary(nowMs: number, windowMs: number): RateLimitSummaryRow;
    /** Per-kind breakdown over the window. */
    rateLimitByKind(nowMs: number, windowMs: number): RateLimitKindRow[];
    /** Recent event rows, newest first. */
    rateLimitEvents(opts?: {
        sinceMs?: number;
        limit?: number;
    }): RateLimitEventRow[];
    /** Record one CPU/memory sample. Never throws — observability must not break
     *  the observed path. Negative/NaN numbers are clamped so a bad reading can't
     *  poison aggregates. */
    record(s: ResourceSampleInput): void;
    /** Record a batch of samples in one transaction (one tick's worth). Fail-open:
     *  a write error rolls back this batch only and is swallowed. */
    recordSamples(samples: ResourceSampleInput[]): void;
    private static clampNonNeg;
    /**
     * Per-source CPU% + RSS rollup over the trailing window: current (latest
     * sample), plus avg/peak across the window, plus the synthetic `aggregate`
     * row if present. Newest-first ordering by recency of last sample.
     */
    summary(sinceMs: number): ResourceSourceSummaryRow[];
    /** Recent raw samples, newest first (paginated). */
    recentSamples(opts?: {
        sinceMs?: number;
        limit?: number;
        source?: string;
    }): ResourceSampleRow[];
    /** Total number of CPU/mem samples stored. */
    sampleCount(): number;
    /** Delete samples older than `cutoffMs`. Returns rows deleted. Fail-open. */
    pruneOlderThan(cutoffMs: number): number;
    close(): void;
}
//# sourceMappingURL=ResourceLedger.d.ts.map