@symerian/symi 3.0.17 → 3.0.19

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 (259) hide show
  1. package/dist/{audio-preflight-CBDFctZN.js → audio-preflight-BfmZbg4Y.js} +4 -4
  2. package/dist/{audio-preflight-gsZSpG-6.js → audio-preflight-DcuC-liM.js} +4 -4
  3. package/dist/build-info.json +3 -3
  4. package/dist/bundled/boot-md/handler.js +8 -8
  5. package/dist/bundled/session-memory/handler.js +7 -7
  6. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  7. package/dist/{chrome-nPMY1XTJ.js → chrome-Bo7cbvFK.js} +5 -5
  8. package/dist/{chrome-BjVab8gM.js → chrome-DYp18Q0t.js} +5 -5
  9. package/dist/{deliver-D-QFqm31.js → deliver-ChSIbiMM.js} +1 -1
  10. package/dist/{deliver-B4-bcot9.js → deliver-DEgRQM4J.js} +1 -1
  11. package/dist/extensionAPI.js +7 -7
  12. package/dist/{image-CDwtQjmt.js → image-Bx-hvoNJ.js} +1 -1
  13. package/dist/{image-CcS-vzTA.js → image-CQl_mjWk.js} +1 -1
  14. package/dist/llm-slug-generator.js +7 -7
  15. package/dist/{manager-BnEdHzmO.js → manager-D_pn0urG.js} +1 -1
  16. package/dist/{manager-09r0qPze.js → manager-YQxK2t0C.js} +1 -1
  17. package/dist/{pi-embedded-CWsY69-4.js → pi-embedded-CLw_ZzEZ.js} +16 -16
  18. package/dist/{pi-embedded-helpers-BBMy-lqr.js → pi-embedded-helpers-B5I53aw6.js} +4 -4
  19. package/dist/{pi-embedded-helpers-ChEYbgVj.js → pi-embedded-helpers-sUAEIC9X.js} +4 -4
  20. package/dist/plugin-sdk/{accounts-BfyWsC_i.js → accounts-CWFytwbR.js} +3 -3
  21. package/dist/plugin-sdk/{active-listener-DcJW7xAT.js → active-listener-BkZ4jHrL.js} +2 -2
  22. package/dist/plugin-sdk/{agent-scope-ChbGV6of.js → agent-scope-C9gfY_Gk.js} +2 -2
  23. package/dist/plugin-sdk/{audio-preflight-D3GtNLqW.js → audio-preflight-HKbdzXLZ.js} +21 -21
  24. package/dist/plugin-sdk/{bindings-CN2Qmefj.js → bindings-BaKIqPPy.js} +2 -2
  25. package/dist/plugin-sdk/{channel-web-DTyqujjA.js → channel-web-D5nWiTH1.js} +18 -18
  26. package/dist/plugin-sdk/{chrome-BKzAKr3K.js → chrome-klTSnz-9.js} +3 -3
  27. package/dist/plugin-sdk/{chunk-DhDkBujV.js → chunk-BbrYSny_.js} +1 -1
  28. package/dist/plugin-sdk/{command-format-CVrYFyZS.js → command-format-BN6tyZt6.js} +1 -1
  29. package/dist/plugin-sdk/{commands-registry-17yfZkHZ.js → commands-registry-CTzKKtY6.js} +4 -4
  30. package/dist/plugin-sdk/{config-7wk65zKC.js → config-Crv2qEdJ.js} +9 -9
  31. package/dist/plugin-sdk/{consolidate-exbAW0ml.js → consolidate-DT1QH65Q.js} +2 -2
  32. package/dist/plugin-sdk/{deliver-TxAcw7J5.js → deliver-7rOvAlrc.js} +12 -12
  33. package/dist/plugin-sdk/{diagnostic-Debx4frd.js → diagnostic-0nsxhWp7.js} +1 -1
  34. package/dist/plugin-sdk/{fs-safe-wBYbAkJF.js → fs-safe-DfWYBeWF.js} +1 -1
  35. package/dist/plugin-sdk/{gemini-auth-7U2pm2Ky.js → gemini-auth-C0N0_u49.js} +1 -1
  36. package/dist/plugin-sdk/{image-BtDVmYA5.js → image-WOSl2apK.js} +4 -4
  37. package/dist/plugin-sdk/index.js +43 -43
  38. package/dist/plugin-sdk/{ir-CKMvRrGW.js → ir-9J84MTls.js} +4 -4
  39. package/dist/plugin-sdk/{local-roots-c_gaPs01.js → local-roots-OLRDbvyY.js} +3 -3
  40. package/dist/plugin-sdk/{login-DUym1Jy0.js → login-C7x4q0i2.js} +7 -7
  41. package/dist/plugin-sdk/{login-qr-B-WBdvrX.js → login-qr-Dv5_MoAW.js} +9 -9
  42. package/dist/plugin-sdk/{manager-B71SCzos.js → manager-C83tK17x.js} +8 -8
  43. package/dist/plugin-sdk/{manifest-registry-Dnic6Chh.js → manifest-registry-CJMV-PI7.js} +1 -1
  44. package/dist/plugin-sdk/{markdown-tables-Dur7OTlM.js → markdown-tables-DXNKz5y_.js} +1 -1
  45. package/dist/plugin-sdk/{message-channel-BrAhJJV_.js → message-channel-aGy1HbQQ.js} +1 -1
  46. package/dist/plugin-sdk/{model-selection-B9qaVQSJ.js → model-selection-C-3-tpe7.js} +4 -4
  47. package/dist/plugin-sdk/{outbound-DB1wDM8b.js → outbound-DquCeSy5.js} +6 -6
  48. package/dist/plugin-sdk/{pi-auth-json-ZO118hoy.js → pi-auth-json-D9PDCXGn.js} +1 -1
  49. package/dist/plugin-sdk/{pi-embedded-helpers-s_U0Un7j.js → pi-embedded-helpers-D3ygfH7l.js} +16 -16
  50. package/dist/plugin-sdk/{plugins-DF81oSaI.js → plugins-DOwnSg9D.js} +4 -4
  51. package/dist/plugin-sdk/{pw-ai-CTwP02uv.js → pw-ai-rlengLjb.js} +8 -8
  52. package/dist/plugin-sdk/{qmd-manager-CBaSGant.js → qmd-manager-BzxFjRFa.js} +4 -4
  53. package/dist/plugin-sdk/{registry-CZVURNhF.js → registry-5iFfixlB.js} +2 -2
  54. package/dist/plugin-sdk/{replies-hwRbkU3z.js → replies-BXOzO_H5.js} +7 -7
  55. package/dist/plugin-sdk/{reply-prefix-CaXmzZlx.js → reply-prefix-INAKTqCU.js} +1 -1
  56. package/dist/plugin-sdk/{resolve-outbound-target-fxVSOBmk.js → resolve-outbound-target-DvbxHtqp.js} +2 -2
  57. package/dist/plugin-sdk/{resolve-route-ClCyiOeu.js → resolve-route-URXlY3AK.js} +3 -3
  58. package/dist/plugin-sdk/{runner-Cq5jvwQ7.js → runner-Bv0_DWoH.js} +9 -9
  59. package/dist/plugin-sdk/{session-B_TkB65Y.js → session-C3r8l7ou.js} +4 -4
  60. package/dist/plugin-sdk/{skill-commands-0LF9HTGr.js → skill-commands-KjLUGIdZ.js} +5 -5
  61. package/dist/plugin-sdk/{skills-BIT_O7J0.js → skills-BrsD4L5c.js} +7 -7
  62. package/dist/plugin-sdk/{sqlite-Bx5Y5U5X.js → sqlite-CjW7ME1H.js} +1 -1
  63. package/dist/plugin-sdk/{subsystem-CXqYeDy-.js → subsystem-DcOg1xJr.js} +1 -1
  64. package/dist/plugin-sdk/{synthesis-DtsYAj1E.js → synthesis-CY7YAasV.js} +38 -38
  65. package/dist/plugin-sdk/{target-errors-B8mokOeH.js → target-errors-BVWJGWFq.js} +2 -2
  66. package/dist/plugin-sdk/{thinking-Ca0DhqzO.js → thinking-CtsTDPOi.js} +3 -3
  67. package/dist/plugin-sdk/{tokens-CvlONEqh.js → tokens-8lqOTZCB.js} +1 -1
  68. package/dist/plugin-sdk/{tool-images-DpBaWEHT.js → tool-images-Cl_rGIUZ.js} +2 -2
  69. package/dist/plugin-sdk/{tool-loop-detection-BOvUFa0f.js → tool-loop-detection-Da4WUT_P.js} +2 -2
  70. package/dist/plugin-sdk/{unified-runner-CnM7lyNd.js → unified-runner-nwMnsZyj.js} +60 -60
  71. package/dist/plugin-sdk/web-BlweOZDp.js +54 -0
  72. package/dist/plugin-sdk/{whatsapp-actions-CvnfsFJm.js → whatsapp-actions-DpfaGYs7.js} +21 -21
  73. package/dist/{pw-ai-BW8_KeDf.js → pw-ai-BqxJG-Wh.js} +1 -1
  74. package/dist/{pw-ai-j9IE1K0-.js → pw-ai-C-NSGye0.js} +1 -1
  75. package/dist/{runner-8ALr2UII.js → runner-COGFTeDw.js} +1 -1
  76. package/dist/{runner-C4-9kFdR.js → runner-DhCi2lT1.js} +1 -1
  77. package/dist/{synthesis-Cph3LhA1.js → synthesis-CXZu24Vx.js} +7 -7
  78. package/dist/{synthesis-Cus0A2dL.js → synthesis-DrPxcMlQ.js} +7 -7
  79. package/dist/{unified-runner-CX80YMTk.js → unified-runner-iByUazvW.js} +16 -16
  80. package/dist/{web-ChozvJ7I.js → web-EsMQBIYf.js} +7 -7
  81. package/dist/{web-DFlsbXmQ.js → web-PPg5y6xI.js} +7 -7
  82. package/package.json +1 -1
  83. package/dist/plugin-sdk/web-CIPJBHAU.js +0 -54
  84. package/extensions/copilot-proxy/README.md +0 -24
  85. package/extensions/copilot-proxy/index.ts +0 -154
  86. package/extensions/copilot-proxy/node_modules/.bin/symi +0 -21
  87. package/extensions/copilot-proxy/package.json +0 -15
  88. package/extensions/copilot-proxy/symi.plugin.json +0 -9
  89. package/extensions/device-pair/index.ts +0 -642
  90. package/extensions/device-pair/symi.plugin.json +0 -20
  91. package/extensions/diagnostics-otel/index.ts +0 -15
  92. package/extensions/diagnostics-otel/node_modules/.bin/acorn +0 -21
  93. package/extensions/diagnostics-otel/node_modules/.bin/symi +0 -21
  94. package/extensions/diagnostics-otel/package.json +0 -27
  95. package/extensions/diagnostics-otel/src/service.test.ts +0 -290
  96. package/extensions/diagnostics-otel/src/service.ts +0 -666
  97. package/extensions/diagnostics-otel/symi.plugin.json +0 -8
  98. package/extensions/google-antigravity-auth/README.md +0 -24
  99. package/extensions/google-antigravity-auth/index.ts +0 -424
  100. package/extensions/google-antigravity-auth/node_modules/.bin/symi +0 -21
  101. package/extensions/google-antigravity-auth/package.json +0 -15
  102. package/extensions/google-antigravity-auth/symi.plugin.json +0 -9
  103. package/extensions/google-gemini-cli-auth/README.md +0 -35
  104. package/extensions/google-gemini-cli-auth/index.ts +0 -75
  105. package/extensions/google-gemini-cli-auth/node_modules/.bin/symi +0 -21
  106. package/extensions/google-gemini-cli-auth/oauth.test.ts +0 -162
  107. package/extensions/google-gemini-cli-auth/oauth.ts +0 -636
  108. package/extensions/google-gemini-cli-auth/package.json +0 -15
  109. package/extensions/google-gemini-cli-auth/symi.plugin.json +0 -9
  110. package/extensions/learning-loop/index.ts +0 -159
  111. package/extensions/learning-loop/node_modules/.bin/symi +0 -21
  112. package/extensions/learning-loop/package.json +0 -18
  113. package/extensions/learning-loop/src/analytics/gateway-methods.ts +0 -230
  114. package/extensions/learning-loop/src/analytics/metrics-aggregator.ts +0 -153
  115. package/extensions/learning-loop/src/capture/run-tracker.ts +0 -181
  116. package/extensions/learning-loop/src/capture/serializer.ts +0 -74
  117. package/extensions/learning-loop/src/db.ts +0 -583
  118. package/extensions/learning-loop/src/feedback/explicit-feedback.ts +0 -58
  119. package/extensions/learning-loop/src/feedback/implicit-signals.ts +0 -89
  120. package/extensions/learning-loop/src/graph/edge-inference.ts +0 -189
  121. package/extensions/learning-loop/src/graph/graph-retrieval.ts +0 -144
  122. package/extensions/learning-loop/src/graph/graph-store.ts +0 -183
  123. package/extensions/learning-loop/src/hooks.ts +0 -244
  124. package/extensions/learning-loop/src/injection/cache.ts +0 -73
  125. package/extensions/learning-loop/src/injection/context-injector.ts +0 -104
  126. package/extensions/learning-loop/src/injection/prompt-builder.ts +0 -43
  127. package/extensions/learning-loop/src/learning/embedding-bridge.ts +0 -54
  128. package/extensions/learning-loop/src/learning/learning-extractor.ts +0 -217
  129. package/extensions/learning-loop/src/learning/learning-store.ts +0 -158
  130. package/extensions/learning-loop/src/learning/retrieval.ts +0 -87
  131. package/extensions/learning-loop/src/math/confidence-intervals.ts +0 -62
  132. package/extensions/learning-loop/src/math/ewma.ts +0 -51
  133. package/extensions/learning-loop/src/math/weighted-scorer.ts +0 -42
  134. package/extensions/learning-loop/src/schema.ts +0 -176
  135. package/extensions/learning-loop/src/scoring/normalization.ts +0 -32
  136. package/extensions/learning-loop/src/scoring/quality-engine.ts +0 -78
  137. package/extensions/learning-loop/src/scoring/signal-extractors.ts +0 -155
  138. package/extensions/learning-loop/src/test/context-injector.test.ts +0 -142
  139. package/extensions/learning-loop/src/test/fixes.test.ts +0 -1286
  140. package/extensions/learning-loop/src/test/graph.test.ts +0 -711
  141. package/extensions/learning-loop/src/test/integration.test.ts +0 -312
  142. package/extensions/learning-loop/src/test/learning-store.test.ts +0 -191
  143. package/extensions/learning-loop/src/test/math.test.ts +0 -148
  144. package/extensions/learning-loop/src/test/quality-engine.test.ts +0 -231
  145. package/extensions/learning-loop/src/test/run-tracker.test.ts +0 -143
  146. package/extensions/learning-loop/src/types.ts +0 -281
  147. package/extensions/learning-loop/symi.plugin.json +0 -46
  148. package/extensions/llm-task/README.md +0 -97
  149. package/extensions/llm-task/index.ts +0 -6
  150. package/extensions/llm-task/package.json +0 -12
  151. package/extensions/llm-task/src/llm-task-tool.test.ts +0 -138
  152. package/extensions/llm-task/src/llm-task-tool.ts +0 -249
  153. package/extensions/llm-task/symi.plugin.json +0 -21
  154. package/extensions/memory-lancedb/config.ts +0 -161
  155. package/extensions/memory-lancedb/index.test.ts +0 -330
  156. package/extensions/memory-lancedb/index.ts +0 -670
  157. package/extensions/memory-lancedb/node_modules/.bin/arrow2csv +0 -21
  158. package/extensions/memory-lancedb/node_modules/.bin/openai +0 -21
  159. package/extensions/memory-lancedb/node_modules/.bin/symi +0 -21
  160. package/extensions/memory-lancedb/package.json +0 -20
  161. package/extensions/memory-lancedb/symi.plugin.json +0 -71
  162. package/extensions/minimax-portal-auth/README.md +0 -33
  163. package/extensions/minimax-portal-auth/index.ts +0 -161
  164. package/extensions/minimax-portal-auth/node_modules/.bin/symi +0 -21
  165. package/extensions/minimax-portal-auth/oauth.ts +0 -247
  166. package/extensions/minimax-portal-auth/package.json +0 -15
  167. package/extensions/minimax-portal-auth/symi.plugin.json +0 -9
  168. package/extensions/model-equalizer/index.ts +0 -80
  169. package/extensions/model-equalizer/skills/model-equalizer/SKILL.md +0 -58
  170. package/extensions/model-equalizer/src/detection.ts +0 -62
  171. package/extensions/model-equalizer/src/enhancer.ts +0 -63
  172. package/extensions/model-equalizer/src/test/detection.test.ts +0 -218
  173. package/extensions/model-equalizer/src/test/enhancer.test.ts +0 -137
  174. package/extensions/model-equalizer/src/test/integration.test.ts +0 -185
  175. package/extensions/model-equalizer/src/types.ts +0 -24
  176. package/extensions/model-equalizer/symi.plugin.json +0 -12
  177. package/extensions/phone-control/index.ts +0 -421
  178. package/extensions/phone-control/symi.plugin.json +0 -10
  179. package/extensions/pipeline/README.md +0 -75
  180. package/extensions/pipeline/SKILL.md +0 -97
  181. package/extensions/pipeline/index.ts +0 -18
  182. package/extensions/pipeline/package.json +0 -11
  183. package/extensions/pipeline/src/pipeline-tool.test.ts +0 -345
  184. package/extensions/pipeline/src/pipeline-tool.ts +0 -266
  185. package/extensions/pipeline/src/windows-spawn.test.ts +0 -148
  186. package/extensions/pipeline/src/windows-spawn.ts +0 -193
  187. package/extensions/pipeline/symi.plugin.json +0 -10
  188. package/extensions/qwen-portal-auth/README.md +0 -24
  189. package/extensions/qwen-portal-auth/index.ts +0 -134
  190. package/extensions/qwen-portal-auth/oauth.ts +0 -190
  191. package/extensions/qwen-portal-auth/symi.plugin.json +0 -9
  192. package/extensions/talk-voice/index.ts +0 -150
  193. package/extensions/talk-voice/symi.plugin.json +0 -10
  194. package/extensions/thread-ownership/index.test.ts +0 -180
  195. package/extensions/thread-ownership/index.ts +0 -133
  196. package/extensions/thread-ownership/symi.plugin.json +0 -28
  197. package/skills/1password/SKILL.md +0 -71
  198. package/skills/1password/references/cli-examples.md +0 -29
  199. package/skills/1password/references/get-started.md +0 -17
  200. package/skills/apple-notes/SKILL.md +0 -78
  201. package/skills/apple-reminders/SKILL.md +0 -119
  202. package/skills/bear-notes/SKILL.md +0 -108
  203. package/skills/blogwatcher/SKILL.md +0 -70
  204. package/skills/blucli/SKILL.md +0 -48
  205. package/skills/bluebubbles/SKILL.md +0 -132
  206. package/skills/camsnap/SKILL.md +0 -46
  207. package/skills/canvas/SKILL.md +0 -204
  208. package/skills/connect-email/SKILL.md +0 -142
  209. package/skills/document-generation/SKILL.md +0 -83
  210. package/skills/eightctl/SKILL.md +0 -51
  211. package/skills/food-order/SKILL.md +0 -49
  212. package/skills/gemini/SKILL.md +0 -44
  213. package/skills/gh-issues/SKILL.md +0 -865
  214. package/skills/gifgrep/SKILL.md +0 -80
  215. package/skills/github/SKILL.md +0 -164
  216. package/skills/gog/SKILL.md +0 -117
  217. package/skills/goplaces/SKILL.md +0 -53
  218. package/skills/healthcheck/SKILL.md +0 -246
  219. package/skills/himalaya/SKILL.md +0 -258
  220. package/skills/himalaya/references/configuration.md +0 -184
  221. package/skills/himalaya/references/message-composition.md +0 -199
  222. package/skills/imsg/SKILL.md +0 -122
  223. package/skills/long-task/SKILL.md +0 -58
  224. package/skills/long-task/scripts/detach-task.sh +0 -187
  225. package/skills/nano-banana-pro/SKILL.md +0 -59
  226. package/skills/nano-banana-pro/scripts/generate_image.py +0 -184
  227. package/skills/nano-pdf/SKILL.md +0 -39
  228. package/skills/notion/SKILL.md +0 -173
  229. package/skills/obsidian/SKILL.md +0 -82
  230. package/skills/openai-image-gen/SKILL.md +0 -90
  231. package/skills/openai-image-gen/scripts/gen.py +0 -240
  232. package/skills/openai-whisper/SKILL.md +0 -39
  233. package/skills/openai-whisper-api/SKILL.md +0 -53
  234. package/skills/openai-whisper-api/scripts/transcribe.sh +0 -85
  235. package/skills/openhue/SKILL.md +0 -113
  236. package/skills/oracle/SKILL.md +0 -126
  237. package/skills/ordercli/SKILL.md +0 -79
  238. package/skills/peekaboo/SKILL.md +0 -191
  239. package/skills/reactions-extensive/SKILL.md +0 -30
  240. package/skills/reactions-minimal/SKILL.md +0 -31
  241. package/skills/safe-edit/SKILL.md +0 -51
  242. package/skills/sag/SKILL.md +0 -88
  243. package/skills/sherpa-onnx-tts/SKILL.md +0 -104
  244. package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +0 -178
  245. package/skills/songsee/SKILL.md +0 -50
  246. package/skills/sonoscli/SKILL.md +0 -66
  247. package/skills/spotify-player/SKILL.md +0 -65
  248. package/skills/symihub/SKILL.md +0 -78
  249. package/skills/things-mac/SKILL.md +0 -87
  250. package/skills/tmux/SKILL.md +0 -153
  251. package/skills/tmux/scripts/find-sessions.sh +0 -112
  252. package/skills/tmux/scripts/wait-for-text.sh +0 -83
  253. package/skills/trello/SKILL.md +0 -96
  254. package/skills/video-frames/SKILL.md +0 -47
  255. package/skills/video-frames/scripts/frame.sh +0 -81
  256. package/skills/voice-call/SKILL.md +0 -46
  257. package/skills/wacli/SKILL.md +0 -73
  258. package/skills/weather/SKILL.md +0 -113
  259. package/skills/xurl/SKILL.md +0 -462
@@ -1,711 +0,0 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
5
- import { createDatabaseManager } from "../db.js";
6
- import { createEdgeInference } from "../graph/edge-inference.js";
7
- import { createGraphRetrieval } from "../graph/graph-retrieval.js";
8
- import { createGraphStore } from "../graph/graph-store.js";
9
- import { createLearningStore } from "../learning/learning-store.js";
10
- import type { RetrievalResult } from "../learning/retrieval.js";
11
- import type { LearningLoopConfig, CompletedRun, QualityScore, LearningRecord } from "../types.js";
12
-
13
- const TEST_CONFIG: LearningLoopConfig = {
14
- capture: { embedPrompts: false, maxRuns: 1000 },
15
- scoring: {
16
- weights: {
17
- taskCompletion: 0.35,
18
- toolEfficiency: 0.25,
19
- responseAppropriateLength: 0.1,
20
- latencyRelative: 0.1,
21
- userFeedback: 0.2,
22
- },
23
- },
24
- injection: { maxLearnings: 5, minRelevance: 0.3, maxTokens: 500, cacheTtlMs: 60000 },
25
- decay: { halfLifeDays: 30 },
26
- };
27
-
28
- const logger = {
29
- info: () => {},
30
- warn: () => {},
31
- error: () => {},
32
- };
33
-
34
- function makeRun(id: string): CompletedRun {
35
- return {
36
- runId: id,
37
- sessionId: "sess-1",
38
- sessionKey: "sk-1",
39
- agentId: "agent-1",
40
- provider: "openai",
41
- model: "gpt-4",
42
- promptHash: "hash-" + id,
43
- promptLength: 100,
44
- responseLength: 200,
45
- responseToolCallCount: 0,
46
- usage: { input: 50, output: 100, total: 150 },
47
- toolCalls: [],
48
- success: true,
49
- error: null,
50
- durationMs: 1000,
51
- startedAt: Date.now() - 1000,
52
- completedAt: Date.now(),
53
- };
54
- }
55
-
56
- const testScore: QualityScore = {
57
- score: 0.85,
58
- signals: [],
59
- algorithmVersion: 1,
60
- };
61
-
62
- // Generate a simple normalized embedding of specified dimension
63
- function makeEmbedding(seed: number, dim: number = 8): number[] {
64
- const emb: number[] = [];
65
- for (let i = 0; i < dim; i++) {
66
- emb.push(Math.sin(seed + i * 0.7));
67
- }
68
- // Normalize
69
- const norm = Math.sqrt(emb.reduce((s, v) => s + v * v, 0));
70
- return emb.map((v) => v / norm);
71
- }
72
-
73
- /**
74
- * Create an embedding with a target cosine similarity to the base.
75
- * Uses deterministic rotation: result = cos(angle)*base + sin(angle)*orthogonal
76
- * where angle = arccos(targetSimilarity).
77
- */
78
- function makeEmbeddingWithSimilarity(base: number[], targetSimilarity: number): number[] {
79
- const angle = Math.acos(Math.min(1, Math.max(-1, targetSimilarity)));
80
- // Create an orthogonal vector by shifting and normalizing
81
- const shifted = base.map((_, i) => Math.sin(i * 2.3 + 1.7));
82
- // Gram-Schmidt: orth = shifted - (shifted·base)*base
83
- const dot = shifted.reduce((s, v, i) => s + v * base[i]!, 0);
84
- const orthRaw = shifted.map((v, i) => v - dot * base[i]!);
85
- const orthNorm = Math.sqrt(orthRaw.reduce((s, v) => s + v * v, 0));
86
- const orth = orthRaw.map((v) => v / orthNorm);
87
- // Combine
88
- const result = base.map((v, i) => Math.cos(angle) * v + Math.sin(angle) * orth[i]!);
89
- const resultNorm = Math.sqrt(result.reduce((s, v) => s + v * v, 0));
90
- return result.map((v) => v / resultNorm);
91
- }
92
-
93
- describe("Graph Store", () => {
94
- let tmpDir: string;
95
- let db: ReturnType<typeof createDatabaseManager>;
96
- let graphStore: ReturnType<typeof createGraphStore>;
97
- let learningStore: ReturnType<typeof createLearningStore>;
98
-
99
- beforeEach(() => {
100
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "graph-test-"));
101
- db = createDatabaseManager({ stateDir: tmpDir, config: TEST_CONFIG, logger });
102
- graphStore = createGraphStore({ db });
103
- learningStore = createLearningStore({ db, graphStore });
104
-
105
- // Insert runs for FK constraints
106
- db.insertRun(makeRun("run-1"), testScore);
107
- db.insertRun(makeRun("run-2"), testScore);
108
- db.insertRun(makeRun("run-3"), testScore);
109
- });
110
-
111
- afterEach(() => {
112
- db.close();
113
- fs.rmSync(tmpDir, { recursive: true, force: true });
114
- });
115
-
116
- // Test 1: Edge CRUD
117
- describe("Edge CRUD", () => {
118
- it("should add and retrieve edges", () => {
119
- graphStore.addEdge("lrn-a", "lrn-b", "S", 0.8);
120
- graphStore.addEdge("lrn-a", "lrn-c", "T", 0.5);
121
-
122
- const edges = graphStore.getEdges("lrn-a");
123
- expect(edges).toHaveLength(2);
124
- expect(edges.map((e) => e.edgeType).sort()).toEqual(["S", "T"]);
125
- });
126
-
127
- it("should upsert edge weight", () => {
128
- graphStore.addEdge("lrn-a", "lrn-b", "R", 0.3);
129
- graphStore.upsertEdge("lrn-a", "lrn-b", "R", 0.5);
130
-
131
- const edges = graphStore.getEdges("lrn-a", { typeFilter: ["R"] });
132
- expect(edges).toHaveLength(1);
133
- expect(edges[0]!.weight).toBeCloseTo(0.5);
134
- });
135
-
136
- it("should filter edges by type", () => {
137
- graphStore.addEdge("lrn-a", "lrn-b", "S", 0.8);
138
- graphStore.addEdge("lrn-a", "lrn-c", "T", 0.5);
139
- graphStore.addEdge("lrn-a", "lrn-d", "C", 0.6);
140
-
141
- const semantic = graphStore.getEdges("lrn-a", { typeFilter: ["S"] });
142
- expect(semantic).toHaveLength(1);
143
- expect(semantic[0]!.edgeType).toBe("S");
144
- });
145
-
146
- it("should remove edges for a learning", () => {
147
- graphStore.addEdge("lrn-a", "lrn-b", "S", 0.8);
148
- graphStore.addEdge("lrn-a", "lrn-c", "T", 0.5);
149
- graphStore.addEdge("lrn-d", "lrn-a", "C", 0.6);
150
-
151
- graphStore.removeEdgesFor("lrn-a");
152
-
153
- expect(graphStore.getEdges("lrn-a")).toHaveLength(0);
154
- // Edges that referenced lrn-a from other nodes are also gone
155
- expect(graphStore.getEdges("lrn-d")).toHaveLength(0);
156
- });
157
- });
158
-
159
- // Test 2: Centrality
160
- describe("Centrality", () => {
161
- it("should track incremental centrality on edge add", () => {
162
- graphStore.addEdge("lrn-a", "lrn-b", "S", 0.8);
163
-
164
- const centralityA = graphStore.getCentrality("lrn-a");
165
- const centralityB = graphStore.getCentrality("lrn-b");
166
-
167
- const expected = Math.log2(1 + 0.8);
168
- expect(centralityA).toBeCloseTo(expected);
169
- expect(centralityB).toBeCloseTo(expected);
170
- });
171
-
172
- it("should accumulate centrality across multiple edges", () => {
173
- graphStore.addEdge("lrn-a", "lrn-b", "S", 0.8);
174
- graphStore.addEdge("lrn-a", "lrn-c", "T", 0.5);
175
- graphStore.addEdge("lrn-a", "lrn-d", "C", 0.6);
176
-
177
- const centralityA = graphStore.getCentrality("lrn-a");
178
- const expected = Math.log2(1.8) + Math.log2(1.5) + Math.log2(1.6);
179
- expect(centralityA).toBeCloseTo(expected, 5);
180
- });
181
-
182
- it("should update centrality on edge upsert", () => {
183
- graphStore.addEdge("lrn-a", "lrn-b", "R", 0.3);
184
- const before = graphStore.getCentrality("lrn-a");
185
-
186
- graphStore.upsertEdge("lrn-a", "lrn-b", "R", 0.7);
187
- const after = graphStore.getCentrality("lrn-a");
188
-
189
- expect(after).toBeGreaterThan(before);
190
- expect(after).toBeCloseTo(Math.log2(1.7));
191
- });
192
-
193
- it("should recompute neighbors on removeEdgesFor", () => {
194
- graphStore.addEdge("lrn-a", "lrn-b", "S", 0.8);
195
- graphStore.addEdge("lrn-b", "lrn-c", "T", 0.5);
196
-
197
- // lrn-b has centrality from both edges
198
- const beforeB = graphStore.getCentrality("lrn-b");
199
- expect(beforeB).toBeCloseTo(Math.log2(1.8) + Math.log2(1.5));
200
-
201
- // Remove lrn-a edges
202
- graphStore.removeEdgesFor("lrn-a");
203
-
204
- // lrn-b should only have centrality from edge to lrn-c
205
- const afterB = graphStore.getCentrality("lrn-b");
206
- expect(afterB).toBeCloseTo(Math.log2(1.5));
207
- });
208
- });
209
-
210
- // Test 3: Neighbors / hop expansion
211
- describe("Neighbors", () => {
212
- it("should get 1-hop neighbors", () => {
213
- graphStore.addEdge("lrn-a", "lrn-b", "S", 0.8);
214
- graphStore.addEdge("lrn-a", "lrn-c", "T", 0.5);
215
-
216
- const neighbors = graphStore.getNeighbors("lrn-a", 1);
217
- expect(neighbors.size).toBe(2);
218
- expect(neighbors.has("lrn-b")).toBe(true);
219
- expect(neighbors.has("lrn-c")).toBe(true);
220
- });
221
-
222
- it("should get 2-hop neighbors", () => {
223
- graphStore.addEdge("lrn-a", "lrn-b", "S", 0.8);
224
- graphStore.addEdge("lrn-b", "lrn-c", "T", 0.5);
225
-
226
- const neighbors = graphStore.getNeighbors("lrn-a", 2);
227
- expect(neighbors.size).toBe(2);
228
- expect(neighbors.has("lrn-b")).toBe(true);
229
- expect(neighbors.has("lrn-c")).toBe(true);
230
- });
231
-
232
- it("should not include self in neighbors", () => {
233
- graphStore.addEdge("lrn-a", "lrn-b", "S", 0.8);
234
- const neighbors = graphStore.getNeighbors("lrn-a", 1);
235
- expect(neighbors.has("lrn-a")).toBe(false);
236
- });
237
-
238
- it("should filter by edge type", () => {
239
- graphStore.addEdge("lrn-a", "lrn-b", "S", 0.8);
240
- graphStore.addEdge("lrn-a", "lrn-c", "T", 0.5);
241
-
242
- const neighbors = graphStore.getNeighbors("lrn-a", 1, ["S"]);
243
- expect(neighbors.size).toBe(1);
244
- expect(neighbors.has("lrn-b")).toBe(true);
245
- });
246
- });
247
-
248
- // Test 4: Temporal inference
249
- describe("Edge Inference - Temporal", () => {
250
- it("should create T-edges for learnings from same run", () => {
251
- const edgeInference = createEdgeInference({ graphStore, learningStore });
252
-
253
- const id1 = learningStore.addLearning({
254
- runId: "run-1",
255
- category: "tool_pattern",
256
- content: "Pattern alpha",
257
- embedding: null,
258
- confidence: 0.8,
259
- })!;
260
-
261
- const id2 = learningStore.addLearning({
262
- runId: "run-1",
263
- category: "error_recovery",
264
- content: "Recovery beta",
265
- embedding: null,
266
- confidence: 0.7,
267
- })!;
268
-
269
- const learning2 = learningStore.getLearning(id2)!;
270
- edgeInference.onLearningCreated(learning2, "run-1");
271
-
272
- const edges = graphStore.getEdges(id2, { typeFilter: ["T"] });
273
- expect(edges.length).toBeGreaterThanOrEqual(1);
274
- // Should have T-edge connecting id1 and id2
275
- const hasTemporalEdge = edges.some(
276
- (e) =>
277
- (e.sourceId === id1 && e.targetId === id2) || (e.sourceId === id2 && e.targetId === id1),
278
- );
279
- expect(hasTemporalEdge).toBe(true);
280
- });
281
- });
282
-
283
- // Test 5: Semantic inference
284
- describe("Edge Inference - Semantic", () => {
285
- it("should create S-edges for similar embeddings", () => {
286
- const edgeInference = createEdgeInference({ graphStore, learningStore });
287
- const baseEmb = makeEmbedding(1);
288
-
289
- const id1 = learningStore.addLearning({
290
- runId: "run-1",
291
- category: "tool_pattern",
292
- content: "Use grep for code searching patterns",
293
- embedding: baseEmb,
294
- confidence: 0.8,
295
- })!;
296
-
297
- // Cosine ~0.75: above 0.6 threshold, below 0.92 dedup
298
- const similarEmb = makeEmbeddingWithSimilarity(baseEmb, 0.75);
299
- const id2 = learningStore.addLearning({
300
- runId: "run-2",
301
- category: "tool_pattern",
302
- content: "Grep is useful for finding code patterns",
303
- embedding: similarEmb,
304
- confidence: 0.7,
305
- })!;
306
-
307
- expect(id2).not.toBeNull();
308
- const learning2 = learningStore.getLearning(id2)!;
309
- edgeInference.onLearningCreated(learning2, "run-2");
310
-
311
- const edges = graphStore.getEdges(id2, { typeFilter: ["S"] });
312
- expect(edges.length).toBeGreaterThanOrEqual(1);
313
- });
314
-
315
- it("should not create S-edges for dissimilar embeddings", () => {
316
- const edgeInference = createEdgeInference({ graphStore, learningStore });
317
-
318
- const id1 = learningStore.addLearning({
319
- runId: "run-1",
320
- category: "tool_pattern",
321
- content: "Pattern about databases",
322
- embedding: makeEmbedding(1),
323
- confidence: 0.8,
324
- })!;
325
-
326
- // Very different embedding
327
- const id2 = learningStore.addLearning({
328
- runId: "run-2",
329
- category: "error_recovery",
330
- content: "Totally different topic about UI rendering",
331
- embedding: makeEmbedding(100),
332
- confidence: 0.7,
333
- })!;
334
-
335
- const learning2 = learningStore.getLearning(id2)!;
336
- edgeInference.onLearningCreated(learning2, "run-2");
337
-
338
- const edges = graphStore.getEdges(id2, { typeFilter: ["S"] });
339
- // Low cosine similarity should produce no S-edge
340
- expect(edges).toHaveLength(0);
341
- });
342
- });
343
-
344
- // Test 6: Supersession
345
- describe("Edge Inference - Supersession", () => {
346
- it("should create U-edge when new learning supersedes old one", () => {
347
- const edgeInference = createEdgeInference({ graphStore, learningStore });
348
- const baseEmb = makeEmbedding(42);
349
-
350
- const id1 = learningStore.addLearning({
351
- runId: "run-1",
352
- category: "tool_pattern",
353
- content: "Tool X works well with param A",
354
- embedding: baseEmb,
355
- confidence: 0.5,
356
- })!;
357
-
358
- // Same category, cosine ~0.85 (>= 0.8 for supersession, < 0.92 for dedup), higher confidence
359
- const id2 = learningStore.addLearning({
360
- runId: "run-2",
361
- category: "tool_pattern",
362
- content: "Tool X works even better with param A and B",
363
- embedding: makeEmbeddingWithSimilarity(baseEmb, 0.85),
364
- confidence: 0.9,
365
- })!;
366
-
367
- expect(id2).not.toBeNull();
368
- const learning2 = learningStore.getLearning(id2)!;
369
- edgeInference.onLearningCreated(learning2, "run-2");
370
-
371
- const edges = graphStore.getEdges(id1, { typeFilter: ["U"] });
372
- const hasSupersession = edges.some(
373
- (e) => e.sourceId === id1 && e.targetId === id2 && e.weight === 1.0,
374
- );
375
- expect(hasSupersession).toBe(true);
376
- });
377
- });
378
-
379
- // Test 7: Contradiction
380
- describe("Edge Inference - Contradiction", () => {
381
- it("should detect contradiction via negation density", () => {
382
- const edgeInference = createEdgeInference({ graphStore, learningStore });
383
-
384
- const id1 = learningStore.addLearning({
385
- runId: "run-1",
386
- category: "tool_pattern",
387
- content: "Tool grep works effectively for searching code",
388
- embedding: null,
389
- confidence: 0.8,
390
- })!;
391
-
392
- const id2 = learningStore.addLearning({
393
- runId: "run-2",
394
- category: "tool_pattern",
395
- content: "Tool grep does not work effectively for searching code",
396
- embedding: null,
397
- confidence: 0.7,
398
- })!;
399
-
400
- const learning2 = learningStore.getLearning(id2)!;
401
- edgeInference.onLearningCreated(learning2, "run-2");
402
-
403
- const contradictions = graphStore.getContradictions(id2);
404
- expect(contradictions.length).toBeGreaterThanOrEqual(1);
405
- });
406
-
407
- it("should detect contradiction via category opposition", () => {
408
- const edgeInference = createEdgeInference({ graphStore, learningStore });
409
- const baseEmb = makeEmbedding(5);
410
-
411
- const id1 = learningStore.addLearning({
412
- runId: "run-1",
413
- category: "tool_pattern",
414
- content: "Tool X is reliable and fast",
415
- embedding: baseEmb,
416
- confidence: 0.8,
417
- })!;
418
-
419
- // Cosine ~0.78: above 0.7 for contradiction detection, below 0.92 for dedup
420
- const id2 = learningStore.addLearning({
421
- runId: "run-2",
422
- category: "anti_pattern",
423
- content: "Tool X is unreliable and slow",
424
- embedding: makeEmbeddingWithSimilarity(baseEmb, 0.78),
425
- confidence: 0.7,
426
- })!;
427
-
428
- expect(id2).not.toBeNull();
429
- const learning2 = learningStore.getLearning(id2)!;
430
- edgeInference.onLearningCreated(learning2, "run-2");
431
-
432
- const contradictions = graphStore.getContradictions();
433
- expect(contradictions.length).toBeGreaterThanOrEqual(1);
434
- });
435
- });
436
-
437
- // Test 8: Reinforcement
438
- describe("Edge Inference - Reinforcement", () => {
439
- it("should create R-edges for co-applied learnings", () => {
440
- const edgeInference = createEdgeInference({ graphStore, learningStore });
441
-
442
- const id1 = learningStore.addLearning({
443
- runId: "run-1",
444
- category: "tool_pattern",
445
- content: "Learning A",
446
- embedding: null,
447
- confidence: 0.8,
448
- })!;
449
- const id2 = learningStore.addLearning({
450
- runId: "run-1",
451
- category: "error_recovery",
452
- content: "Learning B",
453
- embedding: null,
454
- confidence: 0.7,
455
- })!;
456
-
457
- edgeInference.onRunScored("run-2", [id1, id2], []);
458
-
459
- const [source, target] = id1 < id2 ? [id1, id2] : [id2, id1];
460
- const edges = graphStore.getEdges(source, { typeFilter: ["R"] });
461
- expect(edges).toHaveLength(1);
462
- expect(edges[0]!.weight).toBeCloseTo(0.3);
463
- });
464
-
465
- it("should increment R-edge weight on repeated co-application", () => {
466
- const edgeInference = createEdgeInference({ graphStore, learningStore });
467
-
468
- const id1 = learningStore.addLearning({
469
- runId: "run-1",
470
- category: "tool_pattern",
471
- content: "Learning C",
472
- embedding: null,
473
- confidence: 0.8,
474
- })!;
475
- const id2 = learningStore.addLearning({
476
- runId: "run-1",
477
- category: "error_recovery",
478
- content: "Learning D",
479
- embedding: null,
480
- confidence: 0.7,
481
- })!;
482
-
483
- edgeInference.onRunScored("run-2", [id1, id2], []);
484
- edgeInference.onRunScored("run-3", [id1, id2], []);
485
-
486
- const [source, target] = id1 < id2 ? [id1, id2] : [id2, id1];
487
- const edges = graphStore.getEdges(source, { typeFilter: ["R"] });
488
- expect(edges).toHaveLength(1);
489
- expect(edges[0]!.weight).toBeCloseTo(0.4); // 0.3 + 0.1
490
- });
491
-
492
- it("should create C-edges from injected to extracted learnings", () => {
493
- const edgeInference = createEdgeInference({ graphStore, learningStore });
494
-
495
- const injectedId = learningStore.addLearning({
496
- runId: "run-1",
497
- category: "tool_pattern",
498
- content: "Injected learning",
499
- embedding: null,
500
- confidence: 0.8,
501
- })!;
502
- const extractedId = learningStore.addLearning({
503
- runId: "run-2",
504
- category: "error_recovery",
505
- content: "Extracted learning",
506
- embedding: null,
507
- confidence: 0.7,
508
- })!;
509
-
510
- edgeInference.onRunScored("run-2", [injectedId], [extractedId]);
511
-
512
- const edges = graphStore.getEdges(injectedId, { typeFilter: ["C"] });
513
- const hasCausal = edges.some((e) => e.sourceId === injectedId && e.targetId === extractedId);
514
- expect(hasCausal).toBe(true);
515
- expect(edges.find((e) => e.targetId === extractedId)!.weight).toBeCloseTo(0.6);
516
- });
517
- });
518
-
519
- // Test 9: Hop expansion in retrieval
520
- describe("Graph Retrieval", () => {
521
- it("should expand seeds to include graph neighbors", () => {
522
- const graphRetrieval = createGraphRetrieval({
523
- graphStore,
524
- learningStore,
525
- config: TEST_CONFIG,
526
- });
527
-
528
- // Create learnings
529
- const id1 = learningStore.addLearning({
530
- runId: "run-1",
531
- category: "tool_pattern",
532
- content: "Seed learning about tool usage",
533
- embedding: null,
534
- confidence: 0.8,
535
- })!;
536
- const id2 = learningStore.addLearning({
537
- runId: "run-1",
538
- category: "error_recovery",
539
- content: "Neighbor learning about error handling",
540
- embedding: null,
541
- confidence: 0.7,
542
- })!;
543
-
544
- // Create S-edge between them
545
- graphStore.addEdge(id1, id2, "S", 0.8);
546
-
547
- const learning1 = learningStore.getLearning(id1)!;
548
- const seeds: RetrievalResult[] = [{ learning: learning1, score: 0.9, decayedScore: 0.85 }];
549
-
550
- const results = graphRetrieval.expandWithGraph(seeds, 5);
551
- expect(results.length).toBe(2);
552
-
553
- const neighborResult = results.find((r) => r.learning.id === id2);
554
- expect(neighborResult).toBeDefined();
555
- expect(neighborResult!.decayedScore).toBeLessThan(0.85); // Decayed by hop
556
- });
557
-
558
- it("should apply hop decay correctly", () => {
559
- const graphRetrieval = createGraphRetrieval({
560
- graphStore,
561
- learningStore,
562
- config: TEST_CONFIG,
563
- });
564
-
565
- const id1 = learningStore.addLearning({
566
- runId: "run-1",
567
- category: "tool_pattern",
568
- content: "Seed learning",
569
- embedding: null,
570
- confidence: 0.8,
571
- })!;
572
- const id2 = learningStore.addLearning({
573
- runId: "run-1",
574
- category: "error_recovery",
575
- content: "1-hop neighbor",
576
- embedding: null,
577
- confidence: 0.7,
578
- })!;
579
-
580
- graphStore.addEdge(id1, id2, "C", 0.9); // Causal edge, multiplier = 1.0
581
-
582
- const learning1 = learningStore.getLearning(id1)!;
583
- const seeds: RetrievalResult[] = [{ learning: learning1, score: 1.0, decayedScore: 1.0 }];
584
-
585
- const results = graphRetrieval.expandWithGraph(seeds, 5);
586
- const neighbor = results.find((r) => r.learning.id === id2)!;
587
-
588
- // Score = seedScore * hopDecay * typeMultiplier(C=1.0) * centralityBoost
589
- // centralityBoost = 1.0 + 0.1 * log2(1 + centrality(id2))
590
- const centrality = graphStore.getCentrality(id2);
591
- const expectedCentralityBoost = 1.0 + 0.1 * Math.log2(1 + centrality);
592
- const expectedScore = 1.0 * 0.6 * 1.0 * expectedCentralityBoost;
593
- expect(neighbor.decayedScore).toBeCloseTo(expectedScore, 2);
594
- });
595
-
596
- it("should annotate contested results with X-edges", () => {
597
- const graphRetrieval = createGraphRetrieval({
598
- graphStore,
599
- learningStore,
600
- config: TEST_CONFIG,
601
- });
602
-
603
- const id1 = learningStore.addLearning({
604
- runId: "run-1",
605
- category: "tool_pattern",
606
- content: "Contested seed learning",
607
- embedding: null,
608
- confidence: 0.8,
609
- })!;
610
- const id2 = learningStore.addLearning({
611
- runId: "run-2",
612
- category: "anti_pattern",
613
- content: "Contradicting learning",
614
- embedding: null,
615
- confidence: 0.7,
616
- })!;
617
-
618
- // Create X-edge (contradiction)
619
- graphStore.addEdge(id1, id2, "X", 0.8);
620
-
621
- const learning1 = learningStore.getLearning(id1)!;
622
- const seeds: RetrievalResult[] = [{ learning: learning1, score: 0.9, decayedScore: 0.85 }];
623
-
624
- const results = graphRetrieval.expandWithGraph(seeds, 5);
625
- const contestedResult = results.find((r) => r.learning.id === id1);
626
- expect(contestedResult).toBeDefined();
627
- expect(contestedResult!.contested).toBe(true);
628
-
629
- // Contradiction neighbor should NOT be expanded (X multiplier = 0.0)
630
- const contradictionNeighbor = results.find((r) => r.learning.id === id2);
631
- expect(contradictionNeighbor).toBeUndefined();
632
- });
633
- });
634
-
635
- // Test 10: Integration -- full cycle
636
- describe("Integration", () => {
637
- it("should handle full cycle: create learnings, infer edges, retrieve with expansion", () => {
638
- const edgeInference = createEdgeInference({ graphStore, learningStore });
639
- const graphRetrieval = createGraphRetrieval({
640
- graphStore,
641
- learningStore,
642
- config: TEST_CONFIG,
643
- });
644
-
645
- // Create learnings from same run
646
- const id1 = learningStore.addLearning({
647
- runId: "run-1",
648
- category: "tool_pattern",
649
- content: "Grep is efficient for finding patterns in code",
650
- embedding: null,
651
- confidence: 0.8,
652
- })!;
653
- const learning1 = learningStore.getLearning(id1)!;
654
- edgeInference.onLearningCreated(learning1, "run-1");
655
-
656
- const id2 = learningStore.addLearning({
657
- runId: "run-1",
658
- category: "error_recovery",
659
- content: "When grep fails use broader search terms",
660
- embedding: null,
661
- confidence: 0.7,
662
- })!;
663
- const learning2 = learningStore.getLearning(id2)!;
664
- edgeInference.onLearningCreated(learning2, "run-1");
665
-
666
- // Verify T-edges were created (same run)
667
- const edges = graphStore.getEdges(id1, { typeFilter: ["T"] });
668
- expect(edges.length).toBeGreaterThanOrEqual(1);
669
-
670
- // Simulate run scoring with both applied
671
- edgeInference.onRunScored("run-2", [id1, id2], []);
672
-
673
- // Verify R-edges were created
674
- const rEdges = graphStore.getEdges(id1, { typeFilter: ["R"] });
675
- expect(rEdges.length).toBeGreaterThanOrEqual(1);
676
-
677
- // Retrieve with graph expansion
678
- const seeds: RetrievalResult[] = [{ learning: learning1, score: 0.9, decayedScore: 0.85 }];
679
-
680
- const results = graphRetrieval.expandWithGraph(seeds, 5);
681
- expect(results.length).toBe(2); // Seed + neighbor
682
- expect(results.map((r) => r.learning.id).sort()).toEqual([id1, id2].sort());
683
- });
684
-
685
- it("should clean up graph edges when learning is removed", () => {
686
- const id1 = learningStore.addLearning({
687
- runId: "run-1",
688
- category: "tool_pattern",
689
- content: "Removable learning",
690
- embedding: null,
691
- confidence: 0.8,
692
- })!;
693
- const id2 = learningStore.addLearning({
694
- runId: "run-1",
695
- category: "error_recovery",
696
- content: "Connected learning",
697
- embedding: null,
698
- confidence: 0.7,
699
- })!;
700
-
701
- graphStore.addEdge(id1, id2, "S", 0.8);
702
- expect(graphStore.getEdges(id1)).toHaveLength(1);
703
-
704
- // Remove learning -- should also clean up edges
705
- learningStore.removeLearning(id1);
706
-
707
- expect(graphStore.getEdges(id1)).toHaveLength(0);
708
- expect(graphStore.getEdges(id2, { typeFilter: ["S"] })).toHaveLength(0);
709
- });
710
- });
711
- });