Files
AgentCompany/src/main/database/crash-recovery-helpers.ts
T
2026-04-05 20:14:48 -04:00

81 lines
2.3 KiB
TypeScript

/**
* Pure helpers for crash-recovery logic, extracted for testability.
*
* These encode the behavioral contract of the corresponding AppDatabase methods:
* - interruptStaleRuns
* - retryInterruptedTask
* - markInterruptedTaskFailed
*
* The helpers take narrowly-typed "statement runner" interfaces so they can be
* exercised in unit tests without a live SQLite connection.
*/
export interface StaleRunDb {
/** Update all running runs to 'interrupted' and return number of rows changed. */
interruptRunningRuns(ts: string): number;
/** Mark tasks associated with interrupted runs as 'blocked'. */
blockInterruptedTasks(ts: string): void;
/** Clear active_run_id on tasks whose run is now interrupted. */
clearActiveRunIds(): void;
/** Reset all running agents to idle. */
resetAgentsToIdle(ts: string): void;
/** Release all workspace locks. */
releaseAllWorkspaceLocks(): void;
}
export interface RetryTaskDb {
/** Set task status to 'todo'. */
setTaskTodo(ts: string, taskId: string): void;
/** Reset retry_count and last_failure_at. */
resetTaskRetryFields(ts: string, taskId: string): void;
}
export interface FailTaskDb {
/** Set task status to 'cancelled'. */
setTaskCancelled(ts: string, taskId: string): void;
}
/**
* Transactionally marks all running runs as interrupted and associated tasks
* as blocked. Returns the number of interrupted runs.
*/
export function interruptStaleRunsLogic(
db: StaleRunDb,
transaction: (fn: () => void) => void,
nowIso: () => string,
): number {
let interruptedCount = 0;
transaction(() => {
const ts = nowIso();
interruptedCount = db.interruptRunningRuns(ts);
if (interruptedCount > 0) {
db.blockInterruptedTasks(ts);
}
db.clearActiveRunIds();
db.resetAgentsToIdle(ts);
db.releaseAllWorkspaceLocks();
});
return interruptedCount;
}
/** Resets a task to 'todo' and clears its retry state. */
export function retryInterruptedTaskLogic(
db: RetryTaskDb,
taskId: string,
nowIso: () => string,
): void {
const ts = nowIso();
db.setTaskTodo(ts, taskId);
db.resetTaskRetryFields(ts, taskId);
}
/** Marks a task as 'cancelled'. */
export function markInterruptedTaskFailedLogic(
db: FailTaskDb,
taskId: string,
nowIso: () => string,
): void {
const ts = nowIso();
db.setTaskCancelled(ts, taskId);
}