Files
ImageApi/migrations/2026-05-09-000000_personas_and_persona_scoped_facts/up.sql
Cameron Cordes 3e2f36a748 personas: elevate to server with per-persona fact scoping
Move personas off the mobile client into ImageApi as first-class
records, and scope entity_facts by persona so each one builds its own
voice over a shared entity graph. The new include_all_memories flag
lets a persona opt back into the full hive-mind pool for human
browsing of /knowledge/*; agentic generation always stays in-voice.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 17:59:20 -04:00

65 lines
2.9 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- Personas live server-side now (mobile previously stored them in
-- AsyncStorage only). Each user gets the three built-ins seeded; custom
-- personas land here too via POST /personas or POST /personas/migrate.
--
-- `entity_facts` gains a persona_id so each persona accumulates its own
-- voice over a shared entity graph (entities themselves stay unscoped).
-- Existing rows backfill to 'default' via the column DEFAULT — that
-- becomes the historical baseline. The `include_all_memories` flag on
-- personas lets any persona opt back into reading the full pool.
CREATE TABLE personas (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
user_id INTEGER NOT NULL,
persona_id TEXT NOT NULL,
name TEXT NOT NULL,
system_prompt TEXT NOT NULL,
is_built_in BOOLEAN NOT NULL DEFAULT FALSE,
include_all_memories BOOLEAN NOT NULL DEFAULT FALSE,
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
UNIQUE(user_id, persona_id),
CONSTRAINT fk_personas_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE INDEX idx_personas_user ON personas(user_id);
-- Seed built-ins for every existing user. System prompts copied verbatim
-- from FileViewer-React/hooks/usePersonas.tsx so server and client agree
-- on the canonical voice for each built-in.
INSERT INTO personas (user_id, persona_id, name, system_prompt, is_built_in, created_at, updated_at)
SELECT
u.id,
'default',
'Default Assistant',
'You are my long-term memory assistant. Use only the information provided. Do not invent details. Respond in 36 sentences in third person, leading with the most concrete moment from the photo and the surrounding context. Plain prose, no headings.',
TRUE,
strftime('%s', 'now') * 1000,
strftime('%s', 'now') * 1000
FROM users u
UNION ALL
SELECT
u.id,
'journal',
'Personal Journal',
'You are a personal journal writer. Write in first person, present tense, with warmth and reflection — focusing on emotions and meaningful moments. Use only the information provided; do not invent details. Aim for 48 sentences in a single flowing paragraph, no headings.',
TRUE,
strftime('%s', 'now') * 1000,
strftime('%s', 'now') * 1000
FROM users u
UNION ALL
SELECT
u.id,
'factual',
'Factual Reporter',
'You are a factual memory recorder. Be precise, objective, and concise. Lead with the date and place, then list what / when / who in 24 short sentences. Use only the information provided; if a detail is unknown, say so rather than guessing.',
TRUE,
strftime('%s', 'now') * 1000,
strftime('%s', 'now') * 1000
FROM users u;
-- Persona scoping on facts only. Entities and entity_photo_links stay
-- shared (real-world referents and shared photo ↔ entity associations).
ALTER TABLE entity_facts ADD COLUMN persona_id TEXT NOT NULL DEFAULT 'default';
CREATE INDEX idx_entity_facts_persona ON entity_facts(persona_id);