diff --git a/migrations/0006_acp.up.sql b/migrations/0006_acp.up.sql new file mode 100644 index 0000000..4e86835 --- /dev/null +++ b/migrations/0006_acp.up.sql @@ -0,0 +1,75 @@ +-- ============ AgentKind ============ +CREATE TABLE acp_agent_kinds ( + id UUID PRIMARY KEY, + name TEXT NOT NULL UNIQUE, + display_name TEXT NOT NULL, + description TEXT NOT NULL DEFAULT '', + binary_path TEXT NOT NULL, + args TEXT[] NOT NULL DEFAULT '{}', + encrypted_env BYTEA, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + created_by UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX idx_acp_agent_kinds_enabled ON acp_agent_kinds(enabled) WHERE enabled = TRUE; + +-- ============ Prerequisites for composite FK ============ +ALTER TABLE issues ADD CONSTRAINT issues_project_id_unique UNIQUE (project_id, id); +ALTER TABLE requirements ADD CONSTRAINT requirements_project_id_unique UNIQUE (project_id, id); + +-- ============ Sessions ============ +CREATE TABLE acp_sessions ( + id UUID PRIMARY KEY, + workspace_id UUID NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, + agent_kind_id UUID NOT NULL REFERENCES acp_agent_kinds(id) ON DELETE RESTRICT, + user_id UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT, + issue_id UUID, + requirement_id UUID, + agent_session_id TEXT, + branch TEXT NOT NULL, + cwd_path TEXT NOT NULL, + is_main_worktree BOOLEAN NOT NULL DEFAULT FALSE, + status TEXT NOT NULL, + pid INT, + exit_code INT, + last_error TEXT, + started_at TIMESTAMPTZ NOT NULL DEFAULT now(), + ended_at TIMESTAMPTZ, + CONSTRAINT acp_sessions_status_check CHECK (status IN ('starting','running','crashed','exited')), + CONSTRAINT acp_sessions_issue_project_fk + FOREIGN KEY (project_id, issue_id) + REFERENCES issues (project_id, id) MATCH SIMPLE ON DELETE SET NULL, + CONSTRAINT acp_sessions_req_project_fk + FOREIGN KEY (project_id, requirement_id) + REFERENCES requirements (project_id, id) MATCH SIMPLE ON DELETE SET NULL +); +CREATE INDEX idx_acp_sessions_workspace_status ON acp_sessions(workspace_id, status); +CREATE INDEX idx_acp_sessions_user_status ON acp_sessions(user_id, status); +CREATE INDEX idx_acp_sessions_issue ON acp_sessions(issue_id) WHERE issue_id IS NOT NULL; +CREATE INDEX idx_acp_sessions_requirement ON acp_sessions(requirement_id) WHERE requirement_id IS NOT NULL; + +-- ============ Workspaces main worktree mutex ============ +ALTER TABLE workspaces ADD COLUMN active_main_session_id UUID; +ALTER TABLE workspaces + ADD CONSTRAINT workspaces_active_main_session_fk + FOREIGN KEY (active_main_session_id) + REFERENCES acp_sessions(id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED; + +-- ============ Events ============ +CREATE TABLE acp_events ( + id BIGSERIAL PRIMARY KEY, + session_id UUID NOT NULL REFERENCES acp_sessions(id) ON DELETE CASCADE, + direction TEXT NOT NULL, + rpc_kind TEXT NOT NULL, + method TEXT, + payload JSONB NOT NULL, + payload_size INT NOT NULL, + truncated BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + CONSTRAINT acp_events_direction_check CHECK (direction IN ('in','out')), + CONSTRAINT acp_events_kind_check CHECK (rpc_kind IN ('request','response','notification','error')) +); +CREATE INDEX idx_acp_events_session_id ON acp_events(session_id, id); +CREATE INDEX idx_acp_events_created_at ON acp_events(created_at);