recallnest 2.2.0 → 2.3.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 (249) hide show
  1. package/.env +4 -0
  2. package/CLAUDE.md +1 -1
  3. package/README.md +30 -5
  4. package/README_CN.md +30 -5
  5. package/config.json +55 -0
  6. package/connectors/examples/email-adapter.ts +73 -0
  7. package/connectors/examples/logs-adapter.ts +103 -0
  8. package/connectors/examples/rss-adapter.ts +95 -0
  9. package/logs/daily-recall.error.log +0 -0
  10. package/logs/daily-recall.log +60 -0
  11. package/logs/ingest-2026-04-11.log +110 -0
  12. package/logs/ingest-2026-04-12.log +90 -0
  13. package/logs/ingest-2026-04-13.log +87 -0
  14. package/logs/launchagent-stderr.log +0 -0
  15. package/logs/launchagent-stdout.log +0 -0
  16. package/package.json +1 -1
  17. package/src/cli.ts +30 -0
  18. package/src/connector-types.ts +57 -0
  19. package/src/conversation-importer.ts +25 -1
  20. package/src/data-checkup.ts +31 -1
  21. package/src/doctor.ts +29 -0
  22. package/src/ingest.ts +203 -0
  23. package/src/mcp-server.ts +2 -2
  24. package/src/obsidian-connector.ts +282 -0
  25. package/src/session-schema.ts +2 -2
  26. package/src/source-heartbeat.ts +108 -0
  27. package/.claude-plugin/CLAUDE.md +0 -29
  28. package/.claude-plugin/marketplace.json +0 -18
  29. package/.dockerignore +0 -11
  30. package/.github/workflows/ci.yml +0 -28
  31. package/.mcp.json +0 -9
  32. package/CHANGELOG.md +0 -51
  33. package/DEMO.md +0 -94
  34. package/Dockerfile +0 -20
  35. package/EVAL.md +0 -89
  36. package/FAILURES.md +0 -75
  37. package/OPERATIONS.md +0 -59
  38. package/ROADMAP.md +0 -245
  39. package/SKILL.md +0 -250
  40. package/assets/dashboard.png +0 -0
  41. package/assets/knowledge-graph.png +0 -0
  42. package/assets/recallnest-workbench.png +0 -0
  43. package/assets/screenshots/ui-full.png +0 -0
  44. package/assets/screenshots/ui-overview.png +0 -0
  45. package/assets/screenshots/ui-search.png +0 -0
  46. package/assets/ui/app.js +0 -1031
  47. package/assets/ui/index.html +0 -224
  48. package/assets/ui/styles.css +0 -781
  49. package/assets/wechat-qr.jpg +0 -0
  50. package/bun.lock +0 -337
  51. package/daily-reflections/2026-03-05.md +0 -15
  52. package/daily-reflections/2026-03-06.md +0 -15
  53. package/daily-reflections/2026-03-07.md +0 -7
  54. package/daily-reflections/2026-03-08.md +0 -34
  55. package/daily-reflections/2026-03-09.md +0 -35
  56. package/daily-reflections/2026-03-10.md +0 -36
  57. package/daily-reflections/2026-03-11.md +0 -35
  58. package/daily-reflections/2026-03-12.md +0 -36
  59. package/daily-reflections/2026-03-13.md +0 -35
  60. package/daily-reflections/2026-03-14.md +0 -35
  61. package/daily-reflections/2026-03-15.md +0 -35
  62. package/daily-reflections/2026-03-16.md +0 -41
  63. package/daily-reflections/2026-03-17.md +0 -10
  64. package/daily-reflections/2026-03-18.md +0 -10
  65. package/daily-reflections/2026-03-19.md +0 -10
  66. package/daily-reflections/2026-03-20.md +0 -10
  67. package/daily-reflections/2026-03-21.md +0 -10
  68. package/daily-reflections/2026-03-22.md +0 -10
  69. package/daily-reflections/2026-03-23.md +0 -10
  70. package/daily-reflections/2026-03-24.md +0 -10
  71. package/daily-reflections/2026-03-25.md +0 -32
  72. package/daily-reflections/2026-03-26.md +0 -37
  73. package/daily-reflections/2026-03-27.md +0 -35
  74. package/daily-reflections/2026-03-28.md +0 -35
  75. package/daily-reflections/2026-03-29.md +0 -35
  76. package/daily-reflections/2026-03-30.md +0 -36
  77. package/daily-reflections/2026-03-31.md +0 -35
  78. package/daily-reflections/2026-04-01.md +0 -35
  79. package/daily-reflections/2026-04-02.md +0 -35
  80. package/daily-reflections/2026-04-03.md +0 -37
  81. package/daily-reflections/2026-04-04.md +0 -37
  82. package/daily-reflections/2026-04-05.md +0 -35
  83. package/daily-reflections/2026-04-06.md +0 -36
  84. package/daily-reflections/2026-04-07.md +0 -35
  85. package/daily-reflections/2026-04-08.md +0 -35
  86. package/daily-reflections/2026-04-12.md +0 -35
  87. package/data/alias-map.example.json +0 -22
  88. package/docs/api-reference.md +0 -1291
  89. package/docs/architecture.md +0 -168
  90. package/docs/memory-boundary-contract.md +0 -226
  91. package/docs/memory-categories.md +0 -119
  92. package/docs/pipeline-audit-ultramemory.md +0 -177
  93. package/docs/plans/2026-03-08-query-expansion.md +0 -270
  94. package/docs/plans/2026-03-16-continuity-layer.md +0 -196
  95. package/docs/plans/2026-04-01-recall-boost.md +0 -100
  96. package/docs/plans/mcp-tiering-plan.md +0 -126
  97. package/docs/research/recall-quality-audit-2026-03-22.md +0 -153
  98. package/docs/self-evolution.md +0 -198
  99. package/docs/superpowers/plans/2026-03-19-ingest-pipeline-upgrade.md +0 -782
  100. package/docs/superpowers/plans/2026-04-11-constructive-retrieval-emotion.md +0 -1195
  101. package/docs/superpowers/specs/2026-03-19-ingest-pipeline-upgrade-design.md +0 -77
  102. package/docs/superpowers/specs/2026-03-23-pull-claude-desktop-design.md +0 -95
  103. package/docs/superpowers/specs/2026-04-11-constructive-retrieval-emotion-design.md +0 -398
  104. package/eval/continuity/results-2026-03-16.md +0 -117
  105. package/eval/continuity/results-2026-03-19.md +0 -453
  106. package/eval/continuity/runbook.md +0 -110
  107. package/eval/continuity/seed-cases.md +0 -54
  108. package/eval/reports/2026-03-06-abstract-query-baseline.md +0 -96
  109. package/eval/reports/2026-03-06-aws-ssh-baseline.md +0 -82
  110. package/eval/reports/2026-03-06-baseline.md +0 -82
  111. package/eval/reports/query-expansion.md +0 -138
  112. package/glama.json +0 -6
  113. package/lm +0 -19
  114. package/plan-philosophy-upgrades.md +0 -363
  115. package/plan-product-upgrades.md +0 -155
  116. package/plan-v1.2-distributable.md +0 -105
  117. package/plan.md +0 -140
  118. package/research-memory-evolution.md +0 -440
  119. package/research-rl-agent-memory.md +0 -330
  120. package/scripts/archive-distilled-junk.ts +0 -116
  121. package/scripts/backfill-anchors.ts +0 -130
  122. package/scripts/backfill-l0l1l2.ts +0 -98
  123. package/scripts/blind-scan.ts +0 -588
  124. package/scripts/cleanup-step1-exact-dups.ts +0 -117
  125. package/scripts/cleanup-step2-template-dups.ts +0 -113
  126. package/scripts/cleanup-step3-stale-content.ts +0 -149
  127. package/scripts/cleanup-step4-orphan-scopes.ts +0 -131
  128. package/scripts/delete-distilled-junk.ts +0 -88
  129. package/scripts/diagnose-vector-search.ts +0 -132
  130. package/scripts/distill-facts.ts +0 -305
  131. package/scripts/health-check.ts +0 -343
  132. package/scripts/incremental-ingest.sh +0 -71
  133. package/scripts/migrate-language.ts +0 -75
  134. package/scripts/mlp-health-check.ts +0 -382
  135. package/scripts/pull-claude-desktop.ts +0 -515
  136. package/scripts/re-ingest-cc.sh +0 -66
  137. package/scripts/rebalance-memory-health.ts +0 -181
  138. package/scripts/run-desktop-ingest.sh +0 -11
  139. package/scripts/start-server.sh +0 -12
  140. package/scripts/verify-emotion-decay.ts +0 -148
  141. package/scripts/viz-dashboard.html +0 -423
  142. package/scripts/viz-export.ts +0 -703
  143. package/src/__tests__/access-tracker-novelty.test.ts +0 -228
  144. package/src/__tests__/activity-counter.test.ts +0 -96
  145. package/src/__tests__/admission-control.test.ts +0 -206
  146. package/src/__tests__/anchor-generator.test.ts +0 -67
  147. package/src/__tests__/auto-consolidation.test.ts +0 -168
  148. package/src/__tests__/auto-recall.test.ts +0 -131
  149. package/src/__tests__/babel-integration.test.ts +0 -139
  150. package/src/__tests__/batch-dedup.test.ts +0 -65
  151. package/src/__tests__/capture-engine.test.ts +0 -1144
  152. package/src/__tests__/capture-heuristic.test.ts +0 -171
  153. package/src/__tests__/cascade-forget.test.ts +0 -210
  154. package/src/__tests__/category-min-scores.test.ts +0 -33
  155. package/src/__tests__/cc8-cc1-essential-context.test.ts +0 -298
  156. package/src/__tests__/circuit-breaker.test.ts +0 -99
  157. package/src/__tests__/cluster-consolidation.test.ts +0 -712
  158. package/src/__tests__/confidence-metadata.test.ts +0 -125
  159. package/src/__tests__/confidence-tracker.test.ts +0 -249
  160. package/src/__tests__/conflict-advisor.test.ts +0 -206
  161. package/src/__tests__/conflict-escalation.test.ts +0 -149
  162. package/src/__tests__/conflict-lifecycle.test.ts +0 -83
  163. package/src/__tests__/conflict-output.test.ts +0 -85
  164. package/src/__tests__/conflict-store.test.ts +0 -374
  165. package/src/__tests__/consolidation-engine.test.ts +0 -181
  166. package/src/__tests__/constructive-retrieval.test.ts +0 -509
  167. package/src/__tests__/context-collapse-renderer.test.ts +0 -214
  168. package/src/__tests__/context-composer-pins.test.ts +0 -64
  169. package/src/__tests__/context-composer-task-selection.test.ts +0 -24
  170. package/src/__tests__/context-reconstructor.test.ts +0 -65
  171. package/src/__tests__/context-renderer.test.ts +0 -91
  172. package/src/__tests__/continuity-eval.test.ts +0 -275
  173. package/src/__tests__/continuity-schema.test.ts +0 -97
  174. package/src/__tests__/conversation-importer.test.ts +0 -692
  175. package/src/__tests__/core-recall-eval.test.ts +0 -235
  176. package/src/__tests__/core-summary.test.ts +0 -109
  177. package/src/__tests__/data-checkup.test.ts +0 -225
  178. package/src/__tests__/decay-exemption.test.ts +0 -84
  179. package/src/__tests__/dedup-actions.test.ts +0 -58
  180. package/src/__tests__/dedup-guard.test.ts +0 -39
  181. package/src/__tests__/deterministic-id.test.ts +0 -39
  182. package/src/__tests__/distill-lock.test.ts +0 -176
  183. package/src/__tests__/doctor.test.ts +0 -303
  184. package/src/__tests__/dream-pipeline.test.ts +0 -232
  185. package/src/__tests__/drill-down.test.ts +0 -108
  186. package/src/__tests__/embedder.test.ts +0 -122
  187. package/src/__tests__/emotion-decay.test.ts +0 -46
  188. package/src/__tests__/emotion-detector.test.ts +0 -56
  189. package/src/__tests__/emotion-phase1-integration.test.ts +0 -153
  190. package/src/__tests__/emotion-retrieval.test.ts +0 -56
  191. package/src/__tests__/entity-resolver.test.ts +0 -58
  192. package/src/__tests__/ethics-layer.test.ts +0 -476
  193. package/src/__tests__/event-segmenter.test.ts +0 -153
  194. package/src/__tests__/feature-flags-integration.test.ts +0 -54
  195. package/src/__tests__/frequency-tracker.test.ts +0 -113
  196. package/src/__tests__/graph-export.test.ts +0 -489
  197. package/src/__tests__/hotness-blend.test.ts +0 -76
  198. package/src/__tests__/ingest.test.ts +0 -360
  199. package/src/__tests__/interference-detector.test.ts +0 -203
  200. package/src/__tests__/kg-extractor.test.ts +0 -184
  201. package/src/__tests__/kg-store.test.ts +0 -198
  202. package/src/__tests__/llm-consolidation.test.ts +0 -226
  203. package/src/__tests__/mcp-efficiency.test.ts +0 -288
  204. package/src/__tests__/memory-boundaries.test.ts +0 -237
  205. package/src/__tests__/memory-evolution-b2-b3-e1.test.ts +0 -243
  206. package/src/__tests__/memory-evolution.test.ts +0 -271
  207. package/src/__tests__/memory-health-rebalance.test.ts +0 -144
  208. package/src/__tests__/memory-lint.test.ts +0 -271
  209. package/src/__tests__/memory-output.test.ts +0 -215
  210. package/src/__tests__/multi-vector.test.ts +0 -242
  211. package/src/__tests__/narrative-architecture.test.ts +0 -456
  212. package/src/__tests__/pending-review.test.ts +0 -77
  213. package/src/__tests__/post-retrieval-filter.test.ts +0 -178
  214. package/src/__tests__/ppr-traversal.test.ts +0 -256
  215. package/src/__tests__/prediction-engine.test.ts +0 -349
  216. package/src/__tests__/predictive-prospective-memory.test.ts +0 -416
  217. package/src/__tests__/preference-guard-smoke.test.ts +0 -211
  218. package/src/__tests__/preference-matcher.test.ts +0 -230
  219. package/src/__tests__/preference-slots.test.ts +0 -205
  220. package/src/__tests__/prospective-memory.test.ts +0 -283
  221. package/src/__tests__/query-entity-detector.test.ts +0 -171
  222. package/src/__tests__/query-expander-alias.test.ts +0 -89
  223. package/src/__tests__/query-expander.test.ts +0 -34
  224. package/src/__tests__/recall-governor.test.ts +0 -227
  225. package/src/__tests__/recall-mode.test.ts +0 -64
  226. package/src/__tests__/result-synthesizer.test.ts +0 -198
  227. package/src/__tests__/resume-context.test.ts +0 -4898
  228. package/src/__tests__/retrieval-trace.test.ts +0 -213
  229. package/src/__tests__/retriever-boundary-weight.test.ts +0 -136
  230. package/src/__tests__/retroactive-boost.test.ts +0 -223
  231. package/src/__tests__/salience-scoring.test.ts +0 -106
  232. package/src/__tests__/scope-inventory.test.ts +0 -200
  233. package/src/__tests__/scope-policy.test.ts +0 -69
  234. package/src/__tests__/session-checkpoint.test.ts +0 -259
  235. package/src/__tests__/session-distiller.test.ts +0 -611
  236. package/src/__tests__/session-gc.test.ts +0 -215
  237. package/src/__tests__/skill-engine.test.ts +0 -546
  238. package/src/__tests__/skill-promotion.test.ts +0 -349
  239. package/src/__tests__/store-scope-audit.test.ts +0 -81
  240. package/src/__tests__/supersede-chain.test.ts +0 -154
  241. package/src/__tests__/temporal-parser.test.ts +0 -241
  242. package/src/__tests__/temporal-validity.test.ts +0 -160
  243. package/src/__tests__/term-registry.test.ts +0 -121
  244. package/src/__tests__/tool-output-compressor.test.ts +0 -133
  245. package/src/__tests__/topic-tag.test.ts +0 -113
  246. package/src/__tests__/trusted-memory.test.ts +0 -828
  247. package/src/__tests__/version-manager.test.ts +0 -213
  248. package/src/__tests__/workflow-observation.test.ts +0 -400
  249. package/src/__tests__/write-verifier.test.ts +0 -122
@@ -1,241 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import {
3
- parseTemporalQuery,
4
- matchesTemporalConstraint,
5
- temporalWhereClause,
6
- type TemporalConstraint,
7
- } from "../temporal-parser.js";
8
-
9
- // ============================================================================
10
- // parseTemporalQuery
11
- // ============================================================================
12
-
13
- describe("parseTemporalQuery", () => {
14
- // --- Absolute year (ZH) ---
15
- it("parses '2023年的记忆'", () => {
16
- const result = parseTemporalQuery("2023年的记忆里有什么");
17
- expect(result.constraint).not.toBeNull();
18
- expect(result.constraint!.type).toBe("range");
19
- expect(result.constraint!.startMs).toBe(new Date(2023, 0, 1).getTime());
20
- expect(result.constraint!.endMs).toBe(new Date(2024, 0, 1).getTime() - 1);
21
- // Time expression removed from cleaned query
22
- expect(result.cleanedQuery).not.toContain("2023年");
23
- expect(result.cleanedQuery).toContain("里有什么");
24
- });
25
-
26
- it("parses standalone '2024年'", () => {
27
- const result = parseTemporalQuery("关于 2024年 的项目");
28
- expect(result.constraint).not.toBeNull();
29
- expect(result.constraint!.type).toBe("range");
30
- });
31
-
32
- // --- Absolute year+month (ZH) ---
33
- it("parses '2023年3月'", () => {
34
- const result = parseTemporalQuery("2023年3月我们讨论了什么");
35
- expect(result.constraint).not.toBeNull();
36
- expect(result.constraint!.startMs).toBe(new Date(2023, 2, 1).getTime());
37
- expect(result.constraint!.endMs).toBe(new Date(2023, 3, 1).getTime() - 1);
38
- });
39
-
40
- it("parses '2023年三月'", () => {
41
- const result = parseTemporalQuery("2023年三月的对话");
42
- expect(result.constraint).not.toBeNull();
43
- expect(result.constraint!.startMs).toBe(new Date(2023, 2, 1).getTime());
44
- });
45
-
46
- // --- Absolute year+month (EN) ---
47
- it("parses 'March 2023'", () => {
48
- const result = parseTemporalQuery("What did we discuss in March 2023");
49
- expect(result.constraint).not.toBeNull();
50
- expect(result.constraint!.startMs).toBe(new Date(2023, 2, 1).getTime());
51
- expect(result.constraint!.endMs).toBe(new Date(2023, 3, 1).getTime() - 1);
52
- });
53
-
54
- // --- "in YYYY" ---
55
- it("parses 'in 2022'", () => {
56
- const result = parseTemporalQuery("Projects discussed in 2022");
57
- expect(result.constraint).not.toBeNull();
58
- expect(result.constraint!.type).toBe("range");
59
- expect(result.constraint!.startMs).toBe(new Date(2022, 0, 1).getTime());
60
- });
61
-
62
- // --- Relative: 最近N天/周/月 ---
63
- it("parses '最近7天'", () => {
64
- const result = parseTemporalQuery("最近7天的记忆");
65
- expect(result.constraint).not.toBeNull();
66
- expect(result.constraint!.type).toBe("range");
67
- const now = Date.now();
68
- expect(result.constraint!.startMs).toBeGreaterThan(now - 8 * 86_400_000);
69
- expect(result.constraint!.endMs).toBeLessThanOrEqual(now + 1000);
70
- });
71
-
72
- it("parses '最近2周'", () => {
73
- const result = parseTemporalQuery("最近2周讨论过什么");
74
- expect(result.constraint).not.toBeNull();
75
- const now = Date.now();
76
- expect(result.constraint!.startMs).toBeGreaterThan(now - 15 * 86_400_000);
77
- });
78
-
79
- it("parses '最近3个月'", () => {
80
- const result = parseTemporalQuery("最近3个月的项目进展");
81
- expect(result.constraint).not.toBeNull();
82
- const now = Date.now();
83
- expect(result.constraint!.startMs).toBeGreaterThan(now - 100 * 86_400_000);
84
- });
85
-
86
- // --- Relative: last N days/weeks/months ---
87
- it("parses 'last 30 days'", () => {
88
- const result = parseTemporalQuery("What happened in the last 30 days");
89
- expect(result.constraint).not.toBeNull();
90
- expect(result.constraint!.type).toBe("range");
91
- const now = Date.now();
92
- expect(result.constraint!.startMs).toBeGreaterThan(now - 31 * 86_400_000);
93
- });
94
-
95
- it("parses 'last 2 weeks'", () => {
96
- const result = parseTemporalQuery("Changes in the last 2 weeks");
97
- expect(result.constraint).not.toBeNull();
98
- });
99
-
100
- // --- Relative: 上周/上个月/去年 ---
101
- it("parses '去年'", () => {
102
- const result = parseTemporalQuery("去年我们做了什么项目");
103
- expect(result.constraint).not.toBeNull();
104
- const lastYear = new Date().getFullYear() - 1;
105
- expect(result.constraint!.startMs).toBe(new Date(lastYear, 0, 1).getTime());
106
- expect(result.constraint!.endMs).toBe(new Date(lastYear + 1, 0, 1).getTime() - 1);
107
- });
108
-
109
- it("parses 'last year'", () => {
110
- const result = parseTemporalQuery("Projects from last year");
111
- expect(result.constraint).not.toBeNull();
112
- expect(result.constraint!.type).toBe("range");
113
- });
114
-
115
- it("parses '上个月'", () => {
116
- const result = parseTemporalQuery("上个月的讨论");
117
- expect(result.constraint).not.toBeNull();
118
- });
119
-
120
- it("parses '去年3月'", () => {
121
- const result = parseTemporalQuery("去年3月的事情");
122
- expect(result.constraint).not.toBeNull();
123
- const lastYear = new Date().getFullYear() - 1;
124
- expect(result.constraint!.startMs).toBe(new Date(lastYear, 2, 1).getTime());
125
- });
126
-
127
- // --- Operator syntax ---
128
- it("parses 'after:2024-01'", () => {
129
- const result = parseTemporalQuery("TypeScript projects after:2024-01");
130
- expect(result.constraint).not.toBeNull();
131
- expect(result.constraint!.type).toBe("after");
132
- expect(result.constraint!.startMs).toBe(new Date(2024, 0, 1).getTime());
133
- expect(result.cleanedQuery).toContain("TypeScript projects");
134
- });
135
-
136
- it("parses 'before:2025-06'", () => {
137
- const result = parseTemporalQuery("memory entries before:2025-06");
138
- expect(result.constraint).not.toBeNull();
139
- expect(result.constraint!.type).toBe("before");
140
- });
141
-
142
- // --- No temporal expression ---
143
- it("returns null for non-temporal queries", () => {
144
- const result = parseTemporalQuery("How to configure TypeScript");
145
- expect(result.constraint).toBeNull();
146
- expect(result.cleanedQuery).toBe("How to configure TypeScript");
147
- });
148
-
149
- it("returns null for empty query", () => {
150
- const result = parseTemporalQuery("");
151
- expect(result.constraint).toBeNull();
152
- });
153
-
154
- // --- Cleaned query ---
155
- it("removes temporal expression and preserves the rest", () => {
156
- const result = parseTemporalQuery("2023年我们讨论了 Docker 配置");
157
- expect(result.cleanedQuery).toContain("Docker");
158
- expect(result.cleanedQuery).not.toContain("2023年");
159
- });
160
- });
161
-
162
- // ============================================================================
163
- // matchesTemporalConstraint
164
- // ============================================================================
165
-
166
- describe("matchesTemporalConstraint", () => {
167
- it("matches timestamp within range", () => {
168
- const constraint: TemporalConstraint = {
169
- type: "range",
170
- startMs: new Date(2023, 0, 1).getTime(),
171
- endMs: new Date(2024, 0, 1).getTime() - 1,
172
- anchor: "2023年",
173
- };
174
- const mid2023 = new Date(2023, 6, 1).getTime();
175
- expect(matchesTemporalConstraint(mid2023, constraint)).toBe(true);
176
- });
177
-
178
- it("rejects timestamp outside range", () => {
179
- const constraint: TemporalConstraint = {
180
- type: "range",
181
- startMs: new Date(2023, 0, 1).getTime(),
182
- endMs: new Date(2024, 0, 1).getTime() - 1,
183
- anchor: "2023年",
184
- };
185
- const mid2022 = new Date(2022, 6, 1).getTime();
186
- expect(matchesTemporalConstraint(mid2022, constraint)).toBe(false);
187
- });
188
-
189
- it("handles 'after' constraint", () => {
190
- const constraint: TemporalConstraint = {
191
- type: "after",
192
- startMs: new Date(2024, 0, 1).getTime(),
193
- anchor: "after:2024-01",
194
- };
195
- expect(matchesTemporalConstraint(new Date(2024, 6, 1).getTime(), constraint)).toBe(true);
196
- expect(matchesTemporalConstraint(new Date(2023, 6, 1).getTime(), constraint)).toBe(false);
197
- });
198
-
199
- it("handles 'before' constraint", () => {
200
- const constraint: TemporalConstraint = {
201
- type: "before",
202
- endMs: new Date(2024, 0, 1).getTime(),
203
- anchor: "before:2024-01",
204
- };
205
- expect(matchesTemporalConstraint(new Date(2023, 6, 1).getTime(), constraint)).toBe(true);
206
- expect(matchesTemporalConstraint(new Date(2025, 0, 1).getTime(), constraint)).toBe(false);
207
- });
208
- });
209
-
210
- // ============================================================================
211
- // temporalWhereClause
212
- // ============================================================================
213
-
214
- describe("temporalWhereClause", () => {
215
- it("generates range clause", () => {
216
- const clause = temporalWhereClause({
217
- type: "range",
218
- startMs: 1000,
219
- endMs: 2000,
220
- anchor: "test",
221
- });
222
- expect(clause).toBe("timestamp >= 1000 AND timestamp <= 2000");
223
- });
224
-
225
- it("generates after-only clause", () => {
226
- const clause = temporalWhereClause({
227
- type: "after",
228
- startMs: 1000,
229
- anchor: "test",
230
- });
231
- expect(clause).toBe("timestamp >= 1000");
232
- });
233
-
234
- it("returns null for empty constraint", () => {
235
- const clause = temporalWhereClause({
236
- type: "range",
237
- anchor: "test",
238
- });
239
- expect(clause).toBeNull();
240
- });
241
- });
@@ -1,160 +0,0 @@
1
- /**
2
- * F3: Temporal Validity Windows — tests for eventTime, validUntil,
3
- * retrieval filtering, and auto-GC decay acceleration.
4
- */
5
-
6
- import { describe, expect, test } from "bun:test";
7
- import {
8
- defaultEvolution,
9
- parseEvolution,
10
- patchEvolution,
11
- type EvolutionMetadata,
12
- } from "../memory-evolution.js";
13
-
14
- // ---------------------------------------------------------------------------
15
- // Schema: eventTime field
16
- // ---------------------------------------------------------------------------
17
-
18
- describe("F3: eventTime in EvolutionMetadata", () => {
19
- test("defaultEvolution includes eventTime: null", () => {
20
- const evo = defaultEvolution(1000);
21
- expect(evo.eventTime).toBeNull();
22
- expect(evo.validFrom).toBe(1000);
23
- expect(evo.validUntil).toBeNull();
24
- });
25
-
26
- test("parseEvolution extracts eventTime from metadata", () => {
27
- const metadata = JSON.stringify({
28
- evolution: {
29
- status: "active",
30
- version: 1,
31
- accessCount: 0,
32
- lastAccessedAt: null,
33
- supersededBy: null,
34
- supersedes: null,
35
- evolutionNote: null,
36
- consolidatedInto: null,
37
- contributedToPattern: null,
38
- sourceMemories: [],
39
- validFrom: 1000,
40
- validUntil: null,
41
- eventTime: 5000,
42
- },
43
- });
44
- const evo = parseEvolution(metadata);
45
- expect(evo.eventTime).toBe(5000);
46
- });
47
-
48
- test("parseEvolution defaults eventTime to null when missing", () => {
49
- const metadata = JSON.stringify({
50
- evolution: {
51
- status: "active",
52
- version: 1,
53
- validFrom: 1000,
54
- },
55
- });
56
- const evo = parseEvolution(metadata);
57
- expect(evo.eventTime).toBeNull();
58
- });
59
-
60
- test("patchEvolution can set eventTime", () => {
61
- const metadata = JSON.stringify({ evolution: defaultEvolution(1000) });
62
- const patched = patchEvolution(metadata, { eventTime: 9999 });
63
- const evo = parseEvolution(patched);
64
- expect(evo.eventTime).toBe(9999);
65
- });
66
-
67
- test("patchEvolution can set validUntil", () => {
68
- const metadata = JSON.stringify({ evolution: defaultEvolution(1000) });
69
- const patched = patchEvolution(metadata, { validUntil: 2000 });
70
- const evo = parseEvolution(patched);
71
- expect(evo.validUntil).toBe(2000);
72
- // Other fields unchanged
73
- expect(evo.validFrom).toBe(1000);
74
- expect(evo.status).toBe("active");
75
- });
76
- });
77
-
78
- // ---------------------------------------------------------------------------
79
- // Retrieval: filterByValidity (unit-level via import)
80
- // ---------------------------------------------------------------------------
81
-
82
- // We test the filterByValidity logic indirectly through the exported
83
- // parseEvolution + the contract: expired memories get score * 0.2.
84
- // Direct integration tested via full retriever in separate test files.
85
-
86
- describe("F3: validity window logic", () => {
87
- function makeEvo(overrides: Partial<EvolutionMetadata> = {}): EvolutionMetadata {
88
- return { ...defaultEvolution(1000), ...overrides };
89
- }
90
-
91
- test("memory with validUntil in the future is not expired", () => {
92
- const now = 5000;
93
- const evo = makeEvo({ validUntil: 10000 });
94
- // validUntil > now → not expired
95
- expect(evo.validUntil).toBeGreaterThan(now);
96
- });
97
-
98
- test("memory with validUntil in the past is expired", () => {
99
- const now = 5000;
100
- const evo = makeEvo({ validUntil: 3000 });
101
- // validUntil < now → expired
102
- expect(evo.validUntil).toBeLessThan(now);
103
- });
104
-
105
- test("memory without validUntil never expires", () => {
106
- const evo = makeEvo({ validUntil: null });
107
- expect(evo.validUntil).toBeNull();
108
- });
109
-
110
- test("point-in-time query: memory valid in range", () => {
111
- const evo = makeEvo({ validFrom: 1000, validUntil: 5000 });
112
- const queryTime = 3000;
113
- expect(evo.validFrom).toBeLessThanOrEqual(queryTime);
114
- expect(evo.validUntil).toBeGreaterThanOrEqual(queryTime);
115
- });
116
-
117
- test("point-in-time query: memory not yet valid", () => {
118
- const evo = makeEvo({ validFrom: 5000, validUntil: 10000 });
119
- const queryTime = 3000;
120
- expect(evo.validFrom).toBeGreaterThan(queryTime);
121
- });
122
-
123
- test("point-in-time query: memory already expired at query time", () => {
124
- const evo = makeEvo({ validFrom: 1000, validUntil: 3000 });
125
- const queryTime = 5000;
126
- expect(evo.validUntil).toBeLessThan(queryTime);
127
- });
128
- });
129
-
130
- // ---------------------------------------------------------------------------
131
- // Auto-GC: decay acceleration for expired memories
132
- // ---------------------------------------------------------------------------
133
-
134
- describe("F3: GC decay acceleration", () => {
135
- test("expired memory decay score is halved", () => {
136
- // Simulate the GC logic: if validUntil < now, decayScore *= 0.5
137
- const baseDecayScore = 0.30;
138
- const now = 10000;
139
- const validUntil = 5000; // expired
140
- const adjusted = validUntil < now ? baseDecayScore * 0.5 : baseDecayScore;
141
- expect(adjusted).toBe(0.15);
142
- });
143
-
144
- test("non-expired memory decay score unchanged", () => {
145
- const baseDecayScore = 0.30;
146
- const now = 10000;
147
- const validUntil = 20000; // still valid
148
- const adjusted = validUntil < now ? baseDecayScore * 0.5 : baseDecayScore;
149
- expect(adjusted).toBe(0.30);
150
- });
151
-
152
- test("memory without validUntil — decay score unchanged", () => {
153
- const baseDecayScore = 0.30;
154
- const validUntil = null;
155
- const adjusted = (validUntil != null && validUntil < Date.now())
156
- ? baseDecayScore * 0.5
157
- : baseDecayScore;
158
- expect(adjusted).toBe(0.30);
159
- });
160
- });
@@ -1,121 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
-
3
- import {
4
- buildTaskHintTerms,
5
- extractTerms,
6
- looksLikeContinuityTask,
7
- looksLikeRecallOnlyTask,
8
- taskCueCoverage,
9
- } from "../term-registry.js";
10
-
11
- describe("term registry", () => {
12
- it("extracts compact Chinese and latin cue terms", () => {
13
- expect(extractTerms("继续 RecallNest continuity bridge 适配")).toEqual([
14
- "继续",
15
- "recallnest",
16
- "continuity",
17
- "bridge",
18
- "适配",
19
- ]);
20
- });
21
-
22
- it("can keep later terms when a wider extraction window is requested", () => {
23
- expect(
24
- extractTerms(
25
- "继续 RecallNest continuity helper boundary audit ranking scoring selection orchestration context composer stable query fallback profile forwarding gap runner isolation",
26
- 24,
27
- ),
28
- ).toEqual(expect.arrayContaining([
29
- "profile",
30
- "forwarding",
31
- "runner",
32
- "isolation",
33
- ]));
34
- });
35
-
36
- it("builds task hints for writing prompts across languages", () => {
37
- expect(buildTaskHintTerms("continue my AI writing project")).toEqual([
38
- "写作",
39
- "文章",
40
- "语气",
41
- "风格",
42
- "口语化",
43
- "不端着",
44
- "ai",
45
- "公众号",
46
- ]);
47
- });
48
-
49
- it("treats recall-only wording differently from writing actions", () => {
50
- expect(looksLikeRecallOnlyTask("你还记得我之前的偏好吗")).toBe(true);
51
- expect(looksLikeRecallOnlyTask("不要让我重复前情,继续写文章")).toBe(false);
52
- });
53
-
54
- it("tracks workflow cue coverage from pattern text", () => {
55
- expect(
56
- taskCueCoverage(
57
- "patterns",
58
- "Workflow pattern: Cross-window continuity handoff Tools: resume_context, checkpoint_session, search_memory",
59
- ),
60
- ).toEqual(["search_memory", "resume_context", "checkpoint"]);
61
- });
62
-
63
- it("treats named RecallNest and memory-layer continues as continuity tasks", () => {
64
- expect(looksLikeContinuityTask("回到 RecallNest 继续做")).toBe(true);
65
- expect(looksLikeContinuityTask("接着弄 RecallNest 那个 memory layer")).toBe(true);
66
- expect(looksLikeContinuityTask("把记忆层这条线先续上")).toBe(true);
67
- expect(looksLikeContinuityTask("把之前那套 recall 管线先捡起来")).toBe(true);
68
- expect(looksLikeContinuityTask("pick up where we left off on RecallNest")).toBe(true);
69
- expect(looksLikeContinuityTask("回到 A2A 继续做")).toBe(false);
70
- // Status queries are NOT continuation tasks — entity+cases is sufficient
71
- expect(looksLikeContinuityTask("RecallNest 现在啥情况")).toBe(false);
72
- });
73
-
74
- it("builds RecallNest continuity hints for memory-layer shorthand", () => {
75
- expect(buildTaskHintTerms("把刚才那个 memory layer 接回去")).toEqual(expect.arrayContaining([
76
- "recallnest",
77
- "checkpoint_session",
78
- "resume_context",
79
- "store_memory",
80
- ]));
81
- });
82
-
83
- it("builds RecallNest continuity hints for colloquial memory shorthand", () => {
84
- expect(buildTaskHintTerms("把那条跨窗口记忆的活接着弄完")).toEqual(expect.arrayContaining([
85
- "recallnest",
86
- "checkpoint_session",
87
- "resume_context",
88
- ]));
89
- expect(buildTaskHintTerms("把之前那套 recall 管线先捡起来")).toEqual(expect.arrayContaining([
90
- "recallnest",
91
- "checkpoint_session",
92
- "resume_context",
93
- ]));
94
- });
95
-
96
- it("treats 记忆项目 as continuity task and builds RecallNest hints", () => {
97
- expect(looksLikeContinuityTask("那个记忆项目,之前做到哪了")).toBe(true);
98
- expect(looksLikeContinuityTask("继续记忆项目")).toBe(true);
99
- expect(buildTaskHintTerms("那个记忆项目,之前做到哪了")).toEqual(expect.arrayContaining([
100
- "recallnest",
101
- "resume_context",
102
- ]));
103
- expect(buildTaskHintTerms("continue the memory project")).toEqual(expect.arrayContaining([
104
- "recallnest",
105
- ]));
106
- });
107
-
108
- it("treats 跨终端记忆/记忆功能/记忆服务 shorthand as continuity cue", () => {
109
- // "还搞吗" is a status question, not a continuation verb — correct to return false
110
- expect(looksLikeContinuityTask("那个跨终端记忆还搞吗")).toBe(false);
111
- expect(looksLikeContinuityTask("继续那个跨终端记忆")).toBe(true);
112
- expect(looksLikeContinuityTask("上次做的那个记忆功能")).toBe(true);
113
- expect(looksLikeContinuityTask("之前那个 MCP 记忆服务")).toBe(true);
114
- expect(buildTaskHintTerms("那个跨终端记忆还搞吗")).toEqual(expect.arrayContaining([
115
- "recallnest",
116
- ]));
117
- expect(buildTaskHintTerms("上次做的记忆功能")).toEqual(expect.arrayContaining([
118
- "recallnest",
119
- ]));
120
- });
121
- });
@@ -1,133 +0,0 @@
1
- import { describe, it, expect } from "bun:test";
2
- import { compressToolOutput } from "../tool-output-compressor.js";
3
-
4
- describe("compressToolOutput", () => {
5
- // ========================================================================
6
- // Base64 replacement
7
- // ========================================================================
8
-
9
- it("replaces large base64 blocks with size placeholder", () => {
10
- const base64 = "A".repeat(2000);
11
- const text = `Here is a screenshot:\n${base64}\nEnd of screenshot.`;
12
- const result = compressToolOutput(text);
13
- expect(result).toContain("[image:");
14
- expect(result).toContain("base64");
15
- expect(result).not.toContain("AAAA");
16
- });
17
-
18
- it("preserves short base64-like strings", () => {
19
- const text = "The hash is abc123==";
20
- expect(compressToolOutput(text)).toBe(text);
21
- });
22
-
23
- // ========================================================================
24
- // Git output compression
25
- // ========================================================================
26
-
27
- it("compresses git push boilerplate", () => {
28
- const text = `$ git push origin main
29
- Enumerating objects: 5, done.
30
- Counting objects: 100% (5/5), done.
31
- Delta compression using up to 8 threads
32
- Compressing objects: 100% (3/3), done.
33
- Writing objects: 100% (3/3), 450 bytes | 450.00 KiB/s, done.
34
- Total 3 (delta 2), reused 0 (delta 0)
35
- remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
36
- To github.com:user/repo.git
37
- abc1234..def5678 main -> main
38
-
39
- Next I'll check the status.`;
40
-
41
- const result = compressToolOutput(text);
42
- expect(result).toContain("[git: ok");
43
- expect(result).toContain("main");
44
- expect(result).not.toContain("Enumerating objects");
45
- expect(result).toContain("Next I'll check the status");
46
- });
47
-
48
- it("preserves git push failures", () => {
49
- const text = `$ git push
50
- Enumerating objects: 5, done.
51
- error: failed to push some refs to 'origin'
52
- hint: Updates were rejected because the tip of your branch is behind -> main`;
53
-
54
- const result = compressToolOutput(text);
55
- expect(result).toContain("error");
56
- expect(result).toContain("rejected");
57
- });
58
-
59
- // ========================================================================
60
- // Test output compression
61
- // ========================================================================
62
-
63
- it("compresses passing test summary", () => {
64
- const text = `$ bun test
65
- running 15 tests
66
- test a ... ok
67
- test b ... ok
68
- test c ... ok
69
- test result: ok. 15 passed; 0 failed; 0 ignored
70
-
71
- All tests passed.`;
72
-
73
- const result = compressToolOutput(text);
74
- expect(result.length).toBeLessThanOrEqual(text.length);
75
- expect(result).toContain("All tests passed");
76
- });
77
-
78
- it("preserves failing test output", () => {
79
- const text = `$ npm test
80
- Tests: 2 failed, 8 passed
81
- FAIL src/main.test.ts
82
- ✕ should handle edge case`;
83
-
84
- const result = compressToolOutput(text);
85
- expect(result).toContain("failed");
86
- expect(result).toContain("FAIL");
87
- });
88
-
89
- // ========================================================================
90
- // Safety: user/AI content untouched
91
- // ========================================================================
92
-
93
- it("never modifies plain conversation text", () => {
94
- const text = "User: How do I fix this git push error?\n\nAssistant: You need to pull first.";
95
- expect(compressToolOutput(text)).toBe(text);
96
- });
97
-
98
- it("preserves AI reasoning about tool results", () => {
99
- const text = "The test result shows 15 passed, which means our fix works.";
100
- expect(compressToolOutput(text)).toBe(text);
101
- });
102
-
103
- // ========================================================================
104
- // Edge cases
105
- // ========================================================================
106
-
107
- it("handles empty input", () => {
108
- expect(compressToolOutput("")).toBe("");
109
- });
110
-
111
- it("handles short input", () => {
112
- expect(compressToolOutput("hi")).toBe("hi");
113
- });
114
-
115
- it("handles text without tool outputs", () => {
116
- const text = "This is a normal conversation.\nNo tool outputs here.";
117
- expect(compressToolOutput(text)).toBe(text);
118
- });
119
-
120
- // ========================================================================
121
- // Large output truncation
122
- // ========================================================================
123
-
124
- it("truncates large unmatched tool output", () => {
125
- // Use content with spaces/special chars to avoid base64 regex match
126
- const largeOutput = Array.from({ length: 150 }, (_, i) => `line ${i}: some detailed output here`).join("\n");
127
- const text = `$ some-command\n${largeOutput}\n\nNext step.`;
128
- const result = compressToolOutput(text);
129
- expect(result).toContain("[...");
130
- expect(result).toContain("chars truncated");
131
- expect(result.length).toBeLessThan(text.length);
132
- });
133
- });