@xopcai/xopc 0.0.89 → 0.0.90

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 (384) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  3. package/dist/extensions/feishu/src/workflow-progress.js +1 -1
  4. package/dist/extensions/telegram/src/plugin.js +1 -1
  5. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  6. package/dist/extensions/telegram/src/workflow-progress.js +1 -1
  7. package/dist/extensions/telegram/xopc.extension.json +1 -1
  8. package/dist/extensions/weixin/src/api/api.js +2 -2
  9. package/dist/extensions/weixin/src/auth/accounts.js +1 -1
  10. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  11. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  12. package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
  13. package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
  14. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  15. package/dist/extensions/weixin/src/plugin.js +1 -1
  16. package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
  17. package/dist/extensions/weixin/src/workflow-progress.js +1 -1
  18. package/dist/gateway/static/root/assets/agents-cPvvYLXo.js +222 -0
  19. package/dist/gateway/static/root/assets/apps-page-Bk1_P5FJ.js +1 -0
  20. package/dist/gateway/static/root/assets/channels-settings-CZoeQwHz.js +1 -0
  21. package/dist/gateway/static/root/assets/{channels-status-swr-DaHGkRF1.js → channels-status-swr-BrtH2VzC.js} +1 -1
  22. package/dist/gateway/static/root/assets/circle-check-C23XjkUj.js +1 -0
  23. package/dist/gateway/static/root/assets/cron-api-CyqbgfHM.js +1 -0
  24. package/dist/gateway/static/root/assets/cron-dreaming-jobs-Ip703-qM.js +2 -0
  25. package/dist/gateway/static/root/assets/cron-page-BpLdiQN8.js +1 -0
  26. package/dist/gateway/static/root/assets/dist-BpAiK86n.js +1 -0
  27. package/dist/gateway/static/root/assets/{extension-debug-page-CtuKJ9tE.js → extension-debug-page-D6Ak0STa.js} +1 -1
  28. package/dist/gateway/static/root/assets/{extension-page-ykzjOkR5.js → extension-page-Q0P3d6DW.js} +1 -1
  29. package/dist/gateway/static/root/assets/{extension-settings-page-Ce2qrdpO.js → extension-settings-page-CL55LwU_.js} +1 -1
  30. package/dist/gateway/static/root/assets/eye-DAfL1U7M.js +1 -0
  31. package/dist/gateway/static/root/assets/{fetch-C9FFJjuH.js → fetch-Dqa9iTWl.js} +1 -1
  32. package/dist/gateway/static/root/assets/{field-primitives-BFcrNeTU.js → field-primitives-HUR6JElP.js} +1 -1
  33. package/dist/gateway/static/root/assets/{heartbeat-config-api-CEg4Vr9R.js → heartbeat-config-api-DusckjUX.js} +1 -1
  34. package/dist/gateway/static/root/assets/{index-CZfy9oxs.js → index-BYcGfwcE.js} +97 -97
  35. package/dist/gateway/static/root/assets/index-V7MQ7834.css +1 -0
  36. package/dist/gateway/static/root/assets/logs-page-_HcZ2fgK.js +1 -0
  37. package/dist/gateway/static/root/assets/sessions-page-iezSMjho.js +1 -0
  38. package/dist/gateway/static/root/assets/{settings-form-section-BqdzA28u.js → settings-form-section-a0qGVOlr.js} +1 -1
  39. package/dist/gateway/static/root/assets/settings-page-C9_nYQwM.js +3 -0
  40. package/dist/gateway/static/root/assets/{share-preview-page-Di5Bzh4g.js → share-preview-page-DExl7CJy.js} +1 -1
  41. package/dist/gateway/static/root/assets/skills-page-BlgGD93t.js +2 -0
  42. package/dist/gateway/static/root/assets/{theme-store-CNqbmTNV.js → theme-store-C0Ehmdo5.js} +1 -1
  43. package/dist/gateway/static/root/assets/url-fxyYANfA.js +3 -0
  44. package/dist/gateway/static/root/assets/{utils-BWm2tG2w.js → utils-DRQryzdn.js} +1 -1
  45. package/dist/gateway/static/root/assets/voice-api-key-field-D0viACE2.js +1 -0
  46. package/dist/gateway/static/root/assets/workflow-page.utils-DnG8JBhV.js +1 -0
  47. package/dist/gateway/static/root/assets/workflows-page-BvMobnJP.js +27 -0
  48. package/dist/gateway/static/root/index.html +7 -5
  49. package/dist/package.js +1 -1
  50. package/dist/src/agent/agent-manager.js +7 -7
  51. package/dist/src/agent/agent-scope.js +1 -1
  52. package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
  53. package/dist/src/agent/context/workspace-seed.js +2 -2
  54. package/dist/src/agent/goals/goal-run-store.js +4 -4
  55. package/dist/src/agent/goals/persistent-goal-service.js +1 -1
  56. package/dist/src/agent/goals/post-turn.js +2 -2
  57. package/dist/src/agent/image/load-image-media.js +2 -2
  58. package/dist/src/agent/ipc/bus.js +1 -1
  59. package/dist/src/agent/ipc/inbox.js +2 -2
  60. package/dist/src/agent/ipc/socket.js +1 -1
  61. package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
  62. package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
  63. package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
  64. package/dist/src/agent/mcp/mcp-transport.js +1 -1
  65. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  66. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  67. package/dist/src/agent/memory/dreaming/events.js +1 -1
  68. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  69. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  70. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  71. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  72. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  73. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  74. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  75. package/dist/src/agent/models/manager.js +1 -1
  76. package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
  77. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  78. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  79. package/dist/src/agent/sandbox/path-policy.js +2 -2
  80. package/dist/src/agent/service/build-direct-message-content.js +1 -1
  81. package/dist/src/agent/service.js +4 -4
  82. package/dist/src/agent/session/session-inspector.js +1 -1
  83. package/dist/src/agent/skills/config.js +1 -1
  84. package/dist/src/agent/skills/hub-hash.js +2 -2
  85. package/dist/src/agent/skills/hub-lock.js +1 -1
  86. package/dist/src/agent/skills/hub-pull.js +2 -2
  87. package/dist/src/agent/skills/index.js +1 -1
  88. package/dist/src/agent/skills/managed-store.js +1 -1
  89. package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js +20 -18
  90. package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js.map +1 -1
  91. package/dist/src/agent/skills/scanner.js +1 -1
  92. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  93. package/dist/src/agent/skills/skill-manager.js +1 -1
  94. package/dist/src/agent/tools/cronjob-tool.d.ts +6 -0
  95. package/dist/src/agent/tools/cronjob-tool.js +74 -9
  96. package/dist/src/agent/tools/cronjob-tool.js.map +1 -1
  97. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  98. package/dist/src/agent/tools/edit.d.ts +5 -1
  99. package/dist/src/agent/tools/edit.js +7 -5
  100. package/dist/src/agent/tools/edit.js.map +1 -1
  101. package/dist/src/agent/tools/factory.js +3 -3
  102. package/dist/src/agent/tools/factory.js.map +1 -1
  103. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  104. package/dist/src/agent/tools/send-media.js +1 -1
  105. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  106. package/dist/src/agent/tools/workflow-tool.js +1 -1
  107. package/dist/src/agent/tools/write.d.ts +5 -1
  108. package/dist/src/agent/tools/write.js +8 -6
  109. package/dist/src/agent/tools/write.js.map +1 -1
  110. package/dist/src/agent/workflow/agent-progress.js +2 -0
  111. package/dist/src/agent/workflow/agent-progress.js.map +1 -1
  112. package/dist/src/agent/workflow/builtins/client-proposal.d.ts +12 -0
  113. package/dist/src/agent/workflow/builtins/client-proposal.js +155 -0
  114. package/dist/src/agent/workflow/builtins/client-proposal.js.map +1 -0
  115. package/dist/src/agent/workflow/builtins/competitor-scan.d.ts +12 -0
  116. package/dist/src/agent/workflow/builtins/competitor-scan.js +150 -0
  117. package/dist/src/agent/workflow/builtins/competitor-scan.js.map +1 -0
  118. package/dist/src/agent/workflow/builtins/content-draft.d.ts +13 -0
  119. package/dist/src/agent/workflow/builtins/content-draft.js +146 -0
  120. package/dist/src/agent/workflow/builtins/content-draft.js.map +1 -0
  121. package/dist/src/agent/workflow/builtins/content-repurpose.d.ts +11 -0
  122. package/dist/src/agent/workflow/builtins/content-repurpose.js +137 -0
  123. package/dist/src/agent/workflow/builtins/content-repurpose.js.map +1 -0
  124. package/dist/src/agent/workflow/builtins/decision-compare.d.ts +13 -0
  125. package/dist/src/agent/workflow/builtins/decision-compare.js +173 -0
  126. package/dist/src/agent/workflow/builtins/decision-compare.js.map +1 -0
  127. package/dist/src/agent/workflow/builtins/inbox-triage.d.ts +11 -0
  128. package/dist/src/agent/workflow/builtins/inbox-triage.js +148 -0
  129. package/dist/src/agent/workflow/builtins/inbox-triage.js.map +1 -0
  130. package/dist/src/agent/workflow/builtins/index.d.ts +10 -1
  131. package/dist/src/agent/workflow/builtins/index.js +46 -1
  132. package/dist/src/agent/workflow/builtins/index.js.map +1 -1
  133. package/dist/src/agent/workflow/builtins/meeting-prep.d.ts +12 -0
  134. package/dist/src/agent/workflow/builtins/meeting-prep.js +144 -0
  135. package/dist/src/agent/workflow/builtins/meeting-prep.js.map +1 -0
  136. package/dist/src/agent/workflow/builtins/offer-design.d.ts +12 -0
  137. package/dist/src/agent/workflow/builtins/offer-design.js +161 -0
  138. package/dist/src/agent/workflow/builtins/offer-design.js.map +1 -0
  139. package/dist/src/agent/workflow/builtins/weekly-review.d.ts +12 -0
  140. package/dist/src/agent/workflow/builtins/weekly-review.js +131 -0
  141. package/dist/src/agent/workflow/builtins/weekly-review.js.map +1 -0
  142. package/dist/src/agent/workflow/catalog.js +1 -1
  143. package/dist/src/agent/workflow/step-labels.js +2 -2
  144. package/dist/src/agent/workflow/step-labels.js.map +1 -1
  145. package/dist/src/agent/workflow/subagent-runner.js +3 -1
  146. package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
  147. package/dist/src/agent/workflow/types.d.ts +4 -0
  148. package/dist/src/auth/credentials.js +3 -3
  149. package/dist/src/auth/profiles/store.js +1 -1
  150. package/dist/src/auth/sync-provider-auth.js +1 -1
  151. package/dist/src/browser/cache-dir-policy.js +1 -1
  152. package/dist/src/browser/cdp-local-launcher.js +2 -2
  153. package/dist/src/browser/providers/browser-ext-install.js +3 -3
  154. package/dist/src/browser/providers/cloakbrowser.js +4 -4
  155. package/dist/src/browser/providers/playwright-doctor.js +1 -1
  156. package/dist/src/browser/stealth.js +1 -1
  157. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  158. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  159. package/dist/src/channels/outbound/persist-store.js +1 -1
  160. package/dist/src/channels/pairing/allow-from-file.js +1 -1
  161. package/dist/src/channels/pairing/pairing-store.js +2 -2
  162. package/dist/src/chat-commands/agent-edit.d.ts +4 -0
  163. package/dist/src/chat-commands/agent-edit.js +136 -0
  164. package/dist/src/chat-commands/agent-edit.js.map +1 -0
  165. package/dist/src/chat-commands/builtins/config.js +2 -2
  166. package/dist/src/chat-commands/context.js +1 -1
  167. package/dist/src/chat-commands/index.d.ts +1 -0
  168. package/dist/src/chat-commands/index.js +3 -1
  169. package/dist/src/chat-commands/index.js.map +1 -1
  170. package/dist/src/cli/bin.js +2 -0
  171. package/dist/src/cli/bin.js.map +1 -1
  172. package/dist/src/cli/commands/config.js +1 -1
  173. package/dist/src/cli/commands/cron.js +42 -3
  174. package/dist/src/cli/commands/cron.js.map +1 -1
  175. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  176. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  177. package/dist/src/cli/commands/doctor/checks/session-integrity.js +79 -56
  178. package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -1
  179. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  180. package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
  181. package/dist/src/cli/commands/extension-dev.js +1 -1
  182. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  183. package/dist/src/cli/commands/extension-pack.js +1 -1
  184. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  185. package/dist/src/cli/commands/gateway/logs.js +1 -1
  186. package/dist/src/cli/commands/image.js +1 -1
  187. package/dist/src/cli/commands/init.js +4 -4
  188. package/dist/src/cli/commands/onboard.js +1 -1
  189. package/dist/src/cli/commands/update.js +86 -79
  190. package/dist/src/cli/commands/update.js.map +1 -1
  191. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  192. package/dist/src/commands/agents.config.d.ts +3 -2
  193. package/dist/src/commands/agents.config.js +5 -2
  194. package/dist/src/commands/agents.config.js.map +1 -1
  195. package/dist/src/config/agent-profile.js +1 -1
  196. package/dist/src/config/agent-typed-models.d.ts +2 -7
  197. package/dist/src/config/agent-typed-models.js +3 -14
  198. package/dist/src/config/agent-typed-models.js.map +1 -1
  199. package/dist/src/config/gateway-bind.js +1 -1
  200. package/dist/src/config/index.js +5 -5
  201. package/dist/src/config/loader.js +2 -2
  202. package/dist/src/config/localized-text.d.ts +6 -0
  203. package/dist/src/config/localized-text.js +42 -0
  204. package/dist/src/config/localized-text.js.map +1 -0
  205. package/dist/src/config/models-json.d.ts +6 -6
  206. package/dist/src/config/models-json.js +2 -2
  207. package/dist/src/config/paths-state.js +1 -1
  208. package/dist/src/config/profile.js +2 -2
  209. package/dist/src/config/schema.d.ts +6 -21
  210. package/dist/src/config/schema.js +4 -4
  211. package/dist/src/config/schema.js.map +1 -1
  212. package/dist/src/config/workspace-path.js +1 -1
  213. package/dist/src/cron/executor.d.ts +2 -0
  214. package/dist/src/cron/executor.js +113 -3
  215. package/dist/src/cron/executor.js.map +1 -1
  216. package/dist/src/cron/persistence.js +1 -1
  217. package/dist/src/cron/run-log-store.js +1 -1
  218. package/dist/src/cron/types.d.ts +8 -1
  219. package/dist/src/cron/validation.d.ts +4 -0
  220. package/dist/src/cron/validation.js +4 -3
  221. package/dist/src/cron/validation.js.map +1 -1
  222. package/dist/src/cron/workflow-run-completion.d.ts +23 -0
  223. package/dist/src/cron/workflow-run-completion.js +72 -0
  224. package/dist/src/cron/workflow-run-completion.js.map +1 -0
  225. package/dist/src/daemon/constants.js +1 -1
  226. package/dist/src/daemon/install-plan.js +2 -2
  227. package/dist/src/daemon/launchd.js +2 -2
  228. package/dist/src/daemon/schtasks.js +2 -2
  229. package/dist/src/daemon/systemd.js +2 -2
  230. package/dist/src/extensions/bundle-mcp.js +1 -1
  231. package/dist/src/extensions/discover-extensions.js +1 -1
  232. package/dist/src/extensions/health.js +1 -1
  233. package/dist/src/extensions/loader.js +1 -1
  234. package/dist/src/extensions/lockfile.js +2 -2
  235. package/dist/src/extensions/update.d.ts +51 -0
  236. package/dist/src/extensions/update.js +260 -0
  237. package/dist/src/extensions/update.js.map +1 -0
  238. package/dist/src/gateway/agents-admin.d.ts +15 -8
  239. package/dist/src/gateway/agents-admin.js +78 -29
  240. package/dist/src/gateway/agents-admin.js.map +1 -1
  241. package/dist/src/gateway/file-path-classifier.js +2 -2
  242. package/dist/src/gateway/heartbeat/service.js +1 -1
  243. package/dist/src/gateway/hono/lib/config-payload.d.ts +5 -0
  244. package/dist/src/gateway/hono/lib/config-payload.js +3 -2
  245. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  246. package/dist/src/gateway/hono/lib/extension-store.js +2 -2
  247. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  248. package/dist/src/gateway/hono/middleware/auth.d.ts +2 -0
  249. package/dist/src/gateway/hono/middleware/auth.js +12 -7
  250. package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
  251. package/dist/src/gateway/hono/oauth.js +1 -1
  252. package/dist/src/gateway/hono/routes/agents.js +56 -13
  253. package/dist/src/gateway/hono/routes/agents.js.map +1 -1
  254. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  255. package/dist/src/gateway/hono/routes/config-patch/agents.js +1 -1
  256. package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
  257. package/dist/src/gateway/hono/routes/dreaming.js +1 -1
  258. package/dist/src/gateway/hono/routes/host-fs.js +2 -2
  259. package/dist/src/gateway/hono/routes/models.js +1 -1
  260. package/dist/src/gateway/hono/routes/shares.js +1 -1
  261. package/dist/src/gateway/hono/routes/update.js +55 -107
  262. package/dist/src/gateway/hono/routes/update.js.map +1 -1
  263. package/dist/src/gateway/hono/routes/workflows.js +3 -1
  264. package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
  265. package/dist/src/gateway/hono/routes/workspace.js +4 -4
  266. package/dist/src/gateway/lock.js +3 -3
  267. package/dist/src/gateway/ports.js +1 -1
  268. package/dist/src/gateway/server.js +2 -0
  269. package/dist/src/gateway/server.js.map +1 -1
  270. package/dist/src/gateway/service/agent-runner.js +2 -2
  271. package/dist/src/gateway/service/marketplace-service.js +2 -2
  272. package/dist/src/gateway/service.js +3 -2
  273. package/dist/src/gateway/service.js.map +1 -1
  274. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  275. package/dist/src/heartbeat/index.js +1 -1
  276. package/dist/src/infra/brew.d.ts +4 -0
  277. package/dist/src/infra/brew.js +20 -0
  278. package/dist/src/infra/brew.js.map +1 -0
  279. package/dist/src/infra/package-json.d.ts +2 -0
  280. package/dist/src/infra/package-json.js +23 -0
  281. package/dist/src/infra/package-json.js.map +1 -0
  282. package/dist/src/infra/package-update-steps.d.ts +35 -0
  283. package/dist/src/infra/package-update-steps.js +304 -0
  284. package/dist/src/infra/package-update-steps.js.map +1 -0
  285. package/dist/src/infra/path-env.d.ts +11 -0
  286. package/dist/src/infra/path-env.js +90 -0
  287. package/dist/src/infra/path-env.js.map +1 -0
  288. package/dist/src/infra/path-prepend.d.ts +7 -0
  289. package/dist/src/infra/path-prepend.js +44 -0
  290. package/dist/src/infra/path-prepend.js.map +1 -0
  291. package/dist/src/infra/restart.js +2 -2
  292. package/dist/src/infra/stable-node-path.d.ts +2 -0
  293. package/dist/src/infra/stable-node-path.js +28 -0
  294. package/dist/src/infra/stable-node-path.js.map +1 -0
  295. package/dist/src/infra/update-check.js +1 -1
  296. package/dist/src/infra/update-global.d.ts +30 -23
  297. package/dist/src/infra/update-global.js +114 -65
  298. package/dist/src/infra/update-global.js.map +1 -1
  299. package/dist/src/infra/update-lock.js +3 -3
  300. package/dist/src/infra/update-log.d.ts +1 -0
  301. package/dist/src/infra/update-log.js +12 -0
  302. package/dist/src/infra/update-log.js.map +1 -0
  303. package/dist/src/infra/update-restart.d.ts +20 -0
  304. package/dist/src/infra/update-restart.js +165 -0
  305. package/dist/src/infra/update-restart.js.map +1 -0
  306. package/dist/src/infra/update-runner.d.ts +89 -1
  307. package/dist/src/infra/update-runner.js +604 -173
  308. package/dist/src/infra/update-runner.js.map +1 -1
  309. package/dist/src/infra/update-startup.d.ts +3 -0
  310. package/dist/src/infra/update-startup.js +10 -6
  311. package/dist/src/infra/update-startup.js.map +1 -1
  312. package/dist/src/infra/write-file-atomic.js +2 -2
  313. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  314. package/dist/src/providers/index.js +2 -2
  315. package/dist/src/providers/model-registry.js +1 -1
  316. package/dist/src/routing/resolve-route.d.ts +3 -1
  317. package/dist/src/routing/resolve-route.js.map +1 -1
  318. package/dist/src/session/config-store.js +2 -2
  319. package/dist/src/session/init-session-turn.js +2 -2
  320. package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
  321. package/dist/src/session/parity/sessions-json-file.js +1 -1
  322. package/dist/src/session/parity/transcript-file-lock.js +2 -2
  323. package/dist/src/session/parity/transcript-paths.js +1 -1
  324. package/dist/src/session/resolve-session.js +4 -4
  325. package/dist/src/session/search-index-cache.js +1 -1
  326. package/dist/src/session/search-index.js +1 -1
  327. package/dist/src/session/session-title.js +2 -2
  328. package/dist/src/session/store.d.ts +5 -3
  329. package/dist/src/session/store.js +71 -25
  330. package/dist/src/session/store.js.map +1 -1
  331. package/dist/src/share/share-auto.js +2 -2
  332. package/dist/src/share/share-store.js +3 -3
  333. package/dist/src/share/share-thumbnail.js +2 -2
  334. package/dist/src/share/share-zip.js +1 -1
  335. package/dist/src/share/site-share-store.js +3 -3
  336. package/dist/src/share/site-static-serve.js +1 -1
  337. package/dist/src/tui/clipboard-image.js +3 -3
  338. package/dist/src/tui/theme-manager.js +1 -1
  339. package/dist/src/tui/tui-keybindings-file.js +1 -1
  340. package/dist/src/tui/tui-scoped-models.js +2 -2
  341. package/dist/src/tui/tui-settings.js +1 -1
  342. package/dist/src/tui/tui.js +3 -3
  343. package/dist/src/tunnel/frpc-binary.js +3 -3
  344. package/dist/src/tunnel/frpc-config.js +1 -1
  345. package/dist/src/tunnel/frpc-extract.js +1 -1
  346. package/dist/src/tunnel/tunnel-state.js +1 -1
  347. package/dist/src/utils/logger/audit.js +1 -1
  348. package/dist/src/utils/logger/log-store.js +1 -1
  349. package/dist/src/utils/logger/rotation.js +1 -1
  350. package/dist/src/utils/logger/stats.d.ts +1 -1
  351. package/dist/src/voice/tts/audio.js +1 -1
  352. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  353. package/dist/src/workflows/domain/event.d.ts +3 -0
  354. package/dist/src/workflows/domain/run.d.ts +3 -0
  355. package/dist/src/workflows/domain/run.js.map +1 -1
  356. package/dist/src/workflows/engine/projector.js +17 -0
  357. package/dist/src/workflows/engine/projector.js.map +1 -1
  358. package/dist/src/workflows/engine/workflow-engine.js +127 -0
  359. package/dist/src/workflows/engine/workflow-engine.js.map +1 -1
  360. package/dist/src/workflows/index.js +1 -1
  361. package/dist/src/workflows/service/run-view-to-snapshot.js +3 -1
  362. package/dist/src/workflows/service/run-view-to-snapshot.js.map +1 -1
  363. package/dist/src/workflows/service/workflow-run-service.d.ts +1 -0
  364. package/dist/src/workflows/service/workflow-run-service.js +4 -1
  365. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  366. package/dist/src/workflows/service/workflow-session-bridge.js +1 -1
  367. package/dist/src/workflows/store/event-store.js +1 -1
  368. package/dist/src/workflows/store/run-store.js +1 -1
  369. package/package.json +1 -1
  370. package/dist/gateway/static/root/assets/agents-B6PJB07W.js +0 -222
  371. package/dist/gateway/static/root/assets/apps-page-BOr0B1wv.js +0 -1
  372. package/dist/gateway/static/root/assets/channels-settings-BelUKggl.js +0 -1
  373. package/dist/gateway/static/root/assets/cron-api-CjOg-BIj.js +0 -1
  374. package/dist/gateway/static/root/assets/cron-dreaming-jobs-DueM3rBz.js +0 -2
  375. package/dist/gateway/static/root/assets/cron-page-DhoZmZXb.js +0 -1
  376. package/dist/gateway/static/root/assets/dist-6LecgDx5.js +0 -1
  377. package/dist/gateway/static/root/assets/index-CiN1cQiQ.css +0 -1
  378. package/dist/gateway/static/root/assets/logs-page-BwWLfqvd.js +0 -1
  379. package/dist/gateway/static/root/assets/sessions-page-DV5WN8uk.js +0 -1
  380. package/dist/gateway/static/root/assets/settings-page-CfOBRbPX.js +0 -3
  381. package/dist/gateway/static/root/assets/skills-page-D0H5Kaxg.js +0 -2
  382. package/dist/gateway/static/root/assets/url-aYn-Rj1C.js +0 -7
  383. package/dist/gateway/static/root/assets/voice-api-key-field-X2UfnHeq.js +0 -1
  384. package/dist/gateway/static/root/assets/workflows-page-BOPpO3NG.js +0 -27
@@ -1 +1 @@
1
- {"version":3,"file":"update-runner.js","names":[],"sources":["../../../src/infra/update-runner.ts"],"sourcesContent":["// src/infra/update-runner.ts\n\nimport { spawn } from 'node:child_process';\nimport { access, readdir, unlink } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { createLogger } from '../utils/logger.js';\n\nimport type { UpdateChannel } from './update-channels.js';\n\nconst log = createLogger('UpdateRunner');\n\nconst AUTO_UPDATE_TIMEOUT_MS = 45 * 60 * 1000; // 45 minutes\n\nexport type AutoUpdateResult = {\n ok: boolean;\n exitCode: number | null;\n reason?: string;\n stdout?: string;\n stderr?: string;\n};\n\ntype SpawnUpdateParams = {\n channel: UpdateChannel;\n root?: string | null;\n timeoutMs?: number;\n onProgress?: (line: string, source: 'stdout' | 'stderr') => void | Promise<void>;\n};\n\nfunction createLineEmitter(\n onProgress?: (line: string, source: 'stdout' | 'stderr') => void | Promise<void>,\n) {\n let bufOut = '';\n let bufErr = '';\n const flush = (buf: string, source: 'stdout' | 'stderr'): string => {\n const parts = buf.split('\\n');\n const rest = parts.pop() ?? '';\n for (const line of parts) {\n if (line.length) void onProgress?.(line, source);\n }\n return rest;\n };\n return {\n pushStdout(chunk: string) {\n bufOut += chunk;\n bufOut = flush(bufOut, 'stdout');\n },\n pushStderr(chunk: string) {\n bufErr += chunk;\n bufErr = flush(bufErr, 'stderr');\n },\n flushEnd() {\n if (bufOut.length) void onProgress?.(bufOut, 'stdout');\n if (bufErr.length) void onProgress?.(bufErr, 'stderr');\n },\n };\n}\n\nasync function spawnUpdateCommand(params: SpawnUpdateParams): Promise<AutoUpdateResult> {\n const timeoutMs = params.timeoutMs ?? AUTO_UPDATE_TIMEOUT_MS;\n const baseArgs = ['update', '--yes', '--channel', params.channel, '--json'];\n const argv = await resolveUpdateCommandArgv(baseArgs, params.root ?? null);\n\n return new Promise<AutoUpdateResult>((resolve) => {\n const child = spawn(argv[0], argv.slice(1), {\n env: {\n ...process.env,\n XOPC_AUTO_UPDATE: '1',\n },\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: false,\n });\n\n let stdout = '';\n let stderr = '';\n let stdoutTruncated = false;\n let stderrTruncated = false;\n\n const lineEmitter = createLineEmitter(params.onProgress);\n\n const timeoutId = setTimeout(() => {\n child.kill('SIGTERM');\n }, timeoutMs);\n\n const finish = (result: AutoUpdateResult) => {\n clearTimeout(timeoutId);\n lineEmitter.flushEnd();\n resolve(result);\n };\n\n child.stdout?.on('data', (chunk: Buffer) => {\n const text = chunk.toString();\n stdout += text;\n lineEmitter.pushStdout(text);\n if (stdout.length > 64_000) {\n if (!stdoutTruncated) {\n log.warn('Update command stdout exceeded 64KB; truncating');\n stdoutTruncated = true;\n }\n stdout = stdout.slice(-32_000);\n }\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n const text = chunk.toString();\n stderr += text;\n lineEmitter.pushStderr(text);\n if (stderr.length > 64_000) {\n if (!stderrTruncated) {\n log.warn('Update command stderr exceeded 64KB; truncating');\n stderrTruncated = true;\n }\n stderr = stderr.slice(-32_000);\n }\n });\n\n child.on('error', (err) => {\n log.error({ err }, `Update subprocess spawn error: ${err.message}`);\n finish({ ok: false, exitCode: null, reason: err.message, stdout, stderr });\n });\n\n child.on('exit', (code, signal) => {\n if (signal === 'SIGTERM' || code === 143) {\n log.warn({ code, signal }, 'Update subprocess timed out; attempting lock file cleanup');\n void cleanupNpmLockFiles(params.root).catch((cleanupErr) => {\n log.warn({ err: cleanupErr }, 'Failed to clean npm lock files after timeout');\n });\n finish({ ok: false, exitCode: code, reason: 'timeout', stdout, stderr });\n return;\n }\n finish({\n ok: code === 0,\n exitCode: code,\n reason: code === 0 ? undefined : 'non-zero-exit',\n stdout,\n stderr,\n });\n });\n });\n}\n\nexport async function runAutoUpdateCommand(params: {\n channel: UpdateChannel;\n root?: string | null;\n timeoutMs?: number;\n}): Promise<AutoUpdateResult> {\n return spawnUpdateCommand(params);\n}\n\nexport async function runAutoUpdateCommandWithProgress(params: {\n channel: UpdateChannel;\n root?: string | null;\n timeoutMs?: number;\n onProgress?: (line: string, source: 'stdout' | 'stderr') => void | Promise<void>;\n}): Promise<AutoUpdateResult> {\n return spawnUpdateCommand(params);\n}\n\nasync function cleanupNpmLockFiles(root: string | null | undefined): Promise<void> {\n if (!root) return;\n let entries: string[];\n try {\n entries = await readdir(root);\n } catch {\n return;\n }\n for (const entry of entries) {\n if (entry.startsWith('.package-lock')) {\n try {\n await unlink(join(root, entry));\n } catch {\n // best-effort\n }\n }\n }\n}\n\n/**\n * Resolve the argv array for spawning the update command.\n *\n * Priority:\n * 1. process.execPath + process.argv[1] (current runtime + entry point)\n * 2. process.execPath + known dist entry points in root\n * 3. Fallback to bare `xopc` (assumes global install)\n */\nasync function resolveUpdateCommandArgv(\n baseArgs: string[],\n root: string | null,\n): Promise<string[]> {\n const execPath = process.execPath?.trim();\n const argv1 = process.argv[1]?.trim();\n\n if (execPath && argv1) {\n return [execPath, argv1, ...baseArgs];\n }\n\n if (execPath && root) {\n const candidates = [join(root, 'dist/src/cli/bin.js'), join(root, 'dist/index.js')];\n for (const candidate of candidates) {\n try {\n await access(candidate);\n return [execPath, candidate, ...baseArgs];\n } catch {\n // try next\n }\n }\n }\n\n log.warn('Falling back to bare `xopc` command — version mismatch possible');\n try {\n const { execSync } = await import('node:child_process');\n const cmd = process.platform === 'win32' ? 'where xopc' : 'which xopc';\n const whichResult = execSync(cmd, { encoding: 'utf-8', timeout: 3000 }).trim();\n if (whichResult) {\n log.info({ resolvedPath: whichResult.split(/\\r?\\n/)[0]?.trim() }, 'Resolved xopc via PATH');\n }\n } catch {\n log.warn('Could not resolve `xopc` in PATH; update command may fail');\n }\n\n return ['xopc', ...baseArgs];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"update-runner.js","names":[],"sources":["../../../src/infra/update-runner.ts"],"sourcesContent":["// src/infra/update-runner.ts — unified gateway/CLI update runner (OpenClaw-aligned)\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { readPackageName, readPackageVersion } from './package-json.js';\nimport { runGlobalPackageUpdateSteps, type PackageUpdateStepResult } from './package-update-steps.js';\nimport { createDefaultCommandRunner, runCommandWithTimeout } from './run-command.js';\nimport { resolveStableNodePath } from './stable-node-path.js';\nimport { trimLogTail } from './update-log.js';\nimport { DEFAULT_PACKAGE_CHANNEL, type UpdateChannel } from './update-channels.js';\nimport { compareSemver, resolveNpmChannelTag } from './update-check.js';\nimport { runPostUpdateExtensionSync, type ExtensionPostUpdateResult } from '../extensions/update.js';\nimport {\n cleanupGlobalRenameDirs,\n createGlobalInstallEnv,\n detectGlobalInstallManagerForRoot,\n resolveGlobalInstallSpec,\n resolveGlobalInstallTarget,\n XOPC_PACKAGE_NAME,\n type GlobalInstallManager,\n} from './update-global.js';\nimport {\n maybeRestartGatewayAfterUpdate,\n type InProcessRestartTrigger,\n type UpdateRestartResult,\n} from './update-restart.js';\n\nconst DEFAULT_TIMEOUT_MS = 45 * 60 * 1000;\nconst MAX_LOG_CHARS = 8000;\nconst CORE_PACKAGE_NAMES = new Set([XOPC_PACKAGE_NAME]);\n\nexport type UpdateStepResult = {\n name: string;\n command: string;\n cwd: string;\n durationMs: number;\n exitCode: number | null;\n stdoutTail?: string | null;\n stderrTail?: string | null;\n};\n\nexport type UpdatePostUpdateResult = {\n extensions?: ExtensionPostUpdateResult;\n restart?: UpdateRestartResult;\n};\n\nexport type UpdateRunResult = {\n status: 'ok' | 'error' | 'skipped';\n mode: 'git' | 'pnpm' | 'npm' | 'unknown';\n root?: string;\n reason?: string;\n before?: { sha?: string | null; version?: string | null };\n after?: { sha?: string | null; version?: string | null };\n steps: UpdateStepResult[];\n durationMs: number;\n postUpdate?: UpdatePostUpdateResult;\n};\n\nexport type UpdateStepInfo = {\n name: string;\n command: string;\n index: number;\n total: number;\n};\n\nexport type UpdateStepCompletion = UpdateStepInfo & {\n durationMs: number;\n exitCode: number | null;\n stderrTail?: string | null;\n};\n\nexport type UpdateStepProgress = {\n onStepStart?: (step: UpdateStepInfo) => void;\n onStepComplete?: (step: UpdateStepCompletion) => void;\n};\n\nexport type UpdateInstallSurface =\n | { kind: 'git'; mode: 'git'; root: string; packageRoot: string }\n | { kind: 'global'; mode: GlobalInstallManager; root: string; packageRoot: string }\n | { kind: 'package-root'; mode: 'unknown'; root: string; packageRoot: string }\n | { kind: 'missing'; mode: 'unknown'; root?: string; packageRoot?: undefined };\n\ntype UpdateRunnerOptions = {\n cwd?: string;\n argv1?: string;\n channel?: UpdateChannel;\n timeoutMs?: number;\n progress?: UpdateStepProgress;\n skipExtensionSync?: boolean;\n shouldRestart?: boolean;\n triggerInProcessRestart?: InProcessRestartTrigger;\n};\n\ntype CommandRunner = (\n argv: string[],\n options: { timeoutMs: number; cwd?: string; env?: NodeJS.ProcessEnv },\n) => Promise<{ stdout: string; stderr: string; code: number | null }>;\n\nfunction normalizeDir(value?: string | null): string | null {\n if (!value) return null;\n const trimmed = value.trim();\n return trimmed ? path.resolve(trimmed) : null;\n}\n\nfunction resolveNodeModulesBinPackageRoot(argv1: string): string | null {\n const normalized = path.resolve(argv1);\n const parts = normalized.split(path.sep);\n const binIndex = parts.lastIndexOf('.bin');\n if (binIndex <= 0 || parts[binIndex - 1] !== 'node_modules') return null;\n return path.join(parts.slice(0, binIndex).join(path.sep), path.basename(normalized));\n}\n\nfunction buildStartDirs(opts: UpdateRunnerOptions): string[] {\n const dirs: string[] = [];\n const cwd = normalizeDir(opts.cwd);\n if (cwd) dirs.push(cwd);\n const argv1 = normalizeDir(opts.argv1);\n if (argv1) {\n dirs.push(path.dirname(argv1));\n const packageRoot = resolveNodeModulesBinPackageRoot(argv1);\n if (packageRoot) dirs.push(packageRoot);\n }\n const proc = normalizeDir(process.cwd());\n if (proc) dirs.push(proc);\n return Array.from(new Set(dirs));\n}\n\nasync function resolveComparablePath(target: string): Promise<string> {\n return fs.realpath(target).catch(() => path.resolve(target));\n}\n\nasync function pathsReferToSameLocation(left: string, right: string): Promise<boolean> {\n return (await resolveComparablePath(left)) === (await resolveComparablePath(right));\n}\n\nasync function looksLikeGitCheckout(root: string): Promise<boolean> {\n try {\n await fs.access(path.join(root, '.git'));\n return true;\n } catch {\n return false;\n }\n}\n\nasync function resolveGitRoot(\n runCommand: CommandRunner,\n candidates: string[],\n timeoutMs: number,\n): Promise<string | null> {\n for (const dir of candidates) {\n const res = await runCommand(['git', '-C', dir, 'rev-parse', '--show-toplevel'], {\n timeoutMs,\n }).catch(() => null);\n if (!res || res.code !== 0) continue;\n const root = res.stdout.trim();\n if (root) return root;\n }\n return null;\n}\n\nasync function findPackageRoot(candidates: string[]): Promise<string | null> {\n for (const dir of candidates) {\n let current = dir;\n for (let i = 0; i < 12; i += 1) {\n const pkgPath = path.join(current, 'package.json');\n try {\n const raw = await fs.readFile(pkgPath, 'utf-8');\n const parsed = JSON.parse(raw) as { name?: string };\n const name = parsed?.name?.trim();\n if (name && CORE_PACKAGE_NAMES.has(name)) return current;\n } catch {\n // ignore\n }\n const parent = path.dirname(current);\n if (parent === current) break;\n current = parent;\n }\n }\n return null;\n}\n\nfunction mergeCommandEnvironments(\n baseEnv: NodeJS.ProcessEnv | undefined,\n overrideEnv: NodeJS.ProcessEnv | undefined,\n): NodeJS.ProcessEnv | undefined {\n if (!baseEnv) return overrideEnv;\n if (!overrideEnv) return baseEnv;\n return { ...baseEnv, ...overrideEnv };\n}\n\nasync function buildUpdateCommandRunner(): Promise<{\n defaultCommandEnv: NodeJS.ProcessEnv | undefined;\n runCommand: CommandRunner;\n}> {\n const defaultCommandEnv = await createGlobalInstallEnv();\n return {\n defaultCommandEnv,\n runCommand: async (argv, options) => {\n const res = await runCommandWithTimeout(argv, {\n ...options,\n env: mergeCommandEnvironments(defaultCommandEnv, options.env),\n });\n return { stdout: res.stdout, stderr: res.stderr, code: res.code };\n },\n };\n}\n\ntype RunStepOptions = {\n runCommand: CommandRunner;\n name: string;\n argv: string[];\n cwd: string;\n timeoutMs: number;\n env?: NodeJS.ProcessEnv;\n progress?: UpdateStepProgress;\n stepIndex: number;\n totalSteps: number;\n};\n\nasync function runStep(opts: RunStepOptions): Promise<UpdateStepResult> {\n const { runCommand, name, argv, cwd, timeoutMs, env, progress, stepIndex, totalSteps } = opts;\n const command = argv.join(' ');\n const stepInfo: UpdateStepInfo = { name, command, index: stepIndex, total: totalSteps };\n progress?.onStepStart?.(stepInfo);\n const started = Date.now();\n const result = await runCommand(argv, { cwd, timeoutMs, env });\n const durationMs = Date.now() - started;\n const stderrTail = trimLogTail(result.stderr, MAX_LOG_CHARS);\n progress?.onStepComplete?.({ ...stepInfo, durationMs, exitCode: result.code, stderrTail });\n return {\n name,\n command,\n cwd,\n durationMs,\n exitCode: result.code,\n stdoutTail: trimLogTail(result.stdout, MAX_LOG_CHARS),\n stderrTail,\n };\n}\n\nfunction toUpdateStepResult(step: PackageUpdateStepResult): UpdateStepResult {\n return {\n name: step.name,\n command: step.command,\n cwd: step.cwd,\n durationMs: step.durationMs,\n exitCode: step.exitCode,\n stdoutTail: step.stdoutTail,\n stderrTail: step.stderrTail,\n };\n}\n\nexport async function resolveUpdateInstallSurface(\n opts: Pick<UpdateRunnerOptions, 'cwd' | 'argv1' | 'timeoutMs'> = {},\n): Promise<UpdateInstallSurface> {\n const { runCommand } = await buildUpdateCommandRunner();\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const candidates = buildStartDirs(opts);\n const pkgRoot = await findPackageRoot(candidates);\n\n let gitRoot = await resolveGitRoot(runCommand, candidates, timeoutMs);\n if (gitRoot && pkgRoot && !(await pathsReferToSameLocation(gitRoot, pkgRoot))) {\n gitRoot = null;\n }\n if (gitRoot && !pkgRoot) {\n return { kind: 'missing', mode: 'unknown', root: gitRoot };\n }\n if (gitRoot && pkgRoot && (await pathsReferToSameLocation(gitRoot, pkgRoot))) {\n return { kind: 'git', mode: 'git', root: gitRoot, packageRoot: pkgRoot };\n }\n if (!pkgRoot) {\n return { kind: 'missing', mode: 'unknown' };\n }\n\n const globalManager = await detectGlobalInstallManagerForRoot(runCommand, pkgRoot, timeoutMs);\n if (globalManager) {\n return { kind: 'global', mode: globalManager, root: pkgRoot, packageRoot: pkgRoot };\n }\n\n return { kind: 'package-root', mode: 'unknown', root: pkgRoot, packageRoot: pkgRoot };\n}\n\nasync function runGitUpdate(params: {\n gitRoot: string;\n runCommand: CommandRunner;\n timeoutMs: number;\n progress?: UpdateStepProgress;\n defaultCommandEnv?: NodeJS.ProcessEnv;\n}): Promise<UpdateRunResult> {\n const startedAt = Date.now();\n const steps: UpdateStepResult[] = [];\n const { gitRoot, runCommand, timeoutMs, progress, defaultCommandEnv } = params;\n const totalSteps = 7;\n let stepIndex = 0;\n const step = (name: string, argv: string[], cwd: string, env?: NodeJS.ProcessEnv) =>\n runStep({\n runCommand,\n name,\n argv,\n cwd,\n timeoutMs,\n env,\n progress,\n stepIndex: stepIndex++,\n totalSteps,\n });\n\n const beforeShaResult = await runCommand(['git', '-C', gitRoot, 'rev-parse', 'HEAD'], {\n cwd: gitRoot,\n timeoutMs,\n });\n const beforeSha = beforeShaResult.stdout.trim() || null;\n const beforeVersion = await readPackageVersion(gitRoot);\n\n const statusCheck = await step(\n 'clean check',\n ['git', '-C', gitRoot, 'status', '--porcelain'],\n gitRoot,\n );\n steps.push(statusCheck);\n if (statusCheck.stdoutTail?.trim()) {\n return {\n status: 'skipped',\n mode: 'git',\n root: gitRoot,\n reason: 'dirty',\n before: { sha: beforeSha, version: beforeVersion },\n steps,\n durationMs: Date.now() - startedAt,\n };\n }\n\n const fetchStep = await step(\n 'git fetch',\n ['git', '-C', gitRoot, 'fetch', '--all', '--prune', '--tags'],\n gitRoot,\n );\n steps.push(fetchStep);\n if (fetchStep.exitCode !== 0) {\n return {\n status: 'error',\n mode: 'git',\n root: gitRoot,\n reason: 'fetch-failed',\n before: { sha: beforeSha, version: beforeVersion },\n steps,\n durationMs: Date.now() - startedAt,\n };\n }\n\n const upstreamStep = await step(\n 'upstream check',\n ['git', '-C', gitRoot, 'rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}'],\n gitRoot,\n );\n steps.push(upstreamStep);\n if (upstreamStep.exitCode !== 0) {\n return {\n status: 'skipped',\n mode: 'git',\n root: gitRoot,\n reason: 'no-upstream',\n before: { sha: beforeSha, version: beforeVersion },\n steps,\n durationMs: Date.now() - startedAt,\n };\n }\n\n const rebaseStep = await step(\n 'git rebase',\n ['git', '-C', gitRoot, 'rebase', '@{upstream}'],\n gitRoot,\n );\n steps.push(rebaseStep);\n if (rebaseStep.exitCode !== 0) {\n return {\n status: 'error',\n mode: 'git',\n root: gitRoot,\n reason: 'rebase-failed',\n before: { sha: beforeSha, version: beforeVersion },\n steps,\n durationMs: Date.now() - startedAt,\n };\n }\n\n const depsStep = await step('deps install', ['pnpm', 'install'], gitRoot, defaultCommandEnv);\n steps.push(depsStep);\n if (depsStep.exitCode !== 0) {\n return {\n status: 'error',\n mode: 'git',\n root: gitRoot,\n reason: 'deps-install-failed',\n before: { sha: beforeSha, version: beforeVersion },\n steps,\n durationMs: Date.now() - startedAt,\n };\n }\n\n const buildStep = await step('build', ['pnpm', 'run', 'build'], gitRoot, defaultCommandEnv);\n steps.push(buildStep);\n if (buildStep.exitCode !== 0) {\n return {\n status: 'error',\n mode: 'git',\n root: gitRoot,\n reason: 'build-failed',\n before: { sha: beforeSha, version: beforeVersion },\n steps,\n durationMs: Date.now() - startedAt,\n };\n }\n\n const doctorEntry = path.join(gitRoot, 'dist/src/cli/bin.js');\n const doctorEntryExists = await fs.stat(doctorEntry).then(() => true).catch(() => false);\n if (doctorEntryExists) {\n const doctorNodePath = await resolveStableNodePath(process.execPath);\n const doctorStep = await step(\n 'xopc doctor',\n [doctorNodePath, doctorEntry, 'doctor', '--fix'],\n gitRoot,\n { ...defaultCommandEnv, XOPC_UPDATE_IN_PROGRESS: '1' },\n );\n steps.push(doctorStep);\n if (doctorStep.exitCode !== 0) {\n return {\n status: 'error',\n mode: 'git',\n root: gitRoot,\n reason: 'doctor-failed',\n before: { sha: beforeSha, version: beforeVersion },\n steps,\n durationMs: Date.now() - startedAt,\n };\n }\n }\n\n const afterShaStep = await step(\n 'git rev-parse HEAD (after)',\n ['git', '-C', gitRoot, 'rev-parse', 'HEAD'],\n gitRoot,\n );\n steps.push(afterShaStep);\n const afterVersion = await readPackageVersion(gitRoot);\n\n return {\n status: 'ok',\n mode: 'git',\n root: gitRoot,\n before: { sha: beforeSha, version: beforeVersion },\n after: { sha: afterShaStep.stdoutTail?.trim() ?? null, version: afterVersion },\n steps,\n durationMs: Date.now() - startedAt,\n };\n}\n\nasync function runGlobalUpdate(params: {\n pkgRoot: string;\n globalManager: GlobalInstallManager;\n channel: UpdateChannel;\n runCommand: CommandRunner;\n timeoutMs: number;\n defaultCommandEnv?: NodeJS.ProcessEnv;\n progress?: UpdateStepProgress;\n}): Promise<UpdateRunResult> {\n const startedAt = Date.now();\n const beforeVersion = await readPackageVersion(params.pkgRoot);\n const resolved = await resolveNpmChannelTag({ channel: params.channel, timeoutMs: 10_000 });\n if (!resolved.version) {\n return {\n status: 'error',\n mode: params.globalManager,\n root: params.pkgRoot,\n reason: 'registry-unreachable',\n before: { version: beforeVersion },\n steps: [],\n durationMs: Date.now() - startedAt,\n };\n }\n\n const comparison = compareSemver(beforeVersion ?? '0.0.0', resolved.version);\n if (comparison !== null && comparison >= 0) {\n return {\n status: 'skipped',\n mode: params.globalManager,\n root: params.pkgRoot,\n reason: 'up-to-date',\n before: { version: beforeVersion },\n after: { version: beforeVersion },\n steps: [],\n durationMs: Date.now() - startedAt,\n };\n }\n\n const installTarget = await resolveGlobalInstallTarget({\n manager: params.globalManager,\n runCommand: params.runCommand,\n timeoutMs: params.timeoutMs,\n pkgRoot: params.pkgRoot,\n });\n const packageName = (await readPackageName(params.pkgRoot)) ?? XOPC_PACKAGE_NAME;\n if (installTarget.globalRoot) {\n await cleanupGlobalRenameDirs({ globalRoot: installTarget.globalRoot, packageName });\n }\n\n const spec = resolveGlobalInstallSpec({\n version: resolved.version,\n env: params.defaultCommandEnv,\n });\n\n let stepIndex = 0;\n const packageUpdate = await runGlobalPackageUpdateSteps({\n installTarget,\n installSpec: spec,\n packageName,\n packageRoot: params.pkgRoot,\n runCommand: params.runCommand,\n timeoutMs: params.timeoutMs,\n ...(params.defaultCommandEnv === undefined ? {} : { env: params.defaultCommandEnv }),\n installCwd: params.pkgRoot,\n runStep: async (stepParams) => {\n const result = await runStep({\n runCommand: params.runCommand,\n name: stepParams.name,\n argv: stepParams.argv,\n cwd: stepParams.cwd ?? params.pkgRoot,\n timeoutMs: stepParams.timeoutMs,\n env: stepParams.env,\n progress: params.progress,\n stepIndex: stepIndex++,\n totalSteps: 3,\n });\n return {\n name: result.name,\n command: result.command,\n cwd: result.cwd,\n durationMs: result.durationMs,\n exitCode: result.exitCode,\n stdoutTail: result.stdoutTail,\n stderrTail: result.stderrTail,\n };\n },\n });\n\n const steps = packageUpdate.steps.map(toUpdateStepResult);\n return {\n status: packageUpdate.failedStep ? 'error' : 'ok',\n mode: params.globalManager,\n root: packageUpdate.verifiedPackageRoot ?? params.pkgRoot,\n reason: packageUpdate.failedStep ? 'global-install-failed' : undefined,\n before: { version: beforeVersion },\n after: { version: packageUpdate.afterVersion },\n steps,\n durationMs: Date.now() - startedAt,\n };\n}\n\nexport async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise<UpdateRunResult> {\n const startedAt = Date.now();\n const { defaultCommandEnv, runCommand } = await buildUpdateCommandRunner();\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const channel = opts.channel ?? DEFAULT_PACKAGE_CHANNEL;\n const candidates = buildStartDirs(opts);\n const pkgRoot = await findPackageRoot(candidates);\n\n let gitRoot = await resolveGitRoot(runCommand, candidates, timeoutMs);\n if (!gitRoot && pkgRoot) {\n const cwdRoot = normalizeDir(opts.cwd);\n if (\n cwdRoot &&\n (await pathsReferToSameLocation(cwdRoot, pkgRoot)) &&\n (await looksLikeGitCheckout(cwdRoot))\n ) {\n gitRoot = await resolveComparablePath(cwdRoot);\n }\n }\n if (gitRoot && pkgRoot && !(await pathsReferToSameLocation(gitRoot, pkgRoot))) {\n gitRoot = null;\n }\n\n if (gitRoot && pkgRoot && (await pathsReferToSameLocation(gitRoot, pkgRoot))) {\n return runGitUpdate({\n gitRoot,\n runCommand,\n timeoutMs,\n progress: opts.progress,\n defaultCommandEnv,\n });\n }\n\n if (!pkgRoot) {\n return {\n status: 'error',\n mode: 'unknown',\n reason: 'not-xopc-root',\n steps: [],\n durationMs: Date.now() - startedAt,\n };\n }\n\n const globalManager = await detectGlobalInstallManagerForRoot(runCommand, pkgRoot, timeoutMs);\n if (globalManager) {\n return runGlobalUpdate({\n pkgRoot,\n globalManager,\n channel,\n runCommand,\n timeoutMs,\n defaultCommandEnv,\n progress: opts.progress,\n });\n }\n\n return {\n status: 'skipped',\n mode: 'unknown',\n root: pkgRoot,\n reason: 'not-global-install',\n before: { version: await readPackageVersion(pkgRoot) },\n steps: [],\n durationMs: Date.now() - startedAt,\n };\n}\n\nexport async function runGatewayUpdateWithPostSteps(\n opts: UpdateRunnerOptions = {},\n): Promise<UpdateRunResult> {\n const channel = opts.channel ?? DEFAULT_PACKAGE_CHANNEL;\n const result = await runGatewayUpdate(opts);\n\n if (result.status !== 'ok') {\n return result;\n }\n\n const postUpdate: UpdatePostUpdateResult = {};\n\n if (!opts.skipExtensionSync) {\n const extensions = await runPostUpdateExtensionSync({\n channel,\n timeoutMs: opts.timeoutMs,\n });\n postUpdate.extensions = extensions;\n if (extensions.status === 'error') {\n return {\n ...result,\n status: 'error',\n reason: 'post-update-extensions',\n postUpdate,\n };\n }\n }\n\n const restart = await maybeRestartGatewayAfterUpdate({\n shouldRestart: opts.shouldRestart,\n expectedVersion: result.after?.version ?? undefined,\n triggerInProcessRestart: opts.triggerInProcessRestart,\n });\n postUpdate.restart = restart;\n\n return { ...result, postUpdate };\n}\n\nexport function formatUpdateApiResult(result: UpdateRunResult, channel: UpdateChannel): Record<string, unknown> {\n if (result.status === 'skipped' && result.reason === 'up-to-date') {\n return {\n status: 'up-to-date',\n currentVersion: result.before?.version ?? null,\n latestVersion: result.before?.version ?? null,\n channel,\n mode: result.mode,\n };\n }\n if (result.status === 'ok') {\n return {\n status: 'ok',\n previousVersion: result.before?.version ?? null,\n installedVersion: result.after?.version ?? null,\n channel,\n mode: result.mode,\n steps: result.steps.length,\n postUpdate: result.postUpdate ?? undefined,\n };\n }\n const failed = result.steps.find((step) => step.exitCode !== 0);\n return {\n status: 'error',\n reason: result.reason ?? 'update-failed',\n mode: result.mode,\n message: failed?.stderrTail ?? failed?.stdoutTail ?? result.reason,\n stderrTail: failed?.stderrTail ?? undefined,\n };\n}\n\nexport type AutoUpdateResult = {\n ok: boolean;\n exitCode: number | null;\n reason?: string;\n stdout?: string;\n stderr?: string;\n result?: UpdateRunResult;\n};\n\nexport async function runAutoUpdateCommand(params: {\n channel: UpdateChannel;\n root?: string | null;\n timeoutMs?: number;\n triggerInProcessRestart?: InProcessRestartTrigger;\n}): Promise<AutoUpdateResult> {\n const result = await runGatewayUpdateWithPostSteps({\n channel: params.channel,\n cwd: params.root ?? undefined,\n argv1: process.argv[1],\n timeoutMs: params.timeoutMs,\n triggerInProcessRestart: params.triggerInProcessRestart,\n });\n const payload = formatUpdateApiResult(result, params.channel);\n const stdout = JSON.stringify(payload);\n return {\n ok: result.status === 'ok' || (result.status === 'skipped' && result.reason === 'up-to-date'),\n exitCode: result.status === 'error' ? 1 : 0,\n reason: result.reason,\n stdout,\n stderr: result.steps.find((s) => s.exitCode !== 0)?.stderrTail ?? undefined,\n result,\n };\n}\n\nexport async function runAutoUpdateCommandWithProgress(params: {\n channel: UpdateChannel;\n root?: string | null;\n timeoutMs?: number;\n triggerInProcessRestart?: InProcessRestartTrigger;\n onProgress?: (line: string, source: 'stdout' | 'stderr') => void | Promise<void>;\n}): Promise<AutoUpdateResult> {\n const result = await runGatewayUpdateWithPostSteps({\n channel: params.channel,\n cwd: params.root ?? undefined,\n argv1: process.argv[1],\n timeoutMs: params.timeoutMs,\n triggerInProcessRestart: params.triggerInProcessRestart,\n progress: {\n onStepStart: (step) => void params.onProgress?.(`[${step.index + 1}/${step.total}] ${step.name}`, 'stdout'),\n onStepComplete: (step) => {\n if (step.exitCode !== 0 && step.stderrTail) {\n void params.onProgress?.(step.stderrTail, 'stderr');\n }\n },\n },\n });\n const payload = formatUpdateApiResult(result, params.channel);\n return {\n ok: result.status === 'ok' || (result.status === 'skipped' && result.reason === 'up-to-date'),\n exitCode: result.status === 'error' ? 1 : 0,\n reason: result.reason,\n stdout: JSON.stringify(payload),\n stderr: result.steps.find((s) => s.exitCode !== 0)?.stderrTail ?? undefined,\n result,\n };\n}\n\n// Legacy export for tests that import createDefaultCommandRunner path\nexport { createDefaultCommandRunner };\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import type { Config } from '../config/schema.js';
2
2
  import { type UpdateAvailable } from './update-check.js';
3
+ import type { InProcessRestartTrigger } from './update-restart.js';
3
4
  /** Get the cached update-available state (populated after startup check). */
4
5
  export declare function getUpdateAvailable(): UpdateAvailable | null;
5
6
  /**
@@ -8,6 +9,7 @@ export declare function getUpdateAvailable(): UpdateAvailable | null;
8
9
  export declare function runGatewayUpdateCheck(params: {
9
10
  config: Config;
10
11
  onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;
12
+ triggerInProcessRestart?: InProcessRestartTrigger;
11
13
  /** When true, bypass checkOnStart/auto-disabled early exit and throttle (for POST /api/update/check). */
12
14
  force?: boolean;
13
15
  }): Promise<void>;
@@ -17,4 +19,5 @@ export declare function runGatewayUpdateCheck(params: {
17
19
  export declare function scheduleGatewayUpdateCheck(params: {
18
20
  config: Config;
19
21
  onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;
22
+ triggerInProcessRestart?: InProcessRestartTrigger;
20
23
  }): () => void;
@@ -1,8 +1,8 @@
1
1
  import { PACKAGE_VERSION, init_package_version } from "../package-version.js";
2
- import { init_paths_state, resolveUpdateCheckStatePath } from "../config/paths-state.js";
2
+ import { init_write_file_atomic, writeTextAtomic } from "./write-file-atomic.js";
3
3
  import { createLogger } from "../utils/logger/index.js";
4
4
  import { init_logger } from "../utils/logger.js";
5
- import { init_write_file_atomic, writeTextAtomic } from "./write-file-atomic.js";
5
+ import { init_paths_state, resolveUpdateCheckStatePath } from "../config/paths-state.js";
6
6
  import { acquireUpdateLock } from "./update-lock.js";
7
7
  import { normalizeUpdateChannel } from "./update-channels.js";
8
8
  import { compareSemver, detectInstallKind, resolveNpmChannelTag, resolvePackageRoot } from "./update-check.js";
@@ -136,7 +136,8 @@ async function runGatewayUpdateCheck(params) {
136
136
  nextState,
137
137
  now,
138
138
  root,
139
- config
139
+ config,
140
+ triggerInProcessRestart: params.triggerInProcessRestart
140
141
  });
141
142
  } else {
142
143
  delete nextState.lastAvailableVersion;
@@ -203,16 +204,19 @@ async function handleAutoUpdate(params) {
203
204
  const { runAutoUpdateCommand } = await import("./update-runner.js");
204
205
  const result = await runAutoUpdateCommand({
205
206
  channel,
206
- root
207
+ root,
208
+ triggerInProcessRestart: params.triggerInProcessRestart
207
209
  });
208
210
  if (result.ok) {
209
211
  nextState.autoLastSuccessVersion = version;
210
212
  nextState.autoLastSuccessAt = new Date(now).toISOString();
213
+ const restartMode = result.result?.postUpdate?.restart?.mode;
211
214
  log.info({
212
215
  channel,
213
216
  version,
214
- tag
215
- }, "Auto-update applied successfully; restart gateway to refresh browser extension artifacts");
217
+ tag,
218
+ restartMode
219
+ }, restartMode === "in-process" ? "Auto-update applied; gateway restart scheduled" : "Auto-update applied successfully");
216
220
  } else log.warn({
217
221
  channel,
218
222
  version,
@@ -1 +1 @@
1
- {"version":3,"file":"update-startup.js","names":[],"sources":["../../../src/infra/update-startup.ts"],"sourcesContent":["// src/infra/update-startup.ts\n\nimport { createHash, randomUUID } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport { writeTextAtomic } from './write-file-atomic.js';\nimport type { Config } from '../config/schema.js';\nimport { resolveUpdateCheckStatePath } from '../config/paths-state.js';\nimport { acquireUpdateLock } from './update-lock.js';\nimport { PACKAGE_VERSION } from '../package-version.js';\nimport { createLogger } from '../utils/logger.js';\n\nimport { normalizeUpdateChannel, DEFAULT_PACKAGE_CHANNEL } from './update-channels.js';\nimport {\n compareSemver,\n resolveNpmChannelTag,\n detectInstallKind,\n resolvePackageRoot,\n type InstallKind,\n type UpdateAvailable,\n} from './update-check.js';\n\nconst log = createLogger('UpdateCheck');\n\n// --- State persistence ---\n\nconst CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours\nconst ONE_HOUR_MS = 60 * 60 * 1000;\n\ntype UpdateCheckState = {\n /** `package.json` version at the last successful registry check (used to bypass 24h throttle when you bump the local version). */\n lastCheckPackageVersion?: string;\n lastCheckedAt?: string;\n lastAvailableVersion?: string;\n lastAvailableTag?: string;\n lastNotifiedVersion?: string;\n lastNotifiedTag?: string;\n autoInstallId?: string;\n autoFirstSeenVersion?: string;\n autoFirstSeenTag?: string;\n autoFirstSeenAt?: string;\n autoLastAttemptVersion?: string;\n autoLastAttemptAt?: string;\n autoLastSuccessVersion?: string;\n autoLastSuccessAt?: string;\n};\n\n// --- In-memory cache ---\n\nlet updateAvailableCache: UpdateAvailable | null = null;\n\n/** Get the cached update-available state (populated after startup check). */\nexport function getUpdateAvailable(): UpdateAvailable | null {\n return updateAvailableCache;\n}\n\n// --- Core logic ---\n\nasync function readState(statePath: string): Promise<UpdateCheckState> {\n try {\n const raw = await readFile(statePath, 'utf-8');\n const parsed = JSON.parse(raw) as UpdateCheckState;\n if (!parsed || typeof parsed !== 'object') {\n log.warn({ statePath }, 'Update check state file contains non-object; resetting');\n return {};\n }\n return parsed;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n log.warn({ err, statePath }, 'Failed to read update check state; resetting');\n }\n return {};\n }\n}\n\nasync function writeState(statePath: string, state: UpdateCheckState): Promise<void> {\n await writeTextAtomic(statePath, JSON.stringify(state, null, 2));\n}\n\nfunction resolveCheckIntervalMs(config: Config): number {\n const auto = config.update?.auto;\n if (!auto?.enabled) return CHECK_INTERVAL_MS;\n\n const channel = normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n if (channel === 'beta') {\n const hours = auto.betaCheckIntervalHours ?? 1;\n return Math.max(ONE_HOUR_MS / 4, Math.floor(hours * ONE_HOUR_MS));\n }\n return ONE_HOUR_MS;\n}\n\n/**\n * Compute a deterministic delay for stable auto-update rollout,\n * based on a per-installation hash to spread updates over time.\n */\nfunction resolveStableJitterMs(\n installId: string,\n version: string,\n tag: string,\n jitterWindowMs: number,\n): number {\n if (jitterWindowMs <= 0) return 0;\n const hash = createHash('sha256').update(`${installId}:${version}:${tag}`).digest();\n const bucket = hash.readUInt32BE(0);\n return bucket % (Math.floor(jitterWindowMs) + 1);\n}\n\n/**\n * Main startup update check. Called once when the gateway starts (or on demand).\n */\nexport async function runGatewayUpdateCheck(params: {\n config: Config;\n onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;\n /** When true, bypass checkOnStart/auto-disabled early exit and throttle (for POST /api/update/check). */\n force?: boolean;\n}): Promise<void> {\n const { config, force } = params;\n\n const autoEnabled = config.update?.auto?.enabled ?? false;\n const shouldCheckHints = config.update?.checkOnStart !== false;\n if (!force && !shouldCheckHints && !autoEnabled) return;\n\n const statePath = resolveUpdateCheckStatePath();\n const state = await readState(statePath);\n const now = Date.now();\n\n // Hydrate from persisted state if within throttle window\n const lastCheckedAt = state.lastCheckedAt ? Date.parse(state.lastCheckedAt) : null;\n if (state.lastAvailableVersion && (shouldCheckHints || force)) {\n const comparison = compareSemver(PACKAGE_VERSION, state.lastAvailableVersion);\n if (comparison !== null && comparison < 0) {\n const cached: UpdateAvailable = {\n currentVersion: PACKAGE_VERSION,\n latestVersion: state.lastAvailableVersion,\n channel: state.lastAvailableTag ?? 'latest',\n };\n updateAvailableCache = cached;\n params.onUpdateAvailableChange?.(cached);\n }\n }\n\n const checkIntervalMs = resolveCheckIntervalMs(config);\n // Re-check npm when the local package version changed (e.g. after editing package.json) even within 24h.\n const shouldBypassThrottleForVersion =\n state.lastCheckPackageVersion === undefined || state.lastCheckPackageVersion !== PACKAGE_VERSION;\n if (\n !force &&\n !shouldBypassThrottleForVersion &&\n lastCheckedAt &&\n Number.isFinite(lastCheckedAt) &&\n now - lastCheckedAt < checkIntervalMs\n ) {\n return; // Within throttle window\n }\n\n // Install kind: auto-install only for npm global installs, but we still query npm in git\n // so the Web UI / CLI can show \"newer on registry\" and the top reminder bar.\n const root = await resolvePackageRoot();\n let installKind: InstallKind = 'unknown';\n if (root) {\n installKind = await detectInstallKind(root);\n if (installKind === 'git') {\n log.info('Update check: git checkout (hint-only; use git pull to update, no auto npm install)');\n }\n }\n\n // Query npm registry\n const channel = normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n const resolved = await resolveNpmChannelTag({ channel, timeoutMs: 2500 });\n\n const nextState: UpdateCheckState = {\n ...state,\n lastCheckedAt: new Date(now).toISOString(),\n };\n\n if (!resolved.version) {\n nextState.lastCheckPackageVersion = PACKAGE_VERSION;\n await writeState(statePath, nextState);\n return;\n }\n\n const comparison = compareSemver(PACKAGE_VERSION, resolved.version);\n if (comparison !== null && comparison < 0) {\n // Update available\n const updateInfo: UpdateAvailable = {\n currentVersion: PACKAGE_VERSION,\n latestVersion: resolved.version,\n channel: resolved.tag,\n };\n\n if (shouldCheckHints || force) {\n updateAvailableCache = updateInfo;\n params.onUpdateAvailableChange?.(updateInfo);\n }\n\n nextState.lastAvailableVersion = resolved.version;\n nextState.lastAvailableTag = resolved.tag;\n\n // Log notification (once per version)\n const shouldNotify =\n state.lastNotifiedVersion !== resolved.version || state.lastNotifiedTag !== resolved.tag;\n if ((shouldCheckHints || force) && shouldNotify) {\n log.info(\n { currentVersion: PACKAGE_VERSION, latestVersion: resolved.version, tag: resolved.tag },\n `Update available (${resolved.tag}): v${resolved.version} (current v${PACKAGE_VERSION}). Run: xopc update`,\n );\n nextState.lastNotifiedVersion = resolved.version;\n nextState.lastNotifiedTag = resolved.tag;\n }\n\n // Auto-update logic (never from a git worktree)\n if (\n autoEnabled &&\n (channel === 'stable' || channel === 'beta') &&\n installKind !== 'git'\n ) {\n await handleAutoUpdate({\n channel,\n version: resolved.version,\n tag: resolved.tag,\n state,\n nextState,\n now,\n root,\n config,\n });\n }\n } else {\n // Current version is up to date or newer\n delete nextState.lastAvailableVersion;\n delete nextState.lastAvailableTag;\n updateAvailableCache = null;\n params.onUpdateAvailableChange?.(null);\n }\n\n nextState.lastCheckPackageVersion = PACKAGE_VERSION;\n await writeState(statePath, nextState);\n}\n\nasync function handleAutoUpdate(params: {\n channel: 'stable' | 'beta';\n version: string;\n tag: string;\n state: UpdateCheckState;\n nextState: UpdateCheckState;\n now: number;\n root: string | null;\n config: Config;\n}): Promise<void> {\n const { channel, version, tag, state, nextState, now, root, config } = params;\n const auto = config.update?.auto;\n if (!auto) return;\n\n const stableDelayHours = auto.stableDelayHours ?? 6;\n const stableJitterHours = auto.stableJitterHours ?? 12;\n const betaCheckIntervalHours = auto.betaCheckIntervalHours ?? 1;\n\n // Rate limit: don't re-attempt same version within interval\n const attemptIntervalMs =\n channel === 'beta'\n ? Math.max(ONE_HOUR_MS / 4, Math.floor(betaCheckIntervalHours * ONE_HOUR_MS))\n : ONE_HOUR_MS;\n const lastAttemptAt = state.autoLastAttemptAt ? Date.parse(state.autoLastAttemptAt) : null;\n const recentAttempt =\n state.autoLastAttemptVersion === version &&\n lastAttemptAt !== null &&\n Number.isFinite(lastAttemptAt) &&\n now - lastAttemptAt < attemptIntervalMs;\n\n if (recentAttempt) {\n log.info({ version, tag }, 'Auto-update deferred: recent attempt exists');\n return;\n }\n\n // Stable rollout delay + jitter\n if (channel === 'stable') {\n if (!nextState.autoInstallId) {\n nextState.autoInstallId = state.autoInstallId?.trim() || randomUUID();\n }\n // Track first-seen time for this version\n if (state.autoFirstSeenVersion !== version || state.autoFirstSeenTag !== tag) {\n nextState.autoFirstSeenVersion = version;\n nextState.autoFirstSeenTag = tag;\n nextState.autoFirstSeenAt = new Date(now).toISOString();\n } else {\n nextState.autoFirstSeenAt = state.autoFirstSeenAt;\n }\n\n const firstSeenMs = nextState.autoFirstSeenAt ? Date.parse(nextState.autoFirstSeenAt) : now;\n const baseDelayMs = Math.max(0, stableDelayHours) * ONE_HOUR_MS;\n const jitterWindowMs = Math.max(0, stableJitterHours) * ONE_HOUR_MS;\n const jitterMs = resolveStableJitterMs(nextState.autoInstallId, version, tag, jitterWindowMs);\n const applyAfterMs = firstSeenMs + baseDelayMs + jitterMs;\n\n if (now < applyAfterMs) {\n log.info(\n { version, tag, applyAfter: new Date(applyAfterMs).toISOString() },\n 'Auto-update deferred: stable rollout window not yet due',\n );\n return;\n }\n }\n\n // Execute auto-update\n nextState.autoLastAttemptVersion = version;\n nextState.autoLastAttemptAt = new Date(now).toISOString();\n\n log.info({ channel, version, tag }, 'Starting auto-update');\n\n const lock = await acquireUpdateLock('auto');\n if (!lock) {\n log.info({ version, tag }, 'Auto-update skipped: another update is in progress');\n return;\n }\n try {\n const { runAutoUpdateCommand } = await import('./update-runner.js');\n const result = await runAutoUpdateCommand({ channel, root });\n if (result.ok) {\n nextState.autoLastSuccessVersion = version;\n nextState.autoLastSuccessAt = new Date(now).toISOString();\n log.info({ channel, version, tag }, 'Auto-update applied successfully; restart gateway to refresh browser extension artifacts');\n } else {\n log.warn(\n { channel, version, tag, exitCode: result.exitCode, reason: result.reason },\n `Auto-update attempt failed: ${result.reason ?? `exit ${result.exitCode}`}`,\n );\n }\n } catch (err) {\n log.error({ err, channel, version }, 'Auto-update command threw');\n } finally {\n await lock.release();\n }\n}\n\n/**\n * Schedule periodic update checks. Returns a cleanup function to stop the timer.\n */\nexport function scheduleGatewayUpdateCheck(params: {\n config: Config;\n onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;\n}): () => void {\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const tick = async () => {\n if (stopped) return;\n try {\n await runGatewayUpdateCheck(params);\n } catch (err) {\n log.warn({ err }, 'Periodic update check failed');\n }\n if (!stopped) {\n const intervalMs = resolveCheckIntervalMs(params.config);\n timer = setTimeout(() => void tick(), intervalMs);\n }\n };\n\n // Initial check after a short delay (don't block startup)\n timer = setTimeout(() => void tick(), 5000);\n\n return () => {\n stopped = true;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"update-startup.js","names":[],"sources":["../../../src/infra/update-startup.ts"],"sourcesContent":["// src/infra/update-startup.ts\n\nimport { createHash, randomUUID } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport { writeTextAtomic } from './write-file-atomic.js';\nimport type { Config } from '../config/schema.js';\nimport { resolveUpdateCheckStatePath } from '../config/paths-state.js';\nimport { acquireUpdateLock } from './update-lock.js';\nimport { PACKAGE_VERSION } from '../package-version.js';\nimport { createLogger } from '../utils/logger.js';\n\nimport { normalizeUpdateChannel, DEFAULT_PACKAGE_CHANNEL } from './update-channels.js';\nimport {\n compareSemver,\n resolveNpmChannelTag,\n detectInstallKind,\n resolvePackageRoot,\n type InstallKind,\n type UpdateAvailable,\n} from './update-check.js';\nimport type { InProcessRestartTrigger } from './update-restart.js';\n\nconst log = createLogger('UpdateCheck');\n\n// --- State persistence ---\n\nconst CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours\nconst ONE_HOUR_MS = 60 * 60 * 1000;\n\ntype UpdateCheckState = {\n /** `package.json` version at the last successful registry check (used to bypass 24h throttle when you bump the local version). */\n lastCheckPackageVersion?: string;\n lastCheckedAt?: string;\n lastAvailableVersion?: string;\n lastAvailableTag?: string;\n lastNotifiedVersion?: string;\n lastNotifiedTag?: string;\n autoInstallId?: string;\n autoFirstSeenVersion?: string;\n autoFirstSeenTag?: string;\n autoFirstSeenAt?: string;\n autoLastAttemptVersion?: string;\n autoLastAttemptAt?: string;\n autoLastSuccessVersion?: string;\n autoLastSuccessAt?: string;\n};\n\n// --- In-memory cache ---\n\nlet updateAvailableCache: UpdateAvailable | null = null;\n\n/** Get the cached update-available state (populated after startup check). */\nexport function getUpdateAvailable(): UpdateAvailable | null {\n return updateAvailableCache;\n}\n\n// --- Core logic ---\n\nasync function readState(statePath: string): Promise<UpdateCheckState> {\n try {\n const raw = await readFile(statePath, 'utf-8');\n const parsed = JSON.parse(raw) as UpdateCheckState;\n if (!parsed || typeof parsed !== 'object') {\n log.warn({ statePath }, 'Update check state file contains non-object; resetting');\n return {};\n }\n return parsed;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n log.warn({ err, statePath }, 'Failed to read update check state; resetting');\n }\n return {};\n }\n}\n\nasync function writeState(statePath: string, state: UpdateCheckState): Promise<void> {\n await writeTextAtomic(statePath, JSON.stringify(state, null, 2));\n}\n\nfunction resolveCheckIntervalMs(config: Config): number {\n const auto = config.update?.auto;\n if (!auto?.enabled) return CHECK_INTERVAL_MS;\n\n const channel = normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n if (channel === 'beta') {\n const hours = auto.betaCheckIntervalHours ?? 1;\n return Math.max(ONE_HOUR_MS / 4, Math.floor(hours * ONE_HOUR_MS));\n }\n return ONE_HOUR_MS;\n}\n\n/**\n * Compute a deterministic delay for stable auto-update rollout,\n * based on a per-installation hash to spread updates over time.\n */\nfunction resolveStableJitterMs(\n installId: string,\n version: string,\n tag: string,\n jitterWindowMs: number,\n): number {\n if (jitterWindowMs <= 0) return 0;\n const hash = createHash('sha256').update(`${installId}:${version}:${tag}`).digest();\n const bucket = hash.readUInt32BE(0);\n return bucket % (Math.floor(jitterWindowMs) + 1);\n}\n\n/**\n * Main startup update check. Called once when the gateway starts (or on demand).\n */\nexport async function runGatewayUpdateCheck(params: {\n config: Config;\n onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;\n triggerInProcessRestart?: InProcessRestartTrigger;\n /** When true, bypass checkOnStart/auto-disabled early exit and throttle (for POST /api/update/check). */\n force?: boolean;\n}): Promise<void> {\n const { config, force } = params;\n\n const autoEnabled = config.update?.auto?.enabled ?? false;\n const shouldCheckHints = config.update?.checkOnStart !== false;\n if (!force && !shouldCheckHints && !autoEnabled) return;\n\n const statePath = resolveUpdateCheckStatePath();\n const state = await readState(statePath);\n const now = Date.now();\n\n // Hydrate from persisted state if within throttle window\n const lastCheckedAt = state.lastCheckedAt ? Date.parse(state.lastCheckedAt) : null;\n if (state.lastAvailableVersion && (shouldCheckHints || force)) {\n const comparison = compareSemver(PACKAGE_VERSION, state.lastAvailableVersion);\n if (comparison !== null && comparison < 0) {\n const cached: UpdateAvailable = {\n currentVersion: PACKAGE_VERSION,\n latestVersion: state.lastAvailableVersion,\n channel: state.lastAvailableTag ?? 'latest',\n };\n updateAvailableCache = cached;\n params.onUpdateAvailableChange?.(cached);\n }\n }\n\n const checkIntervalMs = resolveCheckIntervalMs(config);\n // Re-check npm when the local package version changed (e.g. after editing package.json) even within 24h.\n const shouldBypassThrottleForVersion =\n state.lastCheckPackageVersion === undefined || state.lastCheckPackageVersion !== PACKAGE_VERSION;\n if (\n !force &&\n !shouldBypassThrottleForVersion &&\n lastCheckedAt &&\n Number.isFinite(lastCheckedAt) &&\n now - lastCheckedAt < checkIntervalMs\n ) {\n return; // Within throttle window\n }\n\n // Install kind: auto-install only for npm global installs, but we still query npm in git\n // so the Web UI / CLI can show \"newer on registry\" and the top reminder bar.\n const root = await resolvePackageRoot();\n let installKind: InstallKind = 'unknown';\n if (root) {\n installKind = await detectInstallKind(root);\n if (installKind === 'git') {\n log.info('Update check: git checkout (hint-only; use git pull to update, no auto npm install)');\n }\n }\n\n // Query npm registry\n const channel = normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n const resolved = await resolveNpmChannelTag({ channel, timeoutMs: 2500 });\n\n const nextState: UpdateCheckState = {\n ...state,\n lastCheckedAt: new Date(now).toISOString(),\n };\n\n if (!resolved.version) {\n nextState.lastCheckPackageVersion = PACKAGE_VERSION;\n await writeState(statePath, nextState);\n return;\n }\n\n const comparison = compareSemver(PACKAGE_VERSION, resolved.version);\n if (comparison !== null && comparison < 0) {\n // Update available\n const updateInfo: UpdateAvailable = {\n currentVersion: PACKAGE_VERSION,\n latestVersion: resolved.version,\n channel: resolved.tag,\n };\n\n if (shouldCheckHints || force) {\n updateAvailableCache = updateInfo;\n params.onUpdateAvailableChange?.(updateInfo);\n }\n\n nextState.lastAvailableVersion = resolved.version;\n nextState.lastAvailableTag = resolved.tag;\n\n // Log notification (once per version)\n const shouldNotify =\n state.lastNotifiedVersion !== resolved.version || state.lastNotifiedTag !== resolved.tag;\n if ((shouldCheckHints || force) && shouldNotify) {\n log.info(\n { currentVersion: PACKAGE_VERSION, latestVersion: resolved.version, tag: resolved.tag },\n `Update available (${resolved.tag}): v${resolved.version} (current v${PACKAGE_VERSION}). Run: xopc update`,\n );\n nextState.lastNotifiedVersion = resolved.version;\n nextState.lastNotifiedTag = resolved.tag;\n }\n\n // Auto-update logic (never from a git worktree)\n if (\n autoEnabled &&\n (channel === 'stable' || channel === 'beta') &&\n installKind !== 'git'\n ) {\n await handleAutoUpdate({\n channel,\n version: resolved.version,\n tag: resolved.tag,\n state,\n nextState,\n now,\n root,\n config,\n triggerInProcessRestart: params.triggerInProcessRestart,\n });\n }\n } else {\n // Current version is up to date or newer\n delete nextState.lastAvailableVersion;\n delete nextState.lastAvailableTag;\n updateAvailableCache = null;\n params.onUpdateAvailableChange?.(null);\n }\n\n nextState.lastCheckPackageVersion = PACKAGE_VERSION;\n await writeState(statePath, nextState);\n}\n\nasync function handleAutoUpdate(params: {\n channel: 'stable' | 'beta';\n version: string;\n tag: string;\n state: UpdateCheckState;\n nextState: UpdateCheckState;\n now: number;\n root: string | null;\n config: Config;\n triggerInProcessRestart?: InProcessRestartTrigger;\n}): Promise<void> {\n const { channel, version, tag, state, nextState, now, root, config } = params;\n const auto = config.update?.auto;\n if (!auto) return;\n\n const stableDelayHours = auto.stableDelayHours ?? 6;\n const stableJitterHours = auto.stableJitterHours ?? 12;\n const betaCheckIntervalHours = auto.betaCheckIntervalHours ?? 1;\n\n // Rate limit: don't re-attempt same version within interval\n const attemptIntervalMs =\n channel === 'beta'\n ? Math.max(ONE_HOUR_MS / 4, Math.floor(betaCheckIntervalHours * ONE_HOUR_MS))\n : ONE_HOUR_MS;\n const lastAttemptAt = state.autoLastAttemptAt ? Date.parse(state.autoLastAttemptAt) : null;\n const recentAttempt =\n state.autoLastAttemptVersion === version &&\n lastAttemptAt !== null &&\n Number.isFinite(lastAttemptAt) &&\n now - lastAttemptAt < attemptIntervalMs;\n\n if (recentAttempt) {\n log.info({ version, tag }, 'Auto-update deferred: recent attempt exists');\n return;\n }\n\n // Stable rollout delay + jitter\n if (channel === 'stable') {\n if (!nextState.autoInstallId) {\n nextState.autoInstallId = state.autoInstallId?.trim() || randomUUID();\n }\n // Track first-seen time for this version\n if (state.autoFirstSeenVersion !== version || state.autoFirstSeenTag !== tag) {\n nextState.autoFirstSeenVersion = version;\n nextState.autoFirstSeenTag = tag;\n nextState.autoFirstSeenAt = new Date(now).toISOString();\n } else {\n nextState.autoFirstSeenAt = state.autoFirstSeenAt;\n }\n\n const firstSeenMs = nextState.autoFirstSeenAt ? Date.parse(nextState.autoFirstSeenAt) : now;\n const baseDelayMs = Math.max(0, stableDelayHours) * ONE_HOUR_MS;\n const jitterWindowMs = Math.max(0, stableJitterHours) * ONE_HOUR_MS;\n const jitterMs = resolveStableJitterMs(nextState.autoInstallId, version, tag, jitterWindowMs);\n const applyAfterMs = firstSeenMs + baseDelayMs + jitterMs;\n\n if (now < applyAfterMs) {\n log.info(\n { version, tag, applyAfter: new Date(applyAfterMs).toISOString() },\n 'Auto-update deferred: stable rollout window not yet due',\n );\n return;\n }\n }\n\n // Execute auto-update\n nextState.autoLastAttemptVersion = version;\n nextState.autoLastAttemptAt = new Date(now).toISOString();\n\n log.info({ channel, version, tag }, 'Starting auto-update');\n\n const lock = await acquireUpdateLock('auto');\n if (!lock) {\n log.info({ version, tag }, 'Auto-update skipped: another update is in progress');\n return;\n }\n try {\n const { runAutoUpdateCommand } = await import('./update-runner.js');\n const result = await runAutoUpdateCommand({\n channel,\n root,\n triggerInProcessRestart: params.triggerInProcessRestart,\n });\n if (result.ok) {\n nextState.autoLastSuccessVersion = version;\n nextState.autoLastSuccessAt = new Date(now).toISOString();\n const restartMode = result.result?.postUpdate?.restart?.mode;\n log.info(\n { channel, version, tag, restartMode },\n restartMode === 'in-process'\n ? 'Auto-update applied; gateway restart scheduled'\n : 'Auto-update applied successfully',\n );\n } else {\n log.warn(\n { channel, version, tag, exitCode: result.exitCode, reason: result.reason },\n `Auto-update attempt failed: ${result.reason ?? `exit ${result.exitCode}`}`,\n );\n }\n } catch (err) {\n log.error({ err, channel, version }, 'Auto-update command threw');\n } finally {\n await lock.release();\n }\n}\n\n/**\n * Schedule periodic update checks. Returns a cleanup function to stop the timer.\n */\nexport function scheduleGatewayUpdateCheck(params: {\n config: Config;\n onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;\n triggerInProcessRestart?: InProcessRestartTrigger;\n}): () => void {\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const tick = async () => {\n if (stopped) return;\n try {\n await runGatewayUpdateCheck(params);\n } catch (err) {\n log.warn({ err }, 'Periodic update check failed');\n }\n if (!stopped) {\n const intervalMs = resolveCheckIntervalMs(params.config);\n timer = setTimeout(() => void tick(), intervalMs);\n }\n };\n\n // Initial check after a short delay (don't block startup)\n timer = setTimeout(() => void tick(), 5000);\n\n return () => {\n stopped = true;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
- import path from "node:path";
3
- import { chmodSync, closeSync, copyFileSync, fsyncSync, mkdirSync, openSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
2
  import { randomUUID } from "node:crypto";
3
+ import { chmodSync, closeSync, copyFileSync, fsyncSync, mkdirSync, openSync, renameSync, rmSync, writeFileSync } from "node:fs";
5
4
  import { chmod, copyFile, mkdir, open, rename, rm } from "node:fs/promises";
5
+ import path from "node:path";
6
6
  //#region src/infra/write-file-atomic.ts
7
7
  /**
8
8
  * Durable single-file writes: temp file → fsync → rename to target.
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
- import { dirname, join } from "node:path";
4
3
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
4
+ import { dirname, join } from "node:path";
5
5
  //#region src/providers/auth-runtime/auth-profile-store.ts
6
6
  /**
7
7
  * {@link AuthProfileStore} implementations.
@@ -1,8 +1,8 @@
1
1
  import { __esmMin, __exportAll } from "../../_virtual/_rolldown/runtime.js";
2
- import { hasProviderAuthOnDiskSync, init_sync_provider_auth } from "../auth/sync-provider-auth.js";
3
2
  import { PROVIDER_ENV_MAP, getApiKeyFromEnv, init_env_keys } from "./env-keys.js";
4
- import { ModelRegistry, getModelRegistry, init_model_registry, prewarmModelRegistry, resetModelRegistry } from "./model-registry.js";
5
3
  import { CredentialResolver, hasCredentials, init_credentials, resolveApiKey } from "../auth/credentials.js";
4
+ import { hasProviderAuthOnDiskSync, init_sync_provider_auth } from "../auth/sync-provider-auth.js";
5
+ import { ModelRegistry, getModelRegistry, init_model_registry, prewarmModelRegistry, resetModelRegistry } from "./model-registry.js";
6
6
  import { getProviderRegistry, init_plugin_registry } from "./plugin-registry.js";
7
7
  import { getModel, getModels, getProviders } from "@earendil-works/pi-ai";
8
8
  //#region src/providers/index.ts
@@ -1,10 +1,10 @@
1
1
  import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
2
  import { createLogger } from "../utils/logger/index.js";
3
3
  import { init_logger } from "../utils/logger.js";
4
+ import { getApiKeyFromEnv, init_env_keys } from "./env-keys.js";
4
5
  import { resolveModelsJsonPath } from "../config/paths.js";
5
6
  import { init_resolve_config_value, resolveConfigValue, resolveHeaders } from "../config/resolve-config-value.js";
6
7
  import { getDefaultModelValues, init_models_json, validateModelsConfig } from "../config/models-json.js";
7
- import { getApiKeyFromEnv, init_env_keys } from "./env-keys.js";
8
8
  import { existsSync, readFileSync } from "fs";
9
9
  import { getModels, getProviders } from "@earendil-works/pi-ai";
10
10
  //#region src/providers/model-registry.ts
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Combines binding rules, identity links, and config to pick an agent and session keys.
5
5
  */
6
+ import type { LocalizedText } from '../config/localized-text.js';
6
7
  import type { BindingRule, RouteInput, RouteResult } from './bindings.js';
7
8
  /**
8
9
  * Route context type (alias for RouteInput)
@@ -52,7 +53,8 @@ export interface AgentConfig {
52
53
  /** Registered agents */
53
54
  list?: Array<{
54
55
  id: string;
55
- name?: string;
56
+ name?: LocalizedText;
57
+ description?: LocalizedText;
56
58
  enabled?: boolean;
57
59
  [key: string]: unknown;
58
60
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-route.js","names":["resolveDefaultAgentIdFromConfig","resolveBindingRoute"],"sources":["../../../src/routing/resolve-route.ts"],"sourcesContent":["/**\n * Route resolution\n *\n * Combines binding rules, identity links, and config to pick an agent and session keys.\n */\n\nimport { resolveDefaultAgentId as resolveDefaultAgentIdFromConfig } from '../agent/agent-scope.js';\nimport type { Config } from '../config/schema.js';\nimport type { BindingRule, RouteInput, RouteResult } from './bindings.js';\n\n/**\n * Route context type (alias for RouteInput)\n */\nexport type RouteContext = RouteInput;\nimport { parseBindingRules, resolveRoute as resolveBindingRoute } from './bindings.js';\nimport { buildSessionKey, normalizeSessionKey, parseSessionKey } from './session-key.js';\nimport {\n buildAgentMainSessionKey,\n buildAgentPeerSessionKey,\n} from './agent-session-key.js';\nimport { normalizeAccountId } from './account-id.js';\n\n/**\n * Identity link map: canonical peer id -> aliases across channels.\n *\n * Shape: `{ canonicalName: [alias1, alias2, ...] }`\n */\nexport type IdentityLinks = Record<string, string[]>;\n\n/**\n * Session-related routing options.\n */\nexport interface SessionConfig {\n scope?: 'per-sender' | 'global';\n mainKey?: string;\n /** How DM sessions are scoped / merged */\n dmScope?: 'main' | 'per-peer' | 'per-channel-peer' | 'per-account-channel-peer';\n /** Cross-channel identity aliases */\n identityLinks?: IdentityLinks;\n resetTriggers?: string[];\n idleMinutes?: number;\n reset?: {\n mode?: 'daily' | 'idle';\n atHour?: number;\n idleMinutes?: number;\n };\n resetByType?: {\n direct?: SessionConfig['reset'];\n group?: SessionConfig['reset'];\n thread?: SessionConfig['reset'];\n };\n resetByChannel?: Record<string, NonNullable<SessionConfig['reset']>>;\n /** Optional session store tuning */\n storage?: {\n pruneAfterMs?: number;\n maxEntries?: number;\n };\n}\n\n/**\n * Agent list and default id from config.\n */\nexport interface AgentConfig {\n /** Default agent id */\n default?: string;\n /** Registered agents */\n list?: Array<{\n id: string;\n name?: string;\n enabled?: boolean;\n [key: string]: unknown;\n }>;\n}\n\n/**\n * Subset of app config used for routing.\n */\nexport interface RoutingConfig {\n agents?: AgentConfig;\n bindings?: BindingRule[] | any[];\n session?: SessionConfig;\n}\n\n/**\n * Input to `resolveRoute`.\n */\nexport interface ResolveRouteInput extends RouteInput {\n /** Routing config snapshot */\n config: RoutingConfig;\n /** Optional thread id for threaded channels */\n threadId?: string | null;\n}\n\n/**\n * Resolved route including session keys.\n */\nexport interface ResolveRouteResult extends RouteResult {\n /** Active session key for this turn */\n sessionKey: string;\n /** Main session key (DM merge target) */\n mainSessionKey: string;\n /** Whether routing used the main or a per-session key */\n lastRoutePolicy: 'main' | 'session';\n}\n\n/**\n * Apply identity links and return a canonical lowercased peer id.\n */\nexport function applyIdentityLinks(\n peerId: string,\n channel: string,\n identityLinks?: IdentityLinks\n): string {\n if (!identityLinks) {\n return peerId.toLowerCase();\n }\n \n const normalizedPeerId = peerId.trim().toLowerCase();\n if (!normalizedPeerId) {\n return normalizedPeerId;\n }\n \n const candidates = new Set<string>();\n candidates.add(normalizedPeerId);\n \n const channelPrefix = channel.trim().toLowerCase();\n if (channelPrefix) {\n candidates.add(`${channelPrefix}:${normalizedPeerId}`);\n }\n \n // Match any alias to its canonical id\n for (const [canonical, aliases] of Object.entries(identityLinks)) {\n if (!Array.isArray(aliases)) {\n continue;\n }\n \n for (const alias of aliases) {\n const normalizedAlias = alias.trim().toLowerCase();\n if (candidates.has(normalizedAlias)) {\n return canonical.trim().toLowerCase();\n }\n }\n }\n \n return normalizedPeerId;\n}\n\n/**\n * Default agent id from config (`agents.default`, `list[].default`, or `main`).\n */\nexport function getDefaultAgentId(config: RoutingConfig): string {\n return resolveDefaultAgentIdFromConfig(config as Config);\n}\n\n/**\n * Whether `agentId` appears in the enabled agent list (or list is absent).\n */\nexport function agentExists(agentId: string, config: RoutingConfig): boolean {\n if (!config.agents?.list) {\n return true; // No list: treat every id as valid\n }\n \n return config.agents.list.some(\n (agent) => agent.enabled !== false && agent.id.toLowerCase() === agentId.toLowerCase()\n );\n}\n\n/**\n * Return `agentId` if listed, otherwise the default agent id.\n */\nexport function pickFirstExistingAgentId(agentId: string, config: RoutingConfig): string {\n if (!agentId) {\n return getDefaultAgentId(config);\n }\n \n if (agentExists(agentId, config)) {\n return agentId.toLowerCase();\n }\n \n return getDefaultAgentId(config);\n}\n\n/**\n * Thin wrapper around `buildSessionKey` for route inputs.\n */\nexport function buildRouteSessionKey(\n agentId: string,\n channel: string,\n accountId: string,\n peerKind: string,\n peerId: string,\n threadId?: string | null,\n scopeId?: string | null,\n): string {\n return buildSessionKey({\n agentId,\n source: channel,\n accountId,\n peerKind,\n peerId,\n threadId: threadId || undefined,\n scopeId: scopeId || undefined,\n dmScope: peerKind === 'dm' || peerKind === 'direct' ? 'per-account-channel-peer' : undefined,\n });\n}\n\n/**\n * Map session vs main key to policy label.\n */\nexport function deriveLastRoutePolicy(\n sessionKey: string,\n mainSessionKey: string\n): 'main' | 'session' {\n return sessionKey === mainSessionKey ? 'main' : 'session';\n}\n\n/**\n * Resolve agent and session keys from channel context and config.\n */\nexport function resolveRoute(input: ResolveRouteInput): ResolveRouteResult {\n const { config, threadId } = input;\n \n const channel = (input.channel ?? '').trim().toLowerCase() || 'unknown';\n const accountId = normalizeAccountId(input.accountId);\n const peerKind = (input.peerKind ?? 'dm').toLowerCase();\n const rawPeerId = (input.peerId ?? '').trim();\n \n const peerId = applyIdentityLinks(rawPeerId, channel, config.session?.identityLinks ?? {});\n \n const rules = Array.isArray(config.bindings)\n ? parseBindingRules({ bindings: config.bindings })\n : [];\n \n const bindingResult = resolveBindingRoute(\n {\n channel,\n accountId,\n peerKind: input.peerKind,\n peerId: rawPeerId,\n guildId: input.guildId,\n teamId: input.teamId,\n memberRoleIds: input.memberRoleIds,\n },\n rules,\n getDefaultAgentId(config)\n );\n \n const agentId = pickFirstExistingAgentId(bindingResult.agentId, config);\n \n const dmScope = config.session?.dmScope ?? 'main';\n\n let sessionKey: string;\n let mainSessionKey: string;\n\n const identityLinks = config.session?.identityLinks;\n const mainKey = undefined;\n\n if (peerKind === 'dm' || peerKind === 'direct') {\n mainSessionKey = buildAgentMainSessionKey({ agentId, mainKey });\n\n sessionKey = buildAgentPeerSessionKey({\n agentId,\n mainKey,\n channel,\n accountId,\n peerKind: 'direct',\n peerId,\n identityLinks,\n dmScope,\n });\n } else {\n const mappedKind = peerKind === 'group' || peerKind === 'channel' ? peerKind : peerKind;\n sessionKey = buildAgentPeerSessionKey({\n agentId,\n mainKey,\n channel,\n accountId,\n peerKind: mappedKind as 'group' | 'channel',\n peerId,\n identityLinks,\n });\n mainSessionKey = buildAgentPeerSessionKey({\n agentId,\n mainKey,\n channel,\n accountId,\n peerKind: mappedKind as 'group' | 'channel',\n peerId: 'main',\n identityLinks,\n });\n }\n\n if (threadId && !sessionKey.includes(':thread:')) {\n sessionKey = `${sessionKey}:thread:${threadId.toLowerCase()}`;\n }\n \n sessionKey = normalizeSessionKey(sessionKey);\n mainSessionKey = normalizeSessionKey(mainSessionKey);\n \n return {\n ...bindingResult,\n agentId,\n sessionKey,\n mainSessionKey,\n lastRoutePolicy: deriveLastRoutePolicy(sessionKey, mainSessionKey),\n };\n}\n\n/**\n * Parse basic routing fields from a session key string.\n */\nexport function resolveRouteFromSessionKey(\n sessionKey: string,\n _config: RoutingConfig\n): { agentId: string; source: string; accountId: string; peerKind: string; peerId: string } | null {\n const parsed = parseSessionKey(sessionKey);\n if (!parsed) {\n return null;\n }\n \n return {\n agentId: parsed.agentId,\n source: parsed.source,\n accountId: parsed.accountId,\n peerKind: parsed.peerKind,\n peerId: parsed.peerId,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"resolve-route.js","names":["resolveDefaultAgentIdFromConfig","resolveBindingRoute"],"sources":["../../../src/routing/resolve-route.ts"],"sourcesContent":["/**\n * Route resolution\n *\n * Combines binding rules, identity links, and config to pick an agent and session keys.\n */\n\nimport { resolveDefaultAgentId as resolveDefaultAgentIdFromConfig } from '../agent/agent-scope.js';\nimport type { Config } from '../config/schema.js';\nimport type { LocalizedText } from '../config/localized-text.js';\nimport type { BindingRule, RouteInput, RouteResult } from './bindings.js';\n\n/**\n * Route context type (alias for RouteInput)\n */\nexport type RouteContext = RouteInput;\nimport { parseBindingRules, resolveRoute as resolveBindingRoute } from './bindings.js';\nimport { buildSessionKey, normalizeSessionKey, parseSessionKey } from './session-key.js';\nimport {\n buildAgentMainSessionKey,\n buildAgentPeerSessionKey,\n} from './agent-session-key.js';\nimport { normalizeAccountId } from './account-id.js';\n\n/**\n * Identity link map: canonical peer id -> aliases across channels.\n *\n * Shape: `{ canonicalName: [alias1, alias2, ...] }`\n */\nexport type IdentityLinks = Record<string, string[]>;\n\n/**\n * Session-related routing options.\n */\nexport interface SessionConfig {\n scope?: 'per-sender' | 'global';\n mainKey?: string;\n /** How DM sessions are scoped / merged */\n dmScope?: 'main' | 'per-peer' | 'per-channel-peer' | 'per-account-channel-peer';\n /** Cross-channel identity aliases */\n identityLinks?: IdentityLinks;\n resetTriggers?: string[];\n idleMinutes?: number;\n reset?: {\n mode?: 'daily' | 'idle';\n atHour?: number;\n idleMinutes?: number;\n };\n resetByType?: {\n direct?: SessionConfig['reset'];\n group?: SessionConfig['reset'];\n thread?: SessionConfig['reset'];\n };\n resetByChannel?: Record<string, NonNullable<SessionConfig['reset']>>;\n /** Optional session store tuning */\n storage?: {\n pruneAfterMs?: number;\n maxEntries?: number;\n };\n}\n\n/**\n * Agent list and default id from config.\n */\nexport interface AgentConfig {\n /** Default agent id */\n default?: string;\n /** Registered agents */\n list?: Array<{\n id: string;\n name?: LocalizedText;\n description?: LocalizedText;\n enabled?: boolean;\n [key: string]: unknown;\n }>;\n}\n\n/**\n * Subset of app config used for routing.\n */\nexport interface RoutingConfig {\n agents?: AgentConfig;\n bindings?: BindingRule[] | any[];\n session?: SessionConfig;\n}\n\n/**\n * Input to `resolveRoute`.\n */\nexport interface ResolveRouteInput extends RouteInput {\n /** Routing config snapshot */\n config: RoutingConfig;\n /** Optional thread id for threaded channels */\n threadId?: string | null;\n}\n\n/**\n * Resolved route including session keys.\n */\nexport interface ResolveRouteResult extends RouteResult {\n /** Active session key for this turn */\n sessionKey: string;\n /** Main session key (DM merge target) */\n mainSessionKey: string;\n /** Whether routing used the main or a per-session key */\n lastRoutePolicy: 'main' | 'session';\n}\n\n/**\n * Apply identity links and return a canonical lowercased peer id.\n */\nexport function applyIdentityLinks(\n peerId: string,\n channel: string,\n identityLinks?: IdentityLinks\n): string {\n if (!identityLinks) {\n return peerId.toLowerCase();\n }\n \n const normalizedPeerId = peerId.trim().toLowerCase();\n if (!normalizedPeerId) {\n return normalizedPeerId;\n }\n \n const candidates = new Set<string>();\n candidates.add(normalizedPeerId);\n \n const channelPrefix = channel.trim().toLowerCase();\n if (channelPrefix) {\n candidates.add(`${channelPrefix}:${normalizedPeerId}`);\n }\n \n // Match any alias to its canonical id\n for (const [canonical, aliases] of Object.entries(identityLinks)) {\n if (!Array.isArray(aliases)) {\n continue;\n }\n \n for (const alias of aliases) {\n const normalizedAlias = alias.trim().toLowerCase();\n if (candidates.has(normalizedAlias)) {\n return canonical.trim().toLowerCase();\n }\n }\n }\n \n return normalizedPeerId;\n}\n\n/**\n * Default agent id from config (`agents.default`, `list[].default`, or `main`).\n */\nexport function getDefaultAgentId(config: RoutingConfig): string {\n return resolveDefaultAgentIdFromConfig(config as Config);\n}\n\n/**\n * Whether `agentId` appears in the enabled agent list (or list is absent).\n */\nexport function agentExists(agentId: string, config: RoutingConfig): boolean {\n if (!config.agents?.list) {\n return true; // No list: treat every id as valid\n }\n \n return config.agents.list.some(\n (agent) => agent.enabled !== false && agent.id.toLowerCase() === agentId.toLowerCase()\n );\n}\n\n/**\n * Return `agentId` if listed, otherwise the default agent id.\n */\nexport function pickFirstExistingAgentId(agentId: string, config: RoutingConfig): string {\n if (!agentId) {\n return getDefaultAgentId(config);\n }\n \n if (agentExists(agentId, config)) {\n return agentId.toLowerCase();\n }\n \n return getDefaultAgentId(config);\n}\n\n/**\n * Thin wrapper around `buildSessionKey` for route inputs.\n */\nexport function buildRouteSessionKey(\n agentId: string,\n channel: string,\n accountId: string,\n peerKind: string,\n peerId: string,\n threadId?: string | null,\n scopeId?: string | null,\n): string {\n return buildSessionKey({\n agentId,\n source: channel,\n accountId,\n peerKind,\n peerId,\n threadId: threadId || undefined,\n scopeId: scopeId || undefined,\n dmScope: peerKind === 'dm' || peerKind === 'direct' ? 'per-account-channel-peer' : undefined,\n });\n}\n\n/**\n * Map session vs main key to policy label.\n */\nexport function deriveLastRoutePolicy(\n sessionKey: string,\n mainSessionKey: string\n): 'main' | 'session' {\n return sessionKey === mainSessionKey ? 'main' : 'session';\n}\n\n/**\n * Resolve agent and session keys from channel context and config.\n */\nexport function resolveRoute(input: ResolveRouteInput): ResolveRouteResult {\n const { config, threadId } = input;\n \n const channel = (input.channel ?? '').trim().toLowerCase() || 'unknown';\n const accountId = normalizeAccountId(input.accountId);\n const peerKind = (input.peerKind ?? 'dm').toLowerCase();\n const rawPeerId = (input.peerId ?? '').trim();\n \n const peerId = applyIdentityLinks(rawPeerId, channel, config.session?.identityLinks ?? {});\n \n const rules = Array.isArray(config.bindings)\n ? parseBindingRules({ bindings: config.bindings })\n : [];\n \n const bindingResult = resolveBindingRoute(\n {\n channel,\n accountId,\n peerKind: input.peerKind,\n peerId: rawPeerId,\n guildId: input.guildId,\n teamId: input.teamId,\n memberRoleIds: input.memberRoleIds,\n },\n rules,\n getDefaultAgentId(config)\n );\n \n const agentId = pickFirstExistingAgentId(bindingResult.agentId, config);\n \n const dmScope = config.session?.dmScope ?? 'main';\n\n let sessionKey: string;\n let mainSessionKey: string;\n\n const identityLinks = config.session?.identityLinks;\n const mainKey = undefined;\n\n if (peerKind === 'dm' || peerKind === 'direct') {\n mainSessionKey = buildAgentMainSessionKey({ agentId, mainKey });\n\n sessionKey = buildAgentPeerSessionKey({\n agentId,\n mainKey,\n channel,\n accountId,\n peerKind: 'direct',\n peerId,\n identityLinks,\n dmScope,\n });\n } else {\n const mappedKind = peerKind === 'group' || peerKind === 'channel' ? peerKind : peerKind;\n sessionKey = buildAgentPeerSessionKey({\n agentId,\n mainKey,\n channel,\n accountId,\n peerKind: mappedKind as 'group' | 'channel',\n peerId,\n identityLinks,\n });\n mainSessionKey = buildAgentPeerSessionKey({\n agentId,\n mainKey,\n channel,\n accountId,\n peerKind: mappedKind as 'group' | 'channel',\n peerId: 'main',\n identityLinks,\n });\n }\n\n if (threadId && !sessionKey.includes(':thread:')) {\n sessionKey = `${sessionKey}:thread:${threadId.toLowerCase()}`;\n }\n \n sessionKey = normalizeSessionKey(sessionKey);\n mainSessionKey = normalizeSessionKey(mainSessionKey);\n \n return {\n ...bindingResult,\n agentId,\n sessionKey,\n mainSessionKey,\n lastRoutePolicy: deriveLastRoutePolicy(sessionKey, mainSessionKey),\n };\n}\n\n/**\n * Parse basic routing fields from a session key string.\n */\nexport function resolveRouteFromSessionKey(\n sessionKey: string,\n _config: RoutingConfig\n): { agentId: string; source: string; accountId: string; peerKind: string; peerId: string } | null {\n const parsed = parseSessionKey(sessionKey);\n if (!parsed) {\n return null;\n }\n \n return {\n agentId: parsed.agentId,\n source: parsed.source,\n accountId: parsed.accountId,\n peerKind: parsed.peerKind,\n peerId: parsed.peerId,\n };\n}\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
+ import { init_write_file_atomic, writeTextAtomic } from "../infra/write-file-atomic.js";
1
2
  import { createLogger } from "../utils/logger/index.js";
2
3
  import { init_logger } from "../utils/logger.js";
3
- import { init_write_file_atomic, writeTextAtomic } from "../infra/write-file-atomic.js";
4
+ import { mkdir, readFile } from "fs/promises";
4
5
  import { join } from "path";
5
6
  import { existsSync } from "fs";
6
- import { mkdir, readFile } from "fs/promises";
7
7
  //#region src/session/config-store.ts
8
8
  /**
9
9
  * Session Config Store
@@ -1,7 +1,7 @@
1
- import { SessionConfigSchema, init_schema } from "../config/schema.js";
2
- import { init_session_key, parseSessionKey } from "../routing/session-key.js";
3
1
  import { createLogger } from "../utils/logger/index.js";
4
2
  import { init_logger } from "../utils/logger.js";
3
+ import { SessionConfigSchema, init_schema } from "../config/schema.js";
4
+ import { init_session_key, parseSessionKey } from "../routing/session-key.js";
5
5
  import { evaluateSessionFreshness, resolveSessionResetPolicy } from "./reset-policy.js";
6
6
  import { resolveChannelResetConfig, resolveSessionResetType } from "./reset-type.js";
7
7
  import { resolveSessionLifecycleTimestamps } from "./lifecycle-timestamps.js";
@@ -3,9 +3,9 @@ import { loadEntriesFromFile } from "./load-jsonl-entries.js";
3
3
  import { withTranscriptFileLock } from "./transcript-file-lock.js";
4
4
  import { emitSessionTranscriptUpdate } from "../transcript-events.js";
5
5
  import { buildSessionContextForLlm, isTranscriptContextEntry } from "../session-context-for-llm.js";
6
- import path from "node:path";
7
- import { existsSync } from "node:fs";
8
6
  import { randomUUID } from "node:crypto";
7
+ import { existsSync } from "node:fs";
8
+ import path from "node:path";
9
9
  import { CURRENT_SESSION_VERSION, SessionManager } from "@earendil-works/pi-coding-agent";
10
10
  //#region src/session/parity/jsonl-transcript-io.ts
11
11
  init_write_file_atomic();
@@ -1,9 +1,9 @@
1
1
  import { init_write_file_atomic, writeTextAtomic } from "../../infra/write-file-atomic.js";
2
2
  import { readSessionsJsonFileRaw } from "./sessions-json-file-read.js";
3
3
  import { commitSessionsJsonWrite, getSessionsJsonWriteStats, invalidateSessionsJsonCache, noteSessionsJsonWritten, readSessionsJsonCached, resetSessionsJsonCacheForTest } from "./sessions-json-cache.js";
4
- import { dirname } from "node:path";
5
4
  import { existsSync } from "node:fs";
6
5
  import { mkdir } from "node:fs/promises";
6
+ import { dirname } from "node:path";
7
7
  import lockfile from "proper-lockfile";
8
8
  //#region src/session/parity/sessions-json-file.ts
9
9
  init_write_file_atomic();
@@ -1,7 +1,7 @@
1
- import path from "node:path";
2
- import { existsSync } from "node:fs";
3
1
  import { createHash } from "node:crypto";
2
+ import { existsSync } from "node:fs";
4
3
  import { mkdir, open, readFile, rm, stat } from "node:fs/promises";
4
+ import path from "node:path";
5
5
  //#region src/session/parity/transcript-file-lock.ts
6
6
  /**
7
7
  * Cross-process (and re-entrant same-process) advisory lock for session transcript JSONL.
@@ -1,8 +1,8 @@
1
1
  import { __esmMin } from "../../../_virtual/_rolldown/runtime.js";
2
2
  import { formatSessionArchiveTimestamp, init_artifacts } from "./artifacts.js";
3
3
  import { init_session_id, validateSessionId } from "./session-id.js";
4
- import { dirname, relative, resolve } from "node:path";
5
4
  import fs from "node:fs";
5
+ import { dirname, relative, resolve } from "node:path";
6
6
  //#region src/session/parity/transcript-paths.ts
7
7
  function resolveSessionsDir(opts) {
8
8
  const sessionsDir = opts?.sessionsDir?.trim();
@@ -1,10 +1,10 @@
1
- import { SessionConfigSchema, init_schema } from "../config/schema.js";
2
- import { init_agent_scope, resolveDefaultAgentId } from "../agent/agent-scope.js";
3
- import { init_agent_session_key, normalizeAgentId, resolveAgentIdFromSessionKey } from "../routing/agent-session-key.js";
4
- import { init_session_key, parseSessionKey } from "../routing/session-key.js";
5
1
  import { createLogger } from "../utils/logger/index.js";
6
2
  import { init_logger } from "../utils/logger.js";
3
+ import { init_agent_scope, resolveDefaultAgentId } from "../agent/agent-scope.js";
7
4
  import { init_paths, resolveSessionsMapPath } from "../config/paths.js";
5
+ import { SessionConfigSchema, init_schema } from "../config/schema.js";
6
+ import { init_agent_session_key, normalizeAgentId, resolveAgentIdFromSessionKey } from "../routing/agent-session-key.js";
7
+ import { init_session_key, parseSessionKey } from "../routing/session-key.js";
8
8
  import { readSessionsJsonFile } from "./parity/sessions-json-file.js";
9
9
  import { normalizeThinkLevel, normalizeVerboseLevel } from "../agent/transcript/thinking-types.js";
10
10
  import { evaluateSessionFreshness, resolveSessionResetPolicy } from "./reset-policy.js";
@@ -1,7 +1,7 @@
1
1
  import { FILENAMES, init_paths } from "../config/paths.js";
2
2
  import { SessionSearchIndex } from "./search-index.js";
3
- import { join } from "node:path";
4
3
  import { stat } from "node:fs/promises";
4
+ import { join } from "node:path";
5
5
  //#region src/session/search-index-cache.ts
6
6
  /**
7
7
  * Shared cache for {@link SessionSearchIndex} builds (invalidated on session writes).
@@ -2,8 +2,8 @@ import { init_transcript_paths, resolveSessionFilePath } from "./parity/transcri
2
2
  import { FILENAMES, init_paths } from "../config/paths.js";
3
3
  import { isTranscriptContextEntry } from "./session-context-for-llm.js";
4
4
  import { readTranscriptRowsFromFile, rowsToLlmMessages } from "./parity/jsonl-transcript-io.js";
5
- import { join } from "node:path";
6
5
  import { readFile } from "node:fs/promises";
6
+ import { join } from "node:path";
7
7
  //#region src/session/search-index.ts
8
8
  /**
9
9
  * In-memory inverted index over session transcripts (`sessions.json` + JSONL).
@@ -1,7 +1,7 @@
1
- import { isCronSessionKey } from "../routing/session-key-utils.js";
2
- import { init_session_key, parseSessionKey } from "../routing/session-key.js";
3
1
  import { createLogger } from "../utils/logger/index.js";
4
2
  import { init_logger } from "../utils/logger.js";
3
+ import { isCronSessionKey } from "../routing/session-key-utils.js";
4
+ import { init_session_key, parseSessionKey } from "../routing/session-key.js";
5
5
  import { init_providers, resolveModel } from "../providers/index.js";
6
6
  import { readAgentMessageContent } from "../agent/memory/agent-message-access.js";
7
7
  import { stripSessionStartupContextFromUserText } from "../agent/reply/startup-context.js";
@@ -22,6 +22,7 @@ export declare class SessionStore {
22
22
  private compactor;
23
23
  private storeMutationDepth;
24
24
  private storeMutationChain;
25
+ private allSessionsMapCache?;
25
26
  /** Cache of per-agent sessions dirs to avoid re-resolution on every call. */
26
27
  private agentSessionsDirCache;
27
28
  constructor(options: SessionStoreOptions, windowConfig?: Partial<WindowConfig>, compactionConfig?: Partial<CompactionConfig>);
@@ -39,10 +40,11 @@ export declare class SessionStore {
39
40
  private transcriptPathForEntry;
40
41
  private readMapForKey;
41
42
  private readMap;
43
+ private invalidateAllSessionsMapCache;
44
+ private discoverSessionMapPaths;
42
45
  /**
43
- * OpenClaw-aligned: read sessions.json from ALL known agents and merge into a single map.
44
- * Used by aggregation queries (list, getByAgent, getByAccount, etc.) so the gateway UI
45
- * can display sessions across all agents.
46
+ * Unified cross-agent aggregation entry for global session views.
47
+ * Reads configured agents plus existing per-agent session maps under the state directory.
46
48
  */
47
49
  private readAllMaps;
48
50
  private getDiskEntry;