diff --git a/backend/internal/models/models.go b/backend/internal/models/models.go new file mode 100644 index 0000000..cd2186a --- /dev/null +++ b/backend/internal/models/models.go @@ -0,0 +1,106 @@ +// Package models defines the database entities for the Control Center Go backend. +// Structs map 1:1 to the PostgreSQL schema defined in backend/migrations/. +package models + +import ( + "time" + + "github.com/jackc/pgx/v5/pgtype" +) + +// AgentStatus represents the possible lifecycle states of an agent. +type AgentStatus string + +const ( + AgentStatusActive AgentStatus = "active" + AgentStatusIdle AgentStatus = "idle" + AgentStatusThinking AgentStatus = "thinking" + AgentStatusError AgentStatus = "error" + AgentStatusOffline AgentStatus = "offline" +) + +// Agent represents a registered agent and its current state. +type Agent struct { + ID pgtype.UUID `db:"id" json:"id"` + Name string `db:"name" json:"name"` + Status AgentStatus `db:"status" json:"status"` + Task *string `db:"task" json:"task,omitempty"` + Progress int32 `db:"progress" json:"progress"` + SessionKey *string `db:"session_key" json:"session_key,omitempty"` + Channel *string `db:"channel" json:"channel,omitempty"` + LastActivity time.Time `db:"last_activity" json:"last_activity"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +// SessionStatus represents the possible states of an agent session. +type SessionStatus string + +const ( + SessionStatusRunning SessionStatus = "running" + SessionStatusCompleted SessionStatus = "completed" + SessionStatusCrashed SessionStatus = "crashed" + SessionStatusTerminated SessionStatus = "terminated" +) + +// Session tracks an agent session over time. +type Session struct { + ID pgtype.UUID `db:"id" json:"id"` + AgentID pgtype.UUID `db:"agent_id" json:"agent_id"` + StartedAt time.Time `db:"started_at" json:"started_at"` + EndedAt *time.Time `db:"ended_at" json:"ended_at,omitempty"` + Status SessionStatus `db:"status" json:"status"` +} + +// TaskLogStatus represents the possible states of a task log entry. +type TaskLogStatus string + +const ( + TaskLogStatusPending TaskLogStatus = "pending" + TaskLogStatusRunning TaskLogStatus = "running" + TaskLogStatusCompleted TaskLogStatus = "completed" + TaskLogStatusFailed TaskLogStatus = "failed" + TaskLogStatusCancelled TaskLogStatus = "cancelled" +) + +// TaskLog records a historical task assigned to an agent. +type TaskLog struct { + ID pgtype.UUID `db:"id" json:"id"` + AgentID pgtype.UUID `db:"agent_id" json:"agent_id"` + Task string `db:"task" json:"task"` + Status TaskLogStatus `db:"status" json:"status"` + StartedAt time.Time `db:"started_at" json:"started_at"` + CompletedAt *time.Time `db:"completed_at" json:"completed_at,omitempty"` + ErrorMessage *string `db:"error_message" json:"error_message,omitempty"` +} + +// ProjectStatus represents the possible states of a project. +type ProjectStatus string + +const ( + ProjectStatusPlanned ProjectStatus = "planned" + ProjectStatusInProgress ProjectStatus = "in_progress" + ProjectStatusCompleted ProjectStatus = "completed" + ProjectStatusPaused ProjectStatus = "paused" + ProjectStatusCancelled ProjectStatus = "cancelled" +) + +// Project represents a project managed by the Control Center. +type Project struct { + ID pgtype.UUID `db:"id" json:"id"` + Name string `db:"name" json:"name"` + Description *string `db:"description" json:"description,omitempty"` + Status ProjectStatus `db:"status" json:"status"` + AgentID *pgtype.UUID `db:"agent_id" json:"agent_id,omitempty"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +// AgentEvent represents an event in the agent lifecycle or telemetry stream. +type AgentEvent struct { + ID pgtype.UUID `db:"id" json:"id"` + AgentID pgtype.UUID `db:"agent_id" json:"agent_id"` + EventType string `db:"event_type" json:"event_type"` + Payload *map[string]interface{} `db:"payload" json:"payload,omitempty"` + CreatedAt time.Time `db:"created_at" json:"created_at"` +} diff --git a/backend/migrations/001_initial_schema.down.sql b/backend/migrations/001_initial_schema.down.sql new file mode 100644 index 0000000..829aa4a --- /dev/null +++ b/backend/migrations/001_initial_schema.down.sql @@ -0,0 +1,9 @@ +-- Migration: 001_initial_schema (down) +-- Description: Reverts the core Control Center database schema. + +-- Drop in reverse dependency order to avoid FK conflicts +DROP TABLE IF EXISTS agent_events; +DROP TABLE IF EXISTS task_logs; +DROP TABLE IF EXISTS sessions; +DROP TABLE IF EXISTS projects; +DROP TABLE IF EXISTS agents; diff --git a/backend/migrations/001_initial_schema.up.sql b/backend/migrations/001_initial_schema.up.sql new file mode 100644 index 0000000..26425d7 --- /dev/null +++ b/backend/migrations/001_initial_schema.up.sql @@ -0,0 +1,97 @@ +-- Migration: 001_initial_schema +-- Description: Creates the core Control Center database schema. + +-- Enable UUID extension +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +-- ============================================ +-- Table: agents +-- ============================================ +CREATE TABLE agents ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + name TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'idle' + CHECK (status IN ('active', 'idle', 'thinking', 'error', 'offline')), + task TEXT, + progress INT NOT NULL DEFAULT 0 + CHECK (progress >= 0 AND progress <= 100), + session_key TEXT, + channel TEXT, + last_activity TIMESTAMPTZ NOT NULL DEFAULT NOW(), + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +COMMENT ON TABLE agents IS 'Registered agents and their current state'; +COMMENT ON COLUMN agents.status IS 'Agent lifecycle status: active, idle, thinking, error, offline'; + +-- ============================================ +-- Table: sessions +-- ============================================ +CREATE TABLE sessions ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + agent_id UUID NOT NULL, + started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + ended_at TIMESTAMPTZ, + status TEXT NOT NULL DEFAULT 'running' + CHECK (status IN ('running', 'completed', 'crashed', 'terminated')), + CONSTRAINT fk_sessions_agent + FOREIGN KEY (agent_id) REFERENCES agents(id) + ON DELETE CASCADE +); + +COMMENT ON TABLE sessions IS 'Agent session history'; + +-- ============================================ +-- Table: task_logs +-- ============================================ +CREATE TABLE task_logs ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + agent_id UUID NOT NULL, + task TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'pending' + CHECK (status IN ('pending', 'running', 'completed', 'failed', 'cancelled')), + started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + completed_at TIMESTAMPTZ, + error_message TEXT, + CONSTRAINT fk_task_logs_agent + FOREIGN KEY (agent_id) REFERENCES agents(id) + ON DELETE CASCADE +); + +COMMENT ON TABLE task_logs IS 'Historical record of tasks assigned to agents'; + +-- ============================================ +-- Table: projects +-- ============================================ +CREATE TABLE projects ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + name TEXT NOT NULL, + description TEXT, + status TEXT NOT NULL DEFAULT 'planned' + CHECK (status IN ('planned', 'in_progress', 'completed', 'paused', 'cancelled')), + agent_id UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT fk_projects_agent + FOREIGN KEY (agent_id) REFERENCES agents(id) + ON DELETE SET NULL +); + +COMMENT ON TABLE projects IS 'Projects managed by the Control Center'; + +-- ============================================ +-- Table: agent_events +-- ============================================ +CREATE TABLE agent_events ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + agent_id UUID NOT NULL, + event_type TEXT NOT NULL, + payload JSONB, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT fk_agent_events_agent + FOREIGN KEY (agent_id) REFERENCES agents(id) + ON DELETE CASCADE +); + +COMMENT ON TABLE agent_events IS 'Event stream for agent lifecycle and telemetry'; diff --git a/backend/migrations/002_add_indexes.down.sql b/backend/migrations/002_add_indexes.down.sql new file mode 100644 index 0000000..49f08c3 --- /dev/null +++ b/backend/migrations/002_add_indexes.down.sql @@ -0,0 +1,20 @@ +-- Migration: 002_add_indexes (down) +-- Description: Remove all indexes added in 002_add_indexes. + +DROP INDEX IF EXISTS idx_agents_status; +DROP INDEX IF EXISTS idx_agents_last_activity; +DROP INDEX IF EXISTS idx_agents_created_at; + +DROP INDEX IF EXISTS idx_sessions_agent_id; +DROP INDEX IF EXISTS idx_sessions_status; +DROP INDEX IF EXISTS idx_sessions_started_at; + +DROP INDEX IF EXISTS idx_task_logs_agent_started; +DROP INDEX IF EXISTS idx_task_logs_status; + +DROP INDEX IF EXISTS idx_agent_events_agent_created; +DROP INDEX IF EXISTS idx_agent_events_event_type; + +DROP INDEX IF EXISTS idx_projects_status; +DROP INDEX IF EXISTS idx_projects_agent_id; +DROP INDEX IF EXISTS idx_projects_created; diff --git a/backend/migrations/002_add_indexes.up.sql b/backend/migrations/002_add_indexes.up.sql new file mode 100644 index 0000000..3411379 --- /dev/null +++ b/backend/migrations/002_add_indexes.up.sql @@ -0,0 +1,25 @@ +-- Migration: 002_add_indexes +-- Description: Add performance indexes for common query patterns. + +-- agents: status filtering, activity ordering +CREATE INDEX idx_agents_status ON agents(status); +CREATE INDEX idx_agents_last_activity ON agents(last_activity DESC); +CREATE INDEX idx_agents_created_at ON agents(created_at DESC); + +-- sessions: agent session lookups, active session checks +CREATE INDEX idx_sessions_agent_id ON sessions(agent_id); +CREATE INDEX idx_sessions_status ON sessions(status); +CREATE INDEX idx_sessions_started_at ON sessions(started_at DESC); + +-- task_logs: agent task history, chronological ordering +CREATE INDEX idx_task_logs_agent_started ON task_logs(agent_id, started_at DESC); +CREATE INDEX idx_task_logs_status ON task_logs(status); + +-- agent_events: event stream queries +CREATE INDEX idx_agent_events_agent_created ON agent_events(agent_id, created_at DESC); +CREATE INDEX idx_agent_events_event_type ON agent_events(event_type); + +-- projects: status filtering, agent assignment +CREATE INDEX idx_projects_status ON projects(status); +CREATE INDEX idx_projects_agent_id ON projects(agent_id); +CREATE INDEX idx_projects_created ON projects(created_at DESC);