@xopcai/xopc 0.0.99 → 0.0.100

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 (349) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/src/account-manager.d.ts +3 -3
  3. package/dist/extensions/telegram/src/account-manager.js +8 -8
  4. package/dist/extensions/telegram/src/account-manager.js.map +1 -1
  5. package/dist/extensions/telegram/src/actions/message-actions.d.ts +4 -0
  6. package/dist/extensions/telegram/src/actions/message-actions.js +71 -0
  7. package/dist/extensions/telegram/src/actions/message-actions.js.map +1 -0
  8. package/dist/extensions/telegram/src/adapters/config-adapter.js +9 -1
  9. package/dist/extensions/telegram/src/adapters/config-adapter.js.map +1 -1
  10. package/dist/extensions/telegram/src/adapters/config-surface.js +1 -1
  11. package/dist/extensions/telegram/src/adapters/config-surface.js.map +1 -1
  12. package/dist/extensions/telegram/src/adapters/outbound-adapter.d.ts +2 -1
  13. package/dist/extensions/telegram/src/adapters/outbound-adapter.js +18 -4
  14. package/dist/extensions/telegram/src/adapters/outbound-adapter.js.map +1 -1
  15. package/dist/extensions/telegram/src/adapters/security-adapter.js +2 -1
  16. package/dist/extensions/telegram/src/adapters/security-adapter.js.map +1 -1
  17. package/dist/extensions/telegram/src/adapters/streaming-adapter.js +90 -6
  18. package/dist/extensions/telegram/src/adapters/streaming-adapter.js.map +1 -1
  19. package/dist/extensions/telegram/src/adapters/types.d.ts +9 -1
  20. package/dist/extensions/telegram/src/api-root.d.ts +4 -0
  21. package/dist/extensions/telegram/src/api-root.js +42 -0
  22. package/dist/extensions/telegram/src/api-root.js.map +1 -0
  23. package/dist/extensions/telegram/src/approval-delivery.d.ts +9 -0
  24. package/dist/extensions/telegram/src/approval-delivery.js +36 -0
  25. package/dist/extensions/telegram/src/approval-delivery.js.map +1 -0
  26. package/dist/extensions/telegram/src/approval-store.d.ts +25 -0
  27. package/dist/extensions/telegram/src/approval-store.js +61 -0
  28. package/dist/extensions/telegram/src/approval-store.js.map +1 -0
  29. package/dist/extensions/telegram/src/config-schema.d.ts +185 -35
  30. package/dist/extensions/telegram/src/config-schema.js +67 -45
  31. package/dist/extensions/telegram/src/config-schema.js.map +1 -1
  32. package/dist/extensions/telegram/src/conversation-id.d.ts +6 -0
  33. package/dist/extensions/telegram/src/conversation-id.js +18 -0
  34. package/dist/extensions/telegram/src/conversation-id.js.map +1 -0
  35. package/dist/extensions/telegram/src/doctor.d.ts +5 -0
  36. package/dist/extensions/telegram/src/doctor.js +80 -0
  37. package/dist/extensions/telegram/src/doctor.js.map +1 -0
  38. package/dist/extensions/telegram/src/exec-approval-handler.d.ts +7 -0
  39. package/dist/extensions/telegram/src/exec-approval-handler.js +36 -0
  40. package/dist/extensions/telegram/src/exec-approval-handler.js.map +1 -0
  41. package/dist/extensions/telegram/src/exec-approvals.d.ts +8 -0
  42. package/dist/extensions/telegram/src/exec-approvals.js +20 -0
  43. package/dist/extensions/telegram/src/exec-approvals.js.map +1 -0
  44. package/dist/extensions/telegram/src/focus-handler.d.ts +12 -0
  45. package/dist/extensions/telegram/src/focus-handler.js +55 -0
  46. package/dist/extensions/telegram/src/focus-handler.js.map +1 -0
  47. package/dist/extensions/telegram/src/group-config-resolver.d.ts +12 -0
  48. package/dist/extensions/telegram/src/group-config-resolver.js +17 -0
  49. package/dist/extensions/telegram/src/group-config-resolver.js.map +1 -0
  50. package/dist/extensions/telegram/src/inbound-coalescer.d.ts +15 -0
  51. package/dist/extensions/telegram/src/inbound-coalescer.js +107 -0
  52. package/dist/extensions/telegram/src/inbound-coalescer.js.map +1 -0
  53. package/dist/extensions/telegram/src/inbound-processor.d.ts +1 -0
  54. package/dist/extensions/telegram/src/inbound-processor.js +41 -4
  55. package/dist/extensions/telegram/src/inbound-processor.js.map +1 -1
  56. package/dist/extensions/telegram/src/network-errors.d.ts +2 -0
  57. package/dist/extensions/telegram/src/network-errors.js +23 -0
  58. package/dist/extensions/telegram/src/network-errors.js.map +1 -0
  59. package/dist/extensions/telegram/src/plugin.d.ts +11 -0
  60. package/dist/extensions/telegram/src/plugin.js +191 -57
  61. package/dist/extensions/telegram/src/plugin.js.map +1 -1
  62. package/dist/extensions/telegram/src/polling-session.d.ts +17 -0
  63. package/dist/extensions/telegram/src/polling-session.js +112 -0
  64. package/dist/extensions/telegram/src/polling-session.js.map +1 -0
  65. package/dist/extensions/telegram/src/proxy-fetch.d.ts +2 -0
  66. package/dist/extensions/telegram/src/proxy-fetch.js +14 -0
  67. package/dist/extensions/telegram/src/proxy-fetch.js.map +1 -0
  68. package/dist/extensions/telegram/src/reactions.d.ts +14 -0
  69. package/dist/extensions/telegram/src/reactions.js +48 -0
  70. package/dist/extensions/telegram/src/reactions.js.map +1 -0
  71. package/dist/extensions/telegram/src/reply-params.d.ts +15 -0
  72. package/dist/extensions/telegram/src/reply-params.js +28 -0
  73. package/dist/extensions/telegram/src/reply-params.js.map +1 -0
  74. package/dist/extensions/telegram/src/startup-errors.d.ts +3 -0
  75. package/dist/extensions/telegram/src/startup-errors.js +27 -0
  76. package/dist/extensions/telegram/src/startup-errors.js.map +1 -0
  77. package/dist/extensions/telegram/src/telegram-actions.d.ts +25 -0
  78. package/dist/extensions/telegram/src/telegram-actions.js +65 -0
  79. package/dist/extensions/telegram/src/telegram-actions.js.map +1 -0
  80. package/dist/extensions/telegram/src/thread-bindings.d.ts +15 -0
  81. package/dist/extensions/telegram/src/thread-bindings.js +22 -0
  82. package/dist/extensions/telegram/src/thread-bindings.js.map +1 -0
  83. package/dist/extensions/telegram/src/token-resolver.d.ts +8 -0
  84. package/dist/extensions/telegram/src/token-resolver.js +34 -0
  85. package/dist/extensions/telegram/src/token-resolver.js.map +1 -0
  86. package/dist/extensions/telegram/src/update-offset-store.d.ts +9 -0
  87. package/dist/extensions/telegram/src/update-offset-store.js +66 -0
  88. package/dist/extensions/telegram/src/update-offset-store.js.map +1 -0
  89. package/dist/extensions/telegram/xopc.extension.json +1 -1
  90. package/dist/gateway/static/root/assets/{agents-4KTK-Ku7.js → agents-C5jtBK-H.js} +1 -1
  91. package/dist/gateway/static/root/assets/{apps-page-DkgInY2w.js → apps-page-smJl-qf_.js} +1 -1
  92. package/dist/gateway/static/root/assets/channels-settings-CH6KgFK6.js +1 -0
  93. package/dist/gateway/static/root/assets/channels-status-swr-CV70xvY7.js +8 -0
  94. package/dist/gateway/static/root/assets/{cron-api-BRDli2Mm.js → cron-api-CgBO8B7I.js} +1 -1
  95. package/dist/gateway/static/root/assets/{cron-page-BzEr5Za8.js → cron-page-Be0cW1cR.js} +1 -1
  96. package/dist/gateway/static/root/assets/{dist-BsHh1vX0.js → dist-P8HRGUuh.js} +1 -1
  97. package/dist/gateway/static/root/assets/{extension-debug-page-TWt5q_ef.js → extension-debug-page-BowuycYu.js} +1 -1
  98. package/dist/gateway/static/root/assets/{extension-page-C3kV8ZvZ.js → extension-page-27-Zpv8O.js} +1 -1
  99. package/dist/gateway/static/root/assets/{extension-settings-page-Csy9LNC6.js → extension-settings-page-DDFa_rIq.js} +1 -1
  100. package/dist/gateway/static/root/assets/{fetch-BLLOP2CM.js → fetch-drJUxxKP.js} +1 -1
  101. package/dist/gateway/static/root/assets/{field-primitives-MXbM8HvY.js → field-primitives-BWLssxEb.js} +1 -1
  102. package/dist/gateway/static/root/assets/{heartbeat-config-api-CkoyqfRx.js → heartbeat-config-api-BVuYNYT_.js} +1 -1
  103. package/dist/gateway/static/root/assets/index-CRDAmr3W.js +4840 -0
  104. package/dist/gateway/static/root/assets/index-DjNDHzwG.css +1 -0
  105. package/dist/gateway/static/root/assets/{logs-page-B2vY41ph.js → logs-page-BDX8IrDQ.js} +1 -1
  106. package/dist/gateway/static/root/assets/{note-detail-page-B7qE5pkO.js → note-detail-page-BmQ9KeiA.js} +1 -1
  107. package/dist/gateway/static/root/assets/{note-time-NBBReVR4.js → note-time-14I9Sgqh.js} +1 -1
  108. package/dist/gateway/static/root/assets/{notes-page-CsWYaqYM.js → notes-page-BMfP0FFf.js} +1 -1
  109. package/dist/gateway/static/root/assets/{sessions-page-BPGY3SCT.js → sessions-page-C3scyChq.js} +1 -1
  110. package/dist/gateway/static/root/assets/{settings-advanced-gate-DtA451RM.js → settings-advanced-gate-2ODuCjHP.js} +1 -1
  111. package/dist/gateway/static/root/assets/{settings-form-section-DYR_SPlX.js → settings-form-section-DUoGKAiW.js} +1 -1
  112. package/dist/gateway/static/root/assets/{settings-page-BJUgZQzN.js → settings-page-CttAfOWV.js} +1 -1
  113. package/dist/gateway/static/root/assets/{share-preview-page-3u8VFmdo.js → share-preview-page-B77xqiiI.js} +1 -1
  114. package/dist/gateway/static/root/assets/{skills-page-DuyhcH4U.js → skills-page-B_bAZGzB.js} +1 -1
  115. package/dist/gateway/static/root/assets/{theme-store-e2q2yjs4.js → theme-store-BgCPrmxU.js} +1 -1
  116. package/dist/gateway/static/root/assets/url-q28QhI7l.js +3 -0
  117. package/dist/gateway/static/root/assets/{utils-B4Vw3fnc.js → utils-CCGejlcX.js} +1 -1
  118. package/dist/gateway/static/root/assets/{voice-api-key-field-BFY67Hhp.js → voice-api-key-field-DZzzihcY.js} +1 -1
  119. package/dist/gateway/static/root/assets/{workflow-page.utils-yUJ5jexd.js → workflow-page.utils-D5RMAmT4.js} +1 -1
  120. package/dist/gateway/static/root/assets/{workflows-page-DIMHCpNt.js → workflows-page-DEYpC6Bv.js} +1 -1
  121. package/dist/gateway/static/root/index.html +5 -5
  122. package/dist/package.js +1 -1
  123. package/dist/src/agent/agent-manager.d.ts +4 -0
  124. package/dist/src/agent/agent-manager.js +68 -4
  125. package/dist/src/agent/agent-manager.js.map +1 -1
  126. package/dist/src/agent/child-agent-factory.d.ts +1 -0
  127. package/dist/src/agent/child-agent-factory.js +13 -10
  128. package/dist/src/agent/child-agent-factory.js.map +1 -1
  129. package/dist/src/agent/embedded/run-for-session.d.ts +1 -0
  130. package/dist/src/agent/embedded/run-for-session.js +1 -0
  131. package/dist/src/agent/embedded/run-for-session.js.map +1 -1
  132. package/dist/src/agent/embedded/run-turn.js +1 -17
  133. package/dist/src/agent/embedded/run-turn.js.map +1 -1
  134. package/dist/src/agent/embedded/session-runner.js +3 -1
  135. package/dist/src/agent/embedded/session-runner.js.map +1 -1
  136. package/dist/src/agent/embedded/session-tool-result-guard.d.ts +1 -0
  137. package/dist/src/agent/embedded/session-tool-result-guard.js +1 -0
  138. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  139. package/dist/src/agent/inbound/attachment-pipeline.d.ts +37 -0
  140. package/dist/src/agent/inbound/attachment-pipeline.js +169 -0
  141. package/dist/src/agent/inbound/attachment-pipeline.js.map +1 -0
  142. package/dist/src/agent/inbound/turn-dispatcher.d.ts +3 -5
  143. package/dist/src/agent/inbound/turn-dispatcher.js +1 -2
  144. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  145. package/dist/src/agent/memory/index.d.ts +2 -0
  146. package/dist/src/agent/memory/index.js +2 -1
  147. package/dist/src/agent/memory/memory-flush.d.ts +27 -0
  148. package/dist/src/agent/memory/memory-flush.js +124 -0
  149. package/dist/src/agent/memory/memory-flush.js.map +1 -0
  150. package/dist/src/agent/messaging/outbound-coordinator.js +2 -1
  151. package/dist/src/agent/messaging/outbound-coordinator.js.map +1 -1
  152. package/dist/src/agent/messaging/stream-manager.d.ts +2 -0
  153. package/dist/src/agent/messaging/stream-manager.js +3 -0
  154. package/dist/src/agent/messaging/stream-manager.js.map +1 -1
  155. package/dist/src/agent/orchestration/agent-orchestrator.d.ts +0 -4
  156. package/dist/src/agent/orchestration/agent-orchestrator.js +40 -107
  157. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  158. package/dist/src/agent/prompt/contribution.d.ts +9 -0
  159. package/dist/src/agent/prompt/contribution.js +1 -0
  160. package/dist/src/agent/prompt/safety.js +0 -10
  161. package/dist/src/agent/prompt/safety.js.map +1 -1
  162. package/dist/src/agent/prompt/sanitize-for-prompt.d.ts +6 -0
  163. package/dist/src/agent/prompt/sanitize-for-prompt.js +13 -0
  164. package/dist/src/agent/prompt/sanitize-for-prompt.js.map +1 -0
  165. package/dist/src/agent/prompt/sections/behavior.d.ts +5 -0
  166. package/dist/src/agent/prompt/sections/behavior.js +50 -0
  167. package/dist/src/agent/prompt/sections/behavior.js.map +1 -0
  168. package/dist/src/agent/prompt/sections/memory-skills.d.ts +9 -0
  169. package/dist/src/agent/prompt/sections/memory-skills.js +59 -0
  170. package/dist/src/agent/prompt/sections/memory-skills.js.map +1 -0
  171. package/dist/src/agent/prompt/sections/messaging-runtime.d.ts +16 -0
  172. package/dist/src/agent/prompt/sections/messaging-runtime.js +69 -0
  173. package/dist/src/agent/prompt/sections/messaging-runtime.js.map +1 -0
  174. package/dist/src/agent/prompt/sections/project-context.d.ts +16 -0
  175. package/dist/src/agent/prompt/sections/project-context.js +58 -0
  176. package/dist/src/agent/prompt/sections/project-context.js.map +1 -0
  177. package/dist/src/agent/prompt/sections/tooling.d.ts +5 -0
  178. package/dist/src/agent/prompt/sections/tooling.js +119 -0
  179. package/dist/src/agent/prompt/sections/tooling.js.map +1 -0
  180. package/dist/src/agent/prompt/sections/workspace-runtime.d.ts +11 -0
  181. package/dist/src/agent/prompt/sections/workspace-runtime.js +32 -0
  182. package/dist/src/agent/prompt/sections/workspace-runtime.js.map +1 -0
  183. package/dist/src/agent/prompt/service-prompt-builder.d.ts +13 -0
  184. package/dist/src/agent/prompt/service-prompt-builder.js +54 -13
  185. package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
  186. package/dist/src/agent/prompt/subagent-context.d.ts +15 -0
  187. package/dist/src/agent/prompt/subagent-context.js +68 -0
  188. package/dist/src/agent/prompt/subagent-context.js.map +1 -0
  189. package/dist/src/agent/prompt/system-prompt-params.d.ts +53 -0
  190. package/dist/src/agent/prompt/system-prompt-params.js +74 -0
  191. package/dist/src/agent/prompt/system-prompt-params.js.map +1 -0
  192. package/dist/src/agent/prompt/system-prompt.d.ts +22 -11
  193. package/dist/src/agent/prompt/system-prompt.js +79 -193
  194. package/dist/src/agent/prompt/system-prompt.js.map +1 -1
  195. package/dist/src/agent/prompt/types.d.ts +3 -0
  196. package/dist/src/agent/service/build-direct-message-content.d.ts +5 -21
  197. package/dist/src/agent/service/build-direct-message-content.js +11 -64
  198. package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
  199. package/dist/src/agent/service/direct-turn-helpers.d.ts +1 -1
  200. package/dist/src/agent/service/direct-turn-helpers.js +7 -0
  201. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  202. package/dist/src/agent/service/process-direct-one-shot.d.ts +3 -3
  203. package/dist/src/agent/service/process-direct-one-shot.js +22 -17
  204. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  205. package/dist/src/agent/service/process-direct-streaming.d.ts +6 -20
  206. package/dist/src/agent/service/process-direct-streaming.js +39 -40
  207. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  208. package/dist/src/agent/service/webchat-tts.d.ts +4 -9
  209. package/dist/src/agent/service/webchat-tts.js +9 -20
  210. package/dist/src/agent/service/webchat-tts.js.map +1 -1
  211. package/dist/src/agent/service.d.ts +8 -19
  212. package/dist/src/agent/service.js +53 -11
  213. package/dist/src/agent/service.js.map +1 -1
  214. package/dist/src/agent/tools/factory.js +2 -0
  215. package/dist/src/agent/tools/factory.js.map +1 -1
  216. package/dist/src/agent/tools/index.d.ts +1 -0
  217. package/dist/src/agent/tools/index.js +2 -1
  218. package/dist/src/agent/tools/media-read-tool.d.ts +2 -0
  219. package/dist/src/agent/tools/media-read-tool.js +89 -0
  220. package/dist/src/agent/tools/media-read-tool.js.map +1 -0
  221. package/dist/src/channels/attachments/attachment-classifiers.d.ts +8 -0
  222. package/dist/src/channels/attachments/attachment-classifiers.js +11 -0
  223. package/dist/src/channels/attachments/attachment-classifiers.js.map +1 -0
  224. package/dist/src/channels/attachments/inbound-persist.d.ts +13 -34
  225. package/dist/src/channels/attachments/inbound-persist.js +34 -102
  226. package/dist/src/channels/attachments/inbound-persist.js.map +1 -1
  227. package/dist/src/channels/attachments/inbound-types.d.ts +13 -0
  228. package/dist/src/channels/attachments/inbound-types.js +30 -0
  229. package/dist/src/channels/attachments/inbound-types.js.map +1 -0
  230. package/dist/src/channels/attachments/outbound-tts-persist.d.ts +3 -12
  231. package/dist/src/channels/attachments/outbound-tts-persist.js +28 -48
  232. package/dist/src/channels/attachments/outbound-tts-persist.js.map +1 -1
  233. package/dist/src/channels/attachments/voice-stt-webchat.d.ts +3 -3
  234. package/dist/src/channels/attachments/voice-stt-webchat.js +8 -14
  235. package/dist/src/channels/attachments/voice-stt-webchat.js.map +1 -1
  236. package/dist/src/channels/channel-domain.d.ts +30 -2
  237. package/dist/src/channels/exec-approval-runtime.d.ts +29 -0
  238. package/dist/src/channels/exec-approval-runtime.js +30 -0
  239. package/dist/src/channels/exec-approval-runtime.js.map +1 -0
  240. package/dist/src/channels/index.js +1 -1
  241. package/dist/src/channels/plugin-types.d.ts +1 -0
  242. package/dist/src/channels/transport-types.d.ts +2 -2
  243. package/dist/src/cli/commands/channels.js +1 -1
  244. package/dist/src/cli/commands/doctor/checks/database-schema.d.ts +2 -0
  245. package/dist/src/cli/commands/doctor/checks/database-schema.js +62 -0
  246. package/dist/src/cli/commands/doctor/checks/database-schema.js.map +1 -0
  247. package/dist/src/cli/commands/doctor/flow.js +2 -0
  248. package/dist/src/cli/commands/doctor/flow.js.map +1 -1
  249. package/dist/src/cli/commands/init.js +2 -57
  250. package/dist/src/cli/commands/init.js.map +1 -1
  251. package/dist/src/cli/commands/onboard/channels/registry.js +1 -1
  252. package/dist/src/cli/commands/setup.js +6 -5
  253. package/dist/src/cli/commands/setup.js.map +1 -1
  254. package/dist/src/cli/utils/init-workspace-core.js +1 -2
  255. package/dist/src/cli/utils/init-workspace-core.js.map +1 -1
  256. package/dist/src/config/index.js +2 -2
  257. package/dist/src/config/paths.d.ts +0 -13
  258. package/dist/src/config/paths.js +1 -20
  259. package/dist/src/config/paths.js.map +1 -1
  260. package/dist/src/config/rules.js +1 -1
  261. package/dist/src/config/schema.d.ts +24 -0
  262. package/dist/src/config/schema.js +33 -2
  263. package/dist/src/config/schema.js.map +1 -1
  264. package/dist/src/cron/executor.js +2 -2
  265. package/dist/src/gateway/heartbeat/service.js +1 -1
  266. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  267. package/dist/src/gateway/hono/routes/config-patch/channels.js +3 -3
  268. package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -1
  269. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  270. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  271. package/dist/src/gateway/hono/routes/media.d.ts +3 -0
  272. package/dist/src/gateway/hono/routes/media.js +50 -0
  273. package/dist/src/gateway/hono/routes/media.js.map +1 -0
  274. package/dist/src/gateway/hono/routes/sessions.js +6 -0
  275. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  276. package/dist/src/gateway/hono/routes/workspace.js +1 -87
  277. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  278. package/dist/src/gateway/service/sessions-api.js +3 -0
  279. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  280. package/dist/src/heartbeat/index.js +1 -1
  281. package/dist/src/media/index.d.ts +5 -0
  282. package/dist/src/media/index.js +5 -0
  283. package/dist/src/media/media-reference.d.ts +23 -0
  284. package/dist/src/media/media-reference.js +34 -0
  285. package/dist/src/media/media-reference.js.map +1 -0
  286. package/dist/src/media/paths.d.ts +5 -0
  287. package/dist/src/media/paths.js +20 -0
  288. package/dist/src/media/paths.js.map +1 -0
  289. package/dist/src/media/session-references.d.ts +8 -0
  290. package/dist/src/media/session-references.js +45 -0
  291. package/dist/src/media/session-references.js.map +1 -0
  292. package/dist/src/media/store.d.ts +19 -0
  293. package/dist/src/media/store.js +131 -0
  294. package/dist/src/media/store.js.map +1 -0
  295. package/dist/src/media/types.d.ts +34 -0
  296. package/dist/src/media/types.js +1 -0
  297. package/dist/src/media/uri.d.ts +15 -0
  298. package/dist/src/media/uri.js +51 -0
  299. package/dist/src/media/uri.js.map +1 -0
  300. package/dist/src/session/session-title.js +6 -3
  301. package/dist/src/session/session-title.js.map +1 -1
  302. package/dist/src/session/store.js +1 -1
  303. package/dist/src/session/store.js.map +1 -1
  304. package/dist/src/session/types.d.ts +12 -7
  305. package/dist/src/session/types.js.map +1 -1
  306. package/dist/src/storage/sqlite/index.d.ts +4 -1
  307. package/dist/src/storage/sqlite/index.js +6 -2
  308. package/dist/src/storage/sqlite/migrations/002_add_flush_columns.sql +3 -0
  309. package/dist/src/storage/sqlite/migrations/discover.d.ts +4 -0
  310. package/dist/src/storage/sqlite/migrations/discover.js +38 -0
  311. package/dist/src/storage/sqlite/migrations/discover.js.map +1 -0
  312. package/dist/src/storage/sqlite/migrations/discover.ts +52 -0
  313. package/dist/src/storage/sqlite/migrations/errors.d.ts +11 -0
  314. package/dist/src/storage/sqlite/migrations/errors.js +27 -0
  315. package/dist/src/storage/sqlite/migrations/errors.js.map +1 -0
  316. package/dist/src/storage/sqlite/migrations/errors.ts +32 -0
  317. package/dist/src/storage/sqlite/migrations/runner.d.ts +22 -0
  318. package/dist/src/storage/sqlite/migrations/runner.js +97 -0
  319. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -0
  320. package/dist/src/storage/sqlite/migrations/runner.ts +153 -0
  321. package/dist/src/storage/sqlite/migrations/types.d.ts +12 -0
  322. package/dist/src/storage/sqlite/migrations/types.js +1 -0
  323. package/dist/src/storage/sqlite/migrations/types.ts +13 -0
  324. package/dist/src/storage/sqlite/row-mappers.d.ts +4 -0
  325. package/dist/src/storage/sqlite/row-mappers.js +4 -0
  326. package/dist/src/storage/sqlite/row-mappers.js.map +1 -1
  327. package/dist/src/storage/sqlite/schema-version.d.ts +6 -0
  328. package/dist/src/storage/sqlite/schema-version.js +29 -0
  329. package/dist/src/storage/sqlite/schema-version.js.map +1 -0
  330. package/dist/src/storage/sqlite/schema.d.ts +5 -3
  331. package/dist/src/storage/sqlite/schema.js +24 -28
  332. package/dist/src/storage/sqlite/schema.js.map +1 -1
  333. package/dist/src/storage/sqlite/schema.sql +2 -0
  334. package/dist/src/storage/sqlite/session-metadata.js +1 -0
  335. package/dist/src/storage/sqlite/session-metadata.js.map +1 -1
  336. package/dist/src/storage/sqlite/session-repository.js +7 -3
  337. package/dist/src/storage/sqlite/session-repository.js.map +1 -1
  338. package/package.json +1 -1
  339. package/dist/gateway/static/root/assets/channels-settings-BxW1Xio4.js +0 -1
  340. package/dist/gateway/static/root/assets/channels-status-swr-Cxm6AvQO.js +0 -8
  341. package/dist/gateway/static/root/assets/index-BJDmBCSl.css +0 -1
  342. package/dist/gateway/static/root/assets/index-Cbg5TS5m.js +0 -4707
  343. package/dist/gateway/static/root/assets/url-DpFBIyN9.js +0 -3
  344. package/dist/src/cli/commands/onboard/workspace.d.ts +0 -14
  345. package/dist/src/cli/commands/onboard/workspace.js +0 -33
  346. package/dist/src/cli/commands/onboard/workspace.js.map +0 -1
  347. package/dist/src/cli/utils/workspace.d.ts +0 -35
  348. package/dist/src/cli/utils/workspace.js +0 -72
  349. package/dist/src/cli/utils/workspace.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../../../src/session/types.ts"],"sourcesContent":["// Session management types\n\nimport type { TranscriptStoredRow } from './session-context-for-llm.js';\n\nexport interface Message {\n role: 'system' | 'user' | 'assistant' | 'tool' | 'toolResult';\n /** Plain string or structured content blocks (tool calls, multimodal). */\n content: string | unknown[];\n timestamp?: string;\n tool_call_id?: string;\n tool_calls?: Array<{\n id: string;\n type: 'function';\n function: {\n name: string;\n arguments: string;\n };\n }>;\n tool_call_id$?: string;\n toolName?: string;\n isError?: boolean;\n name?: string;\n /** Token usage from the LLM response (assistant messages only). */\n usage?: {\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n };\n /** Webchat early-save / UI: inbound file metadata (no base64). Omitted when absent. */\n attachments?: Array<{\n type?: string;\n mimeType?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>;\n}\n\n/** Session status enum */\nexport enum SessionStatus {\n ACTIVE = 'active',\n IDLE = 'idle',\n ARCHIVED = 'archived',\n PINNED = 'pinned',\n}\n\n/** Session routing metadata */\nexport interface SessionRoutingMeta {\n agentId: string;\n source: string;\n accountId: string;\n peerKind: string;\n peerId: string;\n threadId?: string;\n scopeId?: string;\n mainSessionKey?: string;\n lastRoutePolicy?: 'main' | 'session';\n}\n\n/** Session-level statistics (per session) */\nexport interface SessionStats {\n messageCount: number;\n tokenCount: number;\n turnCount?: number;\n lastTurnAt?: number;\n}\n\n/** Global session statistics (aggregate) */\nexport interface GlobalSessionStats {\n totalSessions: number;\n activeSessions: number;\n archivedSessions: number;\n pinnedSessions: number;\n totalMessages: number;\n totalTokens: number;\n oldestSession?: string;\n newestSession?: string;\n byChannel: Record<string, number>;\n}\n\n/** Session metadata (stored in index) */\nexport interface SessionMetadata {\n key: string;\n name?: string;\n status: SessionStatus;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n lastAccessedAt: string;\n messageCount: number;\n estimatedTokens: number;\n compactedCount: number;\n sourceChannel: string;\n sourceChatId: string;\n customData?: Record<string, unknown>;\n /** Routing metadata */\n routing?: SessionRoutingMeta;\n /** Session statistics */\n stats?: SessionStats;\n /**\n * High-level origin for filtering/UI (e.g. `cron`, `heartbeat`, `webchat`).\n * Distinct from `sourceChannel` (routing namespace).\n */\n sessionType?: string;\n /**\n * Stable transcript document id (wrapped on-disk format), aligned with OpenClaw `sessionId`.\n */\n transcriptId?: string;\n /** First activity time for this session row (ISO), from transcript header when available. */\n sessionStartedAt?: string;\n /** Last transcript write / interaction (ISO), updated on each persist. */\n lastInteractionAt?: string;\n /**\n * Epoch ms when the last webchat run was aborted (`POST /api/agent/abort`).\n * Used with `clientCreatedAtMs` on the next POST /api/agent to drop stale queued sends.\n */\n abortCutoffTimestamp?: number;\n}\n\n/** Summary of wrapped transcript (no duplicate message bodies). */\nexport interface SessionTranscriptSummary {\n id: string;\n version: number;\n createdAt: string;\n updatedAt: string;\n compactionCount: number;\n}\n\n/** Session detail (metadata + messages) */\nexport interface SessionDetail extends SessionMetadata {\n messages: Message[];\n /** Present when loaded with `includeTranscriptSummary` (gateway `?include=transcript`). */\n transcriptSummary?: SessionTranscriptSummary;\n /** Present when loaded with `include=transcriptRows` (full on-disk rows, LLM + `kind: 'context'`). */\n transcriptRows?: TranscriptStoredRow[];\n}\n\n/** Session list query parameters */\nexport interface SessionListQuery {\n status?: SessionStatus | SessionStatus[];\n /** Single `sourceChannel`, or comma-separated list (e.g. `telegram,weixin`). */\n channel?: string;\n tags?: string[];\n search?: string;\n sortBy?: 'updatedAt' | 'createdAt' | 'messageCount' | 'lastAccessedAt';\n sortOrder?: 'asc' | 'desc';\n limit?: number;\n offset?: number;\n}\n\n/** Paginated result */\nexport interface PaginatedResult<T> {\n items: T[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\n/** Export format */\nexport type ExportFormat = 'json' | 'markdown';\n\n/** Session export data */\nexport interface SessionExport {\n version: string;\n exportedAt: string;\n metadata: SessionMetadata;\n messages: Message[];\n /** Full on-disk rows (LLM + `kind: 'context'`) for audit / round-trip. */\n transcriptRows: TranscriptStoredRow[];\n}\n\n/** On-disk pre-compaction snapshot (OpenClaw-style checkpoint list). */\nexport interface CompactionCheckpointSummary {\n id: string;\n sizeBytes: number;\n modifiedAt: string;\n}\n\nexport interface CompactionCheckpointDetail extends CompactionCheckpointSummary {\n messageCount: number;\n}\n"],"mappings":";;AAuCA,IAAY,gBAAL,yBAAA,eAAA;AACL,eAAA,YAAA;AACA,eAAA,UAAA;AACA,eAAA,cAAA;AACA,eAAA,YAAA;;KACD"}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../../src/session/types.ts"],"sourcesContent":["// Session management types\n\nimport type { TranscriptStoredRow } from './session-context-for-llm.js';\n\nexport interface Message {\n role: 'system' | 'user' | 'assistant' | 'tool' | 'toolResult';\n /** Plain string or structured content blocks (tool calls, multimodal). */\n content: string | unknown[];\n timestamp?: string;\n tool_call_id?: string;\n tool_calls?: Array<{\n id: string;\n type: 'function';\n function: {\n name: string;\n arguments: string;\n };\n }>;\n tool_call_id$?: string;\n toolName?: string;\n isError?: boolean;\n name?: string;\n /** Token usage from the LLM response (assistant messages only). */\n usage?: {\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n };\n /** Inbound attachment metadata (no base64). */\n media?: Array<{\n id: string;\n bucket: string;\n type: string;\n mimeType: string;\n name: string;\n size: number;\n uri: string;\n path: string;\n }>;\n}\n\n/** Session status enum */\nexport enum SessionStatus {\n ACTIVE = 'active',\n IDLE = 'idle',\n ARCHIVED = 'archived',\n PINNED = 'pinned',\n}\n\n/** Session routing metadata */\nexport interface SessionRoutingMeta {\n agentId: string;\n source: string;\n accountId: string;\n peerKind: string;\n peerId: string;\n threadId?: string;\n scopeId?: string;\n mainSessionKey?: string;\n lastRoutePolicy?: 'main' | 'session';\n}\n\n/** Session-level statistics (per session) */\nexport interface SessionStats {\n messageCount: number;\n tokenCount: number;\n turnCount?: number;\n lastTurnAt?: number;\n}\n\n/** Global session statistics (aggregate) */\nexport interface GlobalSessionStats {\n totalSessions: number;\n activeSessions: number;\n archivedSessions: number;\n pinnedSessions: number;\n totalMessages: number;\n totalTokens: number;\n oldestSession?: string;\n newestSession?: string;\n byChannel: Record<string, number>;\n}\n\n/** Session metadata (stored in index) */\nexport interface SessionMetadata {\n key: string;\n name?: string;\n status: SessionStatus;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n lastAccessedAt: string;\n messageCount: number;\n estimatedTokens: number;\n compactedCount: number;\n lastFlushedAt?: string;\n flushCount?: number;\n sourceChannel: string;\n sourceChatId: string;\n customData?: Record<string, unknown>;\n /** Routing metadata */\n routing?: SessionRoutingMeta;\n /** Session statistics */\n stats?: SessionStats;\n /**\n * High-level origin for filtering/UI (e.g. `cron`, `heartbeat`, `webchat`).\n * Distinct from `sourceChannel` (routing namespace).\n */\n sessionType?: string;\n /**\n * Stable transcript document id (wrapped on-disk format), aligned with OpenClaw `sessionId`.\n */\n transcriptId?: string;\n /** First activity time for this session row (ISO), from transcript header when available. */\n sessionStartedAt?: string;\n /** Last transcript write / interaction (ISO), updated on each persist. */\n lastInteractionAt?: string;\n /**\n * Epoch ms when the last webchat run was aborted (`POST /api/agent/abort`).\n * Used with `clientCreatedAtMs` on the next POST /api/agent to drop stale queued sends.\n */\n abortCutoffTimestamp?: number;\n}\n\n/** Summary of wrapped transcript (no duplicate message bodies). */\nexport interface SessionTranscriptSummary {\n id: string;\n version: number;\n createdAt: string;\n updatedAt: string;\n compactionCount: number;\n}\n\n/** Session detail (metadata + messages) */\nexport interface SessionDetail extends SessionMetadata {\n messages: Message[];\n /** Present when loaded with `includeTranscriptSummary` (gateway `?include=transcript`). */\n transcriptSummary?: SessionTranscriptSummary;\n /** Present when loaded with `include=transcriptRows` (full on-disk rows, LLM + `kind: 'context'`). */\n transcriptRows?: TranscriptStoredRow[];\n}\n\n/** Session list query parameters */\nexport interface SessionListQuery {\n status?: SessionStatus | SessionStatus[];\n /** Single `sourceChannel`, or comma-separated list (e.g. `telegram,weixin`). */\n channel?: string;\n tags?: string[];\n search?: string;\n sortBy?: 'updatedAt' | 'createdAt' | 'messageCount' | 'lastAccessedAt';\n sortOrder?: 'asc' | 'desc';\n limit?: number;\n offset?: number;\n}\n\n/** Paginated result */\nexport interface PaginatedResult<T> {\n items: T[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\n/** Export format */\nexport type ExportFormat = 'json' | 'markdown';\n\n/** Session export data */\nexport interface SessionExport {\n version: string;\n exportedAt: string;\n metadata: SessionMetadata;\n messages: Message[];\n /** Full on-disk rows (LLM + `kind: 'context'`) for audit / round-trip. */\n transcriptRows: TranscriptStoredRow[];\n}\n\n/** On-disk pre-compaction snapshot (OpenClaw-style checkpoint list). */\nexport interface CompactionCheckpointSummary {\n id: string;\n sizeBytes: number;\n modifiedAt: string;\n}\n\nexport interface CompactionCheckpointDetail extends CompactionCheckpointSummary {\n messageCount: number;\n}\n"],"mappings":";;AA0CA,IAAY,gBAAL,yBAAA,eAAA;AACL,eAAA,YAAA;AACA,eAAA,UAAA;AACA,eAAA,cAAA;AACA,eAAA,YAAA;;KACD"}
@@ -1,5 +1,8 @@
1
1
  export { resolveXopcDatabasePath, XOPC_DB_FILENAME } from './paths.js';
2
- export { XOPC_DB_SCHEMA_VERSION, SCHEMA_META_SCHEMA_VERSION_KEY, ensureXopcDatabaseSchema, readSchemaVersionForTest, } from './schema.js';
2
+ export { XOPC_DB_SCHEMA_VERSION, XOPC_DB_BASELINE_SCHEMA_VERSION, SCHEMA_META_SCHEMA_VERSION_KEY, ensureXopcDatabaseSchema, readSchemaVersionForTest, readSchemaVersion, } from './schema.js';
3
+ export { applyPendingMigrations, inspectSchemaMigrationStatus, resolveMigrationsDir, type SchemaMigrationStatus, } from './migrations/runner.js';
4
+ export { DatabaseSchemaMigrationGapError, DatabaseSchemaTooNewError, } from './migrations/errors.js';
5
+ export { discoverSqlMigrations, listRegisteredMigrationTargets } from './migrations/discover.js';
3
6
  export { closeXopcDatabase, getXopcDatabase, isXopcDatabaseOpen, openXopcDatabase, requireXopcDatabase, resetXopcDatabaseSingletonForTest, type OpenXopcDatabaseOptions, type XopcDatabase, } from './connection.js';
4
7
  export { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';
5
8
  export { buildDefaultSessionMetadata, extractRoutingFromSessionKey, parseSessionKeySource, resolveAgentIdFromSessionKey, } from './session-metadata.js';
@@ -1,5 +1,9 @@
1
1
  import { XOPC_DB_FILENAME, resolveXopcDatabasePath } from "../../config/paths-state.js";
2
- import { SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_SCHEMA_VERSION, ensureXopcDatabaseSchema, readSchemaVersionForTest } from "./schema.js";
2
+ import { SCHEMA_META_SCHEMA_VERSION_KEY, readSchemaVersion, readSchemaVersionForTest } from "./schema-version.js";
3
+ import { discoverSqlMigrations, listRegisteredMigrationTargets } from "./migrations/discover.js";
4
+ import { DatabaseSchemaMigrationGapError, DatabaseSchemaTooNewError } from "./migrations/errors.js";
5
+ import { XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_SCHEMA_VERSION, applyPendingMigrations, inspectSchemaMigrationStatus, resolveMigrationsDir } from "./migrations/runner.js";
6
+ import { ensureXopcDatabaseSchema } from "./schema.js";
3
7
  import { closeXopcDatabase, getXopcDatabase, isXopcDatabaseOpen, openXopcDatabase, requireXopcDatabase, resetXopcDatabaseSingletonForTest } from "./connection.js";
4
8
  import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
5
9
  import { buildDefaultSessionMetadata, extractRoutingFromSessionKey, parseSessionKeySource, resolveAgentIdFromSessionKey } from "./session-metadata.js";
@@ -10,4 +14,4 @@ import { appendCronRun, deleteCronRunsForJob, readAllCronRuns, readCronJobHistor
10
14
  import { deleteNoteRecord, getNoteRecord, listNoteRecords, upsertNoteRecord } from "./notes-repository.js";
11
15
  import { resolveAgentIdFromMemoriesDir, searchMemoryIndex, syncMemoryIndex } from "./memory-index-repository.js";
12
16
  import { deleteSessionConfig, getSessionConfig, hasSessionConfig, setSessionConfig, updateSessionConfig } from "./config-repository.js";
13
- export { SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_FILENAME, XOPC_DB_SCHEMA_VERSION, appendCronRun, appendTranscriptEntry, buildDefaultSessionMetadata, buildGlobalSessionStats, captureCompactionCheckpoint, classifyStoredRow, closeXopcDatabase, deleteCronRunsForJob, deleteNoteRecord, deleteSessionConfig, deleteSessionRecord, ensureSessionRecord, ensureXopcDatabaseSchema, estimateTokensFromMessages, extractFtsContent, extractRoutingFromSessionKey, findSessionKeyByTranscriptId, getCompactionCheckpointDetail, getCurrentTranscriptId, getGlobalSessionStats, getNoteRecord, getSessionConfig, getSessionMetadata, getSessionPersistedLevels, getSqliteDatabase, getXopcDatabase, hasSessionConfig, incrementSessionStatsOnAppend, isXopcDatabaseOpen, listCompactionCheckpoints, listNoteRecords, listSessionMetadata, listSessionsByAgent, loadCheckpointRows, loadLlmMessagesForSession, loadTranscriptRows, loadTranscriptRowsForSession, metadataToSessionInsert, openXopcDatabase, paginateTranscriptMessages, parseSessionKeySource, patchSessionMetadata, readAllCronRuns, readCronJobHistory, readSchemaVersionForTest, replaceTranscriptRows, requireXopcDatabase, resetSessionRecord, resetXopcDatabaseSingletonForTest, resolveAgentIdFromMemoriesDir, resolveAgentIdFromSessionKey, resolveSessionAgentId, resolveXopcDatabasePath, restoreCompactionCheckpoint, runSqliteWriteTransaction, searchMemoryIndex, sessionConfigRowToConfig, sessionRowToMetadata, setSessionConfig, syncMemoryIndex, transcriptEntryRowToStoredRow, updateSessionConfig, updateSessionStats, upsertNoteRecord };
17
+ export { DatabaseSchemaMigrationGapError, DatabaseSchemaTooNewError, SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_FILENAME, XOPC_DB_SCHEMA_VERSION, appendCronRun, appendTranscriptEntry, applyPendingMigrations, buildDefaultSessionMetadata, buildGlobalSessionStats, captureCompactionCheckpoint, classifyStoredRow, closeXopcDatabase, deleteCronRunsForJob, deleteNoteRecord, deleteSessionConfig, deleteSessionRecord, discoverSqlMigrations, ensureSessionRecord, ensureXopcDatabaseSchema, estimateTokensFromMessages, extractFtsContent, extractRoutingFromSessionKey, findSessionKeyByTranscriptId, getCompactionCheckpointDetail, getCurrentTranscriptId, getGlobalSessionStats, getNoteRecord, getSessionConfig, getSessionMetadata, getSessionPersistedLevels, getSqliteDatabase, getXopcDatabase, hasSessionConfig, incrementSessionStatsOnAppend, inspectSchemaMigrationStatus, isXopcDatabaseOpen, listCompactionCheckpoints, listNoteRecords, listRegisteredMigrationTargets, listSessionMetadata, listSessionsByAgent, loadCheckpointRows, loadLlmMessagesForSession, loadTranscriptRows, loadTranscriptRowsForSession, metadataToSessionInsert, openXopcDatabase, paginateTranscriptMessages, parseSessionKeySource, patchSessionMetadata, readAllCronRuns, readCronJobHistory, readSchemaVersion, readSchemaVersionForTest, replaceTranscriptRows, requireXopcDatabase, resetSessionRecord, resetXopcDatabaseSingletonForTest, resolveAgentIdFromMemoriesDir, resolveAgentIdFromSessionKey, resolveMigrationsDir, resolveSessionAgentId, resolveXopcDatabasePath, restoreCompactionCheckpoint, runSqliteWriteTransaction, searchMemoryIndex, sessionConfigRowToConfig, sessionRowToMetadata, setSessionConfig, syncMemoryIndex, transcriptEntryRowToStoredRow, updateSessionConfig, updateSessionStats, upsertNoteRecord };
@@ -0,0 +1,3 @@
1
+ -- Migration: Add memory flush tracking columns to sessions table
2
+ ALTER TABLE sessions ADD COLUMN last_flushed_at TEXT;
3
+ ALTER TABLE sessions ADD COLUMN flush_count INTEGER NOT NULL DEFAULT 0;
@@ -0,0 +1,4 @@
1
+ import type { SqlMigration } from './types.js';
2
+ export declare function discoverSqlMigrations(migrationsDir: string): SqlMigration[];
3
+ export declare function validateMigrationSequence(migrations: SqlMigration[]): void;
4
+ export declare function listRegisteredMigrationTargets(migrationsDir: string): number[];
@@ -0,0 +1,38 @@
1
+ import { join } from "node:path";
2
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
3
+ //#region src/storage/sqlite/migrations/discover.ts
4
+ /** Matches `002_add_notes_index.sql` → target version 2. */
5
+ const MIGRATION_FILENAME = /^(\d{3})_[\w-]+\.sql$/;
6
+ function discoverSqlMigrations(migrationsDir) {
7
+ if (!existsSync(migrationsDir)) return [];
8
+ const migrations = [];
9
+ for (const entry of readdirSync(migrationsDir, { withFileTypes: true })) {
10
+ if (!entry.isFile()) continue;
11
+ const match = MIGRATION_FILENAME.exec(entry.name);
12
+ if (!match) continue;
13
+ const targetVersion = Number.parseInt(match[1], 10);
14
+ if (!Number.isInteger(targetVersion) || targetVersion < 2) throw new Error(`Invalid SQLite migration filename "${entry.name}" (target version must be >= 2)`);
15
+ migrations.push({
16
+ targetVersion,
17
+ filename: entry.name,
18
+ sql: readFileSync(join(migrationsDir, entry.name), "utf8")
19
+ });
20
+ }
21
+ migrations.sort((a, b) => a.targetVersion - b.targetVersion);
22
+ validateMigrationSequence(migrations);
23
+ return migrations;
24
+ }
25
+ function validateMigrationSequence(migrations) {
26
+ for (let index = 0; index < migrations.length; index++) {
27
+ const expected = index + 2;
28
+ const actual = migrations[index].targetVersion;
29
+ if (actual !== expected) throw new Error(`SQLite migration sequence gap: expected migration to version ${expected}, found ${migrations[index].filename} (v${actual})`);
30
+ }
31
+ }
32
+ function listRegisteredMigrationTargets(migrationsDir) {
33
+ return discoverSqlMigrations(migrationsDir).map((migration) => migration.targetVersion);
34
+ }
35
+ //#endregion
36
+ export { discoverSqlMigrations, listRegisteredMigrationTargets, validateMigrationSequence };
37
+
38
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","names":[],"sources":["../../../../../src/storage/sqlite/migrations/discover.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport type { SqlMigration } from './types.js';\n\n/** Matches `002_add_notes_index.sql` → target version 2. */\nconst MIGRATION_FILENAME = /^(\\d{3})_[\\w-]+\\.sql$/;\n\nexport function discoverSqlMigrations(migrationsDir: string): SqlMigration[] {\n if (!existsSync(migrationsDir)) {\n return [];\n }\n\n const migrations: SqlMigration[] = [];\n for (const entry of readdirSync(migrationsDir, { withFileTypes: true })) {\n if (!entry.isFile()) continue;\n const match = MIGRATION_FILENAME.exec(entry.name);\n if (!match) continue;\n const targetVersion = Number.parseInt(match[1]!, 10);\n if (!Number.isInteger(targetVersion) || targetVersion < 2) {\n throw new Error(\n `Invalid SQLite migration filename \"${entry.name}\" (target version must be >= 2)`,\n );\n }\n migrations.push({\n targetVersion,\n filename: entry.name,\n sql: readFileSync(join(migrationsDir, entry.name), 'utf8'),\n });\n }\n\n migrations.sort((a, b) => a.targetVersion - b.targetVersion);\n validateMigrationSequence(migrations);\n return migrations;\n}\n\nexport function validateMigrationSequence(migrations: SqlMigration[]): void {\n for (let index = 0; index < migrations.length; index++) {\n const expected = index + 2;\n const actual = migrations[index]!.targetVersion;\n if (actual !== expected) {\n throw new Error(\n `SQLite migration sequence gap: expected migration to version ${expected}, ` +\n `found ${migrations[index]!.filename} (v${actual})`,\n );\n }\n }\n}\n\nexport function listRegisteredMigrationTargets(migrationsDir: string): number[] {\n return discoverSqlMigrations(migrationsDir).map((migration) => migration.targetVersion);\n}\n"],"mappings":";;;;AAMA,MAAM,qBAAqB;AAE3B,SAAgB,sBAAsB,eAAuC;AAC3E,KAAI,CAAC,WAAW,cAAc,CAC5B,QAAO,EAAE;CAGX,MAAM,aAA6B,EAAE;AACrC,MAAK,MAAM,SAAS,YAAY,eAAe,EAAE,eAAe,MAAM,CAAC,EAAE;AACvE,MAAI,CAAC,MAAM,QAAQ,CAAE;EACrB,MAAM,QAAQ,mBAAmB,KAAK,MAAM,KAAK;AACjD,MAAI,CAAC,MAAO;EACZ,MAAM,gBAAgB,OAAO,SAAS,MAAM,IAAK,GAAG;AACpD,MAAI,CAAC,OAAO,UAAU,cAAc,IAAI,gBAAgB,EACtD,OAAM,IAAI,MACR,sCAAsC,MAAM,KAAK,iCAClD;AAEH,aAAW,KAAK;GACd;GACA,UAAU,MAAM;GAChB,KAAK,aAAa,KAAK,eAAe,MAAM,KAAK,EAAE,OAAO;GAC3D,CAAC;;AAGJ,YAAW,MAAM,GAAG,MAAM,EAAE,gBAAgB,EAAE,cAAc;AAC5D,2BAA0B,WAAW;AACrC,QAAO;;AAGT,SAAgB,0BAA0B,YAAkC;AAC1E,MAAK,IAAI,QAAQ,GAAG,QAAQ,WAAW,QAAQ,SAAS;EACtD,MAAM,WAAW,QAAQ;EACzB,MAAM,SAAS,WAAW,OAAQ;AAClC,MAAI,WAAW,SACb,OAAM,IAAI,MACR,gEAAgE,SAAS,UAC9D,WAAW,OAAQ,SAAS,KAAK,OAAO,GACpD;;;AAKP,SAAgB,+BAA+B,eAAiC;AAC9E,QAAO,sBAAsB,cAAc,CAAC,KAAK,cAAc,UAAU,cAAc"}
@@ -0,0 +1,52 @@
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ import type { SqlMigration } from './types.js';
5
+
6
+ /** Matches `002_add_notes_index.sql` → target version 2. */
7
+ const MIGRATION_FILENAME = /^(\d{3})_[\w-]+\.sql$/;
8
+
9
+ export function discoverSqlMigrations(migrationsDir: string): SqlMigration[] {
10
+ if (!existsSync(migrationsDir)) {
11
+ return [];
12
+ }
13
+
14
+ const migrations: SqlMigration[] = [];
15
+ for (const entry of readdirSync(migrationsDir, { withFileTypes: true })) {
16
+ if (!entry.isFile()) continue;
17
+ const match = MIGRATION_FILENAME.exec(entry.name);
18
+ if (!match) continue;
19
+ const targetVersion = Number.parseInt(match[1]!, 10);
20
+ if (!Number.isInteger(targetVersion) || targetVersion < 2) {
21
+ throw new Error(
22
+ `Invalid SQLite migration filename "${entry.name}" (target version must be >= 2)`,
23
+ );
24
+ }
25
+ migrations.push({
26
+ targetVersion,
27
+ filename: entry.name,
28
+ sql: readFileSync(join(migrationsDir, entry.name), 'utf8'),
29
+ });
30
+ }
31
+
32
+ migrations.sort((a, b) => a.targetVersion - b.targetVersion);
33
+ validateMigrationSequence(migrations);
34
+ return migrations;
35
+ }
36
+
37
+ export function validateMigrationSequence(migrations: SqlMigration[]): void {
38
+ for (let index = 0; index < migrations.length; index++) {
39
+ const expected = index + 2;
40
+ const actual = migrations[index]!.targetVersion;
41
+ if (actual !== expected) {
42
+ throw new Error(
43
+ `SQLite migration sequence gap: expected migration to version ${expected}, ` +
44
+ `found ${migrations[index]!.filename} (v${actual})`,
45
+ );
46
+ }
47
+ }
48
+ }
49
+
50
+ export function listRegisteredMigrationTargets(migrationsDir: string): number[] {
51
+ return discoverSqlMigrations(migrationsDir).map((migration) => migration.targetVersion);
52
+ }
@@ -0,0 +1,11 @@
1
+ export declare class DatabaseSchemaTooNewError extends Error {
2
+ readonly dbVersion: number;
3
+ readonly appVersion: number;
4
+ constructor(dbVersion: number, appVersion: number);
5
+ }
6
+ export declare class DatabaseSchemaMigrationGapError extends Error {
7
+ readonly dbVersion: number;
8
+ readonly appVersion: number;
9
+ readonly missingVersion: number;
10
+ constructor(dbVersion: number, appVersion: number, missingVersion: number);
11
+ }
@@ -0,0 +1,27 @@
1
+ //#region src/storage/sqlite/migrations/errors.ts
2
+ var DatabaseSchemaTooNewError = class extends Error {
3
+ dbVersion;
4
+ appVersion;
5
+ constructor(dbVersion, appVersion) {
6
+ super(`xopc database schema version ${dbVersion} is newer than this app supports (${appVersion}). Upgrade xopc to a newer release or restore xopc.db from backup.`);
7
+ this.name = "DatabaseSchemaTooNewError";
8
+ this.dbVersion = dbVersion;
9
+ this.appVersion = appVersion;
10
+ }
11
+ };
12
+ var DatabaseSchemaMigrationGapError = class extends Error {
13
+ dbVersion;
14
+ appVersion;
15
+ missingVersion;
16
+ constructor(dbVersion, appVersion, missingVersion) {
17
+ super(`xopc database schema version ${dbVersion} requires migration to v${missingVersion}, but no migration file is bundled with this app (supports up to v${appVersion}). Upgrade xopc to a release that includes the missing migration.`);
18
+ this.name = "DatabaseSchemaMigrationGapError";
19
+ this.dbVersion = dbVersion;
20
+ this.appVersion = appVersion;
21
+ this.missingVersion = missingVersion;
22
+ }
23
+ };
24
+ //#endregion
25
+ export { DatabaseSchemaMigrationGapError, DatabaseSchemaTooNewError };
26
+
27
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../../../../../src/storage/sqlite/migrations/errors.ts"],"sourcesContent":["export class DatabaseSchemaTooNewError extends Error {\n readonly dbVersion: number;\n readonly appVersion: number;\n\n constructor(dbVersion: number, appVersion: number) {\n super(\n `xopc database schema version ${dbVersion} is newer than this app supports (${appVersion}). ` +\n 'Upgrade xopc to a newer release or restore xopc.db from backup.',\n );\n this.name = 'DatabaseSchemaTooNewError';\n this.dbVersion = dbVersion;\n this.appVersion = appVersion;\n }\n}\n\nexport class DatabaseSchemaMigrationGapError extends Error {\n readonly dbVersion: number;\n readonly appVersion: number;\n readonly missingVersion: number;\n\n constructor(dbVersion: number, appVersion: number, missingVersion: number) {\n super(\n `xopc database schema version ${dbVersion} requires migration to v${missingVersion}, ` +\n `but no migration file is bundled with this app (supports up to v${appVersion}). ` +\n 'Upgrade xopc to a release that includes the missing migration.',\n );\n this.name = 'DatabaseSchemaMigrationGapError';\n this.dbVersion = dbVersion;\n this.appVersion = appVersion;\n this.missingVersion = missingVersion;\n }\n}\n"],"mappings":";AAAA,IAAa,4BAAb,cAA+C,MAAM;CACnD;CACA;CAEA,YAAY,WAAmB,YAAoB;AACjD,QACE,gCAAgC,UAAU,oCAAoC,WAAW,oEAE1F;AACD,OAAK,OAAO;AACZ,OAAK,YAAY;AACjB,OAAK,aAAa;;;AAItB,IAAa,kCAAb,cAAqD,MAAM;CACzD;CACA;CACA;CAEA,YAAY,WAAmB,YAAoB,gBAAwB;AACzE,QACE,gCAAgC,UAAU,0BAA0B,eAAe,oEACd,WAAW,mEAEjF;AACD,OAAK,OAAO;AACZ,OAAK,YAAY;AACjB,OAAK,aAAa;AAClB,OAAK,iBAAiB"}
@@ -0,0 +1,32 @@
1
+ export class DatabaseSchemaTooNewError extends Error {
2
+ readonly dbVersion: number;
3
+ readonly appVersion: number;
4
+
5
+ constructor(dbVersion: number, appVersion: number) {
6
+ super(
7
+ `xopc database schema version ${dbVersion} is newer than this app supports (${appVersion}). ` +
8
+ 'Upgrade xopc to a newer release or restore xopc.db from backup.',
9
+ );
10
+ this.name = 'DatabaseSchemaTooNewError';
11
+ this.dbVersion = dbVersion;
12
+ this.appVersion = appVersion;
13
+ }
14
+ }
15
+
16
+ export class DatabaseSchemaMigrationGapError extends Error {
17
+ readonly dbVersion: number;
18
+ readonly appVersion: number;
19
+ readonly missingVersion: number;
20
+
21
+ constructor(dbVersion: number, appVersion: number, missingVersion: number) {
22
+ super(
23
+ `xopc database schema version ${dbVersion} requires migration to v${missingVersion}, ` +
24
+ `but no migration file is bundled with this app (supports up to v${appVersion}). ` +
25
+ 'Upgrade xopc to a release that includes the missing migration.',
26
+ );
27
+ this.name = 'DatabaseSchemaMigrationGapError';
28
+ this.dbVersion = dbVersion;
29
+ this.appVersion = appVersion;
30
+ this.missingVersion = missingVersion;
31
+ }
32
+ }
@@ -0,0 +1,22 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ import type { ApplyMigrationsOptions } from './types.js';
3
+ /** Baseline schema version applied from schema.sql on first open. */
4
+ export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 2;
5
+ /** Latest schema version this release supports (increment when adding migrations). */
6
+ export declare const XOPC_DB_SCHEMA_VERSION = 2;
7
+ export declare function resolveMigrationsDir(override?: string): string;
8
+ /**
9
+ * Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
10
+ * Each step runs in its own transaction and updates schema_meta on success.
11
+ */
12
+ export declare function applyPendingMigrations(db: DatabaseSync, options?: ApplyMigrationsOptions): number;
13
+ export type SchemaMigrationStatus = {
14
+ dbVersion: number;
15
+ appVersion: number;
16
+ pendingVersions: number[];
17
+ isTooNew: boolean;
18
+ hasMigrationGap: boolean;
19
+ missingVersion: number | null;
20
+ };
21
+ /** Inspect schema version without mutating the database. */
22
+ export declare function inspectSchemaMigrationStatus(db: DatabaseSync, options?: Pick<ApplyMigrationsOptions, 'migrationsDir' | 'targetVersion'>): SchemaMigrationStatus;
@@ -0,0 +1,97 @@
1
+ import { createLogger } from "../../../utils/logger/index.js";
2
+ import { init_logger } from "../../../utils/logger.js";
3
+ import { readSchemaVersion, setSchemaVersion } from "../schema-version.js";
4
+ import { discoverSqlMigrations } from "./discover.js";
5
+ import { DatabaseSchemaMigrationGapError, DatabaseSchemaTooNewError } from "./errors.js";
6
+ import { dirname, join } from "node:path";
7
+ import { existsSync } from "node:fs";
8
+ import { fileURLToPath } from "node:url";
9
+ //#region src/storage/sqlite/migrations/runner.ts
10
+ init_logger();
11
+ const log = createLogger("Sqlite:Migrations");
12
+ /** Baseline schema version applied from schema.sql on first open. */
13
+ const XOPC_DB_BASELINE_SCHEMA_VERSION = 2;
14
+ /** Latest schema version this release supports (increment when adding migrations). */
15
+ const XOPC_DB_SCHEMA_VERSION = 2;
16
+ const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
17
+ function resolveMigrationsDir(override) {
18
+ if (override) return override;
19
+ const siblingDir = join(MODULE_DIR, "migrations");
20
+ if (existsSync(siblingDir)) return siblingDir;
21
+ return MODULE_DIR;
22
+ }
23
+ function migrationByTarget(migrations, targetVersion) {
24
+ return migrations.find((migration) => migration.targetVersion === targetVersion);
25
+ }
26
+ function applySingleMigration(db, migration) {
27
+ log.info({
28
+ targetVersion: migration.targetVersion,
29
+ file: migration.filename
30
+ }, "Applying SQLite migration");
31
+ db.exec("BEGIN IMMEDIATE");
32
+ try {
33
+ db.exec(migration.sql);
34
+ setSchemaVersion(db, migration.targetVersion);
35
+ db.exec("COMMIT");
36
+ } catch (error) {
37
+ try {
38
+ db.exec("ROLLBACK");
39
+ } catch {}
40
+ const em = error instanceof Error ? error.message : String(error);
41
+ throw new Error(`SQLite migration to v${migration.targetVersion} (${migration.filename}) failed: ${em}`, { cause: error });
42
+ }
43
+ }
44
+ /**
45
+ * Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
46
+ * Each step runs in its own transaction and updates schema_meta on success.
47
+ */
48
+ function applyPendingMigrations(db, options = {}) {
49
+ const targetVersion = options.targetVersion ?? 2;
50
+ let currentVersion = readSchemaVersion(db);
51
+ if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
52
+ if (currentVersion >= targetVersion) return currentVersion;
53
+ const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));
54
+ while (currentVersion < targetVersion) {
55
+ const nextVersion = currentVersion + 1;
56
+ const migration = migrationByTarget(migrations, nextVersion);
57
+ if (!migration) throw new DatabaseSchemaMigrationGapError(currentVersion, targetVersion, nextVersion);
58
+ applySingleMigration(db, migration);
59
+ currentVersion = nextVersion;
60
+ }
61
+ return currentVersion;
62
+ }
63
+ /** Inspect schema version without mutating the database. */
64
+ function inspectSchemaMigrationStatus(db, options = {}) {
65
+ const appVersion = options.targetVersion ?? 2;
66
+ const dbVersion = readSchemaVersion(db);
67
+ if (dbVersion > appVersion) return {
68
+ dbVersion,
69
+ appVersion,
70
+ pendingVersions: [],
71
+ isTooNew: true,
72
+ hasMigrationGap: false,
73
+ missingVersion: null
74
+ };
75
+ const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));
76
+ const pendingVersions = [];
77
+ let hasMigrationGap = false;
78
+ let missingVersion = null;
79
+ for (let version = dbVersion + 1; version <= appVersion; version++) if (migrationByTarget(migrations, version)) pendingVersions.push(version);
80
+ else {
81
+ hasMigrationGap = true;
82
+ missingVersion = version;
83
+ break;
84
+ }
85
+ return {
86
+ dbVersion,
87
+ appVersion,
88
+ pendingVersions,
89
+ isTooNew: false,
90
+ hasMigrationGap,
91
+ missingVersion
92
+ };
93
+ }
94
+ //#endregion
95
+ export { XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_SCHEMA_VERSION, applyPendingMigrations, inspectSchemaMigrationStatus, resolveMigrationsDir };
96
+
97
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","names":[],"sources":["../../../../../src/storage/sqlite/migrations/runner.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { readSchemaVersion, setSchemaVersion } from '../schema-version.js';\nimport { discoverSqlMigrations } from './discover.js';\nimport {\n DatabaseSchemaMigrationGapError,\n DatabaseSchemaTooNewError,\n} from './errors.js';\nimport type { ApplyMigrationsOptions, SqlMigration } from './types.js';\n\nconst log = createLogger('Sqlite:Migrations');\n\n/** Baseline schema version applied from schema.sql on first open. */\nexport const XOPC_DB_BASELINE_SCHEMA_VERSION = 2;\n\n/** Latest schema version this release supports (increment when adding migrations). */\nexport const XOPC_DB_SCHEMA_VERSION = 2;\n\nconst MODULE_DIR = dirname(fileURLToPath(import.meta.url));\n\nexport function resolveMigrationsDir(override?: string): string {\n if (override) {\n return override;\n }\n // Packaged Electron gateway bundle: `out/server/index.js` + `out/server/migrations/`.\n const siblingDir = join(MODULE_DIR, 'migrations');\n if (existsSync(siblingDir)) {\n return siblingDir;\n }\n // Dev / dist: SQL files live next to `migrations/runner.js`.\n return MODULE_DIR;\n}\n\nfunction migrationByTarget(\n migrations: SqlMigration[],\n targetVersion: number,\n): SqlMigration | undefined {\n return migrations.find((migration) => migration.targetVersion === targetVersion);\n}\n\nfunction applySingleMigration(db: DatabaseSync, migration: SqlMigration): void {\n log.info({ targetVersion: migration.targetVersion, file: migration.filename }, 'Applying SQLite migration');\n db.exec('BEGIN IMMEDIATE');\n try {\n db.exec(migration.sql);\n setSchemaVersion(db, migration.targetVersion);\n db.exec('COMMIT');\n } catch (error) {\n try {\n db.exec('ROLLBACK');\n } catch {\n /* preserve original error */\n }\n const em = error instanceof Error ? error.message : String(error);\n throw new Error(\n `SQLite migration to v${migration.targetVersion} (${migration.filename}) failed: ${em}`,\n { cause: error },\n );\n }\n}\n\n/**\n * Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.\n * Each step runs in its own transaction and updates schema_meta on success.\n */\nexport function applyPendingMigrations(\n db: DatabaseSync,\n options: ApplyMigrationsOptions = {},\n): number {\n const targetVersion = options.targetVersion ?? XOPC_DB_SCHEMA_VERSION;\n let currentVersion = readSchemaVersion(db);\n\n if (currentVersion > targetVersion) {\n throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);\n }\n\n if (currentVersion >= targetVersion) {\n return currentVersion;\n }\n\n const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));\n\n while (currentVersion < targetVersion) {\n const nextVersion = currentVersion + 1;\n const migration = migrationByTarget(migrations, nextVersion);\n if (!migration) {\n throw new DatabaseSchemaMigrationGapError(currentVersion, targetVersion, nextVersion);\n }\n applySingleMigration(db, migration);\n currentVersion = nextVersion;\n }\n\n return currentVersion;\n}\n\nexport type SchemaMigrationStatus = {\n dbVersion: number;\n appVersion: number;\n pendingVersions: number[];\n isTooNew: boolean;\n hasMigrationGap: boolean;\n missingVersion: number | null;\n};\n\n/** Inspect schema version without mutating the database. */\nexport function inspectSchemaMigrationStatus(\n db: DatabaseSync,\n options: Pick<ApplyMigrationsOptions, 'migrationsDir' | 'targetVersion'> = {},\n): SchemaMigrationStatus {\n const appVersion = options.targetVersion ?? XOPC_DB_SCHEMA_VERSION;\n const dbVersion = readSchemaVersion(db);\n const isTooNew = dbVersion > appVersion;\n\n if (isTooNew) {\n return {\n dbVersion,\n appVersion,\n pendingVersions: [],\n isTooNew: true,\n hasMigrationGap: false,\n missingVersion: null,\n };\n }\n\n const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));\n const pendingVersions: number[] = [];\n let hasMigrationGap = false;\n let missingVersion: number | null = null;\n\n for (let version = dbVersion + 1; version <= appVersion; version++) {\n if (migrationByTarget(migrations, version)) {\n pendingVersions.push(version);\n } else {\n hasMigrationGap = true;\n missingVersion = version;\n break;\n }\n }\n\n return {\n dbVersion,\n appVersion,\n pendingVersions,\n isTooNew: false,\n hasMigrationGap,\n missingVersion,\n };\n}\n"],"mappings":";;;;;;;;;aAMwD;AASxD,MAAM,MAAM,aAAa,oBAAoB;;AAG7C,MAAa,kCAAkC;;AAG/C,MAAa,yBAAyB;AAEtC,MAAM,aAAa,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE1D,SAAgB,qBAAqB,UAA2B;AAC9D,KAAI,SACF,QAAO;CAGT,MAAM,aAAa,KAAK,YAAY,aAAa;AACjD,KAAI,WAAW,WAAW,CACxB,QAAO;AAGT,QAAO;;AAGT,SAAS,kBACP,YACA,eAC0B;AAC1B,QAAO,WAAW,MAAM,cAAc,UAAU,kBAAkB,cAAc;;AAGlF,SAAS,qBAAqB,IAAkB,WAA+B;AAC7E,KAAI,KAAK;EAAE,eAAe,UAAU;EAAe,MAAM,UAAU;EAAU,EAAE,4BAA4B;AAC3G,IAAG,KAAK,kBAAkB;AAC1B,KAAI;AACF,KAAG,KAAK,UAAU,IAAI;AACtB,mBAAiB,IAAI,UAAU,cAAc;AAC7C,KAAG,KAAK,SAAS;UACV,OAAO;AACd,MAAI;AACF,MAAG,KAAK,WAAW;UACb;EAGR,MAAM,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACjE,QAAM,IAAI,MACR,wBAAwB,UAAU,cAAc,IAAI,UAAU,SAAS,YAAY,MACnF,EAAE,OAAO,OAAO,CACjB;;;;;;;AAQL,SAAgB,uBACd,IACA,UAAkC,EAAE,EAC5B;CACR,MAAM,gBAAgB,QAAQ,iBAAA;CAC9B,IAAI,iBAAiB,kBAAkB,GAAG;AAE1C,KAAI,iBAAiB,cACnB,OAAM,IAAI,0BAA0B,gBAAgB,cAAc;AAGpE,KAAI,kBAAkB,cACpB,QAAO;CAGT,MAAM,aAAa,sBAAsB,qBAAqB,QAAQ,cAAc,CAAC;AAErF,QAAO,iBAAiB,eAAe;EACrC,MAAM,cAAc,iBAAiB;EACrC,MAAM,YAAY,kBAAkB,YAAY,YAAY;AAC5D,MAAI,CAAC,UACH,OAAM,IAAI,gCAAgC,gBAAgB,eAAe,YAAY;AAEvF,uBAAqB,IAAI,UAAU;AACnC,mBAAiB;;AAGnB,QAAO;;;AAaT,SAAgB,6BACd,IACA,UAA2E,EAAE,EACtD;CACvB,MAAM,aAAa,QAAQ,iBAAA;CAC3B,MAAM,YAAY,kBAAkB,GAAG;AAGvC,KAFiB,YAAY,WAG3B,QAAO;EACL;EACA;EACA,iBAAiB,EAAE;EACnB,UAAU;EACV,iBAAiB;EACjB,gBAAgB;EACjB;CAGH,MAAM,aAAa,sBAAsB,qBAAqB,QAAQ,cAAc,CAAC;CACrF,MAAM,kBAA4B,EAAE;CACpC,IAAI,kBAAkB;CACtB,IAAI,iBAAgC;AAEpC,MAAK,IAAI,UAAU,YAAY,GAAG,WAAW,YAAY,UACvD,KAAI,kBAAkB,YAAY,QAAQ,CACxC,iBAAgB,KAAK,QAAQ;MACxB;AACL,oBAAkB;AAClB,mBAAiB;AACjB;;AAIJ,QAAO;EACL;EACA;EACA;EACA,UAAU;EACV;EACA;EACD"}
@@ -0,0 +1,153 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ import type { DatabaseSync } from 'node:sqlite';
6
+
7
+ import { createLogger } from '../../../utils/logger.js';
8
+ import { readSchemaVersion, setSchemaVersion } from '../schema-version.js';
9
+ import { discoverSqlMigrations } from './discover.js';
10
+ import {
11
+ DatabaseSchemaMigrationGapError,
12
+ DatabaseSchemaTooNewError,
13
+ } from './errors.js';
14
+ import type { ApplyMigrationsOptions, SqlMigration } from './types.js';
15
+
16
+ const log = createLogger('Sqlite:Migrations');
17
+
18
+ /** Baseline schema version applied from schema.sql on first open. */
19
+ export const XOPC_DB_BASELINE_SCHEMA_VERSION = 2;
20
+
21
+ /** Latest schema version this release supports (increment when adding migrations). */
22
+ export const XOPC_DB_SCHEMA_VERSION = 2;
23
+
24
+ const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
25
+
26
+ export function resolveMigrationsDir(override?: string): string {
27
+ if (override) {
28
+ return override;
29
+ }
30
+ // Packaged Electron gateway bundle: `out/server/index.js` + `out/server/migrations/`.
31
+ const siblingDir = join(MODULE_DIR, 'migrations');
32
+ if (existsSync(siblingDir)) {
33
+ return siblingDir;
34
+ }
35
+ // Dev / dist: SQL files live next to `migrations/runner.js`.
36
+ return MODULE_DIR;
37
+ }
38
+
39
+ function migrationByTarget(
40
+ migrations: SqlMigration[],
41
+ targetVersion: number,
42
+ ): SqlMigration | undefined {
43
+ return migrations.find((migration) => migration.targetVersion === targetVersion);
44
+ }
45
+
46
+ function applySingleMigration(db: DatabaseSync, migration: SqlMigration): void {
47
+ log.info({ targetVersion: migration.targetVersion, file: migration.filename }, 'Applying SQLite migration');
48
+ db.exec('BEGIN IMMEDIATE');
49
+ try {
50
+ db.exec(migration.sql);
51
+ setSchemaVersion(db, migration.targetVersion);
52
+ db.exec('COMMIT');
53
+ } catch (error) {
54
+ try {
55
+ db.exec('ROLLBACK');
56
+ } catch {
57
+ /* preserve original error */
58
+ }
59
+ const em = error instanceof Error ? error.message : String(error);
60
+ throw new Error(
61
+ `SQLite migration to v${migration.targetVersion} (${migration.filename}) failed: ${em}`,
62
+ { cause: error },
63
+ );
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
69
+ * Each step runs in its own transaction and updates schema_meta on success.
70
+ */
71
+ export function applyPendingMigrations(
72
+ db: DatabaseSync,
73
+ options: ApplyMigrationsOptions = {},
74
+ ): number {
75
+ const targetVersion = options.targetVersion ?? XOPC_DB_SCHEMA_VERSION;
76
+ let currentVersion = readSchemaVersion(db);
77
+
78
+ if (currentVersion > targetVersion) {
79
+ throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
80
+ }
81
+
82
+ if (currentVersion >= targetVersion) {
83
+ return currentVersion;
84
+ }
85
+
86
+ const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));
87
+
88
+ while (currentVersion < targetVersion) {
89
+ const nextVersion = currentVersion + 1;
90
+ const migration = migrationByTarget(migrations, nextVersion);
91
+ if (!migration) {
92
+ throw new DatabaseSchemaMigrationGapError(currentVersion, targetVersion, nextVersion);
93
+ }
94
+ applySingleMigration(db, migration);
95
+ currentVersion = nextVersion;
96
+ }
97
+
98
+ return currentVersion;
99
+ }
100
+
101
+ export type SchemaMigrationStatus = {
102
+ dbVersion: number;
103
+ appVersion: number;
104
+ pendingVersions: number[];
105
+ isTooNew: boolean;
106
+ hasMigrationGap: boolean;
107
+ missingVersion: number | null;
108
+ };
109
+
110
+ /** Inspect schema version without mutating the database. */
111
+ export function inspectSchemaMigrationStatus(
112
+ db: DatabaseSync,
113
+ options: Pick<ApplyMigrationsOptions, 'migrationsDir' | 'targetVersion'> = {},
114
+ ): SchemaMigrationStatus {
115
+ const appVersion = options.targetVersion ?? XOPC_DB_SCHEMA_VERSION;
116
+ const dbVersion = readSchemaVersion(db);
117
+ const isTooNew = dbVersion > appVersion;
118
+
119
+ if (isTooNew) {
120
+ return {
121
+ dbVersion,
122
+ appVersion,
123
+ pendingVersions: [],
124
+ isTooNew: true,
125
+ hasMigrationGap: false,
126
+ missingVersion: null,
127
+ };
128
+ }
129
+
130
+ const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));
131
+ const pendingVersions: number[] = [];
132
+ let hasMigrationGap = false;
133
+ let missingVersion: number | null = null;
134
+
135
+ for (let version = dbVersion + 1; version <= appVersion; version++) {
136
+ if (migrationByTarget(migrations, version)) {
137
+ pendingVersions.push(version);
138
+ } else {
139
+ hasMigrationGap = true;
140
+ missingVersion = version;
141
+ break;
142
+ }
143
+ }
144
+
145
+ return {
146
+ dbVersion,
147
+ appVersion,
148
+ pendingVersions,
149
+ isTooNew: false,
150
+ hasMigrationGap,
151
+ missingVersion,
152
+ };
153
+ }
@@ -0,0 +1,12 @@
1
+ /** One on-disk SQL migration file (target schema version after apply). */
2
+ export type SqlMigration = {
3
+ targetVersion: number;
4
+ filename: string;
5
+ sql: string;
6
+ };
7
+ export type ApplyMigrationsOptions = {
8
+ /** Override migrations directory (tests). Defaults to `<schema-module>/migrations`. */
9
+ migrationsDir?: string;
10
+ /** Target schema version. Defaults to {@link XOPC_DB_SCHEMA_VERSION}. */
11
+ targetVersion?: number;
12
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ /** One on-disk SQL migration file (target schema version after apply). */
2
+ export type SqlMigration = {
3
+ targetVersion: number;
4
+ filename: string;
5
+ sql: string;
6
+ };
7
+
8
+ export type ApplyMigrationsOptions = {
9
+ /** Override migrations directory (tests). Defaults to `<schema-module>/migrations`. */
10
+ migrationsDir?: string;
11
+ /** Target schema version. Defaults to {@link XOPC_DB_SCHEMA_VERSION}. */
12
+ targetVersion?: number;
13
+ };
@@ -23,6 +23,8 @@ export type SessionRow = {
23
23
  message_count: number;
24
24
  estimated_tokens: number;
25
25
  compacted_count: number;
26
+ last_flushed_at: string | null;
27
+ flush_count: number;
26
28
  thinking_level: string | null;
27
29
  verbose_level: string | null;
28
30
  };
@@ -68,6 +70,8 @@ export declare function metadataToSessionInsert(sessionKey: string, transcriptId
68
70
  messageCount: number;
69
71
  estimatedTokens: number;
70
72
  compactedCount: number;
73
+ lastFlushedAt: string | null;
74
+ flushCount: number;
71
75
  thinkingLevel: string | null;
72
76
  verboseLevel: string | null;
73
77
  };
@@ -45,6 +45,8 @@ function sessionRowToMetadata(sessionKey, row) {
45
45
  messageCount: row.message_count,
46
46
  estimatedTokens: row.estimated_tokens,
47
47
  compactedCount: row.compacted_count,
48
+ lastFlushedAt: row.last_flushed_at ?? void 0,
49
+ flushCount: row.flush_count,
48
50
  transcriptId: row.current_transcript_id,
49
51
  stats: {
50
52
  messageCount: row.message_count,
@@ -76,6 +78,8 @@ function metadataToSessionInsert(sessionKey, transcriptId, metadata, thinkingLev
76
78
  messageCount: metadata.messageCount,
77
79
  estimatedTokens: metadata.estimatedTokens,
78
80
  compactedCount: metadata.compactedCount,
81
+ lastFlushedAt: metadata.lastFlushedAt ?? null,
82
+ flushCount: metadata.flushCount ?? 0,
79
83
  thinkingLevel: thinkingLevel ?? null,
80
84
  verboseLevel: verboseLevel ?? null
81
85
  };