@avantf/dsh-mem 0.8.1 → 0.9.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 (39) hide show
  1. package/lib/client.js +1088 -303
  2. package/lib/engine/graph-recall/index.d.ts +2 -2
  3. package/lib/engine/graph-recall/traverse.d.ts +2 -2
  4. package/lib/engine/graph-recall/types.d.ts +1 -1
  5. package/lib/engine/mem/config/config_files.d.ts +1 -1
  6. package/lib/engine/mem/defaults.d.ts +4 -1
  7. package/lib/engine/mem/entities/extract.d.ts +34 -14
  8. package/lib/engine/mem/index.d.ts +1 -0
  9. package/lib/engine/mem/modelBootstrap.d.ts +29 -0
  10. package/lib/engine/mem/store/graph_reader.d.ts +2 -2
  11. package/lib/engine/mem/store/memory.d.ts +53 -6
  12. package/lib/engine/mem/store/recall_policy.d.ts +10 -0
  13. package/lib/engine/mem-contract/config.d.ts +135 -0
  14. package/lib/engine/mem-contract/types.d.ts +11 -0
  15. package/lib/engine/mem-retrieval/adapters/auto_vstore.d.ts +10 -0
  16. package/lib/engine/mem-retrieval/adapters/hnswlib.d.ts +28 -0
  17. package/lib/engine/mem-retrieval/adapters/local_bge.d.ts +48 -6
  18. package/lib/engine/mem-retrieval/interfaces.d.ts +22 -2
  19. package/lib/engine/mem-retrieval/text_budget.d.ts +31 -0
  20. package/lib/index.js +1617 -114
  21. package/lib/interface-version.json +2 -2
  22. package/lib/types/client/index.d.ts.map +1 -1
  23. package/lib/types/client/paging.d.ts +58 -8
  24. package/lib/types/client/paging.d.ts.map +1 -1
  25. package/lib/types/client/time_filter.d.ts +54 -0
  26. package/lib/types/client/time_filter.d.ts.map +1 -0
  27. package/lib/types/deps_audit.d.ts +88 -0
  28. package/lib/types/deps_audit.d.ts.map +1 -0
  29. package/lib/types/envinit-bootstrap.d.ts +1 -1
  30. package/lib/types/envinit.d.ts +224 -4
  31. package/lib/types/envinit.d.ts.map +1 -1
  32. package/lib/types/hints.d.ts +58 -0
  33. package/lib/types/hints.d.ts.map +1 -1
  34. package/lib/types/index.d.ts.map +1 -1
  35. package/lib/types/render.d.ts.map +1 -1
  36. package/lib/types/runtime_deps_resource.d.ts +29 -0
  37. package/lib/types/runtime_deps_resource.d.ts.map +1 -0
  38. package/package.json +5 -4
  39. package/res/runtime-deps.yaml +99 -0
@@ -4,8 +4,8 @@
4
4
  * The package owns the algorithms that memory and knowledge share: one-hop, reverse, two-hop, AND
5
5
  * and neighbour walks over a {@link GraphReader}, returning {@link RelationPath}s that keep their
6
6
  * evidence. It deliberately does NOT own storage, filtering policy, extraction, planning, answer
7
- * generation, or anything DSH/Cordis/SQLite-specific — see `docs/GRAPH_RECALL_LIBRARY.md` in the
8
- * mem repository for the boundary and its rationale.
7
+ * generation, or anything DSH/Cordis/SQLite-specific — the boundary and its rationale are in this
8
+ * package's README.
9
9
  *
10
10
  * @example
11
11
  * ```ts
@@ -2,7 +2,7 @@
2
2
  * The traversal half of `@avantf/graph-recall`: five explicit, bounded operators over a
3
3
  * {@link GraphReader}, plus a thin {@link query} dispatcher.
4
4
  *
5
- * Design constraints (docs/GRAPH_RECALL_LIBRARY.md §5), enforced here rather than left to callers:
5
+ * Design constraints (documented in the package README), enforced here rather than left to callers:
6
6
  *
7
7
  * - **Two hops at most.** There is no recursive walk and no depth parameter beyond `1 | 2`.
8
8
  * - **Every operator is bounded.** Reader calls are over-fetched to `limit × 5` (capped at
@@ -21,7 +21,7 @@
21
21
  * back to fuzzy retrieval.
22
22
  */
23
23
  import type { AndOptions, GraphNode, GraphQuery, GraphReader, GraphResult, NeighborOptions, PathOptions, RelationPath } from './types.js';
24
- /** Default page size of every operator (§5 "每个算子有 limit,默认 10"). */
24
+ /** Default page size of every operator ("每个算子有 limit,默认 10"). */
25
25
  export declare const DEFAULT_LIMIT = 10;
26
26
  /**
27
27
  * Hard page-size ceiling. A larger `limit` is clamped instead of honored: the package's contract is
@@ -47,7 +47,7 @@ export interface GraphEdge {
47
47
  /**
48
48
  * When the evidence was recorded: an ISO-8601 timestamp or epoch milliseconds. Optional because
49
49
  * the design's `GraphEdge` does not require it; when present it drives the recency tiebreak
50
- * (§5 "证据时间 DESC"). An adapter maps `edges.created_at` / `facts.created_at` here.
50
+ * ("证据时间 DESC"). An adapter maps `edges.created_at` / `facts.created_at` here.
51
51
  */
52
52
  observedAt?: string | number;
53
53
  }
@@ -15,7 +15,7 @@ import type { AvantfLogger } from '../../mem-contract/index.js';
15
15
  * UNCOMMENTED line is warned about by the loader, so a wrong name here would be a trap rather than a
16
16
  * hint. `config_files.spec.ts` pins the ones this template names.
17
17
  */
18
- export declare const DEFAULT_COMMON_CONFIG = "# avantf-mem \u516C\u5171\u914D\u7F6E\uFF08\u5206\u5C42\u7B2C\u2461\u5C42\uFF09\u3002\u8FD9\u91CC\u5168\u662F\u6CE8\u91CA\uFF1A\u5220\u6389/\u7559\u7A7A = \u7528\u5185\u5EFA\u9ED8\u8BA4\uFF0C\u53D6\u6D88\u6CE8\u91CA\u5373\u751F\u6548\u3002\n# \u5206\u5C42\uFF08\u4F4E\u2192\u9AD8\uFF09\uFF1A\u2460 \u5185\u5EFA\u9ED8\u8BA4 \u2192 \u2461 \u672C\u6587\u4EF6 \u2192 \u2462 \u540C\u76EE\u5F55\u7684 memory.yaml\uFF08\u7F3A\u5931=\u4E0D\u8986\u76D6\uFF09\n# \u2192 \u2463 \u73AF\u5883\u53D8\u91CF \u2192 \u2464 \u63D2\u4EF6/CLI \u663E\u5F0F\u4F20\u53C2\n# \u6240\u6709\u53EF\u7F16\u8F91\u6587\u672C\u90FD\u5728 ~/.avantf \u4E0B\uFF1Aconfigs/*.yaml\uFF08\u672C\u76EE\u5F55\uFF09\u4E0E prompts/*.md\uFF08\u6A21\u578B\u63D0\u793A\u8BCD\uFF09\u3002\n\n# \u6570\u636E\u6839\u4E0D\u5728\u8FD9\u91CC\u914D\uFF1A\u5B83\u7531 \u2463 AVANTF_HOME\uFF08\u5BB6\u65CF\u6839\uFF1A\u540C\u65F6\u51B3\u5B9A\u6570\u636E\u6839\u4E0E models/tools\uFF09\u51B3\u5B9A \u2014\u2014\n# \u53EA\u60F3\u6362\u6570\u636E\u6839\u3001\u4E0D\u642C\u6A21\u578B\u7F13\u5B58\u65F6\u7528 CLI \u7684 \u2464 \u663E\u5F0F\u53C2\u6570 avantf-mem --data-home <path>\uFF08\u63D2\u4EF6\u5219\u662F profile \u7684\n# dataHome\uFF0C\u5C5E \u2461\uFF09\u3002\u672C\u6587\u4EF6\u5C31\u5728\u6570\u636E\u6839\u91CC\u9762\uFF0C\u89E3\u6790\u6839\u65F6\u8FD8\u6CA1\u8BFB\u5230\u5B83\uFF0C\u6240\u4EE5\u5199\u5728\u8FD9\u91CC\u7684 dataHome \u5BF9\u6570\u636E\u6839\u65E0\u6548\uFF08DESIGN \u00A73\uFF09\u3002\n\n# semantic: # \u5D4C\u5165\u540E\u7AEF\uFF08\u8BB0\u5FC6\u4E0E\u77E5\u8BC6\u5171\u7528\uFF0C\u8DE8\u5E93\u5206\u6570\u624D\u53EF\u6BD4\uFF09\n# backend: local_bge\n# local_model: Xenova/bge-base-zh-v1.5 # \u5FC5\u987B\u662F ONNX \u4ED3\u5E93\n# dim: 768\n# auto_download: true\n# auto_migrate: true # \u6362\u6A21\u578B/\u5BBD\u5EA6\u540E\u81EA\u52A8\u5206\u6279\u91CD\u7B97\u65E7\u7A7A\u95F4\u7684\u5411\u91CF\uFF08\u9ED8\u8BA4\u5F00\uFF09\uFF1B\u5173\u6389\u53EA\u5269\u542F\u52A8\u544A\u8B66 + vectors --fix\n# \u26A0\uFE0F cache_dir / mirror \u7531\u5BB6\u65CF\u5E95\u5EA7 @avantf/dsh-plugin-base \u7BA1\u7406\uFF1A\u5199\u5728\u8FD9\u91CC\u4F1A\u88AB\u5FFD\u7565\u5E76\u544A\u8B66\u3002\n# \u7F13\u5B58\u76EE\u5F55\u7528\u73AF\u5883\u53D8\u91CF AVANTF_MEM_MODEL_CACHE\uFF0C\u955C\u50CF\u7528 AVANTF_MEM_MODEL_MIRROR\uFF08\u6216 HF_ENDPOINT\uFF09\u3002\n\n# vectorStore:\n# backend: auto # auto = \u8D8A\u8FC7\u9608\u503C\u81EA\u52A8\u4ECE local_numpy \u8FC1\u5230 hnswlib\n# auto_thresholds:\n# hnswlib: 2000\n# hnswlib_ef_search: 256 # ANN \u7684\u53EC\u56DE/\u901F\u5EA6\u65CB\u94AE\uFF0C\u8BED\u6599\u53D8\u5927\u5148\u8C03\u5B83\n\n# retriever: # \u4E09\u6761\u68C0\u7D22\u817F\u5728\u878D\u5408\u91CC\u7684\u6743\u91CD\u4E0E\"\u76F8\u5173\u6027\u95E8\u69DB\"\n# weight_semantic: 0.55\n# weight_fts: 0.30\n# weight_jaccard: 0.15\n# # \u817F\u95E8\u69DB\u662F\u7EDD\u5BF9\u95E8\u69DB\uFF0C\u6253\u5728\u6BCF\u6761\u817F\u7684\u539F\u59CB\u5206\u4E0A\u3001fuse() \u4E4B\u524D\uFF1B0 = \u5173\u95ED\u8BE5\u95E8\u69DB\uFF0C\u5206\u6570\u7B49\u4E8E\u95E8\u69DB\u4FDD\u7559\u3002\n# # \u8BED\u4E49\u540E\u7AEF\u4E0D\u53EF\u7528\u65F6 min_fts_terms \u7684\u751F\u6548\u503C\u653E\u5BBD\u5230 1\u3002\n# min_semantic_similarity: 0.5 # \u8BED\u4E49\u817F\u4F59\u5F26\uFF1B0.5 \u53EA\u5BF9 bge-base-zh-v1.5/768\uFF08mean pooling\uFF09\u6807\u5B9A\u8FC7\uFF0C\u6362\u6A21\u578B\u8981\u91CD\u6807\n# min_fts_terms: 2 # FTS \u817F\uFF1A\u8FD9\u4E00\u884C\u547D\u4E2D\u51E0\u4E2A\u4E0D\u540C\u7684\u67E5\u8BE2\u8BCD\u5143\uFF08\u62C9\u4E01\u8BCD\u22655\u5B57\u7B26 + CJK 3-gram\uFF09\n# min_jaccard: 0.2 # \u5B9E\u4F53\u817F\uFF1A\u951A\u70B9 Jaccard \u7684**\u4E0B\u754C**\uFF1B\u5B9E\u9645\u8BC1\u636E\u95E8\u69DB\u518D\u6309\u951A\u70B9\u7A00\u6709\u5EA6\u52A0\u6743\uFF08\u00A720.19 \u7EED\uFF09\n# # min_score \u662F\u552F\u4E00\u7684**\u663E\u793A\u5206**\u95E8\u69DB\uFF0C\u4E5F\u662F UI \u663E\u793A\u7684\u90A3\u4E2A score\uFF1A\u5408\u5E76\u6C60\u6309\u6700\u5927\u878D\u5408\u5206\u5F52\u4E00\u5316\u540E\u7684\u503C\uFF0C\n# # \u662F**\u76F8\u5BF9\u91CF**\u2014\u2014\u540C\u4E00\u4E8B\u5B9E\u5728\u4E0D\u540C\u67E5\u8BE2\u91CC\u53EF\u80FD\u901A\u8FC7\u3001\u4E5F\u53EF\u80FD\u4E0D\u901A\u8FC7\uFF08\u53D6\u51B3\u4E8E\u540C\u4E00\u67E5\u8BE2\u91CC\u6709\u6CA1\u6709\u66F4\u5F3A\u7684\u5019\u9009\uFF09\u3002\n# # \u5B83\u53EA\u5E94\u7528\u4E00\u6B21\uFF1A\u5728\u5408\u5E76\u540E\u7684\u5019\u9009\u6C60\u4E0A\u3001\u6309 kind/source \u8FC7\u6EE4\u4E0E slice \u4E4B\u524D\uFF08mem_recall \u4E0E UI\n# # \u56E0\u6B64\u662F\u540C\u4E00\u4E2A\u523B\u5EA6\uFF09\uFF1B0 = \u5173\u95ED\uFF0C\u5206\u6570\u7B49\u4E8E\u95E8\u69DB\u4FDD\u7559\uFF1B\u653E\u5BBD\u6863\u53EA\u964D\u817F\u95E8\u69DB\uFF0C\u4E0D\u964D\u5B83\u3002\n# # \u26A0\uFE0F \u88AB\u5B83\u4E22\u5F03\u7684\u6761\u76EE\u4E0D\u8FDB `dropped_by_floor`\uFF0C\u56E0\u6B64 UI \u7684\u300C\u4E25\u683C\u95E8\u69DB\u4E22\u5F03\u4E86 N \u6761\u300D**\u4E0D\u542B**\u8FD9\u4E00\u7C7B\n# # \uFF08\u4FE1\u5C01\u91CC\u5355\u72EC\u6709 dropped_by_score\uFF1B\u9762\u677F\u8FD9\u4E00\u7248\u8FD8\u6CA1\u6E32\u67D3\u5B83\uFF09\u3002\n# min_score: 0.5\n# # P-11 \u4E2D\u6587\u65F6\u95F4\u7A97\u817F\uFF08\u9ED8\u8BA4\u5173\uFF09\uFF1A\u5F00\u542F\u540E\uFF0C\u5E26\u4E2D\u6587\u65F6\u95F4\u8868\u8FBE\u7684\u67E5\u8BE2\uFF08\u4E0A\u4E2A\u6708 / \u6700\u8FD1\u4E09\u5929 / 2026\u5E749\u6708 \u2026\uFF09\n# # \u4F1A\u989D\u5916\u628A\"\u4E8B\u4EF6\u65F6\u95F4 valid_from \u843D\u5728\u8BE5\u7A97\u53E3\"\u7684\u6D3B\u8DC3\u4E8B\u5B9E\u4F5C\u4E3A\u4E00\u6761\u817F\u53C2\u4E0E\u878D\u5408\u3002\u5B83\u662F**\u52A0\u4E00\u6761\u817F\u3001\u4E0D\u662F\u8FC7\u6EE4\u5668**\uFF1A\n# # \u53EA\u589E\u52A0\u5019\u9009\uFF0C\u7EDD\u4E0D\u5220\u6389\u8BED\u4E49 / FTS / \u5B9E\u4F53\u817F\u5DF2\u7ECF\u627E\u5230\u7684\u4E8B\u5B9E\uFF1B\u6CA1\u6709\u4E8B\u4EF6\u65F6\u95F4\u7684\u4E8B\u5B9E\u4E0D\u4F5C\u4E3A\u8BE5\u817F\u5019\u9009\uFF0C\n# # \u4E5F**\u4E0D\u56DE\u9000\u5230\u5199\u5165\u65F6\u95F4 created_at**\u3002\u5B83\u5171\u7528 jaccard \u6743\u91CD\u4F4D\uFF08\u5BF9\u5916\u7684\u4E09\u952E\u6743\u91CD\u4E0D\u53D8\uFF09\u3002\n# # \u4EC0\u4E48\u65F6\u5019\u8BE5\u5F00\uFF1A\u5148\u7528 mem_admin stats \u770B validity.coverage\uFF08\u6709\u591A\u5C11\u6D3B\u8DC3\u4E8B\u5B9E\u5E26\u4E8B\u4EF6\u65F6\u95F4\uFF09\uFF0C\n# # \u518D\u7528\u56DE\u5F52\u7F51\u786E\u8BA4\u5B83\u786E\u5B9E\u6539\u5584\u65F6\u95F4\u7C7B\u67E5\u8BE2\u2014\u2014\u8986\u76D6\u7387\u22480 \u65F6\u5B83\u7B49\u4E8E\u6CA1\u5F00\u3002\u5199\u5165\u4E8B\u4EF6\u65F6\u95F4\u7528 mem_remember \u7684 event_date\u3002\n# time_window: false\n\n# lifecycle:\n# purge_after_archived_days: 365 # \u5F52\u6863\u4E8B\u5B9E\u7684\u7269\u7406\u6E05\u7406\u7A97\u53E3\uFF08\u6D3B\u8DC3\u65E5\uFF09\n# contradiction_threshold: 0.6\n\n# trust:\n# decay_per_day: 0.0055\u2026\n# presence:\n# heartbeat_minutes: 60 # \u5E38\u9A7B\u8FDB\u7A0B\u7684\u5FC3\u8DF3\uFF08\u6D3B\u8DC3\u65E5\u65F6\u949F\u4E0E\u751F\u547D\u5468\u671F\u626B\u63CF\uFF09\n\n# tools: # \u5916\u90E8\u4E8C\u8FDB\u5236\uFF08pandoc\uFF09\u7684\u53D7\u7BA1\u76EE\u5F55\n# dir: '' # \u7559\u7A7A = \u5BB6\u65CF\u6839 tools\uFF08~/.avantf/env/tools\uFF09\uFF1BAVANTF_TOOLS_DIR \u53EF\u8986\u76D6\n# auto_install: true\n";
18
+ export declare const DEFAULT_COMMON_CONFIG = "# avantf-mem \u516C\u5171\u914D\u7F6E\uFF08\u5206\u5C42\u7B2C\u2461\u5C42\uFF09\u3002\u8FD9\u91CC\u5168\u662F\u6CE8\u91CA\uFF1A\u5220\u6389/\u7559\u7A7A = \u7528\u5185\u5EFA\u9ED8\u8BA4\uFF0C\u53D6\u6D88\u6CE8\u91CA\u5373\u751F\u6548\u3002\n# \u5206\u5C42\uFF08\u4F4E\u2192\u9AD8\uFF09\uFF1A\u2460 \u5185\u5EFA\u9ED8\u8BA4 \u2192 \u2461 \u672C\u6587\u4EF6 \u2192 \u2462 \u540C\u76EE\u5F55\u7684 memory.yaml\uFF08\u7F3A\u5931=\u4E0D\u8986\u76D6\uFF09\n# \u2192 \u2463 \u73AF\u5883\u53D8\u91CF \u2192 \u2464 \u63D2\u4EF6/CLI \u663E\u5F0F\u4F20\u53C2\n# \u6240\u6709\u53EF\u7F16\u8F91\u6587\u672C\u90FD\u5728 ~/.avantf \u4E0B\uFF1Aconfigs/*.yaml\uFF08\u672C\u76EE\u5F55\uFF09\u4E0E prompts/*.md\uFF08\u6A21\u578B\u63D0\u793A\u8BCD\uFF09\u3002\n\n# \u6570\u636E\u6839\u4E0D\u5728\u8FD9\u91CC\u914D\uFF1A\u5B83\u7531 \u2463 AVANTF_HOME\uFF08\u5BB6\u65CF\u6839\uFF1A\u540C\u65F6\u51B3\u5B9A\u6570\u636E\u6839\u4E0E models/tools\uFF09\u51B3\u5B9A \u2014\u2014\n# \u53EA\u60F3\u6362\u6570\u636E\u6839\u3001\u4E0D\u642C\u6A21\u578B\u7F13\u5B58\u65F6\u7528 CLI \u7684 \u2464 \u663E\u5F0F\u53C2\u6570 avantf-mem --data-home <path>\uFF08\u63D2\u4EF6\u5219\u662F profile \u7684\n# dataHome\uFF0C\u5C5E \u2461\uFF09\u3002\u672C\u6587\u4EF6\u5C31\u5728\u6570\u636E\u6839\u91CC\u9762\uFF0C\u89E3\u6790\u6839\u65F6\u8FD8\u6CA1\u8BFB\u5230\u5B83\uFF0C\u6240\u4EE5\u5199\u5728\u8FD9\u91CC\u7684 dataHome \u5BF9\u6570\u636E\u6839\u65E0\u6548\uFF08DESIGN \u00A73\uFF09\u3002\n\n# semantic: # \u5D4C\u5165\u540E\u7AEF\uFF08\u8BB0\u5FC6\u4E0E\u77E5\u8BC6\u5171\u7528\uFF0C\u8DE8\u5E93\u5206\u6570\u624D\u53EF\u6BD4\uFF09\n# backend: local_bge\n# local_model: Xenova/bge-base-zh-v1.5 # \u5FC5\u987B\u662F ONNX \u4ED3\u5E93\n# dim: 768\n# auto_download: true\n# auto_migrate: true # \u6362\u6A21\u578B/\u5BBD\u5EA6\u540E\u81EA\u52A8\u5206\u6279\u91CD\u7B97\u65E7\u7A7A\u95F4\u7684\u5411\u91CF\uFF08\u9ED8\u8BA4\u5F00\uFF09\uFF1B\u5173\u6389\u53EA\u5269\u542F\u52A8\u544A\u8B66 + vectors --fix\n# self_heal: # \u8BED\u4E49\u4F9D\u8D56\u81EA\u6108\uFF08\u9ED8\u8BA4\u5173\uFF09\uFF1A\u5141\u8BB8\u542F\u52A8\u671F\u5B89\u88C5\u7F3A\u5931\u7684 @huggingface/transformers\n# enabled: false # \u6253\u5F00\u540E\u8D70\u5E95\u5EA7 runtime-deps \u5171\u4EAB\u6839\uFF08base \u2265 0.7.0 / interface v6\uFF09\uFF1B\u5931\u8D25\u53EA\u964D\u7EA7\u5230 FTS+\u5B9E\u4F53\n# # \u88C5\u54EA\u4E2A\u5305 / \u4ECE\u54EA\u88C5 / \u63D0\u793A\u8BF4\u4EC0\u4E48\uFF1A\u89C1\u63D2\u4EF6\u81EA\u5E26\u7684 res/runtime-deps.yaml\uFF08\u968F\u5305\u53D1\u5E03\uFF09\n# native_self_heal: # \u539F\u751F\u53EF\u9009\u4F9D\u8D56\u81EA\u6108\uFF1Anodejieba / hnswlib-node\uFF08\u9ED8\u8BA4\u5173\uFF09\n# enabled: false # \u88C5\u54EA\u4E2A\u5305\u3001\u4ECE\u54EA\u88C5\u3001\u63D0\u793A\u8BF4\u4EC0\u4E48\uFF0C\u90FD\u7531\u63D2\u4EF6\u81EA\u5E26\u7684 res/runtime-deps.yaml \u51B3\u5B9A\n# \u26A0\uFE0F cache_dir / mirror \u7531\u5BB6\u65CF\u5E95\u5EA7 @avantf/dsh-plugin-base \u7BA1\u7406\uFF1A\u5199\u5728\u8FD9\u91CC\u4F1A\u88AB\u5FFD\u7565\u5E76\u544A\u8B66\u3002\n# \u7F13\u5B58\u76EE\u5F55\u7528\u73AF\u5883\u53D8\u91CF AVANTF_MEM_MODEL_CACHE\uFF0C\u955C\u50CF\u7528 AVANTF_MEM_MODEL_MIRROR\uFF08\u6216 HF_ENDPOINT\uFF09\u3002\n\n# vectorStore:\n# backend: auto # auto = \u8D8A\u8FC7\u9608\u503C\u81EA\u52A8\u4ECE local_numpy \u8FC1\u5230 hnswlib\n# auto_thresholds:\n# hnswlib: 2000\n# hnswlib_ef_search: 256 # ANN \u7684\u53EC\u56DE/\u901F\u5EA6\u65CB\u94AE\uFF0C\u8BED\u6599\u53D8\u5927\u5148\u8C03\u5B83\n\n# retriever: # \u4E09\u6761\u68C0\u7D22\u817F\u5728\u878D\u5408\u91CC\u7684\u6743\u91CD\u4E0E\"\u76F8\u5173\u6027\u95E8\u69DB\"\n# weight_semantic: 0.55\n# weight_fts: 0.30\n# weight_jaccard: 0.15\n# # \u817F\u95E8\u69DB\u662F\u7EDD\u5BF9\u95E8\u69DB\uFF0C\u6253\u5728\u6BCF\u6761\u817F\u7684\u539F\u59CB\u5206\u4E0A\u3001fuse() \u4E4B\u524D\uFF1B0 = \u5173\u95ED\u8BE5\u95E8\u69DB\uFF0C\u5206\u6570\u7B49\u4E8E\u95E8\u69DB\u4FDD\u7559\u3002\n# # \u8BED\u4E49\u540E\u7AEF\u4E0D\u53EF\u7528\u65F6 min_fts_terms \u7684\u751F\u6548\u503C\u653E\u5BBD\u5230 1\u3002\n# min_semantic_similarity: 0.5 # \u8BED\u4E49\u817F\u4F59\u5F26\uFF1B0.5 \u53EA\u5BF9 bge-base-zh-v1.5/768\uFF08mean pooling\uFF09\u6807\u5B9A\u8FC7\uFF0C\u6362\u6A21\u578B\u8981\u91CD\u6807\n# min_fts_terms: 2 # FTS \u817F\uFF1A\u8FD9\u4E00\u884C\u547D\u4E2D\u51E0\u4E2A\u4E0D\u540C\u7684\u67E5\u8BE2\u8BCD\u5143\uFF08\u62C9\u4E01\u8BCD\u22655\u5B57\u7B26 + CJK 3-gram\uFF09\n# min_jaccard: 0.2 # \u5B9E\u4F53\u817F\uFF1A\u951A\u70B9 Jaccard \u7684**\u4E0B\u754C**\uFF1B\u5B9E\u9645\u8BC1\u636E\u95E8\u69DB\u518D\u6309\u951A\u70B9\u7A00\u6709\u5EA6\u52A0\u6743\uFF08\u00A720.19 \u7EED\uFF09\n# # min_score \u662F\u552F\u4E00\u7684**\u663E\u793A\u5206**\u95E8\u69DB\uFF0C\u4E5F\u662F UI \u663E\u793A\u7684\u90A3\u4E2A score\uFF1A\u5408\u5E76\u6C60\u6309\u6700\u5927\u878D\u5408\u5206\u5F52\u4E00\u5316\u540E\u7684\u503C\uFF0C\n# # \u662F**\u76F8\u5BF9\u91CF**\u2014\u2014\u540C\u4E00\u4E8B\u5B9E\u5728\u4E0D\u540C\u67E5\u8BE2\u91CC\u53EF\u80FD\u901A\u8FC7\u3001\u4E5F\u53EF\u80FD\u4E0D\u901A\u8FC7\uFF08\u53D6\u51B3\u4E8E\u540C\u4E00\u67E5\u8BE2\u91CC\u6709\u6CA1\u6709\u66F4\u5F3A\u7684\u5019\u9009\uFF09\u3002\n# # \u5B83\u53EA\u5E94\u7528\u4E00\u6B21\uFF1A\u5728\u5408\u5E76\u540E\u7684\u5019\u9009\u6C60\u4E0A\u3001\u6309 kind/source \u8FC7\u6EE4\u4E0E slice \u4E4B\u524D\uFF08mem_recall \u4E0E UI\n# # \u56E0\u6B64\u662F\u540C\u4E00\u4E2A\u523B\u5EA6\uFF09\uFF1B0 = \u5173\u95ED\uFF0C\u5206\u6570\u7B49\u4E8E\u95E8\u69DB\u4FDD\u7559\uFF1B\u653E\u5BBD\u6863\u53EA\u964D\u817F\u95E8\u69DB\uFF0C\u4E0D\u964D\u5B83\u3002\n# # \u26A0\uFE0F \u88AB\u5B83\u4E22\u5F03\u7684\u6761\u76EE\u4E0D\u8FDB `dropped_by_floor`\uFF0C\u56E0\u6B64 UI \u7684\u300C\u4E25\u683C\u95E8\u69DB\u4E22\u5F03\u4E86 N \u6761\u300D**\u4E0D\u542B**\u8FD9\u4E00\u7C7B\n# # \uFF08\u4FE1\u5C01\u91CC\u5355\u72EC\u6709 dropped_by_score\uFF1B\u9762\u677F\u8FD9\u4E00\u7248\u8FD8\u6CA1\u6E32\u67D3\u5B83\uFF09\u3002\n# min_score: 0.5\n# # P-11 \u4E2D\u6587\u65F6\u95F4\u7A97\u817F\uFF08\u9ED8\u8BA4\u5173\uFF09\uFF1A\u5F00\u542F\u540E\uFF0C\u5E26\u4E2D\u6587\u65F6\u95F4\u8868\u8FBE\u7684\u67E5\u8BE2\uFF08\u4E0A\u4E2A\u6708 / \u6700\u8FD1\u4E09\u5929 / 2026\u5E749\u6708 \u2026\uFF09\n# # \u4F1A\u989D\u5916\u628A\"\u4E8B\u4EF6\u65F6\u95F4 valid_from \u843D\u5728\u8BE5\u7A97\u53E3\"\u7684\u6D3B\u8DC3\u4E8B\u5B9E\u4F5C\u4E3A\u4E00\u6761\u817F\u53C2\u4E0E\u878D\u5408\u3002\u5B83\u662F**\u52A0\u4E00\u6761\u817F\u3001\u4E0D\u662F\u8FC7\u6EE4\u5668**\uFF1A\n# # \u53EA\u589E\u52A0\u5019\u9009\uFF0C\u7EDD\u4E0D\u5220\u6389\u8BED\u4E49 / FTS / \u5B9E\u4F53\u817F\u5DF2\u7ECF\u627E\u5230\u7684\u4E8B\u5B9E\uFF1B\u6CA1\u6709\u4E8B\u4EF6\u65F6\u95F4\u7684\u4E8B\u5B9E\u4E0D\u4F5C\u4E3A\u8BE5\u817F\u5019\u9009\uFF0C\n# # \u4E5F**\u4E0D\u56DE\u9000\u5230\u5199\u5165\u65F6\u95F4 created_at**\u3002\u5B83\u5171\u7528 jaccard \u6743\u91CD\u4F4D\uFF08\u5BF9\u5916\u7684\u4E09\u952E\u6743\u91CD\u4E0D\u53D8\uFF09\u3002\n# # \u4EC0\u4E48\u65F6\u5019\u8BE5\u5F00\uFF1A\u5148\u7528 mem_admin stats \u770B validity.coverage\uFF08\u6709\u591A\u5C11\u6D3B\u8DC3\u4E8B\u5B9E\u5E26\u4E8B\u4EF6\u65F6\u95F4\uFF09\uFF0C\n# # \u518D\u7528\u56DE\u5F52\u7F51\u786E\u8BA4\u5B83\u786E\u5B9E\u6539\u5584\u65F6\u95F4\u7C7B\u67E5\u8BE2\u2014\u2014\u8986\u76D6\u7387\u22480 \u65F6\u5B83\u7B49\u4E8E\u6CA1\u5F00\u3002\u5199\u5165\u4E8B\u4EF6\u65F6\u95F4\u7528 mem_remember \u7684 event_date\u3002\n# time_window: false\n\n# lifecycle:\n# purge_after_archived_days: 365 # \u5F52\u6863\u4E8B\u5B9E\u7684\u7269\u7406\u6E05\u7406\u7A97\u53E3\uFF08\u6D3B\u8DC3\u65E5\uFF09\n# contradiction_threshold: 0.6\n\n# trust:\n# decay_per_day: 0.0055\u2026\n# presence:\n# heartbeat_minutes: 60 # \u5E38\u9A7B\u8FDB\u7A0B\u7684\u5FC3\u8DF3\uFF08\u6D3B\u8DC3\u65E5\u65F6\u949F\u4E0E\u751F\u547D\u5468\u671F\u626B\u63CF\uFF09\n\n# tools: # \u5916\u90E8\u4E8C\u8FDB\u5236\uFF08pandoc\uFF09\u7684\u53D7\u7BA1\u76EE\u5F55\n# dir: '' # \u7559\u7A7A = \u5BB6\u65CF\u6839 tools\uFF08~/.avantf/env/tools\uFF09\uFF1BAVANTF_TOOLS_DIR \u53EF\u8986\u76D6\n# auto_install: true\n";
19
19
  /**
20
20
  * Ensure the common config exists with content, and hand back the path either way.
21
21
  *
@@ -5,7 +5,10 @@
5
5
  * They are the ENGINE's consumption points of numbers the CONTRACT documents:
6
6
  *
7
7
  * - `DEFAULT_SEARCH_LIMIT` — `query`'s own input schema carries `.default(10)`
8
- * (`contract/src/tools.ts`), and the recall actions' descriptions say "默认 10"; the recall union
8
+ * (`contract/src/tools.ts`), and the recall actions' descriptions say "默认 10" for the
9
+ * exact/facts pages and "默认 50" for a pure time window — the window's page is
10
+ * `TIME_DEFAULT_LIMIT`, which `timePage` applies itself instead of this fallback (see
11
+ * `store/recall_policy.ts`); the recall union
9
12
  * itself is `.optional()`, so the engine is what applies it. It reaches `hybridSearch` (a
10
13
  * non-finite limit).
11
14
  * - `DEFAULT_LIST_LIMIT` — the documented page for `admin list` ("默认 50").
@@ -1,17 +1,3 @@
1
- /**
2
- * Entity extraction: POS tagging and the noun-ish names that become `entities` rows.
3
- *
4
- * Uses nodejieba (optional dependency) for POS; degrades gracefully to a character/regex fallback
5
- * when unavailable, exactly like the `jieba_available()` guard.
6
- *
7
- * Note: nodejieba's `tag()` returns `{ word, tag }` objects, and its POS flag set
8
- * differs from Python jieba (e.g. proper nouns → `nr`, 名动词 → `vn`).
9
- *
10
- * WHAT THIS MODULE IS NOT. It owns what is an entity NAME — nothing about relations. The relation
11
- * half (SVO candidate triples, explicit patterns, the name/statement/confidence gates and the
12
- * predicate vocabulary) lives in `relations/`; the two halves share exactly one thing, the tagging
13
- * pass ({@link tagText}), so a write pays for the dictionary once and drives both.
14
- */
15
1
  export interface ExtractedEntity {
16
2
  name: string;
17
3
  type: string;
@@ -22,6 +8,16 @@ export interface PosToken {
22
8
  word: string;
23
9
  tag: string;
24
10
  }
11
+ /**
12
+ * Point the optional tokenizer at a shared module root (the base `runtime-deps` handle's directory).
13
+ *
14
+ * The memo follows the ROOT: changing it forgets whatever the previous root produced, so the next
15
+ * load resolves from the new one — that is the whole point of setting a root. The cost is at most one
16
+ * extra dictionary parse (~1 s, once) when a root arrives after a lazily loaded copy from this tree;
17
+ * Node's own module cache keeps the import itself cheap. The plugin only sets this once, when its
18
+ * native self-heal item settles.
19
+ */
20
+ export declare function useTokenizerModuleDir(dir: string | undefined): void;
25
21
  /**
26
22
  * Attempts made so far — the mechanism, not the wall time.
27
23
  *
@@ -32,6 +28,30 @@ export interface PosToken {
32
28
  */
33
29
  export declare function jiebaLoadAttempts(): number;
34
30
  export declare function jiebaAvailable(): Promise<boolean>;
31
+ /**
32
+ * What the tokenizer's load can answer, including WHY it is unavailable — the observation a startup
33
+ * warm reports (`modelBootstrap.ts`).
34
+ *
35
+ * `attemptedRoot` vs `root` is the part that makes a failure readable: a probe that ran with no
36
+ * provisioned root in force (`attemptedRoot === undefined`) reports "this tree has no loadable copy",
37
+ * while a root that arrived afterwards (`root !== undefined`) is exactly the recovery
38
+ * {@link useTokenizerModuleDir} promises — and the two produce a different log line, instead of one
39
+ * "unavailable" that a later "ready" would contradict.
40
+ */
41
+ export interface JiebaStatus {
42
+ /** Whether `tag()` is usable now (the memoized load succeeded). */
43
+ readonly available: boolean;
44
+ /** Parses actually run in this process. */
45
+ readonly attempts: number;
46
+ /** The root the last attempt resolved from (`undefined` = this tree). */
47
+ readonly attemptedRoot: string | undefined;
48
+ /** The provisioned root in force NOW (it may have arrived after the attempt). */
49
+ readonly root: string | undefined;
50
+ /** Why the last attempt failed (`undefined` when it succeeded, or when nothing was attempted). */
51
+ readonly error: string | undefined;
52
+ }
53
+ /** The status of the memoized load, starting one only when none has run yet. */
54
+ export declare function jiebaProbe(): Promise<JiebaStatus>;
35
55
  /**
36
56
  * Version of the ENTITY extraction rules below (accepted tags, the regex fallback).
37
57
  *
@@ -35,3 +35,4 @@ export * from './store/floors.js';
35
35
  export { registerSemanticBackend, registerVectorStore, type SemanticBackend, type VectorStore, } from '../mem-retrieval/index.js';
36
36
  export * from './store/relation_backfill.js';
37
37
  export { extractRelationCandidates, normalizeRelation, evaluateRelation, type RelationCandidate, type RelationEvaluation, type RelationExtractor, } from './relations/index.js';
38
+ export { useTokenizerModuleDir } from './entities/extract.js';
@@ -1,3 +1,4 @@
1
+ import { type JiebaStatus } from './entities/extract.js';
1
2
  import type { AvantfRuntime } from './runtime.js';
2
3
  /** The statuses the base's verdict can carry (kept structural — see the note above). */
3
4
  export type DshCompatStatus = 'ok' | 'version-mismatch' | 'version-unknown' | 'probe-skipped' | 'probe-failed';
@@ -72,6 +73,34 @@ export declare function warmModels(rt: AvantfRuntime, options?: WarmOptions, rep
72
73
  * @returns how long the idle gate waited (ms), or `0` when it was not requested.
73
74
  */
74
75
  export declare function awaitStartupGate(options?: WarmOptions): Promise<number>;
76
+ /**
77
+ * The tokenizer states this module reports — one line per observation, in the order they happen.
78
+ *
79
+ * Process-wide on purpose, because the thing being described is: `entities/extract.ts` memoizes the
80
+ * load per process, and a provisioned module root that arrives later (`useTokenizerModuleDir`) can
81
+ * turn the same memo from "unavailable" into "ready". Without the previous verdict a slow repair
82
+ * prints a bare contradiction (`unavailable in 1ms` … `ready in 623ms`); with it the second line
83
+ * says it is a recovery, and the first one knows enough to say it was not final.
84
+ */
85
+ export type TokenizerVerdict = 'unknown' | 'ready' | 'degraded';
86
+ /** One line to log, and the verdict it leaves behind. */
87
+ export interface TokenizerReport {
88
+ level: 'info' | 'warn';
89
+ message: string;
90
+ next: TokenizerVerdict;
91
+ }
92
+ /**
93
+ * The line one tokenizer observation deserves.
94
+ *
95
+ * `previous` is what makes the pair readable: a `ready` after a `degraded` is a RECOVERY (the
96
+ * earlier probe ran before a loadable copy existed — the provisioned root had not been handed over,
97
+ * or the package was not installed yet), not a change of story. A `degraded` line never claims more
98
+ * than it knows: it names the reason and the two ways to fix it, and says that a loadable copy
99
+ * arriving later is picked up automatically.
100
+ *
101
+ * Exported for the test that pins those transitions; it has no side effects.
102
+ */
103
+ export declare function tokenizerReport(status: JiebaStatus, elapsedMs: number, previous: TokenizerVerdict): TokenizerReport;
75
104
  /**
76
105
  * Non-blocking embedding warm, for a caller that knows the model files are already on disk.
77
106
  *
@@ -1,6 +1,6 @@
1
1
  /**
2
- * The memory adapter for the shared `@avantf/graph-recall` port
3
- * (docs/GRAPH_RECALL_LIBRARY.md §6, Phase 3b).
2
+ * The memory adapter for the shared `@avantf/graph-recall` port (Phase 3b). The port — and the
3
+ * obligations an adapter must honour — is documented in that package's README.
4
4
  *
5
5
  * WHY THIS FILE EXISTS. The library owns the traversal algorithms (one hop, reverse, two hops, AND,
6
6
  * neighbours) and knows nothing about SQLite; memory owns the storage and therefore must supply a
@@ -104,7 +104,13 @@ export interface SearchInput {
104
104
  * verify it, so it must come from the same backend.
105
105
  */
106
106
  queryVector?: Float32Array;
107
- /** Per-call output token budget; `0` = unlimited, omitted = `retriever.max_output_tokens`. */
107
+ /**
108
+ * Per-call output token budget; `0` = unlimited, omitted = `retriever.max_output_tokens`.
109
+ *
110
+ * A pure time recall (no `query`) is the one exception: omitting it there means NO budget at all.
111
+ * That page is already bounded by its own `limit` (50 by default), so a budget on top would only
112
+ * rewrite the text of rows the caller asked to see (see `timePage`).
113
+ */
108
114
  maxTokens?: number;
109
115
  /**
110
116
  * Relevance-floor profile for this query. Omitted = the default policy: the configured (strict)
@@ -242,7 +248,10 @@ export interface TitleInput {
242
248
  export interface MemorySearchInput extends Omit<SearchInput, 'query'> {
243
249
  /** The literal query. Omitted = a pure time window (then `time` is required). */
244
250
  query?: string;
245
- /** The exact page's first row (default 0). Only read when the literal pass matches. */
251
+ /**
252
+ * The page's first row (default 0). Read by the exact/facts passes and by the pure time window,
253
+ * where it walks the window one page at a time (see `timePage`).
254
+ */
246
255
  offset?: number;
247
256
  }
248
257
  export declare class MemoryStore {
@@ -455,6 +464,30 @@ export declare class MemoryStore {
455
464
  private sweepConflicts;
456
465
  /** Async bootstrap the semantic backend (download/config model) without blocking. */
457
466
  warmupSemantic(): Promise<boolean>;
467
+ /**
468
+ * Point the semantic backend at a shared runtime-dependency root (the base provider's
469
+ * `runtime-deps` handle) before it warms. Best-effort by contract: a backend without external
470
+ * module resolution ignores it, and a missing root is reported by the backend itself.
471
+ */
472
+ useSemanticModuleDir(dir: string): void;
473
+ /**
474
+ * Point the vector store at a shared runtime-dependency root for its NATIVE binding
475
+ * (`hnswlib-node`), after the DSH plugin's native self-heal item is ready.
476
+ *
477
+ * Same best-effort contract as {@link useSemanticModuleDir}: a store with no native binding (the
478
+ * brute-force backend, or an `auto` store still on it) ignores it, and a store that already fell
479
+ * back to brute force rebuilds its tracked vectors under the new root. Nothing here decides whether
480
+ * a root should exist — the caller's dependency audit does.
481
+ */
482
+ useNativeModuleDir(dir: string): void;
483
+ /**
484
+ * Vectors currently in the LIVE index (sync, one map read).
485
+ *
486
+ * Exposed for the plugin's dependency audit: an `hnswlib-node` notice is conditioned on the corpus
487
+ * actually being large enough for an ANN index to matter (`min_vectors`), and a session line that
488
+ * asks "how big is the store right now" must not run a database query to answer.
489
+ */
490
+ liveVectorCount(): number;
458
491
  /** List contradiction candidates (`resolved` 0 = open, 1 = closed with a recorded verdict). */
459
492
  listContradictions(opts?: {
460
493
  resolved?: number;
@@ -698,7 +731,10 @@ export declare class MemoryStore {
698
731
  * The exact, facts and relation passes reinforce the facts they return, exactly like the fuzzy
699
732
  * pass does for its hits: all are recalls the caller actually reads. A caller that must stay
700
733
  * read-only passes `track: false` (the conditional hint's probe does). Exact, facts, relation and
701
- * time also honour the caller's output budget (their text is the caller's to bound).
734
+ * time also honour the caller's output budget (their text is the caller's to bound). The one
735
+ * difference is the DEFAULT: a time window applies no budget unless the caller names one — its
736
+ * page size (`limit`) is what bounds the list, and the budget would only rewrite rows the caller
737
+ * asked for (see {@link timePage}).
702
738
  *
703
739
  * It does NOT emit a retrieval-health event. Those counters describe the retrieval PIPELINE
704
740
  * (legs, fusion, semantic-live rate); none of an exact, facts, relation or time answer ran a leg,
@@ -714,9 +750,20 @@ export declare class MemoryStore {
714
750
  * happened that day" reads in the order it happened; `score` is the constant 1 exactly like the
715
751
  * exact page's, because there is no relevance number here for the model to compare.
716
752
  *
717
- * It honours `kind`/`source`, pages with the same `offset`/`limit` (default 10, cap 50) and the
718
- * same `max_tokens` budget as every other recall, and reinforces the rows it returns unless
719
- * `track: false` — a window the caller reads is a recall like any other.
753
+ * The PAGE is the contract's, not the caller's guess: `limit` omitted means one page of
754
+ * {@link TIME_DEFAULT_LIMIT} rows (the same 50 the contract caps an explicit page at), and an
755
+ * explicit `limit` (1-50) still wins. A window that holds more than one page is the normal case
756
+ * for a busy day, so the envelope does not leave the caller to infer the shortfall: it reports
757
+ * `remaining = total - (offset + hits.length)`, the count still unseen.
758
+ *
759
+ * The output budget is honoured only when the caller names it (`max_tokens`). Falling back to the
760
+ * configured `retrieval.max_output_tokens` here would rewrite the text of a window's rows behind a
761
+ * page the caller already bounded with `limit` — the page size is the knob for "how much of the
762
+ * list", and the budget stays a knob for a RANKED result (exact/facts/relation/hybrid unchanged).
763
+ * `max_tokens` (including the contract's `0` = unlimited) is passed through unchanged.
764
+ *
765
+ * It honours `kind`/`source`, and reinforces the rows it returns unless `track: false` — a window
766
+ * the caller reads is a recall like any other.
720
767
  */
721
768
  private timePage;
722
769
  /**
@@ -5,6 +5,16 @@ export declare const EXACT_MIN_LATIN = 3;
5
5
  /** The default page of an exact result, and its hard cap. */
6
6
  export declare const EXACT_DEFAULT_LIMIT = 10;
7
7
  export declare const EXACT_MAX_LIMIT = 50;
8
+ /**
9
+ * The default page of a PURE TIME window (`mem_recall.search` with no `query`), and its cap.
10
+ *
11
+ * A window is a list, not a ranked shortlist: the caller asked "what happened then", and one page of
12
+ * ten would silently answer a tenth of the day. It is therefore answered with the widest page the
13
+ * contract allows (`EXACT_MAX_LIMIT`), and the envelope states how much is left
14
+ * (`remaining`) instead of leaving the caller to infer it from `truncated`. Omitting `limit` never
15
+ * means "everything": a window that has outgrown one page is exactly when a cap matters.
16
+ */
17
+ export declare const TIME_DEFAULT_LIMIT = 50;
8
18
  /**
9
19
  * The widest literal match set that still counts as an ANSWER (P1-2, REVIEW-2026-10-24).
10
20
  *
@@ -8,6 +8,135 @@ import { z } from 'zod';
8
8
  * past the window SILENTLY, so every derivation has to agree on the number.
9
9
  */
10
10
  export declare const DEFAULT_MODEL_WINDOW_TOKENS = 512;
11
+ /** One audit entry. `kind` decides which probe runs, and which of `specifier`/`item` is required. */
12
+ export declare const DependencyAuditEntrySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
13
+ id: z.ZodString;
14
+ severity: z.ZodEnum<{
15
+ info: "info";
16
+ warning: "warning";
17
+ degraded: "degraded";
18
+ }>;
19
+ when: z.ZodEnum<{
20
+ degraded: "degraded";
21
+ failed: "failed";
22
+ missing: "missing";
23
+ }>;
24
+ min_vectors: z.ZodOptional<z.ZodNumber>;
25
+ body: z.ZodString;
26
+ kind: z.ZodLiteral<"module">;
27
+ specifier: z.ZodString;
28
+ }, z.core.$strip>, z.ZodObject<{
29
+ id: z.ZodString;
30
+ severity: z.ZodEnum<{
31
+ info: "info";
32
+ warning: "warning";
33
+ degraded: "degraded";
34
+ }>;
35
+ when: z.ZodEnum<{
36
+ degraded: "degraded";
37
+ failed: "failed";
38
+ missing: "missing";
39
+ }>;
40
+ min_vectors: z.ZodOptional<z.ZodNumber>;
41
+ body: z.ZodString;
42
+ kind: z.ZodLiteral<"runtime-deps">;
43
+ item: z.ZodString;
44
+ }, z.core.$strip>], "kind">;
45
+ /** One configured audit entry, discriminated by `kind`. */
46
+ export type DependencyAuditEntry = z.infer<typeof DependencyAuditEntrySchema>;
47
+ /**
48
+ * One `runtime-deps` declaration as SHIPPED WITH THE PLUGIN (`packages/plugin/res/runtime-deps.yaml`).
49
+ *
50
+ * WHY IT IS A RESOURCE AND NOT USER CONFIG. The package set, the sources, the attempts, the verify
51
+ * list and the audit sentences are facts about THIS BUILD: which module the local backend imports,
52
+ * which native bindings this release looks for, what a session should be told when one of them is
53
+ * absent. Leaving them to `configs/common.yaml` made every deployment responsible for keeping that
54
+ * list correct — a stale copy would install a module no code loads, or silently drop the audit — and
55
+ * the shipped defaults were only ever the plugin's own declaration anyway. The operator keeps the one
56
+ * thing that really is a per-deployment decision (the two `enabled` switches); the DATA travels in the
57
+ * package, next to the code that reads it.
58
+ *
59
+ * Validation is strict on purpose: the file is read from the INSTALL directory at mount, and a
60
+ * malformed one is a packaging bug. The plugin logs it and degrades (no self-heal item, no audit)
61
+ * rather than throwing out of the mount — see `packages/plugin/src/runtime_deps_resource.ts`.
62
+ *
63
+ * The schema is the SINGLE definition of the file's shape: the loader validates with it, and the
64
+ * plugin maps it to the base provider's objects field by field (the base never reads YAML).
65
+ */
66
+ export declare const RuntimeDepsResourceSchema: z.ZodObject<{
67
+ version: z.ZodLiteral<1>;
68
+ semantic: z.ZodObject<{
69
+ packages: z.ZodRecord<z.ZodString, z.ZodString>;
70
+ sources: z.ZodArray<z.ZodObject<{
71
+ id: z.ZodString;
72
+ registry: z.ZodDefault<z.ZodString>;
73
+ cache: z.ZodDefault<z.ZodString>;
74
+ }, z.core.$strip>>;
75
+ attempts: z.ZodArray<z.ZodObject<{
76
+ source: z.ZodString;
77
+ manager: z.ZodOptional<z.ZodEnum<{
78
+ npm: "npm";
79
+ pnpm: "pnpm";
80
+ }>>;
81
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
82
+ allowScripts: z.ZodOptional<z.ZodBoolean>;
83
+ }, z.core.$strip>>;
84
+ verify: z.ZodArray<z.ZodString>;
85
+ }, z.core.$strip>;
86
+ native: z.ZodObject<{
87
+ packages: z.ZodRecord<z.ZodString, z.ZodString>;
88
+ sources: z.ZodArray<z.ZodObject<{
89
+ id: z.ZodString;
90
+ registry: z.ZodDefault<z.ZodString>;
91
+ cache: z.ZodDefault<z.ZodString>;
92
+ }, z.core.$strip>>;
93
+ attempts: z.ZodArray<z.ZodObject<{
94
+ source: z.ZodString;
95
+ manager: z.ZodOptional<z.ZodEnum<{
96
+ npm: "npm";
97
+ pnpm: "pnpm";
98
+ }>>;
99
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
100
+ allowScripts: z.ZodOptional<z.ZodBoolean>;
101
+ }, z.core.$strip>>;
102
+ verify: z.ZodArray<z.ZodString>;
103
+ }, z.core.$strip>;
104
+ audit: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
105
+ id: z.ZodString;
106
+ severity: z.ZodEnum<{
107
+ info: "info";
108
+ warning: "warning";
109
+ degraded: "degraded";
110
+ }>;
111
+ when: z.ZodEnum<{
112
+ degraded: "degraded";
113
+ failed: "failed";
114
+ missing: "missing";
115
+ }>;
116
+ min_vectors: z.ZodOptional<z.ZodNumber>;
117
+ body: z.ZodString;
118
+ kind: z.ZodLiteral<"module">;
119
+ specifier: z.ZodString;
120
+ }, z.core.$strip>, z.ZodObject<{
121
+ id: z.ZodString;
122
+ severity: z.ZodEnum<{
123
+ info: "info";
124
+ warning: "warning";
125
+ degraded: "degraded";
126
+ }>;
127
+ when: z.ZodEnum<{
128
+ degraded: "degraded";
129
+ failed: "failed";
130
+ missing: "missing";
131
+ }>;
132
+ min_vectors: z.ZodOptional<z.ZodNumber>;
133
+ body: z.ZodString;
134
+ kind: z.ZodLiteral<"runtime-deps">;
135
+ item: z.ZodString;
136
+ }, z.core.$strip>], "kind">>;
137
+ }, z.core.$strip>;
138
+ /** The parsed `res/runtime-deps.yaml`. */
139
+ export type RuntimeDepsResource = z.infer<typeof RuntimeDepsResourceSchema>;
11
140
  /** The merged runtime config shared by the memory store and the retrieval stack. */
12
141
  export declare const ConfigSchema: z.ZodObject<{
13
142
  dataHome: z.ZodDefault<z.ZodString>;
@@ -20,6 +149,12 @@ export declare const ConfigSchema: z.ZodObject<{
20
149
  cache_dir: z.ZodDefault<z.ZodString>;
21
150
  auto_download: z.ZodDefault<z.ZodBoolean>;
22
151
  auto_migrate: z.ZodDefault<z.ZodBoolean>;
152
+ self_heal: z.ZodPrefault<z.ZodObject<{
153
+ enabled: z.ZodDefault<z.ZodBoolean>;
154
+ }, z.core.$strip>>;
155
+ native_self_heal: z.ZodPrefault<z.ZodObject<{
156
+ enabled: z.ZodDefault<z.ZodBoolean>;
157
+ }, z.core.$strip>>;
23
158
  }, z.core.$strip>>;
24
159
  vectorStore: z.ZodPrefault<z.ZodObject<{
25
160
  backend: z.ZodDefault<z.ZodString>;
@@ -306,9 +306,20 @@ export type ExactSearchResult = ExactPageEnvelope & {
306
306
  * The window IS the question: no literal pass, no relation plan, no fuzzy legs. `hits` are the
307
307
  * active facts inside the window, newest first along `time.field`, and `score` is the same constant
308
308
  * 1 the exact page reports — there is no relevance number here to compare with anything.
309
+ *
310
+ * A window is a LIST, so its page is bounded like every other page (default 50, cap 50) and the
311
+ * envelope has to say how much of the list is not on it: {@link TimeSearchResult.remaining} is that
312
+ * number, spelled out instead of left for the caller to derive from `truncated`.
309
313
  */
310
314
  export type TimeSearchResult = ExactPageEnvelope & {
311
315
  mode: 'time';
316
+ /**
317
+ * How many rows of the window this answer does NOT show: `total - (offset + hits.length)`, never
318
+ * negative. `0` means the window is exhausted (either it fits, or the caller reached its last
319
+ * page). It is the count a caller needs to continue — `truncated` alone says "there is more"
320
+ * without saying how much.
321
+ */
322
+ remaining: number;
312
323
  hits: RecallHit[];
313
324
  };
314
325
  /**
@@ -39,6 +39,16 @@ export declare class AutoVectorStore implements VectorStore {
39
39
  /** Build the ANN index now if one is owed (see the class comment). */
40
40
  prepare(): void;
41
41
  attachPersistence(path: string): void;
42
+ /**
43
+ * Point the ANN side at a shared module root (the plugin's native self-heal item) — for THIS store
44
+ * when it already migrated, and for the one it may migrate to later.
45
+ *
46
+ * The delegate call covers the first case; the module-level root (set by
47
+ * `HnswlibVectorStore.useModuleDir` / `useHnswlibModuleDir`) is what a FUTURE
48
+ * `new HnswlibVectorStore` in `maybeUpgrade` reads. Below the threshold nothing changes: an
49
+ * unmigrated store keeps serving from brute force, which is the intended backend at that size.
50
+ */
51
+ useModuleDir(dir: string): void;
42
52
  flush(): void;
43
53
  /** Reclaim dead entries in the migrated backend (the numpy fallback has none). */
44
54
  compact(): void;
@@ -1,4 +1,13 @@
1
1
  import type { VectorStore } from '../interfaces.js';
2
+ /**
3
+ * Point the loader at a shared module root (the base `runtime-deps` handle's directory).
4
+ *
5
+ * The cache follows the ROOT: a change forgets what was loaded, because the next `require` must come
6
+ * from the new root (that is the whole point of setting one). It costs nothing in practice — Node's
7
+ * own `require` cache makes re-entering the same file cheap, and a live native index is untouched
8
+ * (see `HnswlibVectorStore.useModuleDir`, which only rebuilds when it has no native index at all).
9
+ */
10
+ export declare function useHnswlibModuleDir(dir: string | undefined): void;
2
11
  /**
3
12
  * Search-time beam width, when the config does not say otherwise.
4
13
  *
@@ -63,6 +72,25 @@ export declare class HnswlibVectorStore implements VectorStore {
63
72
  private restores;
64
73
  private rebuilds;
65
74
  constructor(dim: number, efSearch?: number);
75
+ /**
76
+ * Build a native index at `max` capacity, or `null` when the binding cannot be loaded.
77
+ *
78
+ * One place for "make an index": the constructor, `reindex()` and `rebuild()` each used to spell
79
+ * the `require` out themselves, which is how a module root set after construction could never
80
+ * reach them. `null` is the caller's signal to serve from {@link fallback} — a missing binding
81
+ * degrades, it never throws.
82
+ */
83
+ private openNative;
84
+ /**
85
+ * Point this store's native side at a shared module root and, when the binding was unavailable
86
+ * until now, rebuild the index from the tracked vectors under it.
87
+ *
88
+ * This is the case the whole hook exists for: `mem` builds its vector store during mount, while the
89
+ * native self-heal item is still installing, so the constructor may have fallen back. When the item
90
+ * settles, the plugin calls this — and a store that already serves natively is left alone (the
91
+ * binding is the same one).
92
+ */
93
+ useModuleDir(dir: string): void;
66
94
  add(id: number, vec: Float32Array): void;
67
95
  /** Whether the real hnswlib native index is active (false = brute-force fallback). */
68
96
  get native(): boolean;
@@ -53,6 +53,16 @@ export interface ModelEnv {
53
53
  * the eager constructor warm.
54
54
  */
55
55
  deferWarm?: boolean;
56
+ /**
57
+ * Module root to load `@huggingface/transformers` from, when it is not this tree's own optional
58
+ * dependency. The DSH plugin sets it (through {@link LocalBgeBackend.useModuleDir}) to the base
59
+ * provider's shared `runtime-deps` root after the semantic self-heal item is ready.
60
+ *
61
+ * Empty/absent = the pre-existing resolution: `import('@huggingface/transformers')` from this
62
+ * module, i.e. the optional dependency or nothing. A root that no longer resolves falls back to
63
+ * that path with one warning — a deleted store must not be worse than no self-heal.
64
+ */
65
+ moduleDir?: string;
56
66
  }
57
67
  /**
58
68
  * Local BGE embedder via `@huggingface/transformers` (transformers.js, ONNX).
@@ -101,6 +111,8 @@ export declare class LocalBgeBackend implements SemanticBackend {
101
111
  * the weights). The resolved representation is CACHED, so the warning is one line per process.
102
112
  */
103
113
  representation(): SemanticRepresentation;
114
+ /** Point the loader at a shared runtime-dependency root before the first warmup. */
115
+ useModuleDir(dir: string): void;
104
116
  /**
105
117
  * Fire-and-forget retry for call sites that only observe availability. No-op
106
118
  * while the model is ready or an attempt is already in flight; otherwise
@@ -119,14 +131,44 @@ export declare class LocalBgeBackend implements SemanticBackend {
119
131
  */
120
132
  private window;
121
133
  /**
122
- * Bound `text` to the window BEFORE it reaches transformers.js.
134
+ * The window-sized slices `text` is encoded in (see `text_budget.splitToWindows`).
135
+ *
136
+ * WHERE THE BOUNDARY CAN LIVE. The feature-extraction pipeline's `_call` accepts
137
+ * `{pooling, normalize, quantize, precision}` and nothing else, so `max_length` cannot be raised
138
+ * from the call site — past `model_max_length` transformers.js truncates and says nothing. This
139
+ * adapter is therefore the only place that can apply the model's own window, and it SPLITS rather
140
+ * than cuts: a text past the window is a document chunk (or a long fact) whose tail is content,
141
+ * and cutting it dropped 503 of ~1015 tokens out of the vector in production.
142
+ */
143
+ private windows;
144
+ /**
145
+ * Report an input that needed more than one window — once per process, and as an INFO line.
146
+ *
147
+ * Not a warning any more: nothing is lost (every window is pooled into the one vector), so the
148
+ * line is a tuning hint, not a degradation. It carries the actionable number: `chunk_size` is
149
+ * measured in CHARACTERS and {@link windowChars} converts the model's token window into exactly
150
+ * that, which is the thing an operator (or a chunker's default) has to change to keep one window
151
+ * per input. `semantic.max_input_tokens` is deliberately NOT suggested: `resolveWindow` clamps it
152
+ * to the model's declared window, so raising it cannot buy a single extra token.
153
+ */
154
+ private reportSplit;
155
+ /** The configured width's vector: model output padded/truncated, with the one-shot dim warning. */
156
+ private toVector;
157
+ /**
158
+ * ONE vector for several windows: token-weighted mean of the window vectors, re-normalized when the
159
+ * representation normalizes.
123
160
  *
124
- * This is the only place the guard can live: the feature-extraction pipeline's `_call`
125
- * accepts `{pooling, normalize, quantize, precision}` and nothing else, so `max_length`
126
- * cannot be raised or lowered from the call site — past `model_max_length` the tokenizer
127
- * truncates and says nothing.
161
+ * Weighted rather than a plain mean because the last window is usually short, and a plain mean
162
+ * would over-represent a fragment. Mean-of-normalized-vectors then re-normalized is the mean
163
+ * DIRECTION of the windows — the same coordinate space the model's own mean pooling produces, so
164
+ * `representationKey` (`pooling`/`normalize`/window) does not move and stored vectors stay
165
+ * comparable with the ones this writes.
128
166
  */
129
- private bound;
167
+ private pool;
168
+ /** Encode the windows of one text in ONE model call, falling back to one call per window. */
169
+ private encodeWindows;
170
+ /** Encode one plan: a single window verbatim, several pooled. */
171
+ private encodePlan;
130
172
  encode(text: string): Promise<Float32Array>;
131
173
  /**
132
174
  * Encode several texts in ONE model call.