Files
Cameron Cordes 3b9d985025 feat: add /persona_chat endpoints for open chat with persona
- PersonaChatSession dispatches turns through the shared agent loop,
  persisting transcripts keyed on (user_id, persona_id) in the new
  persona_chat table (migration included).
- GET /persona_chat/history returns a rendered transcript (tool
  invocations folded, is_initial flag) matching the file-chat shape.
- POST /persona_chat/turn returns 202 with a turn_id; SSE replay and
  cancel reuse turn_replay_impl/cancel_turn_impl, extracted from the
  actix-attributed handlers so both route families share the logic.
- POST /persona_chat/reset clears the persona transcript.
- Concurrent dispatches for the same (user, persona) are rejected with
  409 via an in-flight gate (InFlightPersonaTurns) whose RAII guard
  drops with the spawned turn task, freeing the slot on completion,
  error, or abort.
2026-08-24 21:25:48 -04:00

22 lines
968 B
SQL

-- Open chat with persona — file-anchored insight chat is keyed by
-- (library_id, file_path). The open chat is keyed by (user_id, persona_id)
-- with a single rolling transcript per pair. No tree branching in v1
-- (matches the locked-in "single rolling conversation per persona" scope);
-- a flat JSON blob is enough and avoids forcing a tree shape onto a
-- surface that's intentionally linear.
--
-- `messages_json` is the same `Vec<ChatMessage>` shape the file-anchored
-- chat persists, so the SSE replay / UI rendering on the mobile client
-- can reuse the same parser without a second schema.
CREATE TABLE persona_chat_conversations (
user_id INTEGER NOT NULL,
persona_id TEXT NOT NULL,
messages_json TEXT NOT NULL DEFAULT '[]',
turn_count INTEGER NOT NULL DEFAULT 0,
updated_at BIGINT NOT NULL,
PRIMARY KEY (user_id, persona_id)
);
CREATE INDEX idx_persona_chat_updated
ON persona_chat_conversations (user_id, updated_at DESC);