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,151 @@
1
+ /**
2
+ * Shared utility functions for the squish codebase
3
+ */
4
+
5
+ import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
6
+ import { config } from '../../config.js';
7
+ import { toSqliteJson } from '../memory/serialization.js';
8
+
9
+ export function normalizeTimestamp(value: any): string | null {
10
+ if (!value) return null;
11
+ if (value instanceof Date) return value.toISOString();
12
+ if (typeof value === 'number') {
13
+ try {
14
+ // Handle different timestamp formats using magnitude thresholds
15
+ // Microseconds: > 100000000000000 (e.g., 1700000000000000)
16
+ // Milliseconds: > 1000000000000 (e.g., 1700000000000)
17
+ // Seconds: <= 1000000000000 (e.g., 1700000000)
18
+ if (value > 100000000000000) {
19
+ return new Date(value / 1000).toISOString();
20
+ } else if (value > 1000000000000) {
21
+ return new Date(value).toISOString();
22
+ } else if (value >= 0) {
23
+ return new Date(value * 1000).toISOString();
24
+ }
25
+ return null;
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+ if (typeof value === 'string') {
31
+ try {
32
+ const parsed = new Date(value);
33
+ if (!isNaN(parsed.getTime())) return parsed.toISOString();
34
+ return null;
35
+ } catch {
36
+ return null;
37
+ }
38
+ }
39
+ return null;
40
+ }
41
+
42
+ export function now(): string {
43
+ return new Date().toISOString();
44
+ }
45
+
46
+ export function isDatabaseUnavailableError(error: any): boolean {
47
+ const message = error?.message || '';
48
+ return [
49
+ 'Database unavailable',
50
+ 'not a valid Win32 application',
51
+ 'invalid ELF header',
52
+ 'bun:',
53
+ 'sql.js wasm asset not found',
54
+ 'SQLite database initialization failed',
55
+ 'working local SQLite driver',
56
+ ].some((pattern) => message.includes(pattern));
57
+ }
58
+
59
+ export async function withDatabaseErrorHandling<T>(
60
+ operation: () => Promise<T>,
61
+ errorMessage: string
62
+ ): Promise<T> {
63
+ try {
64
+ return await operation();
65
+ } catch (dbError: any) {
66
+ if (isDatabaseUnavailableError(dbError)) {
67
+ throw new McpError(ErrorCode.InternalError, errorMessage);
68
+ }
69
+ throw dbError;
70
+ }
71
+ }
72
+
73
+ export function clampLimit(value: number | undefined, defaultValue: number, min: number = 1, max: number = 100): number {
74
+ return Math.min(Math.max(value ?? defaultValue, min), max);
75
+ }
76
+
77
+ export function prepareEmbedding(embedding: number[] | null): { embedding?: number[] | null; embeddingJson?: string | null } {
78
+ if (config.isTeamMode) {
79
+ return { embedding: embedding ?? null };
80
+ }
81
+ return { embeddingJson: toSqliteJson(embedding ?? null) };
82
+ }
83
+
84
+ export function determineOverallStatus(dbStatus: string, redisOk: boolean): string {
85
+ if ((dbStatus === 'ok' || dbStatus === 'unavailable') && redisOk) {
86
+ return 'ok';
87
+ }
88
+ if (dbStatus === 'unavailable') {
89
+ return 'degraded';
90
+ }
91
+ return 'error';
92
+ }
93
+
94
+ // Date parsing utilities - shared between CLI and MCP
95
+ // ============================================================================
96
+
97
+ export function parseDate(input: string): Date | null {
98
+ if (!input) return null;
99
+ const now = new Date();
100
+ const lower = input.toLowerCase().trim();
101
+
102
+ // Direct date parse
103
+ const parsed = new Date(input);
104
+ if (!isNaN(parsed.getTime())) return parsed;
105
+
106
+ // Relative parsing
107
+ const dayMatch = lower.match(/(\d+)\s*day/i);
108
+ const weekMatch = lower.match(/(\d+)\s*week/i);
109
+ const monthMatch = lower.match(/(\d+)\s*month/i);
110
+
111
+ if (lower === 'today') {
112
+ const d = new Date(now);
113
+ d.setHours(0, 0, 0, 0);
114
+ return d;
115
+ }
116
+ if (lower === 'yesterday') return new Date(now.getTime() - 86400000);
117
+ if (lower === 'thisweek' || lower === 'this week') {
118
+ const d = new Date(now);
119
+ d.setDate(d.getDate() - d.getDay());
120
+ d.setHours(0, 0, 0, 0);
121
+ return d;
122
+ }
123
+ if (lower === 'lastweek' || lower === 'last week') {
124
+ const d = new Date(now);
125
+ d.setDate(d.getDate() - d.getDay() - 7);
126
+ return d;
127
+ }
128
+
129
+ if (dayMatch) return new Date(now.getTime() - parseInt(dayMatch[1]) * 86400000);
130
+ if (weekMatch) return new Date(now.getTime() - parseInt(weekMatch[1]) * 604800000);
131
+ if (monthMatch) return new Date(now.getTime() - parseInt(monthMatch[1]) * 2592000000);
132
+
133
+ return null;
134
+ }
135
+
136
+ export function filterByDateRange<T extends { createdAt?: string | null }>(
137
+ items: T[],
138
+ since?: string,
139
+ until?: string
140
+ ): T[] {
141
+ const sinceDate = parseDate(since || '');
142
+ const untilDate = parseDate(until || '');
143
+
144
+ return items.filter(item => {
145
+ if (!item.createdAt) return true;
146
+ const created = new Date(item.createdAt);
147
+ if (sinceDate && created < sinceDate) return false;
148
+ if (untilDate && created > untilDate) return false;
149
+ return true;
150
+ });
151
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Input validation utilities for Squish
3
+ * Consolidates scattered validation patterns into a unified module
4
+ */
5
+
6
+ import { clampLimit as originalClampLimit } from './utils.js';
7
+ import { normalizeTags } from '../memory/serialization.js';
8
+
9
+ /**
10
+ * Validate and normalize a limit value with bounds checking
11
+ */
12
+ export function validateLimit(
13
+ value: number | string | undefined,
14
+ defaultValue: number = 20,
15
+ min: number = 1,
16
+ max: number = 100
17
+ ): number {
18
+ // Handle undefined, null, empty string
19
+ if (value === undefined || value === null || value === '') {
20
+ return defaultValue;
21
+ }
22
+
23
+ // Convert string to number, truncate decimals
24
+ let num: number;
25
+ if (typeof value === 'string') {
26
+ const parsed = parseInt(value, 10);
27
+ if (isNaN(parsed)) {
28
+ return NaN;
29
+ }
30
+ num = parsed;
31
+ } else {
32
+ // Truncate decimals for numbers
33
+ num = Math.trunc(value);
34
+ }
35
+
36
+ // Handle NaN
37
+ if (isNaN(num)) {
38
+ return NaN;
39
+ }
40
+
41
+ // Clamp to bounds
42
+ return Math.min(Math.max(num, min), max);
43
+ }
44
+
45
+ /**
46
+ * Parse an integer with bounds checking
47
+ */
48
+ export function parseIntBounded(
49
+ value: number | string | undefined,
50
+ defaultValue: number,
51
+ min: number,
52
+ max: number
53
+ ): number {
54
+ return validateLimit(value, defaultValue, min, max);
55
+ }
56
+
57
+ /**
58
+ * Validate a project path
59
+ */
60
+ export async function validateProjectPath(
61
+ path: string | undefined,
62
+ options?: { createIfMissing?: boolean; require?: boolean }
63
+ ): Promise<string> {
64
+ const { createIfMissing = false, require: requireProject = false } = options || {};
65
+
66
+ // If path is undefined, throw if required or return current directory
67
+ if (path === undefined || path === '') {
68
+ if (requireProject) {
69
+ throw new Error('Project path is required');
70
+ }
71
+ return process.cwd();
72
+ }
73
+
74
+ // Resolve to absolute path
75
+ const absolutePath = path.startsWith('/') || /^[a-zA-Z]:\\/.test(path)
76
+ ? path
77
+ : path.startsWith('~')
78
+ ? path.replace(/^~/, process.env.HOME || process.env.USERPROFILE || '')
79
+ : path.startsWith('.')
80
+ ? path
81
+ : path;
82
+
83
+ // Check if project exists in database
84
+ const { getProjectByPath } = await import('../projects.js');
85
+ const existingProject = await getProjectByPath(absolutePath);
86
+
87
+ if (existingProject) {
88
+ return absolutePath;
89
+ }
90
+
91
+ // Project doesn't exist
92
+ if (requireProject) {
93
+ throw new Error(`Project not found: ${absolutePath}`);
94
+ }
95
+
96
+ if (createIfMissing) {
97
+ const { ensureProject } = await import('../projects.js');
98
+ await ensureProject(absolutePath);
99
+ return absolutePath;
100
+ }
101
+
102
+ // Return the path even if it doesn't exist in database (for non-db validation)
103
+ return absolutePath;
104
+ }
105
+
106
+ /**
107
+ * Validate a UUID
108
+ */
109
+ export function validateUuid(id: string): boolean {
110
+ if (typeof id !== 'string' || !id) {
111
+ return false;
112
+ }
113
+
114
+ // UUID v4/v5 regex pattern (8-4-4-4-12 hex digits)
115
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
116
+ return uuidRegex.test(id);
117
+ }
118
+
119
+ /**
120
+ * Require a valid UUID, throws if invalid
121
+ */
122
+ export function requireUuid(id: string): string {
123
+ if (!validateUuid(id)) {
124
+ throw new Error('Invalid UUID');
125
+ }
126
+ return id;
127
+ }
128
+
129
+ /**
130
+ * Validate a date value
131
+ *
132
+ * @param value - The date to validate (string, Date, number, or undefined)
133
+ * @returns Date object if valid, null otherwise
134
+ */
135
+ export function validateDate(value: string | Date | number | undefined): Date | null {
136
+ if (!value) {
137
+ return null;
138
+ }
139
+
140
+ try {
141
+ let date: Date;
142
+
143
+ if (value instanceof Date) {
144
+ date = value;
145
+ } else if (typeof value === 'number') {
146
+ // Handle both milliseconds and seconds timestamps
147
+ if (value > 100000000000000) {
148
+ // Microseconds, convert to milliseconds
149
+ date = new Date(value / 1000);
150
+ } else if (value > 1000000000000) {
151
+ // Milliseconds
152
+ date = new Date(value);
153
+ } else if (value >= 0) {
154
+ // Seconds
155
+ date = new Date(value * 1000);
156
+ } else {
157
+ return null;
158
+ }
159
+ } else if (typeof value === 'string') {
160
+ date = new Date(value);
161
+ } else {
162
+ return null;
163
+ }
164
+
165
+ // Check if date is valid
166
+ if (isNaN(date.getTime())) {
167
+ return null;
168
+ }
169
+
170
+ return date;
171
+ } catch {
172
+ return null;
173
+ }
174
+ }
175
+
176
+ // Re-export normalizeTags for tag validation
177
+ export { normalizeTags };
178
+
179
+ // Re-export clampLimit for backward compatibility
180
+ export { clampLimit } from './utils.js';
@@ -0,0 +1,353 @@
1
+ /**
2
+ * Memory Lifecycle Management
3
+ * Implements sector-based decay, tier classification, and eviction policies
4
+ *
5
+ * Decay Formula: newScore = importanceScore * (1 - decayRate/100)^days
6
+ * - decayRate: per-memory integer (e.g., 30 = 30% decay per decay cycle)
7
+ * - days: days since lastDecayAt
8
+ * - Tier demotion occurs when score drops below decayThreshold
9
+ * - Cold memories below threshold get status = 'expired'
10
+ */
11
+
12
+ import { and, eq, lt, gte, desc, inArray, gt } from 'drizzle-orm';
13
+ import { getDb } from '../db/index.js';
14
+ import { getSchema } from '../db/schema.js';
15
+ import { config } from '../config.js';
16
+ import { logger } from './logger.js';
17
+ import { triggerTierChange, triggerDecayApplied } from './memory/hooks.js';
18
+
19
+ /**
20
+ * Default decay intervals by sector (days until decay check)
21
+ * These determine how often memories in each sector are evaluated for decay
22
+ * Can be configured via config.sectorDecayIntervals
23
+ */
24
+ const SECTOR_DECAY_INTERVAL_DAYS: Record<string, number> = {
25
+ episodic: config.sectorDecayIntervals?.episodic || 30,
26
+ semantic: config.sectorDecayIntervals?.semantic || 90,
27
+ procedural: config.sectorDecayIntervals?.procedural || 180,
28
+ autobiographical: config.sectorDecayIntervals?.autobiographical || 365,
29
+ working: config.sectorDecayIntervals?.working || 7,
30
+ };
31
+
32
+ /**
33
+ * Default decay rates by memory type (percentage per decay cycle)
34
+ * Used as fallback when memory.decayRate is not set
35
+ */
36
+ const DEFAULT_DECAY_RATES: Record<string, number> = {
37
+ observation: 5,
38
+ fact: 3,
39
+ decision: 2,
40
+ context: 4,
41
+ preference: 3,
42
+ note: 5,
43
+ reflection: 2,
44
+ };
45
+
46
+ const TIER_THRESHOLDS = {
47
+ hot: { recency: 7, coactivation: 10, salience: 70 },
48
+ cold: { recency: Infinity, coactivation: 0, salience: 0 },
49
+ };
50
+
51
+ export interface LifecycleStats {
52
+ decayed: number;
53
+ evicted: number;
54
+ promoted: number;
55
+ tierChanges: { hot: number; cold: number };
56
+ expired: number;
57
+ }
58
+
59
+ /**
60
+ * Run full lifecycle maintenance on all memories
61
+ */
62
+ export async function runLifecycleMaintenance(projectId?: string): Promise<LifecycleStats> {
63
+ if (!config.lifecycleEnabled) {
64
+ return { decayed: 0, evicted: 0, promoted: 0, tierChanges: { hot: 0, cold: 0 }, expired: 0 };
65
+ }
66
+
67
+ const stats: LifecycleStats = {
68
+ decayed: 0,
69
+ evicted: 0,
70
+ promoted: 0,
71
+ tierChanges: { hot: 0, cold: 0 },
72
+ expired: 0,
73
+ };
74
+
75
+ try {
76
+ await applyDecay(projectId, stats);
77
+ await updateTiers(projectId, stats);
78
+ await evictOldMemories(projectId, stats);
79
+ } catch (error) {
80
+ logger.error('Lifecycle maintenance error', error);
81
+ }
82
+
83
+ return stats;
84
+ }
85
+
86
+ /**
87
+ * Apply decay to memories using per-memory decay rate formula
88
+ *
89
+ * Formula: newScore = importanceScore * (1 - decayRate/100)^days
90
+ * - Uses per-memory decayRate (stored as integer percentage, e.g., 30 = 30%)
91
+ * - Calculates days since lastDecayAt
92
+ * - Demotes tier when score drops below decayThreshold
93
+ * - Sets status = 'expired' for cold memories below threshold
94
+ */
95
+ async function applyDecay(projectId: string | undefined, stats: LifecycleStats): Promise<void> {
96
+ try {
97
+ const db = await getDb();
98
+ const schema = await getSchema();
99
+ const now = new Date();
100
+ const decayThreshold = config.decayThreshold || 0.1;
101
+
102
+ // Fetch memories that need decay processing (not protected, not expired)
103
+ let whereClause: any;
104
+ const conditions = [
105
+ eq(schema.memories.isProtected, false),
106
+ eq(schema.memories.status as any, 'active'),
107
+ ];
108
+
109
+ if (projectId) {
110
+ conditions.push(eq(schema.memories.projectId, projectId));
111
+ }
112
+
113
+ whereClause = and(...conditions);
114
+
115
+ const memories = await (db as any)
116
+ .select()
117
+ .from(schema.memories)
118
+ .where(whereClause)
119
+ .limit(10000);
120
+
121
+ let decayed = 0;
122
+ let expired = 0;
123
+ const expiredIds: string[] = [];
124
+
125
+ for (const memory of memories) {
126
+ // Get decay rate for this memory (fallback to type-based default)
127
+ const decayRate = memory.decayRate || DEFAULT_DECAY_RATES[memory.type] || 5;
128
+
129
+ // Calculate days since last decay
130
+ const lastDecayAt = memory.lastDecayAt ? new Date(memory.lastDecayAt) : new Date(memory.createdAt);
131
+ const daysSinceDecay = Math.max(0, (now.getTime() - lastDecayAt.getTime()) / (24 * 60 * 60 * 1000));
132
+
133
+ // Skip if not enough time has passed for this sector
134
+ const sectorInterval = SECTOR_DECAY_INTERVAL_DAYS[memory.sector] || 30;
135
+ if (daysSinceDecay < sectorInterval) {
136
+ continue;
137
+ }
138
+
139
+ // Apply decay formula: newScore = oldScore * (1 - decayRate/100)^days
140
+ const currentScore = memory.importanceScore || memory.relevanceScore || 50;
141
+ const decayMultiplier = Math.pow(1 - decayRate / 100, daysSinceDecay / sectorInterval);
142
+ const newScore = Math.max(0, Math.round(currentScore * decayMultiplier));
143
+
144
+ // Check if memory should expire (cold tier + below threshold)
145
+ const shouldExpire = memory.tier === 'cold' && newScore < (decayThreshold * 100);
146
+
147
+ if (shouldExpire) {
148
+ expiredIds.push(memory.id);
149
+ expired++;
150
+ logger.debug('Memory expiring', { id: memory.id, score: newScore, tier: memory.tier });
151
+ } else if (newScore !== currentScore) {
152
+ // Only update if score changed
153
+ await (db as any)
154
+ .update(schema.memories)
155
+ .set({
156
+ importanceScore: newScore,
157
+ relevanceScore: newScore,
158
+ lastDecayAt: now,
159
+ updatedAt: now,
160
+ })
161
+ .where(eq(schema.memories.id, memory.id));
162
+ decayed++;
163
+
164
+ // Trigger decay applied hook
165
+ try {
166
+ await triggerDecayApplied({
167
+ memoryId: memory.id,
168
+ content: memory.content,
169
+ type: memory.type,
170
+ tags: typeof memory.tags === 'string' ? memory.tags.split(',') : [],
171
+ project: memory.projectId || undefined,
172
+ source: memory.source || undefined,
173
+ tier: memory.tier,
174
+ importance: newScore,
175
+ oldScore: currentScore,
176
+ newScore: newScore,
177
+ });
178
+ } catch (hookError) {
179
+ logger.error('Error triggering decayApplied hook', hookError);
180
+ }
181
+ }
182
+ }
183
+
184
+ // Batch expire memories
185
+ if (expiredIds.length > 0) {
186
+ await (db as any)
187
+ .update(schema.memories)
188
+ .set({
189
+ status: 'expired',
190
+ updatedAt: now,
191
+ })
192
+ .where(inArray(schema.memories.id, expiredIds));
193
+ }
194
+
195
+ stats.decayed += decayed;
196
+ stats.expired += expired;
197
+
198
+ logger.info('Decay applied', { decayed, expired, total: memories.length });
199
+ } catch (error) {
200
+ logger.error('Error applying decay', error);
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Update memory tiers based on recency, coactivation, and salience
206
+ */
207
+ async function updateTiers(projectId: string | undefined, stats: LifecycleStats): Promise<void> {
208
+ try {
209
+ const db = await getDb();
210
+ const schema = await getSchema();
211
+
212
+ const now = new Date();
213
+
214
+ // Fetch all memories to classify
215
+ const where = projectId ? eq(schema.memories.projectId, projectId) : undefined;
216
+ const memories = await (db as any)
217
+ .select()
218
+ .from(schema.memories)
219
+ .where(where)
220
+ .limit(10000); // Process larger batches now
221
+
222
+ // Calculate tiers in memory (simplified: hot or cold only)
223
+ const tierAssignments = new Map<string, 'hot' | 'cold'>();
224
+ const tierCounts = { hot: 0, cold: 0 };
225
+
226
+ for (const memory of memories) {
227
+ const recencyDays = (now.getTime() - new Date(memory.createdAt).getTime()) / (24 * 60 * 60 * 1000);
228
+ const coactivation = memory.coactivationScore || 0;
229
+ const salience = memory.relevanceScore || 50;
230
+
231
+ let newTier: 'hot' | 'cold' = 'cold';
232
+
233
+ if (
234
+ recencyDays <= TIER_THRESHOLDS.hot.recency &&
235
+ coactivation >= TIER_THRESHOLDS.hot.coactivation &&
236
+ salience >= TIER_THRESHOLDS.hot.salience
237
+ ) {
238
+ newTier = 'hot';
239
+ }
240
+
241
+ if (newTier !== memory.tier) {
242
+ tierAssignments.set(memory.id, newTier);
243
+ tierCounts[newTier]++;
244
+ }
245
+ }
246
+
247
+ if (tierAssignments.size === 0) return;
248
+
249
+ // Group by tier for efficient batched updates
250
+ const hotIds = Array.from(tierAssignments.entries())
251
+ .filter(([_, tier]) => tier === 'hot')
252
+ .map(([id]) => id);
253
+ const coldIds = Array.from(tierAssignments.entries())
254
+ .filter(([_, tier]) => tier === 'cold')
255
+ .map(([id]) => id);
256
+
257
+ // Execute batched updates instead of individual queries
258
+ if (hotIds.length > 0) {
259
+ await (db as any)
260
+ .update(schema.memories)
261
+ .set({ tier: 'hot', updatedAt: now })
262
+ .where(inArray(schema.memories.id, hotIds));
263
+ }
264
+
265
+ if (coldIds.length > 0) {
266
+ await (db as any)
267
+ .update(schema.memories)
268
+ .set({ tier: 'cold', updatedAt: now })
269
+ .where(inArray(schema.memories.id, coldIds));
270
+ }
271
+
272
+ if (coldIds.length > 0) {
273
+ await (db as any)
274
+ .update(schema.memories)
275
+ .set({ tier: 'cold', updatedAt: now })
276
+ .where(inArray(schema.memories.id, coldIds));
277
+ }
278
+
279
+ // Trigger tier change hooks for each memory that changed tier
280
+ if (tierAssignments.size > 0) {
281
+ for (const memory of memories) {
282
+ const newTier = tierAssignments.get(memory.id);
283
+ if (newTier && newTier !== memory.tier) {
284
+ try {
285
+ await triggerTierChange({
286
+ memoryId: memory.id,
287
+ content: memory.content,
288
+ type: memory.type,
289
+ tags: typeof memory.tags === 'string' ? memory.tags.split(',') : [],
290
+ project: memory.projectId || undefined,
291
+ source: memory.source || undefined,
292
+ tier: newTier,
293
+ importance: memory.importanceScore || memory.relevanceScore || 50,
294
+ oldTier: memory.tier,
295
+ newTier: newTier,
296
+ });
297
+ } catch (hookError) {
298
+ logger.error('Error triggering tierChange hook', hookError);
299
+ }
300
+ }
301
+ }
302
+ }
303
+
304
+ // Update stats
305
+ stats.tierChanges.hot = tierCounts.hot;
306
+ stats.tierChanges.cold = tierCounts.cold;
307
+
308
+ logger.debug('Tier updates complete', {
309
+ hot: tierCounts.hot,
310
+ cold: tierCounts.cold,
311
+ });
312
+ } catch (error) {
313
+ logger.error('Error updating tiers', error);
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Evict old, cold memories with low relevance
319
+ */
320
+ async function evictOldMemories(projectId: string | undefined, stats: LifecycleStats): Promise<void> {
321
+ try {
322
+ const db = await getDb();
323
+ const schema = await getSchema();
324
+
325
+ const evictionThreshold = new Date(Date.now() - 180 * 24 * 60 * 60 * 1000); // 180 days
326
+
327
+ const where = projectId
328
+ ? and(
329
+ eq(schema.memories.projectId, projectId),
330
+ lt(schema.memories.createdAt as any, evictionThreshold),
331
+ eq(schema.memories.isProtected, false),
332
+ eq(schema.memories.isPinned, false),
333
+ eq(schema.memories.tier as any, 'cold'),
334
+ lt(schema.memories.relevanceScore as any, 20) // Very low relevance
335
+ )
336
+ : and(
337
+ lt(schema.memories.createdAt as any, evictionThreshold),
338
+ eq(schema.memories.isProtected, false),
339
+ eq(schema.memories.isPinned, false),
340
+ eq(schema.memories.tier as any, 'cold'),
341
+ lt(schema.memories.relevanceScore as any, 20)
342
+ );
343
+
344
+ const result = await (db as any).delete(schema.memories).where(where);
345
+ stats.evicted = result?.rowCount || 0;
346
+ } catch (error) {
347
+ logger.error('Error evicting memories', error);
348
+ }
349
+ }
350
+
351
+
352
+
353
+