import { type TaskRecord } from '../../task-status.js'; import type { SqliteConnection } from '../sqlite-connection.js'; import type { TaskStore, TaskStoreFilter, TaskStoreUpdatePatch } from './task-store.js'; import type { FailedTaskSummary, FailedTaskFilter, FailedTaskDetail } from './task-types.js'; export interface ArtifactContentPrecondition { readonly artifactId: string; readonly sourceTaskId: string; readonly lineageArtifactIdsJson: string; readonly contentJson: string; } export interface TaskArtifactCasInput { readonly taskId: string; readonly expectedDiagnosticJson: string | null; readonly artifacts: readonly ArtifactContentPrecondition[]; readonly patch: TaskStoreUpdatePatch; } export declare class SqliteTaskStore implements TaskStore { private readonly connection; constructor(connection: SqliteConnection); createTask(record: Omit): Promise; getTask(taskId: string): Promise; updateTask(taskId: string, patch: TaskStoreUpdatePatch): Promise; updateTaskIfDiagnosticJsonUnchanged(taskId: string, expectedDiagnosticJson: string | null, patch: TaskStoreUpdatePatch): Promise; /** * Owner-decision CAS: task metadata and every evidence row used to build the * review must still be byte-equal in the same SQL statement that records the * decision. This closes the final evidence check-to-write window. */ updateTaskIfDiagnosticJsonAndArtifactsUnchanged(input: TaskArtifactCasInput): Promise; private updateTaskWithPreconditions; listTasks(filter?: TaskStoreFilter): Promise; deleteTask(taskId: string): Promise; /** * Shared helper for listFailedTasks / countFailedTasks: build the * kind/since WHERE conditions and parameter values, scoped to the given * table alias. Keeping this in one place ensures the list query and the * count query stay consistent when since semantics or task_kind filtering * change — divergence would cause list/total mismatch in the Failed * Tasks UI pagination. */ private static buildFailedTaskConditions; /** * List tasks in 'failed' or 'needs_human_review' status, sorted by last * attempt time descending (most recent failures first). * * `lastAttemptAt` is derived from `MAX(runs.started_at)` via a correlated * subquery; tasks with no runs have NULL lastAttemptAt and sort last * (NULL sorts last in SQLite DESC by default). * * `painId` is extracted from `diagnostic_json.sourcePainId` when present. * * @param filter Optional kind/since/limit/offset filter. */ listFailedTasks(filter?: FailedTaskFilter): Promise; /** * Fetch full detail for a single failed task: the task record, its run * history (most recent first), the last error category, and a pending * agent draft (null in Task 8; Task 13 wires the real store). * * Returns null if the task does not exist or is not in a failed / * needs_human_review status. */ getFailedTaskDetail(taskId: string): Promise; /** * Count tasks in 'failed' or 'needs_human_review' status, with the same * kind/since filtering as listFailedTasks. Used for pagination totals. */ countFailedTasks(filter?: Pick): Promise; /** * Convert a raw DB row into a validated FailedTaskSummary. * * Trust boundary (rc-1, rc-2, rc-3): the row is UNTRUSTED data. Each field * is read with a runtime guard; required fields fail loud, nullable fields * only accept their declared shape. */ private static rowToFailedTaskSummary; private rowToRecord; } //# sourceMappingURL=sqlite-task-store.d.ts.map