squish-memory 1.1.5 → 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 (499) hide show
  1. package/.env.example +32 -16
  2. package/CHANGELOG.md +147 -0
  3. package/README.md +120 -78
  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 +19 -23
  11. package/config/mcp-remote-auth.json +3 -26
  12. package/config/mcp-universal.schema.json +5 -35
  13. package/config/settings.json +107 -52
  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/{dist/core/local-embeddings.js → core/embeddings/local-embeddings.ts} +12 -11
  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 +40 -17
  220. package/dist/config.js +150 -198
  221. package/dist/core/adapters/types.d.ts +13 -33
  222. package/dist/core/adapters/types.js +1 -1
  223. package/dist/core/agent-preferences.d.ts +16 -0
  224. package/dist/core/agent-preferences.js +124 -0
  225. package/dist/core/algorithms/safety/safety-checks.d.ts +1 -5
  226. package/dist/core/algorithms/types.d.ts +0 -8
  227. package/dist/core/associations.d.ts +3 -1
  228. package/dist/core/associations.js +37 -1
  229. package/dist/core/beliefs/decay.d.ts +27 -0
  230. package/dist/core/beliefs/decay.js +217 -0
  231. package/dist/core/beliefs/extractor.d.ts +9 -0
  232. package/dist/core/beliefs/extractor.js +113 -0
  233. package/dist/core/beliefs/store.d.ts +46 -0
  234. package/dist/core/beliefs/store.js +466 -0
  235. package/dist/core/beliefs/types.d.ts +28 -0
  236. package/dist/core/beliefs/types.js +2 -0
  237. package/dist/core/commands/mcp-server.d.ts +0 -1
  238. package/dist/core/commands/mcp-server.js +4 -737
  239. package/dist/core/commands/remember.d.ts +24 -0
  240. package/dist/core/commands/remember.js +144 -0
  241. package/dist/core/{toon.d.ts → compression.d.ts} +6 -4
  242. package/dist/core/{toon.js → compression.js} +8 -8
  243. package/dist/core/context/agent-context.js +1 -1
  244. package/dist/core/embeddings/embeddings.d.ts +29 -0
  245. package/dist/core/embeddings/embeddings.js +546 -0
  246. package/dist/core/embeddings/google-multimodal.js +6 -2
  247. package/dist/core/{local-embeddings.d.ts → embeddings/local-embeddings.d.ts} +1 -1
  248. package/dist/core/embeddings/local-embeddings.js +11 -0
  249. package/dist/core/embeddings/qmd-client.js +1 -1
  250. package/dist/core/embeddings/transformers-local.d.ts +64 -0
  251. package/dist/core/embeddings/transformers-local.js +213 -0
  252. package/dist/core/embeddings.d.ts +1 -28
  253. package/dist/core/embeddings.js +2 -453
  254. package/dist/core/graph/entity-deduplicator.d.ts +24 -0
  255. package/dist/core/graph/entity-deduplicator.js +183 -0
  256. package/dist/core/graph/graph-builder.d.ts +46 -0
  257. package/dist/core/graph/graph-builder.js +174 -0
  258. package/dist/core/graph/graph-traversal.d.ts +80 -0
  259. package/dist/core/graph/graph-traversal.js +315 -0
  260. package/dist/core/graph/index.d.ts +19 -0
  261. package/dist/core/graph/index.js +13 -0
  262. package/dist/core/graph/llm-entity-extractor.d.ts +49 -0
  263. package/dist/core/graph/llm-entity-extractor.js +313 -0
  264. package/dist/core/graph/multi-hop-retrieval.d.ts +48 -0
  265. package/dist/core/graph/multi-hop-retrieval.js +215 -0
  266. package/dist/core/graph/relationship-extractor.d.ts +48 -0
  267. package/dist/core/graph/relationship-extractor.js +351 -0
  268. package/dist/core/hooks/agent-hooks.d.ts +10 -1
  269. package/dist/core/hooks/agent-hooks.js +301 -24
  270. package/dist/core/hot-cache.d.ts +86 -0
  271. package/dist/core/hot-cache.js +285 -0
  272. package/dist/core/index.d.ts +9 -9
  273. package/dist/core/index.js +9 -12
  274. package/dist/core/ingestion/core-memory.d.ts +2 -2
  275. package/dist/core/ingestion/core-memory.js +3 -3
  276. package/dist/core/ingestion/learnings.js +3 -0
  277. package/dist/core/ingestion/signal-engine.d.ts +41 -0
  278. package/dist/core/ingestion/signal-engine.js +201 -0
  279. package/dist/core/{obsidian-vault.d.ts → integrations/obsidian-vault.d.ts} +2 -1
  280. package/dist/core/{obsidian-vault.js → integrations/obsidian-vault.js} +69 -7
  281. package/dist/core/lib/parse-embedding.d.ts +9 -0
  282. package/dist/core/lib/parse-embedding.js +58 -0
  283. package/dist/core/lib/schemas.d.ts +57 -54
  284. package/dist/core/lib/types.d.ts +45 -0
  285. package/dist/core/lib/types.js +6 -0
  286. package/dist/core/lib/utils.d.ts +4 -0
  287. package/dist/core/lib/utils.js +55 -0
  288. package/dist/core/lifecycle.d.ts +0 -1
  289. package/dist/core/lifecycle.js +13 -23
  290. package/dist/core/logger.d.ts +1 -0
  291. package/dist/core/logger.js +14 -8
  292. package/dist/core/mcp/tools.d.ts +0 -2
  293. package/dist/core/mcp/tools.js +0 -87
  294. package/dist/core/mcp/types.d.ts +25 -253
  295. package/dist/core/mcp/types.js +2 -2
  296. package/dist/core/memory/categorizer.js +1 -0
  297. package/dist/core/memory/consolidation.js +2 -28
  298. package/dist/core/memory/entity-extractor.d.ts +4 -0
  299. package/dist/core/memory/entity-extractor.js +30 -16
  300. package/dist/core/memory/explain.d.ts +18 -0
  301. package/dist/core/memory/explain.js +92 -0
  302. package/dist/core/memory/fact-deriver.d.ts +31 -0
  303. package/dist/core/memory/fact-deriver.js +236 -0
  304. package/dist/core/memory/hybrid-retrieval.d.ts +14 -16
  305. package/dist/core/memory/hybrid-retrieval.js +25 -127
  306. package/dist/core/memory/hybrid-scorer.js +6 -23
  307. package/dist/core/memory/hybrid-search.d.ts +10 -7
  308. package/dist/core/memory/hybrid-search.js +458 -221
  309. package/dist/core/memory/importance.d.ts +0 -17
  310. package/dist/core/memory/importance.js +1 -58
  311. package/dist/core/memory/index.d.ts +1 -0
  312. package/dist/core/memory/index.js +1 -0
  313. package/dist/core/memory/memories.d.ts +13 -17
  314. package/dist/core/memory/memories.js +78 -75
  315. package/dist/core/memory/memory-lifecycle.d.ts +2 -2
  316. package/dist/core/memory/memory-lifecycle.js +10 -18
  317. package/dist/core/memory/normalization.d.ts +1 -16
  318. package/dist/core/memory/path-strengthener.d.ts +39 -0
  319. package/dist/core/memory/path-strengthener.js +150 -0
  320. package/dist/core/memory/query-processor.js +37 -3
  321. package/dist/core/memory/retrieval-feedback.d.ts +70 -0
  322. package/dist/core/memory/retrieval-feedback.js +213 -0
  323. package/dist/core/memory/stale-cleaner.d.ts +26 -0
  324. package/dist/core/memory/stale-cleaner.js +97 -0
  325. package/dist/core/memory/stats.d.ts +10 -0
  326. package/dist/core/memory/stats.js +8 -3
  327. package/dist/core/memory/trigger-detector.d.ts +8 -1
  328. package/dist/core/memory/trigger-detector.js +42 -5
  329. package/dist/core/places/index.d.ts +1 -1
  330. package/dist/core/places/index.js +1 -1
  331. package/dist/core/places/places.d.ts +13 -13
  332. package/dist/core/places/places.js +27 -27
  333. package/dist/core/places/rules.js +23 -23
  334. package/dist/core/places/walking.d.ts +3 -3
  335. package/dist/core/places/walking.js +7 -7
  336. package/dist/core/projects.js +8 -0
  337. package/dist/core/runtime/trust-report.d.ts +102 -0
  338. package/dist/core/runtime/trust-report.js +107 -0
  339. package/dist/core/runtime/trust-state.d.ts +12 -0
  340. package/dist/core/runtime/trust-state.js +309 -0
  341. package/dist/core/scheduler/cron-scheduler.d.ts +1 -1
  342. package/dist/core/scheduler/cron-scheduler.js +164 -3
  343. package/dist/core/scheduler/job-runner.js +1 -1
  344. package/dist/core/search/qmd-wrapper.d.ts +36 -0
  345. package/dist/core/search/qmd-wrapper.js +58 -0
  346. package/dist/core/session/auto-load.js +28 -3
  347. package/dist/core/session/entity-tracker.d.ts +62 -0
  348. package/dist/core/session/entity-tracker.js +287 -0
  349. package/dist/core/session/reference-resolver.d.ts +26 -0
  350. package/dist/core/session/reference-resolver.js +121 -0
  351. package/dist/core/session/self-iteration-job.d.ts +15 -0
  352. package/dist/core/session/self-iteration-job.js +163 -58
  353. package/dist/core/session/working-set.d.ts +50 -0
  354. package/dist/core/session/working-set.js +212 -0
  355. package/dist/core/snapshots/creation.d.ts +2 -8
  356. package/dist/core/snapshots/creation.js +3 -12
  357. package/dist/core/utils/summarization-helpers.d.ts +0 -4
  358. package/dist/core/utils/summarization-helpers.js +1 -6
  359. package/dist/db/bootstrap.d.ts +2 -0
  360. package/dist/db/bootstrap.js +229 -280
  361. package/dist/db/drizzle/schema-sqlite.d.ts +702 -1
  362. package/dist/db/drizzle/schema-sqlite.js +83 -4
  363. package/dist/db/drizzle/schema.d.ts +653 -1
  364. package/dist/db/drizzle/schema.js +93 -4
  365. package/dist/db/migrations/associations.d.ts +6 -0
  366. package/dist/db/migrations/associations.js +29 -0
  367. package/dist/db/migrations/beliefs.d.ts +10 -0
  368. package/dist/db/migrations/beliefs.js +76 -0
  369. package/dist/db/migrations/core-memory.d.ts +6 -0
  370. package/dist/db/migrations/core-memory.js +29 -0
  371. package/dist/db/migrations/fts.d.ts +6 -0
  372. package/dist/db/migrations/fts.js +52 -0
  373. package/dist/db/migrations/index.d.ts +25 -0
  374. package/dist/db/migrations/index.js +51 -0
  375. package/dist/db/migrations/indexes.d.ts +6 -0
  376. package/dist/db/migrations/indexes.js +30 -0
  377. package/dist/db/migrations/learnings.d.ts +7 -0
  378. package/dist/db/migrations/learnings.js +26 -0
  379. package/dist/db/migrations/maintenance.d.ts +6 -0
  380. package/dist/db/migrations/maintenance.js +61 -0
  381. package/dist/db/migrations/memories.d.ts +7 -0
  382. package/dist/db/migrations/memories.js +16 -0
  383. package/dist/db/migrations/memory-places.d.ts +6 -0
  384. package/dist/db/migrations/memory-places.js +29 -0
  385. package/dist/db/migrations/places.d.ts +6 -0
  386. package/dist/db/migrations/places.js +43 -0
  387. package/dist/db/migrations/projects.d.ts +3 -0
  388. package/dist/db/migrations/projects.js +13 -0
  389. package/dist/db/migrations/tier-conversion.d.ts +7 -0
  390. package/dist/db/migrations/tier-conversion.js +20 -0
  391. package/dist/db/schema/beliefs.d.ts +9 -0
  392. package/dist/db/schema/beliefs.js +46 -0
  393. package/dist/db/schema/generator.d.ts +38 -0
  394. package/dist/db/schema/generator.js +108 -0
  395. package/dist/db/schema/index.d.ts +19 -20
  396. package/dist/db/schema/index.js +25 -79
  397. package/dist/db/schema/learnings.d.ts +7 -0
  398. package/dist/db/schema/learnings.js +30 -0
  399. package/dist/db/schema/memories.d.ts +7 -0
  400. package/dist/db/schema/memories.js +81 -0
  401. package/dist/db/schema/projects.d.ts +4 -0
  402. package/dist/db/schema/projects.js +31 -0
  403. package/dist/packages/mcp/src/index.d.ts +3 -0
  404. package/dist/packages/mcp/src/index.js +733 -0
  405. package/mcp.json.example +8 -11
  406. package/package.json +57 -76
  407. package/packages/cli/package.json +22 -0
  408. package/packages/cli/src/commands/clean.ts +68 -0
  409. package/packages/cli/src/commands/context.ts +79 -0
  410. package/packages/cli/src/commands/doctor.ts +357 -0
  411. package/packages/cli/src/commands/forget.ts +72 -0
  412. package/packages/cli/src/commands/health.ts +36 -0
  413. package/packages/cli/src/commands/inspect.ts +41 -0
  414. package/packages/cli/src/commands/link.ts +50 -0
  415. package/packages/cli/src/commands/migrate.ts +93 -0
  416. package/packages/cli/src/commands/recall.ts +99 -0
  417. package/packages/cli/src/commands/recent.ts +57 -0
  418. package/packages/cli/src/commands/remember.ts +139 -0
  419. package/packages/cli/src/commands/run.ts +58 -0
  420. package/packages/cli/src/commands/stale.ts +43 -0
  421. package/packages/cli/src/commands/stats.ts +42 -0
  422. package/packages/cli/src/index.ts +57 -0
  423. package/packages/cli/tsconfig.json +24 -0
  424. package/packages/mcp/package.json +26 -0
  425. package/packages/mcp/src/index.ts +877 -0
  426. package/packages/mcp/tsconfig.json +20 -0
  427. package/skills/squish-memory/SKILL.md +38 -35
  428. package/skills/squish-memory/{scripts/install.sh → install.sh} +1 -1
  429. package/skills/squish-memory/references/claude-desktop.json +12 -0
  430. package/skills/squish-memory/references/openclaw.json +13 -0
  431. package/skills/squish-memory/references/opencode.json +14 -0
  432. package/config/hooks/claude-code-hooks.json +0 -39
  433. package/config/hooks/cursor-hooks.json +0 -30
  434. package/config/hooks/opencode-hooks.json +0 -30
  435. package/config/hooks/windsurf-hooks.json +0 -30
  436. package/config/mcp-cli-fallback-policy.json +0 -22
  437. package/config/mcp.json +0 -38
  438. package/config/plugin-manifest.json +0 -101
  439. package/config/plugin-manifest.schema.json +0 -244
  440. package/config/plugin.json +0 -32
  441. package/config/remote-memory-policy.json +0 -32
  442. package/core/commands/context-paging.md +0 -51
  443. package/core/commands/context-status.md +0 -22
  444. package/core/commands/context.md +0 -5
  445. package/core/commands/core-memory.md +0 -56
  446. package/core/commands/health.md +0 -5
  447. package/core/commands/init.md +0 -39
  448. package/core/commands/merge.md +0 -113
  449. package/core/commands/recall.md +0 -5
  450. package/core/commands/remember.md +0 -11
  451. package/core/commands/search.md +0 -10
  452. package/dist/core/commands/managed-sync.d.ts +0 -10
  453. package/dist/core/commands/managed-sync.js +0 -64
  454. package/dist/core/external-folder/index.d.ts +0 -102
  455. package/dist/core/external-folder/index.js +0 -294
  456. package/dist/core/namespaces/index.d.ts +0 -71
  457. package/dist/core/namespaces/index.js +0 -305
  458. package/dist/core/namespaces/uri-parser.d.ts +0 -31
  459. package/dist/core/namespaces/uri-parser.js +0 -74
  460. package/dist/core/search/qmd-search.d.ts +0 -61
  461. package/dist/core/search/qmd-search.js +0 -178
  462. package/dist/core/session-hooks/self-iteration-job.d.ts +0 -20
  463. package/dist/core/session-hooks/self-iteration-job.js +0 -282
  464. package/dist/core/session-hooks/session-hooks.d.ts +0 -18
  465. package/dist/core/session-hooks/session-hooks.js +0 -58
  466. package/dist/core/snapshots.d.ts +0 -29
  467. package/dist/core/snapshots.js +0 -220
  468. package/dist/core/sync/qmd-sync.d.ts +0 -94
  469. package/dist/core/sync/qmd-sync.js +0 -201
  470. package/dist/index.d.ts +0 -7
  471. package/dist/index.js +0 -1677
  472. package/dist/vendor/sql.js/sql-wasm.wasm +0 -0
  473. package/dist/webui/server.d.ts +0 -5
  474. package/dist/webui/server.js +0 -642
  475. package/generated/mcp/manifest.json +0 -23
  476. package/generated/mcp/mcp-servers.json +0 -25
  477. package/generated/mcp/mcporter.json +0 -34
  478. package/generated/mcp/openclaw-memory-qmd.json +0 -17
  479. package/generated/mcp/runtime.json +0 -12
  480. package/scripts/README.md +0 -60
  481. package/scripts/build-release.sh +0 -36
  482. package/scripts/check-secrets.js +0 -132
  483. package/scripts/copy-runtime-assets.mjs +0 -26
  484. package/scripts/generate-mcp.mjs +0 -264
  485. package/scripts/github-release.sh +0 -77
  486. package/scripts/init-dirs.mjs +0 -13
  487. package/scripts/install-claude-code.sh +0 -85
  488. package/scripts/install-cursor.sh +0 -56
  489. package/scripts/install-hooks.sh +0 -73
  490. package/scripts/install-interactive.mjs +0 -357
  491. package/scripts/install-opencode.sh +0 -75
  492. package/scripts/install-plugin.mjs +0 -415
  493. package/scripts/install-windsurf.sh +0 -67
  494. package/scripts/remote-preflight.mjs +0 -62
  495. package/scripts/squish-fallback.mjs +0 -132
  496. package/scripts/test-interactive.mjs +0 -131
  497. package/scripts/verify-mcp.mjs +0 -214
  498. package/skills/squish-memory/scripts/install.mjs +0 -335
  499. package/skills/squish-memory/write_skill.js +0 -2
@@ -0,0 +1,381 @@
1
+ /**
2
+ * Type-specific merge strategies for different memory types.
3
+ * Each type has different merge semantics to preserve meaning and prevent data loss.
4
+ */
5
+
6
+ import type { Memory, MemoryType } from '../../../db/drizzle/schema.js';
7
+
8
+ export interface MergeStrategy {
9
+ type: MemoryType;
10
+ /**
11
+ * Merge a set of source memories into a single canonical memory
12
+ */
13
+ merge(sources: Memory[]): MergedMemory;
14
+ /**
15
+ * Check if memories can be safely merged
16
+ * Returns { ok, reason } where reason explains why merging is not allowed
17
+ */
18
+ canMerge(sources: Memory[]): { ok: boolean; reason?: string };
19
+ }
20
+
21
+ export interface MergedMemory {
22
+ content: string;
23
+ summary: string | null;
24
+ tags: string[];
25
+ metadata: Record<string, unknown>;
26
+ mergeReason: string;
27
+ conflictWarnings: string[];
28
+ }
29
+
30
+ /**
31
+ * Base implementation of MergeStrategy with common functionality.
32
+ * Reduces code duplication across specific merge strategies.
33
+ */
34
+ export abstract class BaseMergeStrategy implements MergeStrategy {
35
+ abstract type: MemoryType;
36
+
37
+ canMerge(sources: Memory[]): { ok: boolean; reason?: string } {
38
+ if (sources.length < 2) {
39
+ return { ok: false, reason: 'Need at least 2 memories to merge' };
40
+ }
41
+ return { ok: true };
42
+ }
43
+
44
+ protected validateSources(sources: Memory[]): boolean {
45
+ return sources.length >= 2;
46
+ }
47
+
48
+ protected handleEmptySources(): MergedMemory {
49
+ return {
50
+ content: '',
51
+ summary: null,
52
+ tags: [],
53
+ metadata: {},
54
+ mergeReason: 'Empty source set',
55
+ conflictWarnings: [],
56
+ };
57
+ }
58
+
59
+ protected mergeTags(sources: Memory[]): string[] {
60
+ const tagSet = new Set<string>();
61
+ for (const source of sources) {
62
+ for (const tag of source.tags || []) {
63
+ tagSet.add(tag);
64
+ }
65
+ }
66
+ return Array.from(tagSet);
67
+ }
68
+
69
+ protected buildBaseMetadata(
70
+ sources: Memory[],
71
+ extra?: Record<string, unknown>
72
+ ): Record<string, unknown> {
73
+ return {
74
+ mergedFrom: sources.map((m) => m.id),
75
+ mergeCount: sources.length,
76
+ ...extra,
77
+ };
78
+ }
79
+
80
+ protected sortByDate(
81
+ sources: Memory[],
82
+ order: 'asc' | 'desc' = 'desc'
83
+ ): Memory[] {
84
+ const sorted = [...sources].sort(
85
+ (a, b) => new Date(b.createdAt!).getTime() - new Date(a.createdAt!).getTime()
86
+ );
87
+ return order === 'asc' ? [...sorted].reverse() : sorted;
88
+ }
89
+
90
+ protected sortChronologically(sources: Memory[]): Memory[] {
91
+ return [...sources].sort(
92
+ (a, b) => new Date(a.createdAt!).getTime() - new Date(b.createdAt!).getTime()
93
+ );
94
+ }
95
+
96
+ // Abstract method each subclass must implement
97
+ abstract merge(sources: Memory[]): MergedMemory;
98
+ }
99
+
100
+ /**
101
+ * FACT strategy: Union of information, remove exact duplicates.
102
+ * Combines all unique facts into a unified statement with provenance tracking.
103
+ */
104
+ class FactMergeStrategy extends BaseMergeStrategy {
105
+ type: MemoryType = 'fact';
106
+
107
+ merge(sources: Memory[]): MergedMemory {
108
+ if (sources.length === 0) {
109
+ return this.handleEmptySources();
110
+ }
111
+
112
+ // Split content into sentences and deduplicate
113
+ const sentenceSet = new Set<string>();
114
+ const timestamps: string[] = [];
115
+
116
+ for (const source of sources) {
117
+ // Extract sentences (split by period, exclamation, question)
118
+ const sentences = source.content
119
+ .split(/[.!?]\s+/)
120
+ .map((s: string) => s.trim())
121
+ .filter((s: string) => s.length > 0);
122
+
123
+ for (const sentence of sentences) {
124
+ sentenceSet.add(sentence);
125
+ }
126
+
127
+ if (source.createdAt) {
128
+ timestamps.push(source.createdAt);
129
+ }
130
+ }
131
+
132
+ // Sort sentences for consistency
133
+ const mergedSentences = Array.from(sentenceSet).sort();
134
+ const content = mergedSentences.join('. ') + (mergedSentences.length > 0 ? '.' : '');
135
+
136
+ // Build merged metadata with provenance
137
+ const metadata = this.buildBaseMetadata(sources, {
138
+ timestamps: timestamps.sort(),
139
+ });
140
+
141
+ return {
142
+ content,
143
+ summary: null,
144
+ tags: this.mergeTags(sources),
145
+ metadata,
146
+ mergeReason: `Merged ${sources.length} facts by combining all unique statements`,
147
+ conflictWarnings: [],
148
+ };
149
+ }
150
+ }
151
+
152
+ /**
153
+ * PREFERENCE strategy: Keep latest by timestamp, track evolution history.
154
+ * Warns if preferences conflict, indicating user changed their mind.
155
+ */
156
+ class PreferenceMergeStrategy extends BaseMergeStrategy {
157
+ type: MemoryType = 'preference';
158
+
159
+ merge(sources: Memory[]): MergedMemory {
160
+ if (sources.length === 0) {
161
+ return this.handleEmptySources();
162
+ }
163
+
164
+ // Sort by creation date (newest first)
165
+ const sorted = this.sortByDate(sources);
166
+
167
+ const latest = sorted[0];
168
+ const warnings: string[] = [];
169
+
170
+ // Check for conflicting preferences
171
+ const uniqueContents = new Set(sources.map((m) => m.content));
172
+ if (uniqueContents.size > 1) {
173
+ warnings.push(`Multiple preferences detected: ${Array.from(uniqueContents).join(', ')}`);
174
+ warnings.push(`Using latest preference from ${latest.createdAt || 'unknown date'}`);
175
+ }
176
+
177
+ // Build history timeline
178
+ const history = sorted.map((m) => ({
179
+ preference: m.content,
180
+ createdAt: m.createdAt,
181
+ confidence: m.confidence || 100,
182
+ source: m.source,
183
+ }));
184
+
185
+ const metadata = this.buildBaseMetadata(sources, {
186
+ preferenceHistory: history,
187
+ latestAt: latest.createdAt,
188
+ });
189
+
190
+ const summary =
191
+ sources.length > 1
192
+ ? `Prefers: ${latest.content} (evolved from ${sources.length} preferences)`
193
+ : null;
194
+
195
+ return {
196
+ content: latest.content,
197
+ summary,
198
+ tags: latest.tags || [],
199
+ metadata,
200
+ mergeReason: `Merged ${sources.length} preference records, keeping latest from ${latest.createdAt}`,
201
+ conflictWarnings: warnings,
202
+ };
203
+ }
204
+ }
205
+
206
+ /**
207
+ * DECISION strategy: Keep latest decision, link to previous ones in timeline.
208
+ * Warns if decisions contradict, preserving rationale history.
209
+ */
210
+ class DecisionMergeStrategy extends BaseMergeStrategy {
211
+ type: MemoryType = 'decision';
212
+
213
+ merge(sources: Memory[]): MergedMemory {
214
+ if (sources.length === 0) {
215
+ return this.handleEmptySources();
216
+ }
217
+
218
+ // Sort by creation date (newest first)
219
+ const sorted = this.sortByDate(sources);
220
+
221
+ const latest = sorted[0];
222
+ const warnings: string[] = [];
223
+
224
+ // Check for conflicting decisions
225
+ const uniqueDecisions = new Set(sources.map((m) => m.content));
226
+ if (uniqueDecisions.size > 1) {
227
+ warnings.push(
228
+ `Decision changed: was ${sorted[sorted.length - 1].content}, now ${latest.content}`
229
+ );
230
+ }
231
+
232
+ // Build decision timeline
233
+ const timeline = sorted.map((m) => ({
234
+ decision: m.content,
235
+ createdAt: m.createdAt,
236
+ confidence: m.confidence || 100,
237
+ rationale: m.summary || 'No rationale recorded',
238
+ }));
239
+
240
+ const metadata = this.buildBaseMetadata(sources, {
241
+ decisionTimeline: timeline,
242
+ currentDecisionAt: latest.createdAt,
243
+ supersedes: sorted.slice(1).map((m) => m.id),
244
+ });
245
+
246
+ const summary = sources.length > 1 ? `Decided: ${latest.content} (${sources.length} decisions)` : null;
247
+
248
+ return {
249
+ content: latest.content,
250
+ summary,
251
+ tags: latest.tags || [],
252
+ metadata,
253
+ mergeReason: `Merged ${sources.length} decision records, keeping latest from ${latest.createdAt}`,
254
+ conflictWarnings: warnings,
255
+ };
256
+ }
257
+ }
258
+
259
+ /**
260
+ * OBSERVATION strategy: Aggregate observations in chronological order.
261
+ * Preserves temporal patterns and frequency information.
262
+ */
263
+ class ObservationMergeStrategy extends BaseMergeStrategy {
264
+ type: MemoryType = 'observation';
265
+
266
+ merge(sources: Memory[]): MergedMemory {
267
+ if (sources.length === 0) {
268
+ return this.handleEmptySources();
269
+ }
270
+
271
+ // Sort chronologically
272
+ const sorted = this.sortChronologically(sources);
273
+
274
+ // Create observation summary
275
+ const observations = sorted.map((m) => `• ${m.content}`).join('\n');
276
+
277
+ const metadata = this.buildBaseMetadata(sources, {
278
+ observationCount: sources.length,
279
+ timeSpan: {
280
+ start: sorted[0].createdAt,
281
+ end: sorted[sorted.length - 1].createdAt,
282
+ },
283
+ chronologicalOrder: sorted.map((m) => ({
284
+ id: m.id,
285
+ content: m.content,
286
+ createdAt: m.createdAt,
287
+ })),
288
+ });
289
+
290
+ return {
291
+ content: `Observations (${sources.length} total):\n${observations}`,
292
+ summary: `${sources.length} observations over time period`,
293
+ tags: this.mergeTags(sources),
294
+ metadata,
295
+ mergeReason: `Merged ${sources.length} observations chronologically`,
296
+ conflictWarnings: [],
297
+ };
298
+ }
299
+ }
300
+
301
+ /**
302
+ * CONTEXT strategy: Union of unique context, remove exact duplicates.
303
+ * Preserves all distinct context items.
304
+ */
305
+ class ContextMergeStrategy extends BaseMergeStrategy {
306
+ type: MemoryType = 'context';
307
+
308
+ merge(sources: Memory[]): MergedMemory {
309
+ if (sources.length === 0) {
310
+ return this.handleEmptySources();
311
+ }
312
+
313
+ // Deduplicate by content (exact matches)
314
+ const uniqueContexts = new Map<string, Memory>();
315
+ for (const source of sources) {
316
+ if (!uniqueContexts.has(source.content)) {
317
+ uniqueContexts.set(source.content, source);
318
+ }
319
+ }
320
+
321
+ // Format as list
322
+ const contextList = Array.from(uniqueContexts.keys())
323
+ .sort()
324
+ .map((content) => `• ${content}`)
325
+ .join('\n');
326
+
327
+ const metadata = this.buildBaseMetadata(sources, {
328
+ uniqueContextCount: uniqueContexts.size,
329
+ totalContextCount: sources.length,
330
+ deduplicatedEntries: sources.length - uniqueContexts.size,
331
+ });
332
+
333
+ return {
334
+ content: contextList,
335
+ summary: `${uniqueContexts.size} context items (${sources.length} total)`,
336
+ tags: this.mergeTags(sources),
337
+ metadata,
338
+ mergeReason: `Merged ${sources.length} context records into ${uniqueContexts.size} unique items`,
339
+ conflictWarnings: [],
340
+ };
341
+ }
342
+ }
343
+
344
+ export const MERGE_STRATEGIES: Record<MemoryType, MergeStrategy> = {
345
+ fact: new FactMergeStrategy(),
346
+ preference: new PreferenceMergeStrategy(),
347
+ decision: new DecisionMergeStrategy(),
348
+ observation: new ObservationMergeStrategy(),
349
+ context: new ContextMergeStrategy(),
350
+ reflection: new ObservationMergeStrategy(),
351
+ note: new ObservationMergeStrategy(), // jot uses observation strategy
352
+ };
353
+
354
+ export function getMergeStrategy(type: MemoryType): MergeStrategy {
355
+ const strategy = MERGE_STRATEGIES[type];
356
+ if (!strategy) {
357
+ throw new Error(`No merge strategy defined for type: ${type}`);
358
+ }
359
+ return strategy;
360
+ }
361
+
362
+ export function mergeMemories(sources: Memory[]): MergedMemory {
363
+ if (sources.length === 0) {
364
+ throw new Error('Cannot merge: no source memories');
365
+ }
366
+
367
+ // All sources must be same type
368
+ const type = sources[0].type;
369
+ const allSameType = sources.every((m) => m.type === type);
370
+ if (!allSameType) {
371
+ throw new Error('Cannot merge: memories must be same type');
372
+ }
373
+
374
+ const strategy = getMergeStrategy(type);
375
+ const canMerge = strategy.canMerge(sources);
376
+ if (!canMerge.ok) {
377
+ throw new Error(`Cannot merge: ${canMerge.reason}`);
378
+ }
379
+
380
+ return strategy.merge(sources);
381
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * TypeScript types and interfaces for memory merging
3
+ */
4
+
5
+ import type { Memory, MemoryMergeProposal, MemoryMergeHistory } from '../../db/drizzle/schema.js';
6
+ import type { SafetyCheckResult } from '../../core/lib/types.js';
7
+
8
+ /**
9
+ * Merge proposal status
10
+ */
11
+ export type MergeProposalStatus = 'pending' | 'approved' | 'rejected' | 'expired';
12
+
13
+ /**
14
+ * Confidence level for merge suggestions
15
+ */
16
+ export type ConfidenceLevel = 'high' | 'medium' | 'low';
17
+
18
+ /**
19
+ * Detection method used to find duplicates
20
+ */
21
+ export type DetectionMethod = 'simhash' | 'minhash' | 'embedding';
22
+
23
+ /**
24
+ * Merge strategy type per memory type
25
+ */
26
+ export type MergeStrategyType = 'union' | 'latest' | 'voting' | 'custom';
27
+
28
+ /**
29
+ * Memory pair candidate for merging
30
+ */
31
+ export interface MemoryMergePair {
32
+ memory1: Memory;
33
+ memory2: Memory;
34
+ similarityScore: number;
35
+ detectionMethod: DetectionMethod;
36
+ confidenceLevel: ConfidenceLevel;
37
+ mergeReason: string;
38
+ }
39
+
40
+ /**
41
+ * Merge proposal with all details
42
+ */
43
+ export interface MergeProposalDetails {
44
+ proposal: MemoryMergeProposal;
45
+ sourceMemories: Memory[];
46
+ previewMerged: {
47
+ content: string;
48
+ summary: string | null;
49
+ tags: string[];
50
+ metadata: Record<string, unknown>;
51
+ };
52
+ analysis: {
53
+ savedTokens: number;
54
+ savedPercentage: number;
55
+ conflictWarnings: string[];
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Merge result after approval
61
+ */
62
+ export interface MergeResult {
63
+ proposalId: string;
64
+ canonicalMemoryId: string;
65
+ mergedMemoryIds: string[];
66
+ tokensSaved: number;
67
+ mergedAt: Date;
68
+ mergeHistoryId?: string;
69
+ }
70
+
71
+ /**
72
+ * Merge reversal result
73
+ */
74
+ export interface MergeReversal {
75
+ mergeHistoryId: string;
76
+ canonicalMemoryId: string;
77
+ restoredMemoryIds: string[];
78
+ reversedAt: Date;
79
+ }
80
+
81
+ /**
82
+ * Detection statistics
83
+ */
84
+ export interface DetectionStatistics {
85
+ totalMemories: number;
86
+ scannedMemories: number;
87
+ candidatesFound: number;
88
+ candidatesRanked: number;
89
+ estimatedTokensSaved: number;
90
+ timeMs: {
91
+ stage1: number;
92
+ stage2: number;
93
+ total: number;
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Project merge statistics
99
+ */
100
+ export interface ProjectMergeStatistics {
101
+ projectId: string;
102
+ totalMemories: number;
103
+ mergeableMemories: number;
104
+ mergedMemories: number;
105
+ canonicalMemories: number;
106
+ pendingProposals: number;
107
+ approvedMerges: number;
108
+ rejectedProposals: number;
109
+ reversedMerges: number;
110
+ tokensSaved: {
111
+ total: number;
112
+ formatted: string;
113
+ percentage: number;
114
+ };
115
+ averageMergeSize: number;
116
+ }
117
+
118
+
119
+
120
+ /**
121
+ * Hash cache entry for fast duplicate detection
122
+ */
123
+ export interface HashCacheEntry {
124
+ memoryId: string;
125
+ simhash: string;
126
+ minhash: number[];
127
+ contentHash: string;
128
+ lastUpdated: Date;
129
+ }
130
+
131
+ /**
132
+ * Stage 1 candidate pair with hash distances
133
+ */
134
+ export interface Stage1CandidatePair {
135
+ memoryId1: string;
136
+ memoryId2: string;
137
+ simhashDistance: number;
138
+ minhashSimilarity: number;
139
+ matched: 'simhash' | 'minhash' | 'both';
140
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Unified response builder for algorithm handlers
3
+ * Provides consistent response formatting across all algorithm handlers
4
+ */
5
+
6
+ export interface ErrorResponse {
7
+ ok: false;
8
+ message: string;
9
+ error: string;
10
+ }
11
+
12
+ export interface SuccessResponse<T = unknown> {
13
+ ok: true;
14
+ message: string;
15
+ data?: T;
16
+ }
17
+
18
+ export type ApiResponse<T = unknown> = ErrorResponse | SuccessResponse<T>;
19
+
20
+ /**
21
+ * Build a success response with optional data
22
+ */
23
+ export function buildSuccessResponse<T = unknown>(
24
+ message: string,
25
+ data?: T
26
+ ): SuccessResponse<T> {
27
+ const response: SuccessResponse<T> = { ok: true, message };
28
+ if (data !== undefined) {
29
+ response.data = data;
30
+ }
31
+ return response;
32
+ }
33
+
34
+ /**
35
+ * Build an error response
36
+ */
37
+ export function buildErrorResponse(
38
+ message: string,
39
+ error?: unknown
40
+ ): ErrorResponse {
41
+ return {
42
+ ok: false,
43
+ message,
44
+ error: error instanceof Error ? error.message : String(error || 'Unknown error'),
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Wrap async handlers with consistent error handling
50
+ */
51
+ export async function withErrorHandler<T>(
52
+ handler: () => Promise<T>,
53
+ errorMessage: string
54
+ ): Promise<ApiResponse<T>> {
55
+ try {
56
+ const result = await handler();
57
+ return buildSuccessResponse(errorMessage, result);
58
+ } catch (error) {
59
+ return buildErrorResponse(errorMessage, error);
60
+ }
61
+ }