recallnest 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/README.md +222 -254
  3. package/README_CN.md +206 -243
  4. package/package.json +3 -2
  5. package/src/capture-engine.ts +26 -4
  6. package/src/context-composer-stable.ts +5 -1
  7. package/src/embedder.ts +24 -0
  8. package/src/env-config.ts +18 -0
  9. package/src/ingest.ts +225 -17
  10. package/src/kg-extractor.ts +17 -0
  11. package/src/memory-output.ts +35 -0
  12. package/src/pivot-apply.ts +52 -1
  13. package/src/pivot-evidence.ts +128 -0
  14. package/src/ppr-traversal.ts +25 -1
  15. package/src/runtime-config.ts +9 -0
  16. package/src/session-engine.ts +11 -4
  17. package/src/session-output.ts +9 -1
  18. package/src/session-store.ts +2 -3
  19. package/src/skill-promotion.ts +67 -1
  20. package/src/__tests__/access-tracker-novelty.test.ts +0 -324
  21. package/src/__tests__/activity-counter.test.ts +0 -125
  22. package/src/__tests__/adaptive-fidelity.test.ts +0 -102
  23. package/src/__tests__/admission-control.test.ts +0 -206
  24. package/src/__tests__/age-label.test.ts +0 -33
  25. package/src/__tests__/anchor-generator.test.ts +0 -67
  26. package/src/__tests__/api-server-security.test.ts +0 -102
  27. package/src/__tests__/auto-consolidation.test.ts +0 -168
  28. package/src/__tests__/auto-recall.test.ts +0 -131
  29. package/src/__tests__/babel-integration.test.ts +0 -144
  30. package/src/__tests__/batch-dedup.test.ts +0 -65
  31. package/src/__tests__/belief-history.test.ts +0 -220
  32. package/src/__tests__/canary-eval.test.ts +0 -114
  33. package/src/__tests__/capture-engine.test.ts +0 -1328
  34. package/src/__tests__/capture-heuristic.test.ts +0 -171
  35. package/src/__tests__/cascade-forget.test.ts +0 -210
  36. package/src/__tests__/category-min-scores.test.ts +0 -116
  37. package/src/__tests__/cc8-cc1-essential-context.test.ts +0 -298
  38. package/src/__tests__/circuit-breaker.test.ts +0 -99
  39. package/src/__tests__/cluster-consolidation.test.ts +0 -1236
  40. package/src/__tests__/confidence-metadata.test.ts +0 -125
  41. package/src/__tests__/confidence-tracker.test.ts +0 -261
  42. package/src/__tests__/conflict-advisor.test.ts +0 -206
  43. package/src/__tests__/conflict-escalation.test.ts +0 -149
  44. package/src/__tests__/conflict-lifecycle.test.ts +0 -83
  45. package/src/__tests__/conflict-output.test.ts +0 -85
  46. package/src/__tests__/conflict-store.test.ts +0 -418
  47. package/src/__tests__/connector-v1.test.ts +0 -181
  48. package/src/__tests__/consolidation-engine.test.ts +0 -520
  49. package/src/__tests__/constructive-retrieval.test.ts +0 -509
  50. package/src/__tests__/context-collapse-renderer.test.ts +0 -214
  51. package/src/__tests__/context-composer-pins.test.ts +0 -64
  52. package/src/__tests__/context-composer-task-selection.test.ts +0 -24
  53. package/src/__tests__/context-reconstructor.test.ts +0 -65
  54. package/src/__tests__/context-renderer.test.ts +0 -91
  55. package/src/__tests__/continuity-eval.test.ts +0 -275
  56. package/src/__tests__/continuity-schema.test.ts +0 -97
  57. package/src/__tests__/contracts.test.ts +0 -262
  58. package/src/__tests__/conversation-importer.test.ts +0 -692
  59. package/src/__tests__/core-recall-eval.test.ts +0 -235
  60. package/src/__tests__/core-summary.test.ts +0 -109
  61. package/src/__tests__/data-checkup.test.ts +0 -304
  62. package/src/__tests__/decay-exemption.test.ts +0 -129
  63. package/src/__tests__/dedup-actions.test.ts +0 -58
  64. package/src/__tests__/dedup-guard.test.ts +0 -39
  65. package/src/__tests__/deterministic-id.test.ts +0 -39
  66. package/src/__tests__/distill-lock.test.ts +0 -305
  67. package/src/__tests__/distill-session-argshape.test.ts +0 -79
  68. package/src/__tests__/doctor.test.ts +0 -319
  69. package/src/__tests__/dream-pipeline.test.ts +0 -820
  70. package/src/__tests__/dream-scope-isolation.test.ts +0 -386
  71. package/src/__tests__/dream-write-batching.test.ts +0 -211
  72. package/src/__tests__/drill-down.test.ts +0 -108
  73. package/src/__tests__/embedder.test.ts +0 -122
  74. package/src/__tests__/emotion-decay.test.ts +0 -46
  75. package/src/__tests__/emotion-detector.test.ts +0 -56
  76. package/src/__tests__/emotion-phase1-integration.test.ts +0 -153
  77. package/src/__tests__/emotion-retrieval.test.ts +0 -56
  78. package/src/__tests__/entity-resolver.test.ts +0 -58
  79. package/src/__tests__/env-config-invariant.test.ts +0 -80
  80. package/src/__tests__/env-config.test.ts +0 -211
  81. package/src/__tests__/error-signature-boost.test.ts +0 -87
  82. package/src/__tests__/error-signature.test.ts +0 -95
  83. package/src/__tests__/error-taxonomy.test.ts +0 -129
  84. package/src/__tests__/ethics-layer.test.ts +0 -476
  85. package/src/__tests__/event-segmenter.test.ts +0 -153
  86. package/src/__tests__/feature-flags-integration.test.ts +0 -54
  87. package/src/__tests__/frequency-tracker.test.ts +0 -113
  88. package/src/__tests__/freshness.test.ts +0 -186
  89. package/src/__tests__/graph-export.test.ts +0 -489
  90. package/src/__tests__/hotness-blend.test.ts +0 -283
  91. package/src/__tests__/ingest-chunking.test.ts +0 -44
  92. package/src/__tests__/ingest-scope-prefix.test.ts +0 -69
  93. package/src/__tests__/ingest.test.ts +0 -360
  94. package/src/__tests__/interference-detector.test.ts +0 -203
  95. package/src/__tests__/kg-extractor.test.ts +0 -192
  96. package/src/__tests__/kg-store.test.ts +0 -300
  97. package/src/__tests__/llm-client-pivot.test.ts +0 -628
  98. package/src/__tests__/llm-consolidation.test.ts +0 -226
  99. package/src/__tests__/maintenance-locks.test.ts +0 -197
  100. package/src/__tests__/mcp-efficiency.test.ts +0 -288
  101. package/src/__tests__/mcp-related-scopes.test.ts +0 -151
  102. package/src/__tests__/memory-boundaries.test.ts +0 -282
  103. package/src/__tests__/memory-evolution-b2-b3-e1.test.ts +0 -362
  104. package/src/__tests__/memory-evolution.test.ts +0 -271
  105. package/src/__tests__/memory-health-rebalance.test.ts +0 -144
  106. package/src/__tests__/memory-lint.test.ts +0 -421
  107. package/src/__tests__/memory-output.test.ts +0 -224
  108. package/src/__tests__/memory-promotion.test.ts +0 -315
  109. package/src/__tests__/memory-utility.test.ts +0 -256
  110. package/src/__tests__/migrate-dedup-ids.test.ts +0 -65
  111. package/src/__tests__/multi-vector.test.ts +0 -242
  112. package/src/__tests__/narrative-architecture.test.ts +0 -456
  113. package/src/__tests__/noise-filter.test.ts +0 -205
  114. package/src/__tests__/noise-prototype-bank.test.ts +0 -280
  115. package/src/__tests__/obsidian-connector.test.ts +0 -363
  116. package/src/__tests__/omitted-reporting.test.ts +0 -98
  117. package/src/__tests__/openai-contract.test.ts +0 -513
  118. package/src/__tests__/pending-review.test.ts +0 -77
  119. package/src/__tests__/pii-redact.test.ts +0 -333
  120. package/src/__tests__/pivot-apply.test.ts +0 -373
  121. package/src/__tests__/pivot-distill-supervisor.test.ts +0 -457
  122. package/src/__tests__/pivot-distill.test.ts +0 -1178
  123. package/src/__tests__/post-retrieval-filter.test.ts +0 -178
  124. package/src/__tests__/ppr-traversal.test.ts +0 -256
  125. package/src/__tests__/prediction-engine.test.ts +0 -349
  126. package/src/__tests__/predictive-prospective-memory.test.ts +0 -416
  127. package/src/__tests__/preference-guard-smoke.test.ts +0 -211
  128. package/src/__tests__/preference-matcher.test.ts +0 -282
  129. package/src/__tests__/preference-slots.test.ts +0 -205
  130. package/src/__tests__/promotion-distinct-sources.test.ts +0 -162
  131. package/src/__tests__/prospective-memory.test.ts +0 -283
  132. package/src/__tests__/pull-from-macbook-sync-only.test.ts +0 -679
  133. package/src/__tests__/query-entity-detector.test.ts +0 -171
  134. package/src/__tests__/query-expander-alias.test.ts +0 -146
  135. package/src/__tests__/query-expander.test.ts +0 -34
  136. package/src/__tests__/recall-gate.test.ts +0 -266
  137. package/src/__tests__/recall-governor.test.ts +0 -227
  138. package/src/__tests__/recall-mode.test.ts +0 -64
  139. package/src/__tests__/resolve-db-path.test.ts +0 -130
  140. package/src/__tests__/resolve-tier.test.ts +0 -64
  141. package/src/__tests__/result-synthesizer.test.ts +0 -198
  142. package/src/__tests__/resume-context.test.ts +0 -4898
  143. package/src/__tests__/retrieval-eval.test.ts +0 -163
  144. package/src/__tests__/retrieval-trace.test.ts +0 -213
  145. package/src/__tests__/retrieve-audit-revision.test.ts +0 -255
  146. package/src/__tests__/retriever-boundary-weight.test.ts +0 -136
  147. package/src/__tests__/retriever-lazy-init-dispatch.test.ts +0 -123
  148. package/src/__tests__/retroactive-boost.test.ts +0 -223
  149. package/src/__tests__/salience-scoring.test.ts +0 -106
  150. package/src/__tests__/scope-inventory.test.ts +0 -200
  151. package/src/__tests__/scope-policy.test.ts +0 -69
  152. package/src/__tests__/scope-suggester.test.ts +0 -80
  153. package/src/__tests__/session-checkpoint.test.ts +0 -291
  154. package/src/__tests__/session-distiller.test.ts +0 -640
  155. package/src/__tests__/session-gc.test.ts +0 -215
  156. package/src/__tests__/skill-engine.test.ts +0 -552
  157. package/src/__tests__/skill-promotion.test.ts +0 -654
  158. package/src/__tests__/skill-verifier.test.ts +0 -122
  159. package/src/__tests__/source-heartbeat.test.ts +0 -254
  160. package/src/__tests__/stable-context-length-contract.test.ts +0 -62
  161. package/src/__tests__/store-patch-metadata.test.ts +0 -235
  162. package/src/__tests__/store-read-consistency.test.ts +0 -122
  163. package/src/__tests__/store-schema-migration.test.ts +0 -87
  164. package/src/__tests__/store-scope-audit.test.ts +0 -81
  165. package/src/__tests__/store-upsert.test.ts +0 -201
  166. package/src/__tests__/supersede-chain.test.ts +0 -154
  167. package/src/__tests__/synthesis-contract.test.ts +0 -252
  168. package/src/__tests__/synthesis-promotion.test.ts +0 -369
  169. package/src/__tests__/temporal-parser.test.ts +0 -241
  170. package/src/__tests__/temporal-validity.test.ts +0 -160
  171. package/src/__tests__/term-registry-terminal-roster.test.ts +0 -127
  172. package/src/__tests__/term-registry.test.ts +0 -121
  173. package/src/__tests__/tool-error-surfacing.test.ts +0 -98
  174. package/src/__tests__/tool-output-compressor.test.ts +0 -160
  175. package/src/__tests__/topic-tag.test.ts +0 -113
  176. package/src/__tests__/trusted-memory.test.ts +0 -867
  177. package/src/__tests__/ui-server-security.test.ts +0 -93
  178. package/src/__tests__/usage-tracker.test.ts +0 -287
  179. package/src/__tests__/utility-weight.test.ts +0 -57
  180. package/src/__tests__/verbatim-self-recall.test.ts +0 -279
  181. package/src/__tests__/version-manager.test.ts +0 -248
  182. package/src/__tests__/workflow-observation-skill-outcome.test.ts +0 -352
  183. package/src/__tests__/workflow-observation.test.ts +0 -433
  184. 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,127 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
-
3
- import { selectStableResults } from "../context-composer-stable-selection.js";
4
- import { selectTaskResults } from "../context-composer-task-selection.js";
5
- import {
6
- PREFERENCE_SPECIFICITY_GROUPS,
7
- TASK_RESULT_SPECIFICITY_GROUPS,
8
- } from "../term-registry.js";
9
- import type { RetrievalResult } from "../retriever.js";
10
-
11
- // 端清单在检索术语簇里的落地。守两件事:
12
- // ① 补上的端名真的解除了过滤(行为层)
13
- // ② **有意的不对称不许被"机械补齐"抹掉**(结构层)——
14
- // taskTerms 补端名、resultTerms 不补。这条最容易被后来人当成漏改而"顺手统一",
15
- // 所以用测试钉住,并把理由写在断言旁边。
16
- // 背景同 memory-boundaries.ts:64-70 的 TRANSCRIPT_SOURCES —— 那处是字面 scope 前缀层,
17
- // 按字面补;这里是检索语义层,两侧方向相反,不能照抄。
18
-
19
- function stableCandidate(text: string): RetrievalResult {
20
- return {
21
- entry: {
22
- id: `test-${text.slice(0, 8)}`,
23
- text,
24
- vector: [],
25
- category: "preferences",
26
- scope: "memory:pivot",
27
- importance: 0.8,
28
- timestamp: Date.now(),
29
- },
30
- score: 1,
31
- sources: {},
32
- } as RetrievalResult;
33
- }
34
-
35
- function taskCandidate(text: string): RetrievalResult {
36
- return {
37
- entry: {
38
- id: `test-${text.slice(0, 8)}`,
39
- text,
40
- vector: [],
41
- category: "cases",
42
- scope: "memory:pivot",
43
- importance: 0.8,
44
- timestamp: Date.now(),
45
- },
46
- score: 1,
47
- sources: {},
48
- } as RetrievalResult;
49
- }
50
-
51
- // 会被 PREFERENCE_SPECIFICITY_GROUPS 认领的偏好(正文含 "codex" + "独立验证")
52
- const CODEX_REVIEW_PREF =
53
- "用户要求互审时由 Codex 做独立验证,交接材料里客观证据自动抽取,判断性材料必须手写填充。";
54
-
55
- describe("端清单 · PREFERENCE_SPECIFICITY_GROUPS", () => {
56
- it("问 kimi / agy / antigravity 时不再把 CC·Codex 的验收偏好滤掉", () => {
57
- for (const task of [
58
- "kimi 互审的时候要注意什么",
59
- "agy 那边是谁在做审查",
60
- "antigravity 的会话是怎么接进来的",
61
- ]) {
62
- expect(
63
- selectStableResults("preferences", [stableCandidate(CODEX_REVIEW_PREF)], 3, { taskSeed: task }),
64
- ).toHaveLength(1);
65
- }
66
- });
67
-
68
- it("问端拓扑本身(三端 / 四端)时同样解除", () => {
69
- for (const task of ["四端的触发规则都装好了吗", "三端现在还一致吗"]) {
70
- expect(
71
- selectStableResults("preferences", [stableCandidate(CODEX_REVIEW_PREF)], 3, { taskSeed: task }),
72
- ).toHaveLength(1);
73
- }
74
- });
75
-
76
- it("与端无关的任务仍然滤掉 —— 放宽不能变成不过滤", () => {
77
- for (const task of ["帮我写一篇公众号文章", "百度网盘上传断链怎么排查"]) {
78
- expect(
79
- selectStableResults("preferences", [stableCandidate(CODEX_REVIEW_PREF)], 3, { taskSeed: task }),
80
- ).toHaveLength(0);
81
- }
82
- });
83
-
84
- it("resultTerms 有意不含端名 —— 别照着 taskTerms 补齐", () => {
85
- // resultTerms 是「谁被这组认领」,加裸端名会把"顺带提了一句 kimi"的无关偏好
86
- // 整条变成可滤对象。已知实例:裸词 "codex" 已因 `.codex/AGENTS.md` 这种
87
- // 路径片段误认领记忆(shadow 实测 b8731a81)。再加端名 = 把缺陷复制三份。
88
- for (const term of ["kimi", "antigravity", "agy", "三端", "四端"]) {
89
- expect(PREFERENCE_SPECIFICITY_GROUPS[0]?.resultTerms).not.toContain(term);
90
- }
91
- expect(PREFERENCE_SPECIFICITY_GROUPS[0]?.taskTerms).toEqual(
92
- expect.arrayContaining(["kimi", "antigravity", "agy", "三端", "四端"]),
93
- );
94
- });
95
- });
96
-
97
- describe("端清单 · 三端历史事件组(TASK_RESULT_SPECIFICITY_GROUPS)", () => {
98
- const historyGroup = TASK_RESULT_SPECIFICITY_GROUPS.find((group) =>
99
- group.resultTerms.includes("three-terminal continuity trigger validation"),
100
- );
101
-
102
- it("resultTerms 是历史专名,不是端清单 —— 不补任何端名", () => {
103
- expect(historyGroup).toBeDefined();
104
- for (const term of ["kimi", "antigravity", "agy"]) {
105
- expect(historyGroup?.resultTerms).not.toContain(term);
106
- }
107
- });
108
-
109
- it("taskTerms 补她真实的问法(三端 / 四端),但同样不补端名", () => {
110
- // "三端" 不是 "三终端" 的子串,补之前真的匹配不上
111
- expect(historyGroup?.taskTerms).toEqual(expect.arrayContaining(["三端", "四端"]));
112
- for (const term of ["kimi", "antigravity", "agy"]) {
113
- expect(historyGroup?.taskTerms).not.toContain(term);
114
- }
115
- });
116
-
117
- it("只提端名、不问触发/接入的任务,不该把这条三端历史放进来", () => {
118
- const history = taskCandidate(
119
- "三端连续性触发验证:three-terminal continuity trigger validation 已完成,continue-style prompts triggered recall reliably。",
120
- );
121
- expect(selectTaskResults("cases", [history], 3, { taskSeed: "kimi 挂了" })).toHaveLength(0);
122
- // 对照:真正相关的问法照旧放行
123
- expect(
124
- selectTaskResults("cases", [history], 3, { taskSeed: "三端的触发验证结论还成立吗" }).length,
125
- ).toBeGreaterThan(0);
126
- });
127
- });
@@ -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
- });