@tznthou/ccrecall 0.4.7 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -57
- package/README_ZH.md +73 -58
- package/dist/api/routes.d.ts +0 -10
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +4 -354
- package/dist/api/routes.js.map +1 -1
- package/dist/api/server.js +3 -3
- package/dist/api/server.js.map +1 -1
- package/dist/core/database.d.ts +14 -49
- package/dist/core/database.d.ts.map +1 -1
- package/dist/core/database.js +113 -225
- package/dist/core/database.js.map +1 -1
- package/dist/core/indexer.d.ts.map +1 -1
- package/dist/core/indexer.js +0 -1
- package/dist/core/indexer.js.map +1 -1
- package/dist/core/maintenance-coordinator.d.ts +3 -9
- package/dist/core/maintenance-coordinator.d.ts.map +1 -1
- package/dist/core/maintenance-coordinator.js +4 -24
- package/dist/core/maintenance-coordinator.js.map +1 -1
- package/dist/core/summarizer.d.ts.map +1 -1
- package/dist/core/summarizer.js +0 -22
- package/dist/core/summarizer.js.map +1 -1
- package/dist/core/types.d.ts +0 -87
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/index.js +0 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/journal.d.ts +0 -11
- package/dist/cli/journal.d.ts.map +0 -1
- package/dist/cli/journal.js +0 -111
- package/dist/cli/journal.js.map +0 -1
- package/dist/core/lint.d.ts +0 -17
- package/dist/core/lint.d.ts.map +0 -1
- package/dist/core/lint.js +0 -51
- package/dist/core/lint.js.map +0 -1
- package/dist/core/outcome-extractor.d.ts +0 -6
- package/dist/core/outcome-extractor.d.ts.map +0 -1
- package/dist/core/outcome-extractor.js +0 -39
- package/dist/core/outcome-extractor.js.map +0 -1
- package/dist/core/outcome-scorer.d.ts +0 -8
- package/dist/core/outcome-scorer.d.ts.map +0 -1
- package/dist/core/outcome-scorer.js +0 -103
- package/dist/core/outcome-scorer.js.map +0 -1
package/README.md
CHANGED
|
@@ -11,13 +11,9 @@ A local memory service for Claude Code — indexes your conversation history, re
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
> 📐 **v0.
|
|
14
|
+
> 📐 **v0.5.0 — The knife-field release: one pipeline, nothing dead**
|
|
15
15
|
>
|
|
16
|
-
>
|
|
17
|
-
>
|
|
18
|
-
> The rule scorer is no longer on the persistence gate; it informs trust grade and promotion priority instead. Background reasoning at [issue #21](https://github.com/tznthou/ccRecall/issues/21).
|
|
19
|
-
>
|
|
20
|
-
> Pre-0.3.0 memories stay queryable as-is. The v22 schema migration runs automatically on first daemon startup; existing rows are not touched. New harvest goes to journal from now on.
|
|
16
|
+
> Post-session extraction via Haiku (~$0.001/session) has been the primary memory write path since v0.4.1, hardened across v0.4.2–v0.4.8. v0.5.0 removes everything that pipeline made obsolete — the journal review queue (zero promotions in its entire history), the rule scorer, checkpoints, and 8 caller-less HTTP endpoints — and rebuilds the replay-dedup registry as compact 64-bit hashes (database: 114MB → 42MB measured). Manual `recall_save` remains available for in-session saves.
|
|
21
17
|
|
|
22
18
|
---
|
|
23
19
|
|
|
@@ -41,12 +37,11 @@ ccRecall is the "memory" counterpart to [ccRewind](https://github.com/tznthou/cc
|
|
|
41
37
|
| **FTS5 full-text search** | Sub-100ms keyword search across all conversation history, fast enough for hook injection |
|
|
42
38
|
| **CJK / mixed-script search** | Trigram tokenizer indexes Chinese / Japanese / Korean text; per-token AND LIKE fallback handles short queries like `UI 記憶` that trigram alone can't match |
|
|
43
39
|
| **Incremental indexing** | Only re-indexes sessions that changed (mtime diffing), handles resumed sessions via UUID dedup |
|
|
44
|
-
| **Metacognition** | `knowledge_map` aggregates topic mentions from sessions + memories. Depth derived from mention count (shallow / medium / deep). Exposed via
|
|
40
|
+
| **Metacognition** | `knowledge_map` aggregates topic mentions from sessions + memories. Depth derived from mention count (shallow / medium / deep). Exposed via MCP `recall_context` |
|
|
45
41
|
| **Forgetting curve** | Memories compress over time: raw → summary → one-liner → deleted. Confidence decays on unused memories. Background maintenance tick runs every 5 min |
|
|
46
|
-
| **Trust two-tier** (v0.3.0) | Hooks write to a low-trust `session_journal` (reviewable, never recalled directly); manual `recall_save` writes high-trust `memories`. Promote candidates with `ccmem promote <id>`; rejected entries auto-clear after 7 days |
|
|
47
42
|
| **Cross-project memory** (v0.4.1) | Memories surface across projects via topic intersection — if two projects share a `knowledge_map` topic, high-confidence memories from one appear in the other's startup injection (max 3 rows, confidence ≥ 0.8 gate) |
|
|
48
43
|
| **Watch mode** | chokidar-based JSONL watcher picks up new sessions within 2 s; periodic 10 min full-resync covers missed filesystem events |
|
|
49
|
-
| **Rescue reindex** | `/session/end`
|
|
44
|
+
| **Rescue reindex** | `/session/end` and `/session/last` both retry a reindex on miss, and `/session/last` staleness-gates via `notBefore` — no fresh-session race between the hook, the wrapper, and the daemon |
|
|
50
45
|
| **Auto-start (macOS)** | `ccmem install-daemon` registers a LaunchAgent so the service stays up across reboots |
|
|
51
46
|
| **Read-only** | Never modifies `~/.claude/` — only reads JSONL logs |
|
|
52
47
|
|
|
@@ -61,24 +56,64 @@ flowchart TB
|
|
|
61
56
|
end
|
|
62
57
|
|
|
63
58
|
subgraph Core["ccRecall Service (port 7749)"]
|
|
59
|
+
Watcher["Watcher<br/>chokidar, 2 s debounce"]
|
|
64
60
|
Scanner["Scanner<br/>find JSONL files"]
|
|
65
61
|
Parser["Parser<br/>parse conversations"]
|
|
66
62
|
Summarizer["Summarizer<br/>rule-based extraction"]
|
|
67
|
-
DB["SQLite + FTS5<br/>index & search"]
|
|
68
|
-
API["HTTP API<br/>
|
|
63
|
+
DB[("SQLite + FTS5<br/>index & search")]
|
|
64
|
+
API["HTTP API<br/>5 endpoints"]
|
|
69
65
|
end
|
|
70
66
|
|
|
71
|
-
subgraph Consumers["
|
|
67
|
+
subgraph Consumers["Consumers"]
|
|
72
68
|
Hook["Claude Code Hooks<br/>SessionStart / SessionEnd"]
|
|
69
|
+
Wrapper["Extraction wrapper<br/>post-session Haiku pass"]
|
|
73
70
|
MCP["MCP Server<br/>recall_query / recall_save"]
|
|
74
71
|
end
|
|
75
72
|
|
|
76
|
-
JSONL --> Scanner --> Parser --> Summarizer --> DB
|
|
77
|
-
DB
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
JSONL --> Watcher --> Scanner --> Parser --> Summarizer --> DB
|
|
74
|
+
DB <--> API
|
|
75
|
+
Hook -->|"inject / end-confirm"| API
|
|
76
|
+
Wrapper -->|"GET /session/last"| API
|
|
77
|
+
MCP <-->|"shared SQLite via WAL"| DB
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Arrows point in the direction of the call: hooks and the extraction wrapper talk to the daemon over HTTP, while the MCP server is a separate process that opens the same SQLite file directly (WAL mode) — it never goes through the HTTP API.
|
|
81
|
+
|
|
82
|
+
### Session lifecycle
|
|
83
|
+
|
|
84
|
+
How the two timing-sensitive ends of a session actually flow:
|
|
85
|
+
|
|
86
|
+
```mermaid
|
|
87
|
+
sequenceDiagram
|
|
88
|
+
participant CC as Claude Code
|
|
89
|
+
participant H as Hooks
|
|
90
|
+
participant R as ccRecall
|
|
91
|
+
participant W as Wrapper
|
|
92
|
+
participant X as Haiku extractor
|
|
93
|
+
|
|
94
|
+
rect rgb(235, 244, 255)
|
|
95
|
+
Note over CC,R: Session start
|
|
96
|
+
CC->>H: SessionStart
|
|
97
|
+
H->>R: GET /memory/startup
|
|
98
|
+
R-->>H: 3-tier memory pick (< 300 tokens)
|
|
99
|
+
H-->>CC: inject into context
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
Note over CC,R: During the session, the watcher<br/>reindexes JSONL writes (2 s debounce)
|
|
103
|
+
|
|
104
|
+
rect rgb(255, 244, 235)
|
|
105
|
+
Note over CC,X: Session end
|
|
106
|
+
CC->>H: SessionEnd
|
|
107
|
+
H->>R: POST /session/end (index confirm + rescue)
|
|
108
|
+
W->>R: GET /session/last?notBefore=launch_ts
|
|
109
|
+
R-->>W: sessionId (staleness-gated, subagent-filtered)
|
|
110
|
+
W->>X: text-only transcript
|
|
111
|
+
X->>R: recall_save × 0–5 (via MCP)
|
|
112
|
+
end
|
|
80
113
|
```
|
|
81
114
|
|
|
115
|
+
The `notBefore` gate keeps a stale session from being extracted twice; the subagent filter keeps an Agent-tool side-session from shadowing the real one. Both guards exist because the wrapper queries within seconds of session close, racing the watcher.
|
|
116
|
+
|
|
82
117
|
---
|
|
83
118
|
|
|
84
119
|
## Tech Stack
|
|
@@ -90,7 +125,7 @@ flowchart TB
|
|
|
90
125
|
| FTS5 | Full-text search | Built into SQLite, trigram tokenizer with LIKE fallback for short CJK / mixed-script queries |
|
|
91
126
|
| Native `http` | HTTP server | No Express — minimal surface, localhost only |
|
|
92
127
|
| chokidar | Filesystem watcher | Cross-platform JSONL change detection with 2 s debounce + single-flight |
|
|
93
|
-
| vitest | Testing |
|
|
128
|
+
| vitest | Testing | 629 tests across 39 files, integration-style |
|
|
94
129
|
| `@modelcontextprotocol/sdk` | MCP server | stdio transport, shared SQLite via WAL |
|
|
95
130
|
|
|
96
131
|
---
|
|
@@ -132,20 +167,17 @@ curl "http://127.0.0.1:7749/memory/query?q=authentication&limit=5"
|
|
|
132
167
|
|
|
133
168
|
## API Endpoints
|
|
134
169
|
|
|
135
|
-
|
|
170
|
+
Five endpoints, each with a live caller — v0.5.0 removed the other eight
|
|
171
|
+
(`/journal/*`, `/memory/save`, `/memory/context`, `/metacognition/check`,
|
|
172
|
+
`/session/checkpoint`, `/lint/warnings`); they now return 404.
|
|
173
|
+
|
|
174
|
+
| Endpoint | Method | Description | Caller |
|
|
136
175
|
|----------|--------|-------------|--------|
|
|
137
|
-
| `/health` | GET | Service health + DB stats + integrity check status
|
|
138
|
-
| `/memory/
|
|
139
|
-
| `/memory/
|
|
140
|
-
| `/session/end` | POST |
|
|
141
|
-
| `/
|
|
142
|
-
| `/journal/promote` | POST | Atomically promote a journal entry into `memories` | Live (v0.3.0) |
|
|
143
|
-
| `/journal/reject` | POST | Soft-delete a journal entry (cleared after 7-day TTL by decay sweep) | Live (v0.3.0) |
|
|
144
|
-
| `/memory/context?session_id=...` | GET | Session context lookup | Stub |
|
|
145
|
-
| `/metacognition/check?projectId=...[&topic=...]` | GET | Knowledge map: summary (top/recent/stale topics + counts) or topic detail (memories + related topics) | Live |
|
|
146
|
-
| `/session/checkpoint` | POST | Mid-session snapshot into dedicated `session_checkpoints` table (not harvested as memory) | Live |
|
|
147
|
-
| `/lint/warnings` | GET | Lint report: orphan (session deleted) + stale (low-confidence, long-idle) memory warnings | Live |
|
|
148
|
-
| `/session/last?cwd=...` | GET | Most recent session metadata for a project path (used by post-session extraction wrapper) | Live (v0.4.1) |
|
|
176
|
+
| `/health` | GET | Service health + DB stats + integrity check status | CLI, extraction wrapper |
|
|
177
|
+
| `/memory/startup?project=...` | GET | SessionStart-tier retrieval: cold + recent-confidence + FTS fallback, token-budgeted | SessionStart hook |
|
|
178
|
+
| `/memory/query?q=...&limit=...&project=...` | GET | FTS5 search across memories with optional project filter | SessionStart hook (keyword tier) |
|
|
179
|
+
| `/session/end` | POST | Confirm the just-ended session is indexed (rescue reindex on miss) | SessionEnd hook |
|
|
180
|
+
| `/session/last?cwd=...` | GET | Most recent session metadata for a project path (staleness-gated via `notBefore`) | Extraction wrapper |
|
|
149
181
|
|
|
150
182
|
## MCP Tools
|
|
151
183
|
|
|
@@ -197,21 +229,10 @@ Daemon and hook lifecycle (macOS):
|
|
|
197
229
|
| `ccmem uninstall-daemon` | Stop and remove the LaunchAgent |
|
|
198
230
|
| `ccmem install-hooks` | Merge SessionStart / SessionEnd entries into `~/.claude/settings.json` |
|
|
199
231
|
| `ccmem uninstall-hooks` | Remove ccRecall's hook entries (other hooks untouched) |
|
|
232
|
+
| `ccmem cleanup --orphans` | List (and with `--yes`, delete) memories whose session rows are gone |
|
|
200
233
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
| Command | Purpose |
|
|
204
|
-
|---------|---------|
|
|
205
|
-
| `ccmem promote <id>` | Promote a journal entry into `memories`. Optional `--type` (default `discovery`) and `--confidence` (default `0.7`) |
|
|
206
|
-
| `ccmem reject <id>` | Soft-delete a journal entry; cleaned up after the 7-day TTL by the decay sweep |
|
|
207
|
-
|
|
208
|
-
Surface pending candidates:
|
|
209
|
-
|
|
210
|
-
```bash
|
|
211
|
-
curl http://127.0.0.1:7749/journal/pending
|
|
212
|
-
# or just check the count
|
|
213
|
-
curl http://127.0.0.1:7749/health | jq .journalPendingCount
|
|
214
|
-
```
|
|
234
|
+
`ccmem promote` / `ccmem reject` were removed in v0.5.0 along with the
|
|
235
|
+
journal pipeline they reviewed.
|
|
215
236
|
|
|
216
237
|
---
|
|
217
238
|
|
|
@@ -232,8 +253,6 @@ ccRecall lives alongside Claude Code's built-in auto memory (`~/.claude/projects
|
|
|
232
253
|
|
|
233
254
|
ccRecall's value is the long tail that auto memory can't cover (nobody hand-curates 500 sessions of notes). If Claude defaults to both, auto memory wins because it's already loaded and curated. ccRecall earns its keep when the curated index misses.
|
|
234
255
|
|
|
235
|
-
**Within ccRecall there's now a second trust split** (since v0.3.0). The SessionEnd hook writes to a low-trust `session_journal` table that `recall_query` does **not** read. Promote a candidate to high-trust `memories` with `ccmem promote <id>` to make it queryable. Manual `recall_save` skips the journal entirely — it writes straight to memories. The point is to let the harvester record broadly while keeping recall results clean.
|
|
236
|
-
|
|
237
256
|
---
|
|
238
257
|
|
|
239
258
|
## Running as a service (macOS)
|
|
@@ -265,7 +284,7 @@ Full manual-install, troubleshooting, and uninstall docs:
|
|
|
265
284
|
[docs/launchd.md](docs/launchd.md).
|
|
266
285
|
|
|
267
286
|
Linux/Windows equivalents (systemd unit, Windows service) are planned for
|
|
268
|
-
|
|
287
|
+
a future release. For now, run under `nohup` or your process manager of choice.
|
|
269
288
|
|
|
270
289
|
---
|
|
271
290
|
|
|
@@ -315,8 +334,7 @@ ccRecall/
|
|
|
315
334
|
│ │ ├── indexer.ts # Indexing pipeline orchestrator
|
|
316
335
|
│ │ ├── memory-service.ts # Memory lifecycle (touch / delete / update)
|
|
317
336
|
│ │ ├── compression.ts # L0→L1→L2→delete state machine
|
|
318
|
-
│ │ ├──
|
|
319
|
-
│ │ ├── maintenance-coordinator.ts # Background compression tick + journal decay sweep
|
|
337
|
+
│ │ ├── maintenance-coordinator.ts # Background compression tick
|
|
320
338
|
│ │ ├── watcher.ts # chokidar JSONL watcher (Phase 4e)
|
|
321
339
|
│ │ └── log-safe.ts # scrubErrorMessage — log-injection defence
|
|
322
340
|
│ ├── api/
|
|
@@ -326,8 +344,7 @@ ccRecall/
|
|
|
326
344
|
│ │ ├── server.ts # MCP stdio server entry (shebang bin)
|
|
327
345
|
│ │ └── tools.ts # recall_query + recall_context + recall_save
|
|
328
346
|
│ ├── cli/
|
|
329
|
-
│ │
|
|
330
|
-
│ │ └── journal.ts # ccmem promote / reject (since v0.3.0)
|
|
347
|
+
│ │ └── daemon.ts # install-daemon / uninstall-daemon (macOS)
|
|
331
348
|
│ └── index.ts # HTTP entry point + subcommand dispatch
|
|
332
349
|
├── hooks/
|
|
333
350
|
│ ├── session-start.mjs # Inject memories on SessionStart (stdout)
|
|
@@ -337,11 +354,10 @@ ccRecall/
|
|
|
337
354
|
│ ├── tutorial.md # End-user walkthrough (install → MCP → usage)
|
|
338
355
|
│ ├── architecture.md # Daemon design rationale (contributor-oriented)
|
|
339
356
|
│ └── launchd.md # macOS LaunchAgent install/troubleshoot
|
|
340
|
-
├── tests/ #
|
|
357
|
+
├── tests/ # 524 tests across 34 files (parser, scanner,
|
|
341
358
|
│ │ # summarizer, database, indexer, e2e, MCP,
|
|
342
359
|
│ │ # memories, hooks, watcher, CLI, migrations,
|
|
343
|
-
│ │ # FTS5 CJK edge cases, integrity monitor,
|
|
344
|
-
│ │ # session_journal DAO, promote+reject, sweep, ...)
|
|
360
|
+
│ │ # FTS5 CJK edge cases, integrity monitor, ...)
|
|
345
361
|
│ └── fixtures/ # Sample JSONL + shared test helpers
|
|
346
362
|
├── .mcp.json.example # MCP client config template
|
|
347
363
|
└── NOTICE / SECURITY.md / CONTRIBUTING.md / CODE_OF_CONDUCT.md
|
|
@@ -392,9 +408,9 @@ The real trigger was simpler: I kept re-explaining the same architecture to Clau
|
|
|
392
408
|
| Version | Theme | Status |
|
|
393
409
|
|---------|-------|--------|
|
|
394
410
|
| **v0.3.x** | Manual save, automatic recall — memories come from explicit `recall_save` calls; SessionStart hook and MCP tools inject them into future sessions | Released |
|
|
395
|
-
| **v0.4.
|
|
396
|
-
| **v0.
|
|
397
|
-
| **v0.5+** |
|
|
411
|
+
| **v0.4.x** | Post-session extraction via Haiku, cross-project memory via topic intersection, extraction pipeline hardening (race gates, compression integrity, subagent filtering, security) | Released |
|
|
412
|
+
| **v0.5.0** | The knife field: journal/scorer/harvester pipelines removed (zero promotions ever), endpoints 13 → 5, `message_uuids` dual-hash rebuild (DB 114MB → 42MB) | Released |
|
|
413
|
+
| **v0.5+** | CJK-aware topic extraction (`\p{Script=Han}` tokenizer + Chinese stopwords), L1 keyword injection, README → manifesto | Planned |
|
|
398
414
|
|
|
399
415
|
Tracked in [GitHub Issues](https://github.com/tznthou/ccRecall/issues).
|
|
400
416
|
|
package/README_ZH.md
CHANGED
|
@@ -11,13 +11,9 @@ Claude Code 的本地記憶服務——索引你的對話歷史,按需召回
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
> 📐 **v0.
|
|
14
|
+
> 📐 **v0.5.0 — 砍刀場版本:一條管線,零死代碼**
|
|
15
15
|
>
|
|
16
|
-
>
|
|
17
|
-
>
|
|
18
|
-
> Rule scorer 不再卡在 persistence gate;改成提供 trust grade 與 promote 優先序。完整 rationale 見 [issue #21](https://github.com/tznthou/ccRecall/issues/21)。
|
|
19
|
-
>
|
|
20
|
-
> 0.3.0 之前的 memories 仍可正常查詢。v22 schema migration 在 daemon 第一次啟動時自動跑,既有 row 不會被動。新 harvest 從現在起寫 journal。
|
|
16
|
+
> Post-session extraction via Haiku(約 $0.001/session)自 v0.4.1 起就是主要記憶寫入路徑,v0.4.2–v0.4.8 逐版強化。v0.5.0 把這條管線淘汰掉的東西一次清完——journal 審閱佇列(史上零 promotion)、規則 scorer、checkpoints、8 個零 caller 的 HTTP endpoint——並把 replay 去重登記表重建為精巧的 64-bit hash(資料庫實測 114MB → 42MB)。Manual `recall_save` 仍可用於 session 中即時存記憶。
|
|
21
17
|
|
|
22
18
|
---
|
|
23
19
|
|
|
@@ -41,12 +37,11 @@ ccRecall 是 [ccRewind](https://github.com/tznthou/ccRewind)(對話回放 GUI
|
|
|
41
37
|
| **FTS5 全文搜尋** | 所有對話歷史的關鍵詞搜尋 <100ms,快到可以在 hook 中注入 |
|
|
42
38
|
| **CJK / 中英混合搜尋** | Trigram tokenizer 索引中日韓文字;短 token 查詢(如 `UI 記憶`)以 per-token AND LIKE fallback 補齊,避開 trigram 對 <3 字 token 的盲區 |
|
|
43
39
|
| **增量索引** | 只重新索引有變動的 session(mtime 比對),透過 UUID 去重處理接續 session |
|
|
44
|
-
| **元認知** | `knowledge_map` 聚合 session + memory 的主題提及。由 mention count 衍生深度(shallow / medium / deep)。透過
|
|
40
|
+
| **元認知** | `knowledge_map` 聚合 session + memory 的主題提及。由 mention count 衍生深度(shallow / medium / deep)。透過 MCP `recall_context` 暴露 |
|
|
45
41
|
| **遺忘曲線** | 記憶隨時間壓縮:原始→摘要→一行結論→刪除。未使用的記憶信心衰減。背景維護 tick 每 5 分鐘跑一次 |
|
|
46
|
-
| **Trust 二層**(v0.3.0) | Hook 寫入 low-trust `session_journal`(可審閱、不被 recall);manual `recall_save` 寫 high-trust `memories`。用 `ccmem promote <id>` 升級候選;reject 後 7 天 TTL 自動清 |
|
|
47
42
|
| **跨專案記憶**(v0.4.1) | 記憶透過 topic intersection 跨專案浮出——若兩個專案的 `knowledge_map` 有共同 topic,高信心記憶會出現在另一個專案的 startup injection(最多 3 條,confidence ≥ 0.8 gate) |
|
|
48
43
|
| **Watch mode** | 基於 chokidar 的 JSONL watcher 在 2 秒內偵測新 session;每 10 分鐘 full-resync 補救 FS 事件漏接 |
|
|
49
|
-
| **Rescue reindex** | `/session/end`
|
|
44
|
+
| **Rescue reindex** | `/session/end` 和 `/session/last` 都會在 miss 時重 index 再試一次,`/session/last` 另有 `notBefore` staleness gate——hook、wrapper、daemon 三方不會有 fresh-session race |
|
|
50
45
|
| **macOS 自動啟動** | `ccmem install-daemon` 安裝 LaunchAgent,重開機自動復原服務 |
|
|
51
46
|
| **純唯讀** | 絕不修改 `~/.claude/`——只讀取 JSONL 記錄 |
|
|
52
47
|
|
|
@@ -61,24 +56,64 @@ flowchart TB
|
|
|
61
56
|
end
|
|
62
57
|
|
|
63
58
|
subgraph Core["ccRecall 服務(port 7749)"]
|
|
59
|
+
Watcher["Watcher<br/>chokidar,2 秒 debounce"]
|
|
64
60
|
Scanner["Scanner<br/>掃描 JSONL 檔案"]
|
|
65
61
|
Parser["Parser<br/>解析對話"]
|
|
66
62
|
Summarizer["Summarizer<br/>規則式萃取"]
|
|
67
|
-
DB["SQLite + FTS5<br/>索引與搜尋"]
|
|
68
|
-
API["HTTP API<br/>
|
|
63
|
+
DB[("SQLite + FTS5<br/>索引與搜尋")]
|
|
64
|
+
API["HTTP API<br/>5 個端點"]
|
|
69
65
|
end
|
|
70
66
|
|
|
71
|
-
subgraph Consumers["
|
|
67
|
+
subgraph Consumers["使用端"]
|
|
72
68
|
Hook["Claude Code Hooks<br/>SessionStart / SessionEnd"]
|
|
69
|
+
Wrapper["Extraction wrapper<br/>session 結束後的 Haiku 抽取"]
|
|
73
70
|
MCP["MCP Server<br/>recall_query / recall_save"]
|
|
74
71
|
end
|
|
75
72
|
|
|
76
|
-
JSONL --> Scanner --> Parser --> Summarizer --> DB
|
|
77
|
-
DB
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
JSONL --> Watcher --> Scanner --> Parser --> Summarizer --> DB
|
|
74
|
+
DB <--> API
|
|
75
|
+
Hook -->|"注入 / 收尾確認"| API
|
|
76
|
+
Wrapper -->|"GET /session/last"| API
|
|
77
|
+
MCP <-->|"WAL 模式共用 SQLite"| DB
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
箭頭方向代表「誰呼叫誰」:hooks 和 extraction wrapper 走 HTTP 找 daemon;MCP server 則是獨立進程,直接開同一個 SQLite 檔(WAL 模式)——完全不經過 HTTP API。
|
|
81
|
+
|
|
82
|
+
### Session 生命週期
|
|
83
|
+
|
|
84
|
+
一個 session 頭尾兩端(都是跟時間賽跑的環節)實際怎麼跑:
|
|
85
|
+
|
|
86
|
+
```mermaid
|
|
87
|
+
sequenceDiagram
|
|
88
|
+
participant CC as Claude Code
|
|
89
|
+
participant H as Hooks
|
|
90
|
+
participant R as ccRecall
|
|
91
|
+
participant W as Wrapper
|
|
92
|
+
participant X as Haiku 抽取器
|
|
93
|
+
|
|
94
|
+
rect rgb(235, 244, 255)
|
|
95
|
+
Note over CC,R: Session 開始
|
|
96
|
+
CC->>H: SessionStart
|
|
97
|
+
H->>R: GET /memory/startup
|
|
98
|
+
R-->>H: 三層記憶挑選(< 300 tokens)
|
|
99
|
+
H-->>CC: 注入 context
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
Note over CC,R: Session 進行中,watcher 持續<br/>增量索引 JSONL(2 秒 debounce)
|
|
103
|
+
|
|
104
|
+
rect rgb(255, 244, 235)
|
|
105
|
+
Note over CC,X: Session 結束
|
|
106
|
+
CC->>H: SessionEnd
|
|
107
|
+
H->>R: POST /session/end(確認已索引 + rescue)
|
|
108
|
+
W->>R: GET /session/last?notBefore=launch_ts
|
|
109
|
+
R-->>W: sessionId(過 staleness gate、濾掉 subagent)
|
|
110
|
+
W->>X: 純文字 transcript
|
|
111
|
+
X->>R: recall_save × 0–5 筆(經 MCP)
|
|
112
|
+
end
|
|
80
113
|
```
|
|
81
114
|
|
|
115
|
+
`notBefore` gate 擋掉「抽到上一個舊 session」的重複抽取;subagent 過濾擋掉 Agent tool 側線 session 蓋台。這兩道防線存在的原因是同一個:wrapper 在 session 關閉後幾秒內就查詢,跟 watcher 的索引在賽跑。
|
|
116
|
+
|
|
82
117
|
---
|
|
83
118
|
|
|
84
119
|
## 技術棧
|
|
@@ -90,7 +125,7 @@ flowchart TB
|
|
|
90
125
|
| FTS5 | 全文搜尋 | SQLite 內建、trigram tokenizer,短 token / 中英混合查詢透過 LIKE fallback 補齊 |
|
|
91
126
|
| 原生 `http` | HTTP 伺服器 | 不用 Express——最小表面積、僅 localhost |
|
|
92
127
|
| chokidar | 檔案系統 watcher | 跨平台 JSONL 變動偵測,2 秒 debounce + single-flight |
|
|
93
|
-
| vitest | 測試 |
|
|
128
|
+
| vitest | 測試 | 629 個測試(39 檔案)、整合式風格 |
|
|
94
129
|
| `@modelcontextprotocol/sdk` | MCP server | stdio transport,透過 WAL 共用 SQLite |
|
|
95
130
|
|
|
96
131
|
---
|
|
@@ -132,20 +167,17 @@ curl "http://127.0.0.1:7749/memory/query?q=authentication&limit=5"
|
|
|
132
167
|
|
|
133
168
|
## API 端點
|
|
134
169
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
|
140
|
-
|
|
141
|
-
| `/
|
|
142
|
-
| `/
|
|
143
|
-
| `/
|
|
144
|
-
| `/
|
|
145
|
-
| `/
|
|
146
|
-
| `/session/checkpoint` | POST | 會話中途快照寫入獨立 `session_checkpoints` 表(不會被 harvest 成 memory) | 已上線 |
|
|
147
|
-
| `/lint/warnings` | GET | Lint 報告:orphan(session 已刪)+ stale(低信心、長期未用)記憶警告 | 已上線 |
|
|
148
|
-
| `/session/last?cwd=...` | GET | 回傳專案路徑的最新 session metadata(post-session extraction wrapper 使用) | 已上線(v0.4.1) |
|
|
170
|
+
五個端點,每個都有活的 caller——v0.5.0 移除了其餘八個
|
|
171
|
+
(`/journal/*`、`/memory/save`、`/memory/context`、`/metacognition/check`、
|
|
172
|
+
`/session/checkpoint`、`/lint/warnings`),現在一律回 404。
|
|
173
|
+
|
|
174
|
+
| 端點 | 方法 | 說明 | Caller |
|
|
175
|
+
|------|------|------|--------|
|
|
176
|
+
| `/health` | GET | 服務健康 + DB 統計 + integrity 檢查狀態 | CLI、extraction wrapper |
|
|
177
|
+
| `/memory/startup?project=...` | GET | SessionStart 級檢索:cold + recent-confidence + FTS fallback,帶 token 預算 | SessionStart hook |
|
|
178
|
+
| `/memory/query?q=...&limit=...&project=...` | GET | FTS5 跨記憶搜尋,可選 project 過濾 | SessionStart hook(keyword 層) |
|
|
179
|
+
| `/session/end` | POST | 確認剛結束的 session 已被索引(miss 時 rescue reindex) | SessionEnd hook |
|
|
180
|
+
| `/session/last?cwd=...` | GET | 回傳專案路徑的最新 session metadata(`notBefore` staleness gate) | Extraction wrapper |
|
|
149
181
|
|
|
150
182
|
## MCP 工具
|
|
151
183
|
|
|
@@ -195,21 +227,9 @@ Daemon 與 hook 生命週期(macOS):
|
|
|
195
227
|
| `ccmem uninstall-daemon` | 停掉並移除 LaunchAgent |
|
|
196
228
|
| `ccmem install-hooks` | 把 SessionStart / SessionEnd 條目合併進 `~/.claude/settings.json` |
|
|
197
229
|
| `ccmem uninstall-hooks` | 移除 ccRecall 自己的 hook 條目(其他 hook 不動) |
|
|
230
|
+
| `ccmem cleanup --orphans` | 列出(加 `--yes` 則刪除)session 已消失的 orphan 記憶 |
|
|
198
231
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
| 指令 | 用途 |
|
|
202
|
-
|------|------|
|
|
203
|
-
| `ccmem promote <id>` | 把 journal 條目升級到 `memories`。可選 `--type`(預設 `discovery`)、`--confidence`(預設 `0.7`) |
|
|
204
|
-
| `ccmem reject <id>` | soft-delete journal 條目;7 天 TTL 後由 decay sweep 清掉 |
|
|
205
|
-
|
|
206
|
-
查待 promote 候選:
|
|
207
|
-
|
|
208
|
-
```bash
|
|
209
|
-
curl http://127.0.0.1:7749/journal/pending
|
|
210
|
-
# 或只看計數
|
|
211
|
-
curl http://127.0.0.1:7749/health | jq .journalPendingCount
|
|
212
|
-
```
|
|
232
|
+
`ccmem promote` / `ccmem reject` 已於 v0.5.0 隨 journal 管線一併移除。
|
|
213
233
|
|
|
214
234
|
---
|
|
215
235
|
|
|
@@ -230,8 +250,6 @@ ccRecall 和 Claude Code 內建的 auto memory(`~/.claude/projects/*/memory/`
|
|
|
230
250
|
|
|
231
251
|
ccRecall 的價值在長尾——幾百個 session 不可能全手工整理。如果 Claude 兩邊都試,auto memory 永遠會贏(本來就在 context 裡而且已經被策展)。ccRecall 存在的意義是:策展索引漏掉時,長尾那堆還在資料庫裡可以撈出來。
|
|
232
252
|
|
|
233
|
-
**ccRecall 內部從 v0.3.0 起多了一層 trust 拆分。** SessionEnd hook 寫到 low-trust `session_journal` 表——`recall_query` 不會去讀那裡。要讓某筆候選進到查詢結果,跑 `ccmem promote <id>` 升級到 high-trust `memories`。Manual `recall_save` 完全不經 journal——直接寫 memories。設計目的:讓 harvester 廣捕,但 recall 結果保持乾淨。
|
|
234
|
-
|
|
235
253
|
---
|
|
236
254
|
|
|
237
255
|
## 作為服務運行(macOS)
|
|
@@ -260,7 +278,7 @@ installer 的行為:
|
|
|
260
278
|
|
|
261
279
|
完整手動安裝、troubleshooting、uninstall 文件:[docs/launchd.md](docs/launchd.md)。
|
|
262
280
|
|
|
263
|
-
Linux/Windows 對應版本(systemd unit、Windows service
|
|
281
|
+
Linux/Windows 對應版本(systemd unit、Windows service)列在未來版本。目前
|
|
264
282
|
Linux 可用 `nohup` 或自選 process manager。
|
|
265
283
|
|
|
266
284
|
---
|
|
@@ -308,8 +326,7 @@ ccRecall/
|
|
|
308
326
|
│ │ ├── indexer.ts # 索引 pipeline 調度
|
|
309
327
|
│ │ ├── memory-service.ts # 記憶生命週期(touch / delete / update)
|
|
310
328
|
│ │ ├── compression.ts # L0→L1→L2→delete 狀態機
|
|
311
|
-
│ │ ├──
|
|
312
|
-
│ │ ├── maintenance-coordinator.ts # 背景壓縮 tick + journal decay sweep
|
|
329
|
+
│ │ ├── maintenance-coordinator.ts # 背景壓縮 tick
|
|
313
330
|
│ │ ├── watcher.ts # chokidar JSONL watcher(Phase 4e)
|
|
314
331
|
│ │ └── log-safe.ts # scrubErrorMessage — log-injection 防護
|
|
315
332
|
│ ├── api/
|
|
@@ -319,8 +336,7 @@ ccRecall/
|
|
|
319
336
|
│ │ ├── server.ts # MCP stdio server 入口(含 shebang)
|
|
320
337
|
│ │ └── tools.ts # recall_query + recall_context + recall_save
|
|
321
338
|
│ ├── cli/
|
|
322
|
-
│ │
|
|
323
|
-
│ │ └── journal.ts # ccmem promote / reject(v0.3.0 新增)
|
|
339
|
+
│ │ └── daemon.ts # install-daemon / uninstall-daemon(macOS)
|
|
324
340
|
│ └── index.ts # HTTP 入口 + 子指令分派
|
|
325
341
|
├── hooks/
|
|
326
342
|
│ ├── session-start.mjs # SessionStart 注入記憶(stdout)
|
|
@@ -330,11 +346,10 @@ ccRecall/
|
|
|
330
346
|
│ ├── tutorial_zh.md # 使用者教學(安裝 → MCP → 日常使用)
|
|
331
347
|
│ ├── architecture_zh.md # Daemon 設計取捨(給 contributor 看)
|
|
332
348
|
│ └── launchd.md # macOS LaunchAgent 安裝/troubleshoot
|
|
333
|
-
├── tests/ #
|
|
349
|
+
├── tests/ # 524 個測試橫跨 34 檔案(parser、scanner、
|
|
334
350
|
│ │ # summarizer、database、indexer、e2e、MCP、
|
|
335
351
|
│ │ # memories、hooks、watcher、CLI、migrations、
|
|
336
|
-
│ │ # FTS5 CJK edge cases、integrity monitor
|
|
337
|
-
│ │ # session_journal DAO、promote+reject、sweep 等)
|
|
352
|
+
│ │ # FTS5 CJK edge cases、integrity monitor 等)
|
|
338
353
|
│ └── fixtures/ # 測試用 JSONL + 共用 helpers
|
|
339
354
|
├── .mcp.json.example # MCP client 設定範本
|
|
340
355
|
└── NOTICE / SECURITY.md / CONTRIBUTING.md / CODE_OF_CONDUCT.md
|
|
@@ -385,9 +400,9 @@ Anthropic Claude Code 團隊的 Thariq 在 2026 年 4 月[發表了 context 管
|
|
|
385
400
|
| 版本 | 主題 | 狀態 |
|
|
386
401
|
|------|------|------|
|
|
387
402
|
| **v0.3.x** | 手動存、自動召回——記憶來自明確的 `recall_save` 呼叫;SessionStart hook 和 MCP 工具在未來 session 注入 | 已釋出 |
|
|
388
|
-
| **v0.4.
|
|
389
|
-
| **v0.
|
|
390
|
-
| **v0.5+** |
|
|
403
|
+
| **v0.4.x** | Post-session extraction via Haiku、跨專案 topic intersection 記憶、萃取管線強化(race gate、壓縮完整性、subagent 過濾、安全性) | 已釋出 |
|
|
404
|
+
| **v0.5.0** | 砍刀場:journal/scorer/harvester 管線移除(史上零 promotion)、端點 13 → 5、`message_uuids` 雙 hash 重建(DB 114MB → 42MB) | 已釋出 |
|
|
405
|
+
| **v0.5+** | CJK topic 抽取(`\p{Script=Han}` tokenizer + 中文 stopwords)、L1 keyword injection、README → manifesto | 規劃中 |
|
|
391
406
|
|
|
392
407
|
追蹤於 [GitHub Issues](https://github.com/tznthou/ccRecall/issues)。
|
|
393
408
|
|
package/dist/api/routes.d.ts
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
import http from 'node:http';
|
|
2
2
|
import type { Database } from '../core/database.js';
|
|
3
|
-
import type { SessionMeta, OutcomeStatus, JournalEntryInput } from '../core/types.js';
|
|
4
3
|
import type { IntegrityCheckRecord } from '../core/integrity-monitor.js';
|
|
5
|
-
export declare function inferConfidence(outcome: OutcomeStatus): number;
|
|
6
|
-
/** P1 (#21): hook auto-harvester 改寫 session_journal 不寫 memories;
|
|
7
|
-
* manual recall_save 仍直寫 memories。promote 路徑 (C4) 會把 journal entry
|
|
8
|
-
* 搬到 memories table 並設 confidence。
|
|
9
|
-
*
|
|
10
|
-
* Score 重算: summarizer 已用 scorer 過 noise/process-report hard floor,
|
|
11
|
-
* 但 score + reasons metadata 沒儲在 sessions.harvest_text。重算成本低
|
|
12
|
-
* (regex 對 <2KB 文字),換 schema 不變的代價可接受。 */
|
|
13
|
-
export declare function buildJournalCandidate(session: SessionMeta): JournalEntryInput | null;
|
|
14
4
|
/** Coalesces concurrent rescue calls onto one underlying run. The SessionEnd
|
|
15
5
|
* hook (/session/end) and the extraction wrapper (/session/last) both fire
|
|
16
6
|
* within the same session close — without this each miss stacks its own full
|
package/dist/api/routes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAA;AAG5B,OAAO,KAAK,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAA;AAG5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAMnD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAqCxE;;;;;gCAKgC;AAChC,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAM5E;AAED,MAAM,WAAW,qBAAqB;IACpC;;mDAE+C;IAC/C,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC;;gFAE4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;6EAEyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;4EAEwE;IACxE,gBAAgB,CAAC,EAAE;QAAE,aAAa,IAAI,oBAAoB,GAAG,IAAI,CAAA;KAAE,CAAA;CACpE;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,QAAQ,EACZ,IAAI,GAAE,qBAA0B,IAI9B,KAAK,IAAI,CAAC,eAAe,EACzB,KAAK,IAAI,CAAC,cAAc,KACvB,OAAO,CAAC,IAAI,CAAC,CA8NjB"}
|