@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":"row-mappers.js","names":["msg"],"sources":["../../../../src/storage/sqlite/row-mappers.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { SessionAgentConfig } from '../../session/config-types.js';\nimport {\n isTranscriptContextEntry,\n type TranscriptStoredRow,\n type XopcTranscriptContextEntry,\n} from '../../session/session-context-for-llm.js';\nimport { SessionStatus, type GlobalSessionStats, type SessionMetadata } from '../../session/types.js';\nimport {\n buildDefaultSessionMetadata,\n parseSessionKeySource,\n resolveAgentIdFromSessionKey,\n} from './session-metadata.js';\n\nexport type SessionRow = {\n session_key: string;\n agent_id: string;\n current_transcript_id: string;\n status: string;\n name: string | null;\n tags_json: string;\n created_at: number;\n updated_at: number;\n last_accessed_at: number;\n session_started_at: number | null;\n last_interaction_at: number | null;\n source_channel: string;\n source_chat_id: string;\n session_type: string | null;\n routing_json: string | null;\n custom_data_json: string | null;\n abort_cutoff_timestamp: number | null;\n message_count: number;\n estimated_tokens: number;\n compacted_count: number;\n thinking_level: string | null;\n verbose_level: string | null;\n};\n\nexport type TranscriptEntryRow = {\n entry_id: string;\n transcript_id: string;\n seq: number;\n entry_kind: string;\n role: string | null;\n payload_json: string;\n created_at: number;\n};\n\nexport type SessionConfigRow = {\n session_key: string;\n thinking_level: string | null;\n reasoning_level: string | null;\n verbose_level: string | null;\n elevated_mode: string | null;\n model_override: string | null;\n provider_override: string | null;\n working_directory_override: string | null;\n updated_at: number;\n};\n\nfunction parseJson<T>(raw: string | null | undefined): T | undefined {\n if (!raw?.trim()) {\n return undefined;\n }\n try {\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n}\n\nfunction isoFromMs(ms: number | null | undefined): string | undefined {\n if (ms == null || !Number.isFinite(ms)) {\n return undefined;\n }\n return new Date(ms).toISOString();\n}\n\nexport function sessionRowToMetadata(sessionKey: string, row: SessionRow): SessionMetadata {\n const defaults = buildDefaultSessionMetadata(sessionKey);\n const { channel: keySource, chatId: keyChatId } = parseSessionKeySource(sessionKey);\n const routing = parseJson<SessionMetadata['routing']>(row.routing_json) ?? defaults.routing;\n const customData = parseJson<Record<string, unknown>>(row.custom_data_json) ?? defaults.customData;\n const tags = parseJson<string[]>(row.tags_json) ?? [];\n const createdAt = isoFromMs(row.created_at) ?? defaults.createdAt;\n const updatedAt = isoFromMs(row.updated_at) ?? defaults.updatedAt;\n const lastAccessedAt = isoFromMs(row.last_accessed_at) ?? defaults.lastAccessedAt;\n const diskSc = row.source_channel?.trim() ?? '';\n const diskChat = row.source_chat_id?.trim() ?? '';\n\n return {\n ...defaults,\n key: sessionKey,\n status: row.status as SessionStatus,\n name: row.name ?? undefined,\n tags,\n createdAt,\n updatedAt,\n lastAccessedAt,\n sessionStartedAt: isoFromMs(row.session_started_at),\n lastInteractionAt: isoFromMs(row.last_interaction_at),\n sourceChannel: diskSc || keySource,\n sourceChatId: diskChat || keyChatId,\n sessionType: row.session_type ?? defaults.sessionType,\n routing,\n customData,\n abortCutoffTimestamp: row.abort_cutoff_timestamp ?? undefined,\n messageCount: row.message_count,\n estimatedTokens: row.estimated_tokens,\n compactedCount: row.compacted_count,\n transcriptId: row.current_transcript_id,\n stats: {\n messageCount: row.message_count,\n tokenCount: row.estimated_tokens,\n lastTurnAt: row.last_interaction_at ?? undefined,\n },\n };\n}\n\nexport function metadataToSessionInsert(\n sessionKey: string,\n transcriptId: string,\n metadata: SessionMetadata,\n thinkingLevel?: string | null,\n verboseLevel?: string | null,\n): {\n sessionKey: string;\n agentId: string;\n transcriptId: string;\n status: string;\n name: string | null;\n tagsJson: string;\n createdAt: number;\n updatedAt: number;\n lastAccessedAt: number;\n sessionStartedAt: number | null;\n lastInteractionAt: number | null;\n sourceChannel: string;\n sourceChatId: string;\n sessionType: string | null;\n routingJson: string | null;\n customDataJson: string | null;\n abortCutoffTimestamp: number | null;\n messageCount: number;\n estimatedTokens: number;\n compactedCount: number;\n thinkingLevel: string | null;\n verboseLevel: string | null;\n} {\n const now = Date.now();\n return {\n sessionKey,\n agentId: resolveAgentIdFromSessionKey(sessionKey),\n transcriptId,\n status: metadata.status,\n name: metadata.name ?? null,\n tagsJson: JSON.stringify(metadata.tags ?? []),\n createdAt: Date.parse(metadata.createdAt) || now,\n updatedAt: Date.parse(metadata.updatedAt) || now,\n lastAccessedAt: Date.parse(metadata.lastAccessedAt) || now,\n sessionStartedAt: metadata.sessionStartedAt ? Date.parse(metadata.sessionStartedAt) : now,\n lastInteractionAt: metadata.lastInteractionAt ? Date.parse(metadata.lastInteractionAt) : null,\n sourceChannel: metadata.sourceChannel,\n sourceChatId: metadata.sourceChatId,\n sessionType: metadata.sessionType ?? null,\n routingJson: metadata.routing ? JSON.stringify(metadata.routing) : null,\n customDataJson: metadata.customData ? JSON.stringify(metadata.customData) : null,\n abortCutoffTimestamp: metadata.abortCutoffTimestamp ?? null,\n messageCount: metadata.messageCount,\n estimatedTokens: metadata.estimatedTokens,\n compactedCount: metadata.compactedCount,\n thinkingLevel: thinkingLevel ?? null,\n verboseLevel: verboseLevel ?? null,\n };\n}\n\nexport function sessionConfigRowToConfig(row: SessionConfigRow): SessionAgentConfig {\n return {\n ...(row.thinking_level ? { thinkingLevel: row.thinking_level as SessionAgentConfig['thinkingLevel'] } : {}),\n ...(row.reasoning_level ? { reasoningLevel: row.reasoning_level as SessionAgentConfig['reasoningLevel'] } : {}),\n ...(row.verbose_level ? { verboseLevel: row.verbose_level as SessionAgentConfig['verboseLevel'] } : {}),\n ...(row.elevated_mode ? { elevatedMode: row.elevated_mode as SessionAgentConfig['elevatedMode'] } : {}),\n ...(row.model_override ? { modelOverride: row.model_override } : {}),\n ...(row.provider_override ? { providerOverride: row.provider_override } : {}),\n ...(row.working_directory_override ? { workingDirectoryOverride: row.working_directory_override } : {}),\n updatedAt: row.updated_at,\n };\n}\n\nexport function transcriptEntryRowToStoredRow(row: TranscriptEntryRow): TranscriptStoredRow {\n const payload = JSON.parse(row.payload_json) as TranscriptStoredRow;\n return payload;\n}\n\nexport function classifyStoredRow(row: TranscriptStoredRow): {\n entryKind: 'message' | 'context' | 'compaction';\n role: string | null;\n} {\n if (isTranscriptContextEntry(row)) {\n return { entryKind: 'context', role: null };\n }\n const record = row as AgentMessage & { type?: string };\n if (record.type === 'compaction' || (row as { kind?: string }).kind === 'compaction') {\n return { entryKind: 'compaction', role: null };\n }\n return { entryKind: 'message', role: record.role ?? null };\n}\n\nexport function extractFtsContent(row: TranscriptStoredRow): string {\n if (isTranscriptContextEntry(row)) {\n const ctx = row as XopcTranscriptContextEntry;\n return [ctx.text, ctx.id].filter(Boolean).join(' ');\n }\n const msg = row as AgentMessage;\n return extractTextFromMessageContent((msg as { content?: unknown }).content);\n}\n\nfunction extractTextFromMessageContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (!Array.isArray(content)) {\n return '';\n }\n const parts: string[] = [];\n for (const block of content) {\n if (!block || typeof block !== 'object') {\n continue;\n }\n const type = (block as { type?: string }).type;\n if (type === 'text' && typeof (block as { text?: string }).text === 'string') {\n parts.push((block as { text: string }).text);\n }\n }\n return parts.join('\\n');\n}\n\nexport function estimateTokensFromMessages(messages: AgentMessage[]): number {\n let total = 0;\n for (const msg of messages) {\n const content = (msg as { content?: unknown }).content;\n const text = extractTextFromMessageContent(content);\n total += Math.ceil(text.length / 4);\n }\n return total;\n}\n\nexport function buildGlobalSessionStats(sessions: SessionMetadata[]): GlobalSessionStats {\n const byChannel: Record<string, number> = {};\n for (const session of sessions) {\n byChannel[session.sourceChannel] = (byChannel[session.sourceChannel] || 0) + 1;\n }\n let oldestSession: string | undefined;\n let newestSession: string | undefined;\n if (sessions.length > 0) {\n const sorted = [...sessions].sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n oldestSession = sorted[0]!.createdAt;\n newestSession = sorted[sorted.length - 1]!.createdAt;\n }\n return {\n totalSessions: sessions.length,\n activeSessions: sessions.filter(\n (s) => s.status === SessionStatus.ACTIVE || s.status === SessionStatus.IDLE,\n ).length,\n archivedSessions: sessions.filter((s) => s.status === SessionStatus.ARCHIVED).length,\n pinnedSessions: sessions.filter((s) => s.status === SessionStatus.PINNED).length,\n totalMessages: sessions.reduce((sum, s) => sum + s.messageCount, 0),\n totalTokens: sessions.reduce((sum, s) => sum + s.estimatedTokens, 0),\n oldestSession,\n newestSession,\n byChannel,\n };\n}\n"],"mappings":";;;;AA8DA,SAAS,UAAa,KAA+C;AACnE,KAAI,CAAC,KAAK,MAAM,CACd;AAEF,KAAI;AACF,SAAO,KAAK,MAAM,IAAI;SAChB;AACN;;;AAIJ,SAAS,UAAU,IAAmD;AACpE,KAAI,MAAM,QAAQ,CAAC,OAAO,SAAS,GAAG,CACpC;AAEF,QAAO,IAAI,KAAK,GAAG,CAAC,aAAa;;AAGnC,SAAgB,qBAAqB,YAAoB,KAAkC;CACzF,MAAM,WAAW,4BAA4B,WAAW;CACxD,MAAM,EAAE,SAAS,WAAW,QAAQ,cAAc,sBAAsB,WAAW;CACnF,MAAM,UAAU,UAAsC,IAAI,aAAa,IAAI,SAAS;CACpF,MAAM,aAAa,UAAmC,IAAI,iBAAiB,IAAI,SAAS;CACxF,MAAM,OAAO,UAAoB,IAAI,UAAU,IAAI,EAAE;CACrD,MAAM,YAAY,UAAU,IAAI,WAAW,IAAI,SAAS;CACxD,MAAM,YAAY,UAAU,IAAI,WAAW,IAAI,SAAS;CACxD,MAAM,iBAAiB,UAAU,IAAI,iBAAiB,IAAI,SAAS;CACnE,MAAM,SAAS,IAAI,gBAAgB,MAAM,IAAI;CAC7C,MAAM,WAAW,IAAI,gBAAgB,MAAM,IAAI;AAE/C,QAAO;EACL,GAAG;EACH,KAAK;EACL,QAAQ,IAAI;EACZ,MAAM,IAAI,QAAQ,KAAA;EAClB;EACA;EACA;EACA;EACA,kBAAkB,UAAU,IAAI,mBAAmB;EACnD,mBAAmB,UAAU,IAAI,oBAAoB;EACrD,eAAe,UAAU;EACzB,cAAc,YAAY;EAC1B,aAAa,IAAI,gBAAgB,SAAS;EAC1C;EACA;EACA,sBAAsB,IAAI,0BAA0B,KAAA;EACpD,cAAc,IAAI;EAClB,iBAAiB,IAAI;EACrB,gBAAgB,IAAI;EACpB,cAAc,IAAI;EAClB,OAAO;GACL,cAAc,IAAI;GAClB,YAAY,IAAI;GAChB,YAAY,IAAI,uBAAuB,KAAA;GACxC;EACF;;AAGH,SAAgB,wBACd,YACA,cACA,UACA,eACA,cAwBA;CACA,MAAM,MAAM,KAAK,KAAK;AACtB,QAAO;EACL;EACA,SAAS,6BAA6B,WAAW;EACjD;EACA,QAAQ,SAAS;EACjB,MAAM,SAAS,QAAQ;EACvB,UAAU,KAAK,UAAU,SAAS,QAAQ,EAAE,CAAC;EAC7C,WAAW,KAAK,MAAM,SAAS,UAAU,IAAI;EAC7C,WAAW,KAAK,MAAM,SAAS,UAAU,IAAI;EAC7C,gBAAgB,KAAK,MAAM,SAAS,eAAe,IAAI;EACvD,kBAAkB,SAAS,mBAAmB,KAAK,MAAM,SAAS,iBAAiB,GAAG;EACtF,mBAAmB,SAAS,oBAAoB,KAAK,MAAM,SAAS,kBAAkB,GAAG;EACzF,eAAe,SAAS;EACxB,cAAc,SAAS;EACvB,aAAa,SAAS,eAAe;EACrC,aAAa,SAAS,UAAU,KAAK,UAAU,SAAS,QAAQ,GAAG;EACnE,gBAAgB,SAAS,aAAa,KAAK,UAAU,SAAS,WAAW,GAAG;EAC5E,sBAAsB,SAAS,wBAAwB;EACvD,cAAc,SAAS;EACvB,iBAAiB,SAAS;EAC1B,gBAAgB,SAAS;EACzB,eAAe,iBAAiB;EAChC,cAAc,gBAAgB;EAC/B;;AAGH,SAAgB,yBAAyB,KAA2C;AAClF,QAAO;EACL,GAAI,IAAI,iBAAiB,EAAE,eAAe,IAAI,gBAAuD,GAAG,EAAE;EAC1G,GAAI,IAAI,kBAAkB,EAAE,gBAAgB,IAAI,iBAAyD,GAAG,EAAE;EAC9G,GAAI,IAAI,gBAAgB,EAAE,cAAc,IAAI,eAAqD,GAAG,EAAE;EACtG,GAAI,IAAI,gBAAgB,EAAE,cAAc,IAAI,eAAqD,GAAG,EAAE;EACtG,GAAI,IAAI,iBAAiB,EAAE,eAAe,IAAI,gBAAgB,GAAG,EAAE;EACnE,GAAI,IAAI,oBAAoB,EAAE,kBAAkB,IAAI,mBAAmB,GAAG,EAAE;EAC5E,GAAI,IAAI,6BAA6B,EAAE,0BAA0B,IAAI,4BAA4B,GAAG,EAAE;EACtG,WAAW,IAAI;EAChB;;AAGH,SAAgB,8BAA8B,KAA8C;AAE1F,QADgB,KAAK,MAAM,IAAI,aACjB;;AAGhB,SAAgB,kBAAkB,KAGhC;AACA,KAAI,yBAAyB,IAAI,CAC/B,QAAO;EAAE,WAAW;EAAW,MAAM;EAAM;CAE7C,MAAM,SAAS;AACf,KAAI,OAAO,SAAS,gBAAiB,IAA0B,SAAS,aACtE,QAAO;EAAE,WAAW;EAAc,MAAM;EAAM;AAEhD,QAAO;EAAE,WAAW;EAAW,MAAM,OAAO,QAAQ;EAAM;;AAG5D,SAAgB,kBAAkB,KAAkC;AAClE,KAAI,yBAAyB,IAAI,EAAE;EACjC,MAAM,MAAM;AACZ,SAAO,CAAC,IAAI,MAAM,IAAI,GAAG,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;;AAGrD,QAAO,8BAA+BA,IAA8B,QAAQ;;AAG9E,SAAS,8BAA8B,SAA0B;AAC/D,KAAI,OAAO,YAAY,SACrB,QAAO;AAET,KAAI,CAAC,MAAM,QAAQ,QAAQ,CACzB,QAAO;CAET,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,SAAS,SAAS;AAC3B,MAAI,CAAC,SAAS,OAAO,UAAU,SAC7B;AAGF,MADc,MAA4B,SAC7B,UAAU,OAAQ,MAA4B,SAAS,SAClE,OAAM,KAAM,MAA2B,KAAK;;AAGhD,QAAO,MAAM,KAAK,KAAK;;AAGzB,SAAgB,2BAA2B,UAAkC;CAC3E,IAAI,QAAQ;AACZ,MAAK,MAAM,OAAO,UAAU;EAC1B,MAAM,UAAW,IAA8B;EAC/C,MAAM,OAAO,8BAA8B,QAAQ;AACnD,WAAS,KAAK,KAAK,KAAK,SAAS,EAAE;;AAErC,QAAO;;AAGT,SAAgB,wBAAwB,UAAiD;CACvF,MAAM,YAAoC,EAAE;AAC5C,MAAK,MAAM,WAAW,SACpB,WAAU,QAAQ,kBAAkB,UAAU,QAAQ,kBAAkB,KAAK;CAE/E,IAAI;CACJ,IAAI;AACJ,KAAI,SAAS,SAAS,GAAG;EACvB,MAAM,SAAS,CAAC,GAAG,SAAS,CAAC,MAAM,GAAG,MAAM,EAAE,UAAU,cAAc,EAAE,UAAU,CAAC;AACnF,kBAAgB,OAAO,GAAI;AAC3B,kBAAgB,OAAO,OAAO,SAAS,GAAI;;AAE7C,QAAO;EACL,eAAe,SAAS;EACxB,gBAAgB,SAAS,QACtB,MAAM,EAAE,WAAA,YAAmC,EAAE,WAAA,OAC/C,CAAC;EACF,kBAAkB,SAAS,QAAQ,MAAM,EAAE,WAAA,WAAkC,CAAC;EAC9E,gBAAgB,SAAS,QAAQ,MAAM,EAAE,WAAA,SAAgC,CAAC;EAC1E,eAAe,SAAS,QAAQ,KAAK,MAAM,MAAM,EAAE,cAAc,EAAE;EACnE,aAAa,SAAS,QAAQ,KAAK,MAAM,MAAM,EAAE,iBAAiB,EAAE;EACpE;EACA;EACA;EACD"}
1
+ {"version":3,"file":"row-mappers.js","names":["msg"],"sources":["../../../../src/storage/sqlite/row-mappers.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { SessionAgentConfig } from '../../session/config-types.js';\nimport {\n isTranscriptContextEntry,\n type TranscriptStoredRow,\n type XopcTranscriptContextEntry,\n} from '../../session/session-context-for-llm.js';\nimport { SessionStatus, type GlobalSessionStats, type SessionMetadata } from '../../session/types.js';\nimport {\n buildDefaultSessionMetadata,\n parseSessionKeySource,\n resolveAgentIdFromSessionKey,\n} from './session-metadata.js';\n\nexport type SessionRow = {\n session_key: string;\n agent_id: string;\n current_transcript_id: string;\n status: string;\n name: string | null;\n tags_json: string;\n created_at: number;\n updated_at: number;\n last_accessed_at: number;\n session_started_at: number | null;\n last_interaction_at: number | null;\n source_channel: string;\n source_chat_id: string;\n session_type: string | null;\n routing_json: string | null;\n custom_data_json: string | null;\n abort_cutoff_timestamp: number | null;\n message_count: number;\n estimated_tokens: number;\n compacted_count: number;\n last_flushed_at: string | null;\n flush_count: number;\n thinking_level: string | null;\n verbose_level: string | null;\n};\n\nexport type TranscriptEntryRow = {\n entry_id: string;\n transcript_id: string;\n seq: number;\n entry_kind: string;\n role: string | null;\n payload_json: string;\n created_at: number;\n};\n\nexport type SessionConfigRow = {\n session_key: string;\n thinking_level: string | null;\n reasoning_level: string | null;\n verbose_level: string | null;\n elevated_mode: string | null;\n model_override: string | null;\n provider_override: string | null;\n working_directory_override: string | null;\n updated_at: number;\n};\n\nfunction parseJson<T>(raw: string | null | undefined): T | undefined {\n if (!raw?.trim()) {\n return undefined;\n }\n try {\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n}\n\nfunction isoFromMs(ms: number | null | undefined): string | undefined {\n if (ms == null || !Number.isFinite(ms)) {\n return undefined;\n }\n return new Date(ms).toISOString();\n}\n\nexport function sessionRowToMetadata(sessionKey: string, row: SessionRow): SessionMetadata {\n const defaults = buildDefaultSessionMetadata(sessionKey);\n const { channel: keySource, chatId: keyChatId } = parseSessionKeySource(sessionKey);\n const routing = parseJson<SessionMetadata['routing']>(row.routing_json) ?? defaults.routing;\n const customData = parseJson<Record<string, unknown>>(row.custom_data_json) ?? defaults.customData;\n const tags = parseJson<string[]>(row.tags_json) ?? [];\n const createdAt = isoFromMs(row.created_at) ?? defaults.createdAt;\n const updatedAt = isoFromMs(row.updated_at) ?? defaults.updatedAt;\n const lastAccessedAt = isoFromMs(row.last_accessed_at) ?? defaults.lastAccessedAt;\n const diskSc = row.source_channel?.trim() ?? '';\n const diskChat = row.source_chat_id?.trim() ?? '';\n\n return {\n ...defaults,\n key: sessionKey,\n status: row.status as SessionStatus,\n name: row.name ?? undefined,\n tags,\n createdAt,\n updatedAt,\n lastAccessedAt,\n sessionStartedAt: isoFromMs(row.session_started_at),\n lastInteractionAt: isoFromMs(row.last_interaction_at),\n sourceChannel: diskSc || keySource,\n sourceChatId: diskChat || keyChatId,\n sessionType: row.session_type ?? defaults.sessionType,\n routing,\n customData,\n abortCutoffTimestamp: row.abort_cutoff_timestamp ?? undefined,\n messageCount: row.message_count,\n estimatedTokens: row.estimated_tokens,\n compactedCount: row.compacted_count,\n lastFlushedAt: row.last_flushed_at ?? undefined,\n flushCount: row.flush_count,\n transcriptId: row.current_transcript_id,\n stats: {\n messageCount: row.message_count,\n tokenCount: row.estimated_tokens,\n lastTurnAt: row.last_interaction_at ?? undefined,\n },\n };\n}\n\nexport function metadataToSessionInsert(\n sessionKey: string,\n transcriptId: string,\n metadata: SessionMetadata,\n thinkingLevel?: string | null,\n verboseLevel?: string | null,\n): {\n sessionKey: string;\n agentId: string;\n transcriptId: string;\n status: string;\n name: string | null;\n tagsJson: string;\n createdAt: number;\n updatedAt: number;\n lastAccessedAt: number;\n sessionStartedAt: number | null;\n lastInteractionAt: number | null;\n sourceChannel: string;\n sourceChatId: string;\n sessionType: string | null;\n routingJson: string | null;\n customDataJson: string | null;\n abortCutoffTimestamp: number | null;\n messageCount: number;\n estimatedTokens: number;\n compactedCount: number;\n lastFlushedAt: string | null;\n flushCount: number;\n thinkingLevel: string | null;\n verboseLevel: string | null;\n} {\n const now = Date.now();\n return {\n sessionKey,\n agentId: resolveAgentIdFromSessionKey(sessionKey),\n transcriptId,\n status: metadata.status,\n name: metadata.name ?? null,\n tagsJson: JSON.stringify(metadata.tags ?? []),\n createdAt: Date.parse(metadata.createdAt) || now,\n updatedAt: Date.parse(metadata.updatedAt) || now,\n lastAccessedAt: Date.parse(metadata.lastAccessedAt) || now,\n sessionStartedAt: metadata.sessionStartedAt ? Date.parse(metadata.sessionStartedAt) : now,\n lastInteractionAt: metadata.lastInteractionAt ? Date.parse(metadata.lastInteractionAt) : null,\n sourceChannel: metadata.sourceChannel,\n sourceChatId: metadata.sourceChatId,\n sessionType: metadata.sessionType ?? null,\n routingJson: metadata.routing ? JSON.stringify(metadata.routing) : null,\n customDataJson: metadata.customData ? JSON.stringify(metadata.customData) : null,\n abortCutoffTimestamp: metadata.abortCutoffTimestamp ?? null,\n messageCount: metadata.messageCount,\n estimatedTokens: metadata.estimatedTokens,\n compactedCount: metadata.compactedCount,\n lastFlushedAt: metadata.lastFlushedAt ?? null,\n flushCount: metadata.flushCount ?? 0,\n thinkingLevel: thinkingLevel ?? null,\n verboseLevel: verboseLevel ?? null,\n };\n}\n\nexport function sessionConfigRowToConfig(row: SessionConfigRow): SessionAgentConfig {\n return {\n ...(row.thinking_level ? { thinkingLevel: row.thinking_level as SessionAgentConfig['thinkingLevel'] } : {}),\n ...(row.reasoning_level ? { reasoningLevel: row.reasoning_level as SessionAgentConfig['reasoningLevel'] } : {}),\n ...(row.verbose_level ? { verboseLevel: row.verbose_level as SessionAgentConfig['verboseLevel'] } : {}),\n ...(row.elevated_mode ? { elevatedMode: row.elevated_mode as SessionAgentConfig['elevatedMode'] } : {}),\n ...(row.model_override ? { modelOverride: row.model_override } : {}),\n ...(row.provider_override ? { providerOverride: row.provider_override } : {}),\n ...(row.working_directory_override ? { workingDirectoryOverride: row.working_directory_override } : {}),\n updatedAt: row.updated_at,\n };\n}\n\nexport function transcriptEntryRowToStoredRow(row: TranscriptEntryRow): TranscriptStoredRow {\n const payload = JSON.parse(row.payload_json) as TranscriptStoredRow;\n return payload;\n}\n\nexport function classifyStoredRow(row: TranscriptStoredRow): {\n entryKind: 'message' | 'context' | 'compaction';\n role: string | null;\n} {\n if (isTranscriptContextEntry(row)) {\n return { entryKind: 'context', role: null };\n }\n const record = row as AgentMessage & { type?: string };\n if (record.type === 'compaction' || (row as { kind?: string }).kind === 'compaction') {\n return { entryKind: 'compaction', role: null };\n }\n return { entryKind: 'message', role: record.role ?? null };\n}\n\nexport function extractFtsContent(row: TranscriptStoredRow): string {\n if (isTranscriptContextEntry(row)) {\n const ctx = row as XopcTranscriptContextEntry;\n return [ctx.text, ctx.id].filter(Boolean).join(' ');\n }\n const msg = row as AgentMessage;\n return extractTextFromMessageContent((msg as { content?: unknown }).content);\n}\n\nfunction extractTextFromMessageContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (!Array.isArray(content)) {\n return '';\n }\n const parts: string[] = [];\n for (const block of content) {\n if (!block || typeof block !== 'object') {\n continue;\n }\n const type = (block as { type?: string }).type;\n if (type === 'text' && typeof (block as { text?: string }).text === 'string') {\n parts.push((block as { text: string }).text);\n }\n }\n return parts.join('\\n');\n}\n\nexport function estimateTokensFromMessages(messages: AgentMessage[]): number {\n let total = 0;\n for (const msg of messages) {\n const content = (msg as { content?: unknown }).content;\n const text = extractTextFromMessageContent(content);\n total += Math.ceil(text.length / 4);\n }\n return total;\n}\n\nexport function buildGlobalSessionStats(sessions: SessionMetadata[]): GlobalSessionStats {\n const byChannel: Record<string, number> = {};\n for (const session of sessions) {\n byChannel[session.sourceChannel] = (byChannel[session.sourceChannel] || 0) + 1;\n }\n let oldestSession: string | undefined;\n let newestSession: string | undefined;\n if (sessions.length > 0) {\n const sorted = [...sessions].sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n oldestSession = sorted[0]!.createdAt;\n newestSession = sorted[sorted.length - 1]!.createdAt;\n }\n return {\n totalSessions: sessions.length,\n activeSessions: sessions.filter(\n (s) => s.status === SessionStatus.ACTIVE || s.status === SessionStatus.IDLE,\n ).length,\n archivedSessions: sessions.filter((s) => s.status === SessionStatus.ARCHIVED).length,\n pinnedSessions: sessions.filter((s) => s.status === SessionStatus.PINNED).length,\n totalMessages: sessions.reduce((sum, s) => sum + s.messageCount, 0),\n totalTokens: sessions.reduce((sum, s) => sum + s.estimatedTokens, 0),\n oldestSession,\n newestSession,\n byChannel,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ export declare const SCHEMA_META_SCHEMA_VERSION_KEY = "schema_version";
3
+ export declare function ensureSchemaMetaTable(db: DatabaseSync): void;
4
+ export declare function readSchemaVersion(db: DatabaseSync): number;
5
+ export declare function setSchemaVersion(db: DatabaseSync, version: number): void;
6
+ export declare function readSchemaVersionForTest(db: DatabaseSync): number;
@@ -0,0 +1,29 @@
1
+ //#region src/storage/sqlite/schema-version.ts
2
+ const SCHEMA_META_SCHEMA_VERSION_KEY = "schema_version";
3
+ function ensureSchemaMetaTable(db) {
4
+ db.exec(`
5
+ CREATE TABLE IF NOT EXISTS schema_meta (
6
+ key TEXT PRIMARY KEY,
7
+ value TEXT NOT NULL
8
+ );
9
+ `);
10
+ }
11
+ function readSchemaVersion(db) {
12
+ const row = db.prepare(`SELECT value FROM schema_meta WHERE key = ?`).get(SCHEMA_META_SCHEMA_VERSION_KEY);
13
+ if (!row?.value) return 0;
14
+ const parsed = Number.parseInt(row.value, 10);
15
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;
16
+ }
17
+ function setSchemaVersion(db, version) {
18
+ if (!Number.isInteger(version) || version < 1) throw new Error(`Invalid schema version: ${version}`);
19
+ db.prepare(`INSERT INTO schema_meta (key, value) VALUES (?, ?)
20
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(SCHEMA_META_SCHEMA_VERSION_KEY, String(version));
21
+ }
22
+ function readSchemaVersionForTest(db) {
23
+ if (db.prepare(`PRAGMA table_info(schema_meta)`).all().length === 0) return 0;
24
+ return readSchemaVersion(db);
25
+ }
26
+ //#endregion
27
+ export { SCHEMA_META_SCHEMA_VERSION_KEY, ensureSchemaMetaTable, readSchemaVersion, readSchemaVersionForTest, setSchemaVersion };
28
+
29
+ //# sourceMappingURL=schema-version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-version.js","names":[],"sources":["../../../../src/storage/sqlite/schema-version.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\n\nexport const SCHEMA_META_SCHEMA_VERSION_KEY = 'schema_version';\n\ntype TableInfoRow = {\n name: string;\n};\n\nexport function ensureSchemaMetaTable(db: DatabaseSync): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS schema_meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n `);\n}\n\nexport function readSchemaVersion(db: DatabaseSync): number {\n const row = db\n .prepare(`SELECT value FROM schema_meta WHERE key = ?`)\n .get(SCHEMA_META_SCHEMA_VERSION_KEY) as { value?: string } | undefined;\n if (!row?.value) {\n return 0;\n }\n const parsed = Number.parseInt(row.value, 10);\n return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;\n}\n\nexport function setSchemaVersion(db: DatabaseSync, version: number): void {\n if (!Number.isInteger(version) || version < 1) {\n throw new Error(`Invalid schema version: ${version}`);\n }\n db.prepare(\n `INSERT INTO schema_meta (key, value) VALUES (?, ?)\n ON CONFLICT(key) DO UPDATE SET value = excluded.value`,\n ).run(SCHEMA_META_SCHEMA_VERSION_KEY, String(version));\n}\n\nexport function readSchemaVersionForTest(db: DatabaseSync): number {\n const rows = db.prepare(`PRAGMA table_info(schema_meta)`).all() as TableInfoRow[];\n if (rows.length === 0) {\n return 0;\n }\n return readSchemaVersion(db);\n}\n"],"mappings":";AAEA,MAAa,iCAAiC;AAM9C,SAAgB,sBAAsB,IAAwB;AAC5D,IAAG,KAAK;;;;;IAKN;;AAGJ,SAAgB,kBAAkB,IAA0B;CAC1D,MAAM,MAAM,GACT,QAAQ,8CAA8C,CACtD,IAAI,+BAA+B;AACtC,KAAI,CAAC,KAAK,MACR,QAAO;CAET,MAAM,SAAS,OAAO,SAAS,IAAI,OAAO,GAAG;AAC7C,QAAO,OAAO,UAAU,OAAO,IAAI,SAAS,IAAI,SAAS;;AAG3D,SAAgB,iBAAiB,IAAkB,SAAuB;AACxE,KAAI,CAAC,OAAO,UAAU,QAAQ,IAAI,UAAU,EAC1C,OAAM,IAAI,MAAM,2BAA2B,UAAU;AAEvD,IAAG,QACD;4DAED,CAAC,IAAI,gCAAgC,OAAO,QAAQ,CAAC;;AAGxD,SAAgB,yBAAyB,IAA0B;AAEjE,KADa,GAAG,QAAQ,iCAAiC,CAAC,KAClD,CAAC,WAAW,EAClB,QAAO;AAET,QAAO,kBAAkB,GAAG"}
@@ -1,5 +1,7 @@
1
1
  import type { DatabaseSync } from 'node:sqlite';
2
- export declare const XOPC_DB_SCHEMA_VERSION = 1;
3
- export declare const SCHEMA_META_SCHEMA_VERSION_KEY = "schema_version";
2
+ export { SCHEMA_META_SCHEMA_VERSION_KEY, readSchemaVersionForTest, readSchemaVersion, setSchemaVersion, ensureSchemaMetaTable, } from './schema-version.js';
3
+ export { XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_SCHEMA_VERSION, } from './migrations/runner.js';
4
+ /**
5
+ * Ensure schema_meta exists, apply baseline schema.sql on first open, then run pending migrations.
6
+ */
4
7
  export declare function ensureXopcDatabaseSchema(db: DatabaseSync): void;
5
- export declare function readSchemaVersionForTest(db: DatabaseSync): number;
@@ -1,43 +1,39 @@
1
+ import { SCHEMA_META_SCHEMA_VERSION_KEY, ensureSchemaMetaTable, readSchemaVersion, readSchemaVersionForTest, setSchemaVersion } from "./schema-version.js";
2
+ import { XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_SCHEMA_VERSION, applyPendingMigrations } from "./migrations/runner.js";
1
3
  import { dirname, join } from "node:path";
2
4
  import { readFileSync } from "node:fs";
3
5
  import { fileURLToPath } from "node:url";
4
6
  //#region src/storage/sqlite/schema.ts
5
- const XOPC_DB_SCHEMA_VERSION = 1;
6
- const SCHEMA_META_SCHEMA_VERSION_KEY = "schema_version";
7
7
  const SCHEMA_DIR = dirname(fileURLToPath(import.meta.url));
8
8
  function readSchemaSql() {
9
9
  return readFileSync(join(SCHEMA_DIR, "schema.sql"), "utf8");
10
10
  }
11
- function ensureXopcDatabaseSchema(db) {
12
- db.exec(`
13
- CREATE TABLE IF NOT EXISTS schema_meta (
14
- key TEXT PRIMARY KEY,
15
- value TEXT NOT NULL
16
- );
17
- `);
18
- const currentVersion = readSchemaVersion(db);
19
- if (currentVersion === 0) {
11
+ function bootstrapFreshDatabase(db) {
12
+ db.exec("BEGIN IMMEDIATE");
13
+ try {
20
14
  db.exec(readSchemaSql());
21
- setSchemaVersion(db, 1);
22
- return;
15
+ setSchemaVersion(db, 2);
16
+ db.exec("COMMIT");
17
+ } catch (error) {
18
+ try {
19
+ db.exec("ROLLBACK");
20
+ } catch {}
21
+ throw error;
23
22
  }
24
- if (currentVersion !== 1) throw new Error(`Unsupported xopc database schema version ${currentVersion} (expected 1)`);
25
- }
26
- function readSchemaVersion(db) {
27
- const row = db.prepare(`SELECT value FROM schema_meta WHERE key = ?`).get(SCHEMA_META_SCHEMA_VERSION_KEY);
28
- if (!row?.value) return 0;
29
- const parsed = Number.parseInt(row.value, 10);
30
- return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;
31
- }
32
- function setSchemaVersion(db, version) {
33
- db.prepare(`INSERT INTO schema_meta (key, value) VALUES (?, ?)
34
- ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(SCHEMA_META_SCHEMA_VERSION_KEY, String(version));
35
23
  }
36
- function readSchemaVersionForTest(db) {
37
- if (db.prepare(`PRAGMA table_info(schema_meta)`).all().length === 0) return 0;
38
- return readSchemaVersion(db);
24
+ /**
25
+ * Ensure schema_meta exists, apply baseline schema.sql on first open, then run pending migrations.
26
+ */
27
+ function ensureXopcDatabaseSchema(db) {
28
+ ensureSchemaMetaTable(db);
29
+ let currentVersion = readSchemaVersion(db);
30
+ if (currentVersion === 0) {
31
+ bootstrapFreshDatabase(db);
32
+ currentVersion = 2;
33
+ }
34
+ applyPendingMigrations(db, { targetVersion: 2 });
39
35
  }
40
36
  //#endregion
41
- export { SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_SCHEMA_VERSION, ensureXopcDatabaseSchema, readSchemaVersionForTest };
37
+ export { SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_SCHEMA_VERSION, ensureSchemaMetaTable, ensureXopcDatabaseSchema, readSchemaVersion, readSchemaVersionForTest, setSchemaVersion };
42
38
 
43
39
  //# sourceMappingURL=schema.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","names":[],"sources":["../../../../src/storage/sqlite/schema.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { DatabaseSync } from 'node:sqlite';\n\nexport const XOPC_DB_SCHEMA_VERSION = 1;\n\nexport const SCHEMA_META_SCHEMA_VERSION_KEY = 'schema_version';\n\ntype TableInfoRow = {\n name: string;\n};\n\nconst SCHEMA_DIR = dirname(fileURLToPath(import.meta.url));\n\nfunction readSchemaSql(): string {\n return readFileSync(join(SCHEMA_DIR, 'schema.sql'), 'utf8');\n}\n\nexport function ensureXopcDatabaseSchema(db: DatabaseSync): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS schema_meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n `);\n\n const currentVersion = readSchemaVersion(db);\n if (currentVersion === 0) {\n db.exec(readSchemaSql());\n setSchemaVersion(db, XOPC_DB_SCHEMA_VERSION);\n return;\n }\n\n if (currentVersion !== XOPC_DB_SCHEMA_VERSION) {\n throw new Error(\n `Unsupported xopc database schema version ${currentVersion} (expected ${XOPC_DB_SCHEMA_VERSION})`,\n );\n }\n}\n\nfunction readSchemaVersion(db: DatabaseSync): number {\n const row = db\n .prepare(`SELECT value FROM schema_meta WHERE key = ?`)\n .get(SCHEMA_META_SCHEMA_VERSION_KEY) as { value?: string } | undefined;\n if (!row?.value) {\n return 0;\n }\n const parsed = Number.parseInt(row.value, 10);\n return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;\n}\n\nfunction setSchemaVersion(db: DatabaseSync, version: number): void {\n db.prepare(\n `INSERT INTO schema_meta (key, value) VALUES (?, ?)\n ON CONFLICT(key) DO UPDATE SET value = excluded.value`,\n ).run(SCHEMA_META_SCHEMA_VERSION_KEY, String(version));\n}\n\nexport function readSchemaVersionForTest(db: DatabaseSync): number {\n const rows = db.prepare(`PRAGMA table_info(schema_meta)`).all() as TableInfoRow[];\n if (rows.length === 0) {\n return 0;\n }\n return readSchemaVersion(db);\n}\n"],"mappings":";;;;AAMA,MAAa,yBAAyB;AAEtC,MAAa,iCAAiC;AAM9C,MAAM,aAAa,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE1D,SAAS,gBAAwB;AAC/B,QAAO,aAAa,KAAK,YAAY,aAAa,EAAE,OAAO;;AAG7D,SAAgB,yBAAyB,IAAwB;AAC/D,IAAG,KAAK;;;;;IAKN;CAEF,MAAM,iBAAiB,kBAAkB,GAAG;AAC5C,KAAI,mBAAmB,GAAG;AACxB,KAAG,KAAK,eAAe,CAAC;AACxB,mBAAiB,IAAA,EAA2B;AAC5C;;AAGF,KAAI,mBAAA,EACF,OAAM,IAAI,MACR,4CAA4C,eAAe,eAC5D;;AAIL,SAAS,kBAAkB,IAA0B;CACnD,MAAM,MAAM,GACT,QAAQ,8CAA8C,CACtD,IAAI,+BAA+B;AACtC,KAAI,CAAC,KAAK,MACR,QAAO;CAET,MAAM,SAAS,OAAO,SAAS,IAAI,OAAO,GAAG;AAC7C,QAAO,OAAO,UAAU,OAAO,IAAI,SAAS,IAAI,SAAS;;AAG3D,SAAS,iBAAiB,IAAkB,SAAuB;AACjE,IAAG,QACD;4DAED,CAAC,IAAI,gCAAgC,OAAO,QAAQ,CAAC;;AAGxD,SAAgB,yBAAyB,IAA0B;AAEjE,KADa,GAAG,QAAQ,iCAAiC,CAAC,KAClD,CAAC,WAAW,EAClB,QAAO;AAET,QAAO,kBAAkB,GAAG"}
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../../../src/storage/sqlite/schema.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport {\n applyPendingMigrations,\n XOPC_DB_BASELINE_SCHEMA_VERSION,\n XOPC_DB_SCHEMA_VERSION,\n} from './migrations/runner.js';\nimport {\n ensureSchemaMetaTable,\n readSchemaVersion,\n setSchemaVersion,\n} from './schema-version.js';\n\nexport {\n SCHEMA_META_SCHEMA_VERSION_KEY,\n readSchemaVersionForTest,\n readSchemaVersion,\n setSchemaVersion,\n ensureSchemaMetaTable,\n} from './schema-version.js';\nexport {\n XOPC_DB_BASELINE_SCHEMA_VERSION,\n XOPC_DB_SCHEMA_VERSION,\n} from './migrations/runner.js';\n\nconst SCHEMA_DIR = dirname(fileURLToPath(import.meta.url));\n\nfunction readSchemaSql(): string {\n return readFileSync(join(SCHEMA_DIR, 'schema.sql'), 'utf8');\n}\n\nfunction bootstrapFreshDatabase(db: DatabaseSync): void {\n db.exec('BEGIN IMMEDIATE');\n try {\n db.exec(readSchemaSql());\n setSchemaVersion(db, XOPC_DB_BASELINE_SCHEMA_VERSION);\n db.exec('COMMIT');\n } catch (error) {\n try {\n db.exec('ROLLBACK');\n } catch {\n /* preserve original error */\n }\n throw error;\n }\n}\n\n/**\n * Ensure schema_meta exists, apply baseline schema.sql on first open, then run pending migrations.\n */\nexport function ensureXopcDatabaseSchema(db: DatabaseSync): void {\n ensureSchemaMetaTable(db);\n\n let currentVersion = readSchemaVersion(db);\n if (currentVersion === 0) {\n bootstrapFreshDatabase(db);\n currentVersion = XOPC_DB_BASELINE_SCHEMA_VERSION;\n }\n\n applyPendingMigrations(db, { targetVersion: XOPC_DB_SCHEMA_VERSION });\n}\n"],"mappings":";;;;;;AA6BA,MAAM,aAAa,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE1D,SAAS,gBAAwB;AAC/B,QAAO,aAAa,KAAK,YAAY,aAAa,EAAE,OAAO;;AAG7D,SAAS,uBAAuB,IAAwB;AACtD,IAAG,KAAK,kBAAkB;AAC1B,KAAI;AACF,KAAG,KAAK,eAAe,CAAC;AACxB,mBAAiB,IAAA,EAAoC;AACrD,KAAG,KAAK,SAAS;UACV,OAAO;AACd,MAAI;AACF,MAAG,KAAK,WAAW;UACb;AAGR,QAAM;;;;;;AAOV,SAAgB,yBAAyB,IAAwB;AAC/D,uBAAsB,GAAG;CAEzB,IAAI,iBAAiB,kBAAkB,GAAG;AAC1C,KAAI,mBAAmB,GAAG;AACxB,yBAAuB,GAAG;AAC1B,mBAAA;;AAGF,wBAAuB,IAAI,EAAE,eAAA,GAAuC,CAAC"}
@@ -21,6 +21,8 @@ CREATE TABLE IF NOT EXISTS sessions (
21
21
  message_count INTEGER NOT NULL DEFAULT 0,
22
22
  estimated_tokens INTEGER NOT NULL DEFAULT 0,
23
23
  compacted_count INTEGER NOT NULL DEFAULT 0,
24
+ last_flushed_at TEXT,
25
+ flush_count INTEGER NOT NULL DEFAULT 0,
24
26
  thinking_level TEXT,
25
27
  verbose_level TEXT
26
28
  );
@@ -60,6 +60,7 @@ function buildDefaultSessionMetadata(sessionKey) {
60
60
  messageCount: 0,
61
61
  estimatedTokens: 0,
62
62
  compactedCount: 0,
63
+ flushCount: 0,
63
64
  sourceChannel: channel,
64
65
  sourceChatId: chatId,
65
66
  routing,
@@ -1 +1 @@
1
- {"version":3,"file":"session-metadata.js","names":["parseRoutingSessionKey"],"sources":["../../../../src/storage/sqlite/session-metadata.ts"],"sourcesContent":["import { parseSessionKey as parseRoutingSessionKey } from '../../routing/session-key.js';\nimport { SessionStatus, type SessionMetadata } from '../../session/types.js';\n\nexport function resolveAgentIdFromSessionKey(sessionKey: string): string {\n const parsed = parseRoutingSessionKey(sessionKey);\n return parsed?.agentId?.toLowerCase() || 'main';\n}\n\nexport function parseSessionKeySource(key: string): { channel: string; chatId: string } {\n const parts = key.split(':');\n if (parts.length >= 2 && parts[0] === 'heartbeat') {\n return { channel: 'heartbeat', chatId: parts.slice(1).join(':') };\n }\n const parsed = parseRoutingSessionKey(key);\n if (parsed) {\n if (parsed.source === 'cron') {\n return { channel: 'cron', chatId: parsed.peerId };\n }\n return {\n channel: parsed.source,\n chatId: [parsed.accountId, parsed.peerKind, parsed.peerId].join(':'),\n };\n }\n return { channel: 'unknown', chatId: key };\n}\n\nexport function extractRoutingFromSessionKey(key: string): SessionMetadata['routing'] {\n const parsed = parseRoutingSessionKey(key);\n if (!parsed) {\n return undefined;\n }\n return {\n agentId: parsed.agentId?.toLowerCase() || 'main',\n source: parsed.source?.toLowerCase() || 'unknown',\n accountId: parsed.accountId?.toLowerCase() || 'default',\n peerKind: parsed.peerKind?.toLowerCase() || 'dm',\n peerId: parsed.peerId?.toLowerCase() || 'unknown',\n threadId: parsed.threadId,\n scopeId: parsed.scopeId,\n };\n}\n\nexport function buildDefaultSessionMetadata(sessionKey: string): SessionMetadata {\n const { channel, chatId } = parseSessionKeySource(sessionKey);\n const routing = extractRoutingFromSessionKey(sessionKey);\n const isCronSession = channel === 'cron';\n const isHeartbeatSession = channel === 'heartbeat';\n const now = new Date().toISOString();\n return {\n key: sessionKey,\n status: SessionStatus.ACTIVE,\n tags: [],\n createdAt: now,\n updatedAt: now,\n lastAccessedAt: now,\n messageCount: 0,\n estimatedTokens: 0,\n compactedCount: 0,\n sourceChannel: channel,\n sourceChatId: chatId,\n routing,\n ...(isCronSession ? { sessionType: 'cron', customData: { cronJobId: chatId } } : {}),\n ...(isHeartbeatSession ? { sessionType: 'heartbeat', customData: { heartbeatTarget: chatId } } : {}),\n stats: { messageCount: 0, tokenCount: 0 },\n };\n}\n"],"mappings":";;;kBAAyF;AAGzF,SAAgB,6BAA6B,YAA4B;AAEvE,QADeA,gBAAuB,WACzB,EAAE,SAAS,aAAa,IAAI;;AAG3C,SAAgB,sBAAsB,KAAkD;CACtF,MAAM,QAAQ,IAAI,MAAM,IAAI;AAC5B,KAAI,MAAM,UAAU,KAAK,MAAM,OAAO,YACpC,QAAO;EAAE,SAAS;EAAa,QAAQ,MAAM,MAAM,EAAE,CAAC,KAAK,IAAI;EAAE;CAEnE,MAAM,SAASA,gBAAuB,IAAI;AAC1C,KAAI,QAAQ;AACV,MAAI,OAAO,WAAW,OACpB,QAAO;GAAE,SAAS;GAAQ,QAAQ,OAAO;GAAQ;AAEnD,SAAO;GACL,SAAS,OAAO;GAChB,QAAQ;IAAC,OAAO;IAAW,OAAO;IAAU,OAAO;IAAO,CAAC,KAAK,IAAI;GACrE;;AAEH,QAAO;EAAE,SAAS;EAAW,QAAQ;EAAK;;AAG5C,SAAgB,6BAA6B,KAAyC;CACpF,MAAM,SAASA,gBAAuB,IAAI;AAC1C,KAAI,CAAC,OACH;AAEF,QAAO;EACL,SAAS,OAAO,SAAS,aAAa,IAAI;EAC1C,QAAQ,OAAO,QAAQ,aAAa,IAAI;EACxC,WAAW,OAAO,WAAW,aAAa,IAAI;EAC9C,UAAU,OAAO,UAAU,aAAa,IAAI;EAC5C,QAAQ,OAAO,QAAQ,aAAa,IAAI;EACxC,UAAU,OAAO;EACjB,SAAS,OAAO;EACjB;;AAGH,SAAgB,4BAA4B,YAAqC;CAC/E,MAAM,EAAE,SAAS,WAAW,sBAAsB,WAAW;CAC7D,MAAM,UAAU,6BAA6B,WAAW;CACxD,MAAM,gBAAgB,YAAY;CAClC,MAAM,qBAAqB,YAAY;CACvC,MAAM,uBAAM,IAAI,MAAM,EAAC,aAAa;AACpC,QAAO;EACL,KAAK;EACL,QAAA;EACA,MAAM,EAAE;EACR,WAAW;EACX,WAAW;EACX,gBAAgB;EAChB,cAAc;EACd,iBAAiB;EACjB,gBAAgB;EAChB,eAAe;EACf,cAAc;EACd;EACA,GAAI,gBAAgB;GAAE,aAAa;GAAQ,YAAY,EAAE,WAAW,QAAQ;GAAE,GAAG,EAAE;EACnF,GAAI,qBAAqB;GAAE,aAAa;GAAa,YAAY,EAAE,iBAAiB,QAAQ;GAAE,GAAG,EAAE;EACnG,OAAO;GAAE,cAAc;GAAG,YAAY;GAAG;EAC1C"}
1
+ {"version":3,"file":"session-metadata.js","names":["parseRoutingSessionKey"],"sources":["../../../../src/storage/sqlite/session-metadata.ts"],"sourcesContent":["import { parseSessionKey as parseRoutingSessionKey } from '../../routing/session-key.js';\nimport { SessionStatus, type SessionMetadata } from '../../session/types.js';\n\nexport function resolveAgentIdFromSessionKey(sessionKey: string): string {\n const parsed = parseRoutingSessionKey(sessionKey);\n return parsed?.agentId?.toLowerCase() || 'main';\n}\n\nexport function parseSessionKeySource(key: string): { channel: string; chatId: string } {\n const parts = key.split(':');\n if (parts.length >= 2 && parts[0] === 'heartbeat') {\n return { channel: 'heartbeat', chatId: parts.slice(1).join(':') };\n }\n const parsed = parseRoutingSessionKey(key);\n if (parsed) {\n if (parsed.source === 'cron') {\n return { channel: 'cron', chatId: parsed.peerId };\n }\n return {\n channel: parsed.source,\n chatId: [parsed.accountId, parsed.peerKind, parsed.peerId].join(':'),\n };\n }\n return { channel: 'unknown', chatId: key };\n}\n\nexport function extractRoutingFromSessionKey(key: string): SessionMetadata['routing'] {\n const parsed = parseRoutingSessionKey(key);\n if (!parsed) {\n return undefined;\n }\n return {\n agentId: parsed.agentId?.toLowerCase() || 'main',\n source: parsed.source?.toLowerCase() || 'unknown',\n accountId: parsed.accountId?.toLowerCase() || 'default',\n peerKind: parsed.peerKind?.toLowerCase() || 'dm',\n peerId: parsed.peerId?.toLowerCase() || 'unknown',\n threadId: parsed.threadId,\n scopeId: parsed.scopeId,\n };\n}\n\nexport function buildDefaultSessionMetadata(sessionKey: string): SessionMetadata {\n const { channel, chatId } = parseSessionKeySource(sessionKey);\n const routing = extractRoutingFromSessionKey(sessionKey);\n const isCronSession = channel === 'cron';\n const isHeartbeatSession = channel === 'heartbeat';\n const now = new Date().toISOString();\n return {\n key: sessionKey,\n status: SessionStatus.ACTIVE,\n tags: [],\n createdAt: now,\n updatedAt: now,\n lastAccessedAt: now,\n messageCount: 0,\n estimatedTokens: 0,\n compactedCount: 0,\n flushCount: 0,\n sourceChannel: channel,\n sourceChatId: chatId,\n routing,\n ...(isCronSession ? { sessionType: 'cron', customData: { cronJobId: chatId } } : {}),\n ...(isHeartbeatSession ? { sessionType: 'heartbeat', customData: { heartbeatTarget: chatId } } : {}),\n stats: { messageCount: 0, tokenCount: 0 },\n };\n}\n"],"mappings":";;;kBAAyF;AAGzF,SAAgB,6BAA6B,YAA4B;AAEvE,QADeA,gBAAuB,WACzB,EAAE,SAAS,aAAa,IAAI;;AAG3C,SAAgB,sBAAsB,KAAkD;CACtF,MAAM,QAAQ,IAAI,MAAM,IAAI;AAC5B,KAAI,MAAM,UAAU,KAAK,MAAM,OAAO,YACpC,QAAO;EAAE,SAAS;EAAa,QAAQ,MAAM,MAAM,EAAE,CAAC,KAAK,IAAI;EAAE;CAEnE,MAAM,SAASA,gBAAuB,IAAI;AAC1C,KAAI,QAAQ;AACV,MAAI,OAAO,WAAW,OACpB,QAAO;GAAE,SAAS;GAAQ,QAAQ,OAAO;GAAQ;AAEnD,SAAO;GACL,SAAS,OAAO;GAChB,QAAQ;IAAC,OAAO;IAAW,OAAO;IAAU,OAAO;IAAO,CAAC,KAAK,IAAI;GACrE;;AAEH,QAAO;EAAE,SAAS;EAAW,QAAQ;EAAK;;AAG5C,SAAgB,6BAA6B,KAAyC;CACpF,MAAM,SAASA,gBAAuB,IAAI;AAC1C,KAAI,CAAC,OACH;AAEF,QAAO;EACL,SAAS,OAAO,SAAS,aAAa,IAAI;EAC1C,QAAQ,OAAO,QAAQ,aAAa,IAAI;EACxC,WAAW,OAAO,WAAW,aAAa,IAAI;EAC9C,UAAU,OAAO,UAAU,aAAa,IAAI;EAC5C,QAAQ,OAAO,QAAQ,aAAa,IAAI;EACxC,UAAU,OAAO;EACjB,SAAS,OAAO;EACjB;;AAGH,SAAgB,4BAA4B,YAAqC;CAC/E,MAAM,EAAE,SAAS,WAAW,sBAAsB,WAAW;CAC7D,MAAM,UAAU,6BAA6B,WAAW;CACxD,MAAM,gBAAgB,YAAY;CAClC,MAAM,qBAAqB,YAAY;CACvC,MAAM,uBAAM,IAAI,MAAM,EAAC,aAAa;AACpC,QAAO;EACL,KAAK;EACL,QAAA;EACA,MAAM,EAAE;EACR,WAAW;EACX,WAAW;EACX,gBAAgB;EAChB,cAAc;EACd,iBAAiB;EACjB,gBAAgB;EAChB,YAAY;EACZ,eAAe;EACf,cAAc;EACd;EACA,GAAI,gBAAgB;GAAE,aAAa;GAAQ,YAAY,EAAE,WAAW,QAAQ;GAAE,GAAG,EAAE;EACnF,GAAI,qBAAqB;GAAE,aAAa;GAAa,YAAY,EAAE,iBAAiB,QAAQ;GAAE,GAAG,EAAE;EACnG,OAAO;GAAE,cAAc;GAAG,YAAY;GAAG;EAC1C"}
@@ -10,6 +10,7 @@ const SESSION_COLUMNS = `
10
10
  created_at, updated_at, last_accessed_at, session_started_at, last_interaction_at,
11
11
  source_channel, source_chat_id, session_type, routing_json, custom_data_json,
12
12
  abort_cutoff_timestamp, message_count, estimated_tokens, compacted_count,
13
+ last_flushed_at, flush_count,
13
14
  thinking_level, verbose_level
14
15
  `;
15
16
  const SELECT_SESSION = `SELECT ${SESSION_COLUMNS} FROM sessions WHERE session_key = ?`;
@@ -23,14 +24,15 @@ function insertSessionAndTranscript(db, sessionKey, transcriptId, cwd, metadata)
23
24
  created_at, updated_at, last_accessed_at, session_started_at, last_interaction_at,
24
25
  source_channel, source_chat_id, session_type, routing_json, custom_data_json,
25
26
  abort_cutoff_timestamp, message_count, estimated_tokens, compacted_count,
27
+ last_flushed_at, flush_count,
26
28
  thinking_level, verbose_level
27
29
  ) VALUES (
28
30
  ?, ?, ?, ?, ?, ?,
29
31
  ?, ?, ?, ?, ?,
30
32
  ?, ?, ?, ?, ?,
31
- ?, ?, ?, ?,
33
+ ?, ?, ?, ?, ?, ?,
32
34
  ?, ?
33
- )`).run(row.sessionKey, row.agentId, row.transcriptId, row.status, row.name, row.tagsJson, row.createdAt, row.updatedAt, row.lastAccessedAt, row.sessionStartedAt, row.lastInteractionAt, row.sourceChannel, row.sourceChatId, row.sessionType, row.routingJson, row.customDataJson, row.abortCutoffTimestamp, row.messageCount, row.estimatedTokens, row.compactedCount, row.thinkingLevel, row.verboseLevel);
35
+ )`).run(row.sessionKey, row.agentId, row.transcriptId, row.status, row.name, row.tagsJson, row.createdAt, row.updatedAt, row.lastAccessedAt, row.sessionStartedAt, row.lastInteractionAt, row.sourceChannel, row.sourceChatId, row.sessionType, row.routingJson, row.customDataJson, row.abortCutoffTimestamp, row.messageCount, row.estimatedTokens, row.compactedCount, row.lastFlushedAt, row.flushCount, row.thinkingLevel, row.verboseLevel);
34
36
  const now = Date.now();
35
37
  db.prepare(`INSERT INTO transcripts (transcript_id, session_key, status, created_at, cwd)
36
38
  VALUES (?, ?, 'active', ?, ?)`).run(transcriptId, sessionKey, now, cwd);
@@ -171,9 +173,11 @@ function patchSessionMetadata(sessionKey, updates) {
171
173
  message_count = ?,
172
174
  estimated_tokens = ?,
173
175
  compacted_count = ?,
176
+ last_flushed_at = ?,
177
+ flush_count = ?,
174
178
  thinking_level = ?,
175
179
  verbose_level = ?
176
- WHERE session_key = ?`).run(merged.status, merged.name ?? null, JSON.stringify(merged.tags ?? []), Date.parse(merged.updatedAt) || now, Date.parse(merged.lastAccessedAt) || now, merged.sessionStartedAt ? Date.parse(merged.sessionStartedAt) : existing.session_started_at, merged.lastInteractionAt ? Date.parse(merged.lastInteractionAt) : existing.last_interaction_at, merged.sourceChannel, merged.sourceChatId, merged.sessionType ?? null, merged.routing ? JSON.stringify(merged.routing) : null, merged.customData ? JSON.stringify(merged.customData) : null, merged.abortCutoffTimestamp ?? null, merged.messageCount, merged.estimatedTokens, merged.compactedCount, existing.thinking_level, existing.verbose_level, sessionKey);
180
+ WHERE session_key = ?`).run(merged.status, merged.name ?? null, JSON.stringify(merged.tags ?? []), Date.parse(merged.updatedAt) || now, Date.parse(merged.lastAccessedAt) || now, merged.sessionStartedAt ? Date.parse(merged.sessionStartedAt) : existing.session_started_at, merged.lastInteractionAt ? Date.parse(merged.lastInteractionAt) : existing.last_interaction_at, merged.sourceChannel, merged.sourceChatId, merged.sessionType ?? null, merged.routing ? JSON.stringify(merged.routing) : null, merged.customData ? JSON.stringify(merged.customData) : null, merged.abortCutoffTimestamp ?? null, merged.messageCount, merged.estimatedTokens, merged.compactedCount, merged.lastFlushedAt ?? null, merged.flushCount ?? 0, existing.thinking_level, existing.verbose_level, sessionKey);
177
181
  const row = readSessionRow(db, sessionKey);
178
182
  if (!row) throw new Error(`Session not found after patch: ${sessionKey}`);
179
183
  return sessionRowToMetadata(sessionKey, row);
@@ -1 +1 @@
1
- {"version":3,"file":"session-repository.js","names":[],"sources":["../../../../src/storage/sqlite/session-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport { validateSessionId } from '../../session/session-id.js';\nimport type { SessionListQuery, SessionMetadata, PaginatedResult } from '../../session/types.js';\nimport {\n buildDefaultSessionMetadata,\n resolveAgentIdFromSessionKey,\n} from './session-metadata.js';\nimport {\n buildGlobalSessionStats,\n metadataToSessionInsert,\n sessionRowToMetadata,\n type SessionRow,\n} from './row-mappers.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\nconst SESSION_COLUMNS = `\n session_key, agent_id, current_transcript_id, status, name, tags_json,\n created_at, updated_at, last_accessed_at, session_started_at, last_interaction_at,\n source_channel, source_chat_id, session_type, routing_json, custom_data_json,\n abort_cutoff_timestamp, message_count, estimated_tokens, compacted_count,\n thinking_level, verbose_level\n`;\n\nimport { escapeFts5Query } from './fts.js';\n\nconst SELECT_SESSION = `SELECT ${SESSION_COLUMNS} FROM sessions WHERE session_key = ?`;\n\nfunction readSessionRow(db: DatabaseSync, sessionKey: string): SessionRow | undefined {\n return db.prepare(SELECT_SESSION).get(sessionKey) as SessionRow | undefined;\n}\n\nfunction insertSessionAndTranscript(\n db: DatabaseSync,\n sessionKey: string,\n transcriptId: string,\n cwd: string,\n metadata: SessionMetadata,\n): void {\n const row = metadataToSessionInsert(sessionKey, transcriptId, metadata);\n db.prepare(\n `INSERT INTO sessions (\n session_key, agent_id, current_transcript_id, status, name, tags_json,\n created_at, updated_at, last_accessed_at, session_started_at, last_interaction_at,\n source_channel, source_chat_id, session_type, routing_json, custom_data_json,\n abort_cutoff_timestamp, message_count, estimated_tokens, compacted_count,\n thinking_level, verbose_level\n ) VALUES (\n ?, ?, ?, ?, ?, ?,\n ?, ?, ?, ?, ?,\n ?, ?, ?, ?, ?,\n ?, ?, ?, ?,\n ?, ?\n )`,\n ).run(\n row.sessionKey,\n row.agentId,\n row.transcriptId,\n row.status,\n row.name,\n row.tagsJson,\n row.createdAt,\n row.updatedAt,\n row.lastAccessedAt,\n row.sessionStartedAt,\n row.lastInteractionAt,\n row.sourceChannel,\n row.sourceChatId,\n row.sessionType,\n row.routingJson,\n row.customDataJson,\n row.abortCutoffTimestamp,\n row.messageCount,\n row.estimatedTokens,\n row.compactedCount,\n row.thinkingLevel,\n row.verboseLevel,\n );\n\n const now = Date.now();\n db.prepare(\n `INSERT INTO transcripts (transcript_id, session_key, status, created_at, cwd)\n VALUES (?, ?, 'active', ?, ?)`,\n ).run(transcriptId, sessionKey, now, cwd);\n}\n\nexport function ensureSessionInTransaction(\n db: DatabaseSync,\n sessionKey: string,\n cwd: string,\n): SessionMetadata {\n const existing = readSessionRow(db, sessionKey);\n if (existing) {\n return sessionRowToMetadata(sessionKey, existing);\n }\n\n const transcriptId = validateSessionId(randomUUID());\n const metadata = buildDefaultSessionMetadata(sessionKey);\n metadata.transcriptId = transcriptId;\n insertSessionAndTranscript(db, sessionKey, transcriptId, cwd, metadata);\n const row = readSessionRow(db, sessionKey);\n if (!row) {\n throw new Error(`Failed to create session: ${sessionKey}`);\n }\n return sessionRowToMetadata(sessionKey, row);\n}\n\nexport function ensureSessionRecord(sessionKey: string, cwd: string): SessionMetadata {\n return runSqliteWriteTransaction((db) => ensureSessionInTransaction(db, sessionKey, cwd));\n}\n\nexport function readCurrentTranscriptId(db: DatabaseSync, sessionKey: string): string | null {\n const row = db\n .prepare(`SELECT current_transcript_id FROM sessions WHERE session_key = ?`)\n .get(sessionKey) as { current_transcript_id?: string } | undefined;\n return row?.current_transcript_id ?? null;\n}\n\nexport function getSessionMetadata(sessionKey: string): SessionMetadata | null {\n const db = getSqliteDatabase();\n const row = readSessionRow(db, sessionKey);\n if (!row) {\n return null;\n }\n return sessionRowToMetadata(sessionKey, row);\n}\n\nexport function getCurrentTranscriptId(sessionKey: string): string | null {\n return readCurrentTranscriptId(getSqliteDatabase(), sessionKey);\n}\n\nexport function listSessionMetadata(query: SessionListQuery = {}): PaginatedResult<SessionMetadata> {\n const db = getSqliteDatabase();\n const conditions: string[] = [];\n const params: Array<string | number> = [];\n\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status];\n conditions.push(`status IN (${statuses.map(() => '?').join(', ')})`);\n params.push(...statuses);\n }\n\n if (query.channel) {\n const rawChannels = query.channel\n .split(',')\n .map((c) => c.trim().toLowerCase())\n .filter(Boolean);\n const channels = [\n ...new Set(\n rawChannels.flatMap((c) => {\n if (c === 'webchat') return ['webchat', 'ui'];\n if (c === 'gateway') return ['gateway', 'webui'];\n return [c];\n }),\n ),\n ];\n if (channels.length === 0) {\n return { items: [], total: 0, limit: query.limit ?? 50, offset: query.offset ?? 0, hasMore: false };\n }\n const directMatch = `LOWER(source_channel) IN (${channels.map(() => '?').join(', ')})`;\n const keyPatterns = new Set<string>();\n for (const c of channels) {\n if (c === 'webchat' || c === 'ui') {\n keyPatterns.add(`session_key LIKE 'agent:%:webchat:%'`);\n } else if (c === 'gateway' || c === 'webui') {\n keyPatterns.add(`session_key LIKE 'agent:%:gateway:%'`);\n } else {\n keyPatterns.add(`session_key LIKE 'agent:%:${c.replace(/'/g, \"''\")}:%'`);\n }\n }\n const keyFallback =\n keyPatterns.size > 0\n ? `(TRIM(COALESCE(source_channel, '')) = '' AND (${[...keyPatterns].join(' OR ')}))`\n : '';\n conditions.push(keyFallback ? `(${directMatch} OR ${keyFallback})` : directMatch);\n params.push(...channels);\n }\n\n if (query.tags?.length) {\n for (const tag of query.tags) {\n conditions.push(`tags_json LIKE ?`);\n params.push(`%\"${tag}\"%`);\n }\n }\n\n let searchKeys: string[] | null = null;\n if (query.search?.trim()) {\n const rawSearch = query.search.trim();\n const ftsQuery = escapeFts5Query(rawSearch);\n const ftsRows = db\n .prepare(\n `SELECT DISTINCT session_key FROM transcript_fts WHERE transcript_fts MATCH ? LIMIT 500`,\n )\n .all(ftsQuery) as Array<{ session_key: string }>;\n const ftsKeys = new Set(ftsRows.map((r) => r.session_key));\n const like = `%${rawSearch.toLowerCase()}%`;\n const metaRows = db\n .prepare(\n `SELECT session_key FROM sessions\n WHERE LOWER(session_key) LIKE ?\n OR LOWER(COALESCE(name, '')) LIKE ?\n OR LOWER(source_channel) LIKE ?\n OR LOWER(source_chat_id) LIKE ?\n OR LOWER(tags_json) LIKE ?`,\n )\n .all(like, like, like, like, like) as Array<{ session_key: string }>;\n searchKeys = [...new Set([...ftsKeys, ...metaRows.map((r) => r.session_key)])];\n if (searchKeys.length === 0) {\n return { items: [], total: 0, limit: query.limit ?? 50, offset: query.offset ?? 0, hasMore: false };\n }\n conditions.push(`session_key IN (${searchKeys.map(() => '?').join(', ')})`);\n params.push(...searchKeys);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const sortColumn = sessionSortColumn(query.sortBy);\n const sortOrder = query.sortOrder === 'asc' ? 'ASC' : 'DESC';\n\n const countRow = db\n .prepare(`SELECT COUNT(*) AS total FROM sessions ${where}`)\n .get(...params) as { total: number };\n const total = countRow.total;\n\n const limit = query.limit ?? 50;\n const offset = query.offset ?? 0;\n const rows = db\n .prepare(\n `SELECT ${SESSION_COLUMNS} FROM sessions ${where}\n ORDER BY ${sortColumn} ${sortOrder}\n LIMIT ? OFFSET ?`,\n )\n .all(...params, limit, offset) as SessionRow[];\n\n const items = rows.map((row) => sessionRowToMetadata(row.session_key, row));\n return { items, total, limit, offset, hasMore: offset + limit < total };\n}\n\nfunction sessionSortColumn(sortBy: SessionListQuery['sortBy']): string {\n switch (sortBy) {\n case 'createdAt':\n return 'created_at';\n case 'messageCount':\n return 'message_count';\n case 'lastAccessedAt':\n return 'last_accessed_at';\n case 'updatedAt':\n default:\n return 'updated_at';\n }\n}\n\nexport function patchSessionMetadata(\n sessionKey: string,\n updates: Partial<SessionMetadata>,\n): SessionMetadata {\n return runSqliteWriteTransaction((db) => {\n const existing = readSessionRow(db, sessionKey);\n if (!existing) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n\n const current = sessionRowToMetadata(sessionKey, existing);\n const merged = { ...current, ...updates, key: sessionKey };\n const now = Date.now();\n\n db.prepare(\n `UPDATE sessions SET\n status = ?,\n name = ?,\n tags_json = ?,\n updated_at = ?,\n last_accessed_at = ?,\n session_started_at = ?,\n last_interaction_at = ?,\n source_channel = ?,\n source_chat_id = ?,\n session_type = ?,\n routing_json = ?,\n custom_data_json = ?,\n abort_cutoff_timestamp = ?,\n message_count = ?,\n estimated_tokens = ?,\n compacted_count = ?,\n thinking_level = ?,\n verbose_level = ?\n WHERE session_key = ?`,\n ).run(\n merged.status,\n merged.name ?? null,\n JSON.stringify(merged.tags ?? []),\n Date.parse(merged.updatedAt) || now,\n Date.parse(merged.lastAccessedAt) || now,\n merged.sessionStartedAt ? Date.parse(merged.sessionStartedAt) : existing.session_started_at,\n merged.lastInteractionAt ? Date.parse(merged.lastInteractionAt) : existing.last_interaction_at,\n merged.sourceChannel,\n merged.sourceChatId,\n merged.sessionType ?? null,\n merged.routing ? JSON.stringify(merged.routing) : null,\n merged.customData ? JSON.stringify(merged.customData) : null,\n merged.abortCutoffTimestamp ?? null,\n merged.messageCount,\n merged.estimatedTokens,\n merged.compactedCount,\n existing.thinking_level,\n existing.verbose_level,\n sessionKey,\n );\n\n const row = readSessionRow(db, sessionKey);\n if (!row) {\n throw new Error(`Session not found after patch: ${sessionKey}`);\n }\n return sessionRowToMetadata(sessionKey, row);\n });\n}\n\nexport function updateSessionStats(\n sessionKey: string,\n stats: { messageCount: number; estimatedTokens: number; lastInteractionAt?: number },\n): void {\n runSqliteWriteTransaction((db) => {\n const now = stats.lastInteractionAt ?? Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = ?,\n estimated_tokens = ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(stats.messageCount, stats.estimatedTokens, now, now, now, sessionKey);\n });\n}\n\nexport function incrementSessionStatsOnAppend(sessionKey: string, tokenDelta = 0): void {\n runSqliteWriteTransaction((db) => {\n const now = Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = message_count + 1,\n estimated_tokens = estimated_tokens + ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(tokenDelta, now, now, now, sessionKey);\n });\n}\n\nexport function resetSessionRecord(\n sessionKey: string,\n cwd: string,\n): { sessionId: string; previousSessionId: string } | null {\n return runSqliteWriteTransaction((db) => {\n const existing = readSessionRow(db, sessionKey);\n if (!existing) {\n return null;\n }\n\n const previousSessionId = existing.current_transcript_id;\n const now = Date.now();\n db.prepare(\n `UPDATE transcripts SET status = 'archived', archive_reason = 'reset', archived_at = ?\n WHERE transcript_id = ?`,\n ).run(now, previousSessionId);\n\n const newTranscriptId = validateSessionId(randomUUID());\n db.prepare(\n `INSERT INTO transcripts (transcript_id, session_key, status, created_at, cwd)\n VALUES (?, ?, 'active', ?, ?)`,\n ).run(newTranscriptId, sessionKey, now, cwd);\n\n db.prepare(\n `UPDATE sessions SET\n current_transcript_id = ?,\n updated_at = ?,\n session_started_at = ?,\n last_interaction_at = NULL,\n message_count = 0,\n estimated_tokens = 0\n WHERE session_key = ?`,\n ).run(newTranscriptId, now, now, sessionKey);\n\n return { sessionId: newTranscriptId, previousSessionId };\n });\n}\n\nexport function deleteSessionRecord(sessionKey: string): boolean {\n return runSqliteWriteTransaction((db) => {\n const existing = readSessionRow(db, sessionKey);\n if (!existing) {\n return false;\n }\n\n const now = Date.now();\n db.prepare(\n `UPDATE transcripts SET status = 'archived', archive_reason = 'delete', archived_at = ?\n WHERE transcript_id = ?`,\n ).run(now, existing.current_transcript_id);\n\n db.prepare(`DELETE FROM sessions WHERE session_key = ?`).run(sessionKey);\n return true;\n });\n}\n\nexport function listSessionsByAgent(agentId: string): SessionMetadata[] {\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT ${SESSION_COLUMNS} FROM sessions WHERE agent_id = ? ORDER BY updated_at DESC`,\n )\n .all(agentId.toLowerCase()) as SessionRow[];\n return rows.map((row) => sessionRowToMetadata(row.session_key, row));\n}\n\nexport function getSessionPersistedLevels(sessionKey: string): {\n thinkingLevel: string | null;\n verboseLevel: string | null;\n} | null {\n const db = getSqliteDatabase();\n const row = db\n .prepare(`SELECT thinking_level, verbose_level FROM sessions WHERE session_key = ?`)\n .get(sessionKey) as { thinking_level: string | null; verbose_level: string | null } | undefined;\n if (!row) {\n return null;\n }\n return { thinkingLevel: row.thinking_level, verboseLevel: row.verbose_level };\n}\n\nexport function findSessionKeyByTranscriptId(transcriptId: string): string | null {\n const db = getSqliteDatabase();\n const row = db\n .prepare(`SELECT session_key FROM sessions WHERE current_transcript_id = ?`)\n .get(transcriptId) as { session_key?: string } | undefined;\n return row?.session_key ?? null;\n}\n\nexport function getGlobalSessionStats(): ReturnType<typeof buildGlobalSessionStats> {\n const all = listSessionMetadata({ limit: 100_000, offset: 0 });\n return buildGlobalSessionStats(all.items);\n}\n\nexport function resolveSessionAgentId(sessionKey: string): string {\n return resolveAgentIdFromSessionKey(sessionKey);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-repository.js","names":[],"sources":["../../../../src/storage/sqlite/session-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport { validateSessionId } from '../../session/session-id.js';\nimport type { SessionListQuery, SessionMetadata, PaginatedResult } from '../../session/types.js';\nimport {\n buildDefaultSessionMetadata,\n resolveAgentIdFromSessionKey,\n} from './session-metadata.js';\nimport {\n buildGlobalSessionStats,\n metadataToSessionInsert,\n sessionRowToMetadata,\n type SessionRow,\n} from './row-mappers.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\nconst SESSION_COLUMNS = `\n session_key, agent_id, current_transcript_id, status, name, tags_json,\n created_at, updated_at, last_accessed_at, session_started_at, last_interaction_at,\n source_channel, source_chat_id, session_type, routing_json, custom_data_json,\n abort_cutoff_timestamp, message_count, estimated_tokens, compacted_count,\n last_flushed_at, flush_count,\n thinking_level, verbose_level\n`;\n\nimport { escapeFts5Query } from './fts.js';\n\nconst SELECT_SESSION = `SELECT ${SESSION_COLUMNS} FROM sessions WHERE session_key = ?`;\n\nfunction readSessionRow(db: DatabaseSync, sessionKey: string): SessionRow | undefined {\n return db.prepare(SELECT_SESSION).get(sessionKey) as SessionRow | undefined;\n}\n\nfunction insertSessionAndTranscript(\n db: DatabaseSync,\n sessionKey: string,\n transcriptId: string,\n cwd: string,\n metadata: SessionMetadata,\n): void {\n const row = metadataToSessionInsert(sessionKey, transcriptId, metadata);\n db.prepare(\n `INSERT INTO sessions (\n session_key, agent_id, current_transcript_id, status, name, tags_json,\n created_at, updated_at, last_accessed_at, session_started_at, last_interaction_at,\n source_channel, source_chat_id, session_type, routing_json, custom_data_json,\n abort_cutoff_timestamp, message_count, estimated_tokens, compacted_count,\n last_flushed_at, flush_count,\n thinking_level, verbose_level\n ) VALUES (\n ?, ?, ?, ?, ?, ?,\n ?, ?, ?, ?, ?,\n ?, ?, ?, ?, ?,\n ?, ?, ?, ?, ?, ?,\n ?, ?\n )`,\n ).run(\n row.sessionKey,\n row.agentId,\n row.transcriptId,\n row.status,\n row.name,\n row.tagsJson,\n row.createdAt,\n row.updatedAt,\n row.lastAccessedAt,\n row.sessionStartedAt,\n row.lastInteractionAt,\n row.sourceChannel,\n row.sourceChatId,\n row.sessionType,\n row.routingJson,\n row.customDataJson,\n row.abortCutoffTimestamp,\n row.messageCount,\n row.estimatedTokens,\n row.compactedCount,\n row.lastFlushedAt,\n row.flushCount,\n row.thinkingLevel,\n row.verboseLevel,\n );\n\n const now = Date.now();\n db.prepare(\n `INSERT INTO transcripts (transcript_id, session_key, status, created_at, cwd)\n VALUES (?, ?, 'active', ?, ?)`,\n ).run(transcriptId, sessionKey, now, cwd);\n}\n\nexport function ensureSessionInTransaction(\n db: DatabaseSync,\n sessionKey: string,\n cwd: string,\n): SessionMetadata {\n const existing = readSessionRow(db, sessionKey);\n if (existing) {\n return sessionRowToMetadata(sessionKey, existing);\n }\n\n const transcriptId = validateSessionId(randomUUID());\n const metadata = buildDefaultSessionMetadata(sessionKey);\n metadata.transcriptId = transcriptId;\n insertSessionAndTranscript(db, sessionKey, transcriptId, cwd, metadata);\n const row = readSessionRow(db, sessionKey);\n if (!row) {\n throw new Error(`Failed to create session: ${sessionKey}`);\n }\n return sessionRowToMetadata(sessionKey, row);\n}\n\nexport function ensureSessionRecord(sessionKey: string, cwd: string): SessionMetadata {\n return runSqliteWriteTransaction((db) => ensureSessionInTransaction(db, sessionKey, cwd));\n}\n\nexport function readCurrentTranscriptId(db: DatabaseSync, sessionKey: string): string | null {\n const row = db\n .prepare(`SELECT current_transcript_id FROM sessions WHERE session_key = ?`)\n .get(sessionKey) as { current_transcript_id?: string } | undefined;\n return row?.current_transcript_id ?? null;\n}\n\nexport function getSessionMetadata(sessionKey: string): SessionMetadata | null {\n const db = getSqliteDatabase();\n const row = readSessionRow(db, sessionKey);\n if (!row) {\n return null;\n }\n return sessionRowToMetadata(sessionKey, row);\n}\n\nexport function getCurrentTranscriptId(sessionKey: string): string | null {\n return readCurrentTranscriptId(getSqliteDatabase(), sessionKey);\n}\n\nexport function listSessionMetadata(query: SessionListQuery = {}): PaginatedResult<SessionMetadata> {\n const db = getSqliteDatabase();\n const conditions: string[] = [];\n const params: Array<string | number> = [];\n\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status];\n conditions.push(`status IN (${statuses.map(() => '?').join(', ')})`);\n params.push(...statuses);\n }\n\n if (query.channel) {\n const rawChannels = query.channel\n .split(',')\n .map((c) => c.trim().toLowerCase())\n .filter(Boolean);\n const channels = [\n ...new Set(\n rawChannels.flatMap((c) => {\n if (c === 'webchat') return ['webchat', 'ui'];\n if (c === 'gateway') return ['gateway', 'webui'];\n return [c];\n }),\n ),\n ];\n if (channels.length === 0) {\n return { items: [], total: 0, limit: query.limit ?? 50, offset: query.offset ?? 0, hasMore: false };\n }\n const directMatch = `LOWER(source_channel) IN (${channels.map(() => '?').join(', ')})`;\n const keyPatterns = new Set<string>();\n for (const c of channels) {\n if (c === 'webchat' || c === 'ui') {\n keyPatterns.add(`session_key LIKE 'agent:%:webchat:%'`);\n } else if (c === 'gateway' || c === 'webui') {\n keyPatterns.add(`session_key LIKE 'agent:%:gateway:%'`);\n } else {\n keyPatterns.add(`session_key LIKE 'agent:%:${c.replace(/'/g, \"''\")}:%'`);\n }\n }\n const keyFallback =\n keyPatterns.size > 0\n ? `(TRIM(COALESCE(source_channel, '')) = '' AND (${[...keyPatterns].join(' OR ')}))`\n : '';\n conditions.push(keyFallback ? `(${directMatch} OR ${keyFallback})` : directMatch);\n params.push(...channels);\n }\n\n if (query.tags?.length) {\n for (const tag of query.tags) {\n conditions.push(`tags_json LIKE ?`);\n params.push(`%\"${tag}\"%`);\n }\n }\n\n let searchKeys: string[] | null = null;\n if (query.search?.trim()) {\n const rawSearch = query.search.trim();\n const ftsQuery = escapeFts5Query(rawSearch);\n const ftsRows = db\n .prepare(\n `SELECT DISTINCT session_key FROM transcript_fts WHERE transcript_fts MATCH ? LIMIT 500`,\n )\n .all(ftsQuery) as Array<{ session_key: string }>;\n const ftsKeys = new Set(ftsRows.map((r) => r.session_key));\n const like = `%${rawSearch.toLowerCase()}%`;\n const metaRows = db\n .prepare(\n `SELECT session_key FROM sessions\n WHERE LOWER(session_key) LIKE ?\n OR LOWER(COALESCE(name, '')) LIKE ?\n OR LOWER(source_channel) LIKE ?\n OR LOWER(source_chat_id) LIKE ?\n OR LOWER(tags_json) LIKE ?`,\n )\n .all(like, like, like, like, like) as Array<{ session_key: string }>;\n searchKeys = [...new Set([...ftsKeys, ...metaRows.map((r) => r.session_key)])];\n if (searchKeys.length === 0) {\n return { items: [], total: 0, limit: query.limit ?? 50, offset: query.offset ?? 0, hasMore: false };\n }\n conditions.push(`session_key IN (${searchKeys.map(() => '?').join(', ')})`);\n params.push(...searchKeys);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const sortColumn = sessionSortColumn(query.sortBy);\n const sortOrder = query.sortOrder === 'asc' ? 'ASC' : 'DESC';\n\n const countRow = db\n .prepare(`SELECT COUNT(*) AS total FROM sessions ${where}`)\n .get(...params) as { total: number };\n const total = countRow.total;\n\n const limit = query.limit ?? 50;\n const offset = query.offset ?? 0;\n const rows = db\n .prepare(\n `SELECT ${SESSION_COLUMNS} FROM sessions ${where}\n ORDER BY ${sortColumn} ${sortOrder}\n LIMIT ? OFFSET ?`,\n )\n .all(...params, limit, offset) as SessionRow[];\n\n const items = rows.map((row) => sessionRowToMetadata(row.session_key, row));\n return { items, total, limit, offset, hasMore: offset + limit < total };\n}\n\nfunction sessionSortColumn(sortBy: SessionListQuery['sortBy']): string {\n switch (sortBy) {\n case 'createdAt':\n return 'created_at';\n case 'messageCount':\n return 'message_count';\n case 'lastAccessedAt':\n return 'last_accessed_at';\n case 'updatedAt':\n default:\n return 'updated_at';\n }\n}\n\nexport function patchSessionMetadata(\n sessionKey: string,\n updates: Partial<SessionMetadata>,\n): SessionMetadata {\n return runSqliteWriteTransaction((db) => {\n const existing = readSessionRow(db, sessionKey);\n if (!existing) {\n throw new Error(`Session not found: ${sessionKey}`);\n }\n\n const current = sessionRowToMetadata(sessionKey, existing);\n const merged = { ...current, ...updates, key: sessionKey };\n const now = Date.now();\n\n db.prepare(\n `UPDATE sessions SET\n status = ?,\n name = ?,\n tags_json = ?,\n updated_at = ?,\n last_accessed_at = ?,\n session_started_at = ?,\n last_interaction_at = ?,\n source_channel = ?,\n source_chat_id = ?,\n session_type = ?,\n routing_json = ?,\n custom_data_json = ?,\n abort_cutoff_timestamp = ?,\n message_count = ?,\n estimated_tokens = ?,\n compacted_count = ?,\n last_flushed_at = ?,\n flush_count = ?,\n thinking_level = ?,\n verbose_level = ?\n WHERE session_key = ?`,\n ).run(\n merged.status,\n merged.name ?? null,\n JSON.stringify(merged.tags ?? []),\n Date.parse(merged.updatedAt) || now,\n Date.parse(merged.lastAccessedAt) || now,\n merged.sessionStartedAt ? Date.parse(merged.sessionStartedAt) : existing.session_started_at,\n merged.lastInteractionAt ? Date.parse(merged.lastInteractionAt) : existing.last_interaction_at,\n merged.sourceChannel,\n merged.sourceChatId,\n merged.sessionType ?? null,\n merged.routing ? JSON.stringify(merged.routing) : null,\n merged.customData ? JSON.stringify(merged.customData) : null,\n merged.abortCutoffTimestamp ?? null,\n merged.messageCount,\n merged.estimatedTokens,\n merged.compactedCount,\n merged.lastFlushedAt ?? null,\n merged.flushCount ?? 0,\n existing.thinking_level,\n existing.verbose_level,\n sessionKey,\n );\n\n const row = readSessionRow(db, sessionKey);\n if (!row) {\n throw new Error(`Session not found after patch: ${sessionKey}`);\n }\n return sessionRowToMetadata(sessionKey, row);\n });\n}\n\nexport function updateSessionStats(\n sessionKey: string,\n stats: { messageCount: number; estimatedTokens: number; lastInteractionAt?: number },\n): void {\n runSqliteWriteTransaction((db) => {\n const now = stats.lastInteractionAt ?? Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = ?,\n estimated_tokens = ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(stats.messageCount, stats.estimatedTokens, now, now, now, sessionKey);\n });\n}\n\nexport function incrementSessionStatsOnAppend(sessionKey: string, tokenDelta = 0): void {\n runSqliteWriteTransaction((db) => {\n const now = Date.now();\n db.prepare(\n `UPDATE sessions SET\n message_count = message_count + 1,\n estimated_tokens = estimated_tokens + ?,\n updated_at = ?,\n last_accessed_at = ?,\n last_interaction_at = ?\n WHERE session_key = ?`,\n ).run(tokenDelta, now, now, now, sessionKey);\n });\n}\n\nexport function resetSessionRecord(\n sessionKey: string,\n cwd: string,\n): { sessionId: string; previousSessionId: string } | null {\n return runSqliteWriteTransaction((db) => {\n const existing = readSessionRow(db, sessionKey);\n if (!existing) {\n return null;\n }\n\n const previousSessionId = existing.current_transcript_id;\n const now = Date.now();\n db.prepare(\n `UPDATE transcripts SET status = 'archived', archive_reason = 'reset', archived_at = ?\n WHERE transcript_id = ?`,\n ).run(now, previousSessionId);\n\n const newTranscriptId = validateSessionId(randomUUID());\n db.prepare(\n `INSERT INTO transcripts (transcript_id, session_key, status, created_at, cwd)\n VALUES (?, ?, 'active', ?, ?)`,\n ).run(newTranscriptId, sessionKey, now, cwd);\n\n db.prepare(\n `UPDATE sessions SET\n current_transcript_id = ?,\n updated_at = ?,\n session_started_at = ?,\n last_interaction_at = NULL,\n message_count = 0,\n estimated_tokens = 0\n WHERE session_key = ?`,\n ).run(newTranscriptId, now, now, sessionKey);\n\n return { sessionId: newTranscriptId, previousSessionId };\n });\n}\n\nexport function deleteSessionRecord(sessionKey: string): boolean {\n return runSqliteWriteTransaction((db) => {\n const existing = readSessionRow(db, sessionKey);\n if (!existing) {\n return false;\n }\n\n const now = Date.now();\n db.prepare(\n `UPDATE transcripts SET status = 'archived', archive_reason = 'delete', archived_at = ?\n WHERE transcript_id = ?`,\n ).run(now, existing.current_transcript_id);\n\n db.prepare(`DELETE FROM sessions WHERE session_key = ?`).run(sessionKey);\n return true;\n });\n}\n\nexport function listSessionsByAgent(agentId: string): SessionMetadata[] {\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT ${SESSION_COLUMNS} FROM sessions WHERE agent_id = ? ORDER BY updated_at DESC`,\n )\n .all(agentId.toLowerCase()) as SessionRow[];\n return rows.map((row) => sessionRowToMetadata(row.session_key, row));\n}\n\nexport function getSessionPersistedLevels(sessionKey: string): {\n thinkingLevel: string | null;\n verboseLevel: string | null;\n} | null {\n const db = getSqliteDatabase();\n const row = db\n .prepare(`SELECT thinking_level, verbose_level FROM sessions WHERE session_key = ?`)\n .get(sessionKey) as { thinking_level: string | null; verbose_level: string | null } | undefined;\n if (!row) {\n return null;\n }\n return { thinkingLevel: row.thinking_level, verboseLevel: row.verbose_level };\n}\n\nexport function findSessionKeyByTranscriptId(transcriptId: string): string | null {\n const db = getSqliteDatabase();\n const row = db\n .prepare(`SELECT session_key FROM sessions WHERE current_transcript_id = ?`)\n .get(transcriptId) as { session_key?: string } | undefined;\n return row?.session_key ?? null;\n}\n\nexport function getGlobalSessionStats(): ReturnType<typeof buildGlobalSessionStats> {\n const all = listSessionMetadata({ limit: 100_000, offset: 0 });\n return buildGlobalSessionStats(all.items);\n}\n\nexport function resolveSessionAgentId(sessionKey: string): string {\n return resolveAgentIdFromSessionKey(sessionKey);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xopcai/xopc",
3
- "version": "0.0.99",
3
+ "version": "0.0.100",
4
4
  "description": "The OPC workstation that grows with you: AI assistant for One Person Companies — CLI, gateway, multi-channel (Telegram/WeChat), 20+ LLM providers via pi-ai, extensions and skills.",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",