import type { DatabaseManager } from './db.js'; /** * Durable async execution for long-running tool calls * (io.modelcontextprotocol/tasks extension surface). * * A task is a durable handle created before the tool response is sent: the * client polls `tasks/get` (resumable across disconnects), cancels * cooperatively via `tasks/cancel`, and reads the final result or error from * the terminal state. Terminal tasks keep a retention window so late polls * still answer, then are swept by TTL pruning. */ export type TaskStatus = 'working' | 'input_required' | 'completed' | 'failed' | 'cancelled'; export interface Task { taskId: string; tool: string; workspaceId: string | null; args: unknown; status: TaskStatus; statusMessage: string | null; progress: number | null; total: number | null; result: unknown; error: string | null; ttlMs: number; createdAt: string; lastUpdatedAt: string; expiresAt: string; } /** What a task runner can observe about its own task. */ export interface TaskRunContext { taskId: string; /** Human-readable progress line ("3/5 sources harvested") shown to pollers. */ setStatus(message: string): void; setProgress(done: number, total: number): void; /** Cooperative cancellation probe — check between long phases. */ isCancelled(): boolean; } export declare class TaskManager { private readonly db; /** Cooperative-cancelled taskIds — the in-memory flag the runners poll * (`isCancelled`), independent of the persisted status so a runner that * already observed the cancel never re-completes the task. */ private readonly cancelledIds; /** Set once the owning DatabaseManager is closed — post-teardown runners * then no-op instead of throwing against a closed SQLite connection. */ private closed; constructor(db: DatabaseManager); /** * Mark the manager closed (called by the owner on server shutdown): the * DB connection is being torn down, so any late runner finalization is a * no-op rather than an unhandled "database connection is not open" throw. */ close(): void; /** Run a DB op, or short-circuit when the manager/DB is closed. Late * detached-runner writes racing teardown land here — a closed database * means the task's final state is irrelevant, so we no-op silently. */ private guarded; /** * Create a durable task and start its runner detached (never awaited by * the caller — the tool response returns immediately). The row is written * before the runner starts, so a poll arriving before the first update * still sees a `working` task. */ start(input: { tool: string; workspaceId: string | null; args?: unknown; ttlMs?: number; }, run: (ctx: TaskRunContext) => Promise): Task; /** Cooperative cancel: mark the task cancelled; the runner observes it at * its next phase boundary. Cancellation is a request, not a kill — a * runner past its last check still completes, but the task ends * `cancelled` instead of `completed`. */ cancel(taskId: string): Task | null; get(taskId: string): Task | null; /** Page of tasks, newest first. Cursor is an opaque base64 offset. */ list(cursor?: string, limit?: number): { tasks: Task[]; nextCursor?: string; }; /** Sweep expired rows (working tasks past their TTL, terminal tasks past * their retention). Runs opportunistically on list/get and after finish. */ prune(): void; private read; /** Transition a task to a terminal status, extending its retention window. * A task already cancelled stays cancelled — the cancel wins over a late * runner completion. */ private finish; private update; } /** The wire `ttl` field: remaining lifetime in ms, null when expired/unknown. */ export declare function wireTtl(task: Task): number | null; //# sourceMappingURL=tasks.d.ts.map