@ixo/oracle-runtime 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (583) hide show
  1. package/dist/bootstrap/ambient-factory.d.ts +32 -0
  2. package/dist/bootstrap/ambient-factory.d.ts.map +1 -0
  3. package/dist/bootstrap/ambient-factory.js +128 -0
  4. package/dist/bootstrap/create-oracle-app.d.ts +112 -0
  5. package/dist/bootstrap/create-oracle-app.d.ts.map +1 -0
  6. package/dist/bootstrap/create-oracle-app.js +530 -0
  7. package/dist/bootstrap/graceful-shutdown.d.ts +20 -0
  8. package/dist/bootstrap/graceful-shutdown.d.ts.map +1 -0
  9. package/dist/bootstrap/graceful-shutdown.js +61 -0
  10. package/dist/bootstrap/index.d.ts +13 -0
  11. package/dist/bootstrap/index.d.ts.map +1 -0
  12. package/dist/bootstrap/index.js +6 -0
  13. package/dist/bootstrap/inspect.d.ts +74 -0
  14. package/dist/bootstrap/inspect.d.ts.map +1 -0
  15. package/dist/bootstrap/inspect.js +111 -0
  16. package/dist/bootstrap/plugin-loader.d.ts +50 -0
  17. package/dist/bootstrap/plugin-loader.d.ts.map +1 -0
  18. package/dist/bootstrap/plugin-loader.js +119 -0
  19. package/dist/bootstrap/runtime-app-module.d.ts +38 -0
  20. package/dist/bootstrap/runtime-app-module.d.ts.map +1 -0
  21. package/dist/bootstrap/runtime-app-module.js +114 -0
  22. package/dist/bootstrap/schema-composer.d.ts +46 -0
  23. package/dist/bootstrap/schema-composer.d.ts.map +1 -0
  24. package/dist/bootstrap/schema-composer.js +65 -0
  25. package/dist/config/base-env-config.d.ts +31 -0
  26. package/dist/config/base-env-config.d.ts.map +1 -0
  27. package/dist/config/base-env-config.js +70 -0
  28. package/dist/config/base-env-schema.d.ts +77 -0
  29. package/dist/config/base-env-schema.d.ts.map +1 -0
  30. package/dist/config/base-env-schema.js +102 -0
  31. package/dist/events/scoped-emitter.d.ts +33 -0
  32. package/dist/events/scoped-emitter.d.ts.map +1 -0
  33. package/dist/events/scoped-emitter.js +32 -0
  34. package/dist/graph/index.d.ts +8 -0
  35. package/dist/graph/index.d.ts.map +1 -0
  36. package/dist/graph/index.js +6 -0
  37. package/dist/graph/main-agent-types.d.ts +88 -0
  38. package/dist/graph/main-agent-types.d.ts.map +1 -0
  39. package/dist/graph/main-agent-types.js +20 -0
  40. package/dist/graph/main-agent.d.ts +16 -0
  41. package/dist/graph/main-agent.d.ts.map +1 -0
  42. package/dist/graph/main-agent.js +251 -0
  43. package/dist/graph/middlewares/capability-gate-middleware.d.ts +35 -0
  44. package/dist/graph/middlewares/capability-gate-middleware.d.ts.map +1 -0
  45. package/dist/graph/middlewares/capability-gate-middleware.js +54 -0
  46. package/dist/graph/middlewares/index.d.ts +7 -0
  47. package/dist/graph/middlewares/index.d.ts.map +1 -0
  48. package/dist/graph/middlewares/index.js +6 -0
  49. package/dist/graph/middlewares/page-context-middleware.d.ts +23 -0
  50. package/dist/graph/middlewares/page-context-middleware.d.ts.map +1 -0
  51. package/dist/graph/middlewares/page-context-middleware.js +68 -0
  52. package/dist/graph/middlewares/safety-guardrail-middleware.d.ts +26 -0
  53. package/dist/graph/middlewares/safety-guardrail-middleware.d.ts.map +1 -0
  54. package/dist/graph/middlewares/safety-guardrail-middleware.js +88 -0
  55. package/dist/graph/middlewares/summarization-middleware.d.ts +17 -0
  56. package/dist/graph/middlewares/summarization-middleware.d.ts.map +1 -0
  57. package/dist/graph/middlewares/summarization-middleware.js +62 -0
  58. package/dist/graph/middlewares/tool-repetition-guard-middleware.d.ts +24 -0
  59. package/dist/graph/middlewares/tool-repetition-guard-middleware.d.ts.map +1 -0
  60. package/dist/graph/middlewares/tool-repetition-guard-middleware.js +112 -0
  61. package/dist/graph/middlewares/tool-validation-middleware.d.ts +24 -0
  62. package/dist/graph/middlewares/tool-validation-middleware.d.ts.map +1 -0
  63. package/dist/graph/middlewares/tool-validation-middleware.js +61 -0
  64. package/dist/graph/prompt-composer.d.ts +69 -0
  65. package/dist/graph/prompt-composer.d.ts.map +1 -0
  66. package/dist/graph/prompt-composer.js +315 -0
  67. package/dist/graph/state.d.ts +65 -0
  68. package/dist/graph/state.d.ts.map +1 -0
  69. package/dist/graph/state.js +60 -0
  70. package/dist/graph/sub-agent-fallback.d.ts +56 -0
  71. package/dist/graph/sub-agent-fallback.d.ts.map +1 -0
  72. package/dist/graph/sub-agent-fallback.js +79 -0
  73. package/dist/graph/subagent-as-tool.d.ts +77 -0
  74. package/dist/graph/subagent-as-tool.d.ts.map +1 -0
  75. package/dist/graph/subagent-as-tool.js +197 -0
  76. package/dist/graph/wrap-plugin-tool.d.ts +28 -0
  77. package/dist/graph/wrap-plugin-tool.d.ts.map +1 -0
  78. package/dist/graph/wrap-plugin-tool.js +30 -0
  79. package/dist/index.d.ts +33 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +28 -0
  82. package/dist/llm/index.d.ts +2 -0
  83. package/dist/llm/index.d.ts.map +1 -0
  84. package/dist/llm/index.js +1 -0
  85. package/dist/llm/llm-provider.d.ts +41 -0
  86. package/dist/llm/llm-provider.d.ts.map +1 -0
  87. package/dist/llm/llm-provider.js +130 -0
  88. package/dist/manifest/index.d.ts +6 -0
  89. package/dist/manifest/index.d.ts.map +1 -0
  90. package/dist/manifest/index.js +3 -0
  91. package/dist/manifest/schema.d.ts +31 -0
  92. package/dist/manifest/schema.d.ts.map +1 -0
  93. package/dist/manifest/schema.js +44 -0
  94. package/dist/manifest/tier1-renderer.d.ts +31 -0
  95. package/dist/manifest/tier1-renderer.d.ts.map +1 -0
  96. package/dist/manifest/tier1-renderer.js +68 -0
  97. package/dist/manifest/validator.d.ts +34 -0
  98. package/dist/manifest/validator.d.ts.map +1 -0
  99. package/dist/manifest/validator.js +111 -0
  100. package/dist/matrix/checkpointer/matrix-upload-utils.d.ts +66 -0
  101. package/dist/matrix/checkpointer/matrix-upload-utils.d.ts.map +1 -0
  102. package/dist/matrix/checkpointer/matrix-upload-utils.js +228 -0
  103. package/dist/matrix/checkpointer/type.d.ts +4 -0
  104. package/dist/matrix/checkpointer/type.d.ts.map +1 -0
  105. package/dist/matrix/checkpointer/type.js +1 -0
  106. package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.module.d.ts +3 -0
  107. package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.module.d.ts.map +1 -0
  108. package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.module.js +22 -0
  109. package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.service.d.ts +93 -0
  110. package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.service.d.ts.map +1 -0
  111. package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.service.js +856 -0
  112. package/dist/matrix/room-membership.d.ts +11 -0
  113. package/dist/matrix/room-membership.d.ts.map +1 -0
  114. package/dist/matrix/room-membership.js +33 -0
  115. package/dist/meta-tools/index.d.ts +28 -0
  116. package/dist/meta-tools/index.d.ts.map +1 -0
  117. package/dist/meta-tools/index.js +24 -0
  118. package/dist/meta-tools/list-capabilities.d.ts +12 -0
  119. package/dist/meta-tools/list-capabilities.d.ts.map +1 -0
  120. package/dist/meta-tools/list-capabilities.js +55 -0
  121. package/dist/meta-tools/load-capability.d.ts +24 -0
  122. package/dist/meta-tools/load-capability.d.ts.map +1 -0
  123. package/dist/meta-tools/load-capability.js +82 -0
  124. package/dist/modules/auth/auth-header.middleware.d.ts +37 -0
  125. package/dist/modules/auth/auth-header.middleware.d.ts.map +1 -0
  126. package/dist/modules/auth/auth-header.middleware.js +115 -0
  127. package/dist/modules/auth/auth.module.d.ts +9 -0
  128. package/dist/modules/auth/auth.module.d.ts.map +1 -0
  129. package/dist/modules/auth/auth.module.js +27 -0
  130. package/dist/modules/auth/index.d.ts +3 -0
  131. package/dist/modules/auth/index.d.ts.map +1 -0
  132. package/dist/modules/auth/index.js +2 -0
  133. package/dist/modules/auth/validate-ucan-delegation.d.ts +31 -0
  134. package/dist/modules/auth/validate-ucan-delegation.d.ts.map +1 -0
  135. package/dist/modules/auth/validate-ucan-delegation.js +36 -0
  136. package/dist/modules/blob-store/blob-store.module.d.ts +10 -0
  137. package/dist/modules/blob-store/blob-store.module.d.ts.map +1 -0
  138. package/dist/modules/blob-store/blob-store.module.js +25 -0
  139. package/dist/modules/blob-store/blob-store.service.d.ts +66 -0
  140. package/dist/modules/blob-store/blob-store.service.d.ts.map +1 -0
  141. package/dist/modules/blob-store/blob-store.service.js +108 -0
  142. package/dist/modules/blob-store/index.d.ts +3 -0
  143. package/dist/modules/blob-store/index.d.ts.map +1 -0
  144. package/dist/modules/blob-store/index.js +2 -0
  145. package/dist/modules/health/health.controller.d.ts +18 -0
  146. package/dist/modules/health/health.controller.d.ts.map +1 -0
  147. package/dist/modules/health/health.controller.js +48 -0
  148. package/dist/modules/health/health.module.d.ts +8 -0
  149. package/dist/modules/health/health.module.d.ts.map +1 -0
  150. package/dist/modules/health/health.module.js +21 -0
  151. package/dist/modules/index.d.ts +8 -0
  152. package/dist/modules/index.d.ts.map +1 -0
  153. package/dist/modules/index.js +6 -0
  154. package/dist/modules/messages/__test-fixtures__/deps.d.ts +41 -0
  155. package/dist/modules/messages/__test-fixtures__/deps.d.ts.map +1 -0
  156. package/dist/modules/messages/__test-fixtures__/deps.js +73 -0
  157. package/dist/modules/messages/__test-fixtures__/fake-agent.d.ts +27 -0
  158. package/dist/modules/messages/__test-fixtures__/fake-agent.d.ts.map +1 -0
  159. package/dist/modules/messages/__test-fixtures__/fake-agent.js +41 -0
  160. package/dist/modules/messages/__test-fixtures__/fake-response.d.ts +29 -0
  161. package/dist/modules/messages/__test-fixtures__/fake-response.d.ts.map +1 -0
  162. package/dist/modules/messages/__test-fixtures__/fake-response.js +55 -0
  163. package/dist/modules/messages/agent-builder.d.ts +64 -0
  164. package/dist/modules/messages/agent-builder.d.ts.map +1 -0
  165. package/dist/modules/messages/agent-builder.js +219 -0
  166. package/dist/modules/messages/batch-invoker.d.ts +36 -0
  167. package/dist/modules/messages/batch-invoker.d.ts.map +1 -0
  168. package/dist/modules/messages/batch-invoker.js +58 -0
  169. package/dist/modules/messages/dto/list-messages.dto.d.ts +4 -0
  170. package/dist/modules/messages/dto/list-messages.dto.d.ts.map +1 -0
  171. package/dist/modules/messages/dto/list-messages.dto.js +17 -0
  172. package/dist/modules/messages/dto/send-message.dto.d.ts +73 -0
  173. package/dist/modules/messages/dto/send-message.dto.d.ts.map +1 -0
  174. package/dist/modules/messages/dto/send-message.dto.js +318 -0
  175. package/dist/modules/messages/file-processing-credit-sink.port.d.ts +20 -0
  176. package/dist/modules/messages/file-processing-credit-sink.port.d.ts.map +1 -0
  177. package/dist/modules/messages/file-processing-credit-sink.port.js +1 -0
  178. package/dist/modules/messages/file-processing.service.d.ts +195 -0
  179. package/dist/modules/messages/file-processing.service.d.ts.map +1 -0
  180. package/dist/modules/messages/file-processing.service.js +1278 -0
  181. package/dist/modules/messages/homeserver-cache.d.ts +11 -0
  182. package/dist/modules/messages/homeserver-cache.d.ts.map +1 -0
  183. package/dist/modules/messages/homeserver-cache.js +31 -0
  184. package/dist/modules/messages/matrix-listener-bridge.d.ts +63 -0
  185. package/dist/modules/messages/matrix-listener-bridge.d.ts.map +1 -0
  186. package/dist/modules/messages/matrix-listener-bridge.js +280 -0
  187. package/dist/modules/messages/messages.controller.d.ts +13 -0
  188. package/dist/modules/messages/messages.controller.d.ts.map +1 -0
  189. package/dist/modules/messages/messages.controller.js +95 -0
  190. package/dist/modules/messages/messages.module.d.ts +3 -0
  191. package/dist/modules/messages/messages.module.d.ts.map +1 -0
  192. package/dist/modules/messages/messages.module.js +75 -0
  193. package/dist/modules/messages/messages.service.d.ts +112 -0
  194. package/dist/modules/messages/messages.service.d.ts.map +1 -0
  195. package/dist/modules/messages/messages.service.js +279 -0
  196. package/dist/modules/messages/oracle-runtime-bundle.d.ts +38 -0
  197. package/dist/modules/messages/oracle-runtime-bundle.d.ts.map +1 -0
  198. package/dist/modules/messages/oracle-runtime-bundle.js +40 -0
  199. package/dist/modules/messages/post-message-syncer.d.ts +30 -0
  200. package/dist/modules/messages/post-message-syncer.d.ts.map +1 -0
  201. package/dist/modules/messages/post-message-syncer.js +73 -0
  202. package/dist/modules/messages/request-preparer.d.ts +53 -0
  203. package/dist/modules/messages/request-preparer.d.ts.map +1 -0
  204. package/dist/modules/messages/request-preparer.js +139 -0
  205. package/dist/modules/messages/sse-stream-runner.d.ts +73 -0
  206. package/dist/modules/messages/sse-stream-runner.d.ts.map +1 -0
  207. package/dist/modules/messages/sse-stream-runner.js +352 -0
  208. package/dist/modules/messages/sse.utils.d.ts +29 -0
  209. package/dist/modules/messages/sse.utils.d.ts.map +1 -0
  210. package/dist/modules/messages/sse.utils.js +77 -0
  211. package/dist/modules/messages/user-context-fetcher.d.ts +41 -0
  212. package/dist/modules/messages/user-context-fetcher.d.ts.map +1 -0
  213. package/dist/modules/messages/user-context-fetcher.js +117 -0
  214. package/dist/modules/secrets/index.d.ts +2 -0
  215. package/dist/modules/secrets/index.d.ts.map +1 -0
  216. package/dist/modules/secrets/index.js +1 -0
  217. package/dist/modules/secrets/secrets.service.d.ts +29 -0
  218. package/dist/modules/secrets/secrets.service.d.ts.map +1 -0
  219. package/dist/modules/secrets/secrets.service.js +107 -0
  220. package/dist/modules/sessions/dto/create-session.dto.d.ts +6 -0
  221. package/dist/modules/sessions/dto/create-session.dto.d.ts.map +1 -0
  222. package/dist/modules/sessions/dto/create-session.dto.js +5 -0
  223. package/dist/modules/sessions/dto/delete-session.dto.d.ts +6 -0
  224. package/dist/modules/sessions/dto/delete-session.dto.d.ts.map +1 -0
  225. package/dist/modules/sessions/dto/delete-session.dto.js +5 -0
  226. package/dist/modules/sessions/dto/list-sessions.dto.d.ts +7 -0
  227. package/dist/modules/sessions/dto/list-sessions.dto.d.ts.map +1 -0
  228. package/dist/modules/sessions/dto/list-sessions.dto.js +6 -0
  229. package/dist/modules/sessions/session-history-processor.service.d.ts +48 -0
  230. package/dist/modules/sessions/session-history-processor.service.d.ts.map +1 -0
  231. package/dist/modules/sessions/session-history-processor.service.js +254 -0
  232. package/dist/modules/sessions/sessions.controller.d.ts +13 -0
  233. package/dist/modules/sessions/sessions.controller.d.ts.map +1 -0
  234. package/dist/modules/sessions/sessions.controller.js +113 -0
  235. package/dist/modules/sessions/sessions.module.d.ts +3 -0
  236. package/dist/modules/sessions/sessions.module.d.ts.map +1 -0
  237. package/dist/modules/sessions/sessions.module.js +49 -0
  238. package/dist/modules/sessions/sessions.service.d.ts +23 -0
  239. package/dist/modules/sessions/sessions.service.d.ts.map +1 -0
  240. package/dist/modules/sessions/sessions.service.js +168 -0
  241. package/dist/modules/subscription/subscription.middleware.d.ts +37 -0
  242. package/dist/modules/subscription/subscription.middleware.d.ts.map +1 -0
  243. package/dist/modules/subscription/subscription.middleware.js +141 -0
  244. package/dist/modules/subscription/subscription.module.d.ts +12 -0
  245. package/dist/modules/subscription/subscription.module.d.ts.map +1 -0
  246. package/dist/modules/subscription/subscription.module.js +26 -0
  247. package/dist/modules/throttler/throttler.module.d.ts +11 -0
  248. package/dist/modules/throttler/throttler.module.d.ts.map +1 -0
  249. package/dist/modules/throttler/throttler.module.js +32 -0
  250. package/dist/modules/ucan/index.d.ts +7 -0
  251. package/dist/modules/ucan/index.d.ts.map +1 -0
  252. package/dist/modules/ucan/index.js +6 -0
  253. package/dist/modules/ucan/ucan.config.d.ts +87 -0
  254. package/dist/modules/ucan/ucan.config.d.ts.map +1 -0
  255. package/dist/modules/ucan/ucan.config.js +114 -0
  256. package/dist/modules/ucan/ucan.module.d.ts +29 -0
  257. package/dist/modules/ucan/ucan.module.d.ts.map +1 -0
  258. package/dist/modules/ucan/ucan.module.js +46 -0
  259. package/dist/modules/ucan/ucan.service.d.ts +129 -0
  260. package/dist/modules/ucan/ucan.service.d.ts.map +1 -0
  261. package/dist/modules/ucan/ucan.service.js +582 -0
  262. package/dist/modules/ws/emitter.d.ts +12 -0
  263. package/dist/modules/ws/emitter.d.ts.map +1 -0
  264. package/dist/modules/ws/emitter.js +12 -0
  265. package/dist/modules/ws/ws.gateway.d.ts +34 -0
  266. package/dist/modules/ws/ws.gateway.d.ts.map +1 -0
  267. package/dist/modules/ws/ws.gateway.js +241 -0
  268. package/dist/modules/ws/ws.module.d.ts +3 -0
  269. package/dist/modules/ws/ws.module.d.ts.map +1 -0
  270. package/dist/modules/ws/ws.module.js +21 -0
  271. package/dist/modules/ws/ws.service.d.ts +25 -0
  272. package/dist/modules/ws/ws.service.d.ts.map +1 -0
  273. package/dist/modules/ws/ws.service.js +113 -0
  274. package/dist/plugin-api/define-plugin.d.ts +19 -0
  275. package/dist/plugin-api/define-plugin.d.ts.map +1 -0
  276. package/dist/plugin-api/define-plugin.js +25 -0
  277. package/dist/plugin-api/oracle-plugin.d.ts +92 -0
  278. package/dist/plugin-api/oracle-plugin.d.ts.map +1 -0
  279. package/dist/plugin-api/oracle-plugin.js +15 -0
  280. package/dist/plugin-api/tool-helper.d.ts +33 -0
  281. package/dist/plugin-api/tool-helper.d.ts.map +1 -0
  282. package/dist/plugin-api/tool-helper.js +40 -0
  283. package/dist/plugin-api/types.d.ts +378 -0
  284. package/dist/plugin-api/types.d.ts.map +1 -0
  285. package/dist/plugin-api/types.js +1 -0
  286. package/dist/plugins/agui/agui-agent.d.ts +10 -0
  287. package/dist/plugins/agui/agui-agent.d.ts.map +1 -0
  288. package/dist/plugins/agui/agui-agent.js +110 -0
  289. package/dist/plugins/agui/agui.plugin.d.ts +16 -0
  290. package/dist/plugins/agui/agui.plugin.d.ts.map +1 -0
  291. package/dist/plugins/agui/agui.plugin.js +117 -0
  292. package/dist/plugins/agui/index.d.ts +3 -0
  293. package/dist/plugins/agui/index.d.ts.map +1 -0
  294. package/dist/plugins/agui/index.js +2 -0
  295. package/dist/plugins/composio/composio-tools.d.ts +59 -0
  296. package/dist/plugins/composio/composio-tools.d.ts.map +1 -0
  297. package/dist/plugins/composio/composio-tools.js +88 -0
  298. package/dist/plugins/composio/composio-ucan.d.ts +14 -0
  299. package/dist/plugins/composio/composio-ucan.d.ts.map +1 -0
  300. package/dist/plugins/composio/composio-ucan.js +32 -0
  301. package/dist/plugins/composio/composio.plugin.d.ts +45 -0
  302. package/dist/plugins/composio/composio.plugin.d.ts.map +1 -0
  303. package/dist/plugins/composio/composio.plugin.js +116 -0
  304. package/dist/plugins/composio/index.d.ts +4 -0
  305. package/dist/plugins/composio/index.d.ts.map +1 -0
  306. package/dist/plugins/composio/index.js +3 -0
  307. package/dist/plugins/credits/claim-processing.module.d.ts +28 -0
  308. package/dist/plugins/credits/claim-processing.module.d.ts.map +1 -0
  309. package/dist/plugins/credits/claim-processing.module.js +37 -0
  310. package/dist/plugins/credits/claim-processing.service.d.ts +92 -0
  311. package/dist/plugins/credits/claim-processing.service.d.ts.map +1 -0
  312. package/dist/plugins/credits/claim-processing.service.js +406 -0
  313. package/dist/plugins/credits/credits-middleware.d.ts +23 -0
  314. package/dist/plugins/credits/credits-middleware.d.ts.map +1 -0
  315. package/dist/plugins/credits/credits-middleware.js +154 -0
  316. package/dist/plugins/credits/credits.plugin.d.ts +55 -0
  317. package/dist/plugins/credits/credits.plugin.d.ts.map +1 -0
  318. package/dist/plugins/credits/credits.plugin.js +95 -0
  319. package/dist/plugins/credits/file-processing-sink.module.d.ts +18 -0
  320. package/dist/plugins/credits/file-processing-sink.module.d.ts.map +1 -0
  321. package/dist/plugins/credits/file-processing-sink.module.js +56 -0
  322. package/dist/plugins/credits/index.d.ts +8 -0
  323. package/dist/plugins/credits/index.d.ts.map +1 -0
  324. package/dist/plugins/credits/index.js +7 -0
  325. package/dist/plugins/credits/subscription-sink.module.d.ts +23 -0
  326. package/dist/plugins/credits/subscription-sink.module.d.ts.map +1 -0
  327. package/dist/plugins/credits/subscription-sink.module.js +54 -0
  328. package/dist/plugins/credits/token-limiter.d.ts +139 -0
  329. package/dist/plugins/credits/token-limiter.d.ts.map +1 -0
  330. package/dist/plugins/credits/token-limiter.js +302 -0
  331. package/dist/plugins/domain-indexer/domain-indexer-agent.d.ts +7 -0
  332. package/dist/plugins/domain-indexer/domain-indexer-agent.d.ts.map +1 -0
  333. package/dist/plugins/domain-indexer/domain-indexer-agent.js +48 -0
  334. package/dist/plugins/domain-indexer/domain-indexer-tools.d.ts +8 -0
  335. package/dist/plugins/domain-indexer/domain-indexer-tools.d.ts.map +1 -0
  336. package/dist/plugins/domain-indexer/domain-indexer-tools.js +194 -0
  337. package/dist/plugins/domain-indexer/domain-indexer.plugin.d.ts +20 -0
  338. package/dist/plugins/domain-indexer/domain-indexer.plugin.d.ts.map +1 -0
  339. package/dist/plugins/domain-indexer/domain-indexer.plugin.js +78 -0
  340. package/dist/plugins/domain-indexer/index.d.ts +2 -0
  341. package/dist/plugins/domain-indexer/index.d.ts.map +1 -0
  342. package/dist/plugins/domain-indexer/index.js +1 -0
  343. package/dist/plugins/editor/apply-sandbox-output.d.ts +33 -0
  344. package/dist/plugins/editor/apply-sandbox-output.d.ts.map +1 -0
  345. package/dist/plugins/editor/apply-sandbox-output.js +302 -0
  346. package/dist/plugins/editor/block-actions.d.ts +84 -0
  347. package/dist/plugins/editor/block-actions.d.ts.map +1 -0
  348. package/dist/plugins/editor/block-actions.js +471 -0
  349. package/dist/plugins/editor/blocknote-helper.d.ts +147 -0
  350. package/dist/plugins/editor/blocknote-helper.d.ts.map +1 -0
  351. package/dist/plugins/editor/blocknote-helper.js +542 -0
  352. package/dist/plugins/editor/blocknote-tools.d.ts +364 -0
  353. package/dist/plugins/editor/blocknote-tools.d.ts.map +1 -0
  354. package/dist/plugins/editor/blocknote-tools.js +2123 -0
  355. package/dist/plugins/editor/editor-agent.d.ts +47 -0
  356. package/dist/plugins/editor/editor-agent.d.ts.map +1 -0
  357. package/dist/plugins/editor/editor-agent.js +158 -0
  358. package/dist/plugins/editor/editor-mx.d.ts +56 -0
  359. package/dist/plugins/editor/editor-mx.d.ts.map +1 -0
  360. package/dist/plugins/editor/editor-mx.js +100 -0
  361. package/dist/plugins/editor/editor.plugin.d.ts +32 -0
  362. package/dist/plugins/editor/editor.plugin.d.ts.map +1 -0
  363. package/dist/plugins/editor/editor.plugin.js +189 -0
  364. package/dist/plugins/editor/index.d.ts +6 -0
  365. package/dist/plugins/editor/index.d.ts.map +1 -0
  366. package/dist/plugins/editor/index.js +5 -0
  367. package/dist/plugins/editor/mint-invocation-tool.d.ts +72 -0
  368. package/dist/plugins/editor/mint-invocation-tool.d.ts.map +1 -0
  369. package/dist/plugins/editor/mint-invocation-tool.js +173 -0
  370. package/dist/plugins/editor/page-functions.d.ts +100 -0
  371. package/dist/plugins/editor/page-functions.d.ts.map +1 -0
  372. package/dist/plugins/editor/page-functions.js +317 -0
  373. package/dist/plugins/editor/page-tools.d.ts +75 -0
  374. package/dist/plugins/editor/page-tools.d.ts.map +1 -0
  375. package/dist/plugins/editor/page-tools.js +238 -0
  376. package/dist/plugins/editor/prompts.d.ts +22 -0
  377. package/dist/plugins/editor/prompts.d.ts.map +1 -0
  378. package/dist/plugins/editor/prompts.js +451 -0
  379. package/dist/plugins/editor/provider.d.ts +101 -0
  380. package/dist/plugins/editor/provider.d.ts.map +1 -0
  381. package/dist/plugins/editor/provider.js +249 -0
  382. package/dist/plugins/editor/standalone-editor-tool.d.ts +17 -0
  383. package/dist/plugins/editor/standalone-editor-tool.d.ts.map +1 -0
  384. package/dist/plugins/editor/standalone-editor-tool.js +136 -0
  385. package/dist/plugins/editor/survey-helpers.d.ts +112 -0
  386. package/dist/plugins/editor/survey-helpers.d.ts.map +1 -0
  387. package/dist/plugins/editor/survey-helpers.js +358 -0
  388. package/dist/plugins/firecrawl/firecrawl-agent.d.ts +7 -0
  389. package/dist/plugins/firecrawl/firecrawl-agent.d.ts.map +1 -0
  390. package/dist/plugins/firecrawl/firecrawl-agent.js +84 -0
  391. package/dist/plugins/firecrawl/firecrawl-tools.d.ts +37 -0
  392. package/dist/plugins/firecrawl/firecrawl-tools.d.ts.map +1 -0
  393. package/dist/plugins/firecrawl/firecrawl-tools.js +129 -0
  394. package/dist/plugins/firecrawl/firecrawl.plugin.d.ts +32 -0
  395. package/dist/plugins/firecrawl/firecrawl.plugin.d.ts.map +1 -0
  396. package/dist/plugins/firecrawl/firecrawl.plugin.js +69 -0
  397. package/dist/plugins/firecrawl/index.d.ts +3 -0
  398. package/dist/plugins/firecrawl/index.d.ts.map +1 -0
  399. package/dist/plugins/firecrawl/index.js +2 -0
  400. package/dist/plugins/index.d.ts +47 -0
  401. package/dist/plugins/index.d.ts.map +1 -0
  402. package/dist/plugins/index.js +69 -0
  403. package/dist/plugins/matrix-group-chats/channel-memory.module.d.ts +7 -0
  404. package/dist/plugins/matrix-group-chats/channel-memory.module.d.ts.map +1 -0
  405. package/dist/plugins/matrix-group-chats/channel-memory.module.js +22 -0
  406. package/dist/plugins/matrix-group-chats/channel-memory.repo.d.ts +62 -0
  407. package/dist/plugins/matrix-group-chats/channel-memory.repo.d.ts.map +1 -0
  408. package/dist/plugins/matrix-group-chats/channel-memory.repo.js +311 -0
  409. package/dist/plugins/matrix-group-chats/channel-memory.service.d.ts +89 -0
  410. package/dist/plugins/matrix-group-chats/channel-memory.service.d.ts.map +1 -0
  411. package/dist/plugins/matrix-group-chats/channel-memory.service.js +565 -0
  412. package/dist/plugins/matrix-group-chats/channel-memory.summarizer.d.ts +18 -0
  413. package/dist/plugins/matrix-group-chats/channel-memory.summarizer.d.ts.map +1 -0
  414. package/dist/plugins/matrix-group-chats/channel-memory.summarizer.js +128 -0
  415. package/dist/plugins/matrix-group-chats/channel-memory.types.d.ts +50 -0
  416. package/dist/plugins/matrix-group-chats/channel-memory.types.d.ts.map +1 -0
  417. package/dist/plugins/matrix-group-chats/channel-memory.types.js +2 -0
  418. package/dist/plugins/matrix-group-chats/guard.d.ts +83 -0
  419. package/dist/plugins/matrix-group-chats/guard.d.ts.map +1 -0
  420. package/dist/plugins/matrix-group-chats/guard.js +138 -0
  421. package/dist/plugins/matrix-group-chats/index.d.ts +59 -0
  422. package/dist/plugins/matrix-group-chats/index.d.ts.map +1 -0
  423. package/dist/plugins/matrix-group-chats/index.js +140 -0
  424. package/dist/plugins/matrix-group-chats/middleware.d.ts +31 -0
  425. package/dist/plugins/matrix-group-chats/middleware.d.ts.map +1 -0
  426. package/dist/plugins/matrix-group-chats/middleware.js +186 -0
  427. package/dist/plugins/matrix-group-chats/power-levels.d.ts +15 -0
  428. package/dist/plugins/matrix-group-chats/power-levels.d.ts.map +1 -0
  429. package/dist/plugins/matrix-group-chats/power-levels.js +36 -0
  430. package/dist/plugins/matrix-group-chats/room-info.d.ts +23 -0
  431. package/dist/plugins/matrix-group-chats/room-info.d.ts.map +1 -0
  432. package/dist/plugins/matrix-group-chats/room-info.js +34 -0
  433. package/dist/plugins/matrix-group-chats/tools.d.ts +8 -0
  434. package/dist/plugins/matrix-group-chats/tools.d.ts.map +1 -0
  435. package/dist/plugins/matrix-group-chats/tools.js +154 -0
  436. package/dist/plugins/memory/index.d.ts +5 -0
  437. package/dist/plugins/memory/index.d.ts.map +1 -0
  438. package/dist/plugins/memory/index.js +3 -0
  439. package/dist/plugins/memory/memory-tools.d.ts +52 -0
  440. package/dist/plugins/memory/memory-tools.d.ts.map +1 -0
  441. package/dist/plugins/memory/memory-tools.js +88 -0
  442. package/dist/plugins/memory/memory-ucan.d.ts +17 -0
  443. package/dist/plugins/memory/memory-ucan.d.ts.map +1 -0
  444. package/dist/plugins/memory/memory-ucan.js +43 -0
  445. package/dist/plugins/memory/memory.plugin.d.ts +51 -0
  446. package/dist/plugins/memory/memory.plugin.d.ts.map +1 -0
  447. package/dist/plugins/memory/memory.plugin.js +76 -0
  448. package/dist/plugins/memory/types.d.ts +75 -0
  449. package/dist/plugins/memory/types.d.ts.map +1 -0
  450. package/dist/plugins/memory/types.js +2 -0
  451. package/dist/plugins/portal/index.d.ts +3 -0
  452. package/dist/plugins/portal/index.d.ts.map +1 -0
  453. package/dist/plugins/portal/index.js +2 -0
  454. package/dist/plugins/portal/portal-agent.d.ts +9 -0
  455. package/dist/plugins/portal/portal-agent.d.ts.map +1 -0
  456. package/dist/plugins/portal/portal-agent.js +70 -0
  457. package/dist/plugins/portal/portal.plugin.d.ts +16 -0
  458. package/dist/plugins/portal/portal.plugin.d.ts.map +1 -0
  459. package/dist/plugins/portal/portal.plugin.js +115 -0
  460. package/dist/plugins/sandbox/index.d.ts +3 -0
  461. package/dist/plugins/sandbox/index.d.ts.map +1 -0
  462. package/dist/plugins/sandbox/index.js +2 -0
  463. package/dist/plugins/sandbox/sandbox-mcp.d.ts +46 -0
  464. package/dist/plugins/sandbox/sandbox-mcp.d.ts.map +1 -0
  465. package/dist/plugins/sandbox/sandbox-mcp.js +80 -0
  466. package/dist/plugins/sandbox/sandbox-write-blob.d.ts +22 -0
  467. package/dist/plugins/sandbox/sandbox-write-blob.d.ts.map +1 -0
  468. package/dist/plugins/sandbox/sandbox-write-blob.js +80 -0
  469. package/dist/plugins/sandbox/sandbox.plugin.d.ts +80 -0
  470. package/dist/plugins/sandbox/sandbox.plugin.d.ts.map +1 -0
  471. package/dist/plugins/sandbox/sandbox.plugin.js +204 -0
  472. package/dist/plugins/skills/index.d.ts +3 -0
  473. package/dist/plugins/skills/index.d.ts.map +1 -0
  474. package/dist/plugins/skills/index.js +2 -0
  475. package/dist/plugins/skills/skills-tools.d.ts +20 -0
  476. package/dist/plugins/skills/skills-tools.d.ts.map +1 -0
  477. package/dist/plugins/skills/skills-tools.js +204 -0
  478. package/dist/plugins/skills/skills-ucan.d.ts +24 -0
  479. package/dist/plugins/skills/skills-ucan.d.ts.map +1 -0
  480. package/dist/plugins/skills/skills-ucan.js +28 -0
  481. package/dist/plugins/skills/skills.plugin.d.ts +37 -0
  482. package/dist/plugins/skills/skills.plugin.d.ts.map +1 -0
  483. package/dist/plugins/skills/skills.plugin.js +82 -0
  484. package/dist/plugins/slack/index.d.ts +4 -0
  485. package/dist/plugins/slack/index.d.ts.map +1 -0
  486. package/dist/plugins/slack/index.js +3 -0
  487. package/dist/plugins/slack/slack.module.d.ts +9 -0
  488. package/dist/plugins/slack/slack.module.d.ts.map +1 -0
  489. package/dist/plugins/slack/slack.module.js +27 -0
  490. package/dist/plugins/slack/slack.plugin.d.ts +30 -0
  491. package/dist/plugins/slack/slack.plugin.d.ts.map +1 -0
  492. package/dist/plugins/slack/slack.plugin.js +40 -0
  493. package/dist/plugins/slack/slack.service.d.ts +32 -0
  494. package/dist/plugins/slack/slack.service.d.ts.map +1 -0
  495. package/dist/plugins/slack/slack.service.js +157 -0
  496. package/dist/plugins/user-preferences/index.d.ts +4 -0
  497. package/dist/plugins/user-preferences/index.d.ts.map +1 -0
  498. package/dist/plugins/user-preferences/index.js +3 -0
  499. package/dist/plugins/user-preferences/service/user-preferences.service.d.ts +61 -0
  500. package/dist/plugins/user-preferences/service/user-preferences.service.d.ts.map +1 -0
  501. package/dist/plugins/user-preferences/service/user-preferences.service.js +105 -0
  502. package/dist/plugins/user-preferences/user-preferences-http.module.d.ts +10 -0
  503. package/dist/plugins/user-preferences/user-preferences-http.module.d.ts.map +1 -0
  504. package/dist/plugins/user-preferences/user-preferences-http.module.js +23 -0
  505. package/dist/plugins/user-preferences/user-preferences-tool.d.ts +22 -0
  506. package/dist/plugins/user-preferences/user-preferences-tool.d.ts.map +1 -0
  507. package/dist/plugins/user-preferences/user-preferences-tool.js +103 -0
  508. package/dist/plugins/user-preferences/user-preferences.controller.d.ts +18 -0
  509. package/dist/plugins/user-preferences/user-preferences.controller.d.ts.map +1 -0
  510. package/dist/plugins/user-preferences/user-preferences.controller.js +72 -0
  511. package/dist/plugins/user-preferences/user-preferences.plugin.d.ts +27 -0
  512. package/dist/plugins/user-preferences/user-preferences.plugin.d.ts.map +1 -0
  513. package/dist/plugins/user-preferences/user-preferences.plugin.js +66 -0
  514. package/dist/registries/config-schema-registry.d.ts +24 -0
  515. package/dist/registries/config-schema-registry.d.ts.map +1 -0
  516. package/dist/registries/config-schema-registry.js +27 -0
  517. package/dist/registries/index.d.ts +13 -0
  518. package/dist/registries/index.d.ts.map +1 -0
  519. package/dist/registries/index.js +6 -0
  520. package/dist/registries/manifest-registry.d.ts +49 -0
  521. package/dist/registries/manifest-registry.d.ts.map +1 -0
  522. package/dist/registries/manifest-registry.js +53 -0
  523. package/dist/registries/middleware-registry.d.ts +41 -0
  524. package/dist/registries/middleware-registry.d.ts.map +1 -0
  525. package/dist/registries/middleware-registry.js +52 -0
  526. package/dist/registries/shared-state-registry.d.ts +41 -0
  527. package/dist/registries/shared-state-registry.d.ts.map +1 -0
  528. package/dist/registries/shared-state-registry.js +65 -0
  529. package/dist/registries/subagent-registry.d.ts +55 -0
  530. package/dist/registries/subagent-registry.d.ts.map +1 -0
  531. package/dist/registries/subagent-registry.js +106 -0
  532. package/dist/registries/test-fixtures.d.ts +47 -0
  533. package/dist/registries/test-fixtures.d.ts.map +1 -0
  534. package/dist/registries/test-fixtures.js +168 -0
  535. package/dist/registries/tool-registry.d.ts +74 -0
  536. package/dist/registries/tool-registry.d.ts.map +1 -0
  537. package/dist/registries/tool-registry.js +130 -0
  538. package/dist/runtime-context/ambient.d.ts +118 -0
  539. package/dist/runtime-context/ambient.d.ts.map +1 -0
  540. package/dist/runtime-context/ambient.js +1 -0
  541. package/dist/runtime-context/build-plugin.d.ts +20 -0
  542. package/dist/runtime-context/build-plugin.d.ts.map +1 -0
  543. package/dist/runtime-context/build-plugin.js +16 -0
  544. package/dist/runtime-context/build-runtime.d.ts +60 -0
  545. package/dist/runtime-context/build-runtime.d.ts.map +1 -0
  546. package/dist/runtime-context/build-runtime.js +81 -0
  547. package/dist/testing/create-test-runtime.d.ts +95 -0
  548. package/dist/testing/create-test-runtime.d.ts.map +1 -0
  549. package/dist/testing/create-test-runtime.js +302 -0
  550. package/dist/testing/index.d.ts +5 -0
  551. package/dist/testing/index.d.ts.map +1 -0
  552. package/dist/testing/index.js +5 -0
  553. package/dist/testing/integration/chat-client.d.ts +143 -0
  554. package/dist/testing/integration/chat-client.d.ts.map +1 -0
  555. package/dist/testing/integration/chat-client.js +238 -0
  556. package/dist/testing/integration/harness.d.ts +189 -0
  557. package/dist/testing/integration/harness.d.ts.map +1 -0
  558. package/dist/testing/integration/harness.js +461 -0
  559. package/dist/testing/integration/index.d.ts +14 -0
  560. package/dist/testing/integration/index.d.ts.map +1 -0
  561. package/dist/testing/integration/index.js +18 -0
  562. package/dist/testing/integration/setup.d.ts +2 -0
  563. package/dist/testing/integration/setup.d.ts.map +1 -0
  564. package/dist/testing/integration/setup.js +41 -0
  565. package/dist/testing/integration/sse-parser.d.ts +99 -0
  566. package/dist/testing/integration/sse-parser.d.ts.map +1 -0
  567. package/dist/testing/integration/sse-parser.js +125 -0
  568. package/dist/testing/integration/ucan.d.ts +74 -0
  569. package/dist/testing/integration/ucan.d.ts.map +1 -0
  570. package/dist/testing/integration/ucan.js +95 -0
  571. package/dist/testing/integration/wait-for-matrix-loaded.d.ts +19 -0
  572. package/dist/testing/integration/wait-for-matrix-loaded.d.ts.map +1 -0
  573. package/dist/testing/integration/wait-for-matrix-loaded.js +31 -0
  574. package/dist/testing/mocks.d.ts +64 -0
  575. package/dist/testing/mocks.d.ts.map +1 -0
  576. package/dist/testing/mocks.js +141 -0
  577. package/dist/testing/nest-doubles.d.ts +10 -0
  578. package/dist/testing/nest-doubles.d.ts.map +1 -0
  579. package/dist/testing/nest-doubles.js +19 -0
  580. package/dist/utils/emoji.d.ts +3 -0
  581. package/dist/utils/emoji.d.ts.map +1 -0
  582. package/dist/utils/emoji.js +36 -0
  583. package/package.json +102 -0
@@ -0,0 +1,856 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var UserMatrixSqliteSyncService_1;
11
+ import { MatrixManager } from '@ixo/matrix';
12
+ import { getMatrixHomeServerCroppedForDid } from '@ixo/oracles-chain-client';
13
+ import { Injectable, Logger, NotFoundException, } from '@nestjs/common';
14
+ import { createHash } from 'crypto';
15
+ import { Cron, CronExpression } from '@nestjs/schedule';
16
+ import { hours } from '@nestjs/throttler';
17
+ import { File } from 'node:buffer';
18
+ import fsSync from 'node:fs';
19
+ import * as fs from 'node:fs/promises';
20
+ import { promisify } from 'node:util';
21
+ import { gunzip, gzip } from 'node:zlib';
22
+ import Database from 'better-sqlite3';
23
+ import path from 'path';
24
+ import { deleteMediaFromRoom, getMediaFromRoom, getMediaFromRoomByStorageKey, uploadMediaToRoom, } from './matrix-upload-utils.js';
25
+ import { getBaseEnvConfig as getConfig } from '../../config/base-env-config.js';
26
+ const gzipAsync = promisify(gzip);
27
+ const gunzipAsync = promisify(gunzip);
28
+ /**
29
+ * Returns true if the error is permanent (data genuinely unrecoverable),
30
+ * meaning it's safe to create a fresh DB. All other errors are assumed
31
+ * transient and should propagate to prevent data loss.
32
+ */
33
+ function isUnrecoverableDownloadError(error) {
34
+ const message = error instanceof Error ? error.message : String(error);
35
+ // Crypto/decryption failures from Rust NAPI layer (hash mismatch, invalid key, corrupt JSON)
36
+ // These mean the encrypted payload is broken — retrying won't help
37
+ const cryptoPatterns = [
38
+ /decrypt/i,
39
+ /hash/i,
40
+ /mismatch/i,
41
+ /base64/i,
42
+ /serde/i,
43
+ /invalid.*key/i,
44
+ /missing field/i,
45
+ ];
46
+ // Matrix-specific permanent errors
47
+ const matrixPatterns = [
48
+ /M_NOT_FOUND/, // media deleted/redacted from Matrix
49
+ /Event not found/, // event no longer exists
50
+ /not a media event/i, // event type mismatch
51
+ /mxcUrl.*does not begin/i, // malformed content.file.url
52
+ /M_FORBIDDEN/, // access permanently denied
53
+ ];
54
+ return [...cryptoPatterns, ...matrixPatterns].some((p) => p.test(message));
55
+ }
56
+ const config = getConfig();
57
+ /** Configure a SQLite connection with busy timeout for safe concurrent access */
58
+ /** Configure a SQLite connection with pragmas for safe concurrent access on VPS */
59
+ function configureSqliteConnection(db) {
60
+ db.pragma('journal_mode = DELETE');
61
+ db.pragma('busy_timeout = 5000');
62
+ db.pragma('synchronous = NORMAL');
63
+ }
64
+ let UserMatrixSqliteSyncService = class UserMatrixSqliteSyncService {
65
+ static { UserMatrixSqliteSyncService_1 = this; }
66
+ static instance;
67
+ fileEventsDatabase;
68
+ constructor() {
69
+ // check if path exists
70
+ const pathExists = fsSync.existsSync(path.join(config.getOrThrow('SQLITE_DATABASE_PATH')));
71
+ if (!pathExists) {
72
+ fsSync.mkdirSync(path.join(config.getOrThrow('SQLITE_DATABASE_PATH')), {
73
+ recursive: true,
74
+ });
75
+ }
76
+ this.fileEventsDatabase = new Database(path.join(config.getOrThrow('SQLITE_DATABASE_PATH'), 'file_events.db'));
77
+ configureSqliteConnection(this.fileEventsDatabase);
78
+ }
79
+ filePathCache = new Map();
80
+ dbConnectionCache = new Map();
81
+ /** Reference-counted active users — supports nested markUserActive/markUserInactive calls */
82
+ activeUsers = new Map();
83
+ downloadInProgress = new Map();
84
+ recoveryInProgress = new Map();
85
+ lastUploadedChecksum = new Map();
86
+ /**
87
+ * Users whose SQLite checkpoint has been synced from Matrix at least once
88
+ * in this process lifetime. We're a single-node deployment: after the
89
+ * first sync, the local copy IS the source of truth until shutdown (which
90
+ * uploads back to Matrix). Skipping re-syncs on subsequent requests for
91
+ * the same user is the dominant TTFB win.
92
+ */
93
+ syncedUsers = new Set();
94
+ /** Prevents overlapping cron executions from interleaving I/O on the same files */
95
+ cronRunning = false;
96
+ markUserActive(userDid) {
97
+ const count = this.activeUsers.get(userDid) ?? 0;
98
+ this.activeUsers.set(userDid, count + 1);
99
+ }
100
+ markUserInactive(userDid) {
101
+ const count = this.activeUsers.get(userDid) ?? 0;
102
+ if (count <= 1) {
103
+ this.activeUsers.delete(userDid);
104
+ }
105
+ else {
106
+ this.activeUsers.set(userDid, count - 1);
107
+ }
108
+ }
109
+ isUserActive(userDid) {
110
+ return (this.activeUsers.get(userDid) ?? 0) > 0;
111
+ }
112
+ static createUserStorageKey(userDid) {
113
+ const key = `checkpoint_${userDid}_${config.getOrThrow('ORACLE_DID')}`;
114
+ return createHash('sha256').update(key).digest('hex').substring(0, 17);
115
+ }
116
+ static getUserCheckpointDbPath(userDid) {
117
+ const dbPath = path.join(UserMatrixSqliteSyncService_1.checkpointsFolder, userDid, `${UserMatrixSqliteSyncService_1.createUserStorageKey(userDid)}.db`);
118
+ return dbPath;
119
+ }
120
+ static checkpointsFolder = path.join(config.getOrThrow('SQLITE_DATABASE_PATH'), 'user_dbs');
121
+ async onModuleInit() {
122
+ // create checkpoints folder if it doesn't exist
123
+ const exists = await fs
124
+ .access(UserMatrixSqliteSyncService_1.checkpointsFolder)
125
+ .then(() => true)
126
+ .catch(() => false);
127
+ if (!exists) {
128
+ Logger.debug(`Creating checkpoints folder at ${UserMatrixSqliteSyncService_1.checkpointsFolder}`);
129
+ await fs.mkdir(UserMatrixSqliteSyncService_1.checkpointsFolder, {
130
+ recursive: true,
131
+ });
132
+ }
133
+ this.fileEventsDatabase
134
+ .prepare('CREATE TABLE IF NOT EXISTS file_events (storage_key TEXT PRIMARY KEY, event_id TEXT, event TEXT)')
135
+ .run();
136
+ // Add content_checksum column if it doesn't exist (backward-compatible migration)
137
+ try {
138
+ this.fileEventsDatabase
139
+ .prepare('ALTER TABLE file_events ADD COLUMN content_checksum TEXT')
140
+ .run();
141
+ }
142
+ catch {
143
+ // Column already exists, ignore
144
+ }
145
+ // Populate in-memory checksum cache from DB
146
+ const rows = this.fileEventsDatabase
147
+ .prepare('SELECT storage_key, content_checksum FROM file_events WHERE content_checksum IS NOT NULL')
148
+ .all();
149
+ for (const row of rows) {
150
+ this.lastUploadedChecksum.set(row.storage_key, row.content_checksum);
151
+ }
152
+ // Seed filePathCache from disk so the upload cron can find checkpoint
153
+ // files that survived a restart (hybrid approach: scan once on startup,
154
+ // then use the cache for subsequent cron ticks).
155
+ try {
156
+ const userFolders = await fs.readdir(UserMatrixSqliteSyncService_1.checkpointsFolder);
157
+ for (const userDid of userFolders) {
158
+ const dbPath = UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid);
159
+ const fileExists = await fs
160
+ .access(dbPath)
161
+ .then(() => true)
162
+ .catch(() => false);
163
+ if (fileExists) {
164
+ this.filePathCache.set(userDid, {
165
+ filePath: dbPath,
166
+ lastAccessedAt: Date.now(),
167
+ });
168
+ }
169
+ }
170
+ if (this.filePathCache.size > 0) {
171
+ Logger.log(`Seeded filePathCache with ${this.filePathCache.size} existing checkpoint(s) from disk`);
172
+ }
173
+ }
174
+ catch {
175
+ // Checkpoints folder might be empty or inaccessible on first run
176
+ }
177
+ }
178
+ /**
179
+ * Get or create database connection for a user.
180
+ * Ensures database exists and is synced from Matrix on the first request
181
+ * per user per process; subsequent calls reuse the local file.
182
+ * Includes automatic corruption recovery.
183
+ */
184
+ async getUserDatabase(userDid) {
185
+ if (!this.syncedUsers.has(userDid)) {
186
+ await this.syncLocalStorageFromMatrixStorage({ userDid });
187
+ this.syncedUsers.add(userDid);
188
+ }
189
+ return this.openUserDatabaseFromDisk(userDid);
190
+ }
191
+ /**
192
+ * Same as `getUserDatabase` but never triggers a Matrix → SQLite sync.
193
+ * Used by hot paths that follow an earlier `getUserDatabase` call within
194
+ * the same request (e.g. the fire-and-forget post-message sync).
195
+ */
196
+ async getUserDatabaseNoSync(userDid) {
197
+ return this.openUserDatabaseFromDisk(userDid);
198
+ }
199
+ async openUserDatabaseFromDisk(userDid) {
200
+ const dbPath = UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid);
201
+ // Check cache
202
+ const cached = this.dbConnectionCache.get(userDid);
203
+ if (cached) {
204
+ cached.lastAccessedAt = Date.now();
205
+ return cached.db;
206
+ }
207
+ // Open and validate — recover from corruption if needed
208
+ let db = this.openAndValidateDatabase(dbPath, userDid);
209
+ if (!db) {
210
+ // Deduplicate concurrent recovery attempts for the same user
211
+ const existingRecovery = this.recoveryInProgress.get(userDid);
212
+ if (existingRecovery) {
213
+ // Wait for the in-flight recovery but don't skip init/caching below
214
+ db = await existingRecovery;
215
+ }
216
+ else {
217
+ const recoveryPromise = this.recoverCorruptDatabase(userDid, dbPath);
218
+ this.recoveryInProgress.set(userDid, recoveryPromise);
219
+ try {
220
+ db = await recoveryPromise;
221
+ }
222
+ finally {
223
+ this.recoveryInProgress.delete(userDid);
224
+ }
225
+ }
226
+ }
227
+ // Initialize sessions and calls tables if needed
228
+ try {
229
+ this.initializeSessionsAndCallsTables(db);
230
+ }
231
+ catch (error) {
232
+ // Prevent leaked DB handle if table init fails
233
+ try {
234
+ db.close();
235
+ }
236
+ catch {
237
+ // Ignore close errors
238
+ }
239
+ throw error;
240
+ }
241
+ // Cache it
242
+ this.dbConnectionCache.set(userDid, {
243
+ db,
244
+ lastAccessedAt: Date.now(),
245
+ });
246
+ return db;
247
+ }
248
+ /**
249
+ * Attempts cascading recovery when a local database is corrupt:
250
+ * 1. Clear local → re-download from Matrix → validate
251
+ * 2. If Matrix copy also corrupt → delete from Matrix → create fresh empty DB
252
+ */
253
+ async recoverCorruptDatabase(userDid, dbPath) {
254
+ Logger.error(`[CORRUPTION DETECTED] Local SQLite database is corrupt for user ${userDid} at ${dbPath}. Attempting recovery from Matrix backup...`);
255
+ // Clear local corrupt file and re-download from Matrix
256
+ await this.clearLocalCheckpoint(userDid, dbPath);
257
+ await this.syncLocalStorageFromMatrixStorage({ userDid });
258
+ // Check if Matrix had a backup
259
+ const fileExists = await fs
260
+ .access(dbPath)
261
+ .then(() => true)
262
+ .catch(() => false);
263
+ if (fileExists) {
264
+ const db = this.openAndValidateDatabase(dbPath, userDid);
265
+ if (db)
266
+ return db;
267
+ // Matrix copy is also corrupt
268
+ Logger.error(`[CORRUPTION DETECTED] Matrix backup is ALSO corrupt for user ${userDid}. Deleting corrupt backup and starting fresh. User will lose session history.`);
269
+ await this.clearLocalCheckpoint(userDid, dbPath);
270
+ try {
271
+ await this.deleteUserStorageFromMatrix(userDid);
272
+ Logger.warn(`Deleted corrupt Matrix backup for user ${userDid}. Corruption loop broken.`);
273
+ }
274
+ catch (deleteError) {
275
+ Logger.error(`Failed to delete corrupt Matrix backup for user ${userDid}: ${deleteError}`);
276
+ }
277
+ }
278
+ // Create a brand new empty database
279
+ Logger.warn(`Creating fresh database for user ${userDid} after corruption recovery. All previous sessions are lost.`);
280
+ const dir = path.dirname(dbPath);
281
+ await fs.mkdir(dir, { recursive: true });
282
+ const db = new Database(dbPath);
283
+ configureSqliteConnection(db);
284
+ // Ensure the fresh DB is tracked so the upload cron (which iterates
285
+ // filePathCache.keys()) will back it up to Matrix.
286
+ this.filePathCache.set(userDid, {
287
+ filePath: dbPath,
288
+ lastAccessedAt: Date.now(),
289
+ });
290
+ return db;
291
+ }
292
+ /**
293
+ * Opens a SQLite database and validates it is not corrupt.
294
+ * Returns the Database instance if valid, or null if corrupt/missing.
295
+ */
296
+ openAndValidateDatabase(dbPath, userDid) {
297
+ try {
298
+ if (!fsSync.existsSync(dbPath)) {
299
+ return null;
300
+ }
301
+ const db = new Database(dbPath);
302
+ configureSqliteConnection(db);
303
+ // Run integrity check — returns 'ok' if database is healthy
304
+ const result = db.pragma('integrity_check');
305
+ const isOk = result.length === 1 && result[0]?.integrity_check === 'ok';
306
+ if (!isOk) {
307
+ const details = result.map((r) => r.integrity_check).join('; ');
308
+ Logger.error(`[CORRUPTION DETECTED] PRAGMA integrity_check failed for user ${userDid}: ${details}`);
309
+ try {
310
+ db.close();
311
+ }
312
+ catch {
313
+ // Ignore close errors on corrupt DB
314
+ }
315
+ return null;
316
+ }
317
+ return db;
318
+ }
319
+ catch (error) {
320
+ Logger.error(`[CORRUPTION DETECTED] Failed to open SQLite database for user ${userDid} at ${dbPath}: ${error instanceof Error ? error.message : String(error)}`);
321
+ return null;
322
+ }
323
+ }
324
+ /**
325
+ * Deletes local checkpoint file and clears all associated caches for a user.
326
+ */
327
+ async clearLocalCheckpoint(userDid, dbPath) {
328
+ // Close cached connection if exists
329
+ const cached = this.dbConnectionCache.get(userDid);
330
+ if (cached) {
331
+ try {
332
+ cached.db.close();
333
+ }
334
+ catch {
335
+ // Ignore close errors
336
+ }
337
+ this.dbConnectionCache.delete(userDid);
338
+ }
339
+ // Clear file path cache, checksum cache, AND the "synced-once" flag.
340
+ // Dropping `syncedUsers` here is critical: without it, the next
341
+ // `getUserDatabase` call after the local file is deleted would skip the
342
+ // Matrix → SQLite re-download (because we'd think we're still synced),
343
+ // landing in the corruption-recovery path instead of a clean sync.
344
+ this.syncedUsers.delete(userDid);
345
+ this.filePathCache.delete(userDid);
346
+ const storageKey = UserMatrixSqliteSyncService_1.createUserStorageKey(userDid);
347
+ try {
348
+ this.fileEventsDatabase
349
+ .prepare('UPDATE file_events SET content_checksum = NULL WHERE storage_key = ?')
350
+ .run(storageKey);
351
+ // Clear in-memory cache AFTER successful DB update to keep them consistent
352
+ this.lastUploadedChecksum.delete(storageKey);
353
+ }
354
+ catch (error) {
355
+ // Still clear in-memory cache on DB failure — worst case is a redundant upload
356
+ this.lastUploadedChecksum.delete(storageKey);
357
+ Logger.warn(`Failed to clear content_checksum for ${storageKey}: ${error instanceof Error ? error.message : error}`);
358
+ }
359
+ // Delete local file + temp files + leftover WAL/SHM/journal files
360
+ for (const suffix of ['', '.tmp', '-wal', '-shm', '-journal']) {
361
+ try {
362
+ await fs.unlink(dbPath + suffix);
363
+ }
364
+ catch {
365
+ // File may not exist, that's fine
366
+ }
367
+ }
368
+ }
369
+ initializeSessionsAndCallsTables(db) {
370
+ db.exec(`
371
+ CREATE TABLE IF NOT EXISTS sessions (
372
+ session_id TEXT PRIMARY KEY,
373
+ title TEXT,
374
+ last_updated_at TEXT NOT NULL,
375
+ created_at TEXT NOT NULL,
376
+ oracle_name TEXT NOT NULL,
377
+ oracle_did TEXT NOT NULL,
378
+ oracle_entity_did TEXT NOT NULL,
379
+ last_processed_count INTEGER,
380
+ user_context TEXT,
381
+ room_id TEXT,
382
+ slack_thread_ts TEXT
383
+ );
384
+
385
+ CREATE TABLE IF NOT EXISTS calls (
386
+ call_id TEXT PRIMARY KEY,
387
+ session_id TEXT NOT NULL,
388
+ created_at TEXT NOT NULL
389
+ );
390
+
391
+ CREATE INDEX IF NOT EXISTS idx_sessions_updated ON sessions(last_updated_at);
392
+ CREATE INDEX IF NOT EXISTS idx_calls_session ON calls(session_id);
393
+ `);
394
+ }
395
+ async localStorageCacheCleanUpTask() {
396
+ if (this.cronRunning) {
397
+ Logger.debug('Skipping hourly cleanup — another cron task is still running');
398
+ return;
399
+ }
400
+ this.cronRunning = true;
401
+ try {
402
+ const now = Date.now();
403
+ // Close idle database connections
404
+ for (const [userDid, { db, lastAccessedAt },] of this.dbConnectionCache.entries()) {
405
+ if (this.isUserActive(userDid)) {
406
+ Logger.debug(`Skipping DB cleanup for active user ${userDid}`);
407
+ continue;
408
+ }
409
+ if (now - lastAccessedAt > hours(1)) {
410
+ try {
411
+ // Sync to Matrix before closing
412
+ await this.uploadCheckpointToMatrixStorage({ userDid });
413
+ // Close connection (db is already from the loop iteration)
414
+ db.close();
415
+ this.dbConnectionCache.delete(userDid);
416
+ Logger.log(`Closed idle database connection for user ${userDid}`);
417
+ }
418
+ catch (error) {
419
+ Logger.error(`Failed to cleanup DB connection for user ${userDid}`, error);
420
+ }
421
+ }
422
+ }
423
+ // Clean up file cache
424
+ for (const [userDid, { lastAccessedAt },] of this.filePathCache.entries()) {
425
+ if (this.isUserActive(userDid)) {
426
+ Logger.debug(`Skipping file cache cleanup for active user ${userDid}`);
427
+ continue;
428
+ }
429
+ if (now - lastAccessedAt > hours(1)) {
430
+ try {
431
+ await this.uploadCheckpointToMatrixStorage({ userDid });
432
+ }
433
+ catch (error) {
434
+ Logger.error(`Failed to sync checkpoint file to matrix storage for user ${userDid}`, error);
435
+ // failed to sync, continue to next user so we can retry next hour
436
+ continue;
437
+ }
438
+ // sync successful, delete local cache
439
+ const userFolder = path.join(UserMatrixSqliteSyncService_1.checkpointsFolder, userDid);
440
+ const storageKey = UserMatrixSqliteSyncService_1.createUserStorageKey(userDid);
441
+ try {
442
+ await fs.rm(userFolder, { recursive: true });
443
+ Logger.log(`Deleted Local Storage checkpoint folder for user ${userDid} and path ${userFolder}`);
444
+ }
445
+ catch (error) {
446
+ Logger.error(`Failed to delete local checkpoint folder for user ${userDid}: ${error instanceof Error ? error.message : error}`);
447
+ }
448
+ // Always clear caches regardless of fs.rm result — stale cache
449
+ // entries are worse than missing ones (next access re-downloads).
450
+ // `syncedUsers` is cleared here for the same reason: the local
451
+ // file is gone, the next request must re-pull from Matrix.
452
+ this.syncedUsers.delete(userDid);
453
+ this.filePathCache.delete(userDid);
454
+ this.lastUploadedChecksum.delete(storageKey);
455
+ }
456
+ }
457
+ }
458
+ finally {
459
+ this.cronRunning = false;
460
+ }
461
+ }
462
+ /**
463
+ * Get the singleton instance of UserMatrixSqliteSyncService
464
+ * @param maxCacheSize - Maximum number of cached files (default: 100)
465
+ * @returns The singleton instance
466
+ */
467
+ static getInstance() {
468
+ if (!UserMatrixSqliteSyncService_1.instance) {
469
+ UserMatrixSqliteSyncService_1.instance = new UserMatrixSqliteSyncService_1();
470
+ }
471
+ return UserMatrixSqliteSyncService_1.instance;
472
+ }
473
+ /**
474
+ * Load the checkpoint SQLite file for a user.
475
+ * First checks the local cache, then matrix storage if not cached.
476
+ * @param userDid - The user's DID identifier
477
+ * @returns Promise resolving to the SQLite file buffer
478
+ */
479
+ async syncLocalStorageFromMatrixStorage(params) {
480
+ const { userDid } = params;
481
+ // If a download is already in progress for this user, await it instead of starting another
482
+ const existingDownload = this.downloadInProgress.get(userDid);
483
+ if (existingDownload) {
484
+ Logger.debug(`Download already in progress for user ${userDid}, awaiting existing download`);
485
+ return existingDownload;
486
+ }
487
+ const downloadPromise = this._syncLocalStorageFromMatrixStorage(userDid);
488
+ this.downloadInProgress.set(userDid, downloadPromise);
489
+ try {
490
+ await downloadPromise;
491
+ }
492
+ finally {
493
+ this.downloadInProgress.delete(userDid);
494
+ }
495
+ }
496
+ async _syncLocalStorageFromMatrixStorage(userDid) {
497
+ const storageKey = UserMatrixSqliteSyncService_1.createUserStorageKey(userDid);
498
+ const checkpointPath = UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid);
499
+ Logger.debug(`Syncing checkpoint for user ${userDid}, storageKey: ${storageKey}, path: ${checkpointPath}`);
500
+ // Ensure the user's checkpoint directory exists
501
+ const userCheckpointDir = path.dirname(checkpointPath);
502
+ const dirExists = await fs
503
+ .access(userCheckpointDir)
504
+ .then(() => true)
505
+ .catch(() => false);
506
+ if (!dirExists) {
507
+ Logger.debug(`Creating checkpoint directory for user ${userDid}: ${userCheckpointDir}`);
508
+ await fs.mkdir(userCheckpointDir, { recursive: true });
509
+ }
510
+ // check if file exists
511
+ const exists = await fs
512
+ .access(checkpointPath)
513
+ .then(() => true)
514
+ .catch(() => false);
515
+ if (exists) {
516
+ Logger.debug(`Checkpoint file already exists locally for user ${userDid} at ${checkpointPath}`);
517
+ this.filePathCache.set(userDid, {
518
+ filePath: checkpointPath,
519
+ lastAccessedAt: Date.now(),
520
+ });
521
+ return;
522
+ }
523
+ Logger.debug(`Checkpoint file not found locally for user ${userDid}, attempting to download from Matrix`);
524
+ let userDB = null;
525
+ // Step 1: Try cached event lookup (local SQLite — independent concern)
526
+ let cachedEvent;
527
+ try {
528
+ const cachedEventText = this.fileEventsDatabase
529
+ .prepare('SELECT event FROM file_events WHERE storage_key = ?')
530
+ .get(storageKey);
531
+ cachedEvent = cachedEventText
532
+ ? JSON.parse(cachedEventText.event)
533
+ : undefined;
534
+ }
535
+ catch (cacheError) {
536
+ // file_events.db corrupt or locked — skip cache, fall through to direct Matrix lookup
537
+ Logger.warn(`Failed to read cached event for user ${userDid}, falling through to Matrix lookup: ${cacheError instanceof Error ? cacheError.message : String(cacheError)}`);
538
+ }
539
+ // Step 2: Download from Matrix
540
+ try {
541
+ if (cachedEvent) {
542
+ const result = await getMediaFromRoom(undefined, undefined, cachedEvent);
543
+ userDB = {
544
+ ...result,
545
+ contentInfo: {
546
+ ...result.contentInfo,
547
+ storageKey,
548
+ },
549
+ };
550
+ }
551
+ else {
552
+ const mxManager = MatrixManager.getInstance();
553
+ const userHomeServer = await getMatrixHomeServerCroppedForDid(userDid);
554
+ const { roomId } = await mxManager.getOracleRoomIdWithHomeServer({
555
+ userDid,
556
+ oracleEntityDid: config.getOrThrow('ORACLE_ENTITY_DID'),
557
+ userHomeServer,
558
+ });
559
+ if (!roomId) {
560
+ throw new NotFoundException('Room not found or Invalid Session Id');
561
+ }
562
+ Logger.debug(`Downloading checkpoint from Matrix room ${roomId} for user ${userDid}`);
563
+ userDB = await getMediaFromRoomByStorageKey(roomId, storageKey);
564
+ }
565
+ }
566
+ catch (error) {
567
+ if (isUnrecoverableDownloadError(error)) {
568
+ // Permanent failure — data genuinely unrecoverable, safe to start fresh
569
+ Logger.warn(`Unrecoverable download failure for user ${userDid}, will start with fresh database: ${error instanceof Error ? error.message : String(error)}`);
570
+ return;
571
+ }
572
+ // Transient/unknown error — let it propagate so the request fails with 500
573
+ // and the user retries later. This prevents creating an empty DB that would
574
+ // overwrite the good Matrix backup on the next upload cron cycle.
575
+ throw error;
576
+ }
577
+ if (!userDB) {
578
+ Logger.debug(`No checkpoint found in Matrix for user ${userDid} with storageKey ${storageKey}, this is expected for new users`);
579
+ return;
580
+ }
581
+ // Decompress the checkpoint
582
+ const SQLITE_MAGIC = Buffer.from('SQLite format 3\0');
583
+ let decompressedBuffer;
584
+ try {
585
+ decompressedBuffer = await gunzipAsync(userDB.mediaBuffer);
586
+ Logger.log(`Decompressed checkpoint for user ${userDid}: ${bytesToHumanReadable(userDB.mediaBuffer.length)} -> ${bytesToHumanReadable(decompressedBuffer.length)}`);
587
+ }
588
+ catch (_error) {
589
+ // Decompression failed — check if the raw buffer is a valid uncompressed SQLite file
590
+ if (userDB.mediaBuffer.length >= 16 &&
591
+ userDB.mediaBuffer.subarray(0, 16).equals(SQLITE_MAGIC)) {
592
+ Logger.warn(`Checkpoint for user ${userDid} is uncompressed SQLite (legacy format), using as-is`);
593
+ decompressedBuffer = userDB.mediaBuffer;
594
+ }
595
+ else {
596
+ Logger.error(`Checkpoint for user ${userDid} is neither valid gzip nor valid SQLite — skipping download to prevent corruption. Raw bytes (first 16): ${userDB.mediaBuffer.subarray(0, 16).toString('hex')}`);
597
+ return;
598
+ }
599
+ }
600
+ // Validate decompressed data is a valid SQLite file
601
+ if (decompressedBuffer.length < 16 ||
602
+ !decompressedBuffer.subarray(0, 16).equals(SQLITE_MAGIC)) {
603
+ Logger.error(`Decompressed checkpoint for user ${userDid} does not have valid SQLite header — skipping to prevent corruption. Header bytes: ${decompressedBuffer.subarray(0, Math.min(16, decompressedBuffer.length)).toString('hex')}`);
604
+ return;
605
+ }
606
+ Logger.debug(`Saving checkpoint to local cache for user ${userDid} at ${checkpointPath}`);
607
+ // Atomic write: write to temp file then rename (rename is atomic on POSIX)
608
+ const tmpPath = checkpointPath + '.tmp';
609
+ try {
610
+ await fs.writeFile(tmpPath, decompressedBuffer);
611
+ await fs.rename(tmpPath, checkpointPath);
612
+ }
613
+ catch (error) {
614
+ // Clean up orphaned temp file on failure
615
+ try {
616
+ await fs.unlink(tmpPath);
617
+ }
618
+ catch {
619
+ // Ignore cleanup errors
620
+ }
621
+ throw error;
622
+ }
623
+ // Update cache AFTER file is successfully written to disk
624
+ this.filePathCache.set(userDid, {
625
+ filePath: checkpointPath,
626
+ lastAccessedAt: Date.now(),
627
+ });
628
+ Logger.debug(`Successfully saved checkpoint for user ${userDid} at ${checkpointPath}`);
629
+ return;
630
+ }
631
+ /**
632
+ * Sync checkpoint file from local cache to S3.
633
+ * @param userDid - The user's DID identifier
634
+ * @returns Promise that resolves when sync is complete
635
+ */
636
+ async uploadCheckpointToMatrixStorage(params) {
637
+ const { userDid } = params;
638
+ const storageKey = UserMatrixSqliteSyncService_1.createUserStorageKey(userDid);
639
+ const checkpointPath = UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid);
640
+ Logger.debug(`Uploading checkpoint for user ${userDid}, storageKey: ${storageKey}, path: ${checkpointPath}`);
641
+ const exists = await fs
642
+ .access(checkpointPath)
643
+ .then(() => true)
644
+ .catch(() => false);
645
+ if (!exists) {
646
+ Logger.warn(`Checkpoint file not found for user ${userDid} at ${checkpointPath}`);
647
+ return;
648
+ }
649
+ // Handle open database connections — don't close if user has active request
650
+ const cached = this.dbConnectionCache.get(userDid);
651
+ if (cached) {
652
+ if (this.isUserActive(userDid)) {
653
+ // User has an in-flight request — in DELETE journal mode the DB file may be
654
+ // inconsistent mid-transaction, so skip upload. Next cron cycle will pick it up.
655
+ Logger.debug(`Skipping upload for active user ${userDid}, will retry next cycle`);
656
+ return;
657
+ }
658
+ else {
659
+ // No active request — safe to close
660
+ try {
661
+ cached.db.close();
662
+ this.dbConnectionCache.delete(userDid);
663
+ Logger.debug(`Closed cached database connection for user ${userDid}`);
664
+ }
665
+ catch (error) {
666
+ Logger.warn(`Failed to close cached database connection for user ${userDid}: ${error}`);
667
+ }
668
+ }
669
+ }
670
+ // Compute checksum via streaming to avoid loading the entire DB into memory.
671
+ // Streaming reads ~64KB chunks at a time instead of the full file (which can be 100MB+).
672
+ const currentChecksum = await computeFileChecksum(checkpointPath);
673
+ const lastChecksum = this.lastUploadedChecksum.get(storageKey);
674
+ if (currentChecksum === lastChecksum) {
675
+ Logger.debug(`Skipping upload for user ${userDid} — checkpoint unchanged (checksum: ${currentChecksum.substring(0, 12)}...)`);
676
+ return;
677
+ }
678
+ // Only load file into memory when we know the content has changed and needs uploading
679
+ Logger.debug(`Reading checkpoint file for user ${userDid} from ${checkpointPath}`);
680
+ const checkpoint = await fs.readFile(checkpointPath);
681
+ const originalSize = checkpoint.length;
682
+ // Compress the database file with gzip before upload
683
+ const compressedCheckpoint = await gzipAsync(checkpoint);
684
+ const compressedSize = compressedCheckpoint.length;
685
+ const compressionRatio = ((1 - compressedSize / originalSize) *
686
+ 100).toFixed(1);
687
+ Logger.log(`Checkpoint for user ${userDid}: ${bytesToHumanReadable(originalSize)} -> ${bytesToHumanReadable(compressedSize)} (${compressionRatio}% reduction)`);
688
+ const mxManager = MatrixManager.getInstance();
689
+ const userHomeServer = await getMatrixHomeServerCroppedForDid(userDid);
690
+ const { roomId } = await mxManager.getOracleRoomIdWithHomeServer({
691
+ userDid,
692
+ oracleEntityDid: config.getOrThrow('ORACLE_ENTITY_DID'),
693
+ userHomeServer,
694
+ });
695
+ if (!roomId) {
696
+ throw new NotFoundException('Room not found or Invalid Session Id');
697
+ }
698
+ Logger.debug(`Uploading compressed checkpoint to Matrix room ${roomId} for user ${userDid}`);
699
+ const event = await uploadMediaToRoom(roomId, new File([compressedCheckpoint], `${storageKey}.db.gz`, {
700
+ type: 'application/gzip',
701
+ lastModified: Date.now(),
702
+ }), storageKey);
703
+ await this.saveFileEventToDB({
704
+ eventId: event.eventId,
705
+ storageKey: event.storageKey,
706
+ event: event.event,
707
+ contentChecksum: currentChecksum,
708
+ });
709
+ Logger.log(`Successfully uploaded checkpoint to Matrix for user ${userDid}`);
710
+ }
711
+ // Run at :10, :20, :30, :40, :50 — skips :00 to avoid overlapping with the hourly cleanup cron
712
+ async uploadCheckpointToMatrixStorageTask() {
713
+ if (this.cronRunning) {
714
+ Logger.debug('Skipping upload task — another cron task is still running');
715
+ return;
716
+ }
717
+ this.cronRunning = true;
718
+ try {
719
+ Logger.log(`Uploading checkpoint to Matrix storage task started`);
720
+ // Iterate cached file paths instead of scanning the filesystem —
721
+ // only users with known local checkpoints need uploading.
722
+ for (const userDid of this.filePathCache.keys()) {
723
+ try {
724
+ await this.uploadCheckpointToMatrixStorage({ userDid });
725
+ }
726
+ catch (error) {
727
+ Logger.error(`Failed to upload checkpoint to Matrix storage for user ${userDid}`, error instanceof Error ? error.message : String(error), 'File path: ' +
728
+ UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid), 'File Size before gzip: ' +
729
+ bytesToHumanReadable(await fs
730
+ .stat(UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid))
731
+ .then((stats) => stats.size)));
732
+ }
733
+ }
734
+ }
735
+ finally {
736
+ this.cronRunning = false;
737
+ }
738
+ }
739
+ /**
740
+ * Deletes user storage from Matrix and cleans up local cache
741
+ * @param userDid The user DID
742
+ * @param storageKey Optional storage key. If not provided, uses the default user storage key
743
+ * @returns True if deletion was successful, false if not found
744
+ */
745
+ async deleteUserStorageFromMatrix(userDid, storageKey) {
746
+ const key = storageKey || UserMatrixSqliteSyncService_1.createUserStorageKey(userDid);
747
+ Logger.debug(`Deleting storage for user ${userDid} with storageKey ${key}`);
748
+ // Get the user's Matrix room
749
+ const mxManager = MatrixManager.getInstance();
750
+ const userHomeServer = await getMatrixHomeServerCroppedForDid(userDid);
751
+ const { roomId } = await mxManager.getOracleRoomIdWithHomeServer({
752
+ userDid,
753
+ oracleEntityDid: config.getOrThrow('ORACLE_ENTITY_DID'),
754
+ userHomeServer,
755
+ });
756
+ if (!roomId) {
757
+ Logger.warn(`No Matrix room found for user ${userDid}, cannot delete storage`);
758
+ return false;
759
+ }
760
+ // Delete from Matrix
761
+ const deleted = await deleteMediaFromRoom(roomId, key);
762
+ if (deleted) {
763
+ // Clean up local cache
764
+ try {
765
+ // Delete from file events database
766
+ this.fileEventsDatabase
767
+ .prepare('DELETE FROM file_events WHERE storage_key = ?')
768
+ .run(key);
769
+ Logger.debug(`Deleted file event cache for storageKey ${key} from database`);
770
+ }
771
+ catch (error) {
772
+ Logger.warn(`Failed to delete file event cache for storageKey ${key}:`, error);
773
+ }
774
+ // Delete local file if it exists
775
+ try {
776
+ const dbPath = UserMatrixSqliteSyncService_1.getUserCheckpointDbPath(userDid);
777
+ const exists = await fs
778
+ .access(dbPath)
779
+ .then(() => true)
780
+ .catch(() => false);
781
+ if (exists) {
782
+ await fs.unlink(dbPath);
783
+ Logger.debug(`Deleted local checkpoint file at ${dbPath}`);
784
+ }
785
+ }
786
+ catch (error) {
787
+ Logger.warn(`Failed to delete local checkpoint file for user ${userDid}:`, error);
788
+ }
789
+ // Clear database connection cache
790
+ const cached = this.dbConnectionCache.get(userDid);
791
+ if (cached) {
792
+ try {
793
+ cached.db.close();
794
+ this.dbConnectionCache.delete(userDid);
795
+ Logger.debug(`Closed and cleared database connection for ${userDid}`);
796
+ }
797
+ catch (error) {
798
+ Logger.warn(`Failed to close database connection for ${userDid}:`, error);
799
+ }
800
+ }
801
+ // Clear file path cache and checksum cache
802
+ this.filePathCache.delete(userDid);
803
+ this.lastUploadedChecksum.delete(key);
804
+ Logger.log(`Successfully deleted storage for user ${userDid} with storageKey ${key}`);
805
+ }
806
+ return deleted;
807
+ }
808
+ async saveFileEventToDB({ eventId, storageKey, event, contentChecksum, }) {
809
+ this.fileEventsDatabase
810
+ .prepare('INSERT OR REPLACE INTO file_events (storage_key, event_id, event, content_checksum) VALUES (?, ?, ?, ?)')
811
+ .run(storageKey, eventId, JSON.stringify(event), contentChecksum ?? null);
812
+ // Update in-memory cache
813
+ if (contentChecksum) {
814
+ this.lastUploadedChecksum.set(storageKey, contentChecksum);
815
+ }
816
+ }
817
+ };
818
+ __decorate([
819
+ Cron(CronExpression.EVERY_HOUR),
820
+ __metadata("design:type", Function),
821
+ __metadata("design:paramtypes", []),
822
+ __metadata("design:returntype", Promise)
823
+ ], UserMatrixSqliteSyncService.prototype, "localStorageCacheCleanUpTask", null);
824
+ __decorate([
825
+ Cron('0 10,20,30,40,50 * * * *'),
826
+ __metadata("design:type", Function),
827
+ __metadata("design:paramtypes", []),
828
+ __metadata("design:returntype", Promise)
829
+ ], UserMatrixSqliteSyncService.prototype, "uploadCheckpointToMatrixStorageTask", null);
830
+ UserMatrixSqliteSyncService = UserMatrixSqliteSyncService_1 = __decorate([
831
+ Injectable(),
832
+ __metadata("design:paramtypes", [])
833
+ ], UserMatrixSqliteSyncService);
834
+ export { UserMatrixSqliteSyncService };
835
+ /**
836
+ * Compute SHA-256 checksum of a file using streaming reads.
837
+ * Reads in ~64KB chunks to avoid loading the entire file into memory,
838
+ * which matters for large SQLite databases (100MB+).
839
+ */
840
+ function computeFileChecksum(filePath) {
841
+ return new Promise((resolve, reject) => {
842
+ const hash = createHash('sha256');
843
+ const stream = fsSync.createReadStream(filePath);
844
+ stream.on('data', (chunk) => hash.update(chunk));
845
+ stream.on('end', () => resolve(hash.digest('hex')));
846
+ stream.on('error', (err) => {
847
+ stream.destroy();
848
+ reject(err);
849
+ });
850
+ });
851
+ }
852
+ const bytesToHumanReadable = (bytes) => {
853
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
854
+ const index = Math.floor(Math.log(bytes) / Math.log(1024));
855
+ return (bytes / Math.pow(1024, index)).toFixed(2) + ' ' + units[index];
856
+ };