-- Private messages are deliberately excluded from the project event log. BEGIN; CREATE TABLE IF NOT EXISTS workbench.agent_messages ( project_id uuid NOT NULL, message_id uuid NOT NULL, request_id uuid NOT NULL, task_id uuid NOT NULL, run_id uuid NOT NULL, source_session_id text NOT NULL CHECK (length(source_session_id)>0), source_owner_id uuid NOT NULL, conversation_owner_id uuid NOT NULL, target_session_id text, direction text NOT NULL CHECK (direction IN ('to_task','to_chat')), in_reply_to uuid, text text NOT NULL CHECK (length(text) BETWEEN 1 AND 12000 AND text ~ '\S'), status text NOT NULL CHECK (status IN ('pending','enqueued','delivered','failed')), native_message_id text NOT NULL, failure_detail text, created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL, delivered_at timestamptz, PRIMARY KEY (project_id,message_id), UNIQUE (project_id,source_owner_id,source_session_id,request_id), FOREIGN KEY (project_id,task_id) REFERENCES workbench.operational_tasks(project_id,task_id), FOREIGN KEY (project_id,run_id) REFERENCES workbench.operational_task_runs(project_id,run_id), FOREIGN KEY (project_id,in_reply_to) REFERENCES workbench.agent_messages(project_id,message_id), CHECK ((direction='to_task') OR (direction='to_chat' AND in_reply_to IS NOT NULL AND target_session_id IS NOT NULL)) ); CREATE INDEX IF NOT EXISTS agent_messages_pending_idx ON workbench.agent_messages(project_id,created_at,message_id) WHERE status IN ('pending','enqueued'); CREATE INDEX IF NOT EXISTS agent_messages_owner_session_idx ON workbench.agent_messages(project_id,conversation_owner_id,created_at); CREATE INDEX IF NOT EXISTS agent_messages_run_idx ON workbench.agent_messages(project_id,run_id,status); COMMIT;