/** * Skills-usage write batching queue — keeps skill-load latency near zero. * * The {@link recordSkillUsage} recorder (T9689) is on the hot path of every * skill discovery. Even though its DB write is detached via `void`, a burst * of 100+ skill loads (e.g. orchestrator startup walking ~/.cleo/skills/) * would still trigger 100+ independent SQLite transactions. This queue * coalesces them into a single batched INSERT. * * ## Design * * - Bounded ring buffer (default 256 entries). * - Auto-flush triggers: * 1. 250 ms idle (debounce: every enqueue resets the timer). * 2. Queue full — flush immediately when entry 257 would overflow. * 3. `process.beforeExit` — installed exactly once at first enqueue. * - `flushSkillsUsage()` exposed for explicit shutdown / tests. * - `drainSkillsUsageQueue()` test-only seam — synchronously empties * without writing. * * The queue does NOT replace {@link insertUsage}. Direct callers (Hermes * import, tests, the future auto-improve patch path) keep using the * synchronous insert API. ONLY the loader hook (T9689) routes through * the queue, because that's the hot path where batching pays off. * * @task T9694 * @epic T9561 * @saga T9560 * @architecture docs/architecture/SG-CLEO-SKILLS-architecture-v3.md §5 */ import { type NewSkillUsageRow, type SkillUsageRow } from './schema/skills-schema.js'; /** Default ring-buffer capacity. Overridable via {@link SkillsUsageQueue} ctor. */ export declare const SKILLS_USAGE_QUEUE_DEFAULT_CAPACITY = 256; /** Default idle-flush window in milliseconds. */ export declare const SKILLS_USAGE_QUEUE_DEFAULT_IDLE_MS = 250; /** * Options accepted by the {@link SkillsUsageQueue} constructor. */ export interface SkillsUsageQueueOptions { /** Ring-buffer capacity — when reached, the queue flushes immediately. */ readonly capacity?: number; /** Idle-flush window in milliseconds — `0` disables debounced flush. */ readonly idleMs?: number; /** Disable the `process.beforeExit` auto-flush hook (test-only). */ readonly disableBeforeExitHook?: boolean; } /** * In-memory batched writer in front of `skill_usage`. * * Use the module-level singleton via {@link enqueueSkillUsage} / * {@link flushSkillsUsage} in production code. The class is exported so * tests can construct isolated instances pointing at tmp DBs. * * @task T9694 */ export declare class SkillsUsageQueue { private readonly capacity; private readonly idleMs; private readonly disableBeforeExitHook; private buffer; private timer; private beforeExitInstalled; private flushing; constructor(options?: SkillsUsageQueueOptions); /** * Append a row. Triggers an immediate flush if capacity is reached, * otherwise (re-)arms the idle-flush timer. * * @param row - Telemetry payload to persist. */ enqueue(row: NewSkillUsageRow): void; /** * Flush all buffered rows in a single batched INSERT. * * Safe to call concurrently — overlapping callers share the same in-flight * promise so the batch is written exactly once. * * @returns Resolves when the buffered rows are persisted (or swallowed if * the DB is unreachable). */ flush(): Promise; /** * Synchronously discard buffered rows without writing — test-only seam. * * Production code MUST NOT call this. Used by tests that want to assert * "no DB row until flush" without polluting the next test's state. */ drain(): NewSkillUsageRow[]; /** Returns the current buffer length — visible for assertions. */ get size(): number; private installBeforeExitHookOnce; } /** * Return the process-wide singleton queue, creating it on first use. * * Tests that need isolated state should construct their own {@link SkillsUsageQueue} * with `disableBeforeExitHook: true` to avoid leaking listeners across cases. */ export declare function getSkillsUsageQueue(): SkillsUsageQueue; /** * Replace the module-level singleton — used by tests to inject an isolated * queue (pointed at a tmp DB) WITHOUT touching the user-global one. * * Pass `null` to reset back to lazy default-construction on next call. * * @internal */ export declare function __setSkillsUsageQueueSingleton(q: SkillsUsageQueue | null): void; /** * Enqueue a single usage row via the singleton queue. * * @param row - Telemetry payload. * * @task T9694 */ export declare function enqueueSkillUsage(row: NewSkillUsageRow): void; /** * Flush the singleton queue. Returns when the in-flight batch is persisted. * * @task T9694 */ export declare function flushSkillsUsage(): Promise; export type { NewSkillUsageRow, SkillUsageRow }; //# sourceMappingURL=skills-usage-queue.d.ts.map