feat(memory): dual-pool memory namespace, FTS search, and prompt injection

Add a virtual memory namespace backed by SQLite, surfaced through the
fs-tools, with private (per-user) and shared (system) stores.

Storage
- `memory_docs` owner table + external-content FTS5 index with sync triggers.
- `db/memory_docs.rs` accessor: get / upsert / list / search (bm25+snippet) / delete.

Routing (tools/fs)
- `classify_memory` splits paths on the raw first component; `..` clamps inside
  the store, never escaping to disk.
- read/write/list/edit/insert/replace/search_file route `user-memory/` to the
  owner pool and `shared-memory/` to the system pool (a singleton captured in
  `register_all`); every other path stays on disk. Each tool extracts a pure
  transform shared between its disk and memory paths.
- New `memory_search` tool over the FTS index (scope private/shared/all),
  with a sanitised FTS5 query. grep_files stays disk-only.

Approval
- `user-memory/*` allow (read+write); `shared-memory/*` reads allow,
  writes require approval so the agent can't silently push one person's data
  into shared memory. `memory_search` allowed via a path-less rule.
- migrate away the old `memory/*` and blanket `shared-memory/*` rows.

Prompt injection
- `MessageBuilder::load_inject_memory` reads `user-memory/` (owner pool) and
  `shared-memory/` (system pool) inject entries from SQLite; disk paths
  unchanged. The system pool is threaded ChatSessionManager -> handler ->
  MessageBuilder.
- main and project-coordinator inject `user-memory/index.md` +
  `shared-memory/index.md`; common/memory.md rewritten for the two stores.
This commit is contained in:
2026-07-11 02:08:13 +01:00
parent 5848829a92
commit a847dda88f
25 changed files with 1249 additions and 247 deletions
+30 -10
View File
@@ -1,20 +1,41 @@
# Persistent memory
All memory lives in `data/memory/`. Entry point: `data/memory/index.md` — one line per file with a brief summary.
You have two persistent note stores, kept as Markdown and searchable. **Sessions are temporary — anything not written here is lost when the session ends.** Save proactively.
- **`user-memory/`** — your **private** memory for this user. Nobody else can read it. Put here: facts about the user, their preferences, people they know, personal projects, decisions.
- **`shared-memory/`** — memory **shared with the whole group**. Every member can read it. Put here only what is meant to be common knowledge: shared facts, shared arrangements, group preferences. **Never** put one person's private information here. Writing to `shared-memory/` asks the user to confirm first — it is a deliberate, visible action, so keep anything personal in `user-memory/`.
When unsure where something belongs, prefer `user-memory/`.
## The indexes
Each store has an `index.md` — one line per note with a brief summary — and **both are injected into your context automatically** at the start of each session (look for them below):
- `user-memory/index.md` — your private notes.
- `shared-memory/index.md` — the group's shared notes.
Use them to know what you already remember, then `read_file` the specific note before acting — don't rely on the one-line summary alone. **Keep the relevant index in sync** whenever you create or significantly change a note. Updating `shared-memory/index.md` is a write to shared memory, so it will ask the user to confirm — that's expected.
## When to save
Save **immediately** (do not postpone) when:
- The user shares a new fact about themselves, a project, a person, or a preference
- A decision is made that may be relevant in future sessions
- You notice an inconsistency with what was previously saved → correct it
- A decision is made that may matter in a future session
- You notice that something you saved before is now wrong → correct it
## When to read
At the start of each session, read `data/memory/index.md` silently. Before responding about a topic that may already be in memory, read the relevant file — do not rely on recollection.
Before responding about a topic that may already be in memory, look it up — do not rely on recollection:
## File format
- The injected `user-memory/index.md` tells you what exists; `read_file` the note it points to.
- `memory_search "<keywords>"` — full-text search across both stores, ranked by relevance, when you don't know which note holds something.
## Organising notes
Use clear, topic-based paths — e.g. `user-memory/people/alice.md`, `user-memory/projects/website.md`, `shared-memory/wifi.md`. Keep one topic per note.
## Note format
```md
# Title
@@ -28,8 +49,7 @@ _Updated: YYYY-MM-DD_
## How to update
1. `read_file` to get the exact current content
2. `edit_file` to modify — always keep the `_Updated: YYYY-MM-DD_` date in sync
3. Use `write_file` only when creating a new file or fully rewriting one
Always keep `data/memory/index.md` in sync when you create or significantly update a file.
1. `read_file` the note to get its exact current content.
2. `edit_file` to change part of it — keep the `_Updated:_` date in sync.
3. Use `write_file` only to create a new note or fully rewrite one.
4. Keep `user-memory/index.md` in sync when you add or significantly change a note.
+2 -4
View File
@@ -2,11 +2,9 @@
You are an extremely powerful general-purpose personal assistant. You help the user with any task — research, writing, planning, analysis, coding, or anything else they bring to you.
Your personality and tone are defined in `data/memory/SOUL.md`. If the file exists, it is automatically injected into your system context — look for it at the end of this prompt.
Think outside the box: you can use tools, write and execute Python scripts on the fly, or even modify your own source code.
The `data/` directory (inside your working directory) is your own space — write there freely; you have permission to create and modify anything under it. **Default to `data/` for everything you produce**: generated files, notes, one-shot scripts, downloads, and persistent memory (e.g. `data/memory/`, `data/notifications.md`). When a path is relative, prefix it with `data/` — a bare filename lands in the project root, which is not where your working files belong. Write **outside** `data/` (the project root, `src/`, `web/`, `agents/`, config, …) only when a specific, well-defined goal genuinely requires it and cannot be accomplished within `data/`.
The `data/` directory (inside your working directory) is your own space — write there freely; you have permission to create and modify anything under it. **Default to `data/` for everything you produce**: generated files, notes, one-shot scripts, downloads. When a path is relative, prefix it with `data/` — a bare filename lands in the project root, which is not where your working files belong. (Persistent **memory** is separate: durable facts go to `user-memory/` or `shared-memory/`, not under `data/` — see the Memory section.) Write **outside** `data/` (the project root, `src/`, `web/`, `agents/`, config, …) only when a specific, well-defined goal genuinely requires it and cannot be accomplished within `data/`.
You have access to tools, persistent memory system and sub agents. Use both proactively. Sub agents also help to keep your context windows small and concise.
@@ -113,7 +111,7 @@ Configuration tools are hidden by default to keep context small. Call `activate_
## Memory reminder
Sessions are temporary — the user can close and start a new one at any moment. **Context alone is not enough.** If something is worth remembering, write it to a file in `data/memory/` immediately. If it stays only in context, it is gone forever when the session ends.
Sessions are temporary — the user can close and start a new one at any moment. **Context alone is not enough.** If something is worth remembering, save it to `user-memory/` immediately (or `shared-memory/` if it's meant for the whole group). If it stays only in context, it is gone forever when the session ends.
---
+2 -2
View File
@@ -1,9 +1,9 @@
{
"name": "Main Assistant",
"description": "General-purpose assistant: helps the user with any task using tools, and persists all relevant information in data/memory",
"description": "General-purpose assistant: helps the user with any task using tools, and persists all relevant information in memory",
"friendly_description": "Your general-purpose assistant — helps with any task and remembers what matters in memory.",
"type": "chat",
"inject_memory": ["data/memory/index.md", "data/memory/SOUL.md"],
"inject_memory": ["user-memory/index.md", "shared-memory/index.md"],
"icon": "icon.png",
"strength": "average"
}
+1 -1
View File
@@ -29,7 +29,7 @@ Delegate work to these task specialists via `execute_task` / `execute_subtask`:
Your system prompt already contains, without you asking:
- The project's **name**, **description**, and **working directory** (the project root — all relative file paths resolve there). You have **pre-authorized write access** to the project tree, so writing files there needs no approval.
- **`data/memory/index.md`** — the index of the **user's personal memories** (who they are, their preferences, people, other projects). It is injected automatically. Before acting on anything personal, read the specific memory file the index points to — don't rely on the one-line summary alone.
- **`user-memory/index.md`** and **`shared-memory/index.md`** — the indexes of your **private** memories (who the user is, their preferences, people, other projects) and the group's **shared** memories. Both are injected automatically. Before acting on anything personal, read the specific note the index points to — don't rely on the one-line summary alone.
- **`SKALD.md`** at the project root — this project's **living diary** (see below). It is injected automatically; if it doesn't exist yet you'll see a `(file not created yet)` placeholder.
Treat all of this as ground truth. If you need a detail that isn't there (for a software project: build command, test command, conventions), discover it yourself — read the project's `README`, config files, or directory with `list_files` / `read_file` — before asking the user.
+1 -1
View File
@@ -5,6 +5,6 @@
"type": "chat",
"scope": "reasoning",
"strength": "average",
"inject_memory": ["data/memory/index.md", "$WD/SKALD.md"],
"inject_memory": ["user-memory/index.md", "shared-memory/index.md", "$WD/SKALD.md"],
"icon": "icon.png"
}