/**
 * `OpenSquidDaemon` — unified background process for every inbound trigger
 * source that doesn't ride on a host tool-call hook. Source: SCHED.1.
 *
 * Scope: node-cron schedules + HMAC webhook intake + singleton via
 * proper-lockfile. AUTO.5 file watchers and AUTO.6 inbound channels plug
 * into the same lifecycle later without changing the public surface.
 *
 * `start()` atomic: validate cron, acquire lock, register tasks, bind
 * webhook server, write pid file. Rollback on failure so the next start
 * never trips over partial state. `stop()` strict-order: cron tasks →
 * webhook server → pid file → lock release. Each step best-effort; only
 * the lock release is irrecoverable. Signals (SIGTERM/SIGINT) fire stop()
 * exactly once.
 *
 * Singleton via `proper-lockfile.lock(daemonLockPath(), { retries: 0,
 * realpath: false })`. `realpath: false` so the target need not exist;
 * `retries: 0` fails fast on contention. Engine-vocabulary: no consumer
 * product names — consumers wire their own `dispatch` callback.
 *
 * Imported by: src/cli.ts (daemon CLI), runtime tests.
 */
import { type Client } from '@libsql/client';
import type { NotificationRouter } from '../channels/router.js';
import type { RoutingConfig } from '../channels/types.js';
import { AuditLog } from './audit_log.js';
import type { Resumer, ResumeAuditEntry } from './durable/index.js';
import type { Event } from './event.js';
import type { RateLimiter } from './rate_limit.js';
import { type ScheduleEntry } from './schedule_registry.js';
import { type FileWatcherAuditEntry } from './triggers/index.js';
import type { Pack } from './types.js';
import { type WebhookAuditSink } from './webhook_server.js';
import type { Subscription } from './webhook_subscriptions.js';
/** Caller routes `ScheduleEvent` + `WebhookEvent` into the runtime
 * evaluator. The daemon doesn't import the evaluator directly — keeps
 * this module dependency-light AND lets tests dispatch into a stub. */
export type DaemonDispatcher = (event: Event) => Promise<void>;
export type DaemonAuditEntry = {
    event: 'schedule_fired';
    entryId: string;
    fireTime: string;
} | {
    event: 'schedule_rate_limited';
    entryId: string;
    fireTime: string;
} | {
    event: 'schedule_error';
    entryId: string;
    reason: string;
    fireTime: string;
} | {
    event: 'webhook';
    payload: Parameters<WebhookAuditSink>[0];
} | {
    event: 'file_changed';
    payload: FileWatcherAuditEntry;
} | {
    event: 'lifecycle';
    phase: 'start' | 'stop' | 'sigterm' | 'sigint';
    at: string;
} | {
    event: 'resume';
    payload: ResumeAuditEntry;
};
export type DaemonAuditSink = (entry: DaemonAuditEntry) => void;
export interface DaemonOpts {
    /** Daemon walks `pack.skills[].triggers[]` for schedule entries. */
    packs: readonly Pack[];
    /** Resolved + secret-bearing subscription list (see webhook_subscriptions.ts). */
    subscriptions: readonly Subscription[];
    dispatch: DaemonDispatcher;
    /** Webhook port. Default 8765. */
    webhookPort?: number;
    webhookHost?: string;
    /** Rate-limiter (AUTO.2). Applied per-fire on cron + per-request on webhook. */
    rateLimiter?: RateLimiter;
    /** SCHED.2 — router + config used for `deliverOnly: true` subscriptions.
     *  Optional: a daemon with no deliver-only subscriptions doesn't need
     *  either field. If a deliver-only sub fires without these set, the
     *  webhook server returns 200 + audits as `misconfigured`. */
    notificationRouter?: NotificationRouter;
    routingConfig?: RoutingConfig;
    auditLog?: DaemonAuditSink;
    /** DURABLE.4 — restart-safe resume hook. When set, `start()` invokes
     *  `resumer.resumeOnStartup()` AFTER lock acquire + BEFORE cron tasks
     *  register so interrupted runs replay before fresh fires race. The
     *  resumer's own audit entries are surfaced via the daemon audit sink
     *  under the `resume` event variant. Optional — daemons without a
     *  checkpoint store don't need it. */
    resumer?: Resumer;
    /** Patch C — optional injected libsql client backing the unified
     *  `AuditLog`. When omitted, the daemon opens its own
     *  `file:${OPENSQUID_HOME}/opensquid.db` client during `start()` and
     *  closes it during `stop()`. Tests inject a `createClient({ url:
     *  ':memory:' })` here. Caller-provided clients are NEVER closed by the
     *  daemon — caller-owned lifecycle. */
    auditClient?: Client;
    /** Injected clock — tests pass a fake. */
    now?: () => number;
}
export declare class OpenSquidDaemon {
    private readonly opts;
    private readonly auditLog;
    private readonly nowFn;
    private readonly tasks;
    private readonly entries;
    private readonly fileWatchers;
    private webhookServer;
    private release;
    private state;
    private startedAtMs;
    private signalHandlers;
    /** Patch C — unified `audit_log` table sink. Opened in `start()` after
     *  the proper-lockfile acquire; closed in `stop()` AFTER producers
     *  drain so final shutdown writes still land. `null` outside the
     *  running window — `getAuditLog()` enforces that contract. */
    private auditLogInstance;
    private auditClient;
    /** `true` when the daemon opened its own libsql client; `false` when
     *  the caller injected one. Gates the close-on-stop path. */
    private auditClientOwned;
    constructor(opts: DaemonOpts);
    /** Atomic start. Rolls back on any failure. */
    start(): Promise<void>;
    /** Strict order: cron tasks → webhook server → pid file → lock release.
     * Each step is best-effort; only the lock release matters for the next
     * start. proper-lockfile also clears its own state on process exit via
     * signal-exit, so a missed release here doesn't permanently brick. */
    stop(): Promise<void>;
    /** Patch C — public accessor for the daemon's unified `AuditLog`.
     *  Returns `null` outside the running window (before `start()`
     *  completes, after `stop()`, or while a `start()` rollback ran).
     *  Callers who construct their OWN producers (e.g. `InboundRouter`)
     *  fetch this AFTER `await daemon.start()` and wire `adaptInboundRouter
     *  (daemon.getAuditLog()!)` themselves. */
    getAuditLog(): AuditLog | null;
    /** Internal — closes the libsql client iff the daemon opened it. Safe
     *  to call multiple times. Always clears the field references so a
     *  follow-up `start()` re-opens fresh. */
    private closeAuditClient;
    /** PID-aware status read used by the `daemon status` CLI verb. */
    status(): Promise<DaemonStatus>;
    /** Caller-visible registry — read-only snapshot of entries. */
    scheduleEntries(): readonly ScheduleEntry[];
    /** Webhook port — `null` until `start()` returns. */
    webhookBoundPort(): number | null;
    /** Test seam: synchronously fire one entry's path without advancing the
     * cron tick. Lives on the class (not the test file) because it touches
     * the private `entries` map. Production callers never use this. */
    fireEntryForTest(entryId: string): Promise<void>;
    /** Build the deliver-only handler closure passed to the webhook server.
     *  Returns `undefined` when no router/config is wired — that case is
     *  handled by the server (audits as `misconfigured`, returns 200). */
    private buildDeliverOnlyHandler;
    private fireScheduleEntry;
    private rollbackStart;
    /**
     * Walk `packs[].skills[].triggers[]` for `file_changed` triggers and
     * spin up one `FileWatcher` per (pack, skill). Bare config — pack
     * authors declare `paths:` (and optional `ignored:`) on the trigger;
     * everything else (debounce window, awaitWriteFinish thresholds) is
     * driven by the FileWatcher defaults.
     *
     * Skipped silently when a skill has no file_changed triggers (the
     * common case for Phase 1–7 packs). A skill that declares the trigger
     * but omits `paths:` is treated as a YAML mistake by chokidar (it
     * throws at watch-time); we deliberately let that surface inside the
     * `try { ... } catch { rollback }` in `start()` rather than reject at
     * registry-build time, because the YAML-side schema in
     * `runtime/types.ts` allows `paths` to be optional for forward-compat.
     */
    private startFileWatchers;
    private installSignalHandlers;
    private uninstallSignalHandlers;
}
export interface DaemonStatus {
    running: boolean;
    pid?: number;
    uptimeMs?: number | null;
    scheduleCount?: number | null;
    webhookPort?: number | null;
}
//# sourceMappingURL=daemon.d.ts.map