Skip to content

Commit f02ac8e

Browse files
committed
feat: v0.6.0 — experimental compaction markers, user memories, unified notes, plugin message bus, doctor CLI, TUI improvements, cache-bust fix
1 parent 48bdde3 commit f02ac8e

65 files changed

Lines changed: 3528 additions & 360 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ARCHITECTURE.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,21 @@
124124
- Location: `src/features/magic-context/dreamer/queue.ts`, `src/features/magic-context/dreamer/lease.ts`, `src/features/magic-context/dreamer/storage-dream-state.ts`
125125
- Pattern: SQLite-backed queue plus cooperative lease lock.
126126

127+
**User memory pipeline:**
128+
- Purpose: Extract user behavioral observations from historian output, collect candidates, and promote recurring patterns to stable user memories.
129+
- Location: `src/features/magic-context/user-memory/storage-user-memory.ts`, `src/features/magic-context/user-memory/review-user-memories.ts`
130+
- Pattern: Historian extracts candidates, dreamer reviews and promotes, system prompt injects stable memories.
131+
132+
**Plugin message bus:**
133+
- Purpose: Enable asynchronous communication between the TUI plugin and server plugin via SQLite.
134+
- Location: `src/features/magic-context/plugin-messages.ts`
135+
- Pattern: SQLite-backed message queue with direction, type, and payload columns; consumed atomically.
136+
137+
**Compaction markers:**
138+
- Purpose: Inject OpenCode-compatible compaction boundaries into the message table so `filterCompacted` stops at historian's last compartment boundary.
139+
- Location: `src/hooks/magic-context/compaction-marker.ts`, `src/hooks/magic-context/compaction-marker-manager.ts`
140+
- Pattern: Write summary/compaction rows into OpenCode's DB after historian publishes; filter them out from raw reads.
141+
127142
**Agent prompt pack:**
128143
- Purpose: Keep hidden-agent identities and prompt text isolated from runtime wiring.
129144
- Location: `src/agents/dreamer.ts`, `src/agents/historian.ts`, `src/agents/sidekick.ts`, `src/agents/magic-context-prompt.ts`

CONFIGURATION.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,29 @@ It is useful when starting a new session. It's better to choose a fast and cheap
266266

267267
---
268268

269+
## Experimental Features
270+
271+
### `experimental_compaction_markers`
272+
273+
| Key | Type | Default |
274+
|-----|------|---------|
275+
| `experimental.compaction_markers` | `boolean` | `false` |
276+
277+
When enabled, after historian publishes compartments a compaction boundary is injected into OpenCode's message/part tables. This causes `filterCompacted` to stop at the boundary, dramatically reducing the number of messages processed per transform pass.
278+
279+
### `experimental_user_memories`
280+
281+
| Key | Type | Default |
282+
|-----|------|---------|
283+
| `experimental.user_memories.enabled` | `boolean` | `false` |
284+
| `experimental.user_memories.promotion_threshold` | `number` | `3` |
285+
286+
When enabled, historian extracts behavioral observations about the user alongside compartments. These are stored as candidates and reviewed by dreamer during scheduled runs. Recurring patterns that appear across multiple historian runs are promoted to stable user memories, injected into all sessions via `<user-profile>` in the system prompt.
287+
288+
**Requires dreamer to be enabled.** Without dreamer, candidates accumulate but are never promoted to stable memories. The `doctor` command warns about this misconfiguration.
289+
290+
- `promotion_threshold`: minimum number of semantically similar candidate observations before dreamer considers promoting to a stable memory (2–20, default 3).
291+
269292
## Commands
270293

271294
| Command | Description |
@@ -323,6 +346,14 @@ It is useful when starting a new session. It's better to choose a fast and cheap
323346
"model": "github-copilot/gpt-5.4",
324347
"fallback_models": ["anthropic/claude-sonnet-4-6"],
325348
"timeout_ms": 30000
349+
},
350+
351+
"experimental": {
352+
"compaction_markers": false,
353+
"user_memories": {
354+
"enabled": false,
355+
"promotion_threshold": 3
356+
}
326357
}
327358
}
328359
```

README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ Your agent should never stop working to manage its own context. Magic Context is
3939

4040
**5.** TUI sidebar with live context breakdown, token usage, historian status, and memory counts — right inside the terminal.
4141

42+
### 🧪 Experimental
43+
44+
**Compaction Markers** — after historian compresses older messages, Magic Context injects a boundary into OpenCode's message table. OpenCode's built-in message loader stops at the boundary, so the transform processes only the live tail instead of the full session history. Dramatically reduces per-turn overhead on long sessions. Enable with `experimental.compaction_markers: true`.
45+
46+
**User Memories** — historian extracts behavioral observations about you alongside its normal compartment output (communication style, expertise level, review focus, working patterns). Recurring observations are promoted by the dreamer to stable user memories that appear in all sessions via `<user-profile>`. Enable with `experimental.user_memories.enabled: true` (requires dreamer).
47+
4248
---
4349

4450
## Get Started
@@ -253,11 +259,20 @@ An optional background agent that maintains memory quality overnight:
253259
- **Improve** — rewrite verbose memories into terse operational form
254260
- **Maintain docs** — update ARCHITECTURE.md and STRUCTURE.md from codebase changes
255261
- **Evaluate smart notes** — check pending smart note conditions and surface ready notes
262+
- **Review user memories** (experimental) — promote recurring user behavior observations to stable memories
256263

257264
The dreamer runs during a configurable schedule window and creates ephemeral OpenCode child sessions for each task. Since it runs during idle time (typically overnight), it works well with local models — even slower ones like `ollama/mlx-qwen3.5-27b-claude-4.6-opus-reasoning-distilled` are fine since there's no user waiting.
258265

259266
When dreamer is enabled, ARCHITECTURE.md and STRUCTURE.md are automatically injected into the agent's system prompt (configurable via `inject_docs`). Content is cached per-session and refreshed on cache-busting passes.
260267

268+
### User Memories (Experimental)
269+
270+
When `experimental.user_memories.enabled` is set, historian extracts behavioral observations about the user alongside its normal compartment output — things like communication style, expertise level, review focus, and working patterns. These go into a candidate pool.
271+
272+
During dreamer runs, a dedicated review pass checks candidates for recurring patterns across sessions. Observations that appear at least `promotion_threshold` times (default 3) are promoted to stable user memories and injected into all sessions via `<user-profile>` in the system prompt.
273+
274+
Stable user memories are visible and manageable in the dashboard's User Memories page. Requires dreamer to be enabled for the promotion step.
275+
261276
### TUI Sidebar
262277

263278
When running in OpenCode's terminal UI, Magic Context shows a live sidebar panel with:
@@ -322,9 +337,8 @@ All durable states live in a local SQLite database. If the database can't be ope
322337
| `source_contents` | Raw content snapshots for persisted reductions |
323338
| `compartments` | Historian-produced structured history blocks |
324339
| `session_facts` | Categorized durable facts from historian runs |
325-
| `session_notes` | Session-scoped `ctx_note` content |
340+
| `notes` | Unified session notes and project-scoped smart notes |
326341
| `session_meta` | Per-session state — usage, nudge flags, anchors |
327-
| `smart_notes` | Project-scoped smart notes with surface conditions |
328342
| `memories` | Cross-session persistent memories |
329343
| `memory_embeddings` | Embedding vectors for semantic search |
330344
| `dream_state` | Dreamer lease locking and task progress |
@@ -335,6 +349,8 @@ All durable states live in a local SQLite database. If the database can't be ope
335349
| `message_history_index` | Tracks last indexed ordinal per session for incremental FTS population |
336350
| `recomp_compartments` | Staging for `/ctx-recomp` partial progress |
337351
| `recomp_facts` | Staging for `/ctx-recomp` partial progress |
352+
| `user_memory_candidates` | User behavior observations from historian (experimental) |
353+
| `user_memories` | Promoted stable user memories injected into all sessions (experimental) |
338354

339355
---
340356

STRUCTURE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
**`src/features/`:**
5252
- Purpose: Group reusable subsystem logic by feature.
5353
- Contains: Magic-context services, dreamer runtime, sidekick support, storage, scheduler, tagger, and built-in commands.
54-
- Key files: `src/features/magic-context/storage-db.ts`, `src/features/magic-context/storage-meta-persisted.ts`, `src/features/magic-context/dreamer/runner.ts`, `src/features/magic-context/memory/storage-memory.ts`, `src/features/builtin-commands/commands.ts`
54+
- Key files: `src/features/magic-context/storage-db.ts`, `src/features/magic-context/storage-meta-persisted.ts`, `src/features/magic-context/dreamer/runner.ts`, `src/features/magic-context/memory/storage-memory.ts`, `src/features/magic-context/user-memory/storage-user-memory.ts`, `src/features/builtin-commands/commands.ts`
5555

5656
**`src/tools/`:**
5757
- Purpose: Define the agent-facing tool surface.

packages/dashboard/src-tauri/src/commands.rs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,22 @@ pub fn get_session_facts(
106106
pub fn get_session_notes(
107107
state: State<'_, AppState>,
108108
session_id: String,
109-
) -> Result<Vec<db::SessionNote>, String> {
109+
) -> Result<Vec<db::Note>, String> {
110110
let path = state.get_db_path()?;
111111
let conn = db::open_readonly(&path).map_err(|e| e.to_string())?;
112112
db::get_session_notes(&conn, &session_id).map_err(|e| e.to_string())
113113
}
114114

115+
#[tauri::command]
116+
pub fn get_smart_notes(
117+
state: State<'_, AppState>,
118+
project_path: String,
119+
) -> Result<Vec<db::Note>, String> {
120+
let path = state.get_db_path()?;
121+
let conn = db::open_readonly(&path).map_err(|e| e.to_string())?;
122+
db::get_smart_notes(&conn, &project_path).map_err(|e| e.to_string())
123+
}
124+
115125
#[tauri::command]
116126
pub fn get_session_meta(
117127
state: State<'_, AppState>,
@@ -349,6 +359,57 @@ pub async fn test_embedding_endpoint(
349359
}
350360
}
351361

362+
// ── User Memory commands ────────────────────────────────────
363+
364+
#[tauri::command]
365+
pub fn get_user_memories(
366+
state: State<'_, AppState>,
367+
status: Option<String>,
368+
) -> Result<Vec<db::UserMemory>, String> {
369+
let path = state.get_db_path()?;
370+
let conn = db::open_readonly(&path).map_err(|e| e.to_string())?;
371+
db::get_user_memories(&conn, status.as_deref()).map_err(|e| e.to_string())
372+
}
373+
374+
#[tauri::command]
375+
pub fn get_user_memory_candidates(
376+
state: State<'_, AppState>,
377+
) -> Result<Vec<db::UserMemoryCandidate>, String> {
378+
let path = state.get_db_path()?;
379+
let conn = db::open_readonly(&path).map_err(|e| e.to_string())?;
380+
db::get_user_memory_candidates(&conn).map_err(|e| e.to_string())
381+
}
382+
383+
#[tauri::command]
384+
pub fn dismiss_user_memory(
385+
state: State<'_, AppState>,
386+
id: i64,
387+
) -> Result<(), String> {
388+
let path = state.get_db_path()?;
389+
let conn = db::open_readwrite(&path).map_err(|e| e.to_string())?;
390+
db::dismiss_user_memory(&conn, id).map_err(|e| e.to_string())
391+
}
392+
393+
#[tauri::command]
394+
pub fn delete_user_memory(
395+
state: State<'_, AppState>,
396+
id: i64,
397+
) -> Result<(), String> {
398+
let path = state.get_db_path()?;
399+
let conn = db::open_readwrite(&path).map_err(|e| e.to_string())?;
400+
db::delete_user_memory(&conn, id).map_err(|e| e.to_string())
401+
}
402+
403+
#[tauri::command]
404+
pub fn delete_user_memory_candidate(
405+
state: State<'_, AppState>,
406+
id: i64,
407+
) -> Result<(), String> {
408+
let path = state.get_db_path()?;
409+
let conn = db::open_readwrite(&path).map_err(|e| e.to_string())?;
410+
db::delete_user_memory_candidate(&conn, id).map_err(|e| e.to_string())
411+
}
412+
352413
// ── Health commands ─────────────────────────────────────────
353414

354415
#[tauri::command]

0 commit comments

Comments
 (0)