@avantf/dsh-mem 0.8.1 → 0.10.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 (57) hide show
  1. package/README.md +42 -43
  2. package/lib/client.js +1181 -327
  3. package/lib/engine/graph-recall/index.d.ts +2 -2
  4. package/lib/engine/graph-recall/traverse.d.ts +2 -2
  5. package/lib/engine/graph-recall/types.d.ts +1 -1
  6. package/lib/engine/mem/config/config_files.d.ts +1 -1
  7. package/lib/engine/mem/db/dao/facts.d.ts +80 -82
  8. package/lib/engine/mem/dbdrivers/sqlite/facts.d.ts +6 -13
  9. package/lib/engine/mem/defaults.d.ts +4 -1
  10. package/lib/engine/mem/entities/extract.d.ts +34 -14
  11. package/lib/engine/mem/facts_io.d.ts +4 -0
  12. package/lib/engine/mem/index.d.ts +1 -0
  13. package/lib/engine/mem/interfaces/repositories.d.ts +5 -19
  14. package/lib/engine/mem/lifecycle/maintenance.d.ts +8 -4
  15. package/lib/engine/mem/lifecycle/tick.d.ts +11 -10
  16. package/lib/engine/mem/lifecycle/trust.d.ts +24 -37
  17. package/lib/engine/mem/modelBootstrap.d.ts +29 -0
  18. package/lib/engine/mem/runtime.d.ts +4 -8
  19. package/lib/engine/mem/store/facts_restore.d.ts +8 -6
  20. package/lib/engine/mem/store/graph_reader.d.ts +2 -2
  21. package/lib/engine/mem/store/memory.d.ts +98 -37
  22. package/lib/engine/mem/store/recall_policy.d.ts +10 -0
  23. package/lib/engine/mem/store/time_filter.d.ts +1 -1
  24. package/lib/engine/mem/trustHeartbeat.d.ts +1 -1
  25. package/lib/engine/mem-contract/config.d.ts +144 -9
  26. package/lib/engine/mem-contract/index.d.ts +2 -0
  27. package/lib/engine/mem-contract/time.d.ts +75 -0
  28. package/lib/engine/mem-contract/tools.d.ts +9 -15
  29. package/lib/engine/mem-contract/trust_curve.d.ts +119 -0
  30. package/lib/engine/mem-contract/types.d.ts +73 -12
  31. package/lib/engine/mem-retrieval/adapters/auto_vstore.d.ts +10 -0
  32. package/lib/engine/mem-retrieval/adapters/hnswlib.d.ts +28 -0
  33. package/lib/engine/mem-retrieval/adapters/local_bge.d.ts +48 -6
  34. package/lib/engine/mem-retrieval/interfaces.d.ts +22 -2
  35. package/lib/engine/mem-retrieval/text_budget.d.ts +31 -0
  36. package/lib/index.js +2349 -665
  37. package/lib/interface-version.json +2 -2
  38. package/lib/types/client/index.d.ts.map +1 -1
  39. package/lib/types/client/paging.d.ts +58 -8
  40. package/lib/types/client/paging.d.ts.map +1 -1
  41. package/lib/types/client/time_filter.d.ts +54 -0
  42. package/lib/types/client/time_filter.d.ts.map +1 -0
  43. package/lib/types/command.d.ts.map +1 -1
  44. package/lib/types/deps_audit.d.ts +88 -0
  45. package/lib/types/deps_audit.d.ts.map +1 -0
  46. package/lib/types/envinit-bootstrap.d.ts +1 -1
  47. package/lib/types/envinit.d.ts +224 -4
  48. package/lib/types/envinit.d.ts.map +1 -1
  49. package/lib/types/hints.d.ts +58 -0
  50. package/lib/types/hints.d.ts.map +1 -1
  51. package/lib/types/index.d.ts.map +1 -1
  52. package/lib/types/remote.d.ts.map +1 -1
  53. package/lib/types/render.d.ts.map +1 -1
  54. package/lib/types/runtime_deps_resource.d.ts +29 -0
  55. package/lib/types/runtime_deps_resource.d.ts.map +1 -0
  56. package/package.json +26 -24
  57. 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: # \u4FE1\u4EFB\u4E0E\u9057\u5FD8\uFF1A\u65CB\u94AE\u662F\u300C\u5929\u6570 + \u5F62\u72B6\u300D\uFF0C\u4E0D\u662F\u659C\u7387\n# base_lifetime_days: 90 # \u951A\u70B9\uFF1A0.5 \u8D77\u70B9 \u2192 0 \u7684\u6D3B\u8DC3\u65E5\u6570\uFF08\u65B0\u4E8B\u5B9E\u4E0D\u88AB\u53EC\u56DE\u65F6\u7684\u5BFF\u547D\uFF09\n# max_retention_days: 174 # \u53EC\u56DE\u52A0\u5F3A\u6700\u591A\u80FD\u6362\u6765\u7684\u6D3B\u8DC3\u65E5\u6570\uFF1Bceiling \u7531\u5B83\u53CD\u89E3\uFF08accelerating \u4E0B \u2248 0.7492\uFF09\n# decay_shape: accelerating # accelerating\uFF08\u9ED8\u8BA4\uFF09| linear\uFF08\u56DE\u6EDA\u6863\uFF1A\u914D 153 \u65F6 ceiling \u56DE\u5230 0.85\uFF09\n# decay_curvature: 0.05 # accelerating \u7684\u4E0B\u9650\u9879 a\uFF0C\u5408\u6CD5\u533A\u95F4 (0, 0.5]\uFF1Blinear \u4E0B\u4E0D\u53C2\u4E0E\n# recall_gain_below_base: 0.1 # \u4F4E\u4E8E\u57FA\u51C6 0.5 \u65F6\u6BCF\u6B21\u53EC\u56DE\u52A0\u591A\u5C11\uFF08\u5C01\u9876\u57FA\u51C6\uFF1A0.05 \u8D77 5 \u6B21\u5230 0.5\uFF09\n# recall_gain_above_base: 0.01 # \u9AD8\u4E8E\u57FA\u51C6 0.5 \u65F6\u6BCF\u6B21\u53EC\u56DE\u52A0\u591A\u5C11\uFF08\u5C01\u9876\u4E0A\u9762\u53CD\u89E3\u51FA\u7684 ceiling\uFF09\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
  *
@@ -2,12 +2,13 @@
2
2
  * `facts` — the memory store's main table.
3
3
  *
4
4
  * Started with the READ paths the detector, the diagnostics and the clock self-heal need;
5
- * the write path (`persistFact`, archive/restore/feedback, vector repair) moves here next, so
5
+ * the write path (`persistFact`, archive/restore, vector repair) moves here next, so
6
6
  * the store ends up holding business rules only and no SQL. See DESIGN §19.
7
7
  */
8
8
  import type { Db } from '../port.js';
9
9
  import { type TimeRange, type TimeSql } from '../../store/time_filter.js';
10
10
  import type { MemoryKind } from '../../../mem-contract/index.js';
11
+ import { type TrustCurve } from '../../../mem-contract/index.js';
11
12
  /**
12
13
  * Every `facts` column EXCEPT the two BLOBs (`hrr_vector` ~8 KB, `semantic_vector` ~2 KB per row)
13
14
  * and the two DEPRECATED provenance columns (`mirror_source` / `mirror_target`, see below).
@@ -28,7 +29,7 @@ import type { MemoryKind } from '../../../mem-contract/index.js';
28
29
  * `test/db_lifecycle.spec.ts` asserts this list against `PRAGMA table_info(facts)` MINUS those
29
30
  * two deprecations, so a new column fails a test instead of silently vanishing from these reads.
30
31
  */
31
- export declare const FACT_COLUMNS_NO_BLOB = "fact_id, content, kind, title, tags, trust_score, settle_clock,\n pinned, pinned_at, bonus_count, bonus_window_at, last_reinforced_at, archived_clock,\n retrieval_count, helpful_count, last_retrieved_at, embedding_model, vector_store, status,\n supersedes_id, entities_version, conflict_checked, archived_at, archive_reason, ttl_days,\n valid_from, valid_to, valid_from_ms, valid_to_ms, assert_count, created_at, updated_at";
32
+ export declare const FACT_COLUMNS_NO_BLOB = "fact_id, content, kind, title, tags, trust_score, settle_clock,\n pinned, pinned_at, bonus_count, bonus_window_at, last_reinforced_at, archived_clock,\n retrieval_count, helpful_count, last_retrieved_at, embedding_model, vector_store, status,\n supersedes_id, entities_version, conflict_checked, archived_at, archive_reason, ttl_days,\n valid_from, valid_from_ms, assert_count, created_at, updated_at";
32
33
  export declare const FTS_SEARCH_SQL: string;
33
34
  /**
34
35
  * {@link FTS_SEARCH_SQL} with the P-08 source predicate, as a SEPARATE statement rather than one
@@ -191,7 +192,6 @@ export declare class FactsDao {
191
192
  created_at: string;
192
193
  updated_at: string | null;
193
194
  valid_from: string | null;
194
- valid_to: string | null;
195
195
  title: string | null;
196
196
  }>;
197
197
  /** Active rows that carry a persisted vector (index classification). */
@@ -233,14 +233,16 @@ export declare class FactsDao {
233
233
  * `createdAt` is named EXPLICITLY rather than left to the column DEFAULT, because a
234
234
  * superseding revision must INHERIT the archived row's `created_at` (the memory's first
235
235
  * assertion date, distinct from when its text was rewritten). `updated_at` and
236
- * `last_retrieved_at` are stamped `CURRENT_TIMESTAMP` in the same statement: a revision
237
- * whose `created_at` is old must not be judged idle by the next tick, where "idle" reads
238
- * `COALESCE(last_retrieved_at, created_at)`.
236
+ * `last_retrieved_at` are stamped `CURRENT_TIMESTAMP` in the same statement, so a revision
237
+ * carries a fresh "last used" observation like the row it replaces (that column no longer
238
+ * feeds any decision — see TRUST_MODEL.md §2.6).
239
+ *
240
+ * `ttl_days` is NOT named: TTL is gone (v2), the column keeps its DEFAULT 0 and nothing
241
+ * reads or writes it any more.
239
242
  */
240
243
  insertRevision(v: {
241
244
  content: string;
242
245
  kind: MemoryKind;
243
- ttlDays: number;
244
246
  supersedesId: number | null;
245
247
  hrr: Uint8Array;
246
248
  trust: number;
@@ -256,37 +258,32 @@ export declare class FactsDao {
256
258
  */
257
259
  title?: string | null;
258
260
  /**
259
- * P-13: the caller's event time / known end, written straight onto the new row. `null` means
260
- * "unknown" and is what an omitted argument leaves (the column has no default on purpose).
261
+ * P-13: the caller's event time, written straight onto the new row. `null` means "unknown" and
262
+ * is what an omitted argument leaves (the column has no default on purpose).
261
263
  * `assert_count` is deliberately NOT named: the column default (1) is the semantics — "this
262
264
  * content has been asserted once" — and naming it here would be a second place to keep in step.
263
265
  */
264
266
  validFrom?: string | null;
265
- validTo?: string | null;
266
267
  /**
267
- * The INSTANTS the two text bounds name (`valid_from_ms`/`valid_to_ms`).
268
+ * The INSTANT the text bound names (`valid_from_ms`).
268
269
  *
269
270
  * Written in the same statement as the text, never computed later: the text is what a reader
270
271
  * displays and the epoch is what an `event` window compares, so writing them apart would let the
271
272
  * two describe different times. `null` = "no event time" (the axis then excludes the row, D5).
272
273
  */
273
274
  validFromMs?: number | null;
274
- validToMs?: number | null;
275
275
  }): {
276
276
  changes: number;
277
277
  lastInsertRowid: number | bigint;
278
278
  };
279
279
  /**
280
- * P-10: land an explicitly given validity on a row that already exists (a duplicate `add` whose
281
- * caller named `event_date` / `valid_until`). Omitted arguments keep what the row has, exactly
282
- * like `kind`/`ttl_days` (see `add`'s doc).
280
+ * P-10: land an explicitly given event time on a row that already exists (a duplicate `add`
281
+ * whose caller named `event_date`). An omitted argument keeps what the row has, exactly like
282
+ * `kind` (see `add`'s doc).
283
283
  */
284
284
  applyValidity(factId: number, validFrom: {
285
285
  text: string;
286
286
  ms: number;
287
- } | undefined, validTo: {
288
- text: string;
289
- ms: number;
290
287
  } | undefined): void;
291
288
  /** P-10: the row's assertion counter (`1` when the column is somehow NULL on a legacy row). */
292
289
  assertCount(factId: number): number;
@@ -299,12 +296,6 @@ export declare class FactsDao {
299
296
  * two processes cannot interleave between them.
300
297
  */
301
298
  incrementAssertCount(factId: number): number;
302
- /**
303
- * P-07 audit: stamp `valid_to` on a row that is leaving the active corpus (supersede / a
304
- * contradiction verdict). `COALESCE` keeps an end the caller already declared (`valid_until`):
305
- * overwriting it with the archival instant would replace a real answer with an administrative one.
306
- */
307
- markValidTo(factId: number, at: string, atMs?: number | null): void;
308
299
  /**
309
300
  * P-07: "who superseded this fact", derived from the existing `supersedes_id` link — no
310
301
  * redundant column. `MIN` is deterministic when several revisions named the same predecessor
@@ -418,10 +409,15 @@ export declare class FactsDao {
418
409
  * reported zero behind. Same rule as {@link setSemanticVector}: what the check SAW changed.
419
410
  */
420
411
  requeueConflictCheck(ids: readonly number[]): number;
421
- /** Revive an archived row (R18/R19: refresh the idle clock, clear the archive bookkeeping). */
422
- revive(factId: number, kind: MemoryKind, ttlDays: number, trust: number, clock: number): number;
423
- /** Land explicit kind/TTL on a row that already exists (pure duplicate). */
424
- applyValues(factId: number, kind: MemoryKind, ttlDays: number): void;
412
+ /**
413
+ * Revive an archived row (R19: clear the archive bookkeeping / refresh `last_retrieved_at`).
414
+ *
415
+ * `ttl_days` is deliberately NOT named any more: an omitted column keeps whatever the row has
416
+ * (the old writing value is preserved as data), and there is no TTL left to inherit.
417
+ */
418
+ revive(factId: number, kind: MemoryKind, trust: number, clock: number): number;
419
+ /** Land an explicit kind on a row that already exists (pure duplicate). */
420
+ applyValues(factId: number, kind: MemoryKind): void;
425
421
  /**
426
422
  * Land a title on a row that already exists — the ONE write behind every "backfill" path
427
423
  * (JOURNAL_TITLE_SEARCH §11.2/§11.3): a duplicate `add` that names a title for an existing
@@ -478,22 +474,14 @@ export declare class FactsDao {
478
474
  archiveSuperseded(factId: number, clock: number): number;
479
475
  linkSupersede(factId: number, supersedesId: number): void;
480
476
  /**
481
- * Restore an archived fact, settling its trust and refreshing the idle clock (spec §2.6).
477
+ * Restore an archived fact, settling its trust and refreshing `last_retrieved_at` (spec §2.6).
482
478
  *
483
479
  * `AND status = 'archived'` is load-bearing, like the guard on every sibling above: this UPDATE
484
480
  * clears the archive bookkeeping and refreshes `last_retrieved_at`, so ungated it would take an
485
- * ACTIVE row and (a) push its idle clock forward — silently extending the life of a fact the next
486
- * tick was about to re-archive — and (b) reset `trust_score` to the settled value it computed. It
487
- * returned `1` either way, so the caller could not tell that a row it never asked about had moved.
481
+ * ACTIVE row and reset `trust_score` to the settled value it computed. It returned `1` either
482
+ * way, so the caller could not tell that a row it never asked about had moved.
488
483
  */
489
484
  restore(factId: number, trust: number, clock: number): number;
490
- bumpHelpful(factId: number, delta: number): void;
491
- /** Feedback outcome: pin at the permanent threshold (trust snaps to 1). */
492
- pinOutcome(factId: number, clock: number, bonusCount: number, windowAt: string): void;
493
- /** Feedback outcome: the forget line was reached — archive in the same statement. */
494
- forgetOutcome(factId: number, trust: number, clock: number, bonusCount: number, windowAt: string): void;
495
- /** Feedback outcome: reinforced (or decayed) but still active. */
496
- reinforceOutcome(factId: number, trust: number, clock: number, bonusCount: number, windowAt: string): void;
497
485
  pin(factId: number, clock: number): number;
498
486
  unpin(factId: number, clock: number): number;
499
487
  /**
@@ -534,13 +522,24 @@ export declare class FactsDao {
534
522
  /** ACTIVE and permanent — pinned facts are exempt from decay (the report's `pinned`). */
535
523
  countPinnedActive(): number;
536
524
  /**
537
- * ACTIVE facts whose decayed trust would reach zero within `withinDays` ACTIVE days —
538
- * i.e. `trust_score - step × (clock - settle_clock) <= step × withinDays`.
525
+ * ACTIVE, unpinned facts whose decayed trust reaches zero within `withinDays` ACTIVE days.
526
+ *
527
+ * EXACT for every shape, not a linear approximation: the predicate is
528
+ * `trustAfterDays(eff(now), withinDays, curve) <= 0`, which is the same question the tick asks
529
+ * when it archives a row. The caller passes the THRESHOLD (`ceilingForDays(withinDays, curve)`)
530
+ * because "days until zero" is no longer `trust ÷ slope` under `decay_shape: 'accelerating'`;
531
+ * a slope-based SQL predicate under-reported every fact in `(slope × 7, ceilingForDays(7)]`
532
+ * (0.0277 … 0.0515 at the shipped defaults) once accelerating became the default.
533
+ *
534
+ * COST: one row per active unpinned fact, evaluated in JS — the predicate needs `exp`, which the
535
+ * SQL layer cannot express portably. `trust_diagnose` is an operator-only diagnostic and the
536
+ * active set is what `mem_admin list` already walks; if it ever shows up in a profile, prefilter
537
+ * with an index-friendly bound rather than going back to a slope approximation.
539
538
  */
540
539
  countForgettingWithin(p: {
541
- step: number;
540
+ threshold: number;
542
541
  clock: number;
543
- withinDays: number;
542
+ curve: TrustCurve;
544
543
  }): number;
545
544
  /** Facts with a still-live (calendar 24h) reinforcement window — the per-fact quota. */
546
545
  /**
@@ -555,18 +554,15 @@ export declare class FactsDao {
555
554
  count: number;
556
555
  granted: number;
557
556
  };
558
- /** ACTIVE, unpinned facts unused for `days` CALENDAR days (the idle-archival fallback). */
559
- countIdleCandidates(days: number): number;
560
557
  /**
561
- * Rows still due for ②③④ after a budgeted tick — what the NEXT pass will archive.
558
+ * Rows still due for ② after a budgeted tick — what the NEXT pass will archive.
562
559
  *
563
- * One statement over the three predicates (they are alternatives, and the sets are disjoint:
564
- * a row has either a TTL, a forgotten trust score, or an idle clock). Only called when a step
565
- * actually filled its budget, so the common tick pays nothing for it.
560
+ * The same predicate the archival statement runs (`FORGET_PREDICATE`), so "backlog" and "what
561
+ * `archiveForgotten` will do next" cannot disagree. Only called when ② actually filled its
562
+ * budget, so the common tick pays nothing for it.
566
563
  */
567
564
  countArchiveBacklog(p: {
568
565
  forgetThreshold: number;
569
- idleCalendarDays: number;
570
566
  }): number;
571
567
  /**
572
568
  * Drop unusable (stale-dim) vectors so a re-encode can replace them — and re-queue the same
@@ -753,35 +749,50 @@ export declare class FactsDao {
753
749
  * row, and `settle_clock < c` is a seek + range scan of exactly the pending rows.
754
750
  */
755
751
  private static readonly SETTLE_PREDICATE;
756
- /** ① materialize decay, bounded to `budget` rows. */
752
+ /**
753
+ * The ONE forgetting predicate, shared by the archival statement and its backlog count.
754
+ *
755
+ * Kept as a constant for the same reason as {@link SETTLE_PREDICATE}: the count answers "how many
756
+ * will the next pass archive", so it must be the same WHERE body the archival itself runs —
757
+ * otherwise `archived_deferred` and the actual sweep would drift apart.
758
+ */
759
+ private static readonly FORGET_PREDICATE;
760
+ /**
761
+ * ① materialize decay, bounded to `budget` rows.
762
+ *
763
+ * PER ROW, through `trustAfterDays` — not one UPDATE with an inline `trust_score − step·Δ`. The
764
+ * formula lives in `contract/src/trust_curve.ts` and nowhere else; the `linear` branch of that
765
+ * function is bit-identical to the statement this replaced (`T − days·slope`, the same two
766
+ * operations in the same order), so P3's equivalent tier materializes exactly the same numbers.
767
+ *
768
+ * The SELECT is the old UPDATE's WHERE body with the old subquery's ORDER/LIMIT, so the row SET is
769
+ * the same one the statement would have touched (and `settled` still counts rows matched, not rows
770
+ * whose value happened to move).
771
+ *
772
+ * COST: one `UPDATE` PER ROW inside the tick's single `IMMEDIATE` transaction (the accelerating
773
+ * formula needs `exp`, which SQL cannot express here, and a second SQL implementation would
774
+ * drift). That is why every caller is budgeted by `trust.tick_max_facts` (5000 by default) and
775
+ * `admin maintenance` is the only unbudgeted pass — measured ~22 ms per 5000 rows, inside the
776
+ * tick's budget; it is a deliberate trade, not a regression.
777
+ */
757
778
  settleBudgeted(p: {
758
- step: number;
779
+ curve: TrustCurve;
759
780
  clock: number;
760
781
  budget: number;
761
782
  }): number;
762
783
  /** ① materialize decay for every pending row (no budget). */
763
784
  settleAll(p: {
764
- step: number;
785
+ curve: TrustCurve;
765
786
  clock: number;
766
787
  }): number;
788
+ /** The shared per-row write of ①: settle the curve at `clock`, then stamp `settle_clock`. */
789
+ private settleRows;
767
790
  /** Rows still awaiting ① (the budget ran out). */
768
791
  countPendingSettle(p: {
769
792
  clock: number;
770
793
  }): number;
771
794
  /**
772
- * ② calendar TTL — explicit instruction, so `pinned` is NOT exempt.
773
- *
774
- * `budget` (0 = none) caps how many rows ONE tick may archive: the tick is a single IMMEDIATE
775
- * transaction shared by several processes, so an unbudgeted pass over a large backlog held the
776
- * write lock for as long as it took (measured 14400 rows in one tick). The remainder stays due
777
- * and is reported as a deferred count, then archived by the next tick.
778
- */
779
- archiveExpiredByTtl(p: {
780
- clock: number;
781
- budget?: number;
782
- }): number[];
783
- /**
784
- * ③ trust-driven forgetting (① already clamped trust to the threshold).
795
+ * ② trust-driven forgetting (① already clamped trust to the threshold).
785
796
  *
786
797
  * Sargable as written (`status`/`pinned` equality + `trust_score <= ?`), and served by
787
798
  * `idx_facts_forget(status, pinned, trust_score)`: before that index this statement read every
@@ -793,20 +804,7 @@ export declare class FactsDao {
793
804
  budget?: number;
794
805
  }): number[];
795
806
  /**
796
- * ④ calendar fallback: never touched for `idle_calendar_days`.
797
- *
798
- * The comparison is written as `<expression> < julianday('now', :modifier)` — the same
799
- * expression `idx_facts_idle_cutoff` indexes — so it is a range seek. The old form
800
- * (`julianday('now') - <expression> > :days`) called `julianday` on EVERY active row and could
801
- * not use any index (measured 33–212 ms at 180k).
802
- */
803
- archiveIdle(p: {
804
- clock: number;
805
- idleCalendarDays: number;
806
- budget?: number;
807
- }): number[];
808
- /**
809
- * ⑤ physical delete of archived rows past the retention window.
807
+ * ③ physical delete of archived rows past the retention window.
810
808
  *
811
809
  * TWO statements in this order, sharing one predicate:
812
810
  *
@@ -815,7 +813,7 @@ export declare class FactsDao {
815
813
  * DELETE below failed with `SQLITE_CONSTRAINT_FOREIGNKEY` whenever a revision chain
816
814
  * crossed `purge_after_archived_days`. Because the whole tick is ONE transaction and it
817
815
  * runs from the store's constructor, that failure did not merely skip the purge: it rolled
818
- * back settle/TTL/idle with it and left the process unable to start, permanently.
816
+ * back settle/forget with it and left the process unable to start, permanently.
819
817
  * 2. delete them.
820
818
  *
821
819
  * The predicate is built once and interpolated into both statements, so "which rows are
@@ -832,7 +830,7 @@ export declare class FactsDao {
832
830
  /** The two mutually exclusive purge branches (see `purgeArchived` for why they are split). */
833
831
  private static purgeBranches;
834
832
  private static purgeParams;
835
- /** Archived rows still due for ⑤ after a budgeted pass (0 when the pass finished them). */
833
+ /** Archived rows still due for ③ after a budgeted pass (0 when the pass finished them). */
836
834
  countPurgeBacklog(p: {
837
835
  clock: number;
838
836
  enabled: number;
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * @module dbdrivers/sqlite/facts
10
10
  */
11
- import type { MemoryKind } from '../../../mem-contract/index.js';
11
+ import type { MemoryKind, TrustCurve } from '../../../mem-contract/index.js';
12
12
  import { type TimeRange } from '../../store/time_filter.js';
13
13
  import type { FactHitMeta, FactRow, FactVectorRow, FactVectorSpaceRow, HrrVectorRow, InsertRevisionOutcome, MemoryRepository } from '../../interfaces/repositories.js';
14
14
  import type { FactsDao } from './dao/index.js';
@@ -31,14 +31,12 @@ export declare class SqliteMemoryRepository implements MemoryRepository {
31
31
  countInStatus(status: string, kind?: MemoryKind, source?: string): number;
32
32
  countActiveWithValidFrom(): number;
33
33
  countForgettingWithin(p: {
34
- step: number;
34
+ threshold: number;
35
35
  clock: number;
36
- withinDays: number;
36
+ curve: TrustCurve;
37
37
  }): number;
38
- countIdleCandidates(days: number): number;
39
38
  countArchiveBacklog(p: {
40
39
  forgetThreshold: number;
41
- idleCalendarDays: number;
42
40
  }): number;
43
41
  countPendingConflicts(): number;
44
42
  countStaleEntities(version: number): number;
@@ -86,25 +84,20 @@ export declare class SqliteMemoryRepository implements MemoryRepository {
86
84
  hrrRowsForFacts(ids: readonly number[], kind?: MemoryKind, source?: string): HrrVectorRow[];
87
85
  activeIdsIn(ids: readonly number[], kind?: MemoryKind, source?: string, time?: TimeRange): number[];
88
86
  insertRevision(v: Parameters<FactsDao['insertRevision']>[0]): InsertRevisionOutcome;
89
- applyValidity(factId: number, validFrom: Parameters<FactsDao['applyValidity']>[1], validTo: Parameters<FactsDao['applyValidity']>[2]): void;
90
- applyValues(factId: number, kind: MemoryKind, ttlDays: number): void;
87
+ applyValidity(factId: number, validFrom: Parameters<FactsDao['applyValidity']>[1]): void;
88
+ applyValues(factId: number, kind: MemoryKind): void;
91
89
  setTitle(factId: number, title: string): void;
92
90
  insertTitleAlias(factId: number, title: string): void;
93
91
  copyTitleAliases(fromFactId: number, toFactId: number, currentTitle: string): void;
94
92
  dropTitleAlias(factId: number, title: string): void;
95
93
  incrementAssertCount(factId: number): number;
96
- markValidTo(factId: number, at: string, atMs?: number | null): void;
97
94
  linkSupersede(factId: number, supersedesId: number): void;
98
95
  archiveSuperseded(factId: number, clock: number): number;
99
- revive(factId: number, kind: MemoryKind, ttlDays: number, trust: number, clock: number): number;
96
+ revive(factId: number, kind: MemoryKind, trust: number, clock: number): number;
100
97
  archive(factId: number, clock: number, reason: string): number;
101
98
  restore(factId: number, trust: number, clock: number): number;
102
- bumpHelpful(factId: number, delta: number): void;
103
99
  pin(factId: number, clock: number): number;
104
100
  unpin(factId: number, clock: number): number;
105
- pinOutcome(factId: number, clock: number, bonusCount: number, windowAt: string): void;
106
- forgetOutcome(factId: number, trust: number, clock: number, bonusCount: number, windowAt: string): void;
107
- reinforceOutcome(factId: number, trust: number, clock: number, bonusCount: number, windowAt: string): void;
108
101
  touchUsage(ids: readonly number[]): void;
109
102
  settleTrust(factId: number, trust: number, clock: number): void;
110
103
  reinforce(factId: number, trust: number, clock: number, bonusCount: number, windowAt: string): void;
@@ -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
  *
@@ -181,6 +181,10 @@ export interface MetadataDropped {
181
181
  helpful_count: number;
182
182
  pinned: number;
183
183
  supersedes: number;
184
+ /** v2: the imported row carried a `ttl_days` value; TTL is gone, so it is not written back. */
185
+ ttl_days: number;
186
+ /** v2: the imported row carried a `valid_to` value; the known end is gone. */
187
+ valid_to: number;
184
188
  }
185
189
  /** The single JSON object `import` prints (`file` is added by the CLI, which knows the argument). */
186
190
  export interface ImportReport {
@@ -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';
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * @module interfaces/repositories
16
16
  */
17
- import type { ContradictionRecord, FactSourceView, MemoryKind, RelationInput } from '../../mem-contract/index.js';
17
+ import type { ContradictionRecord, FactSourceView, MemoryKind, RelationInput, TrustCurve } from '../../mem-contract/index.js';
18
18
  import type { GraphReader } from '../../graph-recall/index.js';
19
19
  import type { RelationVocabulary, SurfaceMatch } from '../store/relation_planner.js';
20
20
  import type { ExtractedEntity } from '../entities/extract.js';
@@ -36,7 +36,6 @@ export interface FactHitMeta {
36
36
  created_at: string;
37
37
  updated_at: string | null;
38
38
  valid_from: string | null;
39
- valid_to: string | null;
40
39
  title: string | null;
41
40
  }
42
41
  /** One persisted semantic vector, as the reload/repair flows read it. */
@@ -87,14 +86,12 @@ export interface MemoryRepository {
87
86
  countInStatus(status: string, kind?: MemoryKind, source?: string): Awaitable<number>;
88
87
  countActiveWithValidFrom(): Awaitable<number>;
89
88
  countForgettingWithin(p: {
90
- step: number;
89
+ threshold: number;
91
90
  clock: number;
92
- withinDays: number;
91
+ curve: TrustCurve;
93
92
  }): Awaitable<number>;
94
- countIdleCandidates(days: number): Awaitable<number>;
95
93
  countArchiveBacklog(p: {
96
94
  forgetThreshold: number;
97
- idleCalendarDays: number;
98
95
  }): Awaitable<number>;
99
96
  countPendingConflicts(): Awaitable<number>;
100
97
  countStaleEntities(version: number): Awaitable<number>;
@@ -146,7 +143,6 @@ export interface MemoryRepository {
146
143
  insertRevision(v: {
147
144
  content: string;
148
145
  kind: MemoryKind;
149
- ttlDays: number;
150
146
  supersedesId: number | null;
151
147
  hrr: Uint8Array;
152
148
  trust: number;
@@ -157,18 +153,13 @@ export interface MemoryRepository {
157
153
  createdAt: string;
158
154
  title?: string | null;
159
155
  validFrom?: string | null;
160
- validTo?: string | null;
161
156
  validFromMs?: number | null;
162
- validToMs?: number | null;
163
157
  }): Awaitable<InsertRevisionOutcome>;
164
158
  applyValidity(factId: number, validFrom: {
165
159
  text: string;
166
160
  ms: number;
167
- } | undefined, validTo: {
168
- text: string;
169
- ms: number;
170
161
  } | undefined): Awaitable<void>;
171
- applyValues(factId: number, kind: MemoryKind, ttlDays: number): Awaitable<void>;
162
+ applyValues(factId: number, kind: MemoryKind): Awaitable<void>;
172
163
  setTitle(factId: number, title: string): Awaitable<void>;
173
164
  /** v17: record a handle this fact used to carry, so an old title search still finds it. */
174
165
  insertTitleAlias(factId: number, title: string): Awaitable<void>;
@@ -177,18 +168,13 @@ export interface MemoryRepository {
177
168
  /** v17: drop an alias that has become the row's current title (a correction back to it). */
178
169
  dropTitleAlias(factId: number, title: string): Awaitable<void>;
179
170
  incrementAssertCount(factId: number): Awaitable<number>;
180
- markValidTo(factId: number, at: string, atMs?: number | null): Awaitable<void>;
181
171
  linkSupersede(factId: number, supersedesId: number): Awaitable<void>;
182
172
  archiveSuperseded(factId: number, clock: number): Awaitable<number>;
183
- revive(factId: number, kind: MemoryKind, ttlDays: number, trust: number, clock: number): Awaitable<number>;
173
+ revive(factId: number, kind: MemoryKind, trust: number, clock: number): Awaitable<number>;
184
174
  archive(factId: number, clock: number, reason: string): Awaitable<number>;
185
175
  restore(factId: number, trust: number, clock: number): Awaitable<number>;
186
- bumpHelpful(factId: number, delta: number): Awaitable<void>;
187
176
  pin(factId: number, clock: number): Awaitable<number>;
188
177
  unpin(factId: number, clock: number): Awaitable<number>;
189
- pinOutcome(factId: number, clock: number, bonusCount: number, windowAt: string): Awaitable<void>;
190
- forgetOutcome(factId: number, trust: number, clock: number, bonusCount: number, windowAt: string): Awaitable<void>;
191
- reinforceOutcome(factId: number, trust: number, clock: number, bonusCount: number, windowAt: string): Awaitable<void>;
192
178
  touchUsage(ids: readonly number[]): Awaitable<void>;
193
179
  settleTrust(factId: number, trust: number, clock: number): Awaitable<void>;
194
180
  reinforce(factId: number, trust: number, clock: number, bonusCount: number, windowAt: string): Awaitable<void>;