squish-memory 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (704) hide show
  1. package/.env.example +146 -0
  2. package/CHANGELOG.md +202 -0
  3. package/README.md +192 -287
  4. package/{scripts → bin}/dependency-manager.mjs +217 -217
  5. package/{scripts → bin}/detect-clients.mjs +78 -78
  6. package/bin/install-interactive.mjs +321 -0
  7. package/bin/squish-mcp.mjs +46 -0
  8. package/bin/squish.mjs +33 -0
  9. package/config/mcp-migration-map.json +1 -6
  10. package/config/mcp-mode-semantics.json +10 -12
  11. package/config/mcp-remote-auth.json +3 -26
  12. package/config/mcp-universal.schema.json +5 -35
  13. package/config/settings.json +78 -22
  14. package/config.js +5 -0
  15. package/config.ts +218 -0
  16. package/core/adapters/config/claude-code.ts +133 -0
  17. package/core/adapters/config/cursor.ts +90 -0
  18. package/core/adapters/config/opencode.ts +89 -0
  19. package/core/adapters/config/windsurf.ts +90 -0
  20. package/core/adapters/index.ts +102 -0
  21. package/core/adapters/timeline.ts +116 -0
  22. package/core/adapters/types.ts +166 -0
  23. package/core/agent-preferences.ts +140 -0
  24. package/core/algorithms/analytics/token-estimator.ts +216 -0
  25. package/core/algorithms/detection/hash-filters.ts +260 -0
  26. package/core/algorithms/detection/semantic-ranker.ts +194 -0
  27. package/core/algorithms/detection/two-stage-detector.ts +421 -0
  28. package/core/algorithms/handlers/approve-merge.ts +215 -0
  29. package/core/algorithms/handlers/detect-duplicates.ts +192 -0
  30. package/core/algorithms/handlers/get-stats.ts +132 -0
  31. package/core/algorithms/handlers/list-proposals.ts +130 -0
  32. package/core/algorithms/handlers/preview-merge.ts +139 -0
  33. package/core/algorithms/handlers/reject-merge.ts +93 -0
  34. package/core/algorithms/handlers/reverse-merge.ts +155 -0
  35. package/core/algorithms/index.ts +39 -0
  36. package/core/algorithms/operations/cache-maintenance.ts +182 -0
  37. package/core/algorithms/safety/safety-checks.ts +256 -0
  38. package/core/algorithms/strategies/merge-strategies.ts +381 -0
  39. package/core/algorithms/types.ts +140 -0
  40. package/core/algorithms/utils/response-builder.ts +61 -0
  41. package/core/associations.ts +363 -0
  42. package/core/beliefs/decay.ts +289 -0
  43. package/core/beliefs/extractor.ts +131 -0
  44. package/core/beliefs/store.ts +557 -0
  45. package/core/beliefs/types.ts +38 -0
  46. package/core/commands/mcp-server.ts +5 -0
  47. package/core/compression.ts +177 -0
  48. package/core/config.js +2 -0
  49. package/core/consolidation.ts +330 -0
  50. package/core/context/agent-context.ts +388 -0
  51. package/core/context/context-paging.ts +449 -0
  52. package/core/context/context-window.ts +234 -0
  53. package/core/context/context.ts +35 -0
  54. package/core/embeddings/embeddings.ts +616 -0
  55. package/core/embeddings/google-multimodal.ts +200 -0
  56. package/core/embeddings/local-embeddings.ts +12 -0
  57. package/core/embeddings/qmd-client.ts +495 -0
  58. package/core/embeddings/transformers-local.ts +261 -0
  59. package/core/embeddings.js +4 -0
  60. package/core/error-handling.ts +206 -0
  61. package/core/external +219 -0
  62. package/core/graph/entity-deduplicator.ts +232 -0
  63. package/core/graph/graph-builder.ts +257 -0
  64. package/core/graph/graph-traversal.ts +490 -0
  65. package/core/graph/index.ts +24 -0
  66. package/core/graph/llm-entity-extractor.ts +402 -0
  67. package/core/graph/multi-hop-retrieval.ts +317 -0
  68. package/core/graph/relationship-extractor.ts +465 -0
  69. package/core/hooks/agent-hooks.ts +653 -0
  70. package/core/hooks/auto-tagger.ts +149 -0
  71. package/core/hooks/capture-filter.ts +169 -0
  72. package/core/hot-cache.ts +388 -0
  73. package/core/index.ts +10 -0
  74. package/core/ingestion/agent-memory.ts +167 -0
  75. package/core/ingestion/core-memory.ts +326 -0
  76. package/core/ingestion/learnings.ts +260 -0
  77. package/core/ingestion/signal-engine.ts +266 -0
  78. package/core/integrations/obsidian-vault.ts +197 -0
  79. package/core/layers/generator.ts +115 -0
  80. package/core/lib/db-client.ts +168 -0
  81. package/core/lib/parse-embedding.ts +59 -0
  82. package/core/lib/schemas.ts +102 -0
  83. package/core/lib/types.ts +49 -0
  84. package/core/lib/utils.ts +151 -0
  85. package/core/lib/validation.ts +180 -0
  86. package/core/lifecycle.ts +353 -0
  87. package/core/logger.ts +59 -0
  88. package/core/memory/bridge-discovery.ts +395 -0
  89. package/core/memory/categorizer.ts +390 -0
  90. package/core/memory/conflict-detector.ts +62 -0
  91. package/core/memory/consolidation.ts +372 -0
  92. package/core/memory/context-collector.ts +75 -0
  93. package/core/memory/contradiction-resolver.ts +494 -0
  94. package/core/memory/edit-workflow.ts +174 -0
  95. package/core/memory/entity-extractor.ts +426 -0
  96. package/core/memory/entity-resolver.ts +89 -0
  97. package/core/memory/explain.ts +112 -0
  98. package/core/memory/fact-deriver.ts +300 -0
  99. package/core/memory/fact-extractor.ts +120 -0
  100. package/core/memory/feedback-tracker.ts +200 -0
  101. package/core/memory/hooks.ts +230 -0
  102. package/core/memory/hybrid-retrieval.ts +65 -0
  103. package/core/memory/hybrid-scorer.ts +325 -0
  104. package/core/memory/hybrid-search.ts +748 -0
  105. package/core/memory/importance.ts +319 -0
  106. package/core/memory/index.ts +11 -0
  107. package/core/memory/loader.ts +178 -0
  108. package/core/memory/markdown/markdown-storage.ts +318 -0
  109. package/core/memory/memories.ts +565 -0
  110. package/core/memory/memory-lifecycle.ts +51 -0
  111. package/core/memory/memory-manager.ts +53 -0
  112. package/core/memory/migrate.ts +173 -0
  113. package/core/memory/normalization.ts +30 -0
  114. package/core/memory/path-strengthener.ts +211 -0
  115. package/core/memory/progressive-disclosure.ts +392 -0
  116. package/core/memory/query-processor.ts +130 -0
  117. package/core/memory/query-rewriter.ts +153 -0
  118. package/core/memory/response-analyzer.ts +81 -0
  119. package/core/memory/retrieval-feedback.ts +276 -0
  120. package/core/memory/serialization.ts +83 -0
  121. package/core/memory/stale-cleaner.ts +147 -0
  122. package/core/memory/stats.ts +181 -0
  123. package/core/memory/telemetry.ts +392 -0
  124. package/core/memory/temporal-facts.ts +356 -0
  125. package/core/memory/temporal-parser.ts +477 -0
  126. package/core/memory/trigger-detector.ts +104 -0
  127. package/core/memory/write-gate.ts +288 -0
  128. package/core/places/index.ts +14 -0
  129. package/core/places/memory-places.ts +339 -0
  130. package/core/places/places.ts +406 -0
  131. package/core/places/rules.ts +308 -0
  132. package/core/places/walking.ts +192 -0
  133. package/core/projects +89 -0
  134. package/core/projects.ts +131 -0
  135. package/core/redis.ts +82 -0
  136. package/core/responses.ts +187 -0
  137. package/core/runtime/trust-report.ts +195 -0
  138. package/core/runtime/trust-state.ts +360 -0
  139. package/core/scheduler/cron-scheduler.ts +581 -0
  140. package/core/scheduler/heartbeat.ts +91 -0
  141. package/core/scheduler/index.ts +8 -0
  142. package/core/scheduler/job-runner.ts +197 -0
  143. package/core/search/conversations.ts +166 -0
  144. package/core/search/entities.ts +46 -0
  145. package/core/search/folder-context.ts +154 -0
  146. package/core/search/graph-boost.ts +22 -0
  147. package/core/search/index.ts +4 -0
  148. package/core/search/qmd-wrapper.ts +84 -0
  149. package/core/security/encrypt.ts +51 -0
  150. package/core/security/governance.ts +102 -0
  151. package/core/security/privacy.ts +108 -0
  152. package/core/security/secret-detector.ts +122 -0
  153. package/core/session/auto-load.ts +160 -0
  154. package/core/session/entity-tracker.ts +363 -0
  155. package/core/session/index.ts +7 -0
  156. package/core/session/reference-resolver.ts +158 -0
  157. package/core/session/self-iteration-job.ts +478 -0
  158. package/core/session/session-hooks.ts +69 -0
  159. package/core/session/types.ts +36 -0
  160. package/core/session/working-set.ts +275 -0
  161. package/core/snapshots/cleanup.ts +13 -0
  162. package/core/snapshots/comparison.ts +59 -0
  163. package/core/snapshots/creation.ts +139 -0
  164. package/core/snapshots/retrieval.ts +44 -0
  165. package/core/snapshots/stats.ts +63 -0
  166. package/core/storage/cache.ts +241 -0
  167. package/core/storage/database.ts +23 -0
  168. package/core/summarization/cleanup.ts +13 -0
  169. package/core/summarization/queries.ts +32 -0
  170. package/core/summarization/stats.ts +64 -0
  171. package/core/summarization/strategies.ts +52 -0
  172. package/core/summarization.ts +248 -0
  173. package/core/temporal-facts.ts +244 -0
  174. package/core/tracing/collector.ts +470 -0
  175. package/core/tracing/visualizer.ts +195 -0
  176. package/core/utils/cleanup-operations.ts +50 -0
  177. package/core/utils/content-extraction.ts +95 -0
  178. package/core/utils/filter-builder.ts +56 -0
  179. package/core/utils/history-traversal.ts +63 -0
  180. package/core/utils/memory-operations.ts +56 -0
  181. package/core/utils/query-operations.ts +83 -0
  182. package/core/utils/summarization-helpers.ts +45 -0
  183. package/core/utils/temporal-queries.ts +39 -0
  184. package/core/utils/vector-operations.ts +135 -0
  185. package/core/utils/version-management.ts +74 -0
  186. package/core/worker.ts +324 -0
  187. package/db/adapter.ts +215 -0
  188. package/db/bootstrap.ts +1055 -0
  189. package/db/drizzle/migrations/0000_needy_cerebro.sql +402 -0
  190. package/db/drizzle/migrations/meta/0000_snapshot.json +3451 -0
  191. package/db/drizzle/migrations/meta/_journal.json +13 -0
  192. package/db/drizzle/schema-sqlite.ts +1032 -0
  193. package/db/drizzle/schema.ts +1128 -0
  194. package/db/drizzle.config.ts +12 -0
  195. package/db/index.ts +83 -0
  196. package/db/init.sql +5 -0
  197. package/db/migrations/associations.ts +35 -0
  198. package/db/migrations/beliefs.ts +89 -0
  199. package/db/migrations/core-memory.ts +35 -0
  200. package/db/migrations/fts.ts +59 -0
  201. package/db/migrations/index.ts +54 -0
  202. package/db/migrations/indexes.ts +36 -0
  203. package/db/migrations/learnings.ts +34 -0
  204. package/db/migrations/maintenance.ts +68 -0
  205. package/db/migrations/memories.ts +22 -0
  206. package/db/migrations/memory-places.ts +35 -0
  207. package/db/migrations/places.ts +49 -0
  208. package/db/migrations/projects.ts +21 -0
  209. package/db/migrations/tier-conversion.ts +24 -0
  210. package/db/neon.ts +22 -0
  211. package/db/schema/beliefs.ts +50 -0
  212. package/db/schema/generator.ts +159 -0
  213. package/db/schema/index.ts +58 -0
  214. package/db/schema/learnings.ts +32 -0
  215. package/db/schema/memories.ts +83 -0
  216. package/db/schema/projects.ts +33 -0
  217. package/db/schema.ts +13 -0
  218. package/db/supabase.ts +27 -0
  219. package/dist/config.d.ts +61 -14
  220. package/dist/config.js +159 -139
  221. package/dist/core/adapters/config/claude-code.d.ts +45 -0
  222. package/dist/core/adapters/config/claude-code.js +113 -0
  223. package/dist/core/adapters/config/cursor.d.ts +26 -0
  224. package/dist/core/adapters/config/cursor.js +74 -0
  225. package/dist/core/adapters/config/opencode.d.ts +23 -0
  226. package/dist/core/adapters/config/opencode.js +73 -0
  227. package/dist/core/adapters/config/windsurf.d.ts +26 -0
  228. package/dist/core/adapters/config/windsurf.js +74 -0
  229. package/dist/core/adapters/index.d.ts +45 -0
  230. package/dist/core/adapters/index.js +84 -0
  231. package/dist/core/adapters/scripts/install-adapter.d.ts +19 -0
  232. package/dist/core/adapters/scripts/install-adapter.js +149 -0
  233. package/dist/core/adapters/timeline.d.ts +23 -0
  234. package/dist/core/adapters/timeline.js +88 -0
  235. package/dist/core/adapters/types.d.ts +137 -0
  236. package/dist/core/adapters/types.js +50 -0
  237. package/dist/core/agent-preferences.d.ts +16 -0
  238. package/dist/core/agent-preferences.js +124 -0
  239. package/dist/{algorithms → core/algorithms}/analytics/token-estimator.d.ts +1 -1
  240. package/dist/{algorithms → core/algorithms}/analytics/token-estimator.js +3 -3
  241. package/dist/{algorithms → core/algorithms}/detection/semantic-ranker.d.ts +1 -1
  242. package/dist/{algorithms → core/algorithms}/detection/semantic-ranker.js +1 -1
  243. package/dist/{algorithms → core/algorithms}/detection/two-stage-detector.d.ts +1 -1
  244. package/dist/{algorithms → core/algorithms}/detection/two-stage-detector.js +7 -10
  245. package/dist/{algorithms → core/algorithms}/handlers/approve-merge.js +4 -4
  246. package/dist/{algorithms → core/algorithms}/handlers/detect-duplicates.js +3 -3
  247. package/dist/{algorithms → core/algorithms}/handlers/get-stats.js +3 -3
  248. package/dist/{algorithms → core/algorithms}/handlers/list-proposals.js +3 -3
  249. package/dist/{algorithms → core/algorithms}/handlers/preview-merge.js +3 -3
  250. package/dist/{algorithms → core/algorithms}/handlers/reject-merge.js +3 -3
  251. package/dist/{algorithms → core/algorithms}/handlers/reverse-merge.js +3 -3
  252. package/dist/core/algorithms/index.d.ts +21 -0
  253. package/dist/core/algorithms/index.js +26 -0
  254. package/dist/core/algorithms/operations/cache-maintenance.d.ts +12 -0
  255. package/dist/core/algorithms/operations/cache-maintenance.js +157 -0
  256. package/dist/{algorithms → core/algorithms}/safety/safety-checks.d.ts +2 -6
  257. package/dist/{algorithms → core/algorithms}/strategies/merge-strategies.d.ts +19 -1
  258. package/dist/{algorithms → core/algorithms}/strategies/merge-strategies.js +74 -123
  259. package/dist/core/algorithms/types.d.ts +125 -0
  260. package/dist/core/algorithms/types.js +5 -0
  261. package/dist/core/associations.d.ts +3 -2
  262. package/dist/core/associations.js +37 -2
  263. package/dist/core/autosave.d.ts +19 -0
  264. package/dist/core/autosave.js +16 -0
  265. package/dist/core/beliefs/decay.d.ts +27 -0
  266. package/dist/core/beliefs/decay.js +217 -0
  267. package/dist/core/beliefs/extractor.d.ts +9 -0
  268. package/dist/core/beliefs/extractor.js +113 -0
  269. package/dist/core/beliefs/store.d.ts +46 -0
  270. package/dist/core/beliefs/store.js +466 -0
  271. package/dist/core/beliefs/types.d.ts +28 -0
  272. package/dist/core/beliefs/types.js +2 -0
  273. package/dist/core/commands/mcp-server.d.ts +2 -0
  274. package/dist/core/commands/mcp-server.js +6 -0
  275. package/dist/core/commands/remember.d.ts +24 -0
  276. package/dist/core/commands/remember.js +144 -0
  277. package/dist/core/compression.d.ts +45 -0
  278. package/dist/core/compression.js +160 -0
  279. package/dist/core/context/agent-context.d.ts +106 -0
  280. package/dist/core/context/agent-context.js +274 -0
  281. package/dist/core/{context-paging.d.ts → context/context-paging.d.ts} +2 -12
  282. package/dist/core/{context-paging.js → context/context-paging.js} +19 -39
  283. package/dist/core/context/context-window.d.ts +40 -0
  284. package/dist/core/context/context-window.js +177 -0
  285. package/dist/core/context/context.js +22 -0
  286. package/dist/core/embeddings/embeddings.d.ts +29 -0
  287. package/dist/core/embeddings/embeddings.js +546 -0
  288. package/dist/core/embeddings/google-multimodal.js +6 -2
  289. package/dist/core/embeddings/local-embeddings.d.ts +11 -0
  290. package/dist/core/embeddings/local-embeddings.js +11 -0
  291. package/dist/core/embeddings/qmd-client.js +1 -1
  292. package/dist/core/embeddings/transformers-local.d.ts +64 -0
  293. package/dist/core/embeddings/transformers-local.js +213 -0
  294. package/dist/core/embeddings.d.ts +1 -28
  295. package/dist/core/embeddings.js +2 -401
  296. package/dist/core/error-handling.d.ts +63 -0
  297. package/dist/core/error-handling.js +173 -0
  298. package/dist/core/graph/entity-deduplicator.d.ts +24 -0
  299. package/dist/core/graph/entity-deduplicator.js +183 -0
  300. package/dist/core/graph/graph-builder.d.ts +46 -0
  301. package/dist/core/graph/graph-builder.js +174 -0
  302. package/dist/core/graph/graph-traversal.d.ts +80 -0
  303. package/dist/core/graph/graph-traversal.js +315 -0
  304. package/dist/core/graph/index.d.ts +19 -0
  305. package/dist/core/graph/index.js +13 -0
  306. package/dist/core/graph/llm-entity-extractor.d.ts +49 -0
  307. package/dist/core/graph/llm-entity-extractor.js +313 -0
  308. package/dist/core/graph/multi-hop-retrieval.d.ts +48 -0
  309. package/dist/core/graph/multi-hop-retrieval.js +215 -0
  310. package/dist/core/graph/relationship-extractor.d.ts +48 -0
  311. package/dist/core/graph/relationship-extractor.js +351 -0
  312. package/dist/core/hooks/agent-hooks.d.ts +83 -0
  313. package/dist/core/hooks/agent-hooks.js +521 -0
  314. package/dist/core/hooks/auto-tagger.d.ts +19 -0
  315. package/dist/core/hooks/auto-tagger.js +155 -0
  316. package/dist/core/hooks/capture-filter.d.ts +41 -0
  317. package/dist/core/hooks/capture-filter.js +128 -0
  318. package/dist/core/hot-cache.d.ts +86 -0
  319. package/dist/core/hot-cache.js +285 -0
  320. package/dist/core/index.d.ts +9 -9
  321. package/dist/core/index.js +9 -12
  322. package/dist/core/{agent-memory.js → ingestion/agent-memory.js} +5 -7
  323. package/dist/core/{core-memory.d.ts → ingestion/core-memory.d.ts} +2 -2
  324. package/dist/core/{core-memory.js → ingestion/core-memory.js} +7 -7
  325. package/dist/core/ingestion/learnings.d.ts +57 -0
  326. package/dist/core/ingestion/learnings.js +205 -0
  327. package/dist/core/ingestion/signal-engine.d.ts +41 -0
  328. package/dist/core/ingestion/signal-engine.js +201 -0
  329. package/dist/core/integrations/obsidian-vault.d.ts +31 -0
  330. package/dist/core/integrations/obsidian-vault.js +156 -0
  331. package/dist/core/lib/db-client.d.ts +114 -0
  332. package/dist/core/lib/db-client.js +130 -0
  333. package/dist/core/lib/parse-embedding.d.ts +9 -0
  334. package/dist/core/lib/parse-embedding.js +58 -0
  335. package/dist/core/lib/schemas.d.ts +132 -0
  336. package/dist/core/lib/schemas.js +87 -0
  337. package/dist/core/lib/types.d.ts +45 -0
  338. package/dist/core/lib/types.js +6 -0
  339. package/dist/core/{utils.d.ts → lib/utils.d.ts} +5 -0
  340. package/dist/core/lib/utils.js +145 -0
  341. package/dist/core/lib/validation.d.ts +38 -0
  342. package/dist/core/lib/validation.js +151 -0
  343. package/dist/core/lifecycle.d.ts +7 -1
  344. package/dist/core/lifecycle.js +152 -42
  345. package/dist/core/logger.d.ts +1 -0
  346. package/dist/core/logger.js +13 -1
  347. package/dist/core/mcp/tools.d.ts +0 -2
  348. package/dist/core/mcp/tools.js +35 -90
  349. package/dist/core/mcp/types.d.ts +25 -253
  350. package/dist/core/mcp/types.js +2 -2
  351. package/dist/core/memory/categorizer.js +2 -0
  352. package/dist/core/memory/conflict-detector.js +1 -1
  353. package/dist/core/memory/consolidation.d.ts +1 -10
  354. package/dist/core/memory/consolidation.js +4 -39
  355. package/dist/core/memory/context-collector.js +1 -1
  356. package/dist/core/memory/edit-workflow.js +1 -1
  357. package/dist/core/memory/entity-extractor.d.ts +4 -0
  358. package/dist/core/memory/entity-extractor.js +30 -16
  359. package/dist/core/memory/entity-resolver.js +7 -7
  360. package/dist/core/memory/explain.d.ts +18 -0
  361. package/dist/core/memory/explain.js +92 -0
  362. package/dist/core/memory/fact-deriver.d.ts +31 -0
  363. package/dist/core/memory/fact-deriver.js +236 -0
  364. package/dist/core/memory/fact-extractor.js +12 -12
  365. package/dist/core/memory/feedback-tracker.js +1 -1
  366. package/dist/core/memory/hooks.d.ts +88 -0
  367. package/dist/core/memory/hooks.js +174 -0
  368. package/dist/core/memory/hybrid-retrieval.d.ts +14 -16
  369. package/dist/core/memory/hybrid-retrieval.js +25 -127
  370. package/dist/core/memory/hybrid-scorer.js +6 -23
  371. package/dist/core/memory/hybrid-search.d.ts +9 -11
  372. package/dist/core/memory/hybrid-search.js +496 -273
  373. package/dist/core/memory/importance.d.ts +2 -24
  374. package/dist/core/memory/importance.js +7 -91
  375. package/dist/core/memory/index.d.ts +1 -0
  376. package/dist/core/memory/index.js +1 -0
  377. package/dist/core/memory/loader.d.ts +31 -0
  378. package/dist/core/memory/loader.js +141 -0
  379. package/dist/core/memory/markdown/markdown-storage.d.ts +72 -0
  380. package/dist/core/memory/markdown/markdown-storage.js +243 -0
  381. package/dist/core/memory/memories.d.ts +23 -19
  382. package/dist/core/memory/memories.js +243 -228
  383. package/dist/core/memory/memory-lifecycle.d.ts +8 -0
  384. package/dist/core/memory/memory-lifecycle.js +47 -0
  385. package/dist/core/memory/migrate.d.ts +21 -0
  386. package/dist/core/memory/migrate.js +134 -0
  387. package/dist/core/memory/normalization.d.ts +7 -0
  388. package/dist/core/memory/normalization.js +26 -0
  389. package/dist/core/memory/path-strengthener.d.ts +39 -0
  390. package/dist/core/memory/path-strengthener.js +150 -0
  391. package/dist/core/memory/progressive-disclosure.js +1 -1
  392. package/dist/core/memory/query-processor.js +37 -3
  393. package/dist/core/memory/query-rewriter.js +9 -9
  394. package/dist/core/memory/retrieval-feedback.d.ts +70 -0
  395. package/dist/core/memory/retrieval-feedback.js +213 -0
  396. package/dist/core/memory/serialization.d.ts +4 -0
  397. package/dist/core/memory/serialization.js +49 -0
  398. package/dist/core/memory/stale-cleaner.d.ts +26 -0
  399. package/dist/core/memory/stale-cleaner.js +97 -0
  400. package/dist/core/memory/stats.d.ts +15 -0
  401. package/dist/core/memory/stats.js +69 -13
  402. package/dist/core/memory/temporal-facts.js +21 -0
  403. package/dist/core/memory/trigger-detector.d.ts +8 -1
  404. package/dist/core/memory/trigger-detector.js +42 -5
  405. package/dist/core/memory/write-gate.js +1 -1
  406. package/dist/core/places/index.d.ts +14 -0
  407. package/dist/core/places/index.js +14 -0
  408. package/dist/core/places/memory-places.d.ts +68 -0
  409. package/dist/core/places/memory-places.js +261 -0
  410. package/dist/core/places/places.d.ts +88 -0
  411. package/dist/core/places/places.js +314 -0
  412. package/dist/core/places/rules.d.ts +74 -0
  413. package/dist/core/places/rules.js +240 -0
  414. package/dist/core/places/walking.d.ts +56 -0
  415. package/dist/core/places/walking.js +121 -0
  416. package/dist/core/projects.d.ts +5 -0
  417. package/dist/core/projects.js +47 -18
  418. package/dist/core/responses.d.ts +96 -0
  419. package/dist/core/responses.js +122 -0
  420. package/dist/core/runtime/trust-report.d.ts +102 -0
  421. package/dist/core/runtime/trust-report.js +107 -0
  422. package/dist/core/runtime/trust-state.d.ts +12 -0
  423. package/dist/core/runtime/trust-state.js +309 -0
  424. package/dist/core/scheduler/cron-scheduler.d.ts +1 -1
  425. package/dist/core/scheduler/cron-scheduler.js +193 -10
  426. package/dist/core/scheduler/index.d.ts +1 -1
  427. package/dist/core/scheduler/index.js +1 -1
  428. package/dist/core/scheduler/job-runner.js +2 -2
  429. package/dist/core/search/conversations.js +40 -42
  430. package/dist/core/search/entities.js +6 -9
  431. package/dist/core/search/graph-boost.d.ts +7 -0
  432. package/dist/core/search/graph-boost.js +23 -0
  433. package/dist/core/search/qmd-wrapper.d.ts +36 -0
  434. package/dist/core/search/qmd-wrapper.js +58 -0
  435. package/dist/core/security/encrypt.d.ts +6 -0
  436. package/dist/core/security/encrypt.js +47 -0
  437. package/dist/core/{governance.d.ts → security/governance.d.ts} +6 -1
  438. package/dist/core/security/governance.js +79 -0
  439. package/dist/core/session/auto-load.js +34 -9
  440. package/dist/core/session/entity-tracker.d.ts +62 -0
  441. package/dist/core/session/entity-tracker.js +287 -0
  442. package/dist/core/session/index.d.ts +1 -1
  443. package/dist/core/session/index.js +1 -1
  444. package/dist/core/session/reference-resolver.d.ts +26 -0
  445. package/dist/core/session/reference-resolver.js +121 -0
  446. package/dist/core/{session-hooks → session}/self-iteration-job.d.ts +15 -0
  447. package/dist/core/{session-hooks → session}/self-iteration-job.js +195 -90
  448. package/dist/core/session/working-set.d.ts +50 -0
  449. package/dist/core/session/working-set.js +212 -0
  450. package/dist/core/snapshots/creation.d.ts +2 -8
  451. package/dist/core/snapshots/creation.js +3 -12
  452. package/dist/core/{cache.js → storage/cache.js} +2 -2
  453. package/dist/core/utils/memory-operations.js +1 -1
  454. package/dist/core/utils/summarization-helpers.d.ts +0 -4
  455. package/dist/core/utils/summarization-helpers.js +1 -6
  456. package/dist/core/utils/vector-operations.d.ts +71 -0
  457. package/dist/core/utils/vector-operations.js +129 -0
  458. package/dist/db/adapter.d.ts +3 -3
  459. package/dist/db/adapter.js +99 -88
  460. package/dist/db/bootstrap.d.ts +2 -0
  461. package/dist/db/bootstrap.js +921 -674
  462. package/dist/{drizzle → db/drizzle}/schema-sqlite.d.ts +775 -25
  463. package/dist/{drizzle → db/drizzle}/schema-sqlite.js +170 -24
  464. package/dist/{drizzle → db/drizzle}/schema.d.ts +731 -32
  465. package/dist/{drizzle → db/drizzle}/schema.js +192 -32
  466. package/dist/db/drizzle.config.d.ts +3 -0
  467. package/dist/db/drizzle.config.js +12 -0
  468. package/dist/db/index.d.ts +1 -5
  469. package/dist/db/index.js +51 -8
  470. package/dist/db/migrations/associations.d.ts +6 -0
  471. package/dist/db/migrations/associations.js +29 -0
  472. package/dist/db/migrations/beliefs.d.ts +10 -0
  473. package/dist/db/migrations/beliefs.js +76 -0
  474. package/dist/db/migrations/core-memory.d.ts +6 -0
  475. package/dist/db/migrations/core-memory.js +29 -0
  476. package/dist/db/migrations/fts.d.ts +6 -0
  477. package/dist/db/migrations/fts.js +52 -0
  478. package/dist/db/migrations/index.d.ts +25 -0
  479. package/dist/db/migrations/index.js +51 -0
  480. package/dist/db/migrations/indexes.d.ts +6 -0
  481. package/dist/db/migrations/indexes.js +30 -0
  482. package/dist/db/migrations/learnings.d.ts +7 -0
  483. package/dist/db/migrations/learnings.js +26 -0
  484. package/dist/db/migrations/maintenance.d.ts +6 -0
  485. package/dist/db/migrations/maintenance.js +61 -0
  486. package/dist/db/migrations/memories.d.ts +7 -0
  487. package/dist/db/migrations/memories.js +16 -0
  488. package/dist/db/migrations/memory-places.d.ts +6 -0
  489. package/dist/db/migrations/memory-places.js +29 -0
  490. package/dist/db/migrations/places.d.ts +6 -0
  491. package/dist/db/migrations/places.js +43 -0
  492. package/dist/db/migrations/projects.d.ts +3 -0
  493. package/dist/db/migrations/projects.js +13 -0
  494. package/dist/db/migrations/tier-conversion.d.ts +7 -0
  495. package/dist/db/migrations/tier-conversion.js +20 -0
  496. package/dist/db/neon.d.ts +8 -0
  497. package/dist/db/neon.js +20 -0
  498. package/dist/db/schema/beliefs.d.ts +9 -0
  499. package/dist/db/schema/beliefs.js +46 -0
  500. package/dist/db/schema/generator.d.ts +38 -0
  501. package/dist/db/schema/generator.js +108 -0
  502. package/dist/db/schema/index.d.ts +39 -0
  503. package/dist/db/schema/index.js +51 -0
  504. package/dist/db/schema/learnings.d.ts +7 -0
  505. package/dist/db/schema/learnings.js +30 -0
  506. package/dist/db/schema/memories.d.ts +7 -0
  507. package/dist/db/schema/memories.js +81 -0
  508. package/dist/db/schema/projects.d.ts +4 -0
  509. package/dist/db/schema/projects.js +31 -0
  510. package/dist/db/schema/tables/context-sessions.d.ts +9 -0
  511. package/dist/db/schema/tables/context-sessions.js +37 -0
  512. package/dist/db/schema/tables/conversations.d.ts +9 -0
  513. package/dist/db/schema/tables/conversations.js +47 -0
  514. package/dist/db/schema/tables/core-memory.d.ts +9 -0
  515. package/dist/db/schema/tables/core-memory.js +41 -0
  516. package/dist/db/schema/tables/entities.d.ts +9 -0
  517. package/dist/db/schema/tables/entities.js +39 -0
  518. package/dist/db/schema/tables/entity-relations.d.ts +9 -0
  519. package/dist/db/schema/tables/entity-relations.js +31 -0
  520. package/dist/db/schema/tables/learnings.d.ts +9 -0
  521. package/dist/db/schema/tables/learnings.js +66 -0
  522. package/dist/db/schema/tables/memories.d.ts +9 -0
  523. package/dist/db/schema/tables/memories.js +161 -0
  524. package/dist/db/schema/tables/memory-associations.d.ts +9 -0
  525. package/dist/db/schema/tables/memory-associations.js +39 -0
  526. package/dist/db/schema/tables/memory-hash-cache.d.ts +9 -0
  527. package/dist/db/schema/tables/memory-hash-cache.js +29 -0
  528. package/dist/db/schema/tables/memory-merge-history.d.ts +9 -0
  529. package/dist/db/schema/tables/memory-merge-history.js +33 -0
  530. package/dist/db/schema/tables/memory-merge-proposals.d.ts +9 -0
  531. package/dist/db/schema/tables/memory-merge-proposals.js +39 -0
  532. package/dist/db/schema/tables/messages.d.ts +9 -0
  533. package/dist/db/schema/tables/messages.js +41 -0
  534. package/dist/db/schema/tables/namespaces.d.ts +9 -0
  535. package/dist/db/schema/tables/namespaces.js +37 -0
  536. package/dist/db/schema/tables/projects.d.ts +9 -0
  537. package/dist/db/schema/tables/projects.js +31 -0
  538. package/dist/db/schema/tables/users.d.ts +9 -0
  539. package/dist/db/schema/tables/users.js +27 -0
  540. package/dist/db/schema.d.ts +1 -1
  541. package/dist/db/schema.js +2 -2
  542. package/dist/db/supabase.d.ts +9 -0
  543. package/dist/db/supabase.js +24 -0
  544. package/dist/packages/mcp/src/index.d.ts +3 -0
  545. package/dist/packages/mcp/src/index.js +733 -0
  546. package/mcp.json.example +8 -0
  547. package/package.json +132 -173
  548. package/packages/cli/package.json +22 -0
  549. package/packages/cli/src/commands/clean.ts +68 -0
  550. package/packages/cli/src/commands/context.ts +79 -0
  551. package/packages/cli/src/commands/doctor.ts +357 -0
  552. package/packages/cli/src/commands/forget.ts +72 -0
  553. package/packages/cli/src/commands/health.ts +36 -0
  554. package/packages/cli/src/commands/inspect.ts +41 -0
  555. package/packages/cli/src/commands/link.ts +50 -0
  556. package/packages/cli/src/commands/migrate.ts +93 -0
  557. package/packages/cli/src/commands/recall.ts +99 -0
  558. package/packages/cli/src/commands/recent.ts +57 -0
  559. package/packages/cli/src/commands/remember.ts +139 -0
  560. package/packages/cli/src/commands/run.ts +58 -0
  561. package/packages/cli/src/commands/stale.ts +43 -0
  562. package/packages/cli/src/commands/stats.ts +42 -0
  563. package/packages/cli/src/index.ts +57 -0
  564. package/packages/cli/tsconfig.json +24 -0
  565. package/packages/mcp/package.json +26 -0
  566. package/packages/mcp/src/index.ts +877 -0
  567. package/packages/mcp/tsconfig.json +20 -0
  568. package/skills/squish-memory/SKILL.md +107 -114
  569. package/skills/squish-memory/install.sh +3 -3
  570. package/skills/squish-memory/{claude-desktop.json → references/claude-desktop.json} +12 -12
  571. package/skills/squish-memory/{openclaw.json → references/openclaw.json} +13 -13
  572. package/skills/squish-memory/{opencode.json → references/opencode.json} +14 -14
  573. package/.claude-plugin/marketplace.json +0 -20
  574. package/.claude-plugin/plugin.json +0 -32
  575. package/.env.mcp.example +0 -60
  576. package/.mcp.json +0 -11
  577. package/QUICK-START.md +0 -71
  578. package/bin/squish-add.mjs +0 -32
  579. package/bin/squish-rm.mjs +0 -21
  580. package/commands/context-paging.md +0 -51
  581. package/commands/context-status.md +0 -22
  582. package/commands/context.md +0 -5
  583. package/commands/core-memory.md +0 -56
  584. package/commands/health.md +0 -5
  585. package/commands/init.md +0 -39
  586. package/commands/merge.md +0 -113
  587. package/commands/observe.md +0 -5
  588. package/commands/recall.md +0 -5
  589. package/commands/remember.md +0 -11
  590. package/commands/search.md +0 -10
  591. package/config/mcp-cli-fallback-policy.json +0 -22
  592. package/config/mcp.json +0 -38
  593. package/config/plugin-manifest.json +0 -152
  594. package/config/plugin-manifest.schema.json +0 -244
  595. package/config/remote-memory-policy.json +0 -32
  596. package/dist/api/web/index.d.ts +0 -3
  597. package/dist/api/web/index.js +0 -4
  598. package/dist/api/web/web-server.d.ts +0 -3
  599. package/dist/api/web/web-server.js +0 -6
  600. package/dist/api/web/web.d.ts +0 -4
  601. package/dist/api/web/web.js +0 -639
  602. package/dist/commands/managed-sync.d.ts +0 -10
  603. package/dist/commands/managed-sync.js +0 -64
  604. package/dist/commands/mcp-server.d.ts +0 -3
  605. package/dist/commands/mcp-server.js +0 -393
  606. package/dist/core/context.js +0 -24
  607. package/dist/core/governance.js +0 -64
  608. package/dist/core/local-embeddings.d.ts +0 -6
  609. package/dist/core/local-embeddings.js +0 -20
  610. package/dist/core/namespaces/index.d.ts +0 -71
  611. package/dist/core/namespaces/index.js +0 -305
  612. package/dist/core/namespaces/uri-parser.d.ts +0 -31
  613. package/dist/core/namespaces/uri-parser.js +0 -74
  614. package/dist/core/observations.d.ts +0 -26
  615. package/dist/core/observations.js +0 -110
  616. package/dist/core/requirements.d.ts +0 -20
  617. package/dist/core/requirements.js +0 -35
  618. package/dist/core/search/qmd-search.d.ts +0 -61
  619. package/dist/core/search/qmd-search.js +0 -178
  620. package/dist/core/snapshots.d.ts +0 -29
  621. package/dist/core/snapshots.js +0 -220
  622. package/dist/core/sync/qmd-sync.d.ts +0 -106
  623. package/dist/core/sync/qmd-sync.js +0 -213
  624. package/dist/core/utils.js +0 -74
  625. package/dist/index.d.ts +0 -19
  626. package/dist/index.js +0 -997
  627. package/generated/mcp/manifest.json +0 -23
  628. package/generated/mcp/mcp-servers.json +0 -25
  629. package/generated/mcp/mcporter.json +0 -34
  630. package/generated/mcp/openclaw-memory-qmd.json +0 -17
  631. package/generated/mcp/runtime.json +0 -12
  632. package/hooks/hooks.json +0 -52
  633. package/hooks/post-tool-use.js +0 -26
  634. package/hooks/session-end.js +0 -28
  635. package/hooks/session-start.js +0 -33
  636. package/hooks/user-prompt-submit.js +0 -26
  637. package/hooks/utils.js +0 -153
  638. package/npx-installer.js +0 -208
  639. package/packages/plugin-claude-code/README.md +0 -73
  640. package/packages/plugin-claude-code/dist/plugin-wrapper.d.ts +0 -35
  641. package/packages/plugin-claude-code/dist/plugin-wrapper.js +0 -191
  642. package/packages/plugin-claude-code/package.json +0 -31
  643. package/packages/plugin-openclaw/README.md +0 -70
  644. package/packages/plugin-openclaw/dist/index.d.ts +0 -49
  645. package/packages/plugin-openclaw/dist/index.js +0 -262
  646. package/packages/plugin-openclaw/openclaw.plugin.json +0 -94
  647. package/packages/plugin-openclaw/package.json +0 -31
  648. package/packages/plugin-opencode/install.mjs +0 -217
  649. package/packages/plugin-opencode/package.json +0 -21
  650. package/plugin.json +0 -32
  651. package/scripts/build-release.sh +0 -36
  652. package/scripts/check-secrets.js +0 -132
  653. package/scripts/db/check-db.mjs +0 -88
  654. package/scripts/db/fix-all-columns.mjs +0 -52
  655. package/scripts/db/fix-schema-all.mjs +0 -55
  656. package/scripts/db/fix-schema-full.mjs +0 -46
  657. package/scripts/db/fix-schema.mjs +0 -38
  658. package/scripts/db/init-db.mjs +0 -13
  659. package/scripts/db/recreate-db.mjs +0 -14
  660. package/scripts/generate-mcp.mjs +0 -264
  661. package/scripts/github-release.sh +0 -77
  662. package/scripts/init-dirs.mjs +0 -13
  663. package/scripts/install-interactive.mjs +0 -677
  664. package/scripts/install-mcp.mjs +0 -116
  665. package/scripts/install-plugin.mjs +0 -415
  666. package/scripts/install-web.sh +0 -120
  667. package/scripts/install.mjs +0 -340
  668. package/scripts/openclaw-bootstrap.mjs +0 -127
  669. package/scripts/package-release.sh +0 -71
  670. package/scripts/remote-preflight.mjs +0 -62
  671. package/scripts/squish-fallback.mjs +0 -132
  672. package/scripts/test/test-all-systems.mjs +0 -139
  673. package/scripts/test/test-memory-system.mjs +0 -139
  674. package/scripts/test/test-v0.5.0.mjs +0 -210
  675. package/scripts/test-interactive.mjs +0 -131
  676. package/scripts/verify-mcp.mjs +0 -214
  677. package/skills/memory-guide/SKILL.md +0 -332
  678. package/skills/squish-cli/SKILL.md +0 -240
  679. package/skills/squish-mcp/SKILL.md +0 -355
  680. package/skills/squish-memory/install.mjs +0 -335
  681. package/skills/squish-memory/skill.json +0 -32
  682. /package/dist/{algorithms → core/algorithms}/detection/hash-filters.d.ts +0 -0
  683. /package/dist/{algorithms → core/algorithms}/detection/hash-filters.js +0 -0
  684. /package/dist/{algorithms → core/algorithms}/handlers/approve-merge.d.ts +0 -0
  685. /package/dist/{algorithms → core/algorithms}/handlers/detect-duplicates.d.ts +0 -0
  686. /package/dist/{algorithms → core/algorithms}/handlers/get-stats.d.ts +0 -0
  687. /package/dist/{algorithms → core/algorithms}/handlers/list-proposals.d.ts +0 -0
  688. /package/dist/{algorithms → core/algorithms}/handlers/preview-merge.d.ts +0 -0
  689. /package/dist/{algorithms → core/algorithms}/handlers/reject-merge.d.ts +0 -0
  690. /package/dist/{algorithms → core/algorithms}/handlers/reverse-merge.d.ts +0 -0
  691. /package/dist/{algorithms → core/algorithms}/safety/safety-checks.js +0 -0
  692. /package/dist/{algorithms → core/algorithms}/utils/response-builder.d.ts +0 -0
  693. /package/dist/{algorithms → core/algorithms}/utils/response-builder.js +0 -0
  694. /package/dist/core/{context.d.ts → context/context.d.ts} +0 -0
  695. /package/dist/core/{agent-memory.d.ts → ingestion/agent-memory.d.ts} +0 -0
  696. /package/dist/core/{privacy.d.ts → security/privacy.d.ts} +0 -0
  697. /package/dist/core/{privacy.js → security/privacy.js} +0 -0
  698. /package/dist/core/{secret-detector.d.ts → security/secret-detector.d.ts} +0 -0
  699. /package/dist/core/{secret-detector.js → security/secret-detector.js} +0 -0
  700. /package/dist/core/{session-hooks → session}/session-hooks.d.ts +0 -0
  701. /package/dist/core/{session-hooks → session}/session-hooks.js +0 -0
  702. /package/dist/core/{cache.d.ts → storage/cache.d.ts} +0 -0
  703. /package/dist/core/{database.d.ts → storage/database.d.ts} +0 -0
  704. /package/dist/core/{database.js → storage/database.js} +0 -0
@@ -1,116 +1,472 @@
1
1
  /**
2
- * Hybrid Search - Combines BM25 keyword search with vector semantic search
3
- * Uses Reciprocal Rank Fusion (RRF) for intelligent result merging
2
+ * Vector Search - Pure semantic search with optional graph boosting + multi-session support
4
3
  *
5
- * Based on research showing 40-60% improvement over pure vector search:
6
- * - BM25 excels at exact keyword matches
7
- * - Vector search captures semantic similarity
8
- * - RRF merges both without score calibration issues
4
+ * Uses cosine similarity on embeddings + optional graph boost
5
+ * BM25 removed - use qmd-client for hot tier (BM25 + vectors + reranking)
9
6
  */
10
7
  import { getDb } from '../../db/index.js';
11
- import { createDatabaseClient } from '../../core/database.js';
8
+ import { createDatabaseClient } from '../storage/database.js';
12
9
  import { getEmbedding } from '../../core/embeddings.js';
13
- import { getProjectByPath } from '../../core/projects.js';
14
- import { fromSqliteTags, normalizeTags } from './serialization.js';
10
+ import { requireProject } from '../../core/projects.js';
11
+ import { deserializeTags, deserializeMetadata, normalizeTags } from './serialization.js';
12
+ import { computeGraphBoost } from '../search/graph-boost.js';
13
+ import { normalizeTimestamp } from '../lib/utils.js';
14
+ import { parseEmbedding } from '../lib/parse-embedding.js';
15
+ import { cosineSimilarity } from '../utils/vector-operations.js';
16
+ import config from '../../config.js';
17
+ import { getRelatedMemories } from '../associations.js';
18
+ import { getPlaceMemories } from '../places/memory-places.js';
19
+ import { getPlaceByType } from '../places/places.js';
20
+ import { multiHopSearch } from '../graph/multi-hop-retrieval.js';
21
+ import { logger } from '../logger.js';
15
22
  /**
16
- * Reciprocal Rank Fusion (RRF) constant
17
- * Higher values = more influence from lower-ranked items
18
- * 60 is the standard value used in research
23
+ * Detect if query asks about time (temporal queries)
19
24
  */
20
- const RRF_K = 60;
25
+ function isTemporalQuery(query) {
26
+ const temporalIndicators = [
27
+ 'when', 'how long', 'how many', 'ago', 'since', 'until',
28
+ 'before', 'after', 'earlier', 'later', 'yesterday', 'tomorrow',
29
+ 'last week', 'next week', 'last month', 'next month'
30
+ ];
31
+ const lower = query.toLowerCase();
32
+ return temporalIndicators.some(w => lower.includes(w));
33
+ }
34
+ /**
35
+ * Detect if query spans multiple sessions (multi-hop queries)
36
+ */
37
+ function isMultiSessionQuery(query) {
38
+ const multiSessionIndicators = [
39
+ 'when', 'then', 'before', 'after', 'earlier', 'later',
40
+ 'previous', 'next', 'first', 'later', 'subsequently',
41
+ 'across', 'between', 'both', 'another', 'different'
42
+ ];
43
+ const lower = query.toLowerCase();
44
+ return multiSessionIndicators.some(w => lower.includes(w));
45
+ }
21
46
  /**
22
- * Main hybrid search function - combines BM25 and vector search with RRF
47
+ * Check if content contains date/time references
48
+ */
49
+ function hasDateReference(content) {
50
+ const datePatterns = [
51
+ /\b\d{4}\b/, // Years: 2023, 2022
52
+ /\b\d{1,2}\s+(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\b/i, // Month dates
53
+ /\b(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\s+\d{1,2}\b/i,
54
+ /\b\d{1,2}\/\d{1,2}\/\d{2,4}\b/, // Dates: 5/7/2023
55
+ /\b(yesterday|today|tomorrow|last week|last month|last year)\b/i,
56
+ /\b(\d+)\s+(day|week|month|year)s?\s+(ago|before)\b/i,
57
+ ];
58
+ const lower = content.toLowerCase();
59
+ return datePatterns.some(p => p.test(content) || p.test(lower));
60
+ }
61
+ /**
62
+ * Expand query for multi-session retrieval
63
+ */
64
+ function expandQueryForMultiSession(query) {
65
+ const expansions = [query];
66
+ const lower = query.toLowerCase();
67
+ // Add time-based expansions
68
+ if (lower.includes('when')) {
69
+ expansions.push(query.replace(/when/i, '').trim());
70
+ }
71
+ if (lower.includes('before') || lower.includes('after')) {
72
+ expansions.push(query.replace(/before|after/i, '').trim());
73
+ }
74
+ if (lower.includes('earlier') || lower.includes('later')) {
75
+ expansions.push(query.replace(/earlier|later/i, '').trim());
76
+ }
77
+ // Add "mentioning" expansions for factual queries
78
+ if (lower.includes('what') || lower.includes('how')) {
79
+ expansions.push(query + ' mentioned');
80
+ expansions.push(query + ' said');
81
+ }
82
+ return [...new Set(expansions.filter(e => e.length > 2))];
83
+ }
84
+ /**
85
+ * Expand query for temporal retrieval - add date/temporal context
86
+ */
87
+ function expandQueryForTemporal(query) {
88
+ const expansions = [query];
89
+ const lower = query.toLowerCase();
90
+ // For "when" questions, search with entity + date context
91
+ if (lower.includes('when')) {
92
+ // Extract entity name (everything after "when did" or "when is")
93
+ const entityMatch = query.match(/when\s+(?:did|is|was|were)\s+(\w+)/i);
94
+ if (entityMatch) {
95
+ const entity = entityMatch[1];
96
+ // Search with entity name alone (might match date mentions)
97
+ expansions.push(entity);
98
+ expansions.push(query.replace(/when\s+(?:did|is|was|were)\s+/i, '').trim());
99
+ }
100
+ }
101
+ // Add "date" and "time" expansions
102
+ if (lower.includes('when') || lower.includes('how long') || lower.includes('ago')) {
103
+ expansions.push(query + ' date');
104
+ expansions.push(query + ' time');
105
+ }
106
+ return [...new Set(expansions.filter(e => e.length > 2))];
107
+ }
108
+ /**
109
+ * Score with recency + similarity + entity boost (NO LLM required)
110
+ */
111
+ function scoreWithHeuristics(result, query, now) {
112
+ let score = result.similarity ?? 0;
113
+ // 1. Recency boost: Recent = higher (up to +0.1)
114
+ if (result.createdAt) {
115
+ const created = new Date(result.createdAt).getTime();
116
+ const ageHours = (now - created) / (1000 * 60 * 60);
117
+ const recencyScore = Math.max(0, 0.1 * Math.exp(-ageHours / 720)); // Decay over 30 days
118
+ score += recencyScore;
119
+ }
120
+ // 2. Entity overlap: Query words appearing in content = boost
121
+ const queryWords = new Set(query.toLowerCase().split(/\s+/).filter(w => w.length > 3));
122
+ const contentWords = new Set((result.content ?? "").toLowerCase().split(/\s+/));
123
+ const overlap = [...queryWords].filter(w => contentWords.has(w)).length;
124
+ score += overlap * 0.02; // Small boost per matching word
125
+ return score;
126
+ }
127
+ /**
128
+ * Main search function - vectors + graph boost + heuristics + places + sessions
129
+ * Unified search integrating Places, Graph, and Memory
23
130
  */
24
131
  export async function hybridSearch(input, options = {}) {
25
132
  const limit = options.limit ?? input.limit ?? 10;
26
- const bm25Weight = options.bm25Weight ?? 0.5;
27
- const vectorWeight = options.vectorWeight ?? 0.5;
28
- // Run both searches in parallel
29
- const [bm25Results, vectorResults] = await Promise.all([
30
- bm25Search(input, { ...options, limit: limit * 2 }),
31
- vectorSearch(input, { ...options, limit: limit * 2 }),
32
- ]);
33
- // Apply RRF to merge results
34
- return reciprocalRankFusion(bm25Results, vectorResults, { limit, bm25Weight, vectorWeight });
133
+ const enableMultiSession = options.enableMultiSession !== false;
134
+ const enableHeuristics = options.enableHeuristics !== false;
135
+ const isMultiHop = enableMultiSession && isMultiSessionQuery(input.query);
136
+ const isTemporal = isTemporalQuery(input.query);
137
+ let vectorResults;
138
+ if (isMultiHop) {
139
+ // Multi-hop: use expansion to get more coverage
140
+ const expandedQueries = expandQueryForMultiSession(input.query);
141
+ const allResults = [];
142
+ for (const expQuery of expandedQueries) {
143
+ const expResults = await vectorSearch({ ...input, query: expQuery }, { ...options, limit: Math.ceil(limit * 2) });
144
+ allResults.push(...expResults);
145
+ }
146
+ const byId = new Map();
147
+ for (const r of allResults) {
148
+ const existing = byId.get(r.id);
149
+ if (!existing || (r.similarity ?? 0) > (existing.similarity ?? 0)) {
150
+ byId.set(r.id, r);
151
+ }
152
+ }
153
+ vectorResults = Array.from(byId.values());
154
+ }
155
+ else if (isTemporal) {
156
+ // Temporal: fetch more results
157
+ vectorResults = await vectorSearch(input, { ...options, limit: limit * 4 });
158
+ }
159
+ else {
160
+ // Regular query
161
+ vectorResults = await vectorSearch(input, { ...options, limit: limit * 2 });
162
+ }
163
+ // Task 2: Integrate Places into retrieval
164
+ // If placeId or placeType specified, filter/boost results from that place
165
+ if (input.placeId || input.placeType) {
166
+ vectorResults = await applyPlaceFilterAndBoost(vectorResults, input, limit);
167
+ }
168
+ // Task 3: Add session temporal scope
169
+ // If sessionId specified, boost memories from same session
170
+ if (input.sessionId) {
171
+ vectorResults = applySessionBoost(vectorResults, input.sessionId);
172
+ }
173
+ // TEMPORAL: Boost results with dates for temporal queries
174
+ if (isTemporal) {
175
+ vectorResults = applyTemporalBoost(vectorResults);
176
+ }
177
+ // Apply heuristics if enabled (recency + entity overlap)
178
+ if (enableHeuristics) {
179
+ const now = Date.now();
180
+ vectorResults = vectorResults.map(r => ({
181
+ ...r,
182
+ similarity: scoreWithHeuristics(r, input.query, now)
183
+ }));
184
+ }
185
+ // Graph boost
186
+ const graphWeight = config.scoringWeights.graphBoost;
187
+ const candidateIds = vectorResults.map(r => r.id);
188
+ const graphBoostMap = await computeGraphBoost(candidateIds);
189
+ let results = applyGraphBoostWithWeight(vectorResults, graphBoostMap, limit, graphWeight);
190
+ // Expand with associated memories for better coverage
191
+ if (options.includeAssociations !== false) {
192
+ results = await expandWithAssociations(results, limit);
193
+ }
194
+ // Task 4: Enable multi-hop graph traversal by default
195
+ // If query is detected as multi-hop, use actual graph traversal
196
+ if (isMultiHop && options.enableGraphTraversal !== false && input.project) {
197
+ try {
198
+ const graphResults = await multiHopSearch({
199
+ query: input.query,
200
+ project: input.project,
201
+ limit: limit,
202
+ includeVectorResults: false,
203
+ includeGraphResults: true,
204
+ });
205
+ // Merge graph results with vector results
206
+ const existingIds = new Set(results.map(r => r.id));
207
+ for (const gr of graphResults) {
208
+ if (!existingIds.has(gr.id)) {
209
+ results.push({
210
+ ...gr,
211
+ similarity: (gr.similarity ?? 0) * 0.9 // Slightly lower weight
212
+ });
213
+ }
214
+ }
215
+ // Re-sort
216
+ results.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
217
+ }
218
+ catch (e) {
219
+ // Multi-hop failed, continue with vector results
220
+ logger.debug(`[HybridSearch] Multi-hop failed: ${e}`);
221
+ }
222
+ }
223
+ return results;
35
224
  }
36
225
  /**
37
- * BM25 keyword search using SQLite FTS5
226
+ * Task 2: Filter and boost results by place
38
227
  */
39
- async function bm25Search(input, options) {
40
- try {
41
- const db = createDatabaseClient(await getDb());
42
- const sqlite = db.$client;
43
- const limit = options.limit ?? 10;
44
- const tags = normalizeTags(options.tags ?? input.tags);
45
- // Build FTS5 query with proper escaping
46
- const ftsQuery = buildFtsQuery(input.query);
47
- const isEmptyQuery = ftsQuery === '*';
48
- // Build WHERE conditions
49
- const conditions = [];
50
- const params = [];
51
- // For non-empty queries, use FTS5 MATCH
52
- if (!isEmptyQuery) {
53
- conditions.push('memories_fts MATCH ?');
54
- params.push(ftsQuery);
228
+ async function applyPlaceFilterAndBoost(results, input, limit) {
229
+ let placeIds = [];
230
+ // Get place IDs from placeId or placeType
231
+ if (input.placeType && input.project) {
232
+ const project = await requireProject(input.project);
233
+ const place = await getPlaceByType(project.id, input.placeType);
234
+ if (place) {
235
+ placeIds = [place.id];
55
236
  }
56
- if (input.type) {
57
- conditions.push('m.type = ?');
58
- params.push(input.type);
237
+ }
238
+ else if (input.placeId) {
239
+ placeIds = [input.placeId];
240
+ }
241
+ if (placeIds.length === 0) {
242
+ return results;
243
+ }
244
+ // Get memories in this place
245
+ const placeMemories = new Set();
246
+ for (const pid of placeIds) {
247
+ const memIds = await getPlaceMemories(pid, limit * 3);
248
+ for (const mid of memIds) {
249
+ placeMemories.add(mid);
59
250
  }
60
- if (tags.length) {
61
- conditions.push('(' + tags.map(() => 'm.tags LIKE ?').join(' OR ') + ')');
62
- params.push(...tags.map((tag) => `%${tag}%`));
251
+ }
252
+ // Boost memories from the specified place
253
+ const PLACE_BOOST = 0.15;
254
+ const boosted = results.map(r => {
255
+ if (placeMemories.has(r.id)) {
256
+ return {
257
+ ...r,
258
+ similarity: (r.similarity ?? 0) + PLACE_BOOST
259
+ };
63
260
  }
64
- let projectId = null;
65
- if (input.project) {
66
- const project = await getProjectByPath(input.project);
67
- if (project) {
68
- projectId = project.id;
69
- conditions.push('m.project_id = ?');
70
- params.push(project.id);
261
+ return r;
262
+ });
263
+ // Re-sort with place boost
264
+ boosted.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
265
+ return boosted;
266
+ }
267
+ /**
268
+ * Task 3: Boost memories from the same session (temporal)
269
+ */
270
+ function applySessionBoost(results, sessionId) {
271
+ const SESSION_BOOST = 0.1;
272
+ const boosted = results.map(r => {
273
+ // Check if memory's session matches query's session
274
+ const memSession = r.metadata?.sessionMetadata?.sessionId;
275
+ if (memSession === sessionId) {
276
+ return {
277
+ ...r,
278
+ similarity: (r.similarity ?? 0) + SESSION_BOOST
279
+ };
280
+ }
281
+ return r;
282
+ });
283
+ // Re-sort with session boost
284
+ boosted.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
285
+ return boosted;
286
+ }
287
+ /**
288
+ * TEMPORAL FIX: Boost memories that contain date references for "when" questions
289
+ * Also boost by date RECENCY - closer to today = higher for temporal queries
290
+ */
291
+ function applyTemporalBoost(results) {
292
+ const TEMPORAL_BOOST = 0.25; // Moderate boost for date-containing memories
293
+ const boosted = results.map(r => {
294
+ let boost = 0;
295
+ // Boost 1: Has date reference - high priority for temporal
296
+ if (hasDateReference(r.content ?? "")) {
297
+ boost += TEMPORAL_BOOST;
298
+ }
299
+ return {
300
+ ...r,
301
+ similarity: (r.similarity ?? 0) + boost
302
+ };
303
+ });
304
+ // Re-sort with temporal boost
305
+ boosted.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
306
+ return boosted;
307
+ }
308
+ /**
309
+ * MULTI-HOP FIX: Get memories from adjacent sessions using DB query
310
+ * This provides actual cross-session retrieval for multi-hop queries
311
+ */
312
+ async function applySessionAdjacencyBoost(results, input, limit) {
313
+ if (!input.project)
314
+ return results;
315
+ // Extract session tags from top results
316
+ const sessionTags = new Set();
317
+ for (const r of results.slice(0, 5)) {
318
+ const sessionTag = extractSessionTag(r.tags);
319
+ if (sessionTag) {
320
+ sessionTags.add(sessionTag);
321
+ }
322
+ }
323
+ if (sessionTags.size === 0) {
324
+ return results;
325
+ }
326
+ // Get adjacent session tags
327
+ const adjacentSessionTags = new Set();
328
+ for (const tag of sessionTags) {
329
+ const adjacent = getAdjacentSessionTags(tag);
330
+ for (const adj of adjacent) {
331
+ adjacentSessionTags.add(adj);
332
+ }
333
+ }
334
+ if (adjacentSessionTags.size === 0) {
335
+ return results;
336
+ }
337
+ // Fetch memories from adjacent sessions
338
+ const project = await requireProject(input.project);
339
+ const adjacentMemories = [];
340
+ for (const sessionTag of adjacentSessionTags) {
341
+ const memories = await getMemoriesBySession(project.id, sessionTag, limit);
342
+ adjacentMemories.push(...memories);
343
+ }
344
+ if (adjacentMemories.length === 0) {
345
+ return results;
346
+ }
347
+ // Merge with boost
348
+ const existingIds = new Set(results.map(r => r.id));
349
+ const ADJACENT_BOOST = 0.3;
350
+ const merged = [...results];
351
+ for (const mem of adjacentMemories) {
352
+ if (!existingIds.has(mem.id)) {
353
+ merged.push({
354
+ ...mem,
355
+ similarity: (mem.similarity ?? 0) + ADJACENT_BOOST
356
+ });
357
+ }
358
+ }
359
+ // Re-sort
360
+ merged.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
361
+ return merged.slice(0, limit * 2);
362
+ }
363
+ /**
364
+ * Expand results with directly associated memories
365
+ */
366
+ async function expandWithAssociations(results, limit) {
367
+ const allIds = new Set(results.map(r => r.id));
368
+ const expanded = [...results];
369
+ // Get related for top results only
370
+ for (const r of results.slice(0, 3)) {
371
+ try {
372
+ const related = await getRelatedMemories(r.id, 5);
373
+ for (const rel of related) {
374
+ if (!allIds.has(rel.id)) {
375
+ allIds.add(rel.id);
376
+ expanded.push({
377
+ ...rel,
378
+ similarity: (rel.similarity ?? 0) * 0.8 // Slightly lower weight
379
+ });
380
+ }
71
381
  }
72
382
  }
73
- // For empty query with no filters, use "1=1" to match all
74
- const whereClause = conditions.length > 0 ? conditions.join(' AND ') : '1=1';
75
- // Build query based on whether we have a search term or not
76
- const statement = sqlite.prepare(`
77
- SELECT
78
- m.id as id,
79
- m.project_id as projectId,
80
- m.type as type,
81
- m.content as content,
82
- m.summary as summary,
83
- m.tags as tags,
84
- m.metadata as metadata,
85
- ${isEmptyQuery ? '0 as bm25Score' : 'bm25(memories_fts) as bm25Score'},
86
- m.created_at as createdAt
87
- FROM memories m
88
- ${isEmptyQuery ? '' : 'INNER JOIN memories_fts ON m.rowid = memories_fts.rowid'}
89
- WHERE ${whereClause}
90
- ${isEmptyQuery ? 'ORDER BY m.created_at DESC' : 'ORDER BY bm25(memories_fts)'}
91
- LIMIT ?
383
+ catch (e) {
384
+ // Skip errors
385
+ }
386
+ }
387
+ // Re-sort and return top results
388
+ expanded.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
389
+ return expanded.slice(0, limit);
390
+ }
391
+ /**
392
+ * Get memories by session ID (stored in tags as "dialogue-1", "session-2", etc.)
393
+ */
394
+ async function getMemoriesBySession(projectId, sessionTag, limit) {
395
+ try {
396
+ const db = createDatabaseClient(await getDb());
397
+ const sqlite = db.$client;
398
+ // Query memories where tags contain the session tag
399
+ const statement = sqlite.prepare(`
400
+ SELECT
401
+ m.id as id,
402
+ m.project_id as projectId,
403
+ m.type as type,
404
+ m.content as content,
405
+ m.summary as summary,
406
+ m.tags as tags,
407
+ m.metadata as metadata,
408
+ m.embedding as embedding,
409
+ m.embedding_json as embeddingJson,
410
+ m.created_at as createdAt
411
+ FROM memories m
412
+ WHERE m.project_id = ? AND m.tags LIKE ?
413
+ ORDER BY m.created_at DESC
414
+ LIMIT ?
92
415
  `);
93
- const rows = statement.all(...params, limit * 3);
94
- // Return as ranked results (lower BM25 score = better rank)
95
- return rows.map((row, index) => ({
416
+ const rows = statement.all(projectId, `%${sessionTag}%`, limit);
417
+ return rows.map((row) => ({
96
418
  id: row.id,
97
- rank: index + 1,
98
- score: row.bm25Score,
99
- result: {
100
- id: row.id,
101
- projectId: row.projectId,
102
- type: row.type,
103
- content: row.content,
104
- summary: row.summary ?? undefined,
105
- tags: fromSqliteTags(row.tags ?? null),
106
- metadata: row.metadata ? JSON.parse(row.metadata) : null,
107
- createdAt: row.createdAt ? new Date((Number(row.createdAt) || 0) * 1000).toISOString() : undefined,
108
- },
419
+ projectId: row.projectId,
420
+ type: row.type,
421
+ content: row.content,
422
+ summary: row.summary ?? undefined,
423
+ tags: deserializeTags(row.tags ?? null),
424
+ metadata: deserializeMetadata(row.metadata ?? null),
425
+ createdAt: row.createdAt ? (normalizeTimestamp(Number(row.createdAt)) ?? undefined) : undefined,
426
+ similarity: 0.5, // Default similarity for fetched memories
109
427
  }));
110
428
  }
111
429
  catch (error) {
112
- throw error;
430
+ logger.debug(`[HybridSearch] getMemoriesBySession failed: ${error}`);
431
+ return [];
432
+ }
433
+ }
434
+ /**
435
+ * Extract session tag from memory tags (e.g., "dialogue-1", "session_1", "D1", "D2")
436
+ */
437
+ function extractSessionTag(tags) {
438
+ if (!tags)
439
+ return null;
440
+ // Look for patterns:
441
+ // - "dialogue-1", "dialogue_1", "dialogue1"
442
+ // - "session-2", "session_2", "session2"
443
+ // - "conv-1", "conv_1", "conv1"
444
+ // - "D1", "D2" (capital D followed by number)
445
+ for (const tag of tags) {
446
+ const lower = tag.toLowerCase();
447
+ if (/^(dialogue|session|conv|d)[_-]?\d+$/.test(lower)) {
448
+ return tag;
449
+ }
450
+ }
451
+ return null;
452
+ }
453
+ /**
454
+ * Get adjacent session tags (e.g., dialogue-1 -> dialogue-2, D1 -> D2)
455
+ */
456
+ function getAdjacentSessionTags(currentTag) {
457
+ const adjacent = [];
458
+ // Pattern: dialogue-N, session-N, D-N, conv-N (with or without hyphen)
459
+ const match = currentTag.match(/^(.*?)(\d+)$/i);
460
+ if (match) {
461
+ const prefix = match[1];
462
+ const num = parseInt(match[2], 10);
463
+ // Adjacent: previous and next
464
+ if (num > 1) {
465
+ adjacent.push(`${prefix}${num - 1}`);
466
+ }
467
+ adjacent.push(`${prefix}${num + 1}`);
113
468
  }
469
+ return adjacent;
114
470
  }
115
471
  /**
116
472
  * Vector semantic search using cosine similarity
@@ -127,8 +483,6 @@ async function vectorSearch(input, options) {
127
483
  let queryEmbedding = null;
128
484
  if (!isEmptyQuery) {
129
485
  queryEmbedding = await getEmbedding(input.query);
130
- // If embedding fails but query is not empty, still proceed without semantic ranking
131
- // Fall back to recency-based ranking
132
486
  }
133
487
  // Build WHERE conditions
134
488
  const conditions = [];
@@ -143,52 +497,45 @@ async function vectorSearch(input, options) {
143
497
  }
144
498
  let projectId = null;
145
499
  if (input.project) {
146
- const project = await getProjectByPath(input.project);
147
- if (project) {
148
- projectId = project.id;
149
- conditions.push('m.project_id = ?');
150
- params.push(project.id);
151
- }
500
+ const project = await requireProject(input.project);
501
+ projectId = project.id;
502
+ conditions.push('m.project_id = ?');
503
+ params.push(project.id);
152
504
  }
153
505
  const whereClause = conditions.length > 0
154
506
  ? 'WHERE ' + conditions.join(' AND ')
155
507
  : '';
156
508
  // Fetch candidates for vector search
157
- const statement = sqlite.prepare(`
158
- SELECT
159
- m.id as id,
160
- m.project_id as projectId,
161
- m.type as type,
162
- m.content as content,
163
- m.summary as summary,
164
- m.tags as tags,
165
- m.metadata as metadata,
166
- m.embedding as embedding,
167
- m.embedding_json as embeddingJson,
168
- m.created_at as createdAt
169
- FROM memories m
170
- ${whereClause}
171
- ORDER BY m.created_at DESC
172
- LIMIT ?
509
+ const statement = sqlite.prepare(`
510
+ SELECT
511
+ m.id as id,
512
+ m.project_id as projectId,
513
+ m.type as type,
514
+ m.content as content,
515
+ m.summary as summary,
516
+ m.tags as tags,
517
+ m.metadata as metadata,
518
+ m.embedding as embedding,
519
+ m.embedding_json as embeddingJson,
520
+ m.created_at as createdAt
521
+ FROM memories m
522
+ ${whereClause}
523
+ ORDER BY m.created_at DESC
524
+ LIMIT ?
173
525
  `);
174
526
  const rows = statement.all(...params, limit * 3);
175
527
  // If no embedding available, return results ordered by recency
176
528
  if (!queryEmbedding) {
177
- return rows.slice(0, limit * 2).map((item, index) => ({
529
+ return rows.slice(0, limit * 2).map((item) => ({
178
530
  id: item.id,
179
- rank: index + 1,
180
- score: 0, // No similarity score available
181
- result: {
182
- id: item.id,
183
- projectId: item.projectId,
184
- type: item.type,
185
- content: item.content,
186
- summary: item.summary ?? undefined,
187
- tags: fromSqliteTags(item.tags ?? null),
188
- metadata: item.metadata ? JSON.parse(item.metadata) : null,
189
- createdAt: item.createdAt ? new Date((Number(item.createdAt) || 0) * 1000).toISOString() : undefined,
190
- similarity: 0,
191
- },
531
+ projectId: item.projectId,
532
+ type: item.type,
533
+ content: item.content,
534
+ summary: item.summary ?? undefined,
535
+ tags: deserializeTags(item.tags ?? null),
536
+ metadata: deserializeMetadata(item.metadata ?? null),
537
+ createdAt: item.createdAt ? (normalizeTimestamp(Number(item.createdAt)) ?? undefined) : undefined,
538
+ similarity: 0,
192
539
  }));
193
540
  }
194
541
  // Calculate cosine similarity for each result
@@ -211,23 +558,18 @@ async function vectorSearch(input, options) {
211
558
  };
212
559
  })
213
560
  .filter((item) => item !== null);
214
- // Sort by similarity (descending) and return as ranked results
561
+ // Sort by similarity (descending) and return
215
562
  scored.sort((a, b) => b.similarity - a.similarity);
216
- return scored.slice(0, limit * 2).map((item, index) => ({
563
+ return scored.slice(0, limit * 2).map((item) => ({
217
564
  id: item.id,
218
- rank: index + 1,
219
- score: item.similarity,
220
- result: {
221
- id: item.id,
222
- projectId: item.projectId,
223
- type: item.type,
224
- content: item.content,
225
- summary: item.summary ?? undefined,
226
- tags: fromSqliteTags(item.tags ?? null),
227
- metadata: item.metadata ? JSON.parse(item.metadata) : null,
228
- createdAt: item.createdAt ? new Date((Number(item.createdAt) || 0) * 1000).toISOString() : undefined,
229
- similarity: item.similarity,
230
- },
565
+ projectId: item.projectId,
566
+ type: item.type,
567
+ content: item.content,
568
+ summary: item.summary ?? undefined,
569
+ tags: deserializeTags(item.tags ?? null),
570
+ metadata: deserializeMetadata(item.metadata ?? null),
571
+ createdAt: item.createdAt ? (normalizeTimestamp(Number(item.createdAt)) ?? undefined) : undefined,
572
+ similarity: item.similarity,
231
573
  }));
232
574
  }
233
575
  catch (error) {
@@ -235,139 +577,20 @@ async function vectorSearch(input, options) {
235
577
  }
236
578
  }
237
579
  /**
238
- * Reciprocal Rank Fusion (RRF) - merges ranked lists without score calibration
239
- *
240
- * RRF Formula: score(item) = sum(weight_i / (k + rank_i))
241
- * Where k is a constant (typically 60) that prevents high ranks from dominating
242
- *
243
- * Benefits:
244
- * - No need to calibrate different scoring systems (BM25 vs cosine similarity)
245
- * - Handles items that appear in only one list
246
- * - Proven to outperform weighted score fusion in most cases
247
- */
248
- function reciprocalRankFusion(bm25Results, vectorResults, options) {
249
- const { limit, bm25Weight, vectorWeight } = options;
250
- const scores = new Map();
251
- // Process BM25 results
252
- for (const item of bm25Results) {
253
- const rrfScore = (bm25Weight * 2) / (RRF_K + item.rank);
254
- const existing = scores.get(item.id);
255
- if (existing) {
256
- existing.score += rrfScore;
257
- }
258
- else {
259
- scores.set(item.id, { score: rrfScore, result: item.result });
260
- }
261
- }
262
- // Process vector results
263
- for (const item of vectorResults) {
264
- const rrfScore = (vectorWeight * 2) / (RRF_K + item.rank);
265
- const existing = scores.get(item.id);
266
- if (existing) {
267
- existing.score += rrfScore;
268
- }
269
- else {
270
- scores.set(item.id, { score: rrfScore, result: item.result });
271
- }
272
- }
273
- // Sort by RRF score (descending) and return top results
274
- return Array.from(scores.values())
275
- .sort((a, b) => b.score - a.score)
276
- .slice(0, limit)
277
- .map((item) => ({
278
- ...item.result,
279
- similarity: item.result.similarity ?? 0,
280
- }));
281
- }
282
- /**
283
- * Build FTS5 query string from user input
284
- * Handles phrase searches, OR operators for better recall, and special characters
580
+ * Apply small graph boost to results
581
+ * Graph boost is ADDITIVE (not dominating)
285
582
  */
286
- function buildFtsQuery(query) {
287
- // Ensure query is a string
288
- const queryString = typeof query === 'string' ? query : String(query ?? '');
289
- // Remove special characters that could break FTS5 syntax (except quotes)
290
- // Hyphens are problematic in FTS5 - they're treated as exclusion operators
291
- let cleaned = queryString.replace(/[^\w\s"'-]/g, ' ');
292
- // If query contains quotes, preserve as phrase search
293
- if (cleaned.includes('"')) {
294
- return cleaned || '*'; // Return * for empty after cleaning
295
- }
296
- // Split into terms
297
- const terms = cleaned.trim().split(/\s+/).filter((t) => t.length > 0);
298
- if (terms.length === 0) {
299
- return '*'; // Match all for empty query
300
- }
301
- // Wrap terms containing hyphens in quotes (e.g., "trash-cli" prevents hyphen being interpreted as NOT)
302
- const processedTerms = terms.map(term => {
303
- if (term.includes('-')) {
304
- return `"${term}"`;
305
- }
306
- return term;
583
+ function applyGraphBoostWithWeight(results, graphBoostMap, limit, graphWeight) {
584
+ // Apply SMALL additive boost to each result
585
+ // graphWeight should be 0.1-0.3, not > 1
586
+ const boosted = results.map(result => {
587
+ const boost = graphBoostMap[result.id] ?? 0;
588
+ // Add small nudge, don't replace similarity
589
+ const boostedSimilarity = (result.similarity ?? 0) + (boost * graphWeight);
590
+ return { ...result, similarity: boostedSimilarity };
307
591
  });
308
- // For multi-word queries, use OR for better recall (any term matches)
309
- // This is less restrictive than AND and finds more relevant results
310
- if (processedTerms.length > 1) {
311
- return processedTerms.join(' OR ');
312
- }
313
- return processedTerms[0];
314
- }
315
- /**
316
- * Parse embedding from SQLite storage
317
- */
318
- function parseEmbedding(embeddingData) {
319
- if (!embeddingData)
320
- return null;
321
- if (Array.isArray(embeddingData))
322
- return embeddingData;
323
- if (embeddingData instanceof Uint8Array || Buffer.isBuffer(embeddingData)) {
324
- try {
325
- const json = JSON.parse(embeddingData.toString());
326
- if (Array.isArray(json))
327
- return json;
328
- }
329
- catch {
330
- try {
331
- const buffer = embeddingData.buffer;
332
- const arrayBuffer = buffer instanceof ArrayBuffer
333
- ? buffer
334
- : buffer;
335
- const floatArray = new Float32Array(arrayBuffer);
336
- return Array.from(floatArray);
337
- }
338
- catch {
339
- return null;
340
- }
341
- }
342
- }
343
- if (typeof embeddingData === 'string') {
344
- try {
345
- const parsed = JSON.parse(embeddingData);
346
- if (Array.isArray(parsed))
347
- return parsed;
348
- }
349
- catch {
350
- return null;
351
- }
352
- }
353
- return null;
354
- }
355
- /**
356
- * Calculate cosine similarity between two vectors
357
- */
358
- function cosineSimilarity(a, b) {
359
- if (a.length !== b.length)
360
- return 0;
361
- let dotProduct = 0;
362
- let normA = 0;
363
- let normB = 0;
364
- for (let i = 0; i < a.length; i++) {
365
- dotProduct += a[i] * b[i];
366
- normA += a[i] * a[i];
367
- normB += b[i] * b[i];
368
- }
369
- if (normA === 0 || normB === 0)
370
- return 0;
371
- return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
592
+ // Re-sort by boosted similarity
593
+ boosted.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
594
+ return boosted.slice(0, limit);
372
595
  }
373
596
  //# sourceMappingURL=hybrid-search.js.map