Add user_profiles table to Memory.hs for per-user profile data.
Ava is designed to be multi-tenant (multiple users via Telegram). Currently:
users table exists with: id, telegram_id, email, name, created_atsettings table is GLOBAL (key/value, no user_id)We need a proper per-user profile system for:
-- Existing users table
CREATE TABLE users (
id TEXT PRIMARY KEY, -- UUID
telegram_id INTEGER UNIQUE, -- Telegram user ID
email TEXT,
name TEXT NOT NULL,
created_at TIMESTAMP
);
-- Existing settings table (GLOBAL, no user scope)
CREATE TABLE settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS user_profiles (
user_id TEXT PRIMARY KEY REFERENCES users(id),
display_name TEXT, -- What to call them
pronouns TEXT, -- he/him, she/her, they/them, etc.
timezone TEXT, -- America/New_York, etc.
tone_preference TEXT, -- terse, detailed, casual, formal
notes TEXT, -- Free-form notes about the user
updated_at TIMESTAMP NOT NULL,
-- Could add more structured fields:
-- locale TEXT, -- en-US, etc.
-- notification_hours TEXT, -- JSON: {"start": "09:00", "end": "22:00"}
);
-- Modify settings to include optional user_id
CREATE TABLE settings (
key TEXT NOT NULL,
user_id TEXT REFERENCES users(id), -- NULL = global setting
value TEXT NOT NULL,
updated_at TEXT NOT NULL,
PRIMARY KEY (key, user_id)
);
-- Then store as:
-- key: "profile.timezone", user_id: "uuid-123", value: "America/New_York"
-- key: "profile.pronouns", user_id: "uuid-123", value: "he/him"
Recommendation: Option A is cleaner for profile data. Option B is better if we want flexible per-user settings beyond just profile. Could do both.
Add after settings table creation:
-- User profiles table for per-user preferences
SQL.execute_
conn
"CREATE TABLE IF NOT EXISTS user_profiles (\
\ user_id TEXT PRIMARY KEY REFERENCES users(id),\
\ display_name TEXT,\
\ pronouns TEXT,\
\ timezone TEXT DEFAULT 'America/New_York',\
\ tone_preference TEXT DEFAULT 'terse',\
\ notes TEXT,\
\ updated_at TIMESTAMP NOT NULL\
\)"
data UserProfile = UserProfile
{ profileUserId :: Text
, profileDisplayName :: Maybe Text
, profilePronouns :: Maybe Text
, profileTimezone :: Text
, profileTonePreference :: Text
, profileNotes :: Maybe Text
, profileUpdatedAt :: UTCTime
}
deriving (Show, Eq, Generic)
instance Aeson.ToJSON UserProfile
instance Aeson.FromJSON UserProfile
instance SQL.FromRow UserProfile
instance SQL.ToRow UserProfile
-- | Get user profile, creating default if not exists
getUserProfile :: UserId -> IO UserProfile
-- | Update user profile (upsert)
updateUserProfile :: UserProfile -> IO ()
-- | Update a single profile field
setProfileField :: UserId -> Text -> Text -> IO ()
-- e.g., setProfileField uid "timezone" "America/Los_Angeles"
-- | Get profile field
getProfileField :: UserId -> Text -> IO (Maybe Text)
-- Tool: update_user_profile
-- Allows agent to update profile based on learned info
updateUserProfileTool :: UserId -> Engine.Tool
-- Tool schema:
-- { "field": "timezone", "value": "America/Los_Angeles" }
-- { "field": "notes", "value": "Prefers morning meetings, works on Omni" }
In the systemPrompt construction (~line 1555), add:
-- Load user profile
profile <- Memory.getUserProfile (Memory.UserId (Memory.unUserId uid))
let userSection =
"\n\n## User Profile\n"
<> "Name: " <> fromMaybe userName (profileDisplayName profile) <> "\n"
<> maybe "" (\p -> "Pronouns: " <> p <> "\n") (profilePronouns profile)
<> "Timezone: " <> profileTimezone profile <> "\n"
<> maybe "" (\n -> "Notes: " <> n <> "\n") (profileNotes profile)
On first access, create default profile from users table:
getUserProfile uid = do
existing <- queryProfile uid
case existing of
Just p -> pure p
Nothing -> do
user <- getUser uid
let defaultProfile = UserProfile
{ profileUserId = unUserId uid
, profileDisplayName = Just (userName user)
, profilePronouns = Nothing
, profileTimezone = "America/New_York"
, profileTonePreference = "terse"
, profileNotes = Nothing
, profileUpdatedAt = now
}
insertProfile defaultProfile
pure defaultProfile
1. Omni/Agent/Memory.hs
2. Omni/Ava/Telegram/Bot.hs
1. typecheck.sh Omni/Agent/Memory.hs
2. Add unit tests for UserProfile CRUD
3. Test profile appears in prompt
4. Test agent can update profile via tool
Pipeline: verification failed: Build failed for Omni/Agent/Memory.hs (exit 1): 7[10000;10000H7[10000;10000Hthese 15 derivations will be built: /nix/store/0m5fa2krxa2d7m1rd67xnplb90yj9vbw-hs-mod-Omni_Agent_Prompt_IR.drv /nix/store/wrciq3ha3bcvsvdjjjphm4ispziykj2k-hs-mod-Omni_Agent_Trace.drv /nix/store/ppsjkiss9gjb8xvsmalw6lfbk98ajbjk-hs-mod-Omni_Agent_Op.drv /nix/store/kkmxaw0ks2y1ndih75clcsjsq4wc7dy9-hs-mod-Omni_Agent_Models.drv /nix/store/rvrifzh4ra6glx7w7p4znb7z0pgjbwi5-hs-mod-Omni_Agent_Provider.drv /nix/store/xyn2scqg0ygjhz73md9gbw8al9ragcsb-hs-mod-Omni_Agent_Prompt_Hydrate.drv /nix/store/ylwsiw8a9dr2ljc9siwn876bv7gizmnf-hs-mod-Omni_Agent_Prompt_Compile.drv /nix/store/11gi7wrxbsiq1x5g4c7cnn7lq3r4vf16-hs-mod-Omni_Agent_Interpreter_Sequential.drv /nix/store/rwzsc50issjjj8k3i0x582z269j4vv93-hs-mod-Omni_Agent_Programs_Compaction.drv /nix/store/837lb1y7w5fqpa8nfxkds9driy1z4z28-hs-mod-Omni_Agent_Programs_Agent.drv /nix/store/lgpvbrwgjmlp7d0vphgigwn6ik39klbf-hs-mod-Omni_Time.drv /nix/store/x0q0anj5xyg8pmdl2hq1cw17p6jh4qa9-hs-mod-Omni_Agent_Engine.drv /nix/store/riwas6d6ghspjx64h8qckldkaf1s89bi-hs-mod-Omni_Agent_Op_Bridge.drv /nix/store/qqa2b5rv2g8kh477i9plh601l1a1ql97-hs-mod-Omni_Agent_Memory.drv /nix/store/fjllzjh2a9rg5h0bvzmnhw1w4ba5dwfw-omni-agent-memory.drv building '/nix/store/kkmxaw0ks2y1ndih75clcsjsq4wc7dy9-hs-mod-Omni_Agent_Models.drv'... building '/nix/store/0m5fa2krxa2d7m1rd67xnplb90yj9vbw-hs-mod-Omni_Agent_Prompt_IR.drv'... building '/nix/store/wrciq3ha3bcvsvdjjjphm4ispziykj2k-hs-mod-Omni_Agent_Trace.drv'... building '/nix/store/lgpvbrwgjmlp7d0vphgigwn6ik39klbf-hs-mod-Omni_Time.drv'...
Omni/Agent/Models.hs:35:1: error:
Could not find module Data.Yaml'
Use -v (or :set -v in ghci) to see a list of the files searched for.
|
35 | import qualified Data.Yaml as Yaml
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: builder for '/nix/store/kkmxaw0ks2y1ndih75clcsjsq4wc7dy9-hs-mod-Omni_Agent_Models.drv' failed with exit code 1;
last 7 log lines:
>
> Omni/Agent/Models.hs:35:1: error:
> Could not find module Data.Yaml'
> Use -v (or :set -v in ghci) to see a list of the files searched for.
> |
> 35 | import qualified Data.Yaml as Yaml
> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For full logs, run:
nix log /nix/store/kkmxaw0ks2y1ndih75clcsjsq4wc7dy9-hs-mod-Omni_Agent_Models.drv
error: 1 dependencies of derivation '/nix/store/fjllzjh2a9rg5h0bvzmnhw1w4ba5dwfw-omni-agent-memory.drv' failed to build
[1A[1G[2K[+] Omni/Agent/Memory.hs [1A[1G[2K[0m[…] Omni/Agent/Memory.hs[0m[1B
[1A[1G[2K[+] Omni/Agent/Memory.hs [1A[1G[2K[~] Omni/Agent/Memory.hs: warning: you did not specify '--add-root'; the res...[1B[1A[1G[2K[~] Omni/Agent/Memory.hs: /nix/store/fjllzjh2a9rg5h0bvzmnhw1w4ba5dwfw-omni-a...[1B[1A[1G[2K[~] Omni/Agent/Memory.hs: these 15 derivations will be built:…[1B[1A[1G[2K[~] Omni/Agent/Memory.hs: /nix/store/0m5fa2krxa2d7m1rd67xnplb90yj9vbw-hs-m...[1B[1A[1G[2K[~] Omni/Agent/Memory.hs: /nix/store/lgpvbrwgjmlp7d0vphgigwn6ik39klbf-hs-m...[1B[1A[1G[2K[~] Omni/Agent/Memory.hs: building '/nix/store/kkmxaw0ks2y1ndih75clcsjsq4wc7...[1B[1A[1G[2K[~] Omni/Agent/Memory.hs: building '/nix/store/0m5fa2krxa2d7m1rd67xnplb90yj9...[1B[1A[1G[2K[~] Omni/Agent/Memory.hs: building '/nix/store/wrciq3ha3bcvsvdjjjphm4ispziyk...[1B[1A[1G[2K[~] Omni/Agent/Memory.hs: building '/nix/store/lgpvbrwgjmlp7d0vphgigwn6ik39k...[1B[1A[1G[2K[~] Omni/Agent/Memory.hs: Omni/Agent/Models.hs:35:1: error: Could not fin...[1B[1A[1G[2K[~] Omni/Agent/Memory.hs: error: builder for '/nix/store/kkmxaw0ks2y1ndih75c...[1B[1A[1G[2K[~] Omni/Agent/Memory.hs: error: 1 dependencies of derivation '/nix/store/fj...[1B[0m[38;5;1m[2Kfail: bild: realise: Omni/Agent/Memory.hs [0m[0m [0m[1A[1G[2K[0m[38;5;1m[x] Omni/Agent/Memory.hs[0m[1B 1
Pipeline scheduler: started run=pipeline-omni-agent-memory-hs-t-535-1771561218 domain=Omni/Agent/Memory.hs
Pipeline scheduler: run=pipeline-omni-agent-memory-hs-t-535-1771561218 domain=Omni/Agent/Memory.hs status=done cost=13c (fund-spend=failed)
Ava triage: pipeline auto-run reached status=done but the agent made NO git commits and reported blockers (missing files, path mismatches, or need clarification). This task is not actually in review — there's nothing to review. Resetting status to Open so it can be re-scoped.
ORPHAN COMMIT: coder agent produced commit 6f27cfdf27f2f580bf6201faf4dd30264e801f83 on 2026-02-19 but it was never merged into live. Reachable only via branchless reflog. Pipeline scheduler bug — see separate task. To recover: git cherry-pick 6f27cfdf27f2f580bf6201faf4dd30264e801f83 from omni/live (expect conflicts after 6+ weeks of drift). Otherwise re-implement from scratch.
Pipeline: dev completed (run=dev-t-535-1771511118, cost=0.0c)