export interface DispatchInfo { taskId: string; taskHash: string | null; machine: string | null; scheduleTaskId: string | null; sessionName: string | null; tmuxName: string | null; pid: string | null; } export interface ExecutionRecord { id: string; kind: string; status: string; channel: string | null; project: string; source: { trigger: string; }; backend: string; billingMode: string; session: { sessionId: string | null; }; thread: { threadId: string | null; agentSlotId: string | null; } | null; dispatch: DispatchInfo | null; scheduleTaskId: string | null; runtime: { startedAt: string; updatedAt: string; endedAt: string | null; }; metrics: { costUsd: number | null; numTurns: number | null; durationS: number | null; }; text: { label: string | null; finalOutput: string | null; error: string | null; }; } export declare const TERMINAL_STATUSES: Set; interface ExecutionRepoOptions { /** Optional file path override. Defaults to $CORTEX_EXECUTIONS_FILE or DATA_DIR/executions.json */ filePath?: string; } declare class ExecutionRepo { /** In-memory source of truth for all execution records. All sync reads come from here. */ private map; private repo; readonly filePath: string; private mutex; /** Promise chain for fire-and-forget persists, guarded by this.mutex. */ private _pendingPersist; constructor(opts?: ExecutionRepoOptions); /** Load executions from disk into memory. Populates the Map from executions.json. */ load(): void; getExecution(id: string): ExecutionRecord | null; getExecutionByTaskId(taskId: string | null | undefined): ExecutionRecord | null; getRunningExecutions(): ExecutionRecord[]; getAllExecutions(): ExecutionRecord[]; findRunningDispatchMatch({ scheduleTaskId, taskHash, project, taskText }: { scheduleTaskId?: string | null; taskHash?: string | null; project?: string; taskText?: string; }): ExecutionRecord | null; getAll(): ExecutionRecord[]; /** Internal: update a record in-place if not terminal, return updated or original. */ private updateRecordInternal; startLocalExecution({ kind, channel, project, trigger, backend, billingMode, sessionId, label, scheduleTaskId, threadId, agentSlotId }: { kind?: string; channel?: string | null; project?: string; trigger?: string; backend?: string; billingMode?: string; sessionId?: string | null; label?: string | null; scheduleTaskId?: string | null; threadId?: string | null; agentSlotId?: string | null; }): ExecutionRecord; registerDispatchExecution({ taskId, machine, channel, project, scheduleTaskId, taskText, taskHash, sessionName, tmuxName, pid, backend, billingMode }: { taskId: string; machine?: string | null; channel?: string | null; project?: string; scheduleTaskId?: string | null; taskText?: string | null; taskHash?: string | null; sessionName?: string | null; tmuxName?: string | null; pid?: string | null; backend?: string; billingMode?: string; }): ExecutionRecord | null; touchExecution(id: string, patch?: { metrics?: Partial; text?: Partial; dispatch?: Partial; session?: Partial; [key: string]: unknown; }): ExecutionRecord | null; completeExecution(id: string, metrics?: { costUsd?: number | null; numTurns?: number | null; durationS?: number | null; finalOutput?: string | null; error?: string | null; }): ExecutionRecord | null; completeExecutionByTaskId(taskId: string, metrics?: { costUsd?: number | null; numTurns?: number | null; durationS?: number | null; finalOutput?: string | null; error?: string | null; }): ExecutionRecord | null; failExecution(id: string, metrics?: { costUsd?: number | null; numTurns?: number | null; durationS?: number | null; finalOutput?: string | null; error?: string | null; }): ExecutionRecord | null; failExecutionByTaskId(taskId: string, metrics?: { costUsd?: number | null; numTurns?: number | null; durationS?: number | null; finalOutput?: string | null; error?: string | null; }): ExecutionRecord | null; cancelExecution(id: string, metrics?: { costUsd?: number | null; numTurns?: number | null; durationS?: number | null; finalOutput?: string | null; error?: string | null; }): ExecutionRecord | null; cancelExecutionByTaskId(taskId: string, metrics?: { costUsd?: number | null; numTurns?: number | null; durationS?: number | null; finalOutput?: string | null; error?: string | null; }): ExecutionRecord | null; /** Insert or replace a record by ID (used by tests for backdating). */ set(id: string, record: ExecutionRecord): void; /** Mark running executions not matching `keepRunning` as stale. Returns IDs of staled executions. */ markMissingRunningExecutionsStale(keepRunning?: (record: ExecutionRecord) => boolean): Promise; /** Reconcile stale dispatch executions: mark dispatches that are no longer pending and too old as stale. * Returns { count, staled } where count is reconciled count and staled is the list of affected IDs. */ reconcileStaleDispatches({ isTaskPending, isLive, maxAgeMs, graceMs }: { isTaskPending: (taskId: string) => boolean; /** True if the execution is still live in the in-memory registry (in-process dispatch). */ isLive?: (executionId: string) => boolean; /** Hard ceiling: reap even a "live" dispatch once this old (wedged). */ maxAgeMs: number; /** Short grace for a not-pending, not-live dispatch (a crashed in-process orphan). */ graceMs?: number; }): Promise<{ count: number; staled: string[]; }>; /** Await all pending fire-and-forget disk writes AND any in-flight async operations. */ flush(): Promise; /** Return the current _pendingPersist promise (for tests that want to await a specific persist batch). */ queuePersist(): Promise; /** Atomically persist the entire Map to disk. */ private persist; } export declare const executionRepo: ExecutionRepo; export { ExecutionRepo };