@xopcai/xopc 0.0.95 → 0.0.97

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 (442) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  3. package/dist/extensions/feishu/src/workflow-progress.js +1 -1
  4. package/dist/extensions/telegram/src/plugin.js +1 -1
  5. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  6. package/dist/extensions/telegram/src/workflow-progress.js +1 -1
  7. package/dist/extensions/telegram/xopc.extension.json +1 -1
  8. package/dist/extensions/weixin/src/api/api.js +2 -2
  9. package/dist/extensions/weixin/src/auth/accounts.js +1 -1
  10. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  11. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  12. package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
  13. package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
  14. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  15. package/dist/extensions/weixin/src/plugin.js +1 -1
  16. package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
  17. package/dist/extensions/weixin/src/workflow-progress.js +1 -1
  18. package/dist/gateway/static/root/assets/{agents-CKe2LMnz.js → agents-B_YUvNi6.js} +2 -2
  19. package/dist/gateway/static/root/assets/{apps-page-Mi9mMIZ1.js → apps-page-BmwG5aur.js} +1 -1
  20. package/dist/gateway/static/root/assets/{channels-settings-BrdyC101.js → channels-settings-BiwkeKPb.js} +1 -1
  21. package/dist/gateway/static/root/assets/{channels-status-swr-D55Bu0nn.js → channels-status-swr-ChyN473C.js} +1 -1
  22. package/dist/gateway/static/root/assets/{cron-api-CPpx2l-E.js → cron-api-CvSifIfJ.js} +1 -1
  23. package/dist/gateway/static/root/assets/{cron-page-Bx2jB0YN.js → cron-page-BDqTDFy6.js} +1 -1
  24. package/dist/gateway/static/root/assets/{dist-D_AiG_Kg.js → dist-DxsUrjpy.js} +1 -1
  25. package/dist/gateway/static/root/assets/{extension-debug-page-6ieHsxRE.js → extension-debug-page-DV_Av5Jq.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-page-B8nywHRO.js → extension-page-CwZwRhWw.js} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-settings-page-DrskdEIV.js → extension-settings-page-Bb7TR1Se.js} +1 -1
  28. package/dist/gateway/static/root/assets/{fetch-B0aeeY0q.js → fetch-BLLOP2CM.js} +1 -1
  29. package/dist/gateway/static/root/assets/{field-primitives--9ooY8Xl.js → field-primitives-CyqVu1QR.js} +1 -1
  30. package/dist/gateway/static/root/assets/{heartbeat-config-api-DUZ_W1w-.js → heartbeat-config-api-Cd4M1eHt.js} +1 -1
  31. package/dist/gateway/static/root/assets/{index-Dj9FuxCm.js → index-0tS9lV85.js} +74 -74
  32. package/dist/gateway/static/root/assets/index-BJDmBCSl.css +1 -0
  33. package/dist/gateway/static/root/assets/{logs-page-CaXqhpKf.js → logs-page-BsAOSowN.js} +1 -1
  34. package/dist/gateway/static/root/assets/{note-detail-page-B91pLkEI.css → note-detail-page-D4ZIVQbk.css} +1 -1
  35. package/dist/gateway/static/root/assets/{note-detail-page-DYzym2B0.js → note-detail-page-Dlxoy6Ap.js} +54 -53
  36. package/dist/gateway/static/root/assets/{note-time-B-vSi2dR.js → note-time-B-r8yTpQ.js} +1 -1
  37. package/dist/gateway/static/root/assets/{notes-page-BkhWdGiT.js → notes-page-CHFcyqYW.js} +1 -1
  38. package/dist/gateway/static/root/assets/{sessions-page-53YFokoe.js → sessions-page-Ctu0kgt7.js} +1 -1
  39. package/dist/gateway/static/root/assets/{settings-advanced-gate-BaZmaklx.js → settings-advanced-gate-Dh0TyOOg.js} +1 -1
  40. package/dist/gateway/static/root/assets/{settings-form-section-DIJPKpTR.js → settings-form-section-DXMCEW1d.js} +1 -1
  41. package/dist/gateway/static/root/assets/{settings-page-Dvb230FF.js → settings-page-CIkZ7233.js} +1 -1
  42. package/dist/gateway/static/root/assets/{share-preview-page-CRyjTAG6.js → share-preview-page-7RV65xhJ.js} +1 -1
  43. package/dist/gateway/static/root/assets/{skills-page-C5ZJbfAe.js → skills-page-D_Az1SlU.js} +1 -1
  44. package/dist/gateway/static/root/assets/{theme-store-Cg_SuBw0.js → theme-store-e2q2yjs4.js} +1 -1
  45. package/dist/gateway/static/root/assets/url-DpFBIyN9.js +3 -0
  46. package/dist/gateway/static/root/assets/{utils-lMYoWhqo.js → utils-OA_b1q0Q.js} +1 -1
  47. package/dist/gateway/static/root/assets/{voice-api-key-field-Dda2pcUU.js → voice-api-key-field-SJml1hAt.js} +1 -1
  48. package/dist/gateway/static/root/assets/{workflow-page.utils-KIladUrU.js → workflow-page.utils-D90VVCzC.js} +1 -1
  49. package/dist/gateway/static/root/assets/{workflows-page-BTis4Z7Y.js → workflows-page-y7Btji0J.js} +1 -1
  50. package/dist/gateway/static/root/index.html +5 -5
  51. package/dist/package.js +1 -1
  52. package/dist/src/agent/agent-manager.js +12 -8
  53. package/dist/src/agent/agent-manager.js.map +1 -1
  54. package/dist/src/agent/agent-scope.d.ts +0 -1
  55. package/dist/src/agent/agent-scope.js +2 -5
  56. package/dist/src/agent/agent-scope.js.map +1 -1
  57. package/dist/src/agent/bootstrap/bootstrap-cache.d.ts +2 -0
  58. package/dist/src/agent/bootstrap/bootstrap-cache.js +10 -1
  59. package/dist/src/agent/bootstrap/bootstrap-cache.js.map +1 -1
  60. package/dist/src/agent/bootstrap/bootstrap-files.d.ts +2 -1
  61. package/dist/src/agent/bootstrap/bootstrap-files.js +34 -12
  62. package/dist/src/agent/bootstrap/bootstrap-files.js.map +1 -1
  63. package/dist/src/agent/bootstrap/load-bootstrap-files.d.ts +1 -2
  64. package/dist/src/agent/bootstrap/load-bootstrap-files.js +6 -12
  65. package/dist/src/agent/bootstrap/load-bootstrap-files.js.map +1 -1
  66. package/dist/src/agent/bootstrap/types.d.ts +5 -5
  67. package/dist/src/agent/context/workspace-seed.js +6 -6
  68. package/dist/src/agent/context/workspace-seed.js.map +1 -1
  69. package/dist/src/agent/context/workspace-state.d.ts +20 -0
  70. package/dist/src/agent/context/workspace-state.js +57 -0
  71. package/dist/src/agent/context/workspace-state.js.map +1 -0
  72. package/dist/src/agent/context/workspace-templates/AGENTS.md +0 -4
  73. package/dist/src/agent/embedded/index.d.ts +2 -2
  74. package/dist/src/agent/embedded/index.js +3 -3
  75. package/dist/src/agent/embedded/run-turn.js +0 -3
  76. package/dist/src/agent/embedded/run-turn.js.map +1 -1
  77. package/dist/src/agent/embedded/session-manager-init.d.ts +0 -17
  78. package/dist/src/agent/embedded/session-manager-init.js +1 -36
  79. package/dist/src/agent/embedded/session-manager-init.js.map +1 -1
  80. package/dist/src/agent/embedded/session-runner.d.ts +3 -12
  81. package/dist/src/agent/embedded/session-runner.js +12 -26
  82. package/dist/src/agent/embedded/session-runner.js.map +1 -1
  83. package/dist/src/agent/embedded/session-tool-result-guard.js +2 -4
  84. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  85. package/dist/src/agent/embedded/sqlite-hydrating-session-manager.d.ts +10 -0
  86. package/dist/src/agent/embedded/sqlite-hydrating-session-manager.js +34 -0
  87. package/dist/src/agent/embedded/sqlite-hydrating-session-manager.js.map +1 -0
  88. package/dist/src/agent/goals/goal-run-store.js +4 -4
  89. package/dist/src/agent/goals/persistent-goal-service.js +8 -15
  90. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
  91. package/dist/src/agent/goals/post-turn.js +2 -2
  92. package/dist/src/agent/image/load-image-media.js +2 -2
  93. package/dist/src/agent/ipc/bus.js +1 -1
  94. package/dist/src/agent/ipc/inbox.js +2 -2
  95. package/dist/src/agent/ipc/socket.js +1 -1
  96. package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
  97. package/dist/src/agent/mcp/bundle-mcp-runtime.js +2 -2
  98. package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
  99. package/dist/src/agent/mcp/mcp-transport.js +1 -1
  100. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  101. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  102. package/dist/src/agent/memory/dreaming/events.js +1 -1
  103. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  104. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  105. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  106. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  107. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  108. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  109. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  110. package/dist/src/agent/models/manager.js +1 -1
  111. package/dist/src/agent/prompt/memory/index.d.ts +1 -0
  112. package/dist/src/agent/prompt/memory/index.js +34 -80
  113. package/dist/src/agent/prompt/memory/index.js.map +1 -1
  114. package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
  115. package/dist/src/agent/prompt/system-prompt.js +0 -1
  116. package/dist/src/agent/prompt/system-prompt.js.map +1 -1
  117. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  118. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  119. package/dist/src/agent/sandbox/path-policy.js +2 -2
  120. package/dist/src/agent/service/build-direct-message-content.js +1 -1
  121. package/dist/src/agent/service/process-direct-one-shot.js +8 -17
  122. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  123. package/dist/src/agent/service/process-direct-streaming.js +14 -23
  124. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  125. package/dist/src/agent/service.js +7 -11
  126. package/dist/src/agent/service.js.map +1 -1
  127. package/dist/src/agent/session/session-inspector.js +1 -1
  128. package/dist/src/agent/skills/config.js +1 -1
  129. package/dist/src/agent/skills/hub-hash.js +2 -2
  130. package/dist/src/agent/skills/hub-lock.js +1 -1
  131. package/dist/src/agent/skills/hub-pull.js +3 -3
  132. package/dist/src/agent/skills/index.js +1 -1
  133. package/dist/src/agent/skills/managed-store.js +1 -1
  134. package/dist/src/agent/skills/scanner.js +1 -1
  135. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  136. package/dist/src/agent/skills/skill-manager.js +1 -1
  137. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  138. package/dist/src/agent/tools/factory.js +1 -1
  139. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  140. package/dist/src/agent/tools/index.d.ts +0 -1
  141. package/dist/src/agent/tools/index.js +1 -2
  142. package/dist/src/agent/tools/send-media.js +1 -1
  143. package/dist/src/agent/tools/session-search-tool.d.ts +0 -1
  144. package/dist/src/agent/tools/session-search-tool.js +11 -6
  145. package/dist/src/agent/tools/session-search-tool.js.map +1 -1
  146. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  147. package/dist/src/agent/tools/tool-paths.js +1 -3
  148. package/dist/src/agent/tools/tool-paths.js.map +1 -1
  149. package/dist/src/agent/tools/workflow-tool.js +1 -1
  150. package/dist/src/agent/tools/write.js +1 -1
  151. package/dist/src/agent/workflow/catalog.js +1 -1
  152. package/dist/src/auth/credentials.js +3 -3
  153. package/dist/src/auth/profiles/store.js +1 -1
  154. package/dist/src/auth/sync-provider-auth.js +1 -1
  155. package/dist/src/browser/cache-dir-policy.js +1 -1
  156. package/dist/src/browser/cdp-local-launcher.js +2 -2
  157. package/dist/src/browser/providers/browser-ext-install.js +4 -4
  158. package/dist/src/browser/providers/cloakbrowser.js +4 -4
  159. package/dist/src/browser/providers/playwright-doctor.js +1 -1
  160. package/dist/src/browser/stealth.js +1 -1
  161. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  162. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  163. package/dist/src/channels/outbound/persist-store.js +1 -1
  164. package/dist/src/channels/pairing/allow-from-file.js +1 -1
  165. package/dist/src/channels/pairing/pairing-store.js +2 -2
  166. package/dist/src/chat-commands/agent-edit.js +3 -4
  167. package/dist/src/chat-commands/agent-edit.js.map +1 -1
  168. package/dist/src/chat-commands/builtins/config.js +2 -2
  169. package/dist/src/chat-commands/context.js +1 -1
  170. package/dist/src/cli/commands/config.js +1 -1
  171. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  172. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  173. package/dist/src/cli/commands/doctor/checks/session-integrity.js +32 -95
  174. package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -1
  175. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  176. package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
  177. package/dist/src/cli/commands/extension-dev.js +1 -1
  178. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  179. package/dist/src/cli/commands/extension-pack.js +1 -1
  180. package/dist/src/cli/commands/gateway/logs.js +1 -1
  181. package/dist/src/cli/commands/image.js +1 -1
  182. package/dist/src/cli/commands/init.js +5 -7
  183. package/dist/src/cli/commands/init.js.map +1 -1
  184. package/dist/src/cli/commands/onboard.js +0 -8
  185. package/dist/src/cli/commands/onboard.js.map +1 -1
  186. package/dist/src/cli/templates.d.ts +3 -10
  187. package/dist/src/cli/templates.js +4 -32
  188. package/dist/src/cli/templates.js.map +1 -1
  189. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  190. package/dist/src/commands/agents.config.js +1 -1
  191. package/dist/src/config/agent-profile.js +1 -1
  192. package/dist/src/config/gateway-bind.js +1 -1
  193. package/dist/src/config/index.js +7 -8
  194. package/dist/src/config/index.js.map +1 -1
  195. package/dist/src/config/loader.js +2 -2
  196. package/dist/src/config/models-json.js +2 -2
  197. package/dist/src/config/paths-state.d.ts +3 -0
  198. package/dist/src/config/paths-state.js +7 -3
  199. package/dist/src/config/paths-state.js.map +1 -1
  200. package/dist/src/config/paths.d.ts +5 -36
  201. package/dist/src/config/paths.js +7 -52
  202. package/dist/src/config/paths.js.map +1 -1
  203. package/dist/src/config/profile.js +2 -2
  204. package/dist/src/config/schema.d.ts +15 -0
  205. package/dist/src/config/schema.js +11 -0
  206. package/dist/src/config/schema.js.map +1 -1
  207. package/dist/src/config/workspace-path.js +1 -1
  208. package/dist/src/cron/execution-types.d.ts +42 -0
  209. package/dist/src/cron/executor.js +2 -2
  210. package/dist/src/cron/persistence.js +1 -1
  211. package/dist/src/cron/run-log-store.d.ts +4 -8
  212. package/dist/src/cron/run-log-store.js +26 -78
  213. package/dist/src/cron/run-log-store.js.map +1 -1
  214. package/dist/src/cron/service.d.ts +3 -3
  215. package/dist/src/cron/service.js +2 -2
  216. package/dist/src/cron/service.js.map +1 -1
  217. package/dist/src/cron/types.d.ts +1 -42
  218. package/dist/src/daemon/constants.js +1 -1
  219. package/dist/src/daemon/install-plan.js +2 -2
  220. package/dist/src/daemon/launchd.js +2 -2
  221. package/dist/src/daemon/schtasks.js +2 -2
  222. package/dist/src/daemon/systemd.js +2 -2
  223. package/dist/src/extensions/bundle-mcp.js +1 -1
  224. package/dist/src/extensions/discover-extensions.js +1 -1
  225. package/dist/src/extensions/health.js +1 -1
  226. package/dist/src/extensions/loader.js +1 -1
  227. package/dist/src/extensions/lockfile.js +2 -2
  228. package/dist/src/extensions/update.js +1 -1
  229. package/dist/src/gateway/agents-admin.js +4 -4
  230. package/dist/src/gateway/agents-admin.js.map +1 -1
  231. package/dist/src/gateway/file-path-classifier.d.ts +0 -1
  232. package/dist/src/gateway/file-path-classifier.js +2 -8
  233. package/dist/src/gateway/file-path-classifier.js.map +1 -1
  234. package/dist/src/gateway/heartbeat/service.js +1 -1
  235. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  236. package/dist/src/gateway/hono/lib/extension-store.js +2 -2
  237. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  238. package/dist/src/gateway/hono/oauth.js +1 -1
  239. package/dist/src/gateway/hono/routes/agents.js +1 -1
  240. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  241. package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
  242. package/dist/src/gateway/hono/routes/dreaming.js +1 -1
  243. package/dist/src/gateway/hono/routes/host-fs.js +2 -2
  244. package/dist/src/gateway/hono/routes/models.js +1 -1
  245. package/dist/src/gateway/hono/routes/shares.js +1 -1
  246. package/dist/src/gateway/hono/routes/workspace.js +2 -2
  247. package/dist/src/gateway/lock.js +3 -3
  248. package/dist/src/gateway/ports.js +1 -1
  249. package/dist/src/gateway/service/agent-runner.js +2 -2
  250. package/dist/src/gateway/service/marketplace-service.js +2 -2
  251. package/dist/src/gateway/service.js +5 -1
  252. package/dist/src/gateway/service.js.map +1 -1
  253. package/dist/src/gateway/session-reset-service.d.ts +1 -1
  254. package/dist/src/gateway/session-reset-service.js +1 -1
  255. package/dist/src/gateway/session-reset-service.js.map +1 -1
  256. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  257. package/dist/src/heartbeat/index.js +1 -1
  258. package/dist/src/infra/brew.js +1 -1
  259. package/dist/src/infra/node-sqlite.d.ts +1 -0
  260. package/dist/src/infra/node-sqlite.js +17 -0
  261. package/dist/src/infra/node-sqlite.js.map +1 -0
  262. package/dist/src/infra/package-json.js +1 -1
  263. package/dist/src/infra/package-update-steps.js +1 -1
  264. package/dist/src/infra/path-env.js +2 -2
  265. package/dist/src/infra/restart.js +2 -2
  266. package/dist/src/infra/sqlite-errors.d.ts +1 -0
  267. package/dist/src/infra/sqlite-errors.js +77 -0
  268. package/dist/src/infra/sqlite-errors.js.map +1 -0
  269. package/dist/src/infra/stable-node-path.js +1 -1
  270. package/dist/src/infra/unhandled-rejections.d.ts +1 -0
  271. package/dist/src/infra/unhandled-rejections.js +25 -0
  272. package/dist/src/infra/unhandled-rejections.js.map +1 -0
  273. package/dist/src/infra/update-check.js +1 -1
  274. package/dist/src/infra/update-global.js +1 -1
  275. package/dist/src/infra/update-lock.js +3 -3
  276. package/dist/src/infra/update-runner.js +1 -1
  277. package/dist/src/infra/update-startup.js +2 -2
  278. package/dist/src/infra/warning-filter.d.ts +7 -0
  279. package/dist/src/infra/warning-filter.js +59 -0
  280. package/dist/src/infra/warning-filter.js.map +1 -0
  281. package/dist/src/infra/write-file-atomic.js +2 -2
  282. package/dist/src/notes/store.d.ts +3 -9
  283. package/dist/src/notes/store.js +22 -196
  284. package/dist/src/notes/store.js.map +1 -1
  285. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  286. package/dist/src/providers/index.js +2 -2
  287. package/dist/src/providers/model-registry.js +1 -1
  288. package/dist/src/session/config-store.d.ts +6 -75
  289. package/dist/src/session/config-store.js +38 -144
  290. package/dist/src/session/config-store.js.map +1 -1
  291. package/dist/src/session/config-types.d.ts +15 -0
  292. package/dist/src/session/config-types.js +1 -0
  293. package/dist/src/session/index.d.ts +1 -3
  294. package/dist/src/session/index.js +3 -5
  295. package/dist/src/session/init-session-turn.d.ts +0 -6
  296. package/dist/src/session/init-session-turn.js +18 -18
  297. package/dist/src/session/init-session-turn.js.map +1 -1
  298. package/dist/src/session/lifecycle-timestamps.d.ts +5 -2
  299. package/dist/src/session/lifecycle-timestamps.js.map +1 -1
  300. package/dist/src/session/{parity/load-jsonl-entries.js → load-jsonl-entries.js} +1 -1
  301. package/dist/src/session/load-jsonl-entries.js.map +1 -0
  302. package/dist/src/session/manager.d.ts +5 -3
  303. package/dist/src/session/manager.js +1 -5
  304. package/dist/src/session/manager.js.map +1 -1
  305. package/dist/src/session/resolve-session.d.ts +3 -6
  306. package/dist/src/session/resolve-session.js +26 -31
  307. package/dist/src/session/resolve-session.js.map +1 -1
  308. package/dist/src/session/session-context-for-llm.js +5 -1
  309. package/dist/src/session/session-context-for-llm.js.map +1 -1
  310. package/dist/src/session/session-id.js +12 -0
  311. package/dist/src/session/session-id.js.map +1 -0
  312. package/dist/src/session/session-title.js +2 -2
  313. package/dist/src/session/session-workspace.d.ts +1 -1
  314. package/dist/src/session/session-workspace.js.map +1 -1
  315. package/dist/src/session/store.d.ts +14 -63
  316. package/dist/src/session/store.js +172 -847
  317. package/dist/src/session/store.js.map +1 -1
  318. package/dist/src/session/stored-rows-to-file-entries.d.ts +11 -0
  319. package/dist/src/session/stored-rows-to-file-entries.js +95 -0
  320. package/dist/src/session/stored-rows-to-file-entries.js.map +1 -0
  321. package/dist/src/session/transcript-events.d.ts +1 -2
  322. package/dist/src/session/transcript-events.js +5 -12
  323. package/dist/src/session/transcript-events.js.map +1 -1
  324. package/dist/src/session/transcript-format.d.ts +1 -1
  325. package/dist/src/session/transcript-format.js.map +1 -1
  326. package/dist/src/session/transcript-stats.d.ts +1 -0
  327. package/dist/src/session/transcript-stats.js +10 -0
  328. package/dist/src/session/transcript-stats.js.map +1 -0
  329. package/dist/src/share/share-auto.js +2 -2
  330. package/dist/src/share/share-store.js +3 -3
  331. package/dist/src/share/share-thumbnail.js +2 -2
  332. package/dist/src/share/share-zip.js +1 -1
  333. package/dist/src/share/site-share-store.js +3 -3
  334. package/dist/src/share/site-static-serve.js +1 -1
  335. package/dist/src/storage/sqlite/config-repository.d.ts +6 -0
  336. package/dist/src/storage/sqlite/config-repository.js +56 -0
  337. package/dist/src/storage/sqlite/config-repository.js.map +1 -0
  338. package/dist/src/storage/sqlite/connection.d.ts +38 -0
  339. package/dist/src/storage/sqlite/connection.js +258 -0
  340. package/dist/src/storage/sqlite/connection.js.map +1 -0
  341. package/dist/src/storage/sqlite/cron-run-repository.d.ts +5 -0
  342. package/dist/src/storage/sqlite/cron-run-repository.js +97 -0
  343. package/dist/src/storage/sqlite/cron-run-repository.js.map +1 -0
  344. package/dist/src/storage/sqlite/fts.d.ts +2 -0
  345. package/dist/src/storage/sqlite/fts.js +11 -0
  346. package/dist/src/storage/sqlite/fts.js.map +1 -0
  347. package/dist/src/storage/sqlite/index.d.ts +12 -0
  348. package/dist/src/storage/sqlite/index.js +13 -0
  349. package/dist/src/storage/sqlite/memory-index-repository.d.ts +18 -0
  350. package/dist/src/storage/sqlite/memory-index-repository.js +132 -0
  351. package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -0
  352. package/dist/src/storage/sqlite/notes-repository.d.ts +11 -0
  353. package/dist/src/storage/sqlite/notes-repository.js +191 -0
  354. package/dist/src/storage/sqlite/notes-repository.js.map +1 -0
  355. package/dist/src/storage/sqlite/paths.d.ts +1 -0
  356. package/dist/src/storage/sqlite/paths.js +7 -0
  357. package/dist/src/storage/sqlite/paths.js.map +1 -0
  358. package/dist/src/storage/sqlite/row-mappers.d.ts +82 -0
  359. package/dist/src/storage/sqlite/row-mappers.js +164 -0
  360. package/dist/src/storage/sqlite/row-mappers.js.map +1 -0
  361. package/dist/src/storage/sqlite/schema.d.ts +5 -0
  362. package/dist/src/storage/sqlite/schema.js +43 -0
  363. package/dist/src/storage/sqlite/schema.js.map +1 -0
  364. package/dist/src/storage/sqlite/schema.sql +195 -0
  365. package/dist/src/storage/sqlite/session-metadata.d.ts +8 -0
  366. package/dist/src/storage/sqlite/session-metadata.js +83 -0
  367. package/dist/src/storage/sqlite/session-metadata.js.map +1 -0
  368. package/dist/src/storage/sqlite/session-repository.d.ts +29 -0
  369. package/dist/src/storage/sqlite/session-repository.js +268 -0
  370. package/dist/src/storage/sqlite/session-repository.js.map +1 -0
  371. package/dist/src/storage/sqlite/transaction.d.ts +11 -0
  372. package/dist/src/storage/sqlite/transaction.js +115 -0
  373. package/dist/src/storage/sqlite/transaction.js.map +1 -0
  374. package/dist/src/storage/sqlite/transcript-repository.d.ts +34 -0
  375. package/dist/src/storage/sqlite/transcript-repository.js +241 -0
  376. package/dist/src/storage/sqlite/transcript-repository.js.map +1 -0
  377. package/dist/src/tui/clipboard-image.js +3 -3
  378. package/dist/src/tui/theme-manager.js +1 -1
  379. package/dist/src/tui/tui-keybindings-file.js +1 -1
  380. package/dist/src/tui/tui-scoped-models.js +2 -2
  381. package/dist/src/tui/tui-settings.js +1 -1
  382. package/dist/src/tui/tui.js +3 -3
  383. package/dist/src/tunnel/frpc-binary.js +3 -3
  384. package/dist/src/tunnel/frpc-config.js +1 -1
  385. package/dist/src/tunnel/frpc-extract.js +1 -1
  386. package/dist/src/tunnel/tunnel-state.js +1 -1
  387. package/dist/src/utils/logger/audit.js +1 -1
  388. package/dist/src/utils/logger/log-store.js +1 -1
  389. package/dist/src/utils/logger/rotation.js +1 -1
  390. package/dist/src/voice/tts/audio.js +1 -1
  391. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  392. package/dist/src/workflows/service/workflow-session-bridge.js +41 -64
  393. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
  394. package/dist/src/workflows/store/event-store.js +1 -1
  395. package/dist/src/workflows/store/run-store.js +1 -1
  396. package/package.json +2 -2
  397. package/dist/gateway/static/root/assets/index-Bj_l8QDp.css +0 -1
  398. package/dist/gateway/static/root/assets/url-BHHmdJYc.js +0 -3
  399. package/dist/src/agent/context/workspace-templates/BOOTSTRAP.md +0 -61
  400. package/dist/src/agent/embedded/session-manager-cache.d.ts +0 -19
  401. package/dist/src/agent/embedded/session-manager-cache.js +0 -48
  402. package/dist/src/agent/embedded/session-manager-cache.js.map +0 -1
  403. package/dist/src/session/parity/artifacts.d.ts +0 -16
  404. package/dist/src/session/parity/artifacts.js +0 -80
  405. package/dist/src/session/parity/artifacts.js.map +0 -1
  406. package/dist/src/session/parity/jsonl-transcript-io.d.ts +0 -54
  407. package/dist/src/session/parity/jsonl-transcript-io.js +0 -236
  408. package/dist/src/session/parity/jsonl-transcript-io.js.map +0 -1
  409. package/dist/src/session/parity/load-jsonl-entries.js.map +0 -1
  410. package/dist/src/session/parity/session-id.js +0 -18
  411. package/dist/src/session/parity/session-id.js.map +0 -1
  412. package/dist/src/session/parity/sessions-json-cache.d.ts +0 -14
  413. package/dist/src/session/parity/sessions-json-cache.js +0 -98
  414. package/dist/src/session/parity/sessions-json-cache.js.map +0 -1
  415. package/dist/src/session/parity/sessions-json-file-read.d.ts +0 -6
  416. package/dist/src/session/parity/sessions-json-file-read.js +0 -19
  417. package/dist/src/session/parity/sessions-json-file-read.js.map +0 -1
  418. package/dist/src/session/parity/sessions-json-file.d.ts +0 -11
  419. package/dist/src/session/parity/sessions-json-file.js +0 -52
  420. package/dist/src/session/parity/sessions-json-file.js.map +0 -1
  421. package/dist/src/session/parity/sessions-json-patch.d.ts +0 -14
  422. package/dist/src/session/parity/sessions-json-patch.js +0 -40
  423. package/dist/src/session/parity/sessions-json-patch.js.map +0 -1
  424. package/dist/src/session/parity/transcript-file-lock.d.ts +0 -22
  425. package/dist/src/session/parity/transcript-file-lock.js +0 -142
  426. package/dist/src/session/parity/transcript-file-lock.js.map +0 -1
  427. package/dist/src/session/parity/transcript-pagination.d.ts +0 -29
  428. package/dist/src/session/parity/transcript-pagination.js +0 -132
  429. package/dist/src/session/parity/transcript-pagination.js.map +0 -1
  430. package/dist/src/session/parity/transcript-paths.d.ts +0 -13
  431. package/dist/src/session/parity/transcript-paths.js +0 -64
  432. package/dist/src/session/parity/transcript-paths.js.map +0 -1
  433. package/dist/src/session/parity/xopc-session-disk-entry.d.ts +0 -22
  434. package/dist/src/session/search-index-cache.d.ts +0 -6
  435. package/dist/src/session/search-index-cache.js +0 -44
  436. package/dist/src/session/search-index-cache.js.map +0 -1
  437. package/dist/src/session/search-index.d.ts +0 -20
  438. package/dist/src/session/search-index.js +0 -124
  439. package/dist/src/session/search-index.js.map +0 -1
  440. /package/dist/src/{session/parity/xopc-session-disk-entry.js → cron/execution-types.js} +0 -0
  441. /package/dist/src/session/{parity/load-jsonl-entries.d.ts → load-jsonl-entries.d.ts} +0 -0
  442. /package/dist/src/session/{parity/session-id.d.ts → session-id.d.ts} +0 -0
@@ -1,415 +1,85 @@
1
- import { resolveStateDir } from "../config/paths-state.js";
2
- import { init_agent_scope, listAgentEntries, resolveDefaultAgentId } from "../agent/agent-scope.js";
3
- import { init_session_key, parseSessionKey } from "../routing/session-key.js";
4
- import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
5
1
  import { createLogger } from "../utils/logger/index.js";
6
2
  import { init_logger } from "../utils/logger.js";
7
- import { init_artifacts, parseCompactionCheckpointTranscriptFileName } from "./parity/artifacts.js";
8
- import { init_session_id, validateSessionId } from "./parity/session-id.js";
9
- import { archiveFileOnDisk, init_transcript_paths, resolveSessionFilePath, resolveSessionTranscriptPathInDir } from "./parity/transcript-paths.js";
10
- import { FILENAMES, init_paths, resolveSessionsDir } from "../config/paths.js";
11
- import { loadEntriesFromFile } from "./parity/load-jsonl-entries.js";
12
- import { mergeLlmMessagesPreservingContextRows } from "./session-context-for-llm.js";
13
- import { appendPiTranscriptContextEntry, persistMergedTranscriptRows, readTranscriptRowsFromFile, rowsToLlmMessages, writeTranscriptJsonl } from "./parity/jsonl-transcript-io.js";
14
- import { invalidateSessionSearchIndexCache } from "./search-index-cache.js";
3
+ import { init_paths_state, resolveStateDir } from "../config/paths-state.js";
4
+ import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
5
+ import { requireXopcDatabase } from "../storage/sqlite/connection.js";
6
+ import "./types.js";
7
+ import { buildSessionContextForLlm, mergeLlmMessagesPreservingContextRows } from "./session-context-for-llm.js";
8
+ import { estimateTokensFromMessages } from "../storage/sqlite/row-mappers.js";
9
+ import { deleteSessionRecord, ensureSessionRecord, getGlobalSessionStats, getSessionMetadata, listSessionMetadata, listSessionsByAgent, patchSessionMetadata, resetSessionRecord } from "../storage/sqlite/session-repository.js";
10
+ import { appendTranscriptEntry, captureCompactionCheckpoint, getCompactionCheckpointDetail, listCompactionCheckpoints, loadCheckpointRows, loadLlmMessagesForSession, loadTranscriptRowsForSession, replaceTranscriptRows, restoreCompactionCheckpoint } from "../storage/sqlite/transcript-repository.js";
11
+ import "../storage/sqlite/index.js";
15
12
  import { readPostCompactionContext } from "../agent/reply/post-compaction-context.js";
16
13
  import { SessionCompactor } from "../agent/memory/compaction.js";
17
14
  import { SlidingWindow } from "../agent/memory/window.js";
18
15
  import { normalizeCompactionCheckpointId } from "./compaction-checkpoints.js";
19
- import "./types.js";
20
- import { readSessionsJsonFile, withSessionsJsonLock } from "./parity/sessions-json-file.js";
21
- import { buildSessionsJsonStatsPatch, incrementSessionsJsonStatsForAppend, isAppendOnlyLlmTranscriptMessage, patchSessionsJsonEntryStats } from "./parity/sessions-json-patch.js";
22
- import { countTranscriptMessageRows, readDisplayMessagePageFromTranscriptFile } from "./parity/transcript-pagination.js";
23
- import { join } from "path";
24
- import { existsSync } from "fs";
25
- import { copyFile, mkdir, readdir, stat, unlink } from "fs/promises";
26
- import { randomUUID } from "node:crypto";
27
- import { performance } from "node:perf_hooks";
16
+ import { isAppendOnlyLlmTranscriptMessage } from "./transcript-stats.js";
28
17
  //#region src/session/store.ts
29
- init_paths();
30
- init_agent_scope();
31
- init_session_key();
18
+ init_paths_state();
32
19
  init_logger();
33
- init_artifacts();
34
- init_transcript_paths();
35
- init_session_id();
36
20
  const log = createLogger("SessionStore");
37
21
  const INDEX_VERSION = "1.0";
38
- const DELETED_MARKER = ".jsonl.deleted.";
39
- const ALL_SESSIONS_MAP_CACHE_TTL_MS = 2e3;
40
22
  var SessionStore = class {
41
- sessionsDir;
42
- archiveDir;
43
- storePath;
44
23
  window;
45
24
  compactor;
46
- storeMutationDepth = 0;
47
- storeMutationChain = Promise.resolve();
48
- allSessionsMapCache;
49
- /** Cache of per-agent sessions dirs to avoid re-resolution on every call. */
50
- agentSessionsDirCache = /* @__PURE__ */ new Map();
51
25
  constructor(options, windowConfig, compactionConfig) {
52
26
  this.options = options;
53
- const agentId = options.agentId ?? resolveDefaultAgentId(options.config);
54
- this.sessionsDir = options.sessionsDir ?? resolveSessionsDir(options.config, agentId);
55
- this.archiveDir = join(this.sessionsDir, "archive");
56
- this.storePath = join(this.sessionsDir, FILENAMES.SESSIONS_MAP);
57
27
  this.window = new SlidingWindow(windowConfig);
58
28
  this.compactor = new SessionCompactor(compactionConfig);
59
29
  }
60
- getSessionsRoot() {
61
- return this.sessionsDir;
62
- }
63
- /**
64
- * OpenClaw-aligned: resolve the sessions directory for a given session key.
65
- * Extracts agentId from the session key and routes to `agents/<agentId>/sessions/`.
66
- * Falls back to the default sessions directory when agentId cannot be parsed
67
- * or when `sessionsDir` was explicitly provided in options.
68
- */
69
- resolveSessionsDirForKey(sessionKey) {
70
- if (this.options.sessionsDir) return this.sessionsDir;
71
- const parsed = parseSessionKey(sessionKey);
72
- if (!parsed) return this.sessionsDir;
73
- const agentId = parsed.agentId;
74
- const cached = this.agentSessionsDirCache.get(agentId);
75
- if (cached) return cached;
76
- const resolved = resolveSessionsDir(this.options.config, agentId);
77
- this.agentSessionsDirCache.set(agentId, resolved);
78
- return resolved;
79
- }
80
- resolveStorePathForKey(sessionKey) {
81
- return join(this.resolveSessionsDirForKey(sessionKey), FILENAMES.SESSIONS_MAP);
30
+ resolveWorkspaceCwd(sessionKey) {
31
+ return resolveEffectiveAgentProfileForSession(this.options.config, sessionKey).resolvedWorkspacePath;
82
32
  }
83
33
  async runStoreMutation(fn) {
84
- if (this.storeMutationDepth > 0) return fn();
85
- const run = this.storeMutationChain.then(async () => {
86
- this.storeMutationDepth++;
87
- try {
88
- return await fn();
89
- } finally {
90
- this.storeMutationDepth--;
91
- }
92
- });
93
- this.storeMutationChain = run.then(() => void 0).catch(() => void 0);
94
- return run;
34
+ return fn();
95
35
  }
96
36
  async initialize() {
97
- await mkdir(this.sessionsDir, { recursive: true });
98
- await mkdir(this.archiveDir, { recursive: true });
99
- if (!existsSync(this.storePath)) await withSessionsJsonLock(this.storePath, async () => void 0);
100
- log.debug("Session store initialized (sessions.json + JSONL)");
101
- }
102
- transcriptPathForEntry(entry, sessionsDir) {
103
- return resolveSessionFilePath(entry.sessionId, entry, { sessionsDir: sessionsDir ?? this.sessionsDir });
104
- }
105
- async readMapForKey(sessionKey) {
106
- return readSessionsJsonFile(this.resolveStorePathForKey(sessionKey));
107
- }
108
- async readMap() {
109
- return readSessionsJsonFile(this.storePath);
110
- }
111
- invalidateAllSessionsMapCache() {
112
- this.allSessionsMapCache = void 0;
113
- }
114
- async discoverSessionMapPaths() {
115
- const agents = listAgentEntries(this.options.config);
116
- const defaultId = resolveDefaultAgentId(this.options.config);
117
- const agentIds = new Set([defaultId, ...agents.map((agent) => agent.id)]);
118
- const agentsRoot = join(resolveStateDir(process.env), "agents");
119
- if (existsSync(agentsRoot)) {
120
- const entries = await readdir(agentsRoot, { withFileTypes: true }).catch(() => []);
121
- for (const entry of entries) if (entry.isDirectory()) agentIds.add(entry.name);
122
- }
123
- return [...agentIds].map((agentId) => ({
124
- agentId,
125
- mapPath: join(resolveSessionsDir(this.options.config, agentId), FILENAMES.SESSIONS_MAP)
126
- }));
127
- }
128
- /**
129
- * Unified cross-agent aggregation entry for global session views.
130
- * Reads configured agents plus existing per-agent session maps under the state directory.
131
- */
132
- async readAllMaps() {
133
- if (this.options.sessionsDir) return this.readMap();
134
- const nowMs = Date.now();
135
- if (this.allSessionsMapCache && this.allSessionsMapCache.expiresAtMs > nowMs) {
136
- log.debug({ sessionCount: Object.keys(this.allSessionsMapCache.map).length }, "All session maps cache hit");
137
- return this.allSessionsMapCache.map;
138
- }
139
- const startedAt = performance.now();
140
- const paths = await this.discoverSessionMapPaths();
141
- const merged = {};
142
- let scannedMapCount = 0;
143
- for (const { mapPath } of paths) {
144
- if (!existsSync(mapPath)) continue;
145
- const map = await readSessionsJsonFile(mapPath);
146
- Object.assign(merged, map);
147
- scannedMapCount++;
148
- }
149
- this.allSessionsMapCache = {
150
- expiresAtMs: nowMs + ALL_SESSIONS_MAP_CACHE_TTL_MS,
151
- map: merged
152
- };
153
- log.debug({
154
- candidateAgentCount: paths.length,
155
- scannedMapCount,
156
- sessionCount: Object.keys(merged).length,
157
- durationMs: Math.round(performance.now() - startedAt)
158
- }, "All session maps scanned");
159
- return merged;
160
- }
161
- async getDiskEntry(sessionKey) {
162
- return (await this.readMapForKey(sessionKey))[sessionKey];
163
- }
164
- buildDefaultMetadata(key) {
165
- const { channel, chatId } = this.parseSessionKey(key);
166
- const routing = this.extractRoutingFromKey(key);
167
- const isCronSession = channel === "cron";
168
- const isHeartbeatSession = channel === "heartbeat";
169
- const now = (/* @__PURE__ */ new Date()).toISOString();
170
- return {
171
- key,
172
- status: "active",
173
- tags: [],
174
- createdAt: now,
175
- updatedAt: now,
176
- lastAccessedAt: now,
177
- messageCount: 0,
178
- estimatedTokens: 0,
179
- compactedCount: 0,
180
- sourceChannel: channel,
181
- sourceChatId: chatId,
182
- routing,
183
- ...isCronSession ? {
184
- sessionType: "cron",
185
- customData: { cronJobId: chatId }
186
- } : {},
187
- ...isHeartbeatSession ? {
188
- sessionType: "heartbeat",
189
- customData: { heartbeatTarget: chatId }
190
- } : {},
191
- stats: {
192
- messageCount: 0,
193
- tokenCount: 0
194
- }
195
- };
37
+ requireXopcDatabase();
38
+ log.debug("Session store initialized (SQLite)");
196
39
  }
197
- parseSessionKey(key) {
198
- const parts = key.split(":");
199
- if (parts.length >= 2 && parts[0] === "heartbeat") return {
200
- channel: "heartbeat",
201
- chatId: parts.slice(1).join(":")
202
- };
203
- const parsed = parseSessionKey(key);
204
- if (parsed) {
205
- if (parsed.source === "cron") return {
206
- channel: "cron",
207
- chatId: parsed.peerId
208
- };
209
- return {
210
- channel: parsed.source,
211
- chatId: [
212
- parsed.accountId,
213
- parsed.peerKind,
214
- parsed.peerId
215
- ].join(":")
216
- };
217
- }
218
- return {
219
- channel: "unknown",
220
- chatId: key
221
- };
222
- }
223
- extractRoutingFromKey(key) {
224
- const parsed = parseSessionKey(key);
225
- if (!parsed) return;
226
- return {
227
- agentId: parsed.agentId?.toLowerCase() || "main",
228
- source: parsed.source?.toLowerCase() || "unknown",
229
- accountId: parsed.accountId?.toLowerCase() || "default",
230
- peerKind: parsed.peerKind?.toLowerCase() || "dm",
231
- peerId: parsed.peerId?.toLowerCase() || "unknown",
232
- threadId: parsed.threadId,
233
- scopeId: parsed.scopeId
234
- };
40
+ getSessionsRoot() {
41
+ return resolveStateDir();
235
42
  }
236
- /** Resolve on-disk transcript path; creates session row + empty JSONL when missing. */
237
43
  async resolveTranscriptPath(sessionKey) {
238
- const entry = await this.ensureSession(sessionKey);
239
- const sessionsDir = this.resolveSessionsDirForKey(sessionKey);
240
- const absPath = this.transcriptPathForEntry(entry, sessionsDir);
44
+ requireXopcDatabase();
241
45
  return {
242
- sessionId: entry.sessionId,
243
- absPath,
244
- sessionsDir
46
+ sessionId: ensureSessionRecord(sessionKey, this.resolveWorkspaceCwd(sessionKey)).transcriptId,
47
+ sessionKey
245
48
  };
246
49
  }
247
- /** Ensure sessions.json has an entry and transcript file exist for `sessionKey`. */
248
- async ensureSession(sessionKey) {
249
- const keyStorePath = this.resolveStorePathForKey(sessionKey);
250
- const keySessionsDir = this.resolveSessionsDirForKey(sessionKey);
251
- await mkdir(keySessionsDir, { recursive: true });
252
- let changed = false;
253
- const entry = await withSessionsJsonLock(keyStorePath, async (map) => {
254
- const existing = map[sessionKey];
255
- if (existing?.pluginExtensions?.xopc?.metadata) return existing;
256
- let nextEntry = existing;
257
- if (!nextEntry) {
258
- const sessionId = randomUUID();
259
- validateSessionId(sessionId);
260
- const sessionFile = `${sessionId}.jsonl`;
261
- const now = Date.now();
262
- const metadata = this.buildDefaultMetadata(sessionKey);
263
- metadata.transcriptId = sessionId;
264
- nextEntry = {
265
- sessionId,
266
- updatedAt: now,
267
- sessionStartedAt: now,
268
- sessionFile,
269
- pluginExtensions: { xopc: { metadata } }
270
- };
271
- map[sessionKey] = nextEntry;
272
- await writeTranscriptJsonl({
273
- absPath: resolveSessionTranscriptPathInDir(sessionId, keySessionsDir),
274
- sessionId,
275
- cwd: process.cwd(),
276
- rows: []
277
- });
278
- changed = true;
279
- } else if (!nextEntry.pluginExtensions?.xopc?.metadata) {
280
- const metadata = this.buildDefaultMetadata(sessionKey);
281
- metadata.transcriptId = nextEntry.sessionId;
282
- nextEntry.pluginExtensions = { xopc: { metadata } };
283
- map[sessionKey] = nextEntry;
284
- changed = true;
285
- }
286
- return nextEntry;
50
+ async appendTranscriptMessage(sessionKey, message) {
51
+ return this.runStoreMutation(async () => {
52
+ requireXopcDatabase();
53
+ ensureSessionRecord(sessionKey, this.resolveWorkspaceCwd(sessionKey));
54
+ appendTranscriptEntry(sessionKey, message);
287
55
  });
288
- if (changed) this.invalidateAllSessionsMapCache();
289
- return entry;
290
- }
291
- metadataFromEntry(sessionKey, entry) {
292
- const base = entry.pluginExtensions?.xopc?.metadata ?? this.buildDefaultMetadata(sessionKey);
293
- const { channel: keySource, chatId: keyChatId } = this.parseSessionKey(sessionKey);
294
- const diskSc = typeof base.sourceChannel === "string" ? base.sourceChannel.trim() : "";
295
- const diskChat = typeof base.sourceChatId === "string" ? base.sourceChatId.trim() : "";
296
- return {
297
- ...base,
298
- key: sessionKey,
299
- transcriptId: entry.sessionId,
300
- sourceChannel: diskSc || keySource,
301
- sourceChatId: diskChat || keyChatId
302
- };
303
56
  }
304
57
  async getByAgent(agentId) {
305
- const map = await this.readAllMaps();
306
- const out = [];
307
- for (const [key, e] of Object.entries(map)) {
308
- const m = this.metadataFromEntry(key, e);
309
- if (m.routing?.agentId?.toLowerCase() === agentId.toLowerCase()) out.push(m);
310
- }
311
- return out;
58
+ requireXopcDatabase();
59
+ return listSessionsByAgent(agentId);
312
60
  }
313
61
  async getByAccount(accountId) {
314
- const map = await this.readAllMaps();
315
- const out = [];
316
- for (const [key, e] of Object.entries(map)) {
317
- const m = this.metadataFromEntry(key, e);
318
- if (m.routing?.accountId === accountId) out.push(m);
319
- }
320
- return out;
62
+ const { items } = await this.list({ limit: 1e5 });
63
+ return items.filter((m) => m.routing?.accountId === accountId);
321
64
  }
322
65
  async getByPeer(peerKind, peerId) {
323
- const map = await this.readAllMaps();
324
- const out = [];
325
- for (const [key, e] of Object.entries(map)) {
326
- const m = this.metadataFromEntry(key, e);
327
- if (m.routing?.peerKind === peerKind && m.routing.peerId === peerId) out.push(m);
328
- }
329
- return out;
66
+ const { items } = await this.list({ limit: 1e5 });
67
+ return items.filter((m) => m.routing?.peerKind === peerKind && m.routing.peerId === peerId);
330
68
  }
331
69
  async getMainSession(channel, accountId) {
332
- const map = await this.readAllMaps();
333
- for (const [key, e] of Object.entries(map)) {
334
- const m = this.metadataFromEntry(key, e);
335
- if (m.routing?.source === channel && m.routing.accountId === accountId && m.routing.peerKind === "dm" && m.routing.peerId === "main") return m;
336
- }
337
- return null;
70
+ const { items } = await this.list({ limit: 1e5 });
71
+ return items.find((m) => m.routing?.source === channel && m.routing.accountId === accountId && m.routing.peerKind === "dm" && m.routing.peerId === "main") ?? null;
338
72
  }
339
73
  async refreshIndex() {}
340
74
  async list(query = {}) {
341
- const map = await this.readAllMaps();
342
- let sessions = Object.entries(map).map(([k, e]) => this.metadataFromEntry(k, e));
343
- if (query.status) {
344
- const statuses = Array.isArray(query.status) ? query.status : [query.status];
345
- sessions = sessions.filter((s) => statuses.includes(s.status));
346
- }
347
- if (query.channel) {
348
- const rawChannels = query.channel.split(",").map((c) => c.trim().toLowerCase()).filter(Boolean);
349
- /**
350
- * `ui` is a legacy console source; treat as webchat when filtering web sessions.
351
- * `webui` matches slash-command normalization to `gateway` (see `chat-commands/session-key.ts`).
352
- */
353
- const channels = [...new Set(rawChannels.flatMap((c) => {
354
- if (c === "webchat") return ["webchat", "ui"];
355
- if (c === "gateway") return ["gateway", "webui"];
356
- return [c];
357
- }))];
358
- if (channels.length === 0) sessions = [];
359
- else if (channels.length === 1) {
360
- const ch = channels[0];
361
- sessions = sessions.filter((s) => (s.sourceChannel ?? "").toLowerCase() === ch);
362
- } else sessions = sessions.filter((s) => channels.includes((s.sourceChannel ?? "").toLowerCase()));
363
- }
364
- if (query.tags?.length) sessions = sessions.filter((s) => query.tags.some((t) => s.tags.includes(t)));
365
- if (query.search) {
366
- const q = query.search.toLowerCase();
367
- const metadataMatches = sessions.filter((session) => this.sessionMetadataMatchesSearch(session, q));
368
- const metadataMatchedKeys = new Set(metadataMatches.map((session) => session.key));
369
- const contentMatches = [];
370
- const candidates = sessions.filter((session) => !metadataMatchedKeys.has(session.key));
371
- for (const candidate of candidates) if (await this.sessionContentMatchesSearch(candidate.key, q)) contentMatches.push(candidate);
372
- sessions = [...metadataMatches, ...contentMatches];
373
- }
374
- const sortBy = query.sortBy || "updatedAt";
375
- const sortOrder = query.sortOrder || "desc";
376
- sessions.sort((a, b) => {
377
- const av = a[sortBy];
378
- const bv = b[sortBy];
379
- const c = av < bv ? -1 : av > bv ? 1 : 0;
380
- return sortOrder === "asc" ? c : -c;
381
- });
382
- const total = sessions.length;
383
- const limit = query.limit || 50;
384
- const offset = query.offset || 0;
385
- return {
386
- items: sessions.slice(offset, offset + limit),
387
- total,
388
- limit,
389
- offset,
390
- hasMore: offset + limit < total
391
- };
392
- }
393
- sessionMetadataMatchesSearch(session, query) {
394
- return Boolean(session.key.toLowerCase().includes(query) || session.name?.toLowerCase().includes(query) || session.sourceChannel.toLowerCase().includes(query) || session.sourceChatId.toLowerCase().includes(query) || session.tags.some((tag) => tag.toLowerCase().includes(query)));
395
- }
396
- async sessionContentMatchesSearch(sessionKey, query) {
397
- return (await this.loadDisplayMessages(sessionKey)).some((message) => {
398
- if (this.extractTextContent(this.messageContent(message)).toLowerCase().includes(query)) return true;
399
- const attachments = message.attachments;
400
- if (!Array.isArray(attachments)) return false;
401
- return attachments.some((attachment) => {
402
- if (!attachment || typeof attachment !== "object") return false;
403
- const name = attachment.name;
404
- return typeof name === "string" && name.toLowerCase().includes(query);
405
- });
406
- });
75
+ requireXopcDatabase();
76
+ return listSessionMetadata(query);
407
77
  }
408
78
  async get(key, options) {
409
79
  const metadata = await this.getMetadata(key);
410
80
  if (!metadata) return null;
411
81
  const messages = await this.loadDisplayMessages(key);
412
- return await this.buildSessionDetail(key, metadata, messages, options);
82
+ return this.buildSessionDetail(key, metadata, messages, options);
413
83
  }
414
84
  async getMessagePage(key, options = {}) {
415
85
  const metadata = await this.getMetadata(key);
@@ -418,44 +88,22 @@ var SessionStore = class {
418
88
  const offset = Math.max(0, Math.trunc(options.offset ?? 0));
419
89
  const parsedBefore = options.before ? Number.parseInt(options.before, 10) : void 0;
420
90
  const hasBeforeCursor = parsedBefore !== void 0 && Number.isFinite(parsedBefore);
421
- if ((await this.listCompactionCheckpoints(key)).length === 0) {
422
- const entry = await this.getDiskEntry(key);
423
- if (!entry) return null;
424
- const keySessionsDir = this.resolveSessionsDirForKey(key);
425
- const page = await readDisplayMessagePageFromTranscriptFile(this.transcriptPathForEntry(entry, keySessionsDir), {
426
- limit,
427
- offset: hasBeforeCursor ? void 0 : offset,
428
- beforeIndex: hasBeforeCursor ? parsedBefore : void 0
429
- });
430
- const session = await this.buildSessionDetail(key, metadata, page.messages, options);
431
- const nextBeforeCursor = page.startIndex > 0 ? String(page.startIndex) : void 0;
432
- return {
433
- session,
434
- pagination: {
435
- total: page.total,
436
- limit,
437
- offset,
438
- hasMore: page.startIndex > 0,
439
- ...hasBeforeCursor ? { before: String(page.endIndex) } : {},
440
- ...nextBeforeCursor ? { nextBeforeCursor } : {}
441
- }
442
- };
443
- }
444
- const messages = await this.loadDisplayMessages(key);
445
- const total = messages.length;
446
- const endExclusive = hasBeforeCursor ? Math.min(total, Math.max(0, Math.trunc(parsedBefore))) : Math.max(0, total - offset);
447
- const startInclusive = Math.max(0, endExclusive - limit);
448
- const pageMessages = messages.slice(startInclusive, endExclusive);
449
- const session = await this.buildSessionDetail(key, metadata, pageMessages, options);
450
- const nextBeforeCursor = startInclusive > 0 ? String(startInclusive) : void 0;
91
+ const displayMessages = await this.loadDisplayMessages(key);
92
+ const page = this.paginateDisplayMessages(displayMessages, {
93
+ limit,
94
+ offset: hasBeforeCursor ? void 0 : offset,
95
+ beforeEndIndex: hasBeforeCursor ? parsedBefore : void 0
96
+ });
97
+ const session = await this.buildSessionDetail(key, metadata, page.messages, options);
98
+ const nextBeforeCursor = page.startIndex > 0 ? String(page.startIndex) : void 0;
451
99
  return {
452
100
  session,
453
101
  pagination: {
454
- total,
102
+ total: page.total,
455
103
  limit,
456
104
  offset,
457
- hasMore: startInclusive > 0,
458
- ...hasBeforeCursor ? { before: String(endExclusive) } : {},
105
+ hasMore: hasBeforeCursor ? page.startIndex > 0 : offset + limit < page.total,
106
+ ...hasBeforeCursor ? { before: String(page.endIndex) } : {},
459
107
  ...nextBeforeCursor ? { nextBeforeCursor } : {}
460
108
  }
461
109
  };
@@ -482,114 +130,40 @@ var SessionStore = class {
482
130
  };
483
131
  }
484
132
  async loadTranscriptRows(key) {
485
- const entry = await this.getDiskEntry(key);
486
- if (!entry) return [];
487
- return readTranscriptRowsFromFile(this.transcriptPathForEntry(entry, this.resolveSessionsDirForKey(key)));
133
+ requireXopcDatabase();
134
+ return loadTranscriptRowsForSession(key);
488
135
  }
489
136
  async getMetadata(key) {
490
- const entry = await this.getDiskEntry(key);
491
- if (!entry) return null;
492
- return this.metadataFromEntry(key, entry);
137
+ requireXopcDatabase();
138
+ return getSessionMetadata(key);
493
139
  }
494
140
  async updateMetadata(key, updates) {
495
141
  return this.runStoreMutation(async () => {
496
- await withSessionsJsonLock(this.resolveStorePathForKey(key), async (map) => {
497
- const entry = map[key];
498
- if (!entry?.pluginExtensions?.xopc?.metadata) throw new Error(`Session not found: ${key}`);
499
- const meta = {
500
- ...entry.pluginExtensions.xopc.metadata,
501
- ...updates,
502
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
503
- };
504
- entry.pluginExtensions.xopc.metadata = meta;
505
- entry.updatedAt = Date.now();
506
- map[key] = entry;
507
- });
508
- this.invalidateAllSessionsMapCache();
509
- invalidateSessionSearchIndexCache();
142
+ requireXopcDatabase();
143
+ patchSessionMetadata(key, updates);
510
144
  log.debug({
511
145
  key,
512
146
  updates
513
147
  }, "Session metadata updated");
514
148
  });
515
149
  }
516
- /**
517
- * Reset transcript for an existing session key: archive the current JSONL as
518
- * `*.reset.*`, assign a new `sessionId`, and preserve per-session overrides
519
- * on the disk entry (thinking/verbose) and in `sessions/config/*.json`.
520
- */
521
150
  async reset(key) {
522
151
  return this.runStoreMutation(async () => {
523
- const existing = await this.getDiskEntry(key);
524
- if (!existing?.pluginExtensions?.xopc?.metadata) return null;
525
- const previousSessionId = existing.sessionId;
526
- const keySessionsDir = this.resolveSessionsDirForKey(key);
527
- const abs = this.transcriptPathForEntry(existing, keySessionsDir);
528
- if (existsSync(abs)) try {
529
- archiveFileOnDisk(abs, "reset");
530
- } catch (err) {
531
- log.warn({
532
- err,
533
- key
534
- }, "Transcript archive on reset failed");
535
- }
536
- const sessionId = randomUUID();
537
- validateSessionId(sessionId);
538
- const now = Date.now();
539
- await writeTranscriptJsonl({
540
- absPath: resolveSessionTranscriptPathInDir(sessionId, keySessionsDir),
541
- sessionId,
542
- cwd: process.cwd(),
543
- rows: []
544
- });
545
- await withSessionsJsonLock(this.resolveStorePathForKey(key), async (map) => {
546
- const e = map[key];
547
- if (!e?.pluginExtensions?.xopc?.metadata) return;
548
- e.sessionId = sessionId;
549
- e.sessionFile = `${sessionId}.jsonl`;
550
- e.updatedAt = now;
551
- e.sessionStartedAt = now;
552
- e.lastInteractionAt = void 0;
553
- patchSessionsJsonEntryStats(e, buildSessionsJsonStatsPatch(0, 0));
554
- const meta = e.pluginExtensions.xopc.metadata;
555
- meta.transcriptId = sessionId;
556
- meta.updatedAt = new Date(now).toISOString();
557
- map[key] = e;
558
- });
559
- this.invalidateAllSessionsMapCache();
560
- invalidateSessionSearchIndexCache();
561
- log.info({
152
+ requireXopcDatabase();
153
+ const outcome = resetSessionRecord(key, this.resolveWorkspaceCwd(key));
154
+ if (outcome) log.info({
562
155
  key,
563
- previousSessionId,
564
- sessionId
156
+ ...outcome
565
157
  }, "Session reset");
566
- return {
567
- sessionId,
568
- previousSessionId
569
- };
158
+ return outcome;
570
159
  });
571
160
  }
572
161
  async delete(key) {
573
162
  return this.runStoreMutation(async () => {
574
- const entry = await this.getDiskEntry(key);
575
- if (!entry) return false;
576
- const keySessionsDir = this.resolveSessionsDirForKey(key);
577
- const abs = this.transcriptPathForEntry(entry, keySessionsDir);
578
- await withSessionsJsonLock(this.resolveStorePathForKey(key), async (map) => {
579
- delete map[key];
580
- });
581
- try {
582
- if (existsSync(abs)) archiveFileOnDisk(abs, "deleted");
583
- } catch (err) {
584
- log.warn({
585
- err,
586
- key
587
- }, "Transcript archive on delete failed");
588
- }
589
- this.invalidateAllSessionsMapCache();
590
- invalidateSessionSearchIndexCache();
591
- log.info({ key }, "Session deleted");
592
- return true;
163
+ requireXopcDatabase();
164
+ const ok = deleteSessionRecord(key);
165
+ if (ok) log.info({ key }, "Session deleted");
166
+ return ok;
593
167
  });
594
168
  }
595
169
  async deleteMany(keys) {
@@ -608,8 +182,6 @@ var SessionStore = class {
608
182
  }
609
183
  async setStatus(key, status) {
610
184
  await this.updateMetadata(key, { status });
611
- if (status === "archived") await this.moveToArchive(key);
612
- else await this.moveFromArchive(key);
613
185
  }
614
186
  async archive(key) {
615
187
  await this.setStatus(key, "archived");
@@ -623,200 +195,62 @@ var SessionStore = class {
623
195
  async unpin(key) {
624
196
  await this.setStatus(key, "active");
625
197
  }
626
- async moveToArchive(key) {
627
- const entry = await this.getDiskEntry(key);
628
- if (!entry) return;
629
- const keySessionsDir = this.resolveSessionsDirForKey(key);
630
- const abs = this.transcriptPathForEntry(entry, keySessionsDir);
631
- if (existsSync(abs)) try {
632
- archiveFileOnDisk(abs, "deleted");
633
- } catch (err) {
634
- log.warn({
635
- err,
636
- key
637
- }, "Archive transcript rename failed");
638
- }
639
- }
640
- async findMostRecentDeletedTranscript(sessionId, sessionsDir) {
641
- let names;
642
- try {
643
- names = await readdir(sessionsDir);
644
- } catch {
645
- return null;
646
- }
647
- const prefix = `${sessionId}${DELETED_MARKER}`;
648
- const hits = names.filter((n) => n.startsWith(prefix) && n.endsWith("Z"));
649
- hits.sort().reverse();
650
- const first = hits[0];
651
- return first ? join(sessionsDir, first) : null;
652
- }
653
- async moveFromArchive(key) {
654
- const entry = await this.getDiskEntry(key);
655
- if (!entry) return;
656
- const keySessionsDir = this.resolveSessionsDirForKey(key);
657
- const target = resolveSessionTranscriptPathInDir(entry.sessionId, keySessionsDir);
658
- if (existsSync(target)) return;
659
- const src = await this.findMostRecentDeletedTranscript(entry.sessionId, keySessionsDir);
660
- if (!src) return;
661
- try {
662
- const { rename } = await import("fs/promises");
663
- await rename(src, target);
664
- } catch (err) {
665
- log.warn({
666
- err,
667
- key,
668
- src,
669
- target
670
- }, "Unarchive transcript rename failed");
671
- }
672
- }
673
- async loadMessages(key, options) {
674
- const entry = await this.getDiskEntry(key);
675
- if (!entry) return [];
676
- const keySessionsDir = this.resolveSessionsDirForKey(key);
677
- const primary = this.transcriptPathForEntry(entry, keySessionsDir);
678
- if (existsSync(primary)) return rowsToLlmMessages(await readTranscriptRowsFromFile(primary));
679
- if (options?.fromArchive) {
680
- const archived = await this.findMostRecentDeletedTranscript(entry.sessionId, keySessionsDir);
681
- if (!archived) return [];
682
- return rowsToLlmMessages(await readTranscriptRowsFromFile(archived));
683
- }
684
- return [];
685
- }
686
- async loadDisplayMessages(key) {
687
- const entry = await this.getDiskEntry(key);
688
- if (!entry) return [];
689
- const keySessionsDir = this.resolveSessionsDirForKey(key);
690
- const primary = this.transcriptPathForEntry(entry, keySessionsDir);
691
- const transcriptPaths = [];
692
- const checkpoints = await this.listCompactionCheckpoints(key);
693
- for (const checkpoint of [...checkpoints].reverse()) transcriptPaths.push(join(keySessionsDir, `${this.checkpointBasename(entry.sessionId)}${checkpoint.id}.jsonl`));
694
- transcriptPaths.push(primary);
695
- const messages = [];
696
- const seenMessages = /* @__PURE__ */ new Set();
697
- for (const transcriptPath of transcriptPaths) {
698
- if (!existsSync(transcriptPath)) continue;
699
- const rows = await readTranscriptRowsFromFile(transcriptPath);
700
- for (const message of rowsToLlmMessages(rows)) {
701
- if (this.isCompactionSummaryMessage(message)) continue;
702
- const key = this.displayMessageIdentity(message);
703
- if (seenMessages.has(key)) continue;
704
- seenMessages.add(key);
705
- messages.push(message);
706
- }
707
- }
708
- return messages;
198
+ async loadMessages(_key, _options) {
199
+ requireXopcDatabase();
200
+ return loadLlmMessagesForSession(_key);
709
201
  }
710
202
  async loadTranscriptDocument(key) {
711
- const entry = await this.getDiskEntry(key);
712
- if (!entry) return null;
713
- const path = this.transcriptPathForEntry(entry, this.resolveSessionsDirForKey(key));
714
- if (!existsSync(path)) return null;
715
- const entries = loadEntriesFromFile(path);
716
- const header = entries.find((e) => e.type === "session");
717
- if (!header || typeof header.id !== "string") return null;
718
- const sessionHeader = header;
719
- const rows = await readTranscriptRowsFromFile(path);
720
- const compactions = entries.filter((e) => e.type === "compaction").map((c) => ({
721
- at: c.timestamp,
722
- summary: c.summary,
723
- firstKeptIndex: Number.parseInt(String(c.firstKeptEntryId), 10) || 0,
724
- tokensBefore: c.tokensBefore,
725
- tokensAfter: typeof c.details === "object" && c.details && "tokensAfter" in c.details && typeof c.details.tokensAfter === "number" ? c.details.tokensAfter : 0
726
- }));
203
+ const metadata = await this.getMetadata(key);
204
+ if (!metadata?.transcriptId) return null;
205
+ const rows = await this.loadTranscriptRows(key);
206
+ const compactions = [];
207
+ for (const row of rows) if (row.type === "compaction") {
208
+ const c = row;
209
+ compactions.push({
210
+ at: c.at ?? (/* @__PURE__ */ new Date()).toISOString(),
211
+ summary: c.summary ?? "",
212
+ firstKeptIndex: Number(c.firstKeptIndex ?? 0),
213
+ tokensBefore: c.tokensBefore ?? 0,
214
+ tokensAfter: c.tokensAfter ?? 0
215
+ });
216
+ }
727
217
  return {
728
218
  type: "xopc_session_transcript",
729
219
  version: 1,
730
- id: sessionHeader.id,
731
- createdAt: sessionHeader.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
732
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
220
+ id: metadata.transcriptId,
221
+ createdAt: metadata.createdAt,
222
+ updatedAt: metadata.updatedAt,
733
223
  messages: rows,
734
224
  ...compactions.length > 0 ? { compactions } : {}
735
225
  };
736
226
  }
737
- async writeTranscriptAndSyncIndex(key, rows, opts) {
738
- const entry = await this.ensureSession(key);
739
- const keySessionsDir = this.resolveSessionsDirForKey(key);
740
- const abs = this.transcriptPathForEntry(entry, keySessionsDir);
741
- const llm = rowsToLlmMessages(rows);
742
- await persistMergedTranscriptRows({
743
- absPath: abs,
744
- sessionId: entry.sessionId,
745
- cwd: process.cwd(),
746
- rows,
747
- appendCompaction: opts?.appendCompaction
748
- });
749
- await withSessionsJsonLock(this.resolveStorePathForKey(key), async (map) => {
750
- const e = map[key];
751
- if (!e?.pluginExtensions?.xopc?.metadata) return;
752
- patchSessionsJsonEntryStats(e, buildSessionsJsonStatsPatch(llm.length, this.estimateTokens(llm)));
753
- map[key] = e;
754
- });
755
- this.invalidateAllSessionsMapCache();
756
- invalidateSessionSearchIndexCache();
757
- }
758
- /** Incremental sessions.json stats after guard append (OpenClaw transcript-events). */
759
- async syncSessionsJsonFromTranscriptUpdate(update) {
227
+ async syncEmbeddedTranscriptUpdate(update) {
760
228
  const sessionKey = update.sessionKey?.trim();
761
- if (!sessionKey || !existsSync(update.sessionFile)) return;
229
+ if (!sessionKey) return;
762
230
  return this.runStoreMutation(async () => {
763
- await withSessionsJsonLock(this.resolveStorePathForKey(sessionKey), async (map) => {
764
- const e = map[sessionKey];
765
- if (!e?.pluginExtensions?.xopc?.metadata) return;
766
- if (update.message && isAppendOnlyLlmTranscriptMessage(update.message)) {
767
- incrementSessionsJsonStatsForAppend(e);
768
- map[sessionKey] = e;
769
- return;
770
- }
771
- const messageCount = await countTranscriptMessageRows(update.sessionFile);
772
- const llm = rowsToLlmMessages(await readTranscriptRowsFromFile(update.sessionFile));
773
- patchSessionsJsonEntryStats(e, buildSessionsJsonStatsPatch(messageCount, this.estimateTokens(llm)));
774
- map[sessionKey] = e;
775
- });
776
- this.invalidateAllSessionsMapCache();
777
- invalidateSessionSearchIndexCache();
231
+ requireXopcDatabase();
232
+ ensureSessionRecord(sessionKey, this.resolveWorkspaceCwd(sessionKey));
233
+ if (update.message && isAppendOnlyLlmTranscriptMessage(update.message)) appendTranscriptEntry(sessionKey, update.message);
778
234
  });
779
235
  }
780
236
  async appendTranscriptContextEntry(key, entry) {
781
237
  return this.runStoreMutation(async () => {
782
- await this.ensureSession(key);
783
- const disk = await this.getDiskEntry(key);
784
- if (!disk) return;
785
- const keySessionsDir = this.resolveSessionsDirForKey(key);
786
- const absPath = this.transcriptPathForEntry(disk, keySessionsDir);
787
- const row = {
238
+ requireXopcDatabase();
239
+ ensureSessionRecord(key, this.resolveWorkspaceCwd(key));
240
+ appendTranscriptEntry(key, {
788
241
  kind: "context",
789
242
  id: typeof entry.id === "string" ? entry.id : void 0,
790
243
  text: typeof entry.text === "string" ? entry.text : void 0,
791
244
  data: entry.data,
792
245
  createdAt: entry.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
793
- };
794
- await appendPiTranscriptContextEntry({
795
- absPath,
796
- cwd: process.cwd(),
797
- entry: row,
798
- sessionKey: key
799
- });
800
- const llm = rowsToLlmMessages(existsSync(absPath) ? await readTranscriptRowsFromFile(absPath) : []);
801
- await withSessionsJsonLock(this.resolveStorePathForKey(key), async (map) => {
802
- const e = map[key];
803
- if (!e?.pluginExtensions?.xopc?.metadata) return;
804
- patchSessionsJsonEntryStats(e, buildSessionsJsonStatsPatch(llm.length, this.estimateTokens(llm)));
805
- map[key] = e;
806
246
  });
807
- this.invalidateAllSessionsMapCache();
808
- invalidateSessionSearchIndexCache();
809
247
  });
810
248
  }
811
- /**
812
- * Bulk write entry point used by compaction, tests, and admin tools.
813
- * Runtime agent turns must persist via {@link guardSessionManager} + appendMessage.
814
- */
815
249
  async saveMessages(key, messages) {
816
250
  return this.runStoreMutation(async () => {
817
- await this.ensureSession(key);
818
- const merged = mergeLlmMessagesPreservingContextRows(await this.loadTranscriptRows(key), messages);
819
- await this.writeTranscriptAndSyncIndex(key, merged);
251
+ requireXopcDatabase();
252
+ ensureSessionRecord(key, this.resolveWorkspaceCwd(key));
253
+ replaceTranscriptRows(key, mergeLlmMessagesPreservingContextRows(await this.loadTranscriptRows(key), messages));
820
254
  });
821
255
  }
822
256
  getWindowStats(messages) {
@@ -833,62 +267,12 @@ var SessionStore = class {
833
267
  stats: result
834
268
  };
835
269
  }
836
- checkpointBasename(sessionId) {
837
- return `${sessionId}.checkpoint.`;
838
- }
839
- async pruneCompactionCheckpoints(sessionId, sessionsDir) {
840
- const MAX = 15;
841
- const prefix = this.checkpointBasename(sessionId);
842
- let names;
843
- try {
844
- names = await readdir(sessionsDir);
845
- } catch {
846
- return;
847
- }
848
- const candidates = names.filter((n) => n.startsWith(prefix) && n.endsWith(".jsonl"));
849
- if (candidates.length <= MAX) return;
850
- const stats = await Promise.all(candidates.map(async (name) => {
851
- const p = join(sessionsDir, name);
852
- try {
853
- return {
854
- p,
855
- mtimeMs: (await stat(p)).mtimeMs
856
- };
857
- } catch {
858
- return {
859
- p: join(sessionsDir, name),
860
- mtimeMs: 0
861
- };
862
- }
863
- }));
864
- stats.sort((a, b) => a.mtimeMs - b.mtimeMs);
865
- for (let i = 0; i < stats.length - MAX; i++) try {
866
- await unlink(stats[i].p);
867
- } catch {}
868
- }
869
- async captureCompactionCheckpoint(sessionId, transcriptAbs, sessionsDir) {
870
- if (!existsSync(transcriptAbs)) return;
871
- const dest = join(sessionsDir, `${sessionId}.checkpoint.${randomUUID()}.jsonl`);
872
- try {
873
- await copyFile(transcriptAbs, dest);
874
- await this.pruneCompactionCheckpoints(sessionId, sessionsDir);
875
- } catch (err) {
876
- log.warn({
877
- err,
878
- sessionId
879
- }, "Compaction checkpoint copy failed");
880
- }
881
- }
882
270
  async applyCompaction(key, messages, result) {
883
271
  const compacted = this.compactor.applyCompaction(messages, result);
884
272
  return this.runStoreMutation(async () => {
885
- const entry = await this.getDiskEntry(key);
886
- if (!entry) return compacted;
887
- const keySessionsDir = this.resolveSessionsDirForKey(key);
888
- const abs = this.transcriptPathForEntry(entry, keySessionsDir);
889
- await this.captureCompactionCheckpoint(entry.sessionId, abs, keySessionsDir);
890
273
  const merged = mergeLlmMessagesPreservingContextRows(await this.loadTranscriptRows(key), compacted);
891
- await this.writeTranscriptAndSyncIndex(key, merged, { appendCompaction: {
274
+ captureCompactionCheckpoint(key);
275
+ replaceTranscriptRows(key, merged, { appendCompaction: {
892
276
  at: (/* @__PURE__ */ new Date()).toISOString(),
893
277
  summary: result.summary,
894
278
  firstKeptIndex: result.firstKeptIndex,
@@ -923,72 +307,21 @@ var SessionStore = class {
923
307
  };
924
308
  }
925
309
  async listCompactionCheckpoints(key) {
926
- const entry = await this.getDiskEntry(key);
927
- if (!entry) return [];
928
- const keySessionsDir = this.resolveSessionsDirForKey(key);
929
- const sessionId = entry.sessionId;
930
- const prefix = this.checkpointBasename(sessionId);
931
- let names;
932
- try {
933
- names = await readdir(keySessionsDir);
934
- } catch {
935
- return [];
936
- }
937
- const files = names.filter((n) => n.startsWith(prefix) && n.endsWith(".jsonl"));
938
- const valid = (await Promise.all(files.map(async (name) => {
939
- const p = join(keySessionsDir, name);
940
- const id = parseCompactionCheckpointTranscriptFileName(name)?.checkpointId;
941
- if (!id || !normalizeCompactionCheckpointId(id)) return null;
942
- try {
943
- const s = await stat(p);
944
- return {
945
- id: normalizeCompactionCheckpointId(id),
946
- sizeBytes: s.size,
947
- modifiedAt: new Date(s.mtimeMs).toISOString()
948
- };
949
- } catch {
950
- return null;
951
- }
952
- }))).filter((r) => r !== null);
953
- valid.sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt));
954
- return valid;
310
+ requireXopcDatabase();
311
+ return listCompactionCheckpoints(key);
955
312
  }
956
313
  async getCompactionCheckpointDetail(key, checkpointId) {
314
+ requireXopcDatabase();
957
315
  const id = normalizeCompactionCheckpointId(checkpointId);
958
316
  if (!id) return null;
959
- const entry = await this.getDiskEntry(key);
960
- if (!entry) return null;
961
- const cpPath = join(this.resolveSessionsDirForKey(key), `${this.checkpointBasename(entry.sessionId)}${id}.jsonl`);
962
- if (!existsSync(cpPath)) return null;
963
- try {
964
- const llm = rowsToLlmMessages(await readTranscriptRowsFromFile(cpPath));
965
- const s = await stat(cpPath);
966
- return {
967
- id,
968
- sizeBytes: s.size,
969
- modifiedAt: new Date(s.mtimeMs).toISOString(),
970
- messageCount: llm.length
971
- };
972
- } catch {
973
- return null;
974
- }
317
+ return getCompactionCheckpointDetail(key, id);
975
318
  }
976
319
  async restoreCompactionCheckpoint(key, checkpointId) {
977
- const id = normalizeCompactionCheckpointId(checkpointId);
978
- if (!id) throw new Error("Invalid checkpoint id");
979
320
  return this.runStoreMutation(async () => {
980
- const entry = await this.getDiskEntry(key);
981
- if (!entry) throw new Error(`Session not found: ${key}`);
982
- const keySessionsDir = this.resolveSessionsDirForKey(key);
983
- const cpPath = join(keySessionsDir, `${this.checkpointBasename(entry.sessionId)}${id}.jsonl`);
984
- if (!existsSync(cpPath)) throw new Error(`Checkpoint not found: ${id}`);
985
- await copyFile(cpPath, this.transcriptPathForEntry(entry, keySessionsDir));
986
- const messages = await this.loadMessages(key);
987
- await this.saveMessages(key, messages);
988
- log.info({
989
- key,
990
- checkpointId: id
991
- }, "Session transcript restored from compaction checkpoint");
321
+ requireXopcDatabase();
322
+ const id = normalizeCompactionCheckpointId(checkpointId);
323
+ if (!id) throw new Error(`Invalid checkpoint id: ${checkpointId}`);
324
+ restoreCompactionCheckpoint(key, id);
992
325
  });
993
326
  }
994
327
  async deleteSession(key) {
@@ -1002,66 +335,34 @@ var SessionStore = class {
1002
335
  }
1003
336
  async searchInSession(key, keyword) {
1004
337
  const messages = await this.loadDisplayMessages(key);
1005
- const keywordLower = keyword.toLowerCase();
1006
- return this.convertMessages(messages.filter((m) => {
1007
- return this.extractTextContent(this.messageContent(m)).toLowerCase().includes(keywordLower);
1008
- }));
338
+ const q = keyword.toLowerCase();
339
+ return this.convertMessages(messages.filter((m) => this.extractTextContent(this.messageContent(m)).toLowerCase().includes(q)));
1009
340
  }
1010
341
  async exportSession(key, format) {
1011
- const detail = await this.get(key);
1012
- if (!detail) throw new Error(`Session not found: ${key}`);
1013
- if (format === "json") {
1014
- const transcriptRows = await this.loadTranscriptRows(key);
1015
- const exportData = {
1016
- version: INDEX_VERSION,
1017
- exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
1018
- metadata: detail,
1019
- messages: detail.messages,
1020
- transcriptRows
1021
- };
1022
- return JSON.stringify(exportData, null, 2);
1023
- }
342
+ const metadata = await this.getMetadata(key);
343
+ if (!metadata) throw new Error(`Session not found: ${key}`);
344
+ const rows = await this.loadTranscriptRows(key);
345
+ const messages = this.convertMessages(buildSessionContextForLlm(rows));
346
+ const payload = {
347
+ version: INDEX_VERSION,
348
+ exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
349
+ metadata,
350
+ messages,
351
+ transcriptRows: rows
352
+ };
353
+ if (format === "json") return JSON.stringify(payload, null, 2);
1024
354
  const lines = [
1025
- `# ${detail.name || detail.key}`,
1026
- "",
1027
- `- **Channel:** ${detail.sourceChannel}`,
1028
- `- **Created:** ${detail.createdAt}`,
1029
- `- **Messages:** ${detail.messageCount}`,
1030
- `- **Tags:** ${detail.tags.join(", ") || "none"}`,
355
+ `# ${metadata.name ?? metadata.key}`,
1031
356
  "",
1032
- "---",
357
+ `Exported: ${payload.exportedAt}`,
1033
358
  ""
1034
359
  ];
1035
- for (const msg of detail.messages) {
1036
- const role = msg.role === "assistant" ? "Assistant" : msg.role === "user" ? "User" : msg.role;
1037
- lines.push(`## ${role}`, "");
1038
- const body = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
1039
- lines.push(body, "", "---", "");
1040
- }
360
+ for (const msg of messages) lines.push(`## ${msg.role}`, "", typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content), "");
1041
361
  return lines.join("\n");
1042
362
  }
1043
363
  async getStats() {
1044
- const sessions = (await this.list({ limit: 1e5 })).items;
1045
- const byChannel = {};
1046
- for (const s of sessions) byChannel[s.sourceChannel] = (byChannel[s.sourceChannel] || 0) + 1;
1047
- let oldestSession;
1048
- let newestSession;
1049
- if (sessions.length > 0) {
1050
- const sorted = [...sessions].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
1051
- oldestSession = sorted[0].createdAt;
1052
- newestSession = sorted[sorted.length - 1].createdAt;
1053
- }
1054
- return {
1055
- totalSessions: sessions.length,
1056
- activeSessions: sessions.filter((s) => s.status === "active" || s.status === "idle").length,
1057
- archivedSessions: sessions.filter((s) => s.status === "archived").length,
1058
- pinnedSessions: sessions.filter((s) => s.status === "pinned").length,
1059
- totalMessages: sessions.reduce((sum, s) => sum + s.messageCount, 0),
1060
- totalTokens: sessions.reduce((sum, s) => sum + s.estimatedTokens, 0),
1061
- oldestSession,
1062
- newestSession,
1063
- byChannel
1064
- };
364
+ requireXopcDatabase();
365
+ return getGlobalSessionStats();
1065
366
  }
1066
367
  async archiveOld(olderThanDays) {
1067
368
  const cutoff = /* @__PURE__ */ new Date();
@@ -1077,17 +378,44 @@ var SessionStore = class {
1077
378
  return archived;
1078
379
  }
1079
380
  estimateTokens(messages) {
1080
- let total = 0;
1081
- for (const msg of messages) total += Math.ceil(this.extractTextContent(this.messageContent(msg)).length / 4);
1082
- return total;
381
+ return estimateTokensFromMessages(messages);
1083
382
  }
1084
- messageContent(msg) {
1085
- return msg.content;
383
+ async loadDisplayMessages(key) {
384
+ requireXopcDatabase();
385
+ const checkpoints = listCompactionCheckpoints(key);
386
+ const rowSets = [];
387
+ for (const checkpoint of [...checkpoints].reverse()) rowSets.push(loadCheckpointRows(key, checkpoint.id));
388
+ rowSets.push(await this.loadTranscriptRows(key));
389
+ const messages = [];
390
+ const seen = /* @__PURE__ */ new Set();
391
+ for (const rows of rowSets) for (const message of buildSessionContextForLlm(rows)) {
392
+ if (this.isCompactionSummaryMessage(message)) continue;
393
+ const identity = this.displayMessageIdentity(message);
394
+ if (seen.has(identity)) continue;
395
+ seen.add(identity);
396
+ messages.push(message);
397
+ }
398
+ return messages;
1086
399
  }
1087
- isCompactionSummaryMessage(msg) {
1088
- if (msg.role !== "user") return false;
1089
- const text = this.extractTextContent(this.messageContent(msg)).trim();
1090
- return /^\[Previous conversation summary\]/i.test(text);
400
+ paginateDisplayMessages(messages, options = {}) {
401
+ const total = messages.length;
402
+ const limit = Math.min(200, Math.max(1, Math.trunc(options.limit ?? 50)));
403
+ const offset = Math.max(0, Math.trunc(options.offset ?? 0));
404
+ let startIndex;
405
+ let endIndex;
406
+ if (options.beforeEndIndex !== void 0 && Number.isFinite(options.beforeEndIndex)) {
407
+ endIndex = Math.min(total, Math.max(0, Math.trunc(options.beforeEndIndex)));
408
+ startIndex = Math.max(0, endIndex - limit);
409
+ } else {
410
+ endIndex = Math.max(0, total - offset);
411
+ startIndex = Math.max(0, endIndex - limit);
412
+ }
413
+ return {
414
+ messages: messages.slice(startIndex, endIndex),
415
+ total,
416
+ startIndex,
417
+ endIndex
418
+ };
1091
419
  }
1092
420
  displayMessageIdentity(message) {
1093
421
  const record = message;
@@ -1099,6 +427,14 @@ var SessionStore = class {
1099
427
  content: this.messageContent(message)
1100
428
  });
1101
429
  }
430
+ messageContent(msg) {
431
+ return msg.content;
432
+ }
433
+ isCompactionSummaryMessage(msg) {
434
+ if (msg.role !== "user") return false;
435
+ const text = this.extractTextContent(this.messageContent(msg)).trim();
436
+ return /^\[Previous conversation summary\]/i.test(text);
437
+ }
1102
438
  extractTextContent(content) {
1103
439
  if (typeof content === "string") return content;
1104
440
  if (Array.isArray(content)) {
@@ -1119,22 +455,11 @@ var SessionStore = class {
1119
455
  sessionKey: key
1120
456
  });
1121
457
  if (!contextText?.trim()) return;
1122
- const entry = await this.getDiskEntry(key);
1123
- if (!entry) return;
1124
- const keySessionsDir = this.resolveSessionsDirForKey(key);
1125
- const abs = this.transcriptPathForEntry(entry, keySessionsDir);
1126
- const workspaceDir = resolveEffectiveAgentProfileForSession(this.options.config, key).resolvedWorkspacePath;
1127
458
  try {
1128
- await appendPiTranscriptContextEntry({
1129
- absPath: abs,
1130
- cwd: workspaceDir,
1131
- sessionKey: key,
1132
- entry: {
1133
- kind: "context",
1134
- id: `post-compaction-${Date.now()}`,
1135
- text: contextText,
1136
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
1137
- }
459
+ await this.appendTranscriptContextEntry(key, {
460
+ id: `post-compaction-${Date.now()}`,
461
+ text: contextText,
462
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1138
463
  });
1139
464
  } catch (err) {
1140
465
  log.warn({
@@ -1161,7 +486,7 @@ var SessionStore = class {
1161
486
  const inputTokens = typeof rawUsage.input === "number" ? rawUsage.input : void 0;
1162
487
  const outputTokens = typeof rawUsage.output === "number" ? rawUsage.output : void 0;
1163
488
  const totalTokens = typeof rawUsage.totalTokens === "number" ? rawUsage.totalTokens : typeof rawUsage.total === "number" ? rawUsage.total : void 0;
1164
- if (inputTokens != null || outputTokens != null || totalTokens != null) row.usage = {
489
+ if (inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0) row.usage = {
1165
490
  inputTokens,
1166
491
  outputTokens,
1167
492
  totalTokens