@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.
Files changed (44) hide show
  1. package/README.md +73 -57
  2. package/README_ZH.md +73 -58
  3. package/dist/api/routes.d.ts +0 -10
  4. package/dist/api/routes.d.ts.map +1 -1
  5. package/dist/api/routes.js +4 -354
  6. package/dist/api/routes.js.map +1 -1
  7. package/dist/api/server.js +3 -3
  8. package/dist/api/server.js.map +1 -1
  9. package/dist/core/database.d.ts +14 -49
  10. package/dist/core/database.d.ts.map +1 -1
  11. package/dist/core/database.js +113 -225
  12. package/dist/core/database.js.map +1 -1
  13. package/dist/core/indexer.d.ts.map +1 -1
  14. package/dist/core/indexer.js +0 -1
  15. package/dist/core/indexer.js.map +1 -1
  16. package/dist/core/maintenance-coordinator.d.ts +3 -9
  17. package/dist/core/maintenance-coordinator.d.ts.map +1 -1
  18. package/dist/core/maintenance-coordinator.js +4 -24
  19. package/dist/core/maintenance-coordinator.js.map +1 -1
  20. package/dist/core/summarizer.d.ts.map +1 -1
  21. package/dist/core/summarizer.js +0 -22
  22. package/dist/core/summarizer.js.map +1 -1
  23. package/dist/core/types.d.ts +0 -87
  24. package/dist/core/types.d.ts.map +1 -1
  25. package/dist/core/types.js.map +1 -1
  26. package/dist/index.js +0 -16
  27. package/dist/index.js.map +1 -1
  28. package/package.json +1 -1
  29. package/dist/cli/journal.d.ts +0 -11
  30. package/dist/cli/journal.d.ts.map +0 -1
  31. package/dist/cli/journal.js +0 -111
  32. package/dist/cli/journal.js.map +0 -1
  33. package/dist/core/lint.d.ts +0 -17
  34. package/dist/core/lint.d.ts.map +0 -1
  35. package/dist/core/lint.js +0 -51
  36. package/dist/core/lint.js.map +0 -1
  37. package/dist/core/outcome-extractor.d.ts +0 -6
  38. package/dist/core/outcome-extractor.d.ts.map +0 -1
  39. package/dist/core/outcome-extractor.js +0 -39
  40. package/dist/core/outcome-extractor.js.map +0 -1
  41. package/dist/core/outcome-scorer.d.ts +0 -8
  42. package/dist/core/outcome-scorer.d.ts.map +0 -1
  43. package/dist/core/outcome-scorer.js +0 -103
  44. 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.3.0 — Trust split (breaking change for harvest write path)**
14
+ > 📐 **v0.5.0 — The knife-field release: one pipeline, nothing dead**
15
15
  >
16
- > The SessionEnd hook now writes to a low-trust `session_journal` table instead of `memories`. Journal entries are scored, surfaced via `/health` (`journalPendingCount`), and **do not appear in `recall_query` / `recall_context` results** until you run `ccmem promote <id>`. Manual `recall_save` is unchanged — it still writes directly to high-trust `memories`.
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 `/metacognition/check` and MCP `recall_context` |
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` retries a reindex on cache miss — no fresh-session race between the hook and the daemon |
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/>12 endpoints"]
63
+ DB[("SQLite + FTS5<br/>index & search")]
64
+ API["HTTP API<br/>5 endpoints"]
69
65
  end
70
66
 
71
- subgraph Consumers["Context Injection"]
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 --> API
78
- API --> Hook
79
- API --> MCP
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 | 608 tests across 38 files, integration-style |
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
- | Endpoint | Method | Description | Status |
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 + `journalPendingCount` (since v0.3.0) | Live |
138
- | `/memory/query?q=...&limit=...&project=...` | GET | FTS5 search across memories with optional project filter (journal entries excluded by design) | Live |
139
- | `/memory/save` | POST | Save a memory entry (origin-checked) | Live |
140
- | `/session/end` | POST | Harvest a finished session into `session_journal` (idempotent; was `memories` pre-0.3.0) | Live |
141
- | `/journal/pending` | GET | List journal entries awaiting promotion review | Live (v0.3.0) |
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
- Journal review (since v0.3.0):
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
- Phase 5. For now, run under `nohup` or your process manager of choice.
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
- │ │ ├── lint.ts # Orphan / stale memory detection
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
- │ │ ├── daemon.ts # install-daemon / uninstall-daemon (macOS)
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/ # 608 tests across 38 files (parser, scanner,
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.0** | Startup-v1 default + tool description hardening | Released |
396
- | **v0.4.1** | Key-based upsert dedup, post-session memory extraction via Haiku, cross-project memory visibility via topic intersection | Released |
397
- | **v0.5+** | Scorer/journal/harvester deprecation, L1 keyword injection, memory lifecycle history | Planned |
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.3.0 — Trust 二層拆分(harvest 寫入路徑 breaking change)**
14
+ > 📐 **v0.5.0 — 砍刀場版本:一條管線,零死代碼**
15
15
  >
16
- > SessionEnd hook 改寫到新建的 low-trust `session_journal` 表,不再寫 `memories`。Journal 條目會評分、透過 `/health` 的 `journalPendingCount` 露出,**`recall_query` / `recall_context` 不會直接召回 journal 條目**,需要先 `ccmem promote <id>` 升級。Manual `recall_save` 不變——仍直寫 high-trust `memories`。
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)。透過 `/metacognition/check` 與 MCP `recall_context` 暴露 |
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` 遇到 cache miss 時會重 index 再試一次——hook 和 daemon 間不會有 fresh-session race |
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/>12 個端點"]
63
+ DB[("SQLite + FTS5<br/>索引與搜尋")]
64
+ API["HTTP API<br/>5 個端點"]
69
65
  end
70
66
 
71
- subgraph Consumers["Context 注入"]
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 --> API
78
- API --> Hook
79
- API --> MCP
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 | 測試 | 608 個測試(38 檔案)、整合式風格 |
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
- | `/health` | GET | 服務健康 + DB 統計 + integrity 檢查狀態 + `journalPendingCount`(v0.3.0 新增) | 已上線 |
138
- | `/memory/query?q=...&limit=...&project=...` | GET | FTS5 跨 session 搜尋,可選 project 過濾(journal 條目 by design 不含) | 已上線 |
139
- | `/memory/save` | POST | 儲存記憶條目(Origin 驗證) | 已上線 |
140
- | `/session/end` | POST | 把結束的 session 寫入 `session_journal`(idempotent;0.3.0 前是寫 `memories`) | 已上線 |
141
- | `/journal/pending` | GET | 列出待 promote 審閱的 journal 條目 | 已上線(v0.3.0) |
142
- | `/journal/promote` | POST | atomic 把 journal 條目升級到 `memories` | 已上線(v0.3.0) |
143
- | `/journal/reject` | POST | soft-delete journal 條目(7 天 TTL,由 decay sweep 清理) | 已上線(v0.3.0) |
144
- | `/memory/context?session_id=...` | GET | Session context 查詢 | Stub |
145
- | `/metacognition/check?projectId=...[&topic=...]` | GET | 知識地圖:summary(top/recent/stale topics + counts)或 topic detail(memories + related topics) | 已上線 |
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
- Journal 審閱(v0.3.0 新增):
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)列在 Phase 5。目前
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
- │ │ ├── lint.ts # Orphan / stale 記憶偵測
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
- │ │ ├── daemon.ts # install-daemon / uninstall-daemon(macOS)
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/ # 608 個測試橫跨 38 檔案(parser、scanner、
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.0** | Startup-v1 預設 + tool description 強化 | 已釋出 |
389
- | **v0.4.1** | Key-based upsert dedup、post-session memory extraction via Haiku、跨專案 topic intersection 記憶可見性 | 已釋出 |
390
- | **v0.5+** | Scorer/journal/harvester 降級、L1 keyword injection、memory lifecycle history | 規劃中 |
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
 
@@ -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
@@ -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,EAAe,MAAM,qBAAqB,CAAA;AAOhE,OAAO,KAAK,EACwB,WAAW,EAAE,aAAa,EAE5D,iBAAiB,EAClB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AA6DxE,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAI9D;AAED;;;;;;2CAM2C;AAC3C,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,WAAW,GAAG,iBAAiB,GAAG,IAAI,CAcpF;AAgED;;;;;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,CA6djB"}
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"}