Skip to content

Commit 74e9cbc

Browse files
committed
feat: deterministic hook saves — zero data loss via silent Python API
Adds a `hook_silent_save` mode (default `true` in new installs) where the stop and precompact hooks write diary entries directly via the Python API — no AI block, no MCP tool roundtrip, no possibility of the AI forgetting or ignoring the save instruction. **Two modes, controlled by `hook_silent_save` in `~/.mempalace/config.json`:** 1. **Silent mode** (default): Direct call to `tool_diary_write()`. Plain text, no AI involved, deterministic. Save marker advances only after the write is confirmed, so mid-save failures do not lose exchanges. Shows `"✦ N memories woven into the palace"` as a systemMessage notification so the user knows the save fired. 2. **Block mode** (legacy): Returns `{"decision": "block"}` asking the AI to call the MCP tool chain. Non-deterministic — the AI may ignore, summarize lossy, or fail. Kept for backward compatibility. **Extras rolled in:** - Block reasons name "MemPalace" explicitly and instruct the AI not to write to Claude Code's native auto-memory (.md files) — prevents the two memory systems from stepping on each other. - Codex transcript handling (`event_msg` payloads) in `_count_human_messages` + `_extract_recent_messages`. - Tightened stopword leak in diary summaries; docstring polish; test hermeticity fixes (per-test `STATE_DIR` patching). **Tests:** hooks_cli tests cover silent-save path, save-marker advancement after confirmed write only, and systemMessage formatting. Rebased fresh on upstream/develop. Only touches files germane to the feature (hooks_cli.py, tests, hooks/README.md, HOOKS_TUTORIAL.md) — stale fork-local `.sh` wrapper and plugin manifest changes dropped.
1 parent 1b00f93 commit 74e9cbc

4 files changed

Lines changed: 447 additions & 77 deletions

File tree

examples/HOOKS_TUTORIAL.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,27 @@ Add this to your configuration file to enable automatic background saving:
2525
}
2626
]
2727
}
28-
}
28+
}
29+
```
30+
31+
### 3. What changed (v3.1.0+)
32+
33+
Both hooks now have **two-layer capture**:
34+
35+
1. **Auto-mine**: Before blocking the AI, the hook runs the normalizer on the JSONL transcript and upserts chunks directly into the palace. This captures raw tool output (Bash results, search findings, build errors) that the AI would otherwise summarize away.
36+
37+
2. **Updated reason messages**: The block reason now explicitly tells the AI to save tool output verbatim — not just topics and decisions.
38+
39+
### 4. Backfill past conversations (one-time)
40+
41+
The hooks capture conversations going forward, but you probably have months of past sessions. Run this once to mine them all:
42+
43+
```bash
44+
mempalace mine ~/.claude/projects/ --mode convos
45+
```
46+
47+
### 5. Configuration
48+
49+
- **`SAVE_INTERVAL=15`** — How many human messages between saves
50+
- **`MEMPALACE_PYTHON`** — Python interpreter with mempalace + chromadb. Auto-detects: env var → repo venv → system python3
51+
- **`MEMPAL_DIR`** — Optional directory for auto-ingest via `mempalace mine`

hooks/README.md

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ These hook scripts make MemPalace save automatically. No manual "save" commands
66

77
| Hook | When It Fires | What Happens |
88
|------|--------------|-------------|
9-
| **Save Hook** | Every 15 human messages | Blocks the AI, tells it to save key topics/decisions/quotes to the palace |
10-
| **PreCompact Hook** | Right before context compaction | Emergency save — forces the AI to save EVERYTHING before losing context |
9+
| **Save Hook** | Every 15 human messages | Auto-mines transcript (tool output included), then blocks the AI to save topics/decisions/quotes |
10+
| **PreCompact Hook** | Right before context compaction | Auto-mines transcript, then emergency save — forces the AI to save EVERYTHING before losing context |
1111

12-
The AI does the actual filing — it knows the conversation context, so it classifies memories into the right wings/halls/closets. The hooks just tell it WHEN to save.
12+
**Two-layer capture:** Hooks auto-mine the JSONL transcript directly into the palace (capturing raw tool output — Bash results, search findings, build errors). They also block the AI with a reason message telling it to save verbatim tool output and key context. Belt and suspenders — tool output gets stored even if the AI summarizes instead of quoting.
1313

1414
## Install — Claude Code
1515

@@ -68,6 +68,7 @@ Edit `mempal_save_hook.sh` to change:
6868
- **`SAVE_INTERVAL=15`** — How many human messages between saves. Lower = more frequent saves, higher = less interruption.
6969
- **`STATE_DIR`** — Where hook state is stored (defaults to `~/.mempalace/hook_state/`)
7070
- **`MEMPAL_DIR`** — Optional. Set to a conversations directory to auto-run `mempalace mine <dir>` on each save trigger. Leave blank (default) to let the AI handle saving via the block reason message.
71+
- **`MEMPALACE_PYTHON`** — Optional env var. Python interpreter with mempalace + chromadb installed. Auto-detects: `MEMPALACE_PYTHON` env var → repo `venv/bin/python3` → system `python3`. Set this if your venv is in a non-standard location.
7172

7273
### mempalace CLI
7374

@@ -91,15 +92,19 @@ User sends message → AI responds → Claude Code fires Stop hook
9192
9293
┌─── < 15 since last save ──→ echo "{}" (let AI stop)
9394
94-
└─── ≥ 15 since last save ──→ {"decision": "block", "reason": "save..."}
95-
96-
AI saves to palace
97-
98-
AI tries to stop again
99-
100-
stop_hook_active = true
101-
102-
Hook sees flag → echo "{}" (let it through)
95+
└─── ≥ 15 since last save
96+
97+
Auto-mine transcript → palace (tool output captured)
98+
99+
{"decision": "block", "reason": "save tool output verbatim..."}
100+
101+
AI saves to palace (topics, decisions, quotes)
102+
103+
AI tries to stop again
104+
105+
stop_hook_active = true
106+
107+
Hook sees flag → echo "{}" (let it through)
103108
```
104109

105110
The `stop_hook_active` flag prevents infinite loops: block once → AI saves → tries to stop → flag is true → we let it through.
@@ -109,14 +114,18 @@ The `stop_hook_active` flag prevents infinite loops: block once → AI saves →
109114
```
110115
Context window getting full → Claude Code fires PreCompact
111116
112-
Hook ALWAYS blocks
117+
Find transcript (from input or session_id lookup)
118+
119+
Auto-mine transcript → palace (tool output captured)
120+
121+
{"decision": "block", "reason": "save tool output verbatim..."}
113122
114123
AI saves everything
115124
116125
Compaction proceeds
117126
```
118127

119-
No counting needed — compaction always warrants a save.
128+
No counting needed — compaction always warrants a save. The auto-mine captures raw tool output before the AI gets a chance to summarize it away.
120129

121130
## Debugging
122131

@@ -150,6 +159,23 @@ Resolution priority: `$MEMPAL_PYTHON` (if set and executable) → `$(command -v
150159

151160
Note: the `mempalace mine` auto-ingest runs via the `mempalace` CLI, so that command also needs to be on the hook's `PATH`. Installing with `pipx install mempalace` or `uv tool install mempalace` puts it on a stable global location; otherwise extend the hook environment's `PATH` to include your venv's `bin/`.
152161

162+
## Backfill Past Conversations
163+
164+
The hooks only capture conversations going forward. To mine **past** Claude Code sessions into your palace, run a one-time backfill:
165+
166+
```bash
167+
mempalace mine ~/.claude/projects/ --mode convos
168+
```
169+
170+
This scans all JSONL transcripts from previous sessions and files them into the `conversations` wing. On a typical developer machine with months of history, this can yield 50K–200K drawers.
171+
172+
For Codex CLI sessions:
173+
```bash
174+
mempalace mine ~/.codex/sessions/ --mode convos
175+
```
176+
177+
This only needs to be done once — after that, the hooks auto-mine each session as you go.
178+
153179
## Cost
154180

155181
**Zero extra tokens.** The hooks notify the AI that saves happened in the background — the AI doesn't need to write anything in the chat. All filing is handled automatically. Previous versions asked the AI to write diary entries and drawer content in the chat window, which cost ~$1/session in retransmitted tokens.

0 commit comments

Comments
 (0)