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,98 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
-
3
- import { registerAdvancedTools } from "../mcp-tools-advanced.js";
4
- import { registerGovernanceTools } from "../mcp-tools-governance.js";
5
- import { runToolSafely } from "../error-taxonomy.js";
6
-
7
- /**
8
- * P1-7 第二阶段回归:验证「主操作错误」与「C 类逻辑失败」经 registerTool 的
9
- * runToolSafely 包装后带 isError + reason_code,而不是被 handler 自吞成「看起来成功」的文本。
10
- *
11
- * 手法:用注入式 registerTool 捕获真实 handler,并复刻生产 registerTool 的 runToolSafely
12
- * 包装,使测试看到与生产一致的 isError 行为(无需起 MCP server)。
13
- */
14
-
15
- interface ToolResult {
16
- content: { type: string; text: string }[];
17
- isError?: boolean;
18
- }
19
- type CapturedHandler = (args: Record<string, unknown>) => Promise<ToolResult>;
20
-
21
- function captureTools(
22
- register: (deps: any) => void,
23
- getComponents: () => unknown,
24
- ): Map<string, CapturedHandler> {
25
- const handlers = new Map<string, CapturedHandler>();
26
- const deps = {
27
- registerTool: (
28
- name: string,
29
- _desc: string,
30
- _schema: unknown,
31
- handler: (...a: unknown[]) => unknown,
32
- ) => {
33
- // 复刻 mcp-server.ts registerTool 的兜底:把 handler 交给 runToolSafely。
34
- handlers.set(name, (args) => runToolSafely(name, async () => handler(args)) as Promise<ToolResult>);
35
- },
36
- getComponents,
37
- conflictStore: {},
38
- workflowObservationStore: {},
39
- getKGExtractor: () => null,
40
- getKGStore: () => null,
41
- };
42
- register(deps as any);
43
- return handlers;
44
- }
45
-
46
- describe("memory_drill_down error surfacing (P1-7)", () => {
47
- it("surfaces a store error as isError with store_error reason (no swallow)", async () => {
48
- const handlers = captureTools(registerAdvancedTools, () => ({
49
- store: {
50
- getById: async () => {
51
- throw new Error("lancedb table read failed");
52
- },
53
- },
54
- }));
55
- const drill = handlers.get("memory_drill_down");
56
- expect(drill).toBeDefined();
57
- const r = await drill!({ id: "abcd1234", level: "full" });
58
- expect(r.isError).toBe(true);
59
- expect(r.content[0].text).toContain("store_error");
60
- });
61
-
62
- it("still returns plain not-found text (read-only, no isError)", async () => {
63
- const handlers = captureTools(registerAdvancedTools, () => ({
64
- store: { getById: async () => null },
65
- }));
66
- const r = await handlers.get("memory_drill_down")!({ id: "abcd1234", level: "full" });
67
- expect(r.isError).toBeUndefined();
68
- expect(r.content[0].text).toContain("No memory found");
69
- });
70
- });
71
-
72
- describe("forget_memory error surfacing (P1-7 C 类)", () => {
73
- it("throws logic failure into the taxonomy layer (isError) not fake-success text", async () => {
74
- const handlers = captureTools(registerAdvancedTools, () => ({
75
- // store.get → null ⇒ forgetMemory 早返回 success:false(不触碰 kg/audit)
76
- store: { get: async () => null },
77
- }));
78
- const r = await handlers.get("forget_memory")!({ memoryId: "abcd1234", confirm: false });
79
- expect(r.isError).toBe(true);
80
- expect(r.content[0].text).toContain("not found");
81
- });
82
- });
83
-
84
- describe("manage_alias error surfacing (P1-7 C 类)", () => {
85
- it("throws invalid_input on missing add params (not returned as success text)", async () => {
86
- const handlers = captureTools(registerGovernanceTools, () => ({}));
87
- const r = await handlers.get("manage_alias")!({ action: "add" });
88
- expect(r.isError).toBe(true);
89
- expect(r.content[0].text).toContain("invalid_input");
90
- });
91
-
92
- it("throws invalid_input on missing remove trigger", async () => {
93
- const handlers = captureTools(registerGovernanceTools, () => ({}));
94
- const r = await handlers.get("manage_alias")!({ action: "remove" });
95
- expect(r.isError).toBe(true);
96
- expect(r.content[0].text).toContain("invalid_input");
97
- });
98
- });
@@ -1,160 +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
-
134
- // ========================================================================
135
- // 回归:工具输出块位于字符串结尾(原 \z 锚点 bug)
136
- // 旧实现 lookahead 末项是 \z(JS 无此锚点 → 当字面字符 z):
137
- // - 块结尾无字面 z 时整块漏匹配 → 末尾大输出逃过截断
138
- // - 块含字面 z 时惰性匹配在第一个 z 处误截断
139
- // 修复为 (?![\s\S])(字符串绝对结尾)后两种都应正确处理。
140
- // ========================================================================
141
-
142
- it("truncates a large tool output block that ends the string (no trailing marker)", () => {
143
- // 末尾块、无字母 z、后面没有下一个命令/空行大写/## → 旧 \z 实现整块漏匹配
144
- const largeOutput = Array.from({ length: 150 }, (_, i) => `line ${i}: detailed output here`).join("\n");
145
- const text = `$ some-command\n${largeOutput}`;
146
- const result = compressToolOutput(text);
147
- expect(result).toContain("chars truncated");
148
- expect(result.length).toBeLessThan(text.length);
149
- });
150
-
151
- it("does not cut a trailing tool output block at a literal 'z'", () => {
152
- // 末尾大块含字母 z → 旧 \z 实现在第一个 z 处截断匹配,导致块太短逃过截断
153
- const largeOutput = Array.from({ length: 150 }, (_, i) => `line ${i}: zebra output zone`).join("\n");
154
- const text = `$ some-command\n${largeOutput}`;
155
- const result = compressToolOutput(text);
156
- expect(result).toContain("chars truncated");
157
- // 截断尾部应保留块真正的结尾,而不是停在某个 z 之前
158
- expect(result).toContain("zone");
159
- });
160
- });
@@ -1,113 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { detectTopicTag, extractTopicTag, injectTopicTag } from "../topic-tag.js";
3
-
4
- describe("detectTopicTag", () => {
5
- it("detects auth-related content", () => {
6
- expect(detectTopicTag("Implement JWT authentication with OAuth2 flow")).toBe("auth");
7
- });
8
-
9
- it("detects deploy-related content", () => {
10
- expect(detectTopicTag("Set up CI/CD pipeline with GitHub Actions for production deployment")).toBe("deploy");
11
- });
12
-
13
- it("detects infra-related content", () => {
14
- expect(detectTopicTag("Configure Docker containers and Kubernetes cluster")).toBe("infra");
15
- });
16
-
17
- it("detects testing-related content", () => {
18
- expect(detectTopicTag("Write unit tests with bun test and add coverage for the new module")).toBe("testing");
19
- });
20
-
21
- it("detects database-related content", () => {
22
- expect(detectTopicTag("Run SQL migration to add new columns to postgres users table")).toBe("database");
23
- });
24
-
25
- it("detects api-related content", () => {
26
- expect(detectTopicTag("Design REST API endpoints for the new webhook integration")).toBe("api");
27
- });
28
-
29
- it("detects ui-related content", () => {
30
- expect(detectTopicTag("Build React component with Tailwind CSS for the dashboard layout")).toBe("ui");
31
- });
32
-
33
- it("detects performance-related content", () => {
34
- expect(detectTopicTag("Profile cache hit rates and optimize latency bottleneck")).toBe("perf");
35
- });
36
-
37
- it("detects security-related content", () => {
38
- expect(detectTopicTag("Fix XSS vulnerability and add CSRF protection")).toBe("security");
39
- });
40
-
41
- it("detects memory-related content", () => {
42
- expect(detectTopicTag("Improve recall retrieval with better embedding vectors for LanceDB")).toBe("memory");
43
- });
44
-
45
- it("detects mcp-related content", () => {
46
- expect(detectTopicTag("Register new MCP tool in the Model Context Protocol server")).toBe("mcp");
47
- });
48
-
49
- it("detects llm-related content", () => {
50
- expect(detectTopicTag("Optimize prompt engineering for better token efficiency with Claude")).toBe("llm");
51
- });
52
-
53
- it("returns undefined for generic text", () => {
54
- expect(detectTopicTag("Had a great day today")).toBeUndefined();
55
- });
56
-
57
- it("returns undefined for empty text", () => {
58
- expect(detectTopicTag("")).toBeUndefined();
59
- });
60
-
61
- it("picks strongest match when multiple topics present", () => {
62
- const tag = detectTopicTag("Deploy the authentication service to production using Docker");
63
- expect(tag).toBeDefined();
64
- // Multiple topics present; strongest wins
65
- expect(["auth", "deploy", "infra"]).toContain(tag);
66
- });
67
-
68
- it("handles Chinese text with English tech terms", () => {
69
- expect(detectTopicTag("配置 Docker 容器编排和 Kubernetes 集群")).toBe("infra");
70
- });
71
-
72
- it("truncates very long text for efficiency", () => {
73
- const longText = "Fix authentication bug. ".repeat(500);
74
- expect(detectTopicTag(longText)).toBe("auth");
75
- });
76
- });
77
-
78
- describe("extractTopicTag", () => {
79
- it("extracts topicTag from metadata JSON", () => {
80
- expect(extractTopicTag('{"topicTag":"auth","source":"manual"}')).toBe("auth");
81
- });
82
-
83
- it("returns undefined when no topicTag", () => {
84
- expect(extractTopicTag('{"source":"manual"}')).toBeUndefined();
85
- });
86
-
87
- it("returns undefined for invalid JSON", () => {
88
- expect(extractTopicTag("not json")).toBeUndefined();
89
- });
90
-
91
- it("returns undefined for undefined/null metadata", () => {
92
- expect(extractTopicTag(undefined)).toBeUndefined();
93
- expect(extractTopicTag("")).toBeUndefined();
94
- });
95
- });
96
-
97
- describe("injectTopicTag", () => {
98
- it("injects topicTag into metadata JSON", () => {
99
- const result = injectTopicTag('{"source":"manual"}', "deploy");
100
- const parsed = JSON.parse(result);
101
- expect(parsed.topicTag).toBe("deploy");
102
- expect(parsed.source).toBe("manual");
103
- });
104
-
105
- it("overwrites existing topicTag", () => {
106
- const result = injectTopicTag('{"topicTag":"old","source":"manual"}', "new");
107
- expect(JSON.parse(result).topicTag).toBe("new");
108
- });
109
-
110
- it("returns original on invalid JSON", () => {
111
- expect(injectTopicTag("broken", "tag")).toBe("broken");
112
- });
113
- });