import type { DatabaseSync } from "node:sqlite"; import type { Mode, Result } from "../../core/contracts.js"; export type JobState = | "admitted" | "queued" | "running" | "publishing" | "succeeded" | "failed" | "cancelled"; export type TriggerKind = "label" | "command"; export type Row = Record; export interface DatabaseContext { db: DatabaseSync; transaction(operation: () => T): T; audit(jobId: string | null, event: string, detail: string): void; } export interface Job { id: string; repositoryId: number; issueNumber: number; mode: Mode; triggerKind: TriggerKind; triggerKey: string; triggerLabel?: string; actorId: number; actorLogin: string; instruction: string; state: JobState; cancelRequested: boolean; attempts: number; leaseOwner?: string; leaseExpiresAt?: number; workspace?: string; result?: Result; error?: string; createdAt: number; updatedAt: number; } export interface NewJob { repositoryId: number; issueNumber: number; mode: Mode; triggerKind: TriggerKind; triggerKey: string; triggerLabel?: string; actorId: number; actorLogin: string; instruction?: string; } export interface Delivery { id: string; event: string; eventType: string; bodyHash: string; payload: unknown; attempts: number; } export interface OutboxItem { id: string; jobId: string; kind: "claim" | "publish"; attempts: number; } export interface Conversation { repositoryId: number; issueNumber: number; role: "planner" | "implementer"; scope: string; sessionId: string; updatedAt: number; } export const now = (): number => Date.now(); export function mapJob(row: Row): Job { const resultJson = row.result_json === null ? undefined : String(row.result_json); return { id: String(row.id), repositoryId: Number(row.repository_id), issueNumber: Number(row.issue_number), mode: String(row.mode) as Mode, triggerKind: String(row.trigger_kind) as TriggerKind, triggerKey: String(row.trigger_key), ...(row.trigger_label === null ? {} : { triggerLabel: String(row.trigger_label) }), actorId: Number(row.actor_id), actorLogin: String(row.actor_login), instruction: String(row.instruction), state: String(row.state) as JobState, cancelRequested: Number(row.cancel_requested) === 1, attempts: Number(row.attempts), ...(row.lease_owner === null ? {} : { leaseOwner: String(row.lease_owner) }), ...(row.lease_expires_at === null ? {} : { leaseExpiresAt: Number(row.lease_expires_at) }), ...(row.workspace === null ? {} : { workspace: String(row.workspace) }), ...(resultJson ? { result: JSON.parse(resultJson) as Result } : {}), ...(row.error === null ? {} : { error: String(row.error) }), createdAt: Number(row.created_at), updatedAt: Number(row.updated_at), }; } export function mapConversation(row: Row): Conversation { return { repositoryId: Number(row.repository_id), issueNumber: Number(row.issue_number), role: String(row.role) as Conversation["role"], scope: String(row.scope), sessionId: String(row.session_id), updatedAt: Number(row.updated_at), }; }