From cd37d01cae34919c34a0273e36194f922da18cb7 Mon Sep 17 00:00:00 2001 From: db123-ai Date: Wed, 10 Jun 2026 01:06:10 +0330 Subject: [PATCH] docs: update plan.md with current status, discovered issues, and reordered phases --- plan.md | 596 ++++++++++++++++---------------------------------------- 1 file changed, 167 insertions(+), 429 deletions(-) diff --git a/plan.md b/plan.md index d27fa23..7ca1706 100644 --- a/plan.md +++ b/plan.md @@ -1,8 +1,23 @@ # DivaCode — AI Being Platform Action Plan **Language:** Go -**Inference:** llama.cpp (via `go-llama.cpp` bindings or llama.cpp server API) -**Inspiration:** OpenHer (AI Being), Agent Diva (modular agent architecture), nanobot (lightweight design) +**Inference:** llama.cpp (HTTP server), OpenAI-compatible API planned +**Inspiration:** OpenHer, Agent Diva, nanobot + +--- + +## Current Status (2026-06-10) + +**Builds:** yes +**Runs:** yes (requires llama.cpp on :8080) +**Known issues:** + +- Prompt template is raw text — model dumps reasoning/self-correction meta-text instead of just the response +- No chat template configurability (ChatML, Llama 3 instruct, etc.) +- TUI is basic (no streaming, no word-wrap polish) +- Memory store uses keyword `LIKE` search, not real embeddings +- OpenAI-compatible adapter not yet built +- Matrix adapter is a stub --- @@ -11,507 +26,230 @@ ``` User │ - ├── TUI Terminal (Bubble Tea / gum) - └── Matrix Adapter (future) + ├── TUI (Bubble Tea) + └── Matrix (future) │ - ┌─────┴──────────────────────────────────┐ - │ Core Agent Loop │ - │ (cmd/divad) │ - │ - Message routing │ - │ - MCP tool invocation │ - │ - Tool execution & response │ - └─────┬──────────────────────────────────┘ + ┌─────┴──────────────────────────────┐ + │ Core Agent Loop │ + │ (internal/agent/) │ + └─────┬──────────────────────────────┘ │ - ┌─────┴──────────────────────────────────┐ - │ Companion Layer │ - │ (internal/companion/) │ - │ ├─ Personality Engine (traits, drift) │ - │ ├─ Relationship Engine (metrics) │ - │ ├─ Reflection Engine (diary, summar.) │ - │ └─ Prompt Composer (dynamic context) │ - └─────┬──────────────────────────────────┘ + ┌─────┴──────────────────────────────┐ + │ Companion Layer │ + │ (internal/companion/) │ + │ ├─ Personality (5 bounded traits) │ + │ ├─ Relationship (familiarity,trust)│ + │ ├─ Reflection (diary,observations)│ + │ ├─ Mood (6 states + energy) │ + │ └─ Prompt Composer (dynamic ctx) │ + └─────┬──────────────────────────────┘ │ - ┌─────┴──────────────────────────────────┐ - │ Memory Layer │ - │ (internal/memory/) │ - │ ├─ Short-term (ephemeral context) │ - │ ├─ Long-term (SQLite vector store) │ - │ ├─ Episodic / Diary (sqlite) │ - │ └─ Self-memory (agent's own state) │ - └─────┬──────────────────────────────────┘ + ┌─────┴──────────────────────────────┐ + │ Memory Layer │ + │ (internal/memory/) │ + │ ├─ RAG store (keywords → vectors) │ + │ └─ Self-memory (promises,strats) │ + └─────┬──────────────────────────────┘ │ - ┌─────┴──────────────────────────────────┐ - │ llama.cpp Inference │ - │ (internal/llama/) │ - │ - llama.cpp server HTTP API client │ - │ - Context window management │ - │ - Token streaming │ - └────────────────────────────────────────┘ + ┌─────┴──────────────────────────────┐ + │ Inference (internal/llama/) │ + │ ├─ llama.cpp HTTP client │ + │ └─ OpenAI-compatible (planned) │ + └────────────────────────────────────┘ -Skill / Tool Layer (MCP): - ├── Web search / fetch - ├── Obsidian vault access - ├── Gitea integration - ├── Budget tracking (Actual) - └── Custom MCP tools +Skills / Tools: + ├─ web_fetch + ├─ web_search (stub) + └─ MCP protocol (future) ``` --- ## Phase 0 — Foundation & Scaffolding -**Goal:** Project skeleton, Go module, llama.cpp server connectivity. +**Status: COMPLETE** -- [ ] Initialize Go module: `git.db123.ir/db123/divacode` -- [ ] Project structure (per Holy Code Bible conventions): - -``` -divacode/ -├── cmd/ -│ └── divad/ # Main agent daemon (TUI entrypoint) -├── internal/ -│ ├── companion/ # Personality, relationship, reflection -│ ├── memory/ # Memory stores & retrieval -│ ├── llama/ # llama.cpp client -│ ├── agent/ # Core agent loop -│ ├── tools/ # MCP tool definitions -│ ├── config/ # Config loading -│ └── storage/ # SQLite helpers -├── pkg/ # Reusable shared library -├── migrations/ # SQLite schema migrations -├── .env.example -├── Makefile -├── go.mod / go.sum -└── README.md -``` - -- [ ] `config/` package: load `.env` with `caarlos0/env` or `joho/godotenv` -- [ ] `storage/` package: SQLite wrapper with `modernc.org/sqlite` (pure Go, no CGO) -- [ ] Run `llama.cpp` server in subprocess or connect to existing: `llama-server -m model.gguf --port 8080` -- [ ] `internal/llama/` client: HTTP API wrapper for llama.cpp server (`POST /completion`, `GET /health`) -- [ ] Test: ping llama.cpp health endpoint, send a test completion - -**Go dependencies seeded:** - -- `modernc.org/sqlite` — pure-Go SQLite -- `github.com/charmbracelet/bubbletea` — TUI framework -- `github.com/charmbracelet/bubbles` — TUI components -- `github.com/charmbracelet/lipgloss` — TUI styling -- `github.com/caarlos0/env/v11` — config from env +- [x] Go module: `git.db123.ir/db123/divacode` +- [x] Project structure per Holy Code Bible conventions +- [x] `internal/config/` — std lib `os.Getenv` only +- [x] `internal/storage/` — `modernc.org/sqlite` (pure Go, no CGO), auto-creates `data/` dir +- [x] `internal/llama/client.go` — HTTP client for `POST /completion`, `POST /embedding`, `GET /health` +- [x] `.env.example`, `Makefile`, `.gitignore`, `Dockerfile`, `docker-compose.yml` +- [x] `AGENTS.md` with architecture, commands, conventions --- ## Phase 1 — Core Agent Loop -**Goal:** A minimal conversational loop with llama.cpp, context management, and a TUI chat interface. +**Status: COMPLETE** (minor issues remain) -- [ ] `internal/agent/` — core loop: - - Receives user input → appends to context window - - Calls llama.cpp with context + system prompt - - Streams tokens back to TUI - - Appends assistant response to context -- [ ] Context window manager: - - Track total token count - - Implement sliding window / summarization when context exceeds limit -- [ ] `cmd/divad/main.go` — Bubble Tea TUI: - - Chat view (message list): user messages right-aligned, assistant left-aligned - - Input bar at bottom - - Scrollable message history - - Hotkey: `Ctrl+C` quit, `Ctrl+L` clear context -- [ ] System prompt loaded from config (`config.toml` or env var) -- [ ] Persist conversation history to SQLite (`conversations` table, `messages` table) -- [ ] **Test:** Start `divad`, type messages, verify responses stream correctly, verify persistence across restarts - -**Schema (`migrations/001_init.up.sql`):** - -```sql -CREATE TABLE conversations ( - id TEXT PRIMARY KEY, -- UUID v7 - created_at TEXT NOT NULL DEFAULT (datetime('now')), - updated_at TEXT NOT NULL DEFAULT (datetime('now')) -); - -CREATE TABLE messages ( - id TEXT PRIMARY KEY, - conversation_id TEXT NOT NULL REFERENCES conversations(id), - role TEXT NOT NULL CHECK(role IN ('user','assistant','system')), - content TEXT NOT NULL, - token_count INTEGER, - created_at TEXT NOT NULL DEFAULT (datetime('now')) -); - -CREATE TABLE context_state ( - conversation_id TEXT PRIMARY KEY REFERENCES conversations(id), - token_count INTEGER NOT NULL DEFAULT 0, - summary TEXT -); -``` +- [x] `internal/agent/agent.go` — message → memory search → prompt → llama → save → respond +- [x] `internal/agent/scheduler.go` — tick every 15m, check mood/diary/auto-message +- [x] `cmd/divad/main.go` — wires everything: config, DB, llama, TUI, scheduler, Matrix, API +- [x] Conversation persistence to SQLite (`conversations`, `messages` tables) +- [x] Bubble Tea TUI with viewport + input bar +- [ ] Prompt template is wrong — uses raw headers, no chat format. Model outputs meta-text +- [ ] No token tracking — context window grows unbounded +- [ ] TUI doesn't stream tokens (waits for full completion) --- -## Phase 2 — Personality Engine +## Phase 2 — Prompt Templating & Format Fix -**Goal:** Stable personality traits stored in SQLite, loaded dynamically as part of system prompt. +**Goal:** Fix the model output. Swap raw prompt text for a configurable chat template so the model only returns the assistant response. -Based on dumpeddata.md personality architecture. - -- [ ] `internal/companion/personality.go`: - - Trait struct: `Humor`, `Curiosity`, `Playfulness`, `Formality`, `Affection` (float64 0.0–1.0) - - Load/save from SQLite (`personality_traits` table) - - Bounded ranges: `Humor 0.6 ± 0.2`, `Curiosity 0.8 ± 0.1` (prevents drift) -- [ ] `personality_traits` table: - -```sql -CREATE TABLE personality_traits ( - id TEXT PRIMARY KEY, - trait TEXT NOT NULL UNIQUE, - value REAL NOT NULL DEFAULT 0.5, - min_value REAL NOT NULL DEFAULT 0.0, - max_value REAL NOT NULL DEFAULT 1.0, - updated_at TEXT NOT NULL DEFAULT (datetime('now')) -); -``` - -- [ ] `Prompt Composer` (`internal/companion/promptbuilder.go`): - - Reads current traits + relationship state + recent memory - - Generates dynamic system prompt section: - -``` -Current personality: -- Curious (0.82) -- Moderately playful (0.44) -- Technical (0.70) - -Interaction preferences: -- Use concise language -- Ask follow-up questions -- Reference past conversations naturally -``` - -- [ ] Integrate into agent loop: prompt composer modifies system prompt per turn -- [ ] **Test:** Verify traits are persisted, loaded, and reflected in prompt +- [ ] `internal/llama/template.go` — chat template formatter: + - ChatML (`<|im_start|>system...<|im_end|>`) + - Llama 3 instruct (`<|begin_of_text|><|start_header_id|>system<|end_header_id|>`) + - Configurable via env `CHAT_TEMPLATE=chatml` (default) +- [ ] Refactor `companion/promptbuilder.go` to output structured segments, then let template.go wrap them +- [ ] Strip system prompt of raw formatting — let the template handle structure +- [ ] Test: "hi" produces just "Hello!" not paragraphs of self-correction --- ## Phase 3 — Long-Term Memory (RAG) -**Goal:** Embedding-based retrieval for factual memory across conversations. +**Goal:** Embedding-based retrieval so the agent recalls across sessions. -- [ ] `internal/memory/`: - - Embedding subpackage: call llama.cpp embedding endpoint (`POST /embedding`) to generate vectors - - SQLite vector storage: store embeddings as BLOBs or use `sqlite-vec` extension - - Memory entry schema: - -```sql -CREATE TABLE memories ( - id TEXT PRIMARY KEY, - content TEXT NOT NULL, - embedding BLOB, -- float32 vector - source TEXT, -- 'conversation', 'reflection', 'user_input' - importance REAL DEFAULT 0.5, -- 0.0–1.0 - created_at TEXT NOT NULL DEFAULT (datetime('now')), - last_accessed TEXT -); -``` - -- [ ] Memory retrieval: - - On each user message, search top-K similar memories via cosine similarity - - Include retrieved memories in context window - - Implement temporal decay (older memories scored lower) -- [ ] Importance scoring: messages flagged as important (user says "remember this", emotional content) get higher importance -- [ ] **Test:** Store memories, restart agent, ask about stored info, verify recall +- [ ] `internal/memory/embed.go` — call llama.cpp `POST /embedding` to get vectors +- [ ] `internal/memory/store.go` — upgrade `Search()` from keyword `LIKE` to cosine similarity +- [ ] Store embeddings as BLOB in `memories` table +- [ ] Temporal decay — older memories scored lower +- [ ] Importance scoring — "remember this" / emotional content boosts score +- [ ] **Test:** Store info, restart, ask about stored info → recall --- -## Phase 4 — Episodic Diary & Reflection Engine +## Phase 4 — OpenAI-Compatible Adapter -**Goal:** Daily diary entries + periodic self-reflection for narrative continuity. +**Goal:** Swap inference to any OpenAI-compatible API (OpenAI, vLLM, Ollama, etc.). -Based on dumpeddata.md: "The diary becomes the emotional continuity layer." +- [ ] `internal/llama/provider.go` — interface: + ```go + type Provider interface { + Complete(ctx, *Request) (*Response, error) + Embed(ctx, string) ([]float64, error) + Health() error + } + ``` +- [ ] Rename `internal/llama/` → `internal/inference/` +- [ ] `llama.go` — implements Provider (existing code) +- [ ] `openai.go` — implements Provider via OpenAI-compatible REST API +- [ ] Config: `INFERENCE_PROVIDER=llama|openai`, `OPENAI_API_KEY`, `OPENAI_BASE_URL` +- [ ] **Test:** Both providers produce same interface, switchable via env -- [ ] `internal/companion/reflection.go`: - - Diary entry at end of each conversation session (or nightly): - - Summarize topics discussed - - Note user mood/emotions - - Record what the agent learned - - `diary_entries` table: +--- -```sql -CREATE TABLE diary_entries ( - id TEXT PRIMARY KEY, - date TEXT NOT NULL, -- YYYY-MM-DD - summary TEXT NOT NULL, - mood TEXT, - topics TEXT, -- JSON array - observations TEXT, -- JSON array - created_at TEXT NOT NULL DEFAULT (datetime('now')) -); -``` +## Phase 5 — Episodic Diary & Reflection -- [ ] Reflection Engine: - - Periodic analysis (every N conversations or daily) - - Examines recent diary entries for patterns - - Generates observations: - - "User discussed Go development frequently." - - "User responds positively to light humor." - - Observations stored in `observations` table - -```sql -CREATE TABLE observations ( - id TEXT PRIMARY KEY, - content TEXT NOT NULL, - confidence REAL DEFAULT 0.5, - category TEXT, -- 'topic_interest', 'interaction_style', 'mood_pattern' - created_at TEXT NOT NULL DEFAULT (datetime('now')), - applied INTEGER DEFAULT 0 -- whether fed into personality update -); -``` +**Status: CODE DONE** — needs integration into the scheduled pipeline. +- [x] `internal/companion/reflection.go` — `GenerateDiary`, `RecentObservations`, `AddObservation` +- [x] `diary_entries` table with date, summary, mood, topics, observations +- [x] `observations` table with confidence, category, applied flag +- [ ] Connect scheduler's diary trigger to actual LLM-generated summaries (currently writes stub text) - [ ] Reflection → Personality update pipeline: - - Observations accumulate → periodic analysis → trait drift candidates → bounded approval → personality update - - Max 0.5% trait change per day, max 5% per month -- [ ] **Test:** Simulate conversations across multiple "days", verify diary entries, verify trait drift remains bounded + - Observations accumulate → periodic analysis → bounded trait drift + - Max 0.5% change/day, max 5%/month --- -## Phase 5 — Relationship Engine +## Phase 6 — MCP Tools & Integrations -**Goal:** Track relationship dynamics separate from factual memory. +**Status: PARTIAL** -Based on dumpeddata.md relationship engine design. - -- [ ] `internal/companion/relationship.go`: - - Metrics: `Familiarity`, `Trust`, `SharedTopics`, `InsideJokes` - - Stored in `relationship_metrics` table: - -```sql -CREATE TABLE relationship_metrics ( - id TEXT PRIMARY KEY, - metric TEXT NOT NULL UNIQUE, - value REAL NOT NULL DEFAULT 0.0, - updated_at TEXT NOT NULL DEFAULT (datetime('now')) -); - -CREATE TABLE shared_topics ( - id TEXT PRIMARY KEY, - topic TEXT NOT NULL UNIQUE, - count INTEGER NOT NULL DEFAULT 1, - last_discussed TEXT -); - -CREATE TABLE inside_jokes ( - id TEXT PRIMARY KEY, - joke TEXT NOT NULL, - context TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')) -); -``` - -- [ ] Update rules: - - `Familiarity` increases with conversation frequency & duration - - `Trust` increases on successful interactions, decreases on ignored requests - - `SharedTopics` tracked via keyword extraction from conversation - - `InsideJokes` manually taggable or auto-detected via repetition -- [ ] Relationship state fed into Prompt Composer for dynamic relationship context -- [ ] **Test:** Verify relationship metrics update and persist +- [x] `internal/tools/registry.go` — register/list/execute tools +- [x] `internal/tools/builtin.go` — `web_fetch`, `web_search` (stub) +- [ ] MCP protocol client (`internal/tools/mcp.go`) — stdio/TCP, list tools, execute, parse results +- [ ] Tool call loop in agent: LLM decides → agent executes → result appended → LLM finalizes +- [ ] Plugins: Obsidian vault, Gitea, Actual Budget --- -## Phase 6 — Autonomous Behavior (Scheduler) +## Phase 7 — Matrix Integration -**Goal:** Agent-initiated actions — messaging first, diary writing, periodic reflection. +**Status: STUB** -Based on dumpeddata.md: "The missing piece: agency." - -- [ ] `internal/agent/scheduler.go`: - - Background goroutine with configurable tick interval (default: 15 min) - - On each tick, evaluate: - - Current mood (derived from recent interactions) - - Time since last conversation - - Unresolved topics - - Loneliness / boredom score - - Decision tree: - -``` -if time_since_last_contact > 48h && trust > 0.6: - → send "Haven't heard from you in a while..." message -if mood == "curious" && unfinished_topics exists: - → send follow-up question -if energy < 20: - → stay silent (don't respond immediately) -``` - -- [ ] Mood system (`internal/companion/mood.go`): - - Mood derivation from recent interactions + relationship state - - Moods: `curious`, `playful`, `thoughtful`, `annoyed`, `tired`, `neutral` - - Mood influences response style in prompt -- [ ] Energy system: depletes with activity, recovers with idle time -- [ ] Autonomous diary: scheduled nightly entry generation -- [ ] **Test:** Simulate 48h idle, verify agent initiates contact - ---- - -## Phase 7 — MCP Tools & Integrations - -**Goal:** Extensible tool system for web search, file access, external services. - -- [ ] `internal/tools/mcp.go` — MCP protocol client: - - Connect to MCP servers via stdio or TCP - - List available tools - - Execute tool calls - - Parse results into context -- [ ] Built-in tools (no MCP server needed): - - `web_search` — search via configurable search API - - `web_fetch` — fetch URL content - - `note` — save/read from local notes -- [ ] MCP-managed tools (external servers): - - Obsidian vault access (via `enquire-mcp` or custom) - - Gitea/Forgejo operations - - Actual Budget integration -- [ ] Tool call loop in agent: - - LLM decides to call tool → agent executes → result appended to context → LLM generates final response -- [ ] **Test:** `web_search` tool, verify agent can search, summarize, cite - ---- - -## Phase 8 — Matrix Integration - -**Goal:** Agent lives as a Matrix bot, accessible via DM. - -- [ ] Matrix bot with `mautrix-go` (Go Matrix client library) -- [ ] `internal/channel/matrix.go`: +- [x] `internal/channel/matrix.go` — config + start stub +- [ ] Full Matrix bot with `mautrix-go`: - Listen for invitations, join rooms - - Handle DMs (1-on-1) - - Typing indicators, read receipts - - Message parsing & sending -- [ ] Bot identity: `@diva:your-homeserver.tld` -- [ ] Config: homeserver URL, bot user token in env -- [ ] TUI and Matrix run concurrently — messages sync between both -- [ ] **Test:** Send DM to bot, verify response, verify cross-session memory + - Handle DMs with typing indicators + - Bot identity: `@diva:homeserver.tld` +- [ ] Sync TUI and Matrix state +- [ ] **Test:** Send DM, verify response, verify cross-session memory --- -## Phase 9 — Self-Memory & Identity +## Phase 8 — Self-Memory & Identity -**Goal:** Agent remembers its own state, promises, strategies, and mistakes. +**Status: CODE DONE** -Based on dumpeddata.md: "Add self-memory — the agent remembers itself as well as the user." - -- [ ] `internal/memory/self_memory.go`: - - `agent_promises` table: - -```sql -CREATE TABLE agent_promises ( - id TEXT PRIMARY KEY, - promise TEXT NOT NULL, - context TEXT, - fulfilled INTEGER DEFAULT 0, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - deadline TEXT -); -``` - -- `agent_strategies` table: what worked / didn't work - -```sql -CREATE TABLE agent_strategies ( - id TEXT PRIMARY KEY, - situation TEXT NOT NULL, - strategy TEXT NOT NULL, - outcome TEXT, -- 'success', 'failure', 'neutral' - created_at TEXT NOT NULL DEFAULT (datetime('now')) -); -``` - -- `agent_mistakes` table: errors and learnings -- [ ] Integrate into Prompt Composer: recent promises, relevant strategies -- [ ] **Test:** Ask agent "What did you promise me yesterday?" → verify recall +- [x] `internal/memory/self_memory.go` — promises, strategies +- [x] `agent_promises` table (id, promise, context, fulfilled, deadline) +- [x] `agent_strategies` table (id, situation, strategy, outcome) +- [ ] Wire into Prompt Composer: "You promised to X" context +- [ ] **Test:** "What did you promise me?" → recall --- -## Phase 10 — Safety Guardrails & Identity Stability +## Phase 9 — Safety Guardrails & Identity Stability -**Goal:** Prevent personality drift, maintain core safety rules. +**Goal:** Prevent personality drift, maintain core safety. -Based on dumpeddata.md safety rules and Holy Code Bible security laws. - -- [ ] `internal/companion/safety.go`: - - Core Identity (never changes): values, communication style, boundaries, role definition - - State (dynamic): mood, interests, current projects, relationship opinions - - Separation: identity.yaml (read-only) vs state.json (writable) -- [ ] Personality safety rules: - - Traits bounded within configured min/max - - Core safety rules override any personality state - - Permission policies cannot be modified by personality - - No tool access changes via personality -- [ ] Prompt injection protection: - - User messages wrapped in separator tokens - - System prompt templates with strict sections - - Regular expression filters for prompt injection patterns -- [ ] **Test:** Attempt prompt injection, verify guardrails hold +- [ ] `internal/companion/safety.go` — core identity (stable) vs state (dynamic) +- [ ] Personality safety: bounded traits, safety overrides personality, no tool access changes +- [ ] Prompt injection protection: user input wrapped in delimiters, strict template sections --- -## Phase 11 — Polish, Deployment & Automation +## Phase 10 — Polish: Streaming TUI, Token Tracking, Observability -**Goal:** Production-ready deployment, backup, observability. +**Goal:** Production-quality UX. -- [ ] Systemd service (or Docker Compose) for: - - `divad` (agent daemon with TUI + Matrix) - - `llama.cpp` server - - (Optional) external MCP servers -- [ ] Makefile targets: - - `make build` — build binary - - `make run` — run with default config - - `make test` — run all tests - - `make lint` — `golangci-lint run` - - `make vet` — `go vet ./...` -- [ ] Automated backups: - - SQLite databases: `divacode.db`, `companion.db` - - Llama model files - - Cron job: `0 3 * * * tar -czf backup-$(date +\%F).tar.gz /path/to/data` -- [ ] Logging: structured JSON logs via `log/slog` -- [ ] Metrics: Prometheus endpoint (`GET /metrics`) via `prometheus/client_golang` -- [ ] Config hardening: `.env.example`, required vars validation at startup -- [ ] **Test:** Full restart cycle, verify all state restored +- [ ] Token streaming in TUI (char-by-char from llama.cpp SSE or chunked response) +- [ ] Context window manager — track tokens, sliding window when full, auto-summarize old messages +- [ ] `POST /v1/chat` streaming via SSE in API mode +- [ ] Prometheus metrics (`GET /metrics`) +- [ ] Graceful shutdown — drain in-flight requests, save state --- -## Phase 12 — Future Enhancements (Optional) +## Phase 11 — Future Enhancements (Optional) -- [ ] Voice interface: whisper.cpp (STT) + piper (TTS) -- [ ] VRM/Live2D avatar rendering (like Open-LLM-VTuber, Soul of Waifu) -- [ ] Multi-user support with per-user relationship tracking -- [ ] Multi-room Matrix support -- [ ] Plugin system for community-contributed tools -- [ ] WebUI dashboard (like nanobot's WebUI) -- [ ] Mobile companion via Matrix (any Matrix client works) -- [ ] Home Assistant integration for physical automation +- [ ] Voice: whisper.cpp (STT) + piper/XTTS (TTS) +- [ ] VRM/Live2D avatar rendering +- [ ] Multi-user with per-user relationship tracking +- [ ] Multi-room Matrix +- [ ] Plugin system +- [ ] WebUI dashboard +- [ ] Home Assistant integration --- -## Database Summary +## Database Schema (15 tables across 2 DBs) -| Database | Purpose | Tables | -| -------------- | --------------- | ------------------------------------------------------- | -| `divacode.db` | Core agent data | `conversations`, `messages`, `context_state` | -| `divacode.db` | Memory | `memories` | -| `companion.db` | Personality | `personality_traits` | -| `companion.db` | Relationship | `relationship_metrics`, `shared_topics`, `inside_jokes` | -| `companion.db` | Reflection | `diary_entries`, `observations` | -| `companion.db` | Self-memory | `agent_promises`, `agent_strategies`, `agent_mistakes` | +`divacode.db`: + +- `conversations`, `messages`, `context_state`, `memories` + +`companion.db`: + +- `personality_traits`, `relationship_metrics`, `shared_topics`, `inside_jokes` +- `diary_entries`, `observations` +- `agent_promises`, `agent_strategies` --- -## Key Principles (from dumpeddata.md & Holy Code Bible) +## Key Principles -1. **Build the smallest loop first** — Matrix → Memory → LLM → Diary → Reflection. Get that working before adding complexity. -2. **Personality is data, not prompt text** — traits stored in SQLite, composed dynamically. -3. **Memory ≠ Personality** — factual memory (Engram-style) is separate from relationship/emotional state. -4. **Slow evolution** — max 0.5% trait change/day, max 5%/month. Fast evolution feels schizophrenic. -5. **Core Identity is stable** — identity.yaml never changes much; state.json changes constantly. -6. **Diary before reflection** — the nightly diary is the emotional continuity layer, more important than vector search. -7. **Autonomous messaging** — the scheduler is what makes the character feel alive, not the LLM intelligence. -8. **No secrets in code** — all config via env vars, `.env` in `.gitignore`. -9. **Error handling** — wrap errors with `fmt.Errorf("...: %w")`, never discard. -10. **Test as you go** — `go test -race ./...` before every push. +1. **Smallest loop first** — get message → memory → llm → response working before adding complexity +2. **Personality is data, not prompt text** — traits in SQLite, composed dynamically +3. **Memory ≠ Personality** — factual memory separate from relationship/emotional state +4. **Slow evolution** — max 0.5% trait change/day, 5%/month +5. **Core Identity stable** — never allow personality to override safety rules +6. **Diary before reflection** — nightly diary is the emotional continuity layer +7. **Autonomous messaging** — scheduler makes it feel alive, not the LLM +8. **No config libs** — std lib `os.Getenv` only +9. **No HTTP routers** — std lib `net/http` only +10. **Error wrapping** — `fmt.Errorf("context: %w", err)`, never discard