Deleting a session with linked session_worktrees or agent_sessions rows threw a FK violation (500 on DELETE /api/sessions/:id). Both FKs now ON DELETE CASCADE. Idempotent migration: drops the old constraint and re-adds with CASCADE only if confdeltype != 'c'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
151 lines
6.1 KiB
PL/PgSQL
151 lines
6.1 KiB
PL/PgSQL
-- v2.0.0: BooCoder schema — pending changes, tasks, agent registry.
|
|
-- Applied on startup by apps/coder/src/db.ts:applySchema().
|
|
-- Lives in the same 'boochat' database as BooChat's tables.
|
|
|
|
CREATE TABLE IF NOT EXISTS pending_changes (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
session_id UUID NOT NULL,
|
|
task_id UUID,
|
|
file_path TEXT NOT NULL,
|
|
operation TEXT NOT NULL,
|
|
diff TEXT NOT NULL,
|
|
status TEXT NOT NULL DEFAULT 'pending',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
|
|
CONSTRAINT pending_changes_operation_chk CHECK (operation IN ('create', 'edit', 'delete')),
|
|
CONSTRAINT pending_changes_status_chk CHECK (status IN ('pending', 'applied', 'rejected', 'reverted'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
project_id UUID NOT NULL,
|
|
parent_task_id UUID REFERENCES tasks(id),
|
|
state TEXT NOT NULL DEFAULT 'pending',
|
|
input TEXT NOT NULL,
|
|
output_summary TEXT,
|
|
agent TEXT,
|
|
model TEXT,
|
|
execution_path TEXT,
|
|
worktree_path TEXT,
|
|
cost_tokens INTEGER,
|
|
started_at TIMESTAMPTZ,
|
|
ended_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
|
|
CONSTRAINT tasks_state_chk CHECK (state IN ('pending', 'running', 'completed', 'failed', 'blocked', 'cancelled')),
|
|
CONSTRAINT tasks_execution_path_chk CHECK (execution_path IS NULL OR execution_path IN ('native', 'acp', 'pty', 'qwen'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS available_agents (
|
|
name TEXT PRIMARY KEY,
|
|
install_path TEXT,
|
|
version TEXT,
|
|
supports_acp BOOLEAN NOT NULL DEFAULT false,
|
|
supports_mcp_client BOOLEAN NOT NULL DEFAULT false,
|
|
last_probed_at TIMESTAMPTZ
|
|
);
|
|
|
|
-- v2.0.0 Phase 4: link tasks to their inference sessions.
|
|
ALTER TABLE tasks ADD COLUMN IF NOT EXISTS session_id UUID REFERENCES sessions(id);
|
|
|
|
-- v2.0.5: add 'qwen' to execution_path CHECK + arena_id column.
|
|
ALTER TABLE tasks DROP CONSTRAINT IF EXISTS tasks_execution_path_chk;
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'tasks_execution_path_chk') THEN
|
|
ALTER TABLE tasks ADD CONSTRAINT tasks_execution_path_chk
|
|
CHECK (execution_path IS NULL OR execution_path IN ('native', 'acp', 'pty', 'qwen'));
|
|
END IF;
|
|
END $$;
|
|
|
|
-- v2.0.5: arena support — group tasks into competitive arenas.
|
|
ALTER TABLE tasks ADD COLUMN IF NOT EXISTS arena_id UUID;
|
|
|
|
-- Human inbox: tasks needing attention
|
|
CREATE OR REPLACE VIEW human_inbox AS
|
|
SELECT * FROM tasks WHERE state IN ('blocked', 'failed');
|
|
|
|
-- v2.1.0: provider picker — extend available_agents with model discovery.
|
|
ALTER TABLE available_agents ADD COLUMN IF NOT EXISTS models JSONB DEFAULT '[]'::jsonb;
|
|
ALTER TABLE available_agents ADD COLUMN IF NOT EXISTS label TEXT;
|
|
ALTER TABLE available_agents ADD COLUMN IF NOT EXISTS transport TEXT DEFAULT 'pty';
|
|
-- v2.5.10: persisted ACP available_commands (captured during the cold probe), so
|
|
-- an agent's live command set survives the tier-2 probe skip and shows without a
|
|
-- dispatch.
|
|
ALTER TABLE available_agents ADD COLUMN IF NOT EXISTS commands JSONB DEFAULT '[]'::jsonb;
|
|
|
|
-- v2.2.0: Paseo-style session config on tasks.
|
|
ALTER TABLE tasks ADD COLUMN IF NOT EXISTS mode_id TEXT;
|
|
ALTER TABLE tasks ADD COLUMN IF NOT EXISTS thinking_option_id TEXT;
|
|
ALTER TABLE tasks ADD COLUMN IF NOT EXISTS feature_values JSONB;
|
|
|
|
-- v2.6: one shared worktree per session (all agents/panes in the session operate in it).
|
|
CREATE TABLE IF NOT EXISTS session_worktrees (
|
|
session_id UUID PRIMARY KEY REFERENCES sessions(id) ON DELETE CASCADE,
|
|
worktree_path TEXT NOT NULL,
|
|
base_commit TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp()
|
|
);
|
|
-- Migrate existing FK to CASCADE (idempotent: drops the old constraint if present).
|
|
DO $$ BEGIN
|
|
IF EXISTS (
|
|
SELECT 1 FROM pg_constraint
|
|
WHERE conname = 'session_worktrees_session_id_fkey'
|
|
AND confdeltype <> 'c'
|
|
) THEN
|
|
ALTER TABLE session_worktrees DROP CONSTRAINT session_worktrees_session_id_fkey;
|
|
ALTER TABLE session_worktrees ADD CONSTRAINT session_worktrees_session_id_fkey
|
|
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
-- v2.6: one backend session per (session, agent); resumed on switch-back.
|
|
CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
agent TEXT NOT NULL,
|
|
backend TEXT NOT NULL,
|
|
agent_session_id TEXT,
|
|
server_port INTEGER,
|
|
status TEXT NOT NULL DEFAULT 'idle',
|
|
last_active_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
|
|
PRIMARY KEY (session_id, agent),
|
|
CONSTRAINT agent_sessions_backend_chk CHECK (backend IN ('opencode_server', 'acp_warm')),
|
|
CONSTRAINT agent_sessions_status_chk CHECK (status IN ('idle', 'active', 'crashed', 'closed'))
|
|
);
|
|
|
|
-- Migrate existing agent_sessions FK to CASCADE.
|
|
DO $$ BEGIN
|
|
IF EXISTS (
|
|
SELECT 1 FROM pg_constraint
|
|
WHERE conname = 'agent_sessions_session_id_fkey'
|
|
AND confdeltype <> 'c'
|
|
) THEN
|
|
ALTER TABLE agent_sessions DROP CONSTRAINT agent_sessions_session_id_fkey;
|
|
ALTER TABLE agent_sessions ADD CONSTRAINT agent_sessions_session_id_fkey
|
|
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
-- v2.6: config fingerprint for stale-session detection (auto-recover on model change).
|
|
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS config_hash TEXT;
|
|
|
|
-- v2.6: attribution for DiffPanel badges (Phase 1 UX reads this).
|
|
ALTER TABLE pending_changes ADD COLUMN IF NOT EXISTS agent TEXT;
|
|
|
|
-- LISTEN/NOTIFY fast path: every tasks INSERT (from any call site — routes,
|
|
-- new_task tool, arena, MCP server) fires pg_notify('tasks_new') in the same
|
|
-- transaction, so the dispatcher reacts immediately instead of waiting for the
|
|
-- fallback poll. Postgres holds the notification until COMMIT, so the listener
|
|
-- always sees the committed row. A trigger covers all insert paths with no
|
|
-- app-code drift. Idempotent: re-applied on every startup.
|
|
CREATE OR REPLACE FUNCTION notify_tasks_new() RETURNS trigger AS $$
|
|
BEGIN
|
|
PERFORM pg_notify('tasks_new', '');
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS tasks_notify_new ON tasks;
|
|
CREATE TRIGGER tasks_notify_new
|
|
AFTER INSERT ON tasks
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION notify_tasks_new();
|