-- v5: durable inference task queue. Every inference call (sync infer,
-- agent chat, or async POST /inference-tasks) gets a row here. Workers
-- (mcplocal sessions) drain pending rows whose `poolName` matches the
-- pool keys they own when they bind their SSE channel.
CREATE TYPE "InferenceTaskStatus" AS ENUM ('pending', 'claimed', 'running', 'completed', 'error', 'cancelled');

CREATE TABLE "InferenceTask" (
    "id"              TEXT NOT NULL,
    "status"          "InferenceTaskStatus" NOT NULL DEFAULT 'pending',
    "poolName"        TEXT NOT NULL,
    "llmName"         TEXT NOT NULL,
    "model"           TEXT NOT NULL,
    "tier"            TEXT,
    "claimedBy"       TEXT,
    "requestBody"     JSONB NOT NULL,
    "responseBody"    JSONB,
    "errorMessage"    TEXT,
    "streaming"       BOOLEAN NOT NULL DEFAULT false,
    "createdAt"       TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "claimedAt"       TIMESTAMP(3),
    "streamStartedAt" TIMESTAMP(3),
    "completedAt"     TIMESTAMP(3),
    "ownerId"         TEXT NOT NULL,
    "agentId"         TEXT,

    CONSTRAINT "InferenceTask_pkey" PRIMARY KEY ("id")
);

-- Worker claim path: SELECT … WHERE status='pending' AND poolName IN (…)
-- runs on every SSE bind. Compound index keeps that fast as the table grows.
CREATE INDEX "InferenceTask_status_poolName_idx" ON "InferenceTask"("status", "poolName");
-- unbindSession revert path: SELECT … WHERE claimedBy=$1 AND status IN ('claimed','running').
CREATE INDEX "InferenceTask_claimedBy_idx" ON "InferenceTask"("claimedBy");
-- Owner scoping for the async API listing.
CREATE INDEX "InferenceTask_ownerId_idx" ON "InferenceTask"("ownerId");
CREATE INDEX "InferenceTask_agentId_idx" ON "InferenceTask"("agentId");
-- GC sweep predicate: completedAt < now()-7d. Indexed so the daily cleanup
-- doesn't seq-scan once the table grows past a few thousand rows.
CREATE INDEX "InferenceTask_completedAt_idx" ON "InferenceTask"("completedAt");
