import type { CronConfig } from "../../config/types.cron.js"; import type { HeartbeatRunResult } from "../../infra/heartbeat-wake.js"; import type { CronDeliveryStatus, CronJob, CronJobCreate, CronJobPatch, CronMessageChannel, CronRunOutcome, CronRunStatus, CronRunTelemetry, CronStoreFile } from "../types.js"; export type CronEvent = { jobId: string; action: "added" | "updated" | "removed" | "started" | "finished"; runAtMs?: number; durationMs?: number; status?: CronRunStatus; error?: string; summary?: string; delivered?: boolean; deliveryStatus?: CronDeliveryStatus; deliveryError?: string; sessionId?: string; sessionKey?: string; nextRunAtMs?: number; } & CronRunTelemetry; export type Logger = { debug: (obj: unknown, msg?: string) => void; info: (obj: unknown, msg?: string) => void; warn: (obj: unknown, msg?: string) => void; error: (obj: unknown, msg?: string) => void; }; export type CronServiceDeps = { nowMs?: () => number; log: Logger; storePath: string; cronEnabled: boolean; /** CronConfig for session retention settings. */ cronConfig?: CronConfig; /** Default agent id for jobs without an agent id. */ defaultAgentId?: string; /** Resolve session store path for a given agent id. */ resolveSessionStorePath?: (agentId?: string) => string; /** Path to the session store (sessions.json) for reaper use. */ sessionStorePath?: string; enqueueSystemEvent: (text: string, opts?: { agentId?: string; sessionKey?: string; contextKey?: string; }) => void; requestHeartbeatNow: (opts?: { reason?: string; agentId?: string; sessionKey?: string; }) => void; runHeartbeatOnce?: (opts?: { reason?: string; agentId?: string; sessionKey?: string; /** Optional heartbeat config override (e.g. target: "last" for cron-triggered heartbeats). */ heartbeat?: { target?: string; }; }) => Promise; /** * WakeMode=now: max time to wait for runHeartbeatOnce to stop returning * { status:"skipped", reason:"requests-in-flight" } before falling back to * requestHeartbeatNow. */ wakeNowHeartbeatBusyMaxWaitMs?: number; /** WakeMode=now: delay between runHeartbeatOnce retries while busy. */ wakeNowHeartbeatBusyRetryDelayMs?: number; runIsolatedAgentJob: (params: { job: CronJob; message: string; abortSignal?: AbortSignal; }) => Promise<{ summary?: string; /** Last non-empty agent text output (not truncated). */ outputText?: string; /** * `true` when the isolated run already delivered its output to the target * channel (including matching messaging-tool sends). See: * https://github.com/bot/bot/issues/15692 */ delivered?: boolean; /** * `true` when announce/direct delivery was attempted for this run, even * if the final per-message ack status is uncertain. */ deliveryAttempted?: boolean; } & CronRunOutcome & CronRunTelemetry>; sendCronFailureAlert?: (params: { job: CronJob; text: string; channel: CronMessageChannel; to?: string; mode?: "announce" | "webhook"; accountId?: string; }) => Promise; onEvent?: (evt: CronEvent) => void; }; export type CronServiceDepsInternal = Omit & { nowMs: () => number; }; export type CronServiceState = { deps: CronServiceDepsInternal; store: CronStoreFile | null; timer: NodeJS.Timeout | null; running: boolean; op: Promise; warnedDisabled: boolean; storeLoadedAtMs: number | null; storeFileMtimeMs: number | null; }; export declare function createCronServiceState(deps: CronServiceDeps): CronServiceState; export type CronRunMode = "due" | "force"; export type CronWakeMode = "now" | "next-heartbeat"; export type CronStatusSummary = { enabled: boolean; storePath: string; jobs: number; nextWakeAtMs: number | null; }; export type CronRunResult = { ok: true; ran: true; } | { ok: true; ran: false; reason: "not-due"; } | { ok: true; ran: false; reason: "already-running"; } | { ok: false; }; export type CronRemoveResult = { ok: true; removed: boolean; } | { ok: false; removed: false; }; export type CronAddResult = CronJob; export type CronUpdateResult = CronJob; export type CronListResult = CronJob[]; export type CronAddInput = CronJobCreate; export type CronUpdateInput = CronJobPatch;