dsh-session-recall 0.7.4 → 0.7.6

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 CHANGED
@@ -6,7 +6,7 @@ English | [中文](https://github.com/kittimzhe/dsh-session-recall/blob/main/REA
6
6
 
7
7
  Deterministic cross-session full-text retrieval for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): the model-facing `recall` tool lets the agent **search its own past session transcripts** — "that bug we fixed last week", "the font we chose for my resume" — through the trusted `ctx.sessionQuery` seam.
8
8
 
9
- ## Quick Start
9
+ ## Install
10
10
 
11
11
  **Requirements**: Node.js 20 or 22 · a DeepSeek Harness profile that mounts the `tools` and `sessionQuery` services (the shipped `web` / `agent` profiles qualify).
12
12
 
@@ -14,42 +14,61 @@ Deterministic cross-session full-text retrieval for [DeepSeek Harness](https://g
14
14
  dsh plugin --profile web add dsh-session-recall
15
15
  ```
16
16
 
17
+ ## Try it once
18
+
17
19
  The first search builds a persistent FTS5 index automatically — no extra setup. In a session, just ask naturally:
18
20
 
19
21
  ```text
20
22
  recall: which font did we pick for the resume last week?
21
23
  ```
22
24
 
23
- Full details — GitHub install route, `cordis.patch.yml` snippet, configuration — in [Install](#install-out-of-tree-plugin) below.
25
+ The tool surfaces the best-matching event per session — each hit carries the session id, a best-effort title, the date, and a match snippet, rendered as a native search card in the Web UI.
24
26
 
25
- ## Positioning
27
+ ## The session toolchain
26
28
 
27
- `dsh-session-recall` is a **transcript retrieval layer** focused on correctness and control.
29
+ | Plugin | Layer | Answers |
30
+ |---|---|---|
31
+ | [`dsh-session-export`](https://www.npmjs.com/package/dsh-session-export) | Evidence | "What exactly happened in this session?" |
32
+ | **`dsh-session-recall`** | **Memory** | **"What did I do before, and where is it?"** |
33
+ | [`dsh-session-eval`](https://www.npmjs.com/package/dsh-session-eval) | Measurement | "Was that session good? Is the trend improving?" |
28
34
 
29
- - It returns evidence from original session logs, not synthesized summaries.
30
- - It enforces explicit retrieval scope (cwd by default, opt-in widening).
31
- - It favors deterministic behavior over "smart" but lossy memory extraction.
35
+ All three read through the same trusted `ctx.sessionQuery` seam.
32
36
 
33
- If you need agent memory orchestration, use a memory framework; if you need bounded, auditable lookup over historical transcripts, use this plugin.
37
+ ## Contributing
34
38
 
35
- ## Competitive context
39
+ - **Local dev**: `npm install && npm run typecheck && npm test && npm run bundle` (Node 20 or 22).
40
+ - **Start in the source**: [`src/tool.ts`](src/tool.ts) (tool contract), [`src/rank.ts`](src/rank.ts) (recency re-ranking, pinned cwds), [`src/redact.ts`](src/redact.ts) (redaction patterns), [`src/resilient.ts`](src/resilient.ts) (degraded raw-scan). The full source map is in [CONTRIBUTING.md](CONTRIBUTING.md).
41
+ - **Open gaps**: [#6](https://github.com/kittimzhe/dsh-session-recall/issues/6) (redaction patterns), [#7](https://github.com/kittimzhe/dsh-session-recall/issues/7) (tie-break order) — or browse [issues labeled `good first issue`](https://github.com/kittimzhe/dsh-session-recall/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
42
+ - **Roadmap**: [P2: evidence handoff — expose `sessionId` + transcript path from recall hits (#8)](https://github.com/kittimzhe/dsh-session-recall/issues/8), acceptance criteria in the issue; post a comment before starting so effort is not duplicated.
43
+ - **Rules**: behavior changes need tests; doc changes must update `README.md` and `README.zh.md` in sync; releases belong to the maintainer. Details: [CONTRIBUTING.md](CONTRIBUTING.md).
36
44
 
37
- | Capability focus | Memory frameworks | Generic transcript search | `dsh-session-recall` |
38
- |---|---|---|---|
39
- | Retrieval target | Derived memory objects | Varies by implementation | **Original session transcript events** |
40
- | Scope control | Framework-specific | Often coarse | **cwd-scoped default + explicit `all_projects`, `since_days`, `tools`, `errors_only` gate** |
41
- | CJK behavior | Framework-specific | Often tokenizer-limited | **FTS + CJK zero-hit substring fallback** |
42
- | Output contract | Usually framework-native | Varies | **Typed `recall` result with stable fields/hints** |
45
+ ## What the model gets
43
46
 
44
- Name & scope notes (2026-09):
47
+ ```
48
+ recall({ query }) → best-matching event per session, current project only
49
+ recall({ query, all_projects: true }) → search every session on the machine
50
+ recall({ query, session_id }) → search the events of one session
51
+ recall({ query, limit, cursor }) → page through results
52
+ ```
45
53
 
46
- - This plugin is **unrelated to `dsh-recall-plugin`** — that plugin is message undo/rewind (restoring workspace and conversation to before a message was sent).
47
- - It **succeeds `dsh-recall`** — an earlier transcript-search plugin (last release 2026-08-21) with a similar goal; this plugin continues the line with persistent FTS5 indexing, CJK fallback, approval gates, and lineage-scoped authorization.
48
- - It **complements memory frameworks** such as `dsh-mnemon` (write-side memory orchestration): this plugin stays a read-only retrieval layer over original session logs and makes no writes to any memory store.
54
+ Each hit carries the session id, title (best-effort), date, and a match snippet; the result renders as a native search card in the Web UI (`SearchMatchesResultView`). Because the FTS `unicode61` tokenizer indexes an uninterrupted CJK run as a single token, a short Chinese phrase inside a longer sentence would otherwise never match the index — so a zero-hit CJK query automatically falls back to a substring scan over session text (the `sessionQuery.filterEvents` literal text clause). Every whitespace-separated term must match, so `简历 模板` still recovers `简历模板`; the hint reports when that path matched.
55
+
56
+ ## Scoping (the authorization gap)
57
+
58
+ `sessionQuery` is trusted infrastructure — it can read every session. This tool therefore constrains each call itself:
59
+
60
+ - by default, `sessionFilters: [{ kind: 'cwd', values: [<calling agent's cwd>] }]` — only sessions started in the same project directory;
61
+ - `all_projects: true` widens the scope, and only if the deployment allows it (`allowAllProjects: false` disables the argument).
49
62
 
50
- ## Roadmap
63
+ ## Positioning
51
64
 
52
- - **P2: evidence handoff** — one-click bridge to session export for matched sessions.
65
+ `dsh-session-recall` is a **transcript retrieval layer** focused on correctness and control.
66
+
67
+ - It returns evidence from original session logs, not synthesized summaries.
68
+ - It enforces explicit retrieval scope (cwd by default, opt-in widening).
69
+ - It favors deterministic behavior over "smart" but lossy memory extraction.
70
+
71
+ If you need agent memory orchestration, use a memory framework; if you need bounded, auditable lookup over historical transcripts, use this plugin.
53
72
 
54
73
  ## Why
55
74
 
@@ -69,23 +88,20 @@ And the shipped web profile mounts its SQLite FTS5 backend with `openAt: never`
69
88
 
70
89
  Memory plugins extract structured notes with an LLM (lossy, costs tokens); `recall` searches the **original transcripts** — zero extraction, zero loss, works retroactively on day one.
71
90
 
72
- ## What the model gets
73
-
74
- ```
75
- recall({ query }) → best-matching event per session, current project only
76
- recall({ query, all_projects: true }) → search every session on the machine
77
- recall({ query, session_id }) → search the events of one session
78
- recall({ query, limit, cursor }) → page through results
79
- ```
80
-
81
- Each hit carries the session id, title (best-effort), date, and a match snippet; the result renders as a native search card in the Web UI (`SearchMatchesResultView`). Because the FTS `unicode61` tokenizer indexes an uninterrupted CJK run as a single token, a short Chinese phrase inside a longer sentence would otherwise never match the index — so a zero-hit CJK query automatically falls back to a substring scan over session text (the `sessionQuery.filterEvents` literal text clause). Every whitespace-separated term must match, so `简历 模板` still recovers `简历模板`; the hint reports when that path matched.
91
+ ## Competitive context
82
92
 
83
- ## Scoping (the authorization gap)
93
+ | Capability focus | Memory frameworks | Generic transcript search | `dsh-session-recall` |
94
+ |---|---|---|---|
95
+ | Retrieval target | Derived memory objects | Varies by implementation | **Original session transcript events** |
96
+ | Scope control | Framework-specific | Often coarse | **cwd-scoped default + explicit `all_projects`, `since_days`, `tools`, `errors_only` gate** |
97
+ | CJK behavior | Framework-specific | Often tokenizer-limited | **FTS + CJK zero-hit substring fallback** |
98
+ | Output contract | Usually framework-native | Varies | **Typed `recall` result with stable fields/hints** |
84
99
 
85
- `sessionQuery` is trusted infrastructure — it can read every session. This tool therefore constrains each call itself:
100
+ Name & scope notes (2026-09):
86
101
 
87
- - by default, `sessionFilters: [{ kind: 'cwd', values: [<calling agent's cwd>] }]` — only sessions started in the same project directory;
88
- - `all_projects: true` widens the scope, and only if the deployment allows it (`allowAllProjects: false` disables the argument).
102
+ - This plugin is **unrelated to `dsh-recall-plugin`** — that plugin is message undo/rewind (restoring workspace and conversation to before a message was sent).
103
+ - It **succeeds `dsh-recall`** — an earlier transcript-search plugin (last release 2026-08-21) with a similar goal; this plugin continues the line with persistent FTS5 indexing, CJK fallback, approval gates, and lineage-scoped authorization.
104
+ - It **complements memory frameworks** such as `dsh-mnemon` (write-side memory orchestration): this plugin stays a read-only retrieval layer over original session logs and makes no writes to any memory store.
89
105
 
90
106
  ## Install (out-of-tree plugin)
91
107
 
@@ -125,8 +141,22 @@ Plugin row config (all optional):
125
141
  cjkHint: true # explain CJK zero-hit results
126
142
  cjkFallback: true # CJK zero-hit → exact substring scan over session text
127
143
  cjkFallbackScanMax: 50 # max sessions scanned per cross-session fallback (1..500)
144
+ rawScanFallback: true # degraded mode: when the index itself fails, scan logs directly
145
+ rawScanMaxSessions: 200 # degraded scan budget: sessions visited (1..2000)
146
+ rawScanMaxDurationMs: 20000 # degraded scan wall-clock ceiling (1000..120000)
147
+ rawScanMaxSessionBytes: 8388608 # per-log compressed-size cap in the degraded scan
128
148
  ```
129
149
 
150
+ ## Degraded mode (v0.7.5)
151
+
152
+ When the session index itself fails — most notably `SESSION_QUERY_PERSISTENCE_FAILED`, where a single un-migratable session artifact fails *every* indexed search (the v0→v1 migration gate rejecting `subagent/descriptor` version 2, [deepseek-harness discussion #7995](https://github.com/deepseek-ai/deepseek-harness/discussions/7995)) — the `recall` tool degrades to scanning the persisted session logs directly (`$DSH_HOME/sessions`) instead of failing the call:
153
+
154
+ - **Tolerant by construction**: each log is decompressed with a bundled pure-JS zstd decoder and parsed line by line; a corrupt line or unreadable log is skipped and counted, never fatal. The exact session shape that bricks the upstream migration is searchable here.
155
+ - **Affordable**: session headers are read through a streaming decompressor (milliseconds each) so cwd/time/session_id scope filters run before any full decompression; a byte-level term prefilter avoids line parsing for non-matching logs; a wall-clock budget (default 20s) and a per-log size cap (default 8 MiB compressed — pure-JS zstd decompresses ~2 MB/s) keep the tool responsive. Sessions are visited newest-first, and when the budget trips, the result says which portion of the store was covered.
156
+ - **Honest**: results carry `diagnostics.source: "raw-scan"` and a hint explaining the degraded provenance, unreadable/oversized skips, and partial coverage. Live (not yet persisted) sessions are not included.
157
+
158
+ Disable with `rawScanFallback: false` to restore the pre-v0.7.5 fail-fast behavior.
159
+
130
160
  ## Failure behavior
131
161
 
132
162
  Every failure returns a friendly `hint` instead of a raw exception: a disabled index explains the two config keys needed, a stale cursor tells the model to restart without one, an unknown `session_id` suggests discovering sessions first. Title enrichment is best-effort — a failed title batch degrades to untitled rows, never a failed search.
@@ -157,7 +187,7 @@ Every result also carries a `diagnostics` object (v0.5): which engine produced t
157
187
 
158
188
  ## Benchmark
159
189
 
160
- Measured on a real headless profile (Node 25, Apple Silicon, warm filesystem cache).
190
+ Measured on a real headless profile on the machine at hand (Node 25, Apple Silicon, warm filesystem cache) — indicative numbers, not a CI gate. Supported and CI-tested Node versions remain 20 and 22.
161
191
 
162
192
  | Corpus | |
163
193
  |---|---|
@@ -184,16 +214,6 @@ npm test # vitest run
184
214
  npm run bundle # tsdown → lib/
185
215
  ```
186
216
 
187
- ## Session toolchain
188
-
189
- This plugin is one of three layers over the same trusted `ctx.sessionQuery` seam:
190
-
191
- | Plugin | Layer | Answers |
192
- |---|---|---|
193
- | [`dsh-session-export`](https://www.npmjs.com/package/dsh-session-export) | Evidence | "What exactly happened in this session?" |
194
- | `dsh-session-recall` | Memory | "What did I do before, and where is it?" |
195
- | [`dsh-session-eval`](https://www.npmjs.com/package/dsh-session-eval) | Measurement | "Was that session good? Is the trend improving?" |
196
-
197
217
  ## License
198
218
 
199
219
  [MIT](https://github.com/kittimzhe/dsh-session-recall/blob/main/LICENSE)
package/README.zh.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  DeepSeek Harness 的**确定性跨会话全文检索**插件:注册模型可调用的 `recall` 工具,让 agent 能**检索自己过往的会话原文**——"上周修的那个 bug"、"简历选的什么字体"——全部通过可信的 `ctx.sessionQuery` 缝完成。
8
8
 
9
- ## 快速开始
9
+ ## 安装
10
10
 
11
11
  **环境要求**:Node.js 20 或 22 · 挂载了 `tools` 与 `sessionQuery` 服务的 DeepSeek Harness profile(官方 `web` / `agent` profile 均满足)。
12
12
 
@@ -14,13 +14,33 @@ DeepSeek Harness 的**确定性跨会话全文检索**插件:注册模型可
14
14
  dsh plugin --profile web add dsh-session-recall
15
15
  ```
16
16
 
17
+ ## 试一次
18
+
17
19
  首次搜索会自动构建持久化 FTS5 索引——无需额外步骤。会话里直接问:
18
20
 
19
21
  ```text
20
22
  recall:上周给简历选的什么字体来着?
21
23
  ```
22
24
 
23
- 完整细节(GitHub 安装方式、`cordis.patch.yml` 片段、配置项)见下文「安装」一节。
25
+ 工具会按会话返回最强命中——每条带会话 id、尽力补全的标题、日期与命中摘录,在 Web UI 里渲染成原生搜索卡片。
26
+
27
+ ## 会话工具链
28
+
29
+ | 插件 | 层 | 回答的问题 |
30
+ |---|---|---|
31
+ | [`dsh-session-export`](https://www.npmjs.com/package/dsh-session-export) | 证据层 | "这个会话到底发生了什么?" |
32
+ | **`dsh-session-recall`** | **记忆层** | **"我以前做过什么,在哪?"** |
33
+ | [`dsh-session-eval`](https://www.npmjs.com/package/dsh-session-eval) | 评测层 | "刚才的会话好不好?趋势在变好吗?" |
34
+
35
+ 三者都通过同一个受信的 `ctx.sessionQuery` 接缝读取。
36
+
37
+ ## 贡献
38
+
39
+ - **本地开发**:`npm install && npm run typecheck && npm test && npm run bundle`(Node 20 或 22)。
40
+ - **源码入口**:[`src/tool.ts`](src/tool.ts)(工具契约)、[`src/rank.ts`](src/rank.ts)(新近度重排、置顶项目)、[`src/redact.ts`](src/redact.ts)(脱敏规则)、[`src/resilient.ts`](src/resilient.ts)(降级直扫)。完整源码地图见 [CONTRIBUTING.md](CONTRIBUTING.md)。
41
+ - **当前缺口**:[#6](https://github.com/kittimzhe/dsh-session-recall/issues/6)(脱敏规则扩展)、[#7](https://github.com/kittimzhe/dsh-session-recall/issues/7)(同分排序钉死)——或浏览 [`good first issue` 标签](https://github.com/kittimzhe/dsh-session-recall/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。
42
+ - **路线图**:[P2:证据联动——recall 命中携带 sessionId + 转录路径(#8)](https://github.com/kittimzhe/dsh-session-recall/issues/8),验收标准见 issue;认领前请先留言避免撞车。
43
+ - **规矩**:行为变更必须带测试;文档必须 `README.md` 与 `README.zh.md` 同步改;版本发布由维护者执行。详见 [CONTRIBUTING.md](CONTRIBUTING.md)。
24
44
 
25
45
  ## 项目定位
26
46
 
@@ -46,10 +66,6 @@ recall:上周给简历选的什么字体来着?
46
66
  - 本插件与 **`dsh-recall-plugin` 无关**——那是"撤回消息"插件(把工作区与对话回退到某条消息发出之前)。
47
67
  - 本插件是 **`dsh-recall` 的后继者**——后者是更早的会话检索插件(最后更新 2026-08-21);本插件在其方向上续写:持久 FTS5 索引、中文回退、审批闸门、谱系鉴权。
48
68
  - 本插件与 **`dsh-mnemon` 等记忆框架互补**:它们做写入侧的记忆编排;本插件保持只读检索层,只读原始会话日志,不写任何记忆存储。
49
- ## 路线图
50
-
51
- - **P2:证据联动导出** —— 命中后可一键触发对应会话导出。
52
-
53
69
  ## 为什么做这个
54
70
 
55
71
  官方 `@deepseek-ai/dsh-session-query` 的 README 自己列出了缺口:
@@ -124,8 +140,22 @@ dsh plugin --profile web add github:kittimzhe/dsh-session-recall
124
140
  cjkHint: true # CJK 零命中的提示开关
125
141
  cjkFallback: true # CJK 零命中 → 对会话文本做精确子串扫描
126
142
  cjkFallbackScanMax: 50 # 跨会话回退时最多扫描的会话数(1..500)
143
+ rawScanFallback: true # 降级模式:索引整体故障时直扫会话日志
144
+ rawScanMaxSessions: 200 # 降级扫描预算:访问会话数(1..2000)
145
+ rawScanMaxDurationMs: 20000 # 降级扫描墙钟上限(1000..120000)
146
+ rawScanMaxSessionBytes: 8388608 # 降级扫描单日志压缩体积上限(字节)
127
147
  ```
128
148
 
149
+ ## 降级模式(v0.7.5)
150
+
151
+ 当会话索引本身故障——最典型的是 `SESSION_QUERY_PERSISTENCE_FAILED`:一个无法迁移的会话工件让**所有**索引查询全部失败(v0→v1 迁移闸门拒绝 `subagent/descriptor` version 2,见 [deepseek-harness 讨论 #7995](https://github.com/deepseek-ai/deepseek-harness/discussions/7995))——`recall` 工具降级为直接扫描持久化会话日志(`$DSH_HOME/sessions`),而不是让整个调用失败:
152
+
153
+ - **天生容错**:日志用内置的纯 JS zstd 解码器解压、逐行解析;坏行或不可读日志跳过并计数,绝不致命。上游迁移器认不了的那个会话形状,在这里照样可搜。
154
+ - **算得起**:会话 header 走流式解压(毫秒级),cwd/时间/session_id 过滤先于任何全量解压;字节级关键词预过滤让不含关键词的日志免去逐行解析;墙钟预算(默认 20 秒)与单日志体积上限(默认压缩后 8 MiB——纯 JS zstd 解压约 2 MB/s)保证工具响应性。会话按最新优先访问,预算用尽时结果会说明覆盖了哪部分。
155
+ - **诚实**:结果带 `diagnostics.source: "raw-scan"` 和降级来源提示(不可读/超限跳过数、部分覆盖说明)。未落盘的活跃会话不包含在内。
156
+
157
+ 设 `rawScanFallback: false` 可恢复 v0.7.5 之前的快速失败行为。
158
+
129
159
  ## 失败行为
130
160
 
131
161
  所有失败都返回友好的 `hint` 而不是裸异常:索引未开启会说明需要哪两个配置键;游标失效会告诉模型不带游标重开一次;`session_id` 不存在会建议先做跨会话搜索。标题补全是尽力而为——标题批量读取失败只降级为"无标题"行,绝不让搜索失败。
@@ -156,7 +186,7 @@ dsh plugin --profile web add github:kittimzhe/dsh-session-recall
156
186
 
157
187
  ## 基准
158
188
 
159
- 真机 headless profile 实测(Node 25,Apple Silicon,暖文件缓存)。
189
+ 真机 headless profile 实测(手头机器为 Node 25、Apple Silicon、暖文件缓存)——数字仅供参考,不是 CI 门槛;受支持并经 CI 测试的版本仍是 Node 20 与 22。
160
190
 
161
191
  | 语料 | |
162
192
  |---|---|