dsh-session-recall 0.6.0 → 0.7.1

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [中文](https://github.com/kittimzhe/dsh-session-recall/blob/main/README.zh.md)
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/dsh-session-recall)](https://www.npmjs.com/package/dsh-session-recall) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/kittimzhe/dsh-session-recall/blob/main/LICENSE)
5
+ [![CI](https://github.com/kittimzhe/dsh-session-recall/actions/workflows/test.yml/badge.svg)](https://github.com/kittimzhe/dsh-session-recall/actions/workflows/test.yml) [![npm version](https://img.shields.io/npm/v/dsh-session-recall)](https://www.npmjs.com/package/dsh-session-recall) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/kittimzhe/dsh-session-recall/blob/main/LICENSE)
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
 
@@ -21,10 +21,16 @@ If you need agent memory orchestration, use a memory framework; if you need boun
21
21
  | Capability focus | Memory frameworks | Generic transcript search | `dsh-session-recall` |
22
22
  |---|---|---|---|
23
23
  | Retrieval target | Derived memory objects | Varies by implementation | **Original session transcript events** |
24
- | Scope control | Framework-specific | Often coarse | **cwd-scoped default + explicit `all_projects` gate** |
24
+ | Scope control | Framework-specific | Often coarse | **cwd-scoped default + explicit `all_projects`, `since_days`, `tools`, `errors_only` gate** |
25
25
  | CJK behavior | Framework-specific | Often tokenizer-limited | **FTS + CJK zero-hit substring fallback** |
26
26
  | Output contract | Usually framework-native | Varies | **Typed `recall` result with stable fields/hints** |
27
27
 
28
+ Name & scope notes (2026-09):
29
+
30
+ - This plugin is **unrelated to `dsh-recall-plugin`** — that plugin is message undo/rewind (restoring workspace and conversation to before a message was sent).
31
+ - 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.
32
+ - 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.
33
+
28
34
  ## Roadmap
29
35
 
30
36
  - **P2: evidence handoff** — one-click bridge to session export for matched sessions.
package/README.zh.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](https://github.com/kittimzhe/dsh-session-recall/blob/main/README.md) | 中文
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/dsh-session-recall)](https://www.npmjs.com/package/dsh-session-recall) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/kittimzhe/dsh-session-recall/blob/main/LICENSE)
5
+ [![CI](https://github.com/kittimzhe/dsh-session-recall/actions/workflows/test.yml/badge.svg)](https://github.com/kittimzhe/dsh-session-recall/actions/workflows/test.yml) [![npm version](https://img.shields.io/npm/v/dsh-session-recall)](https://www.npmjs.com/package/dsh-session-recall) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/kittimzhe/dsh-session-recall/blob/main/LICENSE)
6
6
 
7
7
  DeepSeek Harness 的**确定性跨会话全文检索**插件:注册模型可调用的 `recall` 工具,让 agent 能**检索自己过往的会话原文**——"上周修的那个 bug"、"简历选的什么字体"——全部通过可信的 `ctx.sessionQuery` 缝完成。
8
8
 
@@ -25,6 +25,11 @@ DeepSeek Harness 的**确定性跨会话全文检索**插件:注册模型可
25
25
  | CJK 体验 | 视实现而定 | 常受分词限制 | **FTS + CJK 零命中子串回退** |
26
26
  | 输出契约 | 框架内部格式 | 不统一 | **类型化 `recall` 结果 + 稳定 hint** |
27
27
 
28
+ 命名与边界说明(2026-09):
29
+
30
+ - 本插件与 **`dsh-recall-plugin` 无关**——那是"撤回消息"插件(把工作区与对话回退到某条消息发出之前)。
31
+ - 本插件是 **`dsh-recall` 的后继者**——后者是更早的会话检索插件(最后更新 2026-08-21);本插件在其方向上续写:持久 FTS5 索引、中文回退、审批闸门、谱系鉴权。
32
+ - 本插件与 **`dsh-mnemon` 等记忆框架互补**:它们做写入侧的记忆编排;本插件保持只读检索层,只读原始会话日志,不写任何记忆存储。
28
33
  ## 路线图
29
34
 
30
35
  - **P2:证据联动导出** —— 命中后可一键触发对应会话导出。
package/lib/index.d.ts CHANGED
@@ -136,6 +136,12 @@ interface RecallArgs {
136
136
  all_projects?: boolean;
137
137
  limit?: number;
138
138
  cursor?: string;
139
+ /** Only return sessions/entries newer than this many days. */
140
+ since_days?: number;
141
+ /** Only return tool-related events whose tool name matches one of these (substring, case-insensitive). */
142
+ tools?: readonly string[];
143
+ /** Only return failed tool calls (tool results carrying an error). */
144
+ errors_only?: boolean;
139
145
  }
140
146
  //#endregion
141
147
  //#region src/rank.d.ts
package/lib/index.js CHANGED
@@ -297,6 +297,34 @@ function cjkZeroHitHint(query, zeroHits, enabled) {
297
297
  * calling agent's own project cwd unless the model explicitly widens the
298
298
  * scope and the deployment allows it.
299
299
  */
300
+ /** Whether a best-match event type is a tool result (vs user/assistant message). */
301
+ function isToolResultType(type) {
302
+ return type === "tool/result" || type.startsWith("tool/");
303
+ }
304
+ /** Apply the dimensional post-filters (sinceDays / tools / errorsOnly) to recall items. */
305
+ function applyDimensionFilters(items, args, now = Date.now()) {
306
+ let out = [...items];
307
+ if (args.since_days !== void 0 && args.since_days > 0) {
308
+ const cutoff = now - args.since_days * 864e5;
309
+ out = out.filter((item) => {
310
+ return Math.max(item.createdAt ?? 0, item.bestMatch?.time ?? 0) >= cutoff;
311
+ });
312
+ }
313
+ if (args.tools !== void 0 && args.tools.length > 0) {
314
+ const needles = args.tools.map((t) => t.toLowerCase());
315
+ out = out.filter((item) => {
316
+ if (!isToolResultType(item.bestMatch?.type ?? "")) return false;
317
+ const snippet = (item.bestMatch?.snippet ?? "").toLowerCase();
318
+ return needles.some((n) => snippet.includes(n));
319
+ });
320
+ }
321
+ if (args.errors_only === true) out = out.filter((item) => {
322
+ if ((item.bestMatch?.type ?? "") !== "tool/result") return false;
323
+ const snippet = item.bestMatch?.snippet ?? "";
324
+ return snippet.includes("[error]") || /\berror\b|\bfailed\b/i.test(snippet);
325
+ });
326
+ return out;
327
+ }
300
328
  /** Walk a lineage trace and collect every session id in the tree. */
301
329
  function collectLineageIds(trace) {
302
330
  const ids = /* @__PURE__ */ new Set();
@@ -317,7 +345,7 @@ const RECALL_TOOL_DESCRIPTION = [
317
345
  "Matches whole words/phrases for English and code identifiers; a zero-hit Chinese (CJK) query automatically falls back to a substring scan in which every whitespace-separated term must match. Returns the best-matching event snippet per session plus the session id.",
318
346
  "Then use the read tool on files, or ask the user, to go deeper — this tool only points at history, it does not resume sessions.",
319
347
  "To save a full evidence report of a hit session, call the transcript_export tool (from dsh-session-export) with its sessionId when available.",
320
- "Scoping: by default only sessions started in the current project directory; pass all_projects=true to search everywhere (the deployment may ignore it or require user approval).",
348
+ "Scoping: by default only sessions started in the current project directory; pass all_projects=true to search everywhere (the deployment may ignore it or require user approval). Narrow further with since_days (only sessions newer than N days), tools (only tool events whose tool name matches, e.g. tools=[\"bash\"]), or errors_only=true (only failed tool calls).",
321
349
  "When the deployment enables redaction, secret-looking text in snippets appears as [REDACTED] or a #hash marker — treat it as removed; do not try to reconstruct or echo it.",
322
350
  "The first search after startup may be slow while the index builds."
323
351
  ].join(" ");
@@ -600,6 +628,19 @@ function createRecallTool(config, engine, approver) {
600
628
  cursor: {
601
629
  type: "string",
602
630
  description: "Opaque continuation cursor from a previous recall result with hasMore=true."
631
+ },
632
+ since_days: {
633
+ type: "integer",
634
+ description: "Only return sessions newer than this many days (0 = no time filter)."
635
+ },
636
+ tools: {
637
+ type: "array",
638
+ items: { type: "string" },
639
+ description: "Only return tool events whose tool name contains one of these substrings (case-insensitive), e.g. [\"bash\",\"read\"]."
640
+ },
641
+ errors_only: {
642
+ type: "boolean",
643
+ description: "Only return failed tool calls (tool results carrying an error)."
603
644
  }
604
645
  },
605
646
  output: {
@@ -675,7 +716,7 @@ function createRecallTool(config, engine, approver) {
675
716
  redacted: 0,
676
717
  diagnostics: null
677
718
  };
678
- let items = eventItems(page, sessionId);
719
+ let items = applyDimensionFilters(eventItems(page, sessionId), args);
679
720
  let hint = null;
680
721
  let diagnostics = {
681
722
  source: "fts",
@@ -690,7 +731,7 @@ function createRecallTool(config, engine, approver) {
690
731
  scanBudget: cfg.cjkFallbackScanMax,
691
732
  ranked: false
692
733
  };
693
- if (docs.length > 0) items = docs.slice(0, limit).map((doc) => ({
734
+ if (docs.length > 0) items = applyDimensionFilters(docs.slice(0, limit).map((doc) => ({
694
735
  sessionId,
695
736
  id8: id8(sessionId),
696
737
  title: null,
@@ -704,7 +745,7 @@ function createRecallTool(config, engine, approver) {
704
745
  time: doc.time,
705
746
  snippet: snippetAround(doc.text, highlight, CJK_SNIPPET_CHARS)
706
747
  }
707
- }));
748
+ })), args);
708
749
  hint = items.length > 0 ? cjkFallbackHint(items.length, cfg.cjkHint) : cjkZeroHitHint(query, true, cfg.cjkHint);
709
750
  } else if (items.length === 0) hint = cjkZeroHitHint(query, true, cfg.cjkHint);
710
751
  const red = applyRedaction(items);
@@ -738,6 +779,7 @@ function createRecallTool(config, engine, approver) {
738
779
  const ranked = rankingActive(rankingOptions);
739
780
  let items = rankItems(toItems(page.items, titles).filter((item) => cwdAllowed(item.cwd, cfg)), rankingOptions);
740
781
  if (allowedIds != null) items = items.filter((item) => allowedIds.has(item.sessionId));
782
+ items = applyDimensionFilters(items, args);
741
783
  let hint = null;
742
784
  let fallbackRan = false;
743
785
  let diagnostics = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-session-recall",
3
3
  "description": "Deterministic cross-session transcript retrieval for DeepSeek Harness: the model-facing `recall` tool searches past session logs with explicit scope control",
4
- "version": "0.6.0",
4
+ "version": "0.7.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },