/** Per-thread tick recurrence, in minutes. The default lines up with
 *  nanobot's 30-min cadence; operators can override per binding with
 *  `/heartbeat interval N` or the IMHUB_HEARTBEAT_DEFAULT_INTERVAL env. */
export declare const DEFAULT_INTERVAL_MIN = 30;
/** Hard lower bound on the recurrence. 1-minute heartbeats are almost
 *  always a misconfiguration (the agent burns through API calls and
 *  there's no time for the previous tick to even finish). */
export declare const MIN_INTERVAL_MIN = 5;
/** Hard upper bound — 24h. Above this just use cron + reminders. */
export declare const MAX_INTERVAL_MIN: number;
/** Max characters in a heartbeat body. Keeps prompt size bounded; the
 *  body is the entire context the cheap decide-step LLM sees, so
 *  unbounded text would silently blow the context window. */
export declare const MAX_BODY_CHARS = 8000;
/** Tick statuses recorded per attempt. */
export type TickStatus = 'pending' | 'skipped' | 'delivered' | 'suppressed' | 'no-agent' | 'failed';
export interface HeartbeatRow {
    id: number;
    /** IM coordinates — the binding scope. */
    platform: string;
    channelId: string;
    threadId: string;
    /** Owner of this binding (the IM user who ran /heartbeat bind). Used
     *  for audit and for picking the same agent the user is chatting
     *  with at tick time. */
    userId: string;
    /** Markdown body — what the decide-step LLM and the execute-step
     *  agent reason against. Plain UTF-8; users can include lists, code
     *  fences, whatever they want. Max MAX_BODY_CHARS. */
    body: string;
    /** Tick recurrence (minutes). MIN_INTERVAL_MIN ≤ x ≤ MAX_INTERVAL_MIN. */
    intervalMinutes: number;
    /** Soft kill-switch. Disabled rows survive but never tick. */
    enabled: boolean;
    /** ISO timestamp of the last attempted tick (null = never ticked). */
    lastTickAt: string | null;
    /** Status of that last tick. */
    lastStatus: TickStatus;
    /** Operator-facing one-liner from the last tick. */
    lastReason: string;
    /** First MAX_PREVIEW_CHARS of the last successfully delivered text. */
    lastTextPreview: string;
    createdAt: string;
    updatedAt: string;
}
/** Read the operator-provided default interval (env override or constant). */
export declare function defaultIntervalMinutes(): number;
/**
 * Create or replace the heartbeat for a thread. UPSERT semantics — one
 * binding per thread, calling bind() again overwrites the body and
 * resets last_tick_at so the new body takes effect on the next tick
 * (not 30 min later).
 */
export declare function upsertBinding(input: {
    platform: string;
    channelId: string;
    threadId: string;
    userId: string;
    body: string;
    intervalMinutes?: number;
}): HeartbeatRow | null;
/** Disable (don't delete) a binding for the given thread. */
export declare function disableBinding(platform: string, channelId: string, threadId: string): boolean;
/** Soft-enable an existing binding (no-op if missing). */
export declare function enableBinding(platform: string, channelId: string, threadId: string): boolean;
/** Replace the interval for an existing binding (no-op if missing). */
export declare function updateInterval(platform: string, channelId: string, threadId: string, intervalMinutes: number): boolean;
/** Stamp the outcome of a tick. Caller is the scheduler / runForBinding. */
export declare function recordTick(input: {
    id: number;
    status: TickStatus;
    reason: string;
    /** When delivered, the (truncated) body text. Ignored for non-delivered. */
    deliveredText?: string;
}): void;
export declare function getBinding(platform: string, channelId: string, threadId: string): HeartbeatRow | null;
/** All bindings, regardless of enabled state. Admin-facing /heartbeat list. */
export declare function listAllBindings(): HeartbeatRow[];
/**
 * All enabled bindings whose next tick is due. "Due" = `last_tick_at IS
 * NULL` OR `datetime(last_tick_at) <= datetime('now', '-N minutes')`.
 *
 * Each row carries its own interval, so we can't precompute a single
 * cutoff — the SQL filter checks per-row using interval_minutes.
 */
export declare function listDueBindings(): HeartbeatRow[];
/** Strictly for tests: drop the table contents. NEVER call from runtime
 *  paths — the alternative is `disableBinding` per row. */
export declare function _truncateForTests(): void;
//# sourceMappingURL=heartbeat-store.d.ts.map