@xopcai/xopc 0.0.85 → 0.0.87

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 (407) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/adapters/cli-login.js +3 -3
  3. package/dist/extensions/feishu/src/adapters/cli-login.js.map +1 -1
  4. package/dist/extensions/telegram/src/delivery-chat-id.d.ts +1 -1
  5. package/dist/extensions/telegram/src/delivery-chat-id.js +1 -1
  6. package/dist/extensions/telegram/src/delivery-chat-id.js.map +1 -1
  7. package/dist/extensions/telegram/src/routing-integration.js +1 -0
  8. package/dist/extensions/telegram/src/routing-integration.js.map +1 -1
  9. package/dist/extensions/telegram/xopc.extension.json +1 -1
  10. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js +2 -2
  11. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js.map +1 -1
  12. package/dist/extensions/weixin/src/api/api.js +2 -2
  13. package/dist/extensions/weixin/src/api/api.js.map +1 -1
  14. package/dist/extensions/weixin/src/auth/accounts.js +12 -12
  15. package/dist/extensions/weixin/src/auth/accounts.js.map +1 -1
  16. package/dist/extensions/weixin/src/delivery-to.js +2 -2
  17. package/dist/extensions/weixin/src/delivery-to.js.map +1 -1
  18. package/dist/extensions/weixin/src/messaging/debug-mode.js +5 -5
  19. package/dist/extensions/weixin/src/messaging/debug-mode.js.map +1 -1
  20. package/dist/extensions/weixin/src/messaging/inbound.js +11 -11
  21. package/dist/extensions/weixin/src/messaging/inbound.js.map +1 -1
  22. package/dist/extensions/weixin/src/storage/sync-buf.js +4 -4
  23. package/dist/extensions/weixin/src/storage/sync-buf.js.map +1 -1
  24. package/dist/extensions/weixin/src/workflow-progress.d.ts +1 -1
  25. package/dist/extensions/weixin/src/workflow-progress.js.map +1 -1
  26. package/dist/gateway/static/root/assets/agents-BEAbXpuP.js +222 -0
  27. package/dist/gateway/static/root/assets/{apps-page-D7v7649T.js → apps-page-Dg8R-Szf.js} +1 -1
  28. package/dist/gateway/static/root/assets/{channels-settings-nCaMb0a7.js → channels-settings-yohw9YSu.js} +1 -1
  29. package/dist/gateway/static/root/assets/{channels-status-swr-C1gZBcJV.js → channels-status-swr-BSHqqCF1.js} +1 -1
  30. package/dist/gateway/static/root/assets/{cron-api-CoYK0hlm.js → cron-api-0h_QT8U3.js} +1 -1
  31. package/dist/gateway/static/root/assets/{cron-page-DeGo-Vjc.js → cron-page-BkfKFfFk.js} +1 -1
  32. package/dist/gateway/static/root/assets/{dist-DaK4dsss.js → dist-Cmjp2APP.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-debug-page-BZngZWbO.js → extension-debug-page-CFa9z_1N.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-page-D6JSyV27.js → extension-page-BI8eaTPq.js} +1 -1
  35. package/dist/gateway/static/root/assets/extension-settings-page-x4BB7q1X.js +1 -0
  36. package/dist/gateway/static/root/assets/{fetch-B2MYHbWg.js → fetch-DRqwef_Q.js} +1 -1
  37. package/dist/gateway/static/root/assets/{field-primitives-Zzl22MvN.js → field-primitives-BiNHBo2Y.js} +1 -1
  38. package/dist/gateway/static/root/assets/{heartbeat-config-api-BtIcpG0O.js → heartbeat-config-api-ZRb8qhuz.js} +1 -1
  39. package/dist/gateway/static/root/assets/{index-D4vM3-P7.js → index-Cu7bKuUi.js} +96 -94
  40. package/dist/gateway/static/root/assets/index-a5gWIdZQ.css +1 -0
  41. package/dist/gateway/static/root/assets/{logs-page-_d4UJ-qQ.js → logs-page-BFZ8GgCv.js} +1 -1
  42. package/dist/gateway/static/root/assets/{sessions-page-5N4aF2Wk.js → sessions-page-CD7AfB-2.js} +1 -1
  43. package/dist/gateway/static/root/assets/settings-form-section-DiqqVs6m.js +1 -0
  44. package/dist/gateway/static/root/assets/settings-page-BBOjEQW3.js +3 -0
  45. package/dist/gateway/static/root/assets/{share-preview-page-D4EG_vM1.js → share-preview-page-n1Gprylk.js} +1 -1
  46. package/dist/gateway/static/root/assets/{skills-page-sPAXhh8w.js → skills-page-CcN_gj--.js} +1 -1
  47. package/dist/gateway/static/root/assets/{theme-store-DryYl3qD.js → theme-store-CZOh1nT3.js} +1 -1
  48. package/dist/gateway/static/root/assets/url-Dd8Q7kZZ.js +3 -0
  49. package/dist/gateway/static/root/assets/{utils-CYO9eTCM.js → utils-CkWBfxs4.js} +1 -1
  50. package/dist/gateway/static/root/assets/{voice-api-key-field-Ds51havm.js → voice-api-key-field-O6awz9hi.js} +1 -1
  51. package/dist/gateway/static/root/index.html +5 -5
  52. package/dist/package.js +1 -1
  53. package/dist/src/agent/agent-scope.d.ts +4 -0
  54. package/dist/src/agent/agent-scope.js +53 -10
  55. package/dist/src/agent/agent-scope.js.map +1 -1
  56. package/dist/src/agent/bootstrap/filter-bootstrap-files.js +2 -1
  57. package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
  58. package/dist/src/agent/embedded/session-tool-result-guard.js +2 -1
  59. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  60. package/dist/src/agent/embedded/tool-result-truncation.js +2 -1
  61. package/dist/src/agent/embedded/tool-result-truncation.js.map +1 -1
  62. package/dist/src/agent/fallback/candidates.js +2 -2
  63. package/dist/src/agent/fallback/candidates.js.map +1 -1
  64. package/dist/src/agent/goals/persistent-goal-apis.d.ts +0 -2
  65. package/dist/src/agent/goals/persistent-goal-service.js +0 -1
  66. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
  67. package/dist/src/agent/image/generation/normalization.js +2 -12
  68. package/dist/src/agent/image/generation/normalization.js.map +1 -1
  69. package/dist/src/agent/image/generation/provider-registry.d.ts +4 -8
  70. package/dist/src/agent/image/generation/provider-registry.js.map +1 -1
  71. package/dist/src/agent/image/generation/runtime.d.ts +2 -2
  72. package/dist/src/agent/image/generation/runtime.js.map +1 -1
  73. package/dist/src/agent/image/generation/types.d.ts +0 -18
  74. package/dist/src/agent/image/image-helpers.js +6 -1
  75. package/dist/src/agent/image/image-helpers.js.map +1 -1
  76. package/dist/src/agent/image/index.d.ts +1 -1
  77. package/dist/src/agent/inbound/inbound-loop.d.ts +5 -0
  78. package/dist/src/agent/inbound/inbound-loop.js +41 -10
  79. package/dist/src/agent/inbound/inbound-loop.js.map +1 -1
  80. package/dist/src/agent/inbound/turn-dispatcher.d.ts +4 -0
  81. package/dist/src/agent/inbound/turn-dispatcher.js +6 -4
  82. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  83. package/dist/src/agent/mcp/bundle-mcp-materialize.js +2 -1
  84. package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
  85. package/dist/src/agent/mcp/bundle-mcp-names.js +2 -1
  86. package/dist/src/agent/mcp/bundle-mcp-names.js.map +1 -1
  87. package/dist/src/agent/mcp/bundle-mcp-runtime.js +2 -1
  88. package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
  89. package/dist/src/agent/mcp/mcp-transport-config.js +2 -1
  90. package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
  91. package/dist/src/agent/mcp/mcp-transport.js +2 -1
  92. package/dist/src/agent/mcp/mcp-transport.js.map +1 -1
  93. package/dist/src/agent/media-generation/runtime-shared.js +2 -9
  94. package/dist/src/agent/media-generation/runtime-shared.js.map +1 -1
  95. package/dist/src/agent/messaging/command-handler.d.ts +6 -0
  96. package/dist/src/agent/messaging/command-handler.js +5 -0
  97. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  98. package/dist/src/agent/prompt/safety.d.ts +0 -7
  99. package/dist/src/agent/prompt/safety.js +1 -20
  100. package/dist/src/agent/prompt/safety.js.map +1 -1
  101. package/dist/src/agent/service/build-direct-message-content.js +1 -1
  102. package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
  103. package/dist/src/agent/service/direct-turn-helpers.d.ts +3 -1
  104. package/dist/src/agent/service/direct-turn-helpers.js +6 -1
  105. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  106. package/dist/src/agent/service/process-direct-one-shot.d.ts +4 -0
  107. package/dist/src/agent/service/process-direct-one-shot.js +15 -2
  108. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  109. package/dist/src/agent/service/process-direct-streaming.d.ts +4 -0
  110. package/dist/src/agent/service/process-direct-streaming.js +34 -4
  111. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  112. package/dist/src/agent/service/webchat-tts.js +1 -1
  113. package/dist/src/agent/service/webchat-tts.js.map +1 -1
  114. package/dist/src/agent/service.d.ts +8 -0
  115. package/dist/src/agent/service.js +21 -1
  116. package/dist/src/agent/service.js.map +1 -1
  117. package/dist/src/agent/tools/create-share-tool.js +27 -20
  118. package/dist/src/agent/tools/create-share-tool.js.map +1 -1
  119. package/dist/src/agent/tools/factory.js +1 -1
  120. package/dist/src/agent/tools/index.d.ts +0 -1
  121. package/dist/src/agent/tools/index.js +4 -5
  122. package/dist/src/agent/tools/shell.js +0 -13
  123. package/dist/src/agent/tools/shell.js.map +1 -1
  124. package/dist/src/agent/tools/workflow-tool.js +10 -4
  125. package/dist/src/agent/tools/workflow-tool.js.map +1 -1
  126. package/dist/src/agent/workflow/builtins/audit-repo.d.ts +5 -1
  127. package/dist/src/agent/workflow/builtins/audit-repo.js +52 -11
  128. package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -1
  129. package/dist/src/agent/workflow/builtins/debug-incident.d.ts +13 -0
  130. package/dist/src/agent/workflow/builtins/debug-incident.js +155 -0
  131. package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -0
  132. package/dist/src/agent/workflow/builtins/index.d.ts +3 -1
  133. package/dist/src/agent/workflow/builtins/index.js +11 -1
  134. package/dist/src/agent/workflow/builtins/index.js.map +1 -1
  135. package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +6 -1
  136. package/dist/src/agent/workflow/builtins/multi-perspective-review.js +66 -30
  137. package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -1
  138. package/dist/src/agent/workflow/builtins/pr-review.d.ts +12 -0
  139. package/dist/src/agent/workflow/builtins/pr-review.js +156 -0
  140. package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -0
  141. package/dist/src/agent/workflow/builtins/research.d.ts +5 -1
  142. package/dist/src/agent/workflow/builtins/research.js +37 -6
  143. package/dist/src/agent/workflow/builtins/research.js.map +1 -1
  144. package/dist/src/agent/workflow/catalog.d.ts +5 -0
  145. package/dist/src/agent/workflow/catalog.js +6 -2
  146. package/dist/src/agent/workflow/catalog.js.map +1 -1
  147. package/dist/src/agent/workflow/channel-capability.d.ts +3 -3
  148. package/dist/src/agent/workflow/index.d.ts +1 -1
  149. package/dist/src/agent/workflow/lint.d.ts +38 -0
  150. package/dist/src/agent/workflow/lint.js +74 -0
  151. package/dist/src/agent/workflow/lint.js.map +1 -0
  152. package/dist/src/agent/workflow/parser.js +13 -1
  153. package/dist/src/agent/workflow/parser.js.map +1 -1
  154. package/dist/src/agent/workflow/runtime.d.ts +3 -0
  155. package/dist/src/agent/workflow/runtime.js +76 -3
  156. package/dist/src/agent/workflow/runtime.js.map +1 -1
  157. package/dist/src/agent/workflow/types.d.ts +11 -1
  158. package/dist/src/browser/index.js +4 -4
  159. package/dist/src/browser/manager.d.ts +1 -3
  160. package/dist/src/browser/manager.js +0 -6
  161. package/dist/src/browser/manager.js.map +1 -1
  162. package/dist/src/browser/providers/browser-ext-install.d.ts +4 -4
  163. package/dist/src/browser/providers/browser-ext-install.js +38 -85
  164. package/dist/src/browser/providers/browser-ext-install.js.map +1 -1
  165. package/dist/src/browser/providers/cloakbrowser.d.ts +0 -5
  166. package/dist/src/browser/providers/cloakbrowser.js +2 -55
  167. package/dist/src/browser/providers/cloakbrowser.js.map +1 -1
  168. package/dist/src/channels/attachments/voice-stt-webchat.js +10 -8
  169. package/dist/src/channels/attachments/voice-stt-webchat.js.map +1 -1
  170. package/dist/src/channels/pairing/allow-from-file.js +9 -9
  171. package/dist/src/channels/pairing/allow-from-file.js.map +1 -1
  172. package/dist/src/channels/pairing/pairing-store.js +6 -6
  173. package/dist/src/channels/pairing/pairing-store.js.map +1 -1
  174. package/dist/src/chat-commands/builtins/session.js +1 -1
  175. package/dist/src/chat-commands/builtins/session.js.map +1 -1
  176. package/dist/src/chat-commands/builtins/tts.js +2 -2
  177. package/dist/src/chat-commands/builtins/tts.js.map +1 -1
  178. package/dist/src/chat-commands/builtins/workflow.js +7 -2
  179. package/dist/src/chat-commands/builtins/workflow.js.map +1 -1
  180. package/dist/src/chat-commands/context.d.ts +3 -0
  181. package/dist/src/chat-commands/context.js +21 -3
  182. package/dist/src/chat-commands/context.js.map +1 -1
  183. package/dist/src/chat-commands/session-key.d.ts +4 -37
  184. package/dist/src/chat-commands/session-key.js +49 -85
  185. package/dist/src/chat-commands/session-key.js.map +1 -1
  186. package/dist/src/chat-commands/types.d.ts +2 -0
  187. package/dist/src/cli/commands/agent/interactive.js +2 -2
  188. package/dist/src/cli/commands/agent/interactive.js.map +1 -1
  189. package/dist/src/cli/commands/agent/sessions.js +2 -2
  190. package/dist/src/cli/commands/agent/sessions.js.map +1 -1
  191. package/dist/src/cli/commands/agent.js +4 -5
  192. package/dist/src/cli/commands/agent.js.map +1 -1
  193. package/dist/src/cli/commands/channels.js +1 -5
  194. package/dist/src/cli/commands/channels.js.map +1 -1
  195. package/dist/src/cli/commands/gateway/lifecycle-core.js +1 -1
  196. package/dist/src/cli/commands/gateway/lifecycle-core.js.map +1 -1
  197. package/dist/src/cli/commands/gateway/logs.d.ts +9 -0
  198. package/dist/src/cli/commands/gateway/logs.js +50 -17
  199. package/dist/src/cli/commands/gateway/logs.js.map +1 -1
  200. package/dist/src/cli/commands/image.js +22 -21
  201. package/dist/src/cli/commands/image.js.map +1 -1
  202. package/dist/src/cli/commands/session/utils.js +2 -2
  203. package/dist/src/cli/commands/session/utils.js.map +1 -1
  204. package/dist/src/cli/commands/update.js +26 -46
  205. package/dist/src/cli/commands/update.js.map +1 -1
  206. package/dist/src/cli/utils/session.d.ts +0 -5
  207. package/dist/src/cli/utils/session.js +1 -6
  208. package/dist/src/cli/utils/session.js.map +1 -1
  209. package/dist/src/commands/agents.config.js +1 -1
  210. package/dist/src/commands/agents.config.js.map +1 -1
  211. package/dist/src/config/agent-profile.js +5 -27
  212. package/dist/src/config/agent-profile.js.map +1 -1
  213. package/dist/src/config/index.js +2 -2
  214. package/dist/src/config/model-input.js +2 -5
  215. package/dist/src/config/model-input.js.map +1 -1
  216. package/dist/src/config/schema.d.ts +201 -217
  217. package/dist/src/config/schema.js +54 -39
  218. package/dist/src/config/schema.js.map +1 -1
  219. package/dist/src/config/workspace-path-helpers.d.ts +1 -2
  220. package/dist/src/config/workspace-path-helpers.js.map +1 -1
  221. package/dist/src/daemon/install-plan.js +25 -1
  222. package/dist/src/daemon/install-plan.js.map +1 -1
  223. package/dist/src/daemon/launchd.d.ts +8 -0
  224. package/dist/src/daemon/launchd.js +5 -12
  225. package/dist/src/daemon/launchd.js.map +1 -1
  226. package/dist/src/daemon/schtasks.d.ts +25 -0
  227. package/dist/src/daemon/schtasks.js +166 -46
  228. package/dist/src/daemon/schtasks.js.map +1 -1
  229. package/dist/src/daemon/service.js +5 -4
  230. package/dist/src/daemon/service.js.map +1 -1
  231. package/dist/src/daemon/systemd.d.ts +6 -0
  232. package/dist/src/daemon/systemd.js +18 -3
  233. package/dist/src/daemon/systemd.js.map +1 -1
  234. package/dist/src/extensions/activation-context.js +0 -1
  235. package/dist/src/extensions/activation-context.js.map +1 -1
  236. package/dist/src/extensions/normalize-manifest.js +0 -1
  237. package/dist/src/extensions/normalize-manifest.js.map +1 -1
  238. package/dist/src/extensions/types/manifest.d.ts +0 -2
  239. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  240. package/dist/src/gateway/agent-builtin-tools.js +1 -0
  241. package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
  242. package/dist/src/gateway/agents-admin.js +10 -2
  243. package/dist/src/gateway/agents-admin.js.map +1 -1
  244. package/dist/src/gateway/heartbeat/service.js +1 -1
  245. package/dist/src/gateway/heartbeat/service.js.map +1 -1
  246. package/dist/src/gateway/hono/app.js +1 -1
  247. package/dist/src/gateway/hono/lib/agent-model.d.ts +18 -10
  248. package/dist/src/gateway/hono/lib/agent-model.js +24 -35
  249. package/dist/src/gateway/hono/lib/agent-model.js.map +1 -1
  250. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  251. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  252. package/dist/src/gateway/hono/lib/safe-voice-config.js +14 -53
  253. package/dist/src/gateway/hono/lib/safe-voice-config.js.map +1 -1
  254. package/dist/src/gateway/hono/routes/config-patch/agents.js +17 -5
  255. package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -1
  256. package/dist/src/gateway/hono/routes/config-patch/channels.js +0 -11
  257. package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -1
  258. package/dist/src/gateway/hono/routes/goals.js +1 -1
  259. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  260. package/dist/src/gateway/hono/routes/sessions.js +28 -7
  261. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  262. package/dist/src/gateway/hono/routes/shares.js +14 -12
  263. package/dist/src/gateway/hono/routes/shares.js.map +1 -1
  264. package/dist/src/gateway/hono/routes/tunnel.js +1 -1
  265. package/dist/src/gateway/hono/routes/update.js +4 -2
  266. package/dist/src/gateway/hono/routes/update.js.map +1 -1
  267. package/dist/src/gateway/hono/sse.js +16 -33
  268. package/dist/src/gateway/hono/sse.js.map +1 -1
  269. package/dist/src/gateway/lock.js +10 -10
  270. package/dist/src/gateway/lock.js.map +1 -1
  271. package/dist/src/gateway/ports.js +6 -6
  272. package/dist/src/gateway/ports.js.map +1 -1
  273. package/dist/src/gateway/resolve-webchat-session-key.d.ts +19 -0
  274. package/dist/src/gateway/resolve-webchat-session-key.js +46 -0
  275. package/dist/src/gateway/resolve-webchat-session-key.js.map +1 -0
  276. package/dist/src/gateway/service/run-gateway-agent.js +27 -11
  277. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  278. package/dist/src/gateway/service/sessions-api.d.ts +3 -0
  279. package/dist/src/gateway/service/sessions-api.js +8 -0
  280. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  281. package/dist/src/gateway/service.d.ts +0 -2
  282. package/dist/src/gateway/service.js +2 -7
  283. package/dist/src/gateway/service.js.map +1 -1
  284. package/dist/src/gateway/session-reset-service.d.ts +20 -0
  285. package/dist/src/gateway/session-reset-service.js +54 -0
  286. package/dist/src/gateway/session-reset-service.js.map +1 -0
  287. package/dist/src/gateway/startup-readiness.d.ts +1 -1
  288. package/dist/src/gateway/startup-readiness.js +1 -0
  289. package/dist/src/gateway/startup-readiness.js.map +1 -1
  290. package/dist/src/infra/gateway-processes.js +2 -2
  291. package/dist/src/infra/gateway-processes.js.map +1 -1
  292. package/dist/src/infra/run-command.d.ts +16 -0
  293. package/dist/src/infra/run-command.js +67 -0
  294. package/dist/src/infra/run-command.js.map +1 -0
  295. package/dist/src/infra/update-global.d.ts +45 -0
  296. package/dist/src/infra/update-global.js +224 -0
  297. package/dist/src/infra/update-global.js.map +1 -0
  298. package/dist/src/mcp/channel-bridge.js +1 -1
  299. package/dist/src/mcp/channel-shared.js +2 -1
  300. package/dist/src/mcp/channel-shared.js.map +1 -1
  301. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  302. package/dist/src/providers/auth-runtime/auth-profile-store.js.map +1 -1
  303. package/dist/src/providers/auth-runtime/resolve-auth.js +1 -12
  304. package/dist/src/providers/auth-runtime/resolve-auth.js.map +1 -1
  305. package/dist/src/providers/auth-runtime/types.d.ts +6 -12
  306. package/dist/src/routing/agent-session-key.d.ts +58 -0
  307. package/dist/src/routing/agent-session-key.js +164 -0
  308. package/dist/src/routing/agent-session-key.js.map +1 -0
  309. package/dist/src/routing/index.d.ts +1 -1
  310. package/dist/src/routing/index.js +4 -2
  311. package/dist/src/routing/index.js.map +1 -1
  312. package/dist/src/routing/resolve-route.d.ts +15 -0
  313. package/dist/src/routing/resolve-route.js +41 -20
  314. package/dist/src/routing/resolve-route.js.map +1 -1
  315. package/dist/src/routing/resolve-tui-session-key.d.ts +25 -0
  316. package/dist/src/routing/resolve-tui-session-key.js +54 -0
  317. package/dist/src/routing/resolve-tui-session-key.js.map +1 -0
  318. package/dist/src/routing/session-key-utils.d.ts +24 -0
  319. package/dist/src/routing/session-key-utils.js +92 -0
  320. package/dist/src/routing/session-key-utils.js.map +1 -0
  321. package/dist/src/routing/session-key.d.ts +19 -49
  322. package/dist/src/routing/session-key.js +143 -116
  323. package/dist/src/routing/session-key.js.map +1 -1
  324. package/dist/src/session/index.d.ts +6 -0
  325. package/dist/src/session/index.js +7 -1
  326. package/dist/src/session/init-session-turn.d.ts +30 -0
  327. package/dist/src/session/init-session-turn.js +102 -0
  328. package/dist/src/session/init-session-turn.js.map +1 -0
  329. package/dist/src/session/lifecycle-timestamps.d.ts +8 -0
  330. package/dist/src/session/lifecycle-timestamps.js +16 -0
  331. package/dist/src/session/lifecycle-timestamps.js.map +1 -0
  332. package/dist/src/session/manager.d.ts +7 -1
  333. package/dist/src/session/manager.js +8 -1
  334. package/dist/src/session/manager.js.map +1 -1
  335. package/dist/src/session/parity/transcript-paths.js +2 -2
  336. package/dist/src/session/parity/transcript-paths.js.map +1 -1
  337. package/dist/src/session/parity/xopc-session-disk-entry.d.ts +6 -0
  338. package/dist/src/session/reset-policy.d.ts +32 -0
  339. package/dist/src/session/reset-policy.js +65 -0
  340. package/dist/src/session/reset-policy.js.map +1 -0
  341. package/dist/src/session/reset-triggers.d.ts +20 -0
  342. package/dist/src/session/reset-triggers.js +63 -0
  343. package/dist/src/session/reset-triggers.js.map +1 -0
  344. package/dist/src/session/reset-type.d.ts +12 -0
  345. package/dist/src/session/reset-type.js +25 -0
  346. package/dist/src/session/reset-type.js.map +1 -0
  347. package/dist/src/session/resolve-session.d.ts +30 -0
  348. package/dist/src/session/resolve-session.js +93 -0
  349. package/dist/src/session/resolve-session.js.map +1 -0
  350. package/dist/src/session/session-title.js +3 -2
  351. package/dist/src/session/session-title.js.map +1 -1
  352. package/dist/src/session/store.d.ts +11 -4
  353. package/dist/src/session/store.js +57 -6
  354. package/dist/src/session/store.js.map +1 -1
  355. package/dist/src/session/transcript-events.js +2 -1
  356. package/dist/src/session/transcript-events.js.map +1 -1
  357. package/dist/src/share/share-url.d.ts +33 -0
  358. package/dist/src/share/share-url.js +56 -14
  359. package/dist/src/share/share-url.js.map +1 -1
  360. package/dist/src/tui/backends/embedded-backend.js +4 -9
  361. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  362. package/dist/src/tui/backends/gateway-sse-backend.js +1 -1
  363. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  364. package/dist/src/tui/components/chat-log.js +3 -3
  365. package/dist/src/tui/components/chat-log.js.map +1 -1
  366. package/dist/src/tui/theme.d.ts +0 -2
  367. package/dist/src/tui/theme.js +1 -3
  368. package/dist/src/tui/theme.js.map +1 -1
  369. package/dist/src/tui/tui-commands.d.ts +3 -0
  370. package/dist/src/tui/tui-commands.js +45 -10
  371. package/dist/src/tui/tui-commands.js.map +1 -1
  372. package/dist/src/tui/tui-keybindings-file.js +1 -21
  373. package/dist/src/tui/tui-keybindings-file.js.map +1 -1
  374. package/dist/src/tui/tui-session-actions.d.ts +28 -0
  375. package/dist/src/tui/tui-session-actions.js +88 -0
  376. package/dist/src/tui/tui-session-actions.js.map +1 -0
  377. package/dist/src/tui/tui.js +52 -47
  378. package/dist/src/tui/tui.js.map +1 -1
  379. package/dist/src/utils/string-coerce.d.ts +2 -0
  380. package/dist/src/utils/string-coerce.js +10 -1
  381. package/dist/src/utils/string-coerce.js.map +1 -1
  382. package/dist/src/voice/stt/config-slice.d.ts +2 -5
  383. package/dist/src/voice/stt/config-slice.js +5 -26
  384. package/dist/src/voice/stt/config-slice.js.map +1 -1
  385. package/dist/src/voice/stt/types.d.ts +1 -18
  386. package/dist/src/voice/stt/types.js +4 -2
  387. package/dist/src/voice/stt/types.js.map +1 -1
  388. package/dist/src/voice/tts/config-slice.d.ts +3 -7
  389. package/dist/src/voice/tts/config-slice.js +7 -38
  390. package/dist/src/voice/tts/config-slice.js.map +1 -1
  391. package/dist/src/voice/tts/merge-config.js +2 -48
  392. package/dist/src/voice/tts/merge-config.js.map +1 -1
  393. package/dist/src/voice/tts/providers/alibaba-speech.js +1 -1
  394. package/dist/src/voice/tts/providers/alibaba-speech.js.map +1 -1
  395. package/dist/src/voice/tts/types.d.ts +1 -29
  396. package/dist/src/voice/tts/types.js +19 -17
  397. package/dist/src/voice/tts/types.js.map +1 -1
  398. package/package.json +1 -4
  399. package/dist/gateway/static/root/assets/agents-D3_-kNlZ.js +0 -222
  400. package/dist/gateway/static/root/assets/extension-settings-page-8PZcmWI7.js +0 -1
  401. package/dist/gateway/static/root/assets/index-ew_2L2We.css +0 -1
  402. package/dist/gateway/static/root/assets/settings-form-section-D_tgb8r2.js +0 -1
  403. package/dist/gateway/static/root/assets/settings-page-C18xBt4X.js +0 -3
  404. package/dist/gateway/static/root/assets/url-BwNL6Rgk.js +0 -3
  405. package/dist/src/agent/tools/browser-legacy-tools.d.ts +0 -17
  406. package/dist/src/agent/tools/browser-legacy-tools.js +0 -766
  407. package/dist/src/agent/tools/browser-legacy-tools.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","names":["parseRoutingSessionKey"],"sources":["../../../src/session/store.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { copyFile, mkdir, readdir, stat, unlink } from 'fs/promises';\nimport { join } from 'path';\nimport { existsSync } from 'fs';\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { CompactionEntry } from '@earendil-works/pi-coding-agent';\n\nimport { loadEntriesFromFile } from './parity/load-jsonl-entries.js';\n\nimport type { Config } from '../config/schema.js';\nimport { resolveSessionsDir, FILENAMES } from '../config/paths.js';\nimport { resolveDefaultAgentId, listAgentEntries } from '../agent/agent-scope.js';\nimport { resolveEffectiveAgentProfileForSession } from '../config/agent-profile.js';\nimport { readPostCompactionContext } from '../agent/reply/post-compaction-context.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../routing/session-key.js';\nimport { createLogger } from '../utils/logger.js';\nimport { SessionCompactor, type CompactionConfig, type CompactionResult } from '../agent/memory/compaction.js';\nimport { SlidingWindow, type WindowConfig } from '../agent/memory/window.js';\nimport { invalidateSessionSearchIndexCache } from './search-index-cache.js';\nimport type { TranscriptCompactionRecord, XopcSessionTranscriptV1 } from './transcript-format.js';\nimport {\n mergeLlmMessagesPreservingContextRows,\n type TranscriptStoredRow,\n type XopcTranscriptContextEntry,\n} from './session-context-for-llm.js';\nimport { normalizeCompactionCheckpointId } from './compaction-checkpoints.js';\nimport type {\n SessionMetadata,\n SessionDetail,\n SessionListQuery,\n PaginatedResult,\n GlobalSessionStats,\n ExportFormat,\n SessionExport,\n SessionTranscriptSummary,\n CompactionCheckpointSummary,\n CompactionCheckpointDetail,\n} from './types.js';\nimport { SessionStatus } from './types.js';\nimport type { Message } from './types.js';\nimport { parseCompactionCheckpointTranscriptFileName } from './parity/artifacts.js';\nimport { archiveFileOnDisk, resolveSessionFilePath, resolveSessionTranscriptPathInDir } from './parity/transcript-paths.js';\nimport { validateSessionId } from './parity/session-id.js';\nimport { readSessionsJsonFile, withSessionsJsonLock } from './parity/sessions-json-file.js';\nimport {\n buildSessionsJsonStatsPatch,\n incrementSessionsJsonStatsForAppend,\n isAppendOnlyLlmTranscriptMessage,\n patchSessionsJsonEntryStats,\n} from './parity/sessions-json-patch.js';\nimport {\n countTranscriptMessageRows,\n readDisplayMessagePageFromTranscriptFile,\n} from './parity/transcript-pagination.js';\nimport type { XopcSessionDiskEntry } from './parity/xopc-session-disk-entry.js';\nimport {\n appendPiTranscriptContextEntry,\n persistMergedTranscriptRows,\n readTranscriptRowsFromFile,\n rowsToLlmMessages,\n writeTranscriptJsonl,\n} from './parity/jsonl-transcript-io.js';\nimport type { SessionTranscriptUpdate } from './transcript-events.js';\n\nconst log = createLogger('SessionStore');\n\nconst INDEX_VERSION = '1.0';\nconst DELETED_MARKER = '.jsonl.deleted.';\n\nexport interface SessionStoreOptions {\n config: Config;\n agentId?: string;\n sessionsDir?: string;\n}\n\nexport class SessionStore {\n private sessionsDir: string;\n private archiveDir: string;\n private storePath: string;\n private window: SlidingWindow;\n private compactor: SessionCompactor;\n private storeMutationDepth = 0;\n private storeMutationChain: Promise<void> = Promise.resolve();\n /** Cache of per-agent sessions dirs to avoid re-resolution on every call. */\n private agentSessionsDirCache = new Map<string, string>();\n\n constructor(\n private options: SessionStoreOptions,\n windowConfig?: Partial<WindowConfig>,\n compactionConfig?: Partial<CompactionConfig>,\n ) {\n const agentId = options.agentId ?? resolveDefaultAgentId(options.config);\n this.sessionsDir = options.sessionsDir ?? resolveSessionsDir(options.config, agentId);\n this.archiveDir = join(this.sessionsDir, 'archive');\n this.storePath = join(this.sessionsDir, FILENAMES.SESSIONS_MAP);\n this.window = new SlidingWindow(windowConfig);\n this.compactor = new SessionCompactor(compactionConfig);\n }\n\n getSessionsRoot(): string {\n return this.sessionsDir;\n }\n\n /**\n * OpenClaw-aligned: resolve the sessions directory for a given session key.\n * Extracts agentId from the session key and routes to `agents/<agentId>/sessions/`.\n * Falls back to the default sessions directory when agentId cannot be parsed\n * or when `sessionsDir` was explicitly provided in options.\n */\n private resolveSessionsDirForKey(sessionKey: string): string {\n if (this.options.sessionsDir) {\n return this.sessionsDir;\n }\n const parsed = parseRoutingSessionKey(sessionKey);\n if (!parsed) {\n return this.sessionsDir;\n }\n const agentId = parsed.agentId;\n const cached = this.agentSessionsDirCache.get(agentId);\n if (cached) {\n return cached;\n }\n const resolved = resolveSessionsDir(this.options.config, agentId);\n this.agentSessionsDirCache.set(agentId, resolved);\n return resolved;\n }\n\n private resolveStorePathForKey(sessionKey: string): string {\n return join(this.resolveSessionsDirForKey(sessionKey), FILENAMES.SESSIONS_MAP);\n }\n\n private async runStoreMutation<T>(fn: () => Promise<T>): Promise<T> {\n if (this.storeMutationDepth > 0) {\n return fn();\n }\n const run = this.storeMutationChain.then(async () => {\n this.storeMutationDepth++;\n try {\n return await fn();\n } finally {\n this.storeMutationDepth--;\n }\n });\n this.storeMutationChain = run.then(() => undefined).catch(() => undefined);\n return run as Promise<T>;\n }\n\n async initialize(): Promise<void> {\n await mkdir(this.sessionsDir, { recursive: true });\n await mkdir(this.archiveDir, { recursive: true });\n if (!existsSync(this.storePath)) {\n await withSessionsJsonLock(this.storePath, async () => undefined);\n }\n log.debug('Session store initialized (sessions.json + JSONL)');\n }\n\n private transcriptPathForEntry(entry: XopcSessionDiskEntry, sessionsDir?: string): string {\n return resolveSessionFilePath(entry.sessionId, entry, { sessionsDir: sessionsDir ?? this.sessionsDir });\n }\n\n private async readMapForKey(sessionKey: string): Promise<Record<string, XopcSessionDiskEntry>> {\n const storePath = this.resolveStorePathForKey(sessionKey);\n return readSessionsJsonFile<XopcSessionDiskEntry>(storePath);\n }\n\n private async readMap(): Promise<Record<string, XopcSessionDiskEntry>> {\n return readSessionsJsonFile<XopcSessionDiskEntry>(this.storePath);\n }\n\n /**\n * OpenClaw-aligned: read sessions.json from ALL known agents and merge into a single map.\n * Used by aggregation queries (list, getByAgent, getByAccount, etc.) so the gateway UI\n * can display sessions across all agents.\n */\n private async readAllMaps(): Promise<Record<string, XopcSessionDiskEntry>> {\n if (this.options.sessionsDir) {\n return this.readMap();\n }\n const agents = listAgentEntries(this.options.config);\n const defaultId = resolveDefaultAgentId(this.options.config);\n const agentIds = new Set<string>([defaultId, ...agents.map((a) => a.id)]);\n\n const merged: Record<string, XopcSessionDiskEntry> = {};\n for (const id of agentIds) {\n const dir = resolveSessionsDir(this.options.config, id);\n const path = join(dir, FILENAMES.SESSIONS_MAP);\n if (!existsSync(path)) {\n continue;\n }\n const map = await readSessionsJsonFile<XopcSessionDiskEntry>(path);\n Object.assign(merged, map);\n }\n return merged;\n }\n\n private async getDiskEntry(sessionKey: string): Promise<XopcSessionDiskEntry | undefined> {\n const map = await this.readMapForKey(sessionKey);\n return map[sessionKey];\n }\n\n private buildDefaultMetadata(key: string): SessionMetadata {\n const { channel, chatId } = this.parseSessionKey(key);\n const routing = this.extractRoutingFromKey(key);\n const isCronSession = channel === 'cron';\n const isHeartbeatSession = channel === 'heartbeat';\n const now = new Date().toISOString();\n return {\n key,\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\n ? { sessionType: 'cron', customData: { cronJobId: chatId } }\n : {}),\n ...(isHeartbeatSession\n ? { sessionType: 'heartbeat', customData: { heartbeatTarget: chatId } }\n : {}),\n stats: { messageCount: 0, tokenCount: 0 },\n };\n }\n\n private parseSessionKey(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\n private extractRoutingFromKey(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\n /** Resolve on-disk transcript path; creates session row + empty JSONL when missing. */\n async resolveTranscriptPath(\n sessionKey: string,\n ): Promise<{ sessionId: string; absPath: string; sessionsDir: string }> {\n const entry = await this.ensureSession(sessionKey);\n const sessionsDir = this.resolveSessionsDirForKey(sessionKey);\n const absPath = this.transcriptPathForEntry(entry, sessionsDir);\n return { sessionId: entry.sessionId, absPath, sessionsDir };\n }\n\n /** Ensure sessions.json has an entry and transcript file exist for `sessionKey`. */\n private async ensureSession(sessionKey: string): Promise<XopcSessionDiskEntry> {\n const keyStorePath = this.resolveStorePathForKey(sessionKey);\n const keySessionsDir = this.resolveSessionsDirForKey(sessionKey);\n await mkdir(keySessionsDir, { recursive: true });\n return withSessionsJsonLock(keyStorePath, async (map) => {\n const existing = map[sessionKey] as XopcSessionDiskEntry | undefined;\n if (existing?.pluginExtensions?.xopc?.metadata) {\n return existing;\n }\n let entry = existing;\n if (!entry) {\n const sessionId = randomUUID();\n validateSessionId(sessionId);\n const sessionFile = `${sessionId}.jsonl`;\n const now = Date.now();\n const metadata = this.buildDefaultMetadata(sessionKey);\n metadata.transcriptId = sessionId;\n entry = {\n sessionId,\n updatedAt: now,\n sessionStartedAt: now,\n sessionFile,\n pluginExtensions: { xopc: { metadata } },\n };\n map[sessionKey] = entry as Record<string, unknown>;\n const abs = resolveSessionTranscriptPathInDir(sessionId, keySessionsDir);\n await writeTranscriptJsonl({\n absPath: abs,\n sessionId,\n cwd: process.cwd(),\n rows: [],\n });\n } else if (!entry.pluginExtensions?.xopc?.metadata) {\n const metadata = this.buildDefaultMetadata(sessionKey);\n metadata.transcriptId = entry.sessionId;\n entry.pluginExtensions = { xopc: { metadata } };\n map[sessionKey] = entry as Record<string, unknown>;\n }\n return entry!;\n });\n }\n\n private metadataFromEntry(sessionKey: string, entry: XopcSessionDiskEntry): SessionMetadata {\n const base = entry.pluginExtensions?.xopc?.metadata ?? this.buildDefaultMetadata(sessionKey);\n const { channel: keySource, chatId: keyChatId } = this.parseSessionKey(sessionKey);\n const diskSc = typeof base.sourceChannel === 'string' ? base.sourceChannel.trim() : '';\n const diskChat = typeof base.sourceChatId === 'string' ? base.sourceChatId.trim() : '';\n return {\n ...base,\n key: sessionKey,\n transcriptId: entry.sessionId,\n sourceChannel: diskSc || keySource,\n sourceChatId: diskChat || keyChatId,\n };\n }\n\n async getByAgent(agentId: string): Promise<SessionMetadata[]> {\n const map = await this.readAllMaps();\n const out: SessionMetadata[] = [];\n for (const [key, e] of Object.entries(map)) {\n const m = this.metadataFromEntry(key, e);\n if (m.routing?.agentId?.toLowerCase() === agentId.toLowerCase()) {\n out.push(m);\n }\n }\n return out;\n }\n\n async getByAccount(accountId: string): Promise<SessionMetadata[]> {\n const map = await this.readAllMaps();\n const out: SessionMetadata[] = [];\n for (const [key, e] of Object.entries(map)) {\n const m = this.metadataFromEntry(key, e);\n if (m.routing?.accountId === accountId) {\n out.push(m);\n }\n }\n return out;\n }\n\n async getByPeer(peerKind: string, peerId: string): Promise<SessionMetadata[]> {\n const map = await this.readAllMaps();\n const out: SessionMetadata[] = [];\n for (const [key, e] of Object.entries(map)) {\n const m = this.metadataFromEntry(key, e);\n if (m.routing?.peerKind === peerKind && m.routing.peerId === peerId) {\n out.push(m);\n }\n }\n return out;\n }\n\n async getMainSession(channel: string, accountId: string): Promise<SessionMetadata | null> {\n const map = await this.readAllMaps();\n for (const [key, e] of Object.entries(map)) {\n const m = this.metadataFromEntry(key, e);\n if (\n m.routing?.source === channel &&\n m.routing.accountId === accountId &&\n m.routing.peerKind === 'dm' &&\n m.routing.peerId === 'main'\n ) {\n return m;\n }\n }\n return null;\n }\n\n async refreshIndex(): Promise<void> {\n /* no-op: sessions.json is authoritative */\n }\n\n async list(query: SessionListQuery = {}): Promise<PaginatedResult<SessionMetadata>> {\n const map = await this.readAllMaps();\n let sessions = Object.entries(map).map(([k, e]) => this.metadataFromEntry(k, e));\n\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status];\n sessions = sessions.filter((s) => statuses.includes(s.status));\n }\n if (query.channel) {\n const rawChannels = query.channel\n .split(',')\n .map((c) => c.trim().toLowerCase())\n .filter(Boolean);\n /**\n * `ui` is a legacy console source; treat as webchat when filtering web sessions.\n * `webui` matches slash-command normalization to `gateway` (see `chat-commands/session-key.ts`).\n */\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 sessions = [];\n } else if (channels.length === 1) {\n const ch = channels[0]!;\n sessions = sessions.filter((s) => (s.sourceChannel ?? '').toLowerCase() === ch);\n } else {\n sessions = sessions.filter((s) => channels.includes((s.sourceChannel ?? '').toLowerCase()));\n }\n }\n if (query.tags?.length) {\n sessions = sessions.filter((s) => query.tags!.some((t) => s.tags.includes(t)));\n }\n if (query.search) {\n const q = query.search.toLowerCase();\n sessions = sessions.filter(\n (s) =>\n s.key.toLowerCase().includes(q) ||\n (s.name?.toLowerCase().includes(q) ?? false) ||\n s.tags.some((t) => t.toLowerCase().includes(q)),\n );\n }\n\n const sortBy = query.sortBy || 'updatedAt';\n const sortOrder = query.sortOrder || 'desc';\n sessions.sort((a, b) => {\n const av = a[sortBy];\n const bv = b[sortBy];\n const c = av < bv ? -1 : av > bv ? 1 : 0;\n return sortOrder === 'asc' ? c : -c;\n });\n\n const total = sessions.length;\n const limit = query.limit || 50;\n const offset = query.offset || 0;\n const items = sessions.slice(offset, offset + limit);\n return { items, total, limit, offset, hasMore: offset + limit < total };\n }\n\n async get(\n key: string,\n options?: { includeTranscriptSummary?: boolean; includeTranscriptRows?: boolean },\n ): Promise<SessionDetail | null> {\n const metadata = await this.getMetadata(key);\n if (!metadata) {\n return null;\n }\n const messages = await this.loadDisplayMessages(key);\n const detail = await this.buildSessionDetail(key, metadata, messages, options);\n return detail;\n }\n\n async getMessagePage(\n key: string,\n options: {\n offset?: number;\n limit?: number;\n before?: string;\n includeTranscriptSummary?: boolean;\n includeTranscriptRows?: boolean;\n } = {},\n ): Promise<{\n session: SessionDetail;\n pagination: {\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n before?: string;\n nextBeforeCursor?: string;\n };\n } | null> {\n const metadata = await this.getMetadata(key);\n if (!metadata) {\n return null;\n }\n\n const limit = Math.min(200, Math.max(1, Math.trunc(options.limit ?? 50)));\n const offset = Math.max(0, Math.trunc(options.offset ?? 0));\n const parsedBefore = options.before ? Number.parseInt(options.before, 10) : undefined;\n const hasBeforeCursor = parsedBefore !== undefined && Number.isFinite(parsedBefore);\n\n const checkpoints = await this.listCompactionCheckpoints(key);\n if (checkpoints.length === 0) {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return null;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const primary = this.transcriptPathForEntry(entry, keySessionsDir);\n const page = await readDisplayMessagePageFromTranscriptFile(primary, {\n limit,\n offset: hasBeforeCursor ? undefined : offset,\n beforeIndex: hasBeforeCursor ? parsedBefore : undefined,\n });\n const session = await this.buildSessionDetail(key, metadata, page.messages, options);\n const nextBeforeCursor = page.startIndex > 0 ? String(page.startIndex) : undefined;\n\n return {\n session,\n pagination: {\n total: page.total,\n limit,\n offset,\n hasMore: page.startIndex > 0,\n ...(hasBeforeCursor ? { before: String(page.endIndex) } : {}),\n ...(nextBeforeCursor ? { nextBeforeCursor } : {}),\n },\n };\n }\n\n const messages = await this.loadDisplayMessages(key);\n const total = messages.length;\n const endExclusive = hasBeforeCursor\n ? Math.min(total, Math.max(0, Math.trunc(parsedBefore!)))\n : Math.max(0, total - offset);\n const startInclusive = Math.max(0, endExclusive - limit);\n const pageMessages = messages.slice(startInclusive, endExclusive);\n const session = await this.buildSessionDetail(key, metadata, pageMessages, options);\n const nextBeforeCursor = startInclusive > 0 ? String(startInclusive) : undefined;\n\n return {\n session,\n pagination: {\n total,\n limit,\n offset,\n hasMore: startInclusive > 0,\n ...(hasBeforeCursor ? { before: String(endExclusive) } : {}),\n ...(nextBeforeCursor ? { nextBeforeCursor } : {}),\n },\n };\n }\n\n private async buildSessionDetail(\n key: string,\n metadata: SessionMetadata,\n messages: AgentMessage[],\n options?: { includeTranscriptSummary?: boolean; includeTranscriptRows?: boolean },\n ): Promise<SessionDetail> {\n let transcriptSummary: SessionTranscriptSummary | undefined;\n if (options?.includeTranscriptSummary) {\n const env = await this.loadTranscriptDocument(key);\n if (env) {\n transcriptSummary = {\n id: env.id,\n version: env.version,\n createdAt: env.createdAt,\n updatedAt: env.updatedAt,\n compactionCount: env.compactions?.length ?? 0,\n };\n }\n }\n let transcriptRows: TranscriptStoredRow[] | undefined;\n if (options?.includeTranscriptRows) {\n transcriptRows = await this.loadTranscriptRows(key);\n }\n return {\n ...metadata,\n messages: this.convertMessages(messages),\n ...(transcriptSummary ? { transcriptSummary } : {}),\n ...(transcriptRows !== undefined ? { transcriptRows } : {}),\n };\n }\n\n async loadTranscriptRows(key: string): Promise<TranscriptStoredRow[]> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return [];\n }\n const path = this.transcriptPathForEntry(entry, this.resolveSessionsDirForKey(key));\n return readTranscriptRowsFromFile(path);\n }\n\n async getMetadata(key: string): Promise<SessionMetadata | null> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return null;\n }\n return this.metadataFromEntry(key, entry);\n }\n\n async updateMetadata(key: string, updates: Partial<SessionMetadata>): Promise<void> {\n return this.runStoreMutation(async () => {\n const keyStorePath = this.resolveStorePathForKey(key);\n await withSessionsJsonLock(keyStorePath, async (map) => {\n const entry = map[key] as XopcSessionDiskEntry | undefined;\n if (!entry?.pluginExtensions?.xopc?.metadata) {\n throw new Error(`Session not found: ${key}`);\n }\n const meta = { ...entry.pluginExtensions.xopc.metadata, ...updates, updatedAt: new Date().toISOString() };\n entry.pluginExtensions.xopc.metadata = meta;\n entry.updatedAt = Date.now();\n map[key] = entry as Record<string, unknown>;\n });\n invalidateSessionSearchIndexCache();\n log.debug({ key, updates }, 'Session metadata updated');\n });\n }\n\n async delete(key: string): Promise<boolean> {\n return this.runStoreMutation(async () => {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return false;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const abs = this.transcriptPathForEntry(entry, keySessionsDir);\n const keyStorePath = this.resolveStorePathForKey(key);\n await withSessionsJsonLock(keyStorePath, async (map) => {\n delete map[key];\n });\n try {\n if (existsSync(abs)) {\n archiveFileOnDisk(abs, 'deleted');\n }\n } catch (err) {\n log.warn({ err, key }, 'Transcript archive on delete failed');\n }\n invalidateSessionSearchIndexCache();\n log.info({ key }, 'Session deleted');\n return true;\n });\n }\n\n async deleteMany(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n const success: string[] = [];\n const failed: string[] = [];\n for (const key of keys) {\n try {\n if (await this.delete(key)) {\n success.push(key);\n } else {\n failed.push(key);\n }\n } catch {\n failed.push(key);\n }\n }\n return { success, failed };\n }\n\n async setStatus(key: string, status: SessionStatus): Promise<void> {\n await this.updateMetadata(key, { status });\n if (status === SessionStatus.ARCHIVED) {\n await this.moveToArchive(key);\n } else {\n await this.moveFromArchive(key);\n }\n }\n\n async archive(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ARCHIVED);\n }\n\n async unarchive(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ACTIVE);\n }\n\n async pin(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.PINNED);\n }\n\n async unpin(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ACTIVE);\n }\n\n private async moveToArchive(key: string): Promise<void> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const abs = this.transcriptPathForEntry(entry, keySessionsDir);\n if (existsSync(abs)) {\n try {\n archiveFileOnDisk(abs, 'deleted');\n } catch (err) {\n log.warn({ err, key }, 'Archive transcript rename failed');\n }\n }\n }\n\n private async findMostRecentDeletedTranscript(sessionId: string, sessionsDir: string): Promise<string | null> {\n let names: string[];\n try {\n names = await readdir(sessionsDir);\n } catch {\n return null;\n }\n const prefix = `${sessionId}${DELETED_MARKER}`;\n const hits = names.filter((n) => n.startsWith(prefix) && n.endsWith('Z'));\n hits.sort().reverse();\n const first = hits[0];\n return first ? join(sessionsDir, first) : null;\n }\n\n private async moveFromArchive(key: string): Promise<void> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const target = resolveSessionTranscriptPathInDir(entry.sessionId, keySessionsDir);\n if (existsSync(target)) {\n return;\n }\n const src = await this.findMostRecentDeletedTranscript(entry.sessionId, keySessionsDir);\n if (!src) {\n return;\n }\n try {\n const { rename } = await import('fs/promises');\n await rename(src, target);\n } catch (err) {\n log.warn({ err, key, src, target }, 'Unarchive transcript rename failed');\n }\n }\n\n async loadMessages(key: string, options?: { fromArchive?: boolean }): Promise<AgentMessage[]> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return [];\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const primary = this.transcriptPathForEntry(entry, keySessionsDir);\n if (existsSync(primary)) {\n const rows = await readTranscriptRowsFromFile(primary);\n return rowsToLlmMessages(rows);\n }\n if (options?.fromArchive) {\n const archived = await this.findMostRecentDeletedTranscript(entry.sessionId, keySessionsDir);\n if (!archived) {\n return [];\n }\n const rows = await readTranscriptRowsFromFile(archived);\n return rowsToLlmMessages(rows);\n }\n return [];\n }\n\n private async loadDisplayMessages(key: string): Promise<AgentMessage[]> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return [];\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const primary = this.transcriptPathForEntry(entry, keySessionsDir);\n const transcriptPaths: string[] = [];\n const checkpoints = await this.listCompactionCheckpoints(key);\n for (const checkpoint of [...checkpoints].reverse()) {\n transcriptPaths.push(join(keySessionsDir, `${this.checkpointBasename(entry.sessionId)}${checkpoint.id}.jsonl`));\n }\n transcriptPaths.push(primary);\n\n const messages: AgentMessage[] = [];\n const seenMessages = new Set<string>();\n for (const transcriptPath of transcriptPaths) {\n if (!existsSync(transcriptPath)) {\n continue;\n }\n const rows = await readTranscriptRowsFromFile(transcriptPath);\n for (const message of rowsToLlmMessages(rows)) {\n if (this.isCompactionSummaryMessage(message)) {\n continue;\n }\n const key = this.displayMessageIdentity(message);\n if (seenMessages.has(key)) {\n continue;\n }\n seenMessages.add(key);\n messages.push(message);\n }\n }\n return messages;\n }\n\n async loadTranscriptDocument(key: string): Promise<XopcSessionTranscriptV1 | null> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return null;\n }\n const path = this.transcriptPathForEntry(entry, this.resolveSessionsDirForKey(key));\n if (!existsSync(path)) {\n return null;\n }\n const entries = loadEntriesFromFile(path);\n const header = entries.find((e) => e.type === 'session');\n if (!header || typeof (header as { id?: unknown }).id !== 'string') {\n return null;\n }\n const sessionHeader = header as { type: 'session'; id: string; timestamp?: string };\n const rows = await readTranscriptRowsFromFile(path);\n const compactions = entries\n .filter((e): e is CompactionEntry => e.type === 'compaction')\n .map((c) => ({\n at: c.timestamp,\n summary: c.summary,\n firstKeptIndex: Number.parseInt(String(c.firstKeptEntryId), 10) || 0,\n tokensBefore: c.tokensBefore,\n tokensAfter:\n typeof c.details === 'object' &&\n c.details &&\n 'tokensAfter' in c.details &&\n typeof (c.details as { tokensAfter?: unknown }).tokensAfter === 'number'\n ? (c.details as { tokensAfter: number }).tokensAfter\n : 0,\n }));\n return {\n type: 'xopc_session_transcript',\n version: 1,\n id: sessionHeader.id,\n createdAt: sessionHeader.timestamp ?? new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n messages: rows,\n ...(compactions.length > 0 ? { compactions } : {}),\n };\n }\n\n private async writeTranscriptAndSyncIndex(\n key: string,\n rows: TranscriptStoredRow[],\n opts?: { appendCompaction?: TranscriptCompactionRecord },\n ): Promise<void> {\n const entry = await this.ensureSession(key);\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const abs = this.transcriptPathForEntry(entry, keySessionsDir);\n const llm = rowsToLlmMessages(rows);\n await persistMergedTranscriptRows({\n absPath: abs,\n sessionId: entry.sessionId,\n cwd: process.cwd(),\n rows,\n appendCompaction: opts?.appendCompaction,\n });\n const keyStorePath = this.resolveStorePathForKey(key);\n await withSessionsJsonLock(keyStorePath, async (map) => {\n const e = map[key] as XopcSessionDiskEntry | undefined;\n if (!e?.pluginExtensions?.xopc?.metadata) {\n return;\n }\n patchSessionsJsonEntryStats(\n e,\n buildSessionsJsonStatsPatch(llm.length, this.estimateTokens(llm)),\n );\n map[key] = e as Record<string, unknown>;\n });\n invalidateSessionSearchIndexCache();\n }\n\n /** Incremental sessions.json stats after guard append (OpenClaw transcript-events). */\n async syncSessionsJsonFromTranscriptUpdate(update: SessionTranscriptUpdate): Promise<void> {\n const sessionKey = update.sessionKey?.trim();\n if (!sessionKey || !existsSync(update.sessionFile)) {\n return;\n }\n return this.runStoreMutation(async () => {\n const keyStorePath = this.resolveStorePathForKey(sessionKey);\n await withSessionsJsonLock(keyStorePath, async (map) => {\n const e = map[sessionKey] as XopcSessionDiskEntry | undefined;\n if (!e?.pluginExtensions?.xopc?.metadata) {\n return;\n }\n\n if (update.message && isAppendOnlyLlmTranscriptMessage(update.message)) {\n incrementSessionsJsonStatsForAppend(e);\n map[sessionKey] = e as Record<string, unknown>;\n return;\n }\n\n const messageCount = await countTranscriptMessageRows(update.sessionFile);\n const rows = await readTranscriptRowsFromFile(update.sessionFile);\n const llm = rowsToLlmMessages(rows);\n patchSessionsJsonEntryStats(\n e,\n buildSessionsJsonStatsPatch(messageCount, this.estimateTokens(llm)),\n );\n map[sessionKey] = e as Record<string, unknown>;\n });\n invalidateSessionSearchIndexCache();\n });\n }\n\n async appendTranscriptContextEntry(\n key: string,\n entry: Omit<XopcTranscriptContextEntry, 'kind'> & Partial<Pick<XopcTranscriptContextEntry, 'kind'>>,\n ): Promise<void> {\n return this.runStoreMutation(async () => {\n await this.ensureSession(key);\n const disk = await this.getDiskEntry(key);\n if (!disk) {\n return;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const absPath = this.transcriptPathForEntry(disk, keySessionsDir);\n const row: XopcTranscriptContextEntry = {\n kind: 'context',\n id: typeof entry.id === 'string' ? entry.id : undefined,\n text: typeof entry.text === 'string' ? entry.text : undefined,\n data: entry.data,\n createdAt: entry.createdAt ?? new Date().toISOString(),\n };\n await appendPiTranscriptContextEntry({\n absPath,\n cwd: process.cwd(),\n entry: row,\n sessionKey: key,\n });\n const rows = existsSync(absPath) ? await readTranscriptRowsFromFile(absPath) : [];\n const llm = rowsToLlmMessages(rows);\n const keyStorePath = this.resolveStorePathForKey(key);\n await withSessionsJsonLock(keyStorePath, async (map) => {\n const e = map[key] as XopcSessionDiskEntry | undefined;\n if (!e?.pluginExtensions?.xopc?.metadata) {\n return;\n }\n patchSessionsJsonEntryStats(\n e,\n buildSessionsJsonStatsPatch(llm.length, this.estimateTokens(llm)),\n );\n map[key] = e as Record<string, unknown>;\n });\n invalidateSessionSearchIndexCache();\n });\n }\n\n /**\n * @deprecated Runtime agent turns must persist via {@link guardSessionManager} + appendMessage.\n * Retained for compaction, tests, and admin tools until fully removed.\n */\n async saveMessages(key: string, messages: AgentMessage[]): Promise<void> {\n return this.runStoreMutation(async () => {\n await this.ensureSession(key);\n const prev = await this.loadTranscriptRows(key);\n const merged = mergeLlmMessagesPreservingContextRows(prev, messages);\n await this.writeTranscriptAndSyncIndex(key, merged);\n });\n }\n\n getWindowStats(messages: AgentMessage[]) {\n return this.window.getStats(messages);\n }\n\n needsCompaction(key: string, messages: AgentMessage[], contextWindow: number) {\n return this.compactor.needsCompaction(messages, contextWindow);\n }\n\n prepareCompaction(\n key: string,\n messages: AgentMessage[],\n contextWindow: number,\n ): { needsCompaction: boolean; messages: AgentMessage[]; stats?: ReturnType<typeof this.compactor.needsCompaction> } {\n const result = this.compactor.needsCompaction(messages, contextWindow);\n return { needsCompaction: result.needed, messages, stats: result };\n }\n\n private checkpointBasename(sessionId: string): string {\n return `${sessionId}.checkpoint.`;\n }\n\n private async pruneCompactionCheckpoints(sessionId: string, sessionsDir: string): Promise<void> {\n const MAX = 15;\n const prefix = this.checkpointBasename(sessionId);\n let names: string[];\n try {\n names = await readdir(sessionsDir);\n } catch {\n return;\n }\n const candidates = names.filter((n) => n.startsWith(prefix) && n.endsWith('.jsonl'));\n if (candidates.length <= MAX) {\n return;\n }\n const stats = await Promise.all(\n candidates.map(async (name) => {\n const p = join(sessionsDir, name);\n try {\n const s = await stat(p);\n return { p, mtimeMs: s.mtimeMs };\n } catch {\n return { p: join(sessionsDir, name), mtimeMs: 0 };\n }\n }),\n );\n stats.sort((a, b) => a.mtimeMs - b.mtimeMs);\n for (let i = 0; i < stats.length - MAX; i++) {\n try {\n await unlink(stats[i]!.p);\n } catch {\n /* ignore */\n }\n }\n }\n\n private async captureCompactionCheckpoint(sessionId: string, transcriptAbs: string, sessionsDir: string): Promise<void> {\n if (!existsSync(transcriptAbs)) {\n return;\n }\n const id = randomUUID();\n const dest = join(sessionsDir, `${sessionId}.checkpoint.${id}.jsonl`);\n try {\n await copyFile(transcriptAbs, dest);\n await this.pruneCompactionCheckpoints(sessionId, sessionsDir);\n } catch (err) {\n log.warn({ err, sessionId }, 'Compaction checkpoint copy failed');\n }\n }\n\n async applyCompaction(\n key: string,\n messages: AgentMessage[],\n result: CompactionResult,\n ): Promise<AgentMessage[]> {\n const compacted = this.compactor.applyCompaction(messages, result);\n return this.runStoreMutation(async () => {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return compacted;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const abs = this.transcriptPathForEntry(entry, keySessionsDir);\n await this.captureCompactionCheckpoint(entry.sessionId, abs, keySessionsDir);\n const prev = await this.loadTranscriptRows(key);\n const merged = mergeLlmMessagesPreservingContextRows(prev, compacted);\n await this.writeTranscriptAndSyncIndex(key, merged, {\n appendCompaction: {\n at: new Date().toISOString(),\n summary: result.summary,\n firstKeptIndex: result.firstKeptIndex,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n },\n });\n const metadata = await this.getMetadata(key);\n if (metadata) {\n await this.updateMetadata(key, { compactedCount: metadata.compactedCount + 1 });\n }\n log.info(\n { key, tokensBefore: result.tokensBefore, tokensAfter: result.tokensAfter, keptMessages: compacted.length },\n 'Session compacted',\n );\n await this.injectPostCompactionContext(key);\n return compacted;\n });\n }\n\n async compact(\n key: string,\n messages: AgentMessage[],\n contextWindow: number,\n instructions?: string,\n force?: boolean,\n ): Promise<CompactionResult> {\n const result = await this.compactor.compact(messages, instructions, force);\n if (result.compacted) {\n await this.applyCompaction(key, messages, result);\n }\n return result;\n }\n\n async getCompactionStats(key: string) {\n const metadata = await this.getMetadata(key);\n if (!metadata) {\n return undefined;\n }\n return {\n compactionCount: metadata.compactedCount,\n totalTokensBefore: 0,\n totalTokensAfter: 0,\n lastCompactionAt: undefined,\n };\n }\n\n async listCompactionCheckpoints(key: string): Promise<CompactionCheckpointSummary[]> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return [];\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const sessionId = entry.sessionId;\n const prefix = this.checkpointBasename(sessionId);\n let names: string[];\n try {\n names = await readdir(keySessionsDir);\n } catch {\n return [];\n }\n const files = names.filter((n) => n.startsWith(prefix) && n.endsWith('.jsonl'));\n const rows = await Promise.all(\n files.map(async (name) => {\n const p = join(keySessionsDir, name);\n const parsed = parseCompactionCheckpointTranscriptFileName(name);\n const id = parsed?.checkpointId;\n if (!id || !normalizeCompactionCheckpointId(id)) {\n return null;\n }\n try {\n const s = await stat(p);\n return {\n id: normalizeCompactionCheckpointId(id)!,\n sizeBytes: s.size,\n modifiedAt: new Date(s.mtimeMs).toISOString(),\n } satisfies CompactionCheckpointSummary;\n } catch {\n return null;\n }\n }),\n );\n const valid = rows.filter((r): r is CompactionCheckpointSummary => r !== null);\n valid.sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt));\n return valid;\n }\n\n async getCompactionCheckpointDetail(\n key: string,\n checkpointId: string,\n ): Promise<CompactionCheckpointDetail | null> {\n const id = normalizeCompactionCheckpointId(checkpointId);\n if (!id) {\n return null;\n }\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return null;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const fname = `${this.checkpointBasename(entry.sessionId)}${id}.jsonl`;\n const cpPath = join(keySessionsDir, fname);\n if (!existsSync(cpPath)) {\n return null;\n }\n try {\n const rows = await readTranscriptRowsFromFile(cpPath);\n const llm = rowsToLlmMessages(rows);\n const s = await stat(cpPath);\n return {\n id,\n sizeBytes: s.size,\n modifiedAt: new Date(s.mtimeMs).toISOString(),\n messageCount: llm.length,\n };\n } catch {\n return null;\n }\n }\n\n async restoreCompactionCheckpoint(key: string, checkpointId: string): Promise<void> {\n const id = normalizeCompactionCheckpointId(checkpointId);\n if (!id) {\n throw new Error('Invalid checkpoint id');\n }\n return this.runStoreMutation(async () => {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n throw new Error(`Session not found: ${key}`);\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const cpPath = join(keySessionsDir, `${this.checkpointBasename(entry.sessionId)}${id}.jsonl`);\n if (!existsSync(cpPath)) {\n throw new Error(`Checkpoint not found: ${id}`);\n }\n const target = this.transcriptPathForEntry(entry, keySessionsDir);\n await copyFile(cpPath, target);\n const messages = await this.loadMessages(key);\n await this.saveMessages(key, messages);\n log.info({ key, checkpointId: id }, 'Session transcript restored from compaction checkpoint');\n });\n }\n\n async deleteSession(key: string): Promise<boolean> {\n return this.delete(key);\n }\n\n async load(key: string, options?: { fromArchive?: boolean }): Promise<AgentMessage[]> {\n return this.loadMessages(key, options);\n }\n\n /** @deprecated See {@link saveMessages}. */\n async save(key: string, messages: AgentMessage[]): Promise<void> {\n return this.saveMessages(key, messages);\n }\n\n async estimateTokenUsage(_key: string, messages: AgentMessage[]): Promise<number> {\n return this.estimateTokens(messages);\n }\n\n async searchInSession(key: string, keyword: string): Promise<Message[]> {\n const messages = await this.loadDisplayMessages(key);\n const keywordLower = keyword.toLowerCase();\n return this.convertMessages(\n messages.filter((m) => {\n const content = this.extractTextContent(this.messageContent(m));\n return content.toLowerCase().includes(keywordLower);\n }),\n );\n }\n\n async exportSession(key: string, format: ExportFormat): Promise<string> {\n const detail = await this.get(key);\n if (!detail) {\n throw new Error(`Session not found: ${key}`);\n }\n if (format === 'json') {\n const transcriptRows = await this.loadTranscriptRows(key);\n const exportData: SessionExport = {\n version: INDEX_VERSION,\n exportedAt: new Date().toISOString(),\n metadata: detail,\n messages: detail.messages,\n transcriptRows,\n };\n return JSON.stringify(exportData, null, 2);\n }\n const lines = [\n `# ${detail.name || detail.key}`,\n '',\n `- **Channel:** ${detail.sourceChannel}`,\n `- **Created:** ${detail.createdAt}`,\n `- **Messages:** ${detail.messageCount}`,\n `- **Tags:** ${detail.tags.join(', ') || 'none'}`,\n '',\n '---',\n '',\n ];\n for (const msg of detail.messages) {\n const role = msg.role === 'assistant' ? 'Assistant' : msg.role === 'user' ? 'User' : msg.role;\n lines.push(`## ${role}`, '');\n const body = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content, null, 2);\n lines.push(body, '', '---', '');\n }\n return lines.join('\\n');\n }\n\n async getStats(): Promise<GlobalSessionStats> {\n const list = await this.list({ limit: 100000 });\n const sessions = list.items;\n const byChannel: Record<string, number> = {};\n for (const s of sessions) {\n byChannel[s.sourceChannel] = (byChannel[s.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((s) => s.status === SessionStatus.ACTIVE || s.status === SessionStatus.IDLE).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\n async archiveOld(olderThanDays: number): Promise<number> {\n const cutoff = new Date();\n cutoff.setDate(cutoff.getDate() - olderThanDays);\n const list = await this.list({ limit: 100000 });\n let archived = 0;\n for (const session of list.items) {\n if (session.status !== SessionStatus.ARCHIVED && session.status !== SessionStatus.PINNED) {\n const lastAccess = new Date(session.lastAccessedAt);\n if (lastAccess < cutoff) {\n await this.archive(session.key);\n archived++;\n }\n }\n }\n return archived;\n }\n\n estimateTokens(messages: AgentMessage[]): number {\n let total = 0;\n for (const msg of messages) {\n total += Math.ceil(this.extractTextContent(this.messageContent(msg)).length / 4);\n }\n return total;\n }\n\n private messageContent(msg: AgentMessage): unknown {\n return (msg as { content?: unknown }).content;\n }\n\n private isCompactionSummaryMessage(msg: AgentMessage): boolean {\n if (msg.role !== 'user') {\n return false;\n }\n const text = this.extractTextContent(this.messageContent(msg)).trim();\n return /^\\[Previous conversation summary\\]/i.test(text);\n }\n\n private displayMessageIdentity(message: AgentMessage): string {\n const record = message as unknown as Record<string, unknown>;\n return JSON.stringify({\n role: message.role,\n timestamp: record.timestamp,\n toolCallId: record.toolCallId ?? record.tool_call_id,\n toolName: record.toolName,\n content: this.messageContent(message),\n });\n }\n\n private extractTextContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n const parts: string[] = [];\n for (const item of content) {\n if (typeof item !== 'object' || item === null || !('type' in item)) {\n continue;\n }\n const c = item as { type?: string; text?: string; name?: string };\n if (c.type === 'text' && typeof c.text === 'string') {\n parts.push(c.text);\n } else if (c.type === 'toolCall' || c.type === 'tool_use') {\n parts.push(c.name ? `[${c.name}]` : '');\n }\n }\n return parts.join('');\n }\n return '';\n }\n\n private async injectPostCompactionContext(key: string): Promise<void> {\n const contextText = readPostCompactionContext({\n cfg: this.options.config,\n sessionKey: key,\n });\n if (!contextText?.trim()) {\n return;\n }\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const abs = this.transcriptPathForEntry(entry, keySessionsDir);\n const workspaceDir = resolveEffectiveAgentProfileForSession(this.options.config, key).resolvedWorkspacePath;\n try {\n await appendPiTranscriptContextEntry({\n absPath: abs,\n cwd: workspaceDir,\n sessionKey: key,\n entry: {\n kind: 'context',\n id: `post-compaction-${Date.now()}`,\n text: contextText,\n createdAt: new Date().toISOString(),\n },\n });\n } catch (err) {\n log.warn({ err, key }, 'Post-compaction context injection failed');\n }\n }\n\n private convertMessages(messages: AgentMessage[]): Message[] {\n return messages.map((m: AgentMessage & Record<string, unknown>) => {\n const c = this.messageContent(m);\n const content: string | unknown[] =\n typeof c === 'string' ? c : Array.isArray(c) ? c : this.extractTextContent(c);\n const row: Message = {\n role: m.role as Message['role'],\n content,\n timestamp: m.timestamp ? new Date(m.timestamp as string | number).toISOString() : undefined,\n tool_call_id: (m.tool_call_id as string | undefined) || (m.toolCallId as string | undefined),\n tool_calls: m.tool_calls as Message['tool_calls'],\n name: m.name as string | undefined,\n };\n if (Array.isArray(m.attachments) && m.attachments.length > 0) {\n row.attachments = m.attachments as Message['attachments'];\n }\n const rawUsage = m.usage as { input?: number; output?: number; totalTokens?: number; total?: number } | undefined;\n if (rawUsage && typeof rawUsage === 'object') {\n const inputTokens = typeof rawUsage.input === 'number' ? rawUsage.input : undefined;\n const outputTokens = typeof rawUsage.output === 'number' ? rawUsage.output : undefined;\n const totalTokens = typeof rawUsage.totalTokens === 'number'\n ? rawUsage.totalTokens\n : typeof rawUsage.total === 'number'\n ? rawUsage.total\n : undefined;\n if (inputTokens != null || outputTokens != null || totalTokens != null) {\n row.usage = { inputTokens, outputTokens, totalTokens };\n }\n }\n return row;\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"store.js","names":["parseRoutingSessionKey"],"sources":["../../../src/session/store.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { copyFile, mkdir, readdir, stat, unlink } from 'fs/promises';\nimport { join } from 'path';\nimport { existsSync } from 'fs';\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { CompactionEntry } from '@earendil-works/pi-coding-agent';\n\nimport { loadEntriesFromFile } from './parity/load-jsonl-entries.js';\n\nimport type { Config } from '../config/schema.js';\nimport { resolveSessionsDir, FILENAMES } from '../config/paths.js';\nimport { resolveDefaultAgentId, listAgentEntries } from '../agent/agent-scope.js';\nimport { resolveEffectiveAgentProfileForSession } from '../config/agent-profile.js';\nimport { readPostCompactionContext } from '../agent/reply/post-compaction-context.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../routing/session-key.js';\nimport { createLogger } from '../utils/logger.js';\nimport { SessionCompactor, type CompactionConfig, type CompactionResult } from '../agent/memory/compaction.js';\nimport { SlidingWindow, type WindowConfig } from '../agent/memory/window.js';\nimport { invalidateSessionSearchIndexCache } from './search-index-cache.js';\nimport type { TranscriptCompactionRecord, XopcSessionTranscriptV1 } from './transcript-format.js';\nimport {\n mergeLlmMessagesPreservingContextRows,\n type TranscriptStoredRow,\n type XopcTranscriptContextEntry,\n} from './session-context-for-llm.js';\nimport { normalizeCompactionCheckpointId } from './compaction-checkpoints.js';\nimport type {\n SessionMetadata,\n SessionDetail,\n SessionListQuery,\n PaginatedResult,\n GlobalSessionStats,\n ExportFormat,\n SessionExport,\n SessionTranscriptSummary,\n CompactionCheckpointSummary,\n CompactionCheckpointDetail,\n} from './types.js';\nimport { SessionStatus } from './types.js';\nimport type { Message } from './types.js';\nimport { parseCompactionCheckpointTranscriptFileName } from './parity/artifacts.js';\nimport { archiveFileOnDisk, resolveSessionFilePath, resolveSessionTranscriptPathInDir } from './parity/transcript-paths.js';\nimport { validateSessionId } from './parity/session-id.js';\nimport { readSessionsJsonFile, withSessionsJsonLock } from './parity/sessions-json-file.js';\nimport {\n buildSessionsJsonStatsPatch,\n incrementSessionsJsonStatsForAppend,\n isAppendOnlyLlmTranscriptMessage,\n patchSessionsJsonEntryStats,\n} from './parity/sessions-json-patch.js';\nimport {\n countTranscriptMessageRows,\n readDisplayMessagePageFromTranscriptFile,\n} from './parity/transcript-pagination.js';\nimport type { XopcSessionDiskEntry } from './parity/xopc-session-disk-entry.js';\nimport {\n appendPiTranscriptContextEntry,\n persistMergedTranscriptRows,\n readTranscriptRowsFromFile,\n rowsToLlmMessages,\n writeTranscriptJsonl,\n} from './parity/jsonl-transcript-io.js';\nimport type { SessionTranscriptUpdate } from './transcript-events.js';\n\nconst log = createLogger('SessionStore');\n\nconst INDEX_VERSION = '1.0';\nconst DELETED_MARKER = '.jsonl.deleted.';\n\nexport interface SessionStoreOptions {\n config: Config;\n agentId?: string;\n sessionsDir?: string;\n}\n\nexport class SessionStore {\n private sessionsDir: string;\n private archiveDir: string;\n private storePath: string;\n private window: SlidingWindow;\n private compactor: SessionCompactor;\n private storeMutationDepth = 0;\n private storeMutationChain: Promise<void> = Promise.resolve();\n /** Cache of per-agent sessions dirs to avoid re-resolution on every call. */\n private agentSessionsDirCache = new Map<string, string>();\n\n constructor(\n private options: SessionStoreOptions,\n windowConfig?: Partial<WindowConfig>,\n compactionConfig?: Partial<CompactionConfig>,\n ) {\n const agentId = options.agentId ?? resolveDefaultAgentId(options.config);\n this.sessionsDir = options.sessionsDir ?? resolveSessionsDir(options.config, agentId);\n this.archiveDir = join(this.sessionsDir, 'archive');\n this.storePath = join(this.sessionsDir, FILENAMES.SESSIONS_MAP);\n this.window = new SlidingWindow(windowConfig);\n this.compactor = new SessionCompactor(compactionConfig);\n }\n\n getSessionsRoot(): string {\n return this.sessionsDir;\n }\n\n /**\n * OpenClaw-aligned: resolve the sessions directory for a given session key.\n * Extracts agentId from the session key and routes to `agents/<agentId>/sessions/`.\n * Falls back to the default sessions directory when agentId cannot be parsed\n * or when `sessionsDir` was explicitly provided in options.\n */\n private resolveSessionsDirForKey(sessionKey: string): string {\n if (this.options.sessionsDir) {\n return this.sessionsDir;\n }\n const parsed = parseRoutingSessionKey(sessionKey);\n if (!parsed) {\n return this.sessionsDir;\n }\n const agentId = parsed.agentId;\n const cached = this.agentSessionsDirCache.get(agentId);\n if (cached) {\n return cached;\n }\n const resolved = resolveSessionsDir(this.options.config, agentId);\n this.agentSessionsDirCache.set(agentId, resolved);\n return resolved;\n }\n\n private resolveStorePathForKey(sessionKey: string): string {\n return join(this.resolveSessionsDirForKey(sessionKey), FILENAMES.SESSIONS_MAP);\n }\n\n private async runStoreMutation<T>(fn: () => Promise<T>): Promise<T> {\n if (this.storeMutationDepth > 0) {\n return fn();\n }\n const run = this.storeMutationChain.then(async () => {\n this.storeMutationDepth++;\n try {\n return await fn();\n } finally {\n this.storeMutationDepth--;\n }\n });\n this.storeMutationChain = run.then(() => undefined).catch(() => undefined);\n return run as Promise<T>;\n }\n\n async initialize(): Promise<void> {\n await mkdir(this.sessionsDir, { recursive: true });\n await mkdir(this.archiveDir, { recursive: true });\n if (!existsSync(this.storePath)) {\n await withSessionsJsonLock(this.storePath, async () => undefined);\n }\n log.debug('Session store initialized (sessions.json + JSONL)');\n }\n\n private transcriptPathForEntry(entry: XopcSessionDiskEntry, sessionsDir?: string): string {\n return resolveSessionFilePath(entry.sessionId, entry, { sessionsDir: sessionsDir ?? this.sessionsDir });\n }\n\n private async readMapForKey(sessionKey: string): Promise<Record<string, XopcSessionDiskEntry>> {\n const storePath = this.resolveStorePathForKey(sessionKey);\n return readSessionsJsonFile<XopcSessionDiskEntry>(storePath);\n }\n\n private async readMap(): Promise<Record<string, XopcSessionDiskEntry>> {\n return readSessionsJsonFile<XopcSessionDiskEntry>(this.storePath);\n }\n\n /**\n * OpenClaw-aligned: read sessions.json from ALL known agents and merge into a single map.\n * Used by aggregation queries (list, getByAgent, getByAccount, etc.) so the gateway UI\n * can display sessions across all agents.\n */\n private async readAllMaps(): Promise<Record<string, XopcSessionDiskEntry>> {\n if (this.options.sessionsDir) {\n return this.readMap();\n }\n const agents = listAgentEntries(this.options.config);\n const defaultId = resolveDefaultAgentId(this.options.config);\n const agentIds = new Set<string>([defaultId, ...agents.map((a) => a.id)]);\n\n const merged: Record<string, XopcSessionDiskEntry> = {};\n for (const id of agentIds) {\n const dir = resolveSessionsDir(this.options.config, id);\n const path = join(dir, FILENAMES.SESSIONS_MAP);\n if (!existsSync(path)) {\n continue;\n }\n const map = await readSessionsJsonFile<XopcSessionDiskEntry>(path);\n Object.assign(merged, map);\n }\n return merged;\n }\n\n private async getDiskEntry(sessionKey: string): Promise<XopcSessionDiskEntry | undefined> {\n const map = await this.readMapForKey(sessionKey);\n return map[sessionKey];\n }\n\n private buildDefaultMetadata(key: string): SessionMetadata {\n const { channel, chatId } = this.parseSessionKey(key);\n const routing = this.extractRoutingFromKey(key);\n const isCronSession = channel === 'cron';\n const isHeartbeatSession = channel === 'heartbeat';\n const now = new Date().toISOString();\n return {\n key,\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\n ? { sessionType: 'cron', customData: { cronJobId: chatId } }\n : {}),\n ...(isHeartbeatSession\n ? { sessionType: 'heartbeat', customData: { heartbeatTarget: chatId } }\n : {}),\n stats: { messageCount: 0, tokenCount: 0 },\n };\n }\n\n private parseSessionKey(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\n private extractRoutingFromKey(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\n /** Resolve on-disk transcript path; creates session row + empty JSONL when missing. */\n async resolveTranscriptPath(\n sessionKey: string,\n ): Promise<{ sessionId: string; absPath: string; sessionsDir: string }> {\n const entry = await this.ensureSession(sessionKey);\n const sessionsDir = this.resolveSessionsDirForKey(sessionKey);\n const absPath = this.transcriptPathForEntry(entry, sessionsDir);\n return { sessionId: entry.sessionId, absPath, sessionsDir };\n }\n\n /** Ensure sessions.json has an entry and transcript file exist for `sessionKey`. */\n private async ensureSession(sessionKey: string): Promise<XopcSessionDiskEntry> {\n const keyStorePath = this.resolveStorePathForKey(sessionKey);\n const keySessionsDir = this.resolveSessionsDirForKey(sessionKey);\n await mkdir(keySessionsDir, { recursive: true });\n return withSessionsJsonLock(keyStorePath, async (map) => {\n const existing = map[sessionKey] as XopcSessionDiskEntry | undefined;\n if (existing?.pluginExtensions?.xopc?.metadata) {\n return existing;\n }\n let entry = existing;\n if (!entry) {\n const sessionId = randomUUID();\n validateSessionId(sessionId);\n const sessionFile = `${sessionId}.jsonl`;\n const now = Date.now();\n const metadata = this.buildDefaultMetadata(sessionKey);\n metadata.transcriptId = sessionId;\n entry = {\n sessionId,\n updatedAt: now,\n sessionStartedAt: now,\n sessionFile,\n pluginExtensions: { xopc: { metadata } },\n };\n map[sessionKey] = entry as Record<string, unknown>;\n const abs = resolveSessionTranscriptPathInDir(sessionId, keySessionsDir);\n await writeTranscriptJsonl({\n absPath: abs,\n sessionId,\n cwd: process.cwd(),\n rows: [],\n });\n } else if (!entry.pluginExtensions?.xopc?.metadata) {\n const metadata = this.buildDefaultMetadata(sessionKey);\n metadata.transcriptId = entry.sessionId;\n entry.pluginExtensions = { xopc: { metadata } };\n map[sessionKey] = entry as Record<string, unknown>;\n }\n return entry!;\n });\n }\n\n private metadataFromEntry(sessionKey: string, entry: XopcSessionDiskEntry): SessionMetadata {\n const base = entry.pluginExtensions?.xopc?.metadata ?? this.buildDefaultMetadata(sessionKey);\n const { channel: keySource, chatId: keyChatId } = this.parseSessionKey(sessionKey);\n const diskSc = typeof base.sourceChannel === 'string' ? base.sourceChannel.trim() : '';\n const diskChat = typeof base.sourceChatId === 'string' ? base.sourceChatId.trim() : '';\n return {\n ...base,\n key: sessionKey,\n transcriptId: entry.sessionId,\n sourceChannel: diskSc || keySource,\n sourceChatId: diskChat || keyChatId,\n };\n }\n\n async getByAgent(agentId: string): Promise<SessionMetadata[]> {\n const map = await this.readAllMaps();\n const out: SessionMetadata[] = [];\n for (const [key, e] of Object.entries(map)) {\n const m = this.metadataFromEntry(key, e);\n if (m.routing?.agentId?.toLowerCase() === agentId.toLowerCase()) {\n out.push(m);\n }\n }\n return out;\n }\n\n async getByAccount(accountId: string): Promise<SessionMetadata[]> {\n const map = await this.readAllMaps();\n const out: SessionMetadata[] = [];\n for (const [key, e] of Object.entries(map)) {\n const m = this.metadataFromEntry(key, e);\n if (m.routing?.accountId === accountId) {\n out.push(m);\n }\n }\n return out;\n }\n\n async getByPeer(peerKind: string, peerId: string): Promise<SessionMetadata[]> {\n const map = await this.readAllMaps();\n const out: SessionMetadata[] = [];\n for (const [key, e] of Object.entries(map)) {\n const m = this.metadataFromEntry(key, e);\n if (m.routing?.peerKind === peerKind && m.routing.peerId === peerId) {\n out.push(m);\n }\n }\n return out;\n }\n\n async getMainSession(channel: string, accountId: string): Promise<SessionMetadata | null> {\n const map = await this.readAllMaps();\n for (const [key, e] of Object.entries(map)) {\n const m = this.metadataFromEntry(key, e);\n if (\n m.routing?.source === channel &&\n m.routing.accountId === accountId &&\n m.routing.peerKind === 'dm' &&\n m.routing.peerId === 'main'\n ) {\n return m;\n }\n }\n return null;\n }\n\n async refreshIndex(): Promise<void> {\n /* no-op: sessions.json is authoritative */\n }\n\n async list(query: SessionListQuery = {}): Promise<PaginatedResult<SessionMetadata>> {\n const map = await this.readAllMaps();\n let sessions = Object.entries(map).map(([k, e]) => this.metadataFromEntry(k, e));\n\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status];\n sessions = sessions.filter((s) => statuses.includes(s.status));\n }\n if (query.channel) {\n const rawChannels = query.channel\n .split(',')\n .map((c) => c.trim().toLowerCase())\n .filter(Boolean);\n /**\n * `ui` is a legacy console source; treat as webchat when filtering web sessions.\n * `webui` matches slash-command normalization to `gateway` (see `chat-commands/session-key.ts`).\n */\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 sessions = [];\n } else if (channels.length === 1) {\n const ch = channels[0]!;\n sessions = sessions.filter((s) => (s.sourceChannel ?? '').toLowerCase() === ch);\n } else {\n sessions = sessions.filter((s) => channels.includes((s.sourceChannel ?? '').toLowerCase()));\n }\n }\n if (query.tags?.length) {\n sessions = sessions.filter((s) => query.tags!.some((t) => s.tags.includes(t)));\n }\n if (query.search) {\n const q = query.search.toLowerCase();\n sessions = sessions.filter(\n (s) =>\n s.key.toLowerCase().includes(q) ||\n (s.name?.toLowerCase().includes(q) ?? false) ||\n s.tags.some((t) => t.toLowerCase().includes(q)),\n );\n }\n\n const sortBy = query.sortBy || 'updatedAt';\n const sortOrder = query.sortOrder || 'desc';\n sessions.sort((a, b) => {\n const av = a[sortBy];\n const bv = b[sortBy];\n const c = av < bv ? -1 : av > bv ? 1 : 0;\n return sortOrder === 'asc' ? c : -c;\n });\n\n const total = sessions.length;\n const limit = query.limit || 50;\n const offset = query.offset || 0;\n const items = sessions.slice(offset, offset + limit);\n return { items, total, limit, offset, hasMore: offset + limit < total };\n }\n\n async get(\n key: string,\n options?: { includeTranscriptSummary?: boolean; includeTranscriptRows?: boolean },\n ): Promise<SessionDetail | null> {\n const metadata = await this.getMetadata(key);\n if (!metadata) {\n return null;\n }\n const messages = await this.loadDisplayMessages(key);\n const detail = await this.buildSessionDetail(key, metadata, messages, options);\n return detail;\n }\n\n async getMessagePage(\n key: string,\n options: {\n offset?: number;\n limit?: number;\n before?: string;\n includeTranscriptSummary?: boolean;\n includeTranscriptRows?: boolean;\n } = {},\n ): Promise<{\n session: SessionDetail;\n pagination: {\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n before?: string;\n nextBeforeCursor?: string;\n };\n } | null> {\n const metadata = await this.getMetadata(key);\n if (!metadata) {\n return null;\n }\n\n const limit = Math.min(200, Math.max(1, Math.trunc(options.limit ?? 50)));\n const offset = Math.max(0, Math.trunc(options.offset ?? 0));\n const parsedBefore = options.before ? Number.parseInt(options.before, 10) : undefined;\n const hasBeforeCursor = parsedBefore !== undefined && Number.isFinite(parsedBefore);\n\n const checkpoints = await this.listCompactionCheckpoints(key);\n if (checkpoints.length === 0) {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return null;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const primary = this.transcriptPathForEntry(entry, keySessionsDir);\n const page = await readDisplayMessagePageFromTranscriptFile(primary, {\n limit,\n offset: hasBeforeCursor ? undefined : offset,\n beforeIndex: hasBeforeCursor ? parsedBefore : undefined,\n });\n const session = await this.buildSessionDetail(key, metadata, page.messages, options);\n const nextBeforeCursor = page.startIndex > 0 ? String(page.startIndex) : undefined;\n\n return {\n session,\n pagination: {\n total: page.total,\n limit,\n offset,\n hasMore: page.startIndex > 0,\n ...(hasBeforeCursor ? { before: String(page.endIndex) } : {}),\n ...(nextBeforeCursor ? { nextBeforeCursor } : {}),\n },\n };\n }\n\n const messages = await this.loadDisplayMessages(key);\n const total = messages.length;\n const endExclusive = hasBeforeCursor\n ? Math.min(total, Math.max(0, Math.trunc(parsedBefore!)))\n : Math.max(0, total - offset);\n const startInclusive = Math.max(0, endExclusive - limit);\n const pageMessages = messages.slice(startInclusive, endExclusive);\n const session = await this.buildSessionDetail(key, metadata, pageMessages, options);\n const nextBeforeCursor = startInclusive > 0 ? String(startInclusive) : undefined;\n\n return {\n session,\n pagination: {\n total,\n limit,\n offset,\n hasMore: startInclusive > 0,\n ...(hasBeforeCursor ? { before: String(endExclusive) } : {}),\n ...(nextBeforeCursor ? { nextBeforeCursor } : {}),\n },\n };\n }\n\n private async buildSessionDetail(\n key: string,\n metadata: SessionMetadata,\n messages: AgentMessage[],\n options?: { includeTranscriptSummary?: boolean; includeTranscriptRows?: boolean },\n ): Promise<SessionDetail> {\n let transcriptSummary: SessionTranscriptSummary | undefined;\n if (options?.includeTranscriptSummary) {\n const env = await this.loadTranscriptDocument(key);\n if (env) {\n transcriptSummary = {\n id: env.id,\n version: env.version,\n createdAt: env.createdAt,\n updatedAt: env.updatedAt,\n compactionCount: env.compactions?.length ?? 0,\n };\n }\n }\n let transcriptRows: TranscriptStoredRow[] | undefined;\n if (options?.includeTranscriptRows) {\n transcriptRows = await this.loadTranscriptRows(key);\n }\n return {\n ...metadata,\n messages: this.convertMessages(messages),\n ...(transcriptSummary ? { transcriptSummary } : {}),\n ...(transcriptRows !== undefined ? { transcriptRows } : {}),\n };\n }\n\n async loadTranscriptRows(key: string): Promise<TranscriptStoredRow[]> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return [];\n }\n const path = this.transcriptPathForEntry(entry, this.resolveSessionsDirForKey(key));\n return readTranscriptRowsFromFile(path);\n }\n\n async getMetadata(key: string): Promise<SessionMetadata | null> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return null;\n }\n return this.metadataFromEntry(key, entry);\n }\n\n async updateMetadata(key: string, updates: Partial<SessionMetadata>): Promise<void> {\n return this.runStoreMutation(async () => {\n const keyStorePath = this.resolveStorePathForKey(key);\n await withSessionsJsonLock(keyStorePath, async (map) => {\n const entry = map[key] as XopcSessionDiskEntry | undefined;\n if (!entry?.pluginExtensions?.xopc?.metadata) {\n throw new Error(`Session not found: ${key}`);\n }\n const meta = { ...entry.pluginExtensions.xopc.metadata, ...updates, updatedAt: new Date().toISOString() };\n entry.pluginExtensions.xopc.metadata = meta;\n entry.updatedAt = Date.now();\n map[key] = entry as Record<string, unknown>;\n });\n invalidateSessionSearchIndexCache();\n log.debug({ key, updates }, 'Session metadata updated');\n });\n }\n\n /**\n * Reset transcript for an existing session key: archive the current JSONL as\n * `*.reset.*`, assign a new `sessionId`, and preserve per-session overrides\n * on the disk entry (thinking/verbose) and in `sessions/config/*.json`.\n */\n async reset(key: string): Promise<{ sessionId: string; previousSessionId: string } | null> {\n return this.runStoreMutation(async () => {\n const existing = await this.getDiskEntry(key);\n if (!existing?.pluginExtensions?.xopc?.metadata) {\n return null;\n }\n\n const previousSessionId = existing.sessionId;\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const abs = this.transcriptPathForEntry(existing, keySessionsDir);\n if (existsSync(abs)) {\n try {\n archiveFileOnDisk(abs, 'reset');\n } catch (err) {\n log.warn({ err, key }, 'Transcript archive on reset failed');\n }\n }\n\n const sessionId = randomUUID();\n validateSessionId(sessionId);\n const now = Date.now();\n const nextAbs = resolveSessionTranscriptPathInDir(sessionId, keySessionsDir);\n await writeTranscriptJsonl({\n absPath: nextAbs,\n sessionId,\n cwd: process.cwd(),\n rows: [],\n });\n\n const keyStorePath = this.resolveStorePathForKey(key);\n await withSessionsJsonLock(keyStorePath, async (map) => {\n const e = map[key] as XopcSessionDiskEntry | undefined;\n if (!e?.pluginExtensions?.xopc?.metadata) {\n return;\n }\n e.sessionId = sessionId;\n e.sessionFile = `${sessionId}.jsonl`;\n e.updatedAt = now;\n e.sessionStartedAt = now;\n e.lastInteractionAt = undefined;\n patchSessionsJsonEntryStats(e, buildSessionsJsonStatsPatch(0, 0));\n const meta = e.pluginExtensions.xopc.metadata;\n meta.transcriptId = sessionId;\n meta.updatedAt = new Date(now).toISOString();\n map[key] = e as Record<string, unknown>;\n });\n\n invalidateSessionSearchIndexCache();\n log.info({ key, previousSessionId, sessionId }, 'Session reset');\n return { sessionId, previousSessionId };\n });\n }\n\n async delete(key: string): Promise<boolean> {\n return this.runStoreMutation(async () => {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return false;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const abs = this.transcriptPathForEntry(entry, keySessionsDir);\n const keyStorePath = this.resolveStorePathForKey(key);\n await withSessionsJsonLock(keyStorePath, async (map) => {\n delete map[key];\n });\n try {\n if (existsSync(abs)) {\n archiveFileOnDisk(abs, 'deleted');\n }\n } catch (err) {\n log.warn({ err, key }, 'Transcript archive on delete failed');\n }\n invalidateSessionSearchIndexCache();\n log.info({ key }, 'Session deleted');\n return true;\n });\n }\n\n async deleteMany(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n const success: string[] = [];\n const failed: string[] = [];\n for (const key of keys) {\n try {\n if (await this.delete(key)) {\n success.push(key);\n } else {\n failed.push(key);\n }\n } catch {\n failed.push(key);\n }\n }\n return { success, failed };\n }\n\n async setStatus(key: string, status: SessionStatus): Promise<void> {\n await this.updateMetadata(key, { status });\n if (status === SessionStatus.ARCHIVED) {\n await this.moveToArchive(key);\n } else {\n await this.moveFromArchive(key);\n }\n }\n\n async archive(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ARCHIVED);\n }\n\n async unarchive(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ACTIVE);\n }\n\n async pin(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.PINNED);\n }\n\n async unpin(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ACTIVE);\n }\n\n private async moveToArchive(key: string): Promise<void> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const abs = this.transcriptPathForEntry(entry, keySessionsDir);\n if (existsSync(abs)) {\n try {\n archiveFileOnDisk(abs, 'deleted');\n } catch (err) {\n log.warn({ err, key }, 'Archive transcript rename failed');\n }\n }\n }\n\n private async findMostRecentDeletedTranscript(sessionId: string, sessionsDir: string): Promise<string | null> {\n let names: string[];\n try {\n names = await readdir(sessionsDir);\n } catch {\n return null;\n }\n const prefix = `${sessionId}${DELETED_MARKER}`;\n const hits = names.filter((n) => n.startsWith(prefix) && n.endsWith('Z'));\n hits.sort().reverse();\n const first = hits[0];\n return first ? join(sessionsDir, first) : null;\n }\n\n private async moveFromArchive(key: string): Promise<void> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const target = resolveSessionTranscriptPathInDir(entry.sessionId, keySessionsDir);\n if (existsSync(target)) {\n return;\n }\n const src = await this.findMostRecentDeletedTranscript(entry.sessionId, keySessionsDir);\n if (!src) {\n return;\n }\n try {\n const { rename } = await import('fs/promises');\n await rename(src, target);\n } catch (err) {\n log.warn({ err, key, src, target }, 'Unarchive transcript rename failed');\n }\n }\n\n async loadMessages(key: string, options?: { fromArchive?: boolean }): Promise<AgentMessage[]> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return [];\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const primary = this.transcriptPathForEntry(entry, keySessionsDir);\n if (existsSync(primary)) {\n const rows = await readTranscriptRowsFromFile(primary);\n return rowsToLlmMessages(rows);\n }\n if (options?.fromArchive) {\n const archived = await this.findMostRecentDeletedTranscript(entry.sessionId, keySessionsDir);\n if (!archived) {\n return [];\n }\n const rows = await readTranscriptRowsFromFile(archived);\n return rowsToLlmMessages(rows);\n }\n return [];\n }\n\n private async loadDisplayMessages(key: string): Promise<AgentMessage[]> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return [];\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const primary = this.transcriptPathForEntry(entry, keySessionsDir);\n const transcriptPaths: string[] = [];\n const checkpoints = await this.listCompactionCheckpoints(key);\n for (const checkpoint of [...checkpoints].reverse()) {\n transcriptPaths.push(join(keySessionsDir, `${this.checkpointBasename(entry.sessionId)}${checkpoint.id}.jsonl`));\n }\n transcriptPaths.push(primary);\n\n const messages: AgentMessage[] = [];\n const seenMessages = new Set<string>();\n for (const transcriptPath of transcriptPaths) {\n if (!existsSync(transcriptPath)) {\n continue;\n }\n const rows = await readTranscriptRowsFromFile(transcriptPath);\n for (const message of rowsToLlmMessages(rows)) {\n if (this.isCompactionSummaryMessage(message)) {\n continue;\n }\n const key = this.displayMessageIdentity(message);\n if (seenMessages.has(key)) {\n continue;\n }\n seenMessages.add(key);\n messages.push(message);\n }\n }\n return messages;\n }\n\n async loadTranscriptDocument(key: string): Promise<XopcSessionTranscriptV1 | null> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return null;\n }\n const path = this.transcriptPathForEntry(entry, this.resolveSessionsDirForKey(key));\n if (!existsSync(path)) {\n return null;\n }\n const entries = loadEntriesFromFile(path);\n const header = entries.find((e) => e.type === 'session');\n if (!header || typeof (header as { id?: unknown }).id !== 'string') {\n return null;\n }\n const sessionHeader = header as { type: 'session'; id: string; timestamp?: string };\n const rows = await readTranscriptRowsFromFile(path);\n const compactions = entries\n .filter((e): e is CompactionEntry => e.type === 'compaction')\n .map((c) => ({\n at: c.timestamp,\n summary: c.summary,\n firstKeptIndex: Number.parseInt(String(c.firstKeptEntryId), 10) || 0,\n tokensBefore: c.tokensBefore,\n tokensAfter:\n typeof c.details === 'object' &&\n c.details &&\n 'tokensAfter' in c.details &&\n typeof (c.details as { tokensAfter?: unknown }).tokensAfter === 'number'\n ? (c.details as { tokensAfter: number }).tokensAfter\n : 0,\n }));\n return {\n type: 'xopc_session_transcript',\n version: 1,\n id: sessionHeader.id,\n createdAt: sessionHeader.timestamp ?? new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n messages: rows,\n ...(compactions.length > 0 ? { compactions } : {}),\n };\n }\n\n private async writeTranscriptAndSyncIndex(\n key: string,\n rows: TranscriptStoredRow[],\n opts?: { appendCompaction?: TranscriptCompactionRecord },\n ): Promise<void> {\n const entry = await this.ensureSession(key);\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const abs = this.transcriptPathForEntry(entry, keySessionsDir);\n const llm = rowsToLlmMessages(rows);\n await persistMergedTranscriptRows({\n absPath: abs,\n sessionId: entry.sessionId,\n cwd: process.cwd(),\n rows,\n appendCompaction: opts?.appendCompaction,\n });\n const keyStorePath = this.resolveStorePathForKey(key);\n await withSessionsJsonLock(keyStorePath, async (map) => {\n const e = map[key] as XopcSessionDiskEntry | undefined;\n if (!e?.pluginExtensions?.xopc?.metadata) {\n return;\n }\n patchSessionsJsonEntryStats(\n e,\n buildSessionsJsonStatsPatch(llm.length, this.estimateTokens(llm)),\n );\n map[key] = e as Record<string, unknown>;\n });\n invalidateSessionSearchIndexCache();\n }\n\n /** Incremental sessions.json stats after guard append (OpenClaw transcript-events). */\n async syncSessionsJsonFromTranscriptUpdate(update: SessionTranscriptUpdate): Promise<void> {\n const sessionKey = update.sessionKey?.trim();\n if (!sessionKey || !existsSync(update.sessionFile)) {\n return;\n }\n return this.runStoreMutation(async () => {\n const keyStorePath = this.resolveStorePathForKey(sessionKey);\n await withSessionsJsonLock(keyStorePath, async (map) => {\n const e = map[sessionKey] as XopcSessionDiskEntry | undefined;\n if (!e?.pluginExtensions?.xopc?.metadata) {\n return;\n }\n\n if (update.message && isAppendOnlyLlmTranscriptMessage(update.message)) {\n incrementSessionsJsonStatsForAppend(e);\n map[sessionKey] = e as Record<string, unknown>;\n return;\n }\n\n const messageCount = await countTranscriptMessageRows(update.sessionFile);\n const rows = await readTranscriptRowsFromFile(update.sessionFile);\n const llm = rowsToLlmMessages(rows);\n patchSessionsJsonEntryStats(\n e,\n buildSessionsJsonStatsPatch(messageCount, this.estimateTokens(llm)),\n );\n map[sessionKey] = e as Record<string, unknown>;\n });\n invalidateSessionSearchIndexCache();\n });\n }\n\n async appendTranscriptContextEntry(\n key: string,\n entry: Omit<XopcTranscriptContextEntry, 'kind'> & Partial<Pick<XopcTranscriptContextEntry, 'kind'>>,\n ): Promise<void> {\n return this.runStoreMutation(async () => {\n await this.ensureSession(key);\n const disk = await this.getDiskEntry(key);\n if (!disk) {\n return;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const absPath = this.transcriptPathForEntry(disk, keySessionsDir);\n const row: XopcTranscriptContextEntry = {\n kind: 'context',\n id: typeof entry.id === 'string' ? entry.id : undefined,\n text: typeof entry.text === 'string' ? entry.text : undefined,\n data: entry.data,\n createdAt: entry.createdAt ?? new Date().toISOString(),\n };\n await appendPiTranscriptContextEntry({\n absPath,\n cwd: process.cwd(),\n entry: row,\n sessionKey: key,\n });\n const rows = existsSync(absPath) ? await readTranscriptRowsFromFile(absPath) : [];\n const llm = rowsToLlmMessages(rows);\n const keyStorePath = this.resolveStorePathForKey(key);\n await withSessionsJsonLock(keyStorePath, async (map) => {\n const e = map[key] as XopcSessionDiskEntry | undefined;\n if (!e?.pluginExtensions?.xopc?.metadata) {\n return;\n }\n patchSessionsJsonEntryStats(\n e,\n buildSessionsJsonStatsPatch(llm.length, this.estimateTokens(llm)),\n );\n map[key] = e as Record<string, unknown>;\n });\n invalidateSessionSearchIndexCache();\n });\n }\n\n /**\n * Bulk write entry point used by compaction, tests, and admin tools.\n * Runtime agent turns must persist via {@link guardSessionManager} + appendMessage.\n */\n async saveMessages(key: string, messages: AgentMessage[]): Promise<void> {\n return this.runStoreMutation(async () => {\n await this.ensureSession(key);\n const prev = await this.loadTranscriptRows(key);\n const merged = mergeLlmMessagesPreservingContextRows(prev, messages);\n await this.writeTranscriptAndSyncIndex(key, merged);\n });\n }\n\n getWindowStats(messages: AgentMessage[]) {\n return this.window.getStats(messages);\n }\n\n needsCompaction(key: string, messages: AgentMessage[], contextWindow: number) {\n return this.compactor.needsCompaction(messages, contextWindow);\n }\n\n prepareCompaction(\n key: string,\n messages: AgentMessage[],\n contextWindow: number,\n ): { needsCompaction: boolean; messages: AgentMessage[]; stats?: ReturnType<typeof this.compactor.needsCompaction> } {\n const result = this.compactor.needsCompaction(messages, contextWindow);\n return { needsCompaction: result.needed, messages, stats: result };\n }\n\n private checkpointBasename(sessionId: string): string {\n return `${sessionId}.checkpoint.`;\n }\n\n private async pruneCompactionCheckpoints(sessionId: string, sessionsDir: string): Promise<void> {\n const MAX = 15;\n const prefix = this.checkpointBasename(sessionId);\n let names: string[];\n try {\n names = await readdir(sessionsDir);\n } catch {\n return;\n }\n const candidates = names.filter((n) => n.startsWith(prefix) && n.endsWith('.jsonl'));\n if (candidates.length <= MAX) {\n return;\n }\n const stats = await Promise.all(\n candidates.map(async (name) => {\n const p = join(sessionsDir, name);\n try {\n const s = await stat(p);\n return { p, mtimeMs: s.mtimeMs };\n } catch {\n return { p: join(sessionsDir, name), mtimeMs: 0 };\n }\n }),\n );\n stats.sort((a, b) => a.mtimeMs - b.mtimeMs);\n for (let i = 0; i < stats.length - MAX; i++) {\n try {\n await unlink(stats[i]!.p);\n } catch {\n /* ignore */\n }\n }\n }\n\n private async captureCompactionCheckpoint(sessionId: string, transcriptAbs: string, sessionsDir: string): Promise<void> {\n if (!existsSync(transcriptAbs)) {\n return;\n }\n const id = randomUUID();\n const dest = join(sessionsDir, `${sessionId}.checkpoint.${id}.jsonl`);\n try {\n await copyFile(transcriptAbs, dest);\n await this.pruneCompactionCheckpoints(sessionId, sessionsDir);\n } catch (err) {\n log.warn({ err, sessionId }, 'Compaction checkpoint copy failed');\n }\n }\n\n async applyCompaction(\n key: string,\n messages: AgentMessage[],\n result: CompactionResult,\n ): Promise<AgentMessage[]> {\n const compacted = this.compactor.applyCompaction(messages, result);\n return this.runStoreMutation(async () => {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return compacted;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const abs = this.transcriptPathForEntry(entry, keySessionsDir);\n await this.captureCompactionCheckpoint(entry.sessionId, abs, keySessionsDir);\n const prev = await this.loadTranscriptRows(key);\n const merged = mergeLlmMessagesPreservingContextRows(prev, compacted);\n await this.writeTranscriptAndSyncIndex(key, merged, {\n appendCompaction: {\n at: new Date().toISOString(),\n summary: result.summary,\n firstKeptIndex: result.firstKeptIndex,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n },\n });\n const metadata = await this.getMetadata(key);\n if (metadata) {\n await this.updateMetadata(key, { compactedCount: metadata.compactedCount + 1 });\n }\n log.info(\n { key, tokensBefore: result.tokensBefore, tokensAfter: result.tokensAfter, keptMessages: compacted.length },\n 'Session compacted',\n );\n await this.injectPostCompactionContext(key);\n return compacted;\n });\n }\n\n async compact(\n key: string,\n messages: AgentMessage[],\n contextWindow: number,\n instructions?: string,\n force?: boolean,\n ): Promise<CompactionResult> {\n const result = await this.compactor.compact(messages, instructions, force);\n if (result.compacted) {\n await this.applyCompaction(key, messages, result);\n }\n return result;\n }\n\n async getCompactionStats(key: string) {\n const metadata = await this.getMetadata(key);\n if (!metadata) {\n return undefined;\n }\n return {\n compactionCount: metadata.compactedCount,\n totalTokensBefore: 0,\n totalTokensAfter: 0,\n lastCompactionAt: undefined,\n };\n }\n\n async listCompactionCheckpoints(key: string): Promise<CompactionCheckpointSummary[]> {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return [];\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const sessionId = entry.sessionId;\n const prefix = this.checkpointBasename(sessionId);\n let names: string[];\n try {\n names = await readdir(keySessionsDir);\n } catch {\n return [];\n }\n const files = names.filter((n) => n.startsWith(prefix) && n.endsWith('.jsonl'));\n const rows = await Promise.all(\n files.map(async (name) => {\n const p = join(keySessionsDir, name);\n const parsed = parseCompactionCheckpointTranscriptFileName(name);\n const id = parsed?.checkpointId;\n if (!id || !normalizeCompactionCheckpointId(id)) {\n return null;\n }\n try {\n const s = await stat(p);\n return {\n id: normalizeCompactionCheckpointId(id)!,\n sizeBytes: s.size,\n modifiedAt: new Date(s.mtimeMs).toISOString(),\n } satisfies CompactionCheckpointSummary;\n } catch {\n return null;\n }\n }),\n );\n const valid = rows.filter((r): r is CompactionCheckpointSummary => r !== null);\n valid.sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt));\n return valid;\n }\n\n async getCompactionCheckpointDetail(\n key: string,\n checkpointId: string,\n ): Promise<CompactionCheckpointDetail | null> {\n const id = normalizeCompactionCheckpointId(checkpointId);\n if (!id) {\n return null;\n }\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return null;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const fname = `${this.checkpointBasename(entry.sessionId)}${id}.jsonl`;\n const cpPath = join(keySessionsDir, fname);\n if (!existsSync(cpPath)) {\n return null;\n }\n try {\n const rows = await readTranscriptRowsFromFile(cpPath);\n const llm = rowsToLlmMessages(rows);\n const s = await stat(cpPath);\n return {\n id,\n sizeBytes: s.size,\n modifiedAt: new Date(s.mtimeMs).toISOString(),\n messageCount: llm.length,\n };\n } catch {\n return null;\n }\n }\n\n async restoreCompactionCheckpoint(key: string, checkpointId: string): Promise<void> {\n const id = normalizeCompactionCheckpointId(checkpointId);\n if (!id) {\n throw new Error('Invalid checkpoint id');\n }\n return this.runStoreMutation(async () => {\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n throw new Error(`Session not found: ${key}`);\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const cpPath = join(keySessionsDir, `${this.checkpointBasename(entry.sessionId)}${id}.jsonl`);\n if (!existsSync(cpPath)) {\n throw new Error(`Checkpoint not found: ${id}`);\n }\n const target = this.transcriptPathForEntry(entry, keySessionsDir);\n await copyFile(cpPath, target);\n const messages = await this.loadMessages(key);\n await this.saveMessages(key, messages);\n log.info({ key, checkpointId: id }, 'Session transcript restored from compaction checkpoint');\n });\n }\n\n async deleteSession(key: string): Promise<boolean> {\n return this.delete(key);\n }\n\n async load(key: string, options?: { fromArchive?: boolean }): Promise<AgentMessage[]> {\n return this.loadMessages(key, options);\n }\n\n async estimateTokenUsage(_key: string, messages: AgentMessage[]): Promise<number> {\n return this.estimateTokens(messages);\n }\n\n async searchInSession(key: string, keyword: string): Promise<Message[]> {\n const messages = await this.loadDisplayMessages(key);\n const keywordLower = keyword.toLowerCase();\n return this.convertMessages(\n messages.filter((m) => {\n const content = this.extractTextContent(this.messageContent(m));\n return content.toLowerCase().includes(keywordLower);\n }),\n );\n }\n\n async exportSession(key: string, format: ExportFormat): Promise<string> {\n const detail = await this.get(key);\n if (!detail) {\n throw new Error(`Session not found: ${key}`);\n }\n if (format === 'json') {\n const transcriptRows = await this.loadTranscriptRows(key);\n const exportData: SessionExport = {\n version: INDEX_VERSION,\n exportedAt: new Date().toISOString(),\n metadata: detail,\n messages: detail.messages,\n transcriptRows,\n };\n return JSON.stringify(exportData, null, 2);\n }\n const lines = [\n `# ${detail.name || detail.key}`,\n '',\n `- **Channel:** ${detail.sourceChannel}`,\n `- **Created:** ${detail.createdAt}`,\n `- **Messages:** ${detail.messageCount}`,\n `- **Tags:** ${detail.tags.join(', ') || 'none'}`,\n '',\n '---',\n '',\n ];\n for (const msg of detail.messages) {\n const role = msg.role === 'assistant' ? 'Assistant' : msg.role === 'user' ? 'User' : msg.role;\n lines.push(`## ${role}`, '');\n const body = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content, null, 2);\n lines.push(body, '', '---', '');\n }\n return lines.join('\\n');\n }\n\n async getStats(): Promise<GlobalSessionStats> {\n const list = await this.list({ limit: 100000 });\n const sessions = list.items;\n const byChannel: Record<string, number> = {};\n for (const s of sessions) {\n byChannel[s.sourceChannel] = (byChannel[s.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((s) => s.status === SessionStatus.ACTIVE || s.status === SessionStatus.IDLE).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\n async archiveOld(olderThanDays: number): Promise<number> {\n const cutoff = new Date();\n cutoff.setDate(cutoff.getDate() - olderThanDays);\n const list = await this.list({ limit: 100000 });\n let archived = 0;\n for (const session of list.items) {\n if (session.status !== SessionStatus.ARCHIVED && session.status !== SessionStatus.PINNED) {\n const lastAccess = new Date(session.lastAccessedAt);\n if (lastAccess < cutoff) {\n await this.archive(session.key);\n archived++;\n }\n }\n }\n return archived;\n }\n\n estimateTokens(messages: AgentMessage[]): number {\n let total = 0;\n for (const msg of messages) {\n total += Math.ceil(this.extractTextContent(this.messageContent(msg)).length / 4);\n }\n return total;\n }\n\n private messageContent(msg: AgentMessage): unknown {\n return (msg as { content?: unknown }).content;\n }\n\n private isCompactionSummaryMessage(msg: AgentMessage): boolean {\n if (msg.role !== 'user') {\n return false;\n }\n const text = this.extractTextContent(this.messageContent(msg)).trim();\n return /^\\[Previous conversation summary\\]/i.test(text);\n }\n\n private displayMessageIdentity(message: AgentMessage): string {\n const record = message as unknown as Record<string, unknown>;\n return JSON.stringify({\n role: message.role,\n timestamp: record.timestamp,\n toolCallId: record.toolCallId ?? record.tool_call_id,\n toolName: record.toolName,\n content: this.messageContent(message),\n });\n }\n\n private extractTextContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n const parts: string[] = [];\n for (const item of content) {\n if (typeof item !== 'object' || item === null || !('type' in item)) {\n continue;\n }\n const c = item as { type?: string; text?: string; name?: string };\n if (c.type === 'text' && typeof c.text === 'string') {\n parts.push(c.text);\n } else if (c.type === 'toolCall' || c.type === 'tool_use') {\n parts.push(c.name ? `[${c.name}]` : '');\n }\n }\n return parts.join('');\n }\n return '';\n }\n\n private async injectPostCompactionContext(key: string): Promise<void> {\n const contextText = readPostCompactionContext({\n cfg: this.options.config,\n sessionKey: key,\n });\n if (!contextText?.trim()) {\n return;\n }\n const entry = await this.getDiskEntry(key);\n if (!entry) {\n return;\n }\n const keySessionsDir = this.resolveSessionsDirForKey(key);\n const abs = this.transcriptPathForEntry(entry, keySessionsDir);\n const workspaceDir = resolveEffectiveAgentProfileForSession(this.options.config, key).resolvedWorkspacePath;\n try {\n await appendPiTranscriptContextEntry({\n absPath: abs,\n cwd: workspaceDir,\n sessionKey: key,\n entry: {\n kind: 'context',\n id: `post-compaction-${Date.now()}`,\n text: contextText,\n createdAt: new Date().toISOString(),\n },\n });\n } catch (err) {\n log.warn({ err, key }, 'Post-compaction context injection failed');\n }\n }\n\n private convertMessages(messages: AgentMessage[]): Message[] {\n return messages.map((m: AgentMessage & Record<string, unknown>) => {\n const c = this.messageContent(m);\n const content: string | unknown[] =\n typeof c === 'string' ? c : Array.isArray(c) ? c : this.extractTextContent(c);\n const row: Message = {\n role: m.role as Message['role'],\n content,\n timestamp: m.timestamp ? new Date(m.timestamp as string | number).toISOString() : undefined,\n tool_call_id: (m.tool_call_id as string | undefined) || (m.toolCallId as string | undefined),\n tool_calls: m.tool_calls as Message['tool_calls'],\n name: m.name as string | undefined,\n };\n if (Array.isArray(m.attachments) && m.attachments.length > 0) {\n row.attachments = m.attachments as Message['attachments'];\n }\n const rawUsage = m.usage as { input?: number; output?: number; totalTokens?: number; total?: number } | undefined;\n if (rawUsage && typeof rawUsage === 'object') {\n const inputTokens = typeof rawUsage.input === 'number' ? rawUsage.input : undefined;\n const outputTokens = typeof rawUsage.output === 'number' ? rawUsage.output : undefined;\n const totalTokens = typeof rawUsage.totalTokens === 'number'\n ? rawUsage.totalTokens\n : typeof rawUsage.total === 'number'\n ? rawUsage.total\n : undefined;\n if (inputTokens != null || outputTokens != null || totalTokens != null) {\n row.usage = { inputTokens, outputTokens, totalTokens };\n }\n }\n return row;\n });\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
- import { normalizeOptionalString } from "../utils/string-coerce.js";
1
+ import { init_string_coerce, normalizeOptionalString } from "../utils/string-coerce.js";
2
2
  //#region src/session/transcript-events.ts
3
+ init_string_coerce();
3
4
  const SESSION_TRANSCRIPT_LISTENERS = /* @__PURE__ */ new Set();
4
5
  function onSessionTranscriptUpdate(listener) {
5
6
  SESSION_TRANSCRIPT_LISTENERS.add(listener);
@@ -1 +1 @@
1
- {"version":3,"file":"transcript-events.js","names":[],"sources":["../../../src/session/transcript-events.ts"],"sourcesContent":["import { normalizeOptionalString } from '../utils/string-coerce.js';\n\nexport type SessionTranscriptUpdate = {\n sessionFile: string;\n sessionKey?: string;\n message?: unknown;\n messageId?: string;\n};\n\ntype SessionTranscriptListener = (update: SessionTranscriptUpdate) => void;\n\nconst SESSION_TRANSCRIPT_LISTENERS = new Set<SessionTranscriptListener>();\n\nexport function onSessionTranscriptUpdate(listener: SessionTranscriptListener): () => void {\n SESSION_TRANSCRIPT_LISTENERS.add(listener);\n return () => {\n SESSION_TRANSCRIPT_LISTENERS.delete(listener);\n };\n}\n\nexport function emitSessionTranscriptUpdate(update: string | SessionTranscriptUpdate): void {\n const normalized =\n typeof update === \"string\"\n ? { sessionFile: update }\n : {\n sessionFile: update.sessionFile,\n sessionKey: update.sessionKey,\n message: update.message,\n messageId: update.messageId,\n };\n const trimmed = normalizeOptionalString(normalized.sessionFile);\n if (!trimmed) {\n return;\n }\n const nextUpdate: SessionTranscriptUpdate = {\n sessionFile: trimmed,\n ...(normalizeOptionalString(normalized.sessionKey)\n ? { sessionKey: normalizeOptionalString(normalized.sessionKey) }\n : {}),\n ...(normalized.message !== undefined ? { message: normalized.message } : {}),\n ...(normalizeOptionalString(normalized.messageId)\n ? { messageId: normalizeOptionalString(normalized.messageId) }\n : {}),\n };\n for (const listener of SESSION_TRANSCRIPT_LISTENERS) {\n try {\n listener(nextUpdate);\n } catch {\n /* ignore */\n }\n }\n}\n"],"mappings":";;AAWA,MAAM,+CAA+B,IAAI,KAAgC;AAEzE,SAAgB,0BAA0B,UAAiD;AACzF,8BAA6B,IAAI,SAAS;AAC1C,cAAa;AACX,+BAA6B,OAAO,SAAS;;;AAIjD,SAAgB,4BAA4B,QAAgD;CAC1F,MAAM,aACJ,OAAO,WAAW,WACd,EAAE,aAAa,QAAQ,GACvB;EACE,aAAa,OAAO;EACpB,YAAY,OAAO;EACnB,SAAS,OAAO;EAChB,WAAW,OAAO;EACnB;CACP,MAAM,UAAU,wBAAwB,WAAW,YAAY;AAC/D,KAAI,CAAC,QACH;CAEF,MAAM,aAAsC;EAC1C,aAAa;EACb,GAAI,wBAAwB,WAAW,WAAW,GAC9C,EAAE,YAAY,wBAAwB,WAAW,WAAW,EAAE,GAC9D,EAAE;EACN,GAAI,WAAW,YAAY,KAAA,IAAY,EAAE,SAAS,WAAW,SAAS,GAAG,EAAE;EAC3E,GAAI,wBAAwB,WAAW,UAAU,GAC7C,EAAE,WAAW,wBAAwB,WAAW,UAAU,EAAE,GAC5D,EAAE;EACP;AACD,MAAK,MAAM,YAAY,6BACrB,KAAI;AACF,WAAS,WAAW;SACd"}
1
+ {"version":3,"file":"transcript-events.js","names":[],"sources":["../../../src/session/transcript-events.ts"],"sourcesContent":["import { normalizeOptionalString } from '../utils/string-coerce.js';\n\nexport type SessionTranscriptUpdate = {\n sessionFile: string;\n sessionKey?: string;\n message?: unknown;\n messageId?: string;\n};\n\ntype SessionTranscriptListener = (update: SessionTranscriptUpdate) => void;\n\nconst SESSION_TRANSCRIPT_LISTENERS = new Set<SessionTranscriptListener>();\n\nexport function onSessionTranscriptUpdate(listener: SessionTranscriptListener): () => void {\n SESSION_TRANSCRIPT_LISTENERS.add(listener);\n return () => {\n SESSION_TRANSCRIPT_LISTENERS.delete(listener);\n };\n}\n\nexport function emitSessionTranscriptUpdate(update: string | SessionTranscriptUpdate): void {\n const normalized =\n typeof update === \"string\"\n ? { sessionFile: update }\n : {\n sessionFile: update.sessionFile,\n sessionKey: update.sessionKey,\n message: update.message,\n messageId: update.messageId,\n };\n const trimmed = normalizeOptionalString(normalized.sessionFile);\n if (!trimmed) {\n return;\n }\n const nextUpdate: SessionTranscriptUpdate = {\n sessionFile: trimmed,\n ...(normalizeOptionalString(normalized.sessionKey)\n ? { sessionKey: normalizeOptionalString(normalized.sessionKey) }\n : {}),\n ...(normalized.message !== undefined ? { message: normalized.message } : {}),\n ...(normalizeOptionalString(normalized.messageId)\n ? { messageId: normalizeOptionalString(normalized.messageId) }\n : {}),\n };\n for (const listener of SESSION_TRANSCRIPT_LISTENERS) {\n try {\n listener(nextUpdate);\n } catch {\n /* ignore */\n }\n }\n}\n"],"mappings":";;oBAAoE;AAWpE,MAAM,+CAA+B,IAAI,KAAgC;AAEzE,SAAgB,0BAA0B,UAAiD;AACzF,8BAA6B,IAAI,SAAS;AAC1C,cAAa;AACX,+BAA6B,OAAO,SAAS;;;AAIjD,SAAgB,4BAA4B,QAAgD;CAC1F,MAAM,aACJ,OAAO,WAAW,WACd,EAAE,aAAa,QAAQ,GACvB;EACE,aAAa,OAAO;EACpB,YAAY,OAAO;EACnB,SAAS,OAAO;EAChB,WAAW,OAAO;EACnB;CACP,MAAM,UAAU,wBAAwB,WAAW,YAAY;AAC/D,KAAI,CAAC,QACH;CAEF,MAAM,aAAsC;EAC1C,aAAa;EACb,GAAI,wBAAwB,WAAW,WAAW,GAC9C,EAAE,YAAY,wBAAwB,WAAW,WAAW,EAAE,GAC9D,EAAE;EACN,GAAI,WAAW,YAAY,KAAA,IAAY,EAAE,SAAS,WAAW,SAAS,GAAG,EAAE;EAC3E,GAAI,wBAAwB,WAAW,UAAU,GAC7C,EAAE,WAAW,wBAAwB,WAAW,UAAU,EAAE,GAC5D,EAAE;EACP;AACD,MAAK,MAAM,YAAY,6BACrB,KAAI;AACF,WAAS,WAAW;SACd"}
@@ -2,6 +2,39 @@ import type { ResolvedShareUrl, ShareReachability } from './share-types.js';
2
2
  export interface ShareUrlContext {
3
3
  gatewayHost: string;
4
4
  gatewayPort: number;
5
+ /**
6
+ * User-configured reverse-proxy origin (`gateway.publicUrl`), already
7
+ * normalized to `${protocol}//${host}` (no trailing slash).
8
+ *
9
+ * When present, the gateway is publicly reachable at this URL even if the
10
+ * built-in FRP tunnel is not running — file shares can be served at
11
+ * `<publicUrl>/s/<token>` and site shares at the subpath fallback
12
+ * `<publicUrl>/site/<token>/`.
13
+ */
14
+ reverseProxyPublicUrl?: string | null;
5
15
  }
6
16
  export declare function resolveShareUrl(token: string, ctx: ShareUrlContext): ResolvedShareUrl;
7
17
  export declare function resolveReachabilityForList(ctx: ShareUrlContext): ShareReachability;
18
+ export interface SiteShareUrlContext extends ShareUrlContext {
19
+ /** Token used as the `/site/:token/` subpath label. */
20
+ token: string;
21
+ /** Subdomain label used at `https://<label>.<publicHostSuffix>/` when an FRP tunnel is active. */
22
+ subdomainLabel: string;
23
+ /** From `siteShare.publicHostSuffix`. */
24
+ publicHostSuffix: string;
25
+ }
26
+ export interface ResolvedSiteShareUrl {
27
+ /** URL the recipient lands on. */
28
+ shareUrl: string;
29
+ /** URL for the auto-rendered site thumbnail. */
30
+ thumbnailUrl: string;
31
+ reachability: ShareReachability;
32
+ reachabilityHint: string | null;
33
+ }
34
+ /**
35
+ * Resolve the URL pair for a site share. Priority mirrors `resolveShareUrl`:
36
+ * 1. FRP tunnel → wildcard subdomain on `<publicHostSuffix>`.
37
+ * 2. Reverse-proxy `publicUrl` → `<publicUrl>/site/<token>/` subpath.
38
+ * 3. Direct gateway bind → `http://<host>:<port>/site/<token>/`.
39
+ */
40
+ export declare function resolveSiteShareUrl(ctx: SiteShareUrlContext): ResolvedSiteShareUrl;
@@ -3,19 +3,22 @@ import { resolveLanGatewayUrl } from "../tunnel/tunnel-qr.js";
3
3
  //#region src/share/share-url.ts
4
4
  function resolveShareUrl(token, ctx) {
5
5
  const tunnelState = loadTunnelState();
6
- const { gatewayHost, gatewayPort } = ctx;
6
+ const { gatewayHost, gatewayPort, reverseProxyPublicUrl } = ctx;
7
7
  const path = `/s/${token}`;
8
- if (tunnelState?.publicUrl) {
9
- const base = tunnelState.publicUrl.replace(/\/+$/, "");
10
- const lanUrl = buildLanUrl(gatewayHost, gatewayPort, path);
11
- return {
12
- shareUrl: `${base}${path}`,
13
- lanUrl,
14
- reachability: "public",
15
- reachabilityHint: null
16
- };
17
- }
18
- if (gatewayHost !== "127.0.0.1" && gatewayHost !== "localhost" && gatewayHost !== "::1") return {
8
+ if (tunnelState?.publicUrl) return {
9
+ shareUrl: `${tunnelState.publicUrl.replace(/\/+$/, "")}${path}`,
10
+ lanUrl: buildLanUrl(gatewayHost, gatewayPort, path),
11
+ reachability: "public",
12
+ reachabilityHint: null
13
+ };
14
+ const reverseProxy = reverseProxyPublicUrl?.trim();
15
+ if (reverseProxy) return {
16
+ shareUrl: `${reverseProxy.replace(/\/+$/, "")}${path}`,
17
+ lanUrl: buildLanUrl(gatewayHost, gatewayPort, path),
18
+ reachability: "public",
19
+ reachabilityHint: null
20
+ };
21
+ if (!isLoopbackHost(gatewayHost)) return {
19
22
  shareUrl: `http://${gatewayHost}:${gatewayPort}${path}`,
20
23
  lanUrl: null,
21
24
  reachability: "lan",
@@ -33,12 +36,51 @@ function buildLanUrl(host, port, path) {
33
36
  if (!lanBase) return null;
34
37
  return `${lanBase.replace(/\/+$/, "")}${path}`;
35
38
  }
39
+ function isLoopbackHost(host) {
40
+ return host === "127.0.0.1" || host === "localhost" || host === "::1";
41
+ }
36
42
  function resolveReachabilityForList(ctx) {
37
43
  if (loadTunnelState()?.publicUrl) return "public";
38
- if (ctx.gatewayHost !== "127.0.0.1" && ctx.gatewayHost !== "localhost" && ctx.gatewayHost !== "::1") return "lan";
44
+ if (ctx.reverseProxyPublicUrl?.trim()) return "public";
45
+ if (!isLoopbackHost(ctx.gatewayHost)) return "lan";
39
46
  return "local-only";
40
47
  }
48
+ /**
49
+ * Resolve the URL pair for a site share. Priority mirrors `resolveShareUrl`:
50
+ * 1. FRP tunnel → wildcard subdomain on `<publicHostSuffix>`.
51
+ * 2. Reverse-proxy `publicUrl` → `<publicUrl>/site/<token>/` subpath.
52
+ * 3. Direct gateway bind → `http://<host>:<port>/site/<token>/`.
53
+ */
54
+ function resolveSiteShareUrl(ctx) {
55
+ if (loadTunnelState()?.publicUrl) {
56
+ const root = `https://${ctx.subdomainLabel}.${ctx.publicHostSuffix}`;
57
+ return {
58
+ shareUrl: `${root}/`,
59
+ thumbnailUrl: `${root}/site/${ctx.token}/thumbnail`,
60
+ reachability: "public",
61
+ reachabilityHint: null
62
+ };
63
+ }
64
+ const reverseProxy = ctx.reverseProxyPublicUrl?.trim();
65
+ if (reverseProxy) {
66
+ const base = reverseProxy.replace(/\/+$/, "");
67
+ return {
68
+ shareUrl: `${base}/site/${ctx.token}/`,
69
+ thumbnailUrl: `${base}/site/${ctx.token}/thumbnail`,
70
+ reachability: "public",
71
+ reachabilityHint: null
72
+ };
73
+ }
74
+ const base = `http://${ctx.gatewayHost}:${ctx.gatewayPort}`;
75
+ const loopback = isLoopbackHost(ctx.gatewayHost);
76
+ return {
77
+ shareUrl: `${base}/site/${ctx.token}/`,
78
+ thumbnailUrl: `${base}/site/${ctx.token}/thumbnail`,
79
+ reachability: loopback ? "local-only" : "lan",
80
+ reachabilityHint: loopback ? "当前仅本机可访问,开启远程隧道后对外可达" : "当前局域网内可访问,开启远程隧道后对外网可达"
81
+ };
82
+ }
41
83
  //#endregion
42
- export { resolveReachabilityForList, resolveShareUrl };
84
+ export { resolveReachabilityForList, resolveShareUrl, resolveSiteShareUrl };
43
85
 
44
86
  //# sourceMappingURL=share-url.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"share-url.js","names":[],"sources":["../../../src/share/share-url.ts"],"sourcesContent":["import { loadTunnelState } from '../tunnel/tunnel-state.js';\nimport { resolveLanGatewayUrl } from '../tunnel/tunnel-qr.js';\n\nimport type { ResolvedShareUrl, ShareReachability } from './share-types.js';\n\nexport interface ShareUrlContext {\n gatewayHost: string;\n gatewayPort: number;\n}\n\nexport function resolveShareUrl(token: string, ctx: ShareUrlContext): ResolvedShareUrl {\n const tunnelState = loadTunnelState();\n const { gatewayHost, gatewayPort } = ctx;\n\n const path = `/s/${token}`;\n\n if (tunnelState?.publicUrl) {\n const base = tunnelState.publicUrl.replace(/\\/+$/, '');\n const lanUrl = buildLanUrl(gatewayHost, gatewayPort, path);\n return {\n shareUrl: `${base}${path}`,\n lanUrl,\n reachability: 'public',\n reachabilityHint: null,\n };\n }\n\n if (gatewayHost !== '127.0.0.1' && gatewayHost !== 'localhost' && gatewayHost !== '::1') {\n return {\n shareUrl: `http://${gatewayHost}:${gatewayPort}${path}`,\n lanUrl: null,\n reachability: 'lan',\n reachabilityHint: '当前局域网内可访问,开启远程隧道后对外网可达',\n };\n }\n\n return {\n shareUrl: `http://localhost:${gatewayPort}${path}`,\n lanUrl: null,\n reachability: 'local-only',\n reachabilityHint: '当前仅本机可访问,开启远程隧道后对外可达',\n };\n}\n\nfunction buildLanUrl(host: string, port: number, path: string): string | null {\n const lanBase = resolveLanGatewayUrl(host, port);\n if (!lanBase) return null;\n return `${lanBase.replace(/\\/+$/, '')}${path}`;\n}\n\nexport function resolveReachabilityForList(ctx: ShareUrlContext): ShareReachability {\n const tunnelState = loadTunnelState();\n if (tunnelState?.publicUrl) return 'public';\n if (ctx.gatewayHost !== '127.0.0.1' && ctx.gatewayHost !== 'localhost' && ctx.gatewayHost !== '::1') {\n return 'lan';\n }\n return 'local-only';\n}\n"],"mappings":";;;AAUA,SAAgB,gBAAgB,OAAe,KAAwC;CACrF,MAAM,cAAc,iBAAiB;CACrC,MAAM,EAAE,aAAa,gBAAgB;CAErC,MAAM,OAAO,MAAM;AAEnB,KAAI,aAAa,WAAW;EAC1B,MAAM,OAAO,YAAY,UAAU,QAAQ,QAAQ,GAAG;EACtD,MAAM,SAAS,YAAY,aAAa,aAAa,KAAK;AAC1D,SAAO;GACL,UAAU,GAAG,OAAO;GACpB;GACA,cAAc;GACd,kBAAkB;GACnB;;AAGH,KAAI,gBAAgB,eAAe,gBAAgB,eAAe,gBAAgB,MAChF,QAAO;EACL,UAAU,UAAU,YAAY,GAAG,cAAc;EACjD,QAAQ;EACR,cAAc;EACd,kBAAkB;EACnB;AAGH,QAAO;EACL,UAAU,oBAAoB,cAAc;EAC5C,QAAQ;EACR,cAAc;EACd,kBAAkB;EACnB;;AAGH,SAAS,YAAY,MAAc,MAAc,MAA6B;CAC5E,MAAM,UAAU,qBAAqB,MAAM,KAAK;AAChD,KAAI,CAAC,QAAS,QAAO;AACrB,QAAO,GAAG,QAAQ,QAAQ,QAAQ,GAAG,GAAG;;AAG1C,SAAgB,2BAA2B,KAAyC;AAElF,KADoB,iBACL,EAAE,UAAW,QAAO;AACnC,KAAI,IAAI,gBAAgB,eAAe,IAAI,gBAAgB,eAAe,IAAI,gBAAgB,MAC5F,QAAO;AAET,QAAO"}
1
+ {"version":3,"file":"share-url.js","names":[],"sources":["../../../src/share/share-url.ts"],"sourcesContent":["import { loadTunnelState } from '../tunnel/tunnel-state.js';\nimport { resolveLanGatewayUrl } from '../tunnel/tunnel-qr.js';\n\nimport type { ResolvedShareUrl, ShareReachability } from './share-types.js';\n\nexport interface ShareUrlContext {\n gatewayHost: string;\n gatewayPort: number;\n /**\n * User-configured reverse-proxy origin (`gateway.publicUrl`), already\n * normalized to `${protocol}//${host}` (no trailing slash).\n *\n * When present, the gateway is publicly reachable at this URL even if the\n * built-in FRP tunnel is not running — file shares can be served at\n * `<publicUrl>/s/<token>` and site shares at the subpath fallback\n * `<publicUrl>/site/<token>/`.\n */\n reverseProxyPublicUrl?: string | null;\n}\n\nexport function resolveShareUrl(token: string, ctx: ShareUrlContext): ResolvedShareUrl {\n const tunnelState = loadTunnelState();\n const { gatewayHost, gatewayPort, reverseProxyPublicUrl } = ctx;\n\n const path = `/s/${token}`;\n\n if (tunnelState?.publicUrl) {\n const base = tunnelState.publicUrl.replace(/\\/+$/, '');\n return {\n shareUrl: `${base}${path}`,\n lanUrl: buildLanUrl(gatewayHost, gatewayPort, path),\n reachability: 'public',\n reachabilityHint: null,\n };\n }\n\n const reverseProxy = reverseProxyPublicUrl?.trim();\n if (reverseProxy) {\n const base = reverseProxy.replace(/\\/+$/, '');\n return {\n shareUrl: `${base}${path}`,\n lanUrl: buildLanUrl(gatewayHost, gatewayPort, path),\n reachability: 'public',\n reachabilityHint: null,\n };\n }\n\n if (!isLoopbackHost(gatewayHost)) {\n return {\n shareUrl: `http://${gatewayHost}:${gatewayPort}${path}`,\n lanUrl: null,\n reachability: 'lan',\n reachabilityHint: '当前局域网内可访问,开启远程隧道后对外网可达',\n };\n }\n\n return {\n shareUrl: `http://localhost:${gatewayPort}${path}`,\n lanUrl: null,\n reachability: 'local-only',\n reachabilityHint: '当前仅本机可访问,开启远程隧道后对外可达',\n };\n}\n\nfunction buildLanUrl(host: string, port: number, path: string): string | null {\n const lanBase = resolveLanGatewayUrl(host, port);\n if (!lanBase) return null;\n return `${lanBase.replace(/\\/+$/, '')}${path}`;\n}\n\nfunction isLoopbackHost(host: string): boolean {\n return host === '127.0.0.1' || host === 'localhost' || host === '::1';\n}\n\nexport function resolveReachabilityForList(ctx: ShareUrlContext): ShareReachability {\n if (loadTunnelState()?.publicUrl) return 'public';\n if (ctx.reverseProxyPublicUrl?.trim()) return 'public';\n if (!isLoopbackHost(ctx.gatewayHost)) return 'lan';\n return 'local-only';\n}\n\n// ── Site shares ────────────────────────────────────────────────────────────\n\nexport interface SiteShareUrlContext extends ShareUrlContext {\n /** Token used as the `/site/:token/` subpath label. */\n token: string;\n /** Subdomain label used at `https://<label>.<publicHostSuffix>/` when an FRP tunnel is active. */\n subdomainLabel: string;\n /** From `siteShare.publicHostSuffix`. */\n publicHostSuffix: string;\n}\n\nexport interface ResolvedSiteShareUrl {\n /** URL the recipient lands on. */\n shareUrl: string;\n /** URL for the auto-rendered site thumbnail. */\n thumbnailUrl: string;\n reachability: ShareReachability;\n reachabilityHint: string | null;\n}\n\n/**\n * Resolve the URL pair for a site share. Priority mirrors `resolveShareUrl`:\n * 1. FRP tunnel → wildcard subdomain on `<publicHostSuffix>`.\n * 2. Reverse-proxy `publicUrl` → `<publicUrl>/site/<token>/` subpath.\n * 3. Direct gateway bind → `http://<host>:<port>/site/<token>/`.\n */\nexport function resolveSiteShareUrl(ctx: SiteShareUrlContext): ResolvedSiteShareUrl {\n const tunnelState = loadTunnelState();\n if (tunnelState?.publicUrl) {\n const root = `https://${ctx.subdomainLabel}.${ctx.publicHostSuffix}`;\n return {\n shareUrl: `${root}/`,\n thumbnailUrl: `${root}/site/${ctx.token}/thumbnail`,\n reachability: 'public',\n reachabilityHint: null,\n };\n }\n\n const reverseProxy = ctx.reverseProxyPublicUrl?.trim();\n if (reverseProxy) {\n const base = reverseProxy.replace(/\\/+$/, '');\n return {\n shareUrl: `${base}/site/${ctx.token}/`,\n thumbnailUrl: `${base}/site/${ctx.token}/thumbnail`,\n reachability: 'public',\n reachabilityHint: null,\n };\n }\n\n const base = `http://${ctx.gatewayHost}:${ctx.gatewayPort}`;\n const loopback = isLoopbackHost(ctx.gatewayHost);\n return {\n shareUrl: `${base}/site/${ctx.token}/`,\n thumbnailUrl: `${base}/site/${ctx.token}/thumbnail`,\n reachability: loopback ? 'local-only' : 'lan',\n reachabilityHint: loopback\n ? '当前仅本机可访问,开启远程隧道后对外可达'\n : '当前局域网内可访问,开启远程隧道后对外网可达',\n };\n}\n"],"mappings":";;;AAoBA,SAAgB,gBAAgB,OAAe,KAAwC;CACrF,MAAM,cAAc,iBAAiB;CACrC,MAAM,EAAE,aAAa,aAAa,0BAA0B;CAE5D,MAAM,OAAO,MAAM;AAEnB,KAAI,aAAa,UAEf,QAAO;EACL,UAAU,GAFC,YAAY,UAAU,QAAQ,QAAQ,GAEhC,GAAG;EACpB,QAAQ,YAAY,aAAa,aAAa,KAAK;EACnD,cAAc;EACd,kBAAkB;EACnB;CAGH,MAAM,eAAe,uBAAuB,MAAM;AAClD,KAAI,aAEF,QAAO;EACL,UAAU,GAFC,aAAa,QAAQ,QAAQ,GAEvB,GAAG;EACpB,QAAQ,YAAY,aAAa,aAAa,KAAK;EACnD,cAAc;EACd,kBAAkB;EACnB;AAGH,KAAI,CAAC,eAAe,YAAY,CAC9B,QAAO;EACL,UAAU,UAAU,YAAY,GAAG,cAAc;EACjD,QAAQ;EACR,cAAc;EACd,kBAAkB;EACnB;AAGH,QAAO;EACL,UAAU,oBAAoB,cAAc;EAC5C,QAAQ;EACR,cAAc;EACd,kBAAkB;EACnB;;AAGH,SAAS,YAAY,MAAc,MAAc,MAA6B;CAC5E,MAAM,UAAU,qBAAqB,MAAM,KAAK;AAChD,KAAI,CAAC,QAAS,QAAO;AACrB,QAAO,GAAG,QAAQ,QAAQ,QAAQ,GAAG,GAAG;;AAG1C,SAAS,eAAe,MAAuB;AAC7C,QAAO,SAAS,eAAe,SAAS,eAAe,SAAS;;AAGlE,SAAgB,2BAA2B,KAAyC;AAClF,KAAI,iBAAiB,EAAE,UAAW,QAAO;AACzC,KAAI,IAAI,uBAAuB,MAAM,CAAE,QAAO;AAC9C,KAAI,CAAC,eAAe,IAAI,YAAY,CAAE,QAAO;AAC7C,QAAO;;;;;;;;AA6BT,SAAgB,oBAAoB,KAAgD;AAElF,KADoB,iBACL,EAAE,WAAW;EAC1B,MAAM,OAAO,WAAW,IAAI,eAAe,GAAG,IAAI;AAClD,SAAO;GACL,UAAU,GAAG,KAAK;GAClB,cAAc,GAAG,KAAK,QAAQ,IAAI,MAAM;GACxC,cAAc;GACd,kBAAkB;GACnB;;CAGH,MAAM,eAAe,IAAI,uBAAuB,MAAM;AACtD,KAAI,cAAc;EAChB,MAAM,OAAO,aAAa,QAAQ,QAAQ,GAAG;AAC7C,SAAO;GACL,UAAU,GAAG,KAAK,QAAQ,IAAI,MAAM;GACpC,cAAc,GAAG,KAAK,QAAQ,IAAI,MAAM;GACxC,cAAc;GACd,kBAAkB;GACnB;;CAGH,MAAM,OAAO,UAAU,IAAI,YAAY,GAAG,IAAI;CAC9C,MAAM,WAAW,eAAe,IAAI,YAAY;AAChD,QAAO;EACL,UAAU,GAAG,KAAK,QAAQ,IAAI,MAAM;EACpC,cAAc,GAAG,KAAK,QAAQ,IAAI,MAAM;EACxC,cAAc,WAAW,eAAe;EACxC,kBAAkB,WACd,yBACA;EACL"}
@@ -42,8 +42,7 @@ var EmbeddedBackend = class {
42
42
  this.running = true;
43
43
  const config = loadConfig();
44
44
  const workspace = getWorkspacePath(config);
45
- const modelConfig = config.agents?.defaults?.model;
46
- const modelId = typeof modelConfig === "string" ? modelConfig : modelConfig?.primary;
45
+ const modelId = config.agents?.defaults?.model?.primary;
47
46
  this.agent = new AgentService(this.bus, {
48
47
  workspace: workspace ?? process.cwd(),
49
48
  model: modelId,
@@ -182,10 +181,7 @@ var EmbeddedBackend = class {
182
181
  }
183
182
  }
184
183
  async getSessionInfo(sessionKey) {
185
- if (!this.agent) {
186
- const modelConfig = loadConfig().agents?.defaults?.model;
187
- return { model: (typeof modelConfig === "string" ? modelConfig : modelConfig?.primary) ?? void 0 };
188
- }
184
+ if (!this.agent) return { model: loadConfig().agents?.defaults?.model?.primary ?? void 0 };
189
185
  try {
190
186
  const cfg = await this.agent.sessionInspector.agentConfig(sessionKey);
191
187
  const parsed = parseModelRef(cfg.model);
@@ -205,8 +201,7 @@ var EmbeddedBackend = class {
205
201
  sessionKey,
206
202
  errorMessage
207
203
  }, `getSessionInfo failed: ${errorMessage}`);
208
- const modelConfig = loadConfig().agents?.defaults?.model;
209
- return { model: (typeof modelConfig === "string" ? modelConfig : modelConfig?.primary) ?? void 0 };
204
+ return { model: loadConfig().agents?.defaults?.model?.primary ?? void 0 };
210
205
  }
211
206
  }
212
207
  async listModels() {
@@ -220,7 +215,7 @@ var EmbeddedBackend = class {
220
215
  }
221
216
  async resetSession(sessionKey) {
222
217
  if (!this.agent) return;
223
- await this.agent.clearSessionMessages(sessionKey);
218
+ await this.agent.resetSession(sessionKey);
224
219
  }
225
220
  async patchSession(_sessionKey, _patch) {}
226
221
  async compactSession(sessionKey, options) {