Initial import of LWLT AIBOT platform
This commit is contained in:
94
control-plane/migrations/005_agent_task_sessions.sql
Normal file
94
control-plane/migrations/005_agent_task_sessions.sql
Normal file
@@ -0,0 +1,94 @@
|
||||
-- A business task owns exactly one external Agent session. The external
|
||||
-- session identifier and every conversational turn stay server-side and are
|
||||
-- encrypted by the application before persistence.
|
||||
CREATE TABLE IF NOT EXISTS agent_sessions (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
task_id uuid NOT NULL UNIQUE REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
conversation_id text,
|
||||
provider text NOT NULL DEFAULT 'superagent',
|
||||
external_session_id_ciphertext text,
|
||||
status text NOT NULL DEFAULT 'pending'
|
||||
CHECK (status IN ('pending', 'active', 'recovering', 'expired', 'failed', 'closed')),
|
||||
current_turn integer NOT NULL DEFAULT 0 CHECK (current_turn >= 0),
|
||||
recovery_count integer NOT NULL DEFAULT 0 CHECK (recovery_count >= 0),
|
||||
last_error text,
|
||||
last_message_at timestamptz,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS agent_sessions_conversation_idx
|
||||
ON agent_sessions (organization_id, conversation_id, updated_at DESC)
|
||||
WHERE conversation_id IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS agent_sessions_pending_idx
|
||||
ON agent_sessions (organization_id, conversation_id, status, updated_at DESC)
|
||||
WHERE conversation_id IS NOT NULL AND status NOT IN ('closed', 'failed');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agent_session_messages (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
agent_session_id uuid NOT NULL REFERENCES agent_sessions(id) ON DELETE CASCADE,
|
||||
task_id uuid NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
turn_no integer NOT NULL CHECK (turn_no > 0),
|
||||
role text NOT NULL CHECK (role IN ('user', 'assistant', 'system')),
|
||||
content_ciphertext text NOT NULL,
|
||||
status text NOT NULL DEFAULT 'queued'
|
||||
CHECK (status IN ('queued', 'sending', 'sent', 'replayed', 'failed')),
|
||||
idempotency_key text,
|
||||
error_code text,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
sent_at timestamptz,
|
||||
UNIQUE (agent_session_id, turn_no, role)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS agent_session_messages_idempotency_idx
|
||||
ON agent_session_messages (organization_id, idempotency_key)
|
||||
WHERE idempotency_key IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS agent_session_messages_queue_idx
|
||||
ON agent_session_messages (agent_session_id, status, role, turn_no);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS agent_session_messages_task_idx
|
||||
ON agent_session_messages (organization_id, task_id, turn_no);
|
||||
|
||||
-- Existing tasks were created before task-scoped sessions existed. Backfill a
|
||||
-- pending session and preserve the already encrypted original instruction as
|
||||
-- the first user turn. The application will attach conversation metadata when
|
||||
-- a later message arrives.
|
||||
INSERT INTO agent_sessions (organization_id, task_id, status, current_turn, created_at, updated_at)
|
||||
SELECT t.organization_id, t.id,
|
||||
CASE WHEN t.status IN (
|
||||
'completed', 'cancelled', 'parse_failed', 'parse_blocked', 'blocked',
|
||||
'failed', 'dry_run', 'reconciliation_pending', 'execution_uncertain',
|
||||
'saved_unverified', 'operation_blocked'
|
||||
) THEN 'closed' ELSE 'pending' END,
|
||||
1,
|
||||
t.created_at,
|
||||
t.updated_at
|
||||
FROM tasks t
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM agent_sessions s WHERE s.task_id = t.id
|
||||
);
|
||||
|
||||
INSERT INTO agent_session_messages
|
||||
(organization_id, agent_session_id, task_id, turn_no, role, content_ciphertext, status, created_at, sent_at)
|
||||
SELECT t.organization_id, s.id, t.id, 1, 'user', t.original_text_ciphertext,
|
||||
CASE
|
||||
WHEN t.status = 'parse_queued' THEN 'queued'
|
||||
WHEN t.status = 'parse_running' THEN 'sending'
|
||||
ELSE 'sent'
|
||||
END,
|
||||
t.created_at,
|
||||
CASE WHEN t.status IN ('parse_queued', 'parse_running') THEN NULL ELSE t.created_at END
|
||||
FROM tasks t
|
||||
JOIN agent_sessions s ON s.task_id = t.id
|
||||
WHERE t.original_text_ciphertext IS NOT NULL
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM agent_session_messages m
|
||||
WHERE m.agent_session_id = s.id
|
||||
AND m.turn_no = 1
|
||||
AND m.role = 'user'
|
||||
);
|
||||
Reference in New Issue
Block a user