docs: update plan.md with current status, discovered issues, and reordered phases
This commit is contained in:
586
plan.md
586
plan.md
@@ -1,8 +1,23 @@
|
|||||||
# DivaCode — AI Being Platform Action Plan
|
# DivaCode — AI Being Platform Action Plan
|
||||||
|
|
||||||
**Language:** Go
|
**Language:** Go
|
||||||
**Inference:** llama.cpp (via `go-llama.cpp` bindings or llama.cpp server API)
|
**Inference:** llama.cpp (HTTP server), OpenAI-compatible API planned
|
||||||
**Inspiration:** OpenHer (AI Being), Agent Diva (modular agent architecture), nanobot (lightweight design)
|
**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
|
User
|
||||||
│
|
│
|
||||||
├── TUI Terminal (Bubble Tea / gum)
|
├── TUI (Bubble Tea)
|
||||||
└── Matrix Adapter (future)
|
└── Matrix (future)
|
||||||
│
|
│
|
||||||
┌─────┴──────────────────────────────────┐
|
┌─────┴──────────────────────────────┐
|
||||||
│ Core Agent Loop │
|
│ Core Agent Loop │
|
||||||
│ (cmd/divad) │
|
│ (internal/agent/) │
|
||||||
│ - Message routing │
|
└─────┬──────────────────────────────┘
|
||||||
│ - MCP tool invocation │
|
|
||||||
│ - Tool execution & response │
|
|
||||||
└─────┬──────────────────────────────────┘
|
|
||||||
│
|
│
|
||||||
┌─────┴──────────────────────────────────┐
|
┌─────┴──────────────────────────────┐
|
||||||
│ Companion Layer │
|
│ Companion Layer │
|
||||||
│ (internal/companion/) │
|
│ (internal/companion/) │
|
||||||
│ ├─ Personality Engine (traits, drift) │
|
│ ├─ Personality (5 bounded traits) │
|
||||||
│ ├─ Relationship Engine (metrics) │
|
│ ├─ Relationship (familiarity,trust)│
|
||||||
│ ├─ Reflection Engine (diary, summar.) │
|
│ ├─ Reflection (diary,observations)│
|
||||||
│ └─ Prompt Composer (dynamic context) │
|
│ ├─ Mood (6 states + energy) │
|
||||||
└─────┬──────────────────────────────────┘
|
│ └─ Prompt Composer (dynamic ctx) │
|
||||||
|
└─────┬──────────────────────────────┘
|
||||||
│
|
│
|
||||||
┌─────┴──────────────────────────────────┐
|
┌─────┴──────────────────────────────┐
|
||||||
│ Memory Layer │
|
│ Memory Layer │
|
||||||
│ (internal/memory/) │
|
│ (internal/memory/) │
|
||||||
│ ├─ Short-term (ephemeral context) │
|
│ ├─ RAG store (keywords → vectors) │
|
||||||
│ ├─ Long-term (SQLite vector store) │
|
│ └─ Self-memory (promises,strats) │
|
||||||
│ ├─ Episodic / Diary (sqlite) │
|
└─────┬──────────────────────────────┘
|
||||||
│ └─ Self-memory (agent's own state) │
|
|
||||||
└─────┬──────────────────────────────────┘
|
|
||||||
│
|
│
|
||||||
┌─────┴──────────────────────────────────┐
|
┌─────┴──────────────────────────────┐
|
||||||
│ llama.cpp Inference │
|
│ Inference (internal/llama/) │
|
||||||
│ (internal/llama/) │
|
│ ├─ llama.cpp HTTP client │
|
||||||
│ - llama.cpp server HTTP API client │
|
│ └─ OpenAI-compatible (planned) │
|
||||||
│ - Context window management │
|
└────────────────────────────────────┘
|
||||||
│ - Token streaming │
|
|
||||||
└────────────────────────────────────────┘
|
|
||||||
|
|
||||||
Skill / Tool Layer (MCP):
|
Skills / Tools:
|
||||||
├── Web search / fetch
|
├─ web_fetch
|
||||||
├── Obsidian vault access
|
├─ web_search (stub)
|
||||||
├── Gitea integration
|
└─ MCP protocol (future)
|
||||||
├── Budget tracking (Actual)
|
|
||||||
└── Custom MCP tools
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 0 — Foundation & Scaffolding
|
## Phase 0 — Foundation & Scaffolding
|
||||||
|
|
||||||
**Goal:** Project skeleton, Go module, llama.cpp server connectivity.
|
**Status: COMPLETE**
|
||||||
|
|
||||||
- [ ] Initialize Go module: `git.db123.ir/db123/divacode`
|
- [x] Go module: `git.db123.ir/db123/divacode`
|
||||||
- [ ] Project structure (per Holy Code Bible conventions):
|
- [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
|
||||||
divacode/
|
- [x] `internal/llama/client.go` — HTTP client for `POST /completion`, `POST /embedding`, `GET /health`
|
||||||
├── cmd/
|
- [x] `.env.example`, `Makefile`, `.gitignore`, `Dockerfile`, `docker-compose.yml`
|
||||||
│ └── divad/ # Main agent daemon (TUI entrypoint)
|
- [x] `AGENTS.md` with architecture, commands, conventions
|
||||||
├── 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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 1 — Core Agent Loop
|
## 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:
|
- [x] `internal/agent/agent.go` — message → memory search → prompt → llama → save → respond
|
||||||
- Receives user input → appends to context window
|
- [x] `internal/agent/scheduler.go` — tick every 15m, check mood/diary/auto-message
|
||||||
- Calls llama.cpp with context + system prompt
|
- [x] `cmd/divad/main.go` — wires everything: config, DB, llama, TUI, scheduler, Matrix, API
|
||||||
- Streams tokens back to TUI
|
- [x] Conversation persistence to SQLite (`conversations`, `messages` tables)
|
||||||
- Appends assistant response to context
|
- [x] Bubble Tea TUI with viewport + input bar
|
||||||
- [ ] Context window manager:
|
- [ ] Prompt template is wrong — uses raw headers, no chat format. Model outputs meta-text
|
||||||
- Track total token count
|
- [ ] No token tracking — context window grows unbounded
|
||||||
- Implement sliding window / summarization when context exceeds limit
|
- [ ] TUI doesn't stream tokens (waits for full completion)
|
||||||
- [ ] `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
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 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/llama/template.go` — chat template formatter:
|
||||||
|
- ChatML (`<|im_start|>system...<|im_end|>`)
|
||||||
- [ ] `internal/companion/personality.go`:
|
- Llama 3 instruct (`<|begin_of_text|><|start_header_id|>system<|end_header_id|>`)
|
||||||
- Trait struct: `Humor`, `Curiosity`, `Playfulness`, `Formality`, `Affection` (float64 0.0–1.0)
|
- Configurable via env `CHAT_TEMPLATE=chatml` (default)
|
||||||
- Load/save from SQLite (`personality_traits` table)
|
- [ ] Refactor `companion/promptbuilder.go` to output structured segments, then let template.go wrap them
|
||||||
- Bounded ranges: `Humor 0.6 ± 0.2`, `Curiosity 0.8 ± 0.1` (prevents drift)
|
- [ ] Strip system prompt of raw formatting — let the template handle structure
|
||||||
- [ ] `personality_traits` table:
|
- [ ] Test: "hi" produces just "Hello!" not paragraphs of self-correction
|
||||||
|
|
||||||
```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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 3 — Long-Term Memory (RAG)
|
## 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/`:
|
- [ ] `internal/memory/embed.go` — call llama.cpp `POST /embedding` to get vectors
|
||||||
- Embedding subpackage: call llama.cpp embedding endpoint (`POST /embedding`) to generate vectors
|
- [ ] `internal/memory/store.go` — upgrade `Search()` from keyword `LIKE` to cosine similarity
|
||||||
- SQLite vector storage: store embeddings as BLOBs or use `sqlite-vec` extension
|
- [ ] Store embeddings as BLOB in `memories` table
|
||||||
- Memory entry schema:
|
- [ ] Temporal decay — older memories scored lower
|
||||||
|
- [ ] Importance scoring — "remember this" / emotional content boosts score
|
||||||
```sql
|
- [ ] **Test:** Store info, restart, ask about stored info → recall
|
||||||
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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 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
|
## Phase 5 — Episodic Diary & Reflection
|
||||||
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'))
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] Reflection Engine:
|
**Status: CODE DONE** — needs integration into the scheduled pipeline.
|
||||||
- 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
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
- [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:
|
- [ ] Reflection → Personality update pipeline:
|
||||||
- Observations accumulate → periodic analysis → trait drift candidates → bounded approval → personality update
|
- Observations accumulate → periodic analysis → bounded trait drift
|
||||||
- Max 0.5% trait change per day, max 5% per month
|
- Max 0.5% change/day, max 5%/month
|
||||||
- [ ] **Test:** Simulate conversations across multiple "days", verify diary entries, verify trait drift remains bounded
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 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.
|
- [x] `internal/tools/registry.go` — register/list/execute tools
|
||||||
|
- [x] `internal/tools/builtin.go` — `web_fetch`, `web_search` (stub)
|
||||||
- [ ] `internal/companion/relationship.go`:
|
- [ ] MCP protocol client (`internal/tools/mcp.go`) — stdio/TCP, list tools, execute, parse results
|
||||||
- Metrics: `Familiarity`, `Trust`, `SharedTopics`, `InsideJokes`
|
- [ ] Tool call loop in agent: LLM decides → agent executes → result appended → LLM finalizes
|
||||||
- Stored in `relationship_metrics` table:
|
- [ ] Plugins: Obsidian vault, Gitea, Actual Budget
|
||||||
|
|
||||||
```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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 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."
|
- [x] `internal/channel/matrix.go` — config + start stub
|
||||||
|
- [ ] Full Matrix bot with `mautrix-go`:
|
||||||
- [ ] `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`:
|
|
||||||
- Listen for invitations, join rooms
|
- Listen for invitations, join rooms
|
||||||
- Handle DMs (1-on-1)
|
- Handle DMs with typing indicators
|
||||||
- Typing indicators, read receipts
|
- Bot identity: `@diva:homeserver.tld`
|
||||||
- Message parsing & sending
|
- [ ] Sync TUI and Matrix state
|
||||||
- [ ] Bot identity: `@diva:your-homeserver.tld`
|
- [ ] **Test:** Send DM, verify response, verify cross-session memory
|
||||||
- [ ] 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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 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."
|
- [x] `internal/memory/self_memory.go` — promises, strategies
|
||||||
|
- [x] `agent_promises` table (id, promise, context, fulfilled, deadline)
|
||||||
- [ ] `internal/memory/self_memory.go`:
|
- [x] `agent_strategies` table (id, situation, strategy, outcome)
|
||||||
- `agent_promises` table:
|
- [ ] Wire into Prompt Composer: "You promised to X" context
|
||||||
|
- [ ] **Test:** "What did you promise me?" → recall
|
||||||
```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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 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 (stable) vs state (dynamic)
|
||||||
|
- [ ] Personality safety: bounded traits, safety overrides personality, no tool access changes
|
||||||
- [ ] `internal/companion/safety.go`:
|
- [ ] Prompt injection protection: user input wrapped in delimiters, strict template sections
|
||||||
- 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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 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:
|
- [ ] Token streaming in TUI (char-by-char from llama.cpp SSE or chunked response)
|
||||||
- `divad` (agent daemon with TUI + Matrix)
|
- [ ] Context window manager — track tokens, sliding window when full, auto-summarize old messages
|
||||||
- `llama.cpp` server
|
- [ ] `POST /v1/chat` streaming via SSE in API mode
|
||||||
- (Optional) external MCP servers
|
- [ ] Prometheus metrics (`GET /metrics`)
|
||||||
- [ ] Makefile targets:
|
- [ ] Graceful shutdown — drain in-flight requests, save state
|
||||||
- `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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 12 — Future Enhancements (Optional)
|
## Phase 11 — Future Enhancements (Optional)
|
||||||
|
|
||||||
- [ ] Voice interface: whisper.cpp (STT) + piper (TTS)
|
- [ ] Voice: whisper.cpp (STT) + piper/XTTS (TTS)
|
||||||
- [ ] VRM/Live2D avatar rendering (like Open-LLM-VTuber, Soul of Waifu)
|
- [ ] VRM/Live2D avatar rendering
|
||||||
- [ ] Multi-user support with per-user relationship tracking
|
- [ ] Multi-user with per-user relationship tracking
|
||||||
- [ ] Multi-room Matrix support
|
- [ ] Multi-room Matrix
|
||||||
- [ ] Plugin system for community-contributed tools
|
- [ ] Plugin system
|
||||||
- [ ] WebUI dashboard (like nanobot's WebUI)
|
- [ ] WebUI dashboard
|
||||||
- [ ] Mobile companion via Matrix (any Matrix client works)
|
- [ ] Home Assistant integration
|
||||||
- [ ] Home Assistant integration for physical automation
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Database Summary
|
## Database Schema (15 tables across 2 DBs)
|
||||||
|
|
||||||
| Database | Purpose | Tables |
|
`divacode.db`:
|
||||||
| -------------- | --------------- | ------------------------------------------------------- |
|
|
||||||
| `divacode.db` | Core agent data | `conversations`, `messages`, `context_state` |
|
- `conversations`, `messages`, `context_state`, `memories`
|
||||||
| `divacode.db` | Memory | `memories` |
|
|
||||||
| `companion.db` | Personality | `personality_traits` |
|
`companion.db`:
|
||||||
| `companion.db` | Relationship | `relationship_metrics`, `shared_topics`, `inside_jokes` |
|
|
||||||
| `companion.db` | Reflection | `diary_entries`, `observations` |
|
- `personality_traits`, `relationship_metrics`, `shared_topics`, `inside_jokes`
|
||||||
| `companion.db` | Self-memory | `agent_promises`, `agent_strategies`, `agent_mistakes` |
|
- `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.
|
1. **Smallest loop first** — get message → memory → llm → response working before adding complexity
|
||||||
2. **Personality is data, not prompt text** — traits stored in SQLite, composed dynamically.
|
2. **Personality is data, not prompt text** — traits in SQLite, composed dynamically
|
||||||
3. **Memory ≠ Personality** — factual memory (Engram-style) is separate from relationship/emotional state.
|
3. **Memory ≠ Personality** — factual memory separate from relationship/emotional state
|
||||||
4. **Slow evolution** — max 0.5% trait change/day, max 5%/month. Fast evolution feels schizophrenic.
|
4. **Slow evolution** — max 0.5% trait change/day, 5%/month
|
||||||
5. **Core Identity is stable** — identity.yaml never changes much; state.json changes constantly.
|
5. **Core Identity stable** — never allow personality to override safety rules
|
||||||
6. **Diary before reflection** — the nightly diary is the emotional continuity layer, more important than vector search.
|
6. **Diary before reflection** — nightly diary is the emotional continuity layer
|
||||||
7. **Autonomous messaging** — the scheduler is what makes the character feel alive, not the LLM intelligence.
|
7. **Autonomous messaging** — scheduler makes it feel alive, not the LLM
|
||||||
8. **No secrets in code** — all config via env vars, `.env` in `.gitignore`.
|
8. **No config libs** — std lib `os.Getenv` only
|
||||||
9. **Error handling** — wrap errors with `fmt.Errorf("...: %w")`, never discard.
|
9. **No HTTP routers** — std lib `net/http` only
|
||||||
10. **Test as you go** — `go test -race ./...` before every push.
|
10. **Error wrapping** — `fmt.Errorf("context: %w", err)`, never discard
|
||||||
|
|||||||
Reference in New Issue
Block a user