import type { Kysely } from "kysely"; import { dropTables, entityTable, execDdl, oneOf } from "../src/migration-helpers.ts"; // 人工/AI 域:decision(待处理中心)、approval、automation_grant、agent_run、artifact(REQ §10、§11.3、§11.4) // 提案不直接晋升事实;审批绑定 plan ID + payload hash + expected revision;授权先落库再唤醒任务。 // biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型 export async function up(db: Kysely): Promise { await execDdl( db, ` ${entityTable( "agent_run", ` task_id uuid references task (id) on delete set null, parent_run_id uuid references agent_run (id) on delete set null, trigger text not null ${oneOf("trigger", ["user", "event", "schedule", "subagent"])}, goal text not null, status text not null default 'queued' ${oneOf("status", ["queued", "running", "waiting", "succeeded", "failed", "cancelled", "budget_exceeded"])}, provider text, model text, thinking_level text, scope jsonb not null default '{}'::jsonb, budget jsonb not null default '{}'::jsonb, usage jsonb not null default '{}'::jsonb, evidence_version text, session_state jsonb, summary text, tool_call_count integer not null default 0, last_event_seq bigint not null default 0, error jsonb, cancel_requested_at timestamptz, started_at timestamptz, finished_at timestamptz, created_by text`, )} create index agent_run_status_idx on agent_run (status); create index agent_run_task_idx on agent_run (task_id) where task_id is not null; create index agent_run_created_idx on agent_run (created_at desc); ${entityTable( "artifact", ` kind text not null ${oneOf("kind", ["evidence", "plan", "proposal", "report", "snapshot", "log"])}, agent_run_id uuid references agent_run (id) on delete set null, task_id uuid references task (id) on delete set null, subject_type text, subject_id uuid, title text not null, mime_type text not null default 'application/json', content jsonb not null default '{}'::jsonb, content_hash text not null, size_bytes bigint, expires_at timestamptz`, )} create index artifact_run_idx on artifact (agent_run_id) where agent_run_id is not null; create index artifact_subject_idx on artifact (subject_type, subject_id) where subject_id is not null; ${entityTable( "decision", ` kind text not null ${oneOf("kind", ["identity_ambiguous", "download_stalled", "download_failed", "path_conflict", "space_insufficient", "approval_required", "metadata_conflict", "source_unreachable", "other"])}, status text not null default 'open' ${oneOf("status", ["open", "investigating", "awaiting_user", "resolved", "dismissed", "expired"])}, severity text not null default 'warning' ${oneOf("severity", ["info", "warning", "critical"])}, title text not null, summary text, subject_type text, subject_id uuid, dedupe_key text not null, evidence jsonb not null default '{}'::jsonb, options jsonb not null default '[]'::jsonb, resolution jsonb, task_id uuid references task (id) on delete set null, agent_run_id uuid references agent_run (id) on delete set null, attempts integer not null default 0, next_review_at timestamptz, resolved_by text, resolved_at timestamptz`, )} create unique index decision_dedupe_open_key on decision (dedupe_key) where status in ('open', 'investigating', 'awaiting_user'); create index decision_status_idx on decision (status, created_at desc); create index decision_next_review_idx on decision (next_review_at) where next_review_at is not null; ${entityTable( "approval", ` plan_type text not null, plan_id uuid not null, payload_hash text not null, expected_revision integer not null, scope jsonb not null default '{}'::jsonb, status text not null default 'pending' ${oneOf("status", ["pending", "approved", "rejected", "revoked", "expired", "consumed"])}, decision_id uuid references decision (id) on delete set null, requested_by text not null, decided_by text, decided_at timestamptz, expires_at timestamptz, reason text`, )} create index approval_plan_idx on approval (plan_type, plan_id); create index approval_pending_idx on approval (created_at) where status = 'pending'; ${entityTable( "automation_grant", ` action text not null, policy_revision_id uuid references policy_revision (id) on delete set null, scope jsonb not null default '{}'::jsonb, budget jsonb not null default '{}'::jsonb, status text not null default 'active' ${oneOf("status", ["active", "revoked", "expired"])}, granted_by text not null, reason text, expires_at timestamptz, revoked_at timestamptz, revoked_by text`, )} create index automation_grant_active_idx on automation_grant (action) where status = 'active'; `, ); } // biome-ignore lint/suspicious/noExplicitAny: 同上 export async function down(db: Kysely): Promise { await execDdl(db, dropTables(["automation_grant", "approval", "decision", "artifact", "agent_run"])); }