-- ============ 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);