@xopcai/xopc 0.0.6 → 0.0.11

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 (412) hide show
  1. package/dist/extensions/weixin/src/api/api.js +1 -1
  2. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  3. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  4. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  5. package/dist/gateway/static/root/assets/{agents-B6s2BvpH.js → agents-BdC4Y-HX.js} +2 -2
  6. package/dist/gateway/static/root/assets/agents-BdC4Y-HX.js.map +1 -0
  7. package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js → apps-page-C-oaSHkm.js} +2 -2
  8. package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js.map → apps-page-C-oaSHkm.js.map} +1 -1
  9. package/dist/gateway/static/root/assets/attachment-load-BDDlItdE.js +1 -0
  10. package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js → channels-settings-BqEUppPO.js} +2 -2
  11. package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js.map → channels-settings-BqEUppPO.js.map} +1 -1
  12. package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js → chat-agents-api-BhqjQ7iL.js} +2 -2
  13. package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js.map → chat-agents-api-BhqjQ7iL.js.map} +1 -1
  14. package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js → cron-page-Cli49RKR.js} +2 -2
  15. package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js.map → cron-page-Cli49RKR.js.map} +1 -1
  16. package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js → cron-utils-Dkj-Ldpf.js} +2 -2
  17. package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js.map → cron-utils-Dkj-Ldpf.js.map} +1 -1
  18. package/dist/gateway/static/root/assets/electron-env-BDtJw9AY.js +2 -0
  19. package/dist/gateway/static/root/assets/electron-env-BDtJw9AY.js.map +1 -0
  20. package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js → extension-debug-page-BMcZlaxF.js} +2 -2
  21. package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js.map → extension-debug-page-BMcZlaxF.js.map} +1 -1
  22. package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js → extension-iframe-host-D5HEF0KR.js} +2 -2
  23. package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js.map → extension-iframe-host-D5HEF0KR.js.map} +1 -1
  24. package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js → extension-page-CXdCSSPl.js} +2 -2
  25. package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js.map → extension-page-CXdCSSPl.js.map} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js → extension-provider-DZCZgQE2.js} +2 -2
  27. package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js.map → extension-provider-DZCZgQE2.js.map} +1 -1
  28. package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js → extension-settings-page-CX6STpx3.js} +2 -2
  29. package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js.map → extension-settings-page-CX6STpx3.js.map} +1 -1
  30. package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js → gateway-config-swr-Cph02QZn.js} +2 -2
  31. package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js.map → gateway-config-swr-Cph02QZn.js.map} +1 -1
  32. package/dist/gateway/static/root/assets/index-Bty3m0mS.css +2 -0
  33. package/dist/gateway/static/root/assets/index-iTUyfzNr.js +16 -0
  34. package/dist/gateway/static/root/assets/index-iTUyfzNr.js.map +1 -0
  35. package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js → logs-page-B9O5l3I8.js} +2 -2
  36. package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js.map → logs-page-B9O5l3I8.js.map} +1 -1
  37. package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js → model-selector-BLiY_O25.js} +2 -2
  38. package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js.map → model-selector-BLiY_O25.js.map} +1 -1
  39. package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js +2 -0
  40. package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js.map +1 -0
  41. package/dist/gateway/static/root/assets/page-header-store-BFpnFTed.js +2 -0
  42. package/dist/gateway/static/root/assets/{page-header-store-HcRZK5CZ.js.map → page-header-store-BFpnFTed.js.map} +1 -1
  43. package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js → session-api-DEhQXWJg.js} +2 -2
  44. package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js.map → session-api-DEhQXWJg.js.map} +1 -1
  45. package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js → session-working-directory-control-DKOtWs3-.js} +3 -3
  46. package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js.map → session-working-directory-control-DKOtWs3-.js.map} +1 -1
  47. package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js → sessions-page-BYlWP1ep.js} +2 -2
  48. package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js.map → sessions-page-BYlWP1ep.js.map} +1 -1
  49. package/dist/gateway/static/root/assets/settings-page-oCnIavdg.js +2 -0
  50. package/dist/gateway/static/root/assets/settings-page-oCnIavdg.js.map +1 -0
  51. package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js → skill-api-DWrn8Az0.js} +2 -2
  52. package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js.map → skill-api-DWrn8Az0.js.map} +1 -1
  53. package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js → skills-page-C59WQpM1.js} +2 -2
  54. package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js.map → skills-page-C59WQpM1.js.map} +1 -1
  55. package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js → theme-store-CywXkKml.js} +2 -2
  56. package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js.map → theme-store-CywXkKml.js.map} +1 -1
  57. package/dist/gateway/static/root/assets/url-D7yWllI8.js +2 -0
  58. package/dist/gateway/static/root/assets/url-D7yWllI8.js.map +1 -0
  59. package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js → useTranslation-CACj0DBJ.js} +2 -2
  60. package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js.map → useTranslation-CACj0DBJ.js.map} +1 -1
  61. package/dist/gateway/static/root/index.html +16 -16
  62. package/dist/package.js +1 -1
  63. package/dist/src/agent/agent-manager.d.ts +1 -0
  64. package/dist/src/agent/agent-manager.js +17 -9
  65. package/dist/src/agent/agent-manager.js.map +1 -1
  66. package/dist/src/agent/background-review/run-background-review.js +2 -0
  67. package/dist/src/agent/background-review/run-background-review.js.map +1 -1
  68. package/dist/src/agent/child-agent-factory.js +2 -0
  69. package/dist/src/agent/child-agent-factory.js.map +1 -1
  70. package/dist/src/agent/context/expand-at-file-mentions.d.ts +4 -0
  71. package/dist/src/agent/context/expand-at-file-mentions.js +69 -0
  72. package/dist/src/agent/context/expand-at-file-mentions.js.map +1 -0
  73. package/dist/src/agent/context/workspace-seed.js +1 -1
  74. package/dist/src/agent/image/index.d.ts +0 -1
  75. package/dist/src/agent/image/index.js +1 -2
  76. package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
  77. package/dist/src/agent/ipc/inbox.js +1 -1
  78. package/dist/src/agent/ipc/socket.js +1 -1
  79. package/dist/src/agent/memory/compaction.d.ts +1 -1
  80. package/dist/src/agent/memory/compaction.js +38 -11
  81. package/dist/src/agent/memory/compaction.js.map +1 -1
  82. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  83. package/dist/src/agent/messaging/command-handler.d.ts +13 -0
  84. package/dist/src/agent/messaging/command-handler.js +14 -2
  85. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  86. package/dist/src/agent/orchestration/agent-orchestrator.js +6 -1
  87. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  88. package/dist/src/agent/service.d.ts +16 -1
  89. package/dist/src/agent/service.js +175 -17
  90. package/dist/src/agent/service.js.map +1 -1
  91. package/dist/src/agent/skills/format-skills-prompt.js.map +1 -1
  92. package/dist/src/agent/skills/hub-hash.js +1 -1
  93. package/dist/src/agent/skills/hub-pull.js +1 -1
  94. package/dist/src/agent/skills/scanner.js +1 -1
  95. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  96. package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
  97. package/dist/src/agent/tools/browser/tools.js +2 -2
  98. package/dist/src/agent/tools/browser/tools.js.map +1 -1
  99. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  100. package/dist/src/agent/tools/image-tool.js +2 -2
  101. package/dist/src/agent/tools/image-tool.js.map +1 -1
  102. package/dist/src/agent/tools/index.d.ts +1 -1
  103. package/dist/src/agent/tools/index.js +2 -2
  104. package/dist/src/agent/tools/read.d.ts +0 -2
  105. package/dist/src/agent/tools/read.js +1 -3
  106. package/dist/src/agent/tools/read.js.map +1 -1
  107. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  108. package/dist/src/agent/tools/write.js +1 -1
  109. package/dist/src/auth/credentials.js +2 -2
  110. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  111. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  112. package/dist/src/channels/index.d.ts +1 -1
  113. package/dist/src/channels/index.js +2 -2
  114. package/dist/src/channels/pipeline.d.ts +8 -1
  115. package/dist/src/channels/pipeline.js +49 -4
  116. package/dist/src/channels/pipeline.js.map +1 -1
  117. package/dist/src/channels/plugin-types.d.ts +14 -0
  118. package/dist/src/chat-commands/builtins/config.d.ts +4 -0
  119. package/dist/src/chat-commands/builtins/config.js +197 -0
  120. package/dist/src/chat-commands/builtins/config.js.map +1 -0
  121. package/dist/src/chat-commands/builtins/context.d.ts +4 -0
  122. package/dist/src/chat-commands/builtins/context.js +44 -0
  123. package/dist/src/chat-commands/builtins/context.js.map +1 -0
  124. package/dist/src/chat-commands/builtins/session.js +111 -0
  125. package/dist/src/chat-commands/builtins/session.js.map +1 -1
  126. package/dist/src/chat-commands/builtins/thinking.js +49 -21
  127. package/dist/src/chat-commands/builtins/thinking.js.map +1 -1
  128. package/dist/src/chat-commands/config-paths.d.ts +10 -0
  129. package/dist/src/chat-commands/config-paths.js +45 -0
  130. package/dist/src/chat-commands/config-paths.js.map +1 -0
  131. package/dist/src/chat-commands/config-value.d.ts +12 -0
  132. package/dist/src/chat-commands/config-value.js +53 -0
  133. package/dist/src/chat-commands/config-value.js.map +1 -0
  134. package/dist/src/chat-commands/context.d.ts +24 -1
  135. package/dist/src/chat-commands/context.js +41 -0
  136. package/dist/src/chat-commands/context.js.map +1 -1
  137. package/dist/src/chat-commands/index.d.ts +2 -0
  138. package/dist/src/chat-commands/index.js +5 -1
  139. package/dist/src/chat-commands/index.js.map +1 -1
  140. package/dist/src/chat-commands/types.d.ts +33 -1
  141. package/dist/src/cli/commands/agent/interactive.js +1 -1
  142. package/dist/src/cli/commands/agent/interactive.js.map +1 -1
  143. package/dist/src/cli/commands/agent.js +21 -9
  144. package/dist/src/cli/commands/agent.js.map +1 -1
  145. package/dist/src/cli/commands/auth.js.map +1 -1
  146. package/dist/src/cli/commands/doctor/checks/channel-config.d.ts +2 -0
  147. package/dist/src/cli/commands/doctor/checks/channel-config.js +113 -0
  148. package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -0
  149. package/dist/src/cli/commands/doctor/checks/channel-plugins.d.ts +2 -0
  150. package/dist/src/cli/commands/doctor/checks/channel-plugins.js +47 -0
  151. package/dist/src/cli/commands/doctor/checks/channel-plugins.js.map +1 -0
  152. package/dist/src/cli/commands/doctor/checks/config-health.d.ts +2 -0
  153. package/dist/src/cli/commands/doctor/checks/config-health.js +82 -0
  154. package/dist/src/cli/commands/doctor/checks/config-health.js.map +1 -0
  155. package/dist/src/cli/commands/doctor/checks/cron-health.d.ts +2 -0
  156. package/dist/src/cli/commands/doctor/checks/cron-health.js +116 -0
  157. package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -0
  158. package/dist/src/cli/commands/doctor/checks/gateway-health.d.ts +2 -0
  159. package/dist/src/cli/commands/doctor/checks/gateway-health.js +64 -0
  160. package/dist/src/cli/commands/doctor/checks/gateway-health.js.map +1 -0
  161. package/dist/src/cli/commands/doctor/checks/gateway-service.d.ts +2 -0
  162. package/dist/src/cli/commands/doctor/checks/gateway-service.js +64 -0
  163. package/dist/src/cli/commands/doctor/checks/gateway-service.js.map +1 -0
  164. package/dist/src/cli/commands/doctor/checks/node-version.d.ts +2 -0
  165. package/dist/src/cli/commands/doctor/checks/node-version.js +33 -0
  166. package/dist/src/cli/commands/doctor/checks/node-version.js.map +1 -0
  167. package/dist/src/cli/commands/doctor/checks/provider-auth.d.ts +2 -0
  168. package/dist/src/cli/commands/doctor/checks/provider-auth.js +91 -0
  169. package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -0
  170. package/dist/src/cli/commands/doctor/checks/security-audit.d.ts +2 -0
  171. package/dist/src/cli/commands/doctor/checks/security-audit.js +85 -0
  172. package/dist/src/cli/commands/doctor/checks/security-audit.js.map +1 -0
  173. package/dist/src/cli/commands/doctor/checks/session-integrity.d.ts +2 -0
  174. package/dist/src/cli/commands/doctor/checks/session-integrity.js +118 -0
  175. package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -0
  176. package/dist/src/cli/commands/doctor/checks/state-integrity.d.ts +2 -0
  177. package/dist/src/cli/commands/doctor/checks/state-integrity.js +99 -0
  178. package/dist/src/cli/commands/doctor/checks/state-integrity.js.map +1 -0
  179. package/dist/src/cli/commands/doctor/checks/version-check.d.ts +2 -0
  180. package/dist/src/cli/commands/doctor/checks/version-check.js +71 -0
  181. package/dist/src/cli/commands/doctor/checks/version-check.js.map +1 -0
  182. package/dist/src/cli/commands/doctor/checks/workspace-status.d.ts +2 -0
  183. package/dist/src/cli/commands/doctor/checks/workspace-status.js +73 -0
  184. package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -0
  185. package/dist/src/cli/commands/doctor/flow.d.ts +9 -0
  186. package/dist/src/cli/commands/doctor/flow.js +51 -0
  187. package/dist/src/cli/commands/doctor/flow.js.map +1 -0
  188. package/dist/src/cli/commands/doctor/format.d.ts +6 -0
  189. package/dist/src/cli/commands/doctor/format.js +61 -0
  190. package/dist/src/cli/commands/doctor/format.js.map +1 -0
  191. package/dist/src/cli/commands/doctor/index.js +44 -0
  192. package/dist/src/cli/commands/doctor/index.js.map +1 -0
  193. package/dist/src/cli/commands/doctor/types.d.ts +20 -0
  194. package/dist/src/cli/commands/doctor/types.js +1 -0
  195. package/dist/src/cli/commands/extension.js +10 -0
  196. package/dist/src/cli/commands/extension.js.map +1 -1
  197. package/dist/src/cli/commands/init.js +1 -2
  198. package/dist/src/cli/commands/init.js.map +1 -1
  199. package/dist/src/cli/commands/session/utils.js.map +1 -1
  200. package/dist/src/cli/commands/update.d.ts +1 -0
  201. package/dist/src/cli/commands/update.js +171 -0
  202. package/dist/src/cli/commands/update.js.map +1 -0
  203. package/dist/src/cli/index.d.ts +2 -2
  204. package/dist/src/cli/index.js +4 -2
  205. package/dist/src/cli/index.js.map +1 -1
  206. package/dist/src/cli/utils/init-workspace.js +1 -1
  207. package/dist/src/config/index.d.ts +1 -0
  208. package/dist/src/config/index.js +4 -3
  209. package/dist/src/config/index.js.map +1 -1
  210. package/dist/src/config/loader.js +1 -1
  211. package/dist/src/config/models-json.d.ts +15 -15
  212. package/dist/src/config/paths.js.map +1 -1
  213. package/dist/src/config/profile.js +1 -1
  214. package/dist/src/config/runtime-overrides.d.ts +8 -0
  215. package/dist/src/config/runtime-overrides.js +40 -0
  216. package/dist/src/config/runtime-overrides.js.map +1 -0
  217. package/dist/src/config/schema.d.ts +34 -104
  218. package/dist/src/config/schema.js +18 -39
  219. package/dist/src/config/schema.js.map +1 -1
  220. package/dist/src/cron/persistence.js +1 -1
  221. package/dist/src/cron/run-log-store.js +1 -1
  222. package/dist/src/daemon/launchd.js +2 -2
  223. package/dist/src/daemon/launchd.js.map +1 -1
  224. package/dist/src/daemon/systemd.js +2 -2
  225. package/dist/src/daemon/systemd.js.map +1 -1
  226. package/dist/src/extensions/health.js +1 -1
  227. package/dist/src/extensions/loader.d.ts +1 -1
  228. package/dist/src/extensions/loader.js +5 -8
  229. package/dist/src/extensions/loader.js.map +1 -1
  230. package/dist/src/extensions/lockfile.js +1 -1
  231. package/dist/src/extensions/sdk/index.js +6 -1
  232. package/dist/src/extensions/sdk/index.js.map +1 -0
  233. package/dist/src/gateway/agents-admin.js +1 -1
  234. package/dist/src/gateway/agents-admin.js.map +1 -1
  235. package/dist/src/gateway/hono/lib/static-ui.js +1 -1
  236. package/dist/src/gateway/hono/oauth.js +1 -1
  237. package/dist/src/gateway/hono/routes/config.js +1 -1
  238. package/dist/src/gateway/hono/routes/doctor.d.ts +3 -0
  239. package/dist/src/gateway/hono/routes/doctor.js +35 -0
  240. package/dist/src/gateway/hono/routes/doctor.js.map +1 -0
  241. package/dist/src/gateway/hono/routes/index.js +4 -0
  242. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  243. package/dist/src/gateway/hono/routes/models.js +64 -11
  244. package/dist/src/gateway/hono/routes/models.js.map +1 -1
  245. package/dist/src/gateway/hono/routes/public-gateway.js +10 -0
  246. package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
  247. package/dist/src/gateway/hono/routes/update.d.ts +3 -0
  248. package/dist/src/gateway/hono/routes/update.js +141 -0
  249. package/dist/src/gateway/hono/routes/update.js.map +1 -0
  250. package/dist/src/gateway/hono/routes/workspace.js +82 -2
  251. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  252. package/dist/src/gateway/lock.js +1 -1
  253. package/dist/src/gateway/ports.js +98 -3
  254. package/dist/src/gateway/ports.js.map +1 -1
  255. package/dist/src/gateway/service.d.ts +1 -4
  256. package/dist/src/gateway/service.js +13 -20
  257. package/dist/src/gateway/service.js.map +1 -1
  258. package/dist/src/gateway/workspace-fs-file-list.d.ts +5 -0
  259. package/dist/src/gateway/workspace-fs-file-list.js +56 -0
  260. package/dist/src/gateway/workspace-fs-file-list.js.map +1 -0
  261. package/dist/src/gateway/workspace-heartbeat-path.js +1 -1
  262. package/dist/src/gateway/workspace-ripgrep.d.ts +5 -0
  263. package/dist/src/gateway/workspace-ripgrep.js +88 -4
  264. package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
  265. package/dist/src/infra/update-channels.d.ts +14 -0
  266. package/dist/src/infra/update-channels.js +30 -0
  267. package/dist/src/infra/update-channels.js.map +1 -0
  268. package/dist/src/infra/update-check.d.ts +53 -0
  269. package/dist/src/infra/update-check.js +155 -0
  270. package/dist/src/infra/update-check.js.map +1 -0
  271. package/dist/src/infra/update-runner.d.ts +18 -0
  272. package/dist/src/infra/update-runner.js +112 -0
  273. package/dist/src/infra/update-runner.js.map +1 -0
  274. package/dist/src/infra/update-startup.d.ts +20 -0
  275. package/dist/src/infra/update-startup.js +246 -0
  276. package/dist/src/infra/update-startup.js.map +1 -0
  277. package/dist/src/providers/extension-stream-bridge.d.ts +3 -0
  278. package/dist/src/providers/extension-stream-bridge.js +239 -0
  279. package/dist/src/providers/extension-stream-bridge.js.map +1 -0
  280. package/dist/src/providers/index.d.ts +7 -2
  281. package/dist/src/providers/index.js +77 -14
  282. package/dist/src/providers/index.js.map +1 -1
  283. package/dist/src/providers/model-registry.js +1 -1
  284. package/dist/src/providers/plugin-registry.js +92 -87
  285. package/dist/src/providers/plugin-registry.js.map +1 -1
  286. package/dist/src/routing/bindings.js +1 -1
  287. package/dist/src/routing/index.d.ts +1 -1
  288. package/dist/src/routing/index.js +2 -2
  289. package/dist/src/routing/index.js.map +1 -1
  290. package/dist/src/routing/resolve-route.js +1 -1
  291. package/dist/src/routing/session-key.d.ts +0 -5
  292. package/dist/src/routing/session-key.js +1 -27
  293. package/dist/src/routing/session-key.js.map +1 -1
  294. package/dist/src/session/chat-export.d.ts +5 -0
  295. package/dist/src/session/chat-export.js +35 -0
  296. package/dist/src/session/chat-export.js.map +1 -0
  297. package/dist/src/session/config-store.js +1 -1
  298. package/dist/src/session/manager.d.ts +1 -1
  299. package/dist/src/session/manager.js +2 -2
  300. package/dist/src/session/manager.js.map +1 -1
  301. package/dist/src/session/store.d.ts +1 -1
  302. package/dist/src/session/store.js +3 -7
  303. package/dist/src/session/store.js.map +1 -1
  304. package/dist/src/session/types.d.ts +0 -10
  305. package/dist/src/session/types.js.map +1 -1
  306. package/dist/src/utils/logger/audit.js +1 -1
  307. package/dist/src/utils/logger/log-store.js +1 -1
  308. package/dist/src/utils/logger/rotation.js +1 -1
  309. package/dist/src/voice/tts/audio.js +1 -1
  310. package/package.json +2 -2
  311. package/dist/gateway/static/root/assets/agents-B6s2BvpH.js.map +0 -1
  312. package/dist/gateway/static/root/assets/attachment-load-6pRlDPZ8.js +0 -1
  313. package/dist/gateway/static/root/assets/index-DBZ5eXW5.js +0 -16
  314. package/dist/gateway/static/root/assets/index-DBZ5eXW5.js.map +0 -1
  315. package/dist/gateway/static/root/assets/index-KsVMH-Jo.css +0 -2
  316. package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js +0 -2
  317. package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js.map +0 -1
  318. package/dist/gateway/static/root/assets/page-header-store-HcRZK5CZ.js +0 -2
  319. package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js +0 -2
  320. package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js.map +0 -1
  321. package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js +0 -2
  322. package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js.map +0 -1
  323. package/dist/gateway/static/root/assets/url-QmwQTJ-j.js +0 -2
  324. package/dist/gateway/static/root/assets/url-QmwQTJ-j.js.map +0 -1
  325. package/dist/src/acp/commands.d.ts +0 -11
  326. package/dist/src/acp/commands.js +0 -17
  327. package/dist/src/acp/commands.js.map +0 -1
  328. package/dist/src/acp/control-plane/identity-reconcile.d.ts +0 -36
  329. package/dist/src/acp/control-plane/identity-reconcile.js +0 -124
  330. package/dist/src/acp/control-plane/identity-reconcile.js.map +0 -1
  331. package/dist/src/acp/control-plane/index.d.ts +0 -10
  332. package/dist/src/acp/control-plane/index.js +0 -6
  333. package/dist/src/acp/control-plane/manager.d.ts +0 -86
  334. package/dist/src/acp/control-plane/manager.js +0 -502
  335. package/dist/src/acp/control-plane/manager.js.map +0 -1
  336. package/dist/src/acp/control-plane/manager.types.d.ts +0 -125
  337. package/dist/src/acp/control-plane/manager.types.js +0 -14
  338. package/dist/src/acp/control-plane/manager.types.js.map +0 -1
  339. package/dist/src/acp/control-plane/manager.utils.d.ts +0 -29
  340. package/dist/src/acp/control-plane/manager.utils.js +0 -46
  341. package/dist/src/acp/control-plane/manager.utils.js.map +0 -1
  342. package/dist/src/acp/control-plane/runtime-cache-manager.d.ts +0 -49
  343. package/dist/src/acp/control-plane/runtime-cache-manager.js +0 -155
  344. package/dist/src/acp/control-plane/runtime-cache-manager.js.map +0 -1
  345. package/dist/src/acp/control-plane/runtime-cache.d.ts +0 -45
  346. package/dist/src/acp/control-plane/runtime-cache.js +0 -58
  347. package/dist/src/acp/control-plane/runtime-cache.js.map +0 -1
  348. package/dist/src/acp/control-plane/runtime-options.d.ts +0 -30
  349. package/dist/src/acp/control-plane/runtime-options.js +0 -92
  350. package/dist/src/acp/control-plane/runtime-options.js.map +0 -1
  351. package/dist/src/acp/control-plane/session-actor-queue.d.ts +0 -22
  352. package/dist/src/acp/control-plane/session-actor-queue.js +0 -70
  353. package/dist/src/acp/control-plane/session-actor-queue.js.map +0 -1
  354. package/dist/src/acp/control-plane/session-lifecycle-manager.d.ts +0 -59
  355. package/dist/src/acp/control-plane/session-lifecycle-manager.js +0 -209
  356. package/dist/src/acp/control-plane/session-lifecycle-manager.js.map +0 -1
  357. package/dist/src/acp/control-plane/session-store.d.ts +0 -39
  358. package/dist/src/acp/control-plane/session-store.js +0 -149
  359. package/dist/src/acp/control-plane/session-store.js.map +0 -1
  360. package/dist/src/acp/control-plane/turn-manager.d.ts +0 -40
  361. package/dist/src/acp/control-plane/turn-manager.js +0 -134
  362. package/dist/src/acp/control-plane/turn-manager.js.map +0 -1
  363. package/dist/src/acp/event-mapper.d.ts +0 -48
  364. package/dist/src/acp/event-mapper.js +0 -94
  365. package/dist/src/acp/event-mapper.js.map +0 -1
  366. package/dist/src/acp/index.d.ts +0 -10
  367. package/dist/src/acp/index.js +0 -5
  368. package/dist/src/acp/meta.d.ts +0 -15
  369. package/dist/src/acp/meta.js +0 -36
  370. package/dist/src/acp/meta.js.map +0 -1
  371. package/dist/src/acp/routing-integration.d.ts +0 -37
  372. package/dist/src/acp/routing-integration.js +0 -58
  373. package/dist/src/acp/routing-integration.js.map +0 -1
  374. package/dist/src/acp/runtime/backends/index.d.ts +0 -4
  375. package/dist/src/acp/runtime/backends/index.js +0 -2
  376. package/dist/src/acp/runtime/backends/local.d.ts +0 -136
  377. package/dist/src/acp/runtime/backends/local.js +0 -603
  378. package/dist/src/acp/runtime/backends/local.js.map +0 -1
  379. package/dist/src/acp/runtime/error-text.d.ts +0 -16
  380. package/dist/src/acp/runtime/error-text.js +0 -40
  381. package/dist/src/acp/runtime/error-text.js.map +0 -1
  382. package/dist/src/acp/runtime/errors.d.ts +0 -31
  383. package/dist/src/acp/runtime/errors.js +0 -47
  384. package/dist/src/acp/runtime/errors.js.map +0 -1
  385. package/dist/src/acp/runtime/index.d.ts +0 -7
  386. package/dist/src/acp/runtime/index.js +0 -4
  387. package/dist/src/acp/runtime/registry.d.ts +0 -35
  388. package/dist/src/acp/runtime/registry.js +0 -85
  389. package/dist/src/acp/runtime/registry.js.map +0 -1
  390. package/dist/src/acp/runtime/session-identity.d.ts +0 -35
  391. package/dist/src/acp/runtime/session-identity.js +0 -134
  392. package/dist/src/acp/runtime/session-identity.js.map +0 -1
  393. package/dist/src/acp/runtime/types.d.ts +0 -214
  394. package/dist/src/acp/secret-file.d.ts +0 -7
  395. package/dist/src/acp/secret-file.js +0 -19
  396. package/dist/src/acp/secret-file.js.map +0 -1
  397. package/dist/src/acp/server.d.ts +0 -48
  398. package/dist/src/acp/server.js +0 -300
  399. package/dist/src/acp/server.js.map +0 -1
  400. package/dist/src/acp/session.d.ts +0 -29
  401. package/dist/src/acp/session.js +0 -30
  402. package/dist/src/acp/session.js.map +0 -1
  403. package/dist/src/acp/types.d.ts +0 -39
  404. package/dist/src/acp/types.js +0 -13
  405. package/dist/src/acp/types.js.map +0 -1
  406. package/dist/src/agent/image/describe-images.d.ts +0 -18
  407. package/dist/src/agent/image/describe-images.js +0 -19
  408. package/dist/src/agent/image/describe-images.js.map +0 -1
  409. package/dist/src/cli/commands/acp.d.ts +0 -4
  410. package/dist/src/cli/commands/acp.js +0 -200
  411. package/dist/src/cli/commands/acp.js.map +0 -1
  412. /package/dist/src/{acp/runtime/types.js → cli/commands/doctor/index.d.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","names":["writeConfigToDisk"],"sources":["../../../src/gateway/service.ts"],"sourcesContent":["import crypto from 'crypto';\nimport { join } from 'path';\nimport { AgentService } from '../agent/service.js';\nimport { ChannelManager } from '../channels/manager.js';\nimport { CHAT_CHANNEL_ORDER } from '../channels/registry.js';\nimport { MessageBus, MessageBusShutdownError } from '../infra/bus/index.js';\nimport type { Config as SurfaceConfig } from '../config/config-surface.js';\nimport { loadConfig, saveConfig as writeConfigToDisk } from '../config/index.js';\nimport { getWorkspacePath } from '../config/schema.js';\nimport { CronService } from '../cron/index.js';\nimport { computeBundledExtensionExtensionsPatch } from '../extensions/bundled-extension-activation.js';\nimport { ExtensionLoader } from '../extensions/index.js';\nimport { HeartbeatService, heartbeatRunnerConfigFromConfig } from './heartbeat/index.js';\nimport { ConfigHotReloader } from '../config/reload.js';\nimport { SessionManager } from '../session/index.js';\nimport type { Config } from '../config/schema.js';\nimport type { SessionListQuery, ExportFormat } from '../session/types.js';\nimport { resolveGatewayAuth, assertGatewayAuthConfigured, validateToken, extractToken, type ResolvedGatewayAuth } from './auth.js';\nimport { getModelRegistry } from '../providers/index.js';\nimport {\n createLogger,\n getLogDir,\n getLogStats,\n inboundCorrelationMetadataFromAsyncLogContext,\n} from '../utils/logger.js';\nimport {\n resolveConfigPath,\n resolveCronJobsPath,\n resolveStateDir,\n resolveAgentDir,\n resolveWorkspaceExtensionsDir,\n} from '../config/paths.js';\nimport { AgentRunRelay, type RelayEvent } from './agent-run-relay.js';\nimport { ClarifyBridge, type ClarifyBridgeRequest } from './clarify-bridge.js';\nimport { registerClarifyBridge } from './clarify-runtime.js';\nimport {\n deleteManagedSkill as deleteManagedSkillDir,\n installSkillFromZip,\n listManagedSkillDirs,\n} from '../agent/skills/managed-store.js';\nimport {\n downloadSkillZipBuffer,\n fetchMarketplacePackageDetail,\n listSkillPackages,\n resolveSkillZipDownloadUrl,\n resolveSkillsStoreBaseUrl,\n skillIdForMarketplaceInstall,\n type MarketplacePackageDetail,\n type SkillsStoreListParams,\n type SkillsStoreListResponse,\n} from '../agent/skills/skills-store-client.js';\nimport { createSkillConfigManager } from '../agent/skills/config.js';\nimport { removeSkillsLockEntry } from '../agent/skills/hub-lock.js';\nimport type { SkillCatalogEntry } from '../agent/agent-manager.js';\nimport type { ManagedSkillListItem } from '../agent/skills/managed-store.js';\n\nconst log = createLogger('GatewayService');\nimport { registerAcpRuntimeBackend } from '../acp/runtime/registry.js';\nimport { createLocalAcpRuntimeBackend } from '../acp/runtime/backends/local.js';\nimport { PACKAGE_VERSION } from '../package-version.js';\nimport { buildSessionKey, parseSessionKey } from '../routing/session-key.js';\nimport { getDefaultAgentId } from '../routing/resolve-route.js';\nimport { MAX_CHAT_ATTACHMENTS } from './chat-limits.js';\n\n// ========== SSE Event System ==========\n\nexport interface ServiceEvent {\n id: string;\n type: string;\n payload: unknown;\n}\n\ntype EventListener = (event: ServiceEvent) => Promise<void> | void;\n\nconst EVENT_BUFFER_SIZE = 200; // ring buffer per subscriber for Last-Event-ID replay\n\nexport interface GatewayServiceConfig {\n configPath?: string;\n enableHotReload?: boolean;\n}\n\nexport class GatewayService {\n private bus: MessageBus;\n private config: Config;\n private configPath: string;\n private agentService: AgentService;\n private channelManager: ChannelManager;\n private cronService: CronService;\n private extensionLoader: ExtensionLoader | null = null;\n private heartbeatService: HeartbeatService;\n private sessionManager: SessionManager;\n private running = false;\n private configReloader: ConfigHotReloader | null = null;\n private startTime = Date.now();\n private workspacePath: string;\n\n // Authentication\n private auth: ResolvedGatewayAuth;\n\n // SSE event system\n private eventCounter = 0;\n private subscribers = new Map<string, EventListener>();\n private eventBuffers = new Map<string, ServiceEvent[]>();\n\n // Agent run relay for resuming SSE streams\n public readonly runRelay = new AgentRunRelay();\n\n /** Per-run abort for webchat (POST /api/agent/abort or client disconnect). */\n private runAbortControllers = new Map<string, AbortController>();\n\n private readonly clarifyBridge = new ClarifyBridge();\n\n /** Maps webchat session key → active `runId` for `clarify` tool routing. */\n private activeWebchatRunBySession = new Map<string, string>();\n\n constructor(private serviceConfig: GatewayServiceConfig = {}) {\n this.bus = new MessageBus();\n this.configPath = serviceConfig.configPath || resolveConfigPath();\n this.config = loadConfig(this.configPath);\n\n // Initialize authentication\n this.auth = resolveGatewayAuth({\n authConfig: this.config.gateway?.auth,\n });\n\n // Validate auth configuration\n assertGatewayAuthConfigured(this.auth);\n\n // Log token info (not the token itself)\n if (this.auth.mode === 'token') {\n const tokenPreview = this.auth.token ? `${this.auth.token.slice(0, 4)}***` : 'none';\n log.info({ mode: this.auth.mode, token: tokenPreview }, 'Authentication configured');\n } else {\n log.info({ mode: this.auth.mode }, 'Authentication disabled');\n }\n\n // Initialize channel manager\n this.channelManager = new ChannelManager(this.config, this.bus);\n\n // Initialize extension loader\n this.workspacePath = getWorkspacePath(this.config) || './workspace';\n this.initializeExtensionLoader();\n\n // Initialize ModelRegistry (loads from models.json)\n const registry = getModelRegistry();\n log.debug({ \n modelCount: registry.getAll().length, \n error: registry.getError() || 'none' \n }, 'ModelRegistry initialized');\n\n // Initialize agent service with extension registry\n const modelConfig = this.config.agents?.defaults?.model;\n const cronRef: { service?: CronService } = {};\n this.agentService = new AgentService(this.bus, {\n workspace: this.workspacePath,\n model: typeof modelConfig === 'string' ? modelConfig : modelConfig?.primary,\n config: this.config,\n extensionRegistry: this.extensionLoader?.getRegistry(),\n getCronService: () => cronRef.service,\n gatewayClarify: {\n requestClarification: (sessionKey, request) => {\n const runId = this.activeWebchatRunBySession.get(sessionKey);\n const publishSse = runId\n ? (e: RelayEvent) => {\n this.agentService.enqueueWebchatSseEvent(sessionKey, e);\n }\n : undefined;\n const parsed = parseSessionKey(sessionKey);\n const deliver =\n parsed?.source === 'telegram'\n ? async (ctx: { sessionKey: string; requestId: string; request: ClarifyBridgeRequest }) => {\n await this.deliverTelegramClarify(ctx);\n }\n : undefined;\n if (!runId && !deliver) {\n return Promise.reject(\n new Error('Clarify is not available for this session (use webchat, Telegram, or CLI)'),\n );\n }\n return this.clarifyBridge.startRequest({\n sessionKey,\n runId,\n relay: this.runRelay,\n publishSse,\n request,\n deliver,\n });\n },\n },\n });\n\n\n // Set channel manager reference for model switching\n this.agentService.setChannelManager(this.channelManager);\n this.channelManager.setSessionModelHooks({\n getModelForSession: (sk) => this.agentService.getModelForSession(sk),\n switchModelForSession: (sk, id) => this.agentService.switchModelForSession(sk, id),\n });\n\n // Initialize cron service\n this.cronService = new CronService({\n filePath: resolveCronJobsPath(),\n agentService: this.agentService,\n messageBus: this.bus,\n });\n cronRef.service = this.cronService;\n\n // Initialize session manager\n this.sessionManager = new SessionManager({\n config: this.config,\n });\n\n this.heartbeatService = new HeartbeatService({\n agentService: this.agentService,\n messageBus: this.bus,\n cronService: this.cronService,\n sessionStore: this.sessionManager.getStore(),\n getConfig: () => this.config,\n });\n\n this.cronService.setDeps({\n agentService: this.agentService,\n messageBus: this.bus,\n heartbeatService: this.heartbeatService,\n getDefaultCronAgentId: () => getDefaultAgentId(this.config),\n });\n }\n\n /**\n * Create extension loader and resolve configs (load runs in start() before channels).\n */\n private initializeExtensionLoader(): void {\n try {\n const aid = getDefaultAgentId(this.config);\n this.extensionLoader = new ExtensionLoader({\n workspaceDir: this.workspacePath,\n extensionsDir: resolveWorkspaceExtensionsDir(this.config, aid),\n });\n this.extensionLoader.setConfig(this.config as Parameters<ExtensionLoader['setConfig']>[0]);\n } catch (error) {\n log.warn({ error }, 'Failed to initialize extension loader');\n }\n }\n\n /**\n * Load extensions and register SDK / full ChannelPlugin instances with ChannelManager.\n */\n private async loadExtensionsAndRegisterChannels(): Promise<void> {\n if (!this.extensionLoader) {\n return;\n }\n try {\n await this.extensionLoader.loadByActivationPlan();\n const reg = this.extensionLoader.getRegistry();\n for (const plugin of reg.channelPlugins) {\n this.channelManager.registerPlugin(plugin);\n }\n log.debug(\n {\n extensionRecords: reg.extensions.size,\n channelPlugins: reg.channelPlugins.length,\n },\n 'Extensions loaded and channel plugins registered',\n );\n } catch (err) {\n log.warn({ err }, 'Failed to load extensions');\n }\n }\n\n /**\n * Initialize ACP runtime backend\n */\n private async initializeAcpRuntime(): Promise<void> {\n try {\n // Check if ACP is enabled in config\n if (!this.config.acp?.enabled) {\n log.debug('ACP runtime disabled in config');\n return;\n }\n\n // Create and register local ACP runtime backend\n const backend = createLocalAcpRuntimeBackend(this.bus);\n registerAcpRuntimeBackend(backend);\n \n log.debug({ backendId: backend.id }, 'ACP runtime backend registered');\n } catch (error) {\n log.warn({ error }, 'Failed to initialize ACP runtime');\n }\n }\n\n async start(): Promise<void> {\n if (this.running) return;\n\n log.debug('Starting gateway service...');\n this.startTime = Date.now();\n this.running = true;\n\n registerClarifyBridge(this.clarifyBridge);\n\n this.channelManager.setOutboundHooks({\n runMessageSending: (to, content, channel) =>\n this.agentService.invokeOutboundMessageSending(to, content, channel),\n runMessageSent: (to, content, success, error, channel) =>\n this.agentService.invokeOutboundMessageSent(to, content, success, error, channel),\n });\n this.channelManager.enableOutboundPersistence(resolveAgentDir(this.config, getDefaultAgentId(this.config)));\n\n if (this.extensionLoader) {\n this.extensionLoader.setRuntimeContext({\n bus: this.bus,\n sessionManager: this.sessionManager,\n });\n }\n\n await this.loadExtensionsAndRegisterChannels();\n\n // Start channels (initialize first, then start)\n await this.channelManager.initialize();\n await this.channelManager.start();\n await this.channelManager.replayPendingOutboundMessages();\n\n // Initialize session manager\n await this.sessionManager.initialize();\n log.debug('Session manager initialized');\n\n this.cronService.setDeps({\n agentService: this.agentService,\n messageBus: this.bus,\n heartbeatService: this.heartbeatService,\n sessionStore: this.sessionManager.getStore(),\n getDefaultCronAgentId: () => getDefaultAgentId(this.config),\n });\n\n this.sessionManager.on('sessionUpdated', (data: { key: string; name?: string; tags?: string[] }) => {\n this.emit('session.updated', { key: data.key, name: data.name, tags: data.tags });\n });\n\n // Initialize ACP runtime backend\n await this.initializeAcpRuntime();\n\n // Start cron service\n if (this.config.cron?.enabled !== false) {\n await this.cronService.initialize();\n }\n\n this.heartbeatService.start(heartbeatRunnerConfigFromConfig(this.config));\n\n // Start agent service (runs in background)\n this.agentService.start().catch((err) => {\n log.error({ err }, 'Agent service error');\n });\n\n // Start outbound message processor\n this.startOutboundProcessor().catch((err) => {\n log.error({ err }, 'Outbound processor error');\n });\n\n // Setup config hot reload\n if (this.serviceConfig.enableHotReload !== false) {\n this.setupConfigReloader();\n }\n\n log.debug('Gateway service started');\n }\n\n async stop(): Promise<void> {\n if (!this.running) return;\n\n log.debug('Stopping gateway service...');\n\n // Stop config reloader\n if (this.configReloader) {\n await this.configReloader.stop();\n this.configReloader = null;\n }\n\n // Stop heartbeat service\n this.heartbeatService.stop();\n\n registerClarifyBridge(null);\n this.clarifyBridge.dispose();\n this.agentService.stop();\n\n // Unblock `consumeOutbound()` / `consumeInbound()` waiters before stopping channels (CLI agent does the same).\n this.running = false;\n this.bus.shutdown();\n\n await this.channelManager.stop();\n\n // Stop cron service\n await this.cronService.stop();\n\n log.debug('Gateway service stopped');\n }\n\n /**\n * Start processing outbound messages and send through channels\n */\n private async startOutboundProcessor(): Promise<void> {\n log.debug('Starting outbound message processor');\n while (this.running) {\n try {\n const msg = await this.bus.consumeOutbound();\n await this.channelManager.send(msg);\n } catch (error) {\n if (error instanceof MessageBusShutdownError) {\n break;\n }\n const em = error instanceof Error ? error.message : String(error);\n log.error(\n { err: error, errorMessage: em, phase: 'outbound_consume' },\n `Outbound pipeline failed (will retry in 1s): ${em}`,\n );\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n }\n\n /**\n * Setup config hot reload using ConfigHotReloader\n */\n private setupConfigReloader(): void {\n this.configReloader = new ConfigHotReloader(\n this.configPath,\n this.config,\n {\n onModelsReload: (newConfig) => this.handleModelsReload(newConfig),\n onAgentDefaultsReload: (newConfig) => this.handleAgentDefaultsReload(newConfig),\n onChannelsReload: (newConfig) => this.handleChannelsReload(newConfig),\n onCronReload: (newConfig) => this.handleCronReload(newConfig),\n onHeartbeatReload: (newConfig) => this.handleHeartbeatReload(newConfig),\n onToolsReload: (newConfig) => this.handleToolsReload(newConfig),\n onFullRestart: (newConfig) => {\n log.warn(\n { requiresProcessRestart: true, hint: 'Restart the gateway process (hot reload cannot apply this change).' },\n 'Config reload: full gateway restart required — see prior \"restartPaths\" info log',\n );\n this.config = newConfig;\n this.emit('config.reload', { section: 'full', requiresRestart: true });\n },\n },\n {\n debounceMs: 300,\n enabled: this.serviceConfig.enableHotReload !== false,\n }\n );\n this.configReloader.start();\n }\n\n /**\n * Handle models config hot reload\n */\n private handleModelsReload(newConfig: Config): void {\n log.debug('Reloading models config...');\n this.config = newConfig;\n getModelRegistry().refresh();\n this.emit('config.reload', { section: 'models' });\n log.debug('Models config reloaded');\n }\n\n /**\n * Handle agent defaults config hot reload\n */\n private handleAgentDefaultsReload(newConfig: Config): void {\n log.debug('Reloading agent defaults...');\n this.config = newConfig;\n this.agentService.applyAgentDefaultsFromConfig(newConfig);\n this.emit('config.reload', { section: 'agents' });\n log.debug('Agent defaults reloaded');\n }\n\n /**\n * Apply `latest.channels` to every registered channel plugin (Telegram, Weixin, extensions).\n * Single runtime path for: file watcher hot reload, API saves, and Weixin QR follow-up.\n */\n private async handleChannelsReload(newConfig: Config): Promise<void> {\n log.debug('Reloading channels config...');\n this.config = newConfig;\n await this.channelManager.updateConfig(newConfig);\n this.emit('config.reload', { section: 'channels' });\n this.emit('channels.status', { channels: this.getChannelsStatus() });\n log.debug('Channels config reloaded');\n }\n\n /** After persisting config to disk: align plugins + debounced reload baseline (watchers skip duplicate diffs). */\n private async syncChannelPluginsAfterPersist(): Promise<void> {\n await this.handleChannelsReload(this.config);\n this.configReloader?.syncCurrentConfig(this.config);\n }\n\n /**\n * Handle cron config hot reload\n */\n private handleCronReload(newConfig: Config): void {\n log.debug('Reloading cron config...');\n this.config = newConfig;\n this.cronService.updateConfig(newConfig);\n this.emit('config.reload', { section: 'cron' });\n log.debug('Cron config reloaded');\n }\n\n /**\n * Handle heartbeat config hot reload\n */\n private handleHeartbeatReload(newConfig: Config): void {\n log.debug('Reloading heartbeat config...');\n this.config = newConfig;\n this.heartbeatService.updateConfig(newConfig);\n this.emit('config.reload', { section: 'heartbeat' });\n log.debug('Heartbeat config reloaded');\n }\n\n /**\n * Apply `gateway.heartbeat` from current config after PATCH /api/config (and when hot reload is off).\n * File watcher uses `handleHeartbeatReload` with the same effect when paths match.\n */\n reloadHeartbeatFromCurrentConfig(): void {\n this.handleHeartbeatReload(this.config);\n }\n\n /**\n * Handle tools config hot reload\n */\n private handleToolsReload(newConfig: Config): void {\n log.debug('Reloading tools config...');\n this.config = newConfig;\n this.emit('config.reload', { section: 'tools' });\n log.debug('Tools config reloaded');\n }\n\n /**\n * Reload configuration from disk (manual trigger)\n */\n async reloadConfig(): Promise<{ reloaded: boolean; error?: string }> {\n if (!this.configReloader) {\n return { reloaded: false, error: 'Config reloader not initialized' };\n }\n const result = await this.configReloader.triggerReload();\n return { reloaded: result.success, error: result.error };\n }\n\n /**\n * After Weixin QR login: token files may change without a `channels.weixin` JSON diff, so run the same\n * channel apply as an API save, then force Weixin long-poll restart (see `reloadMonitorsWithConfig`).\n */\n async afterWeixinCredentialsPersisted(): Promise<void> {\n const next = loadConfig(this.configPath);\n this.config = next;\n this.agentService.applyAgentDefaultsFromConfig(next);\n this.configReloader?.syncCurrentConfig(next);\n await this.handleChannelsReload(next);\n const { weixinPlugin } = await import('../channels/weixin/index.js');\n await weixinPlugin.reloadMonitorsWithConfig(this.config, this.bus);\n log.info('Weixin monitors restarted after credential login');\n }\n\n /**\n * Save current config to disk\n */\n /**\n * Persist and replace `this.config` with the validated file contents so runtime matches disk\n * (PATCH merge objects can drift from Zod-normalized output).\n */\n private async writeConfigAndReloadFromDisk(configToWrite: Config): Promise<void> {\n await writeConfigToDisk(configToWrite, this.configPath);\n this.config = loadConfig(this.configPath);\n this.agentService.applyAgentDefaultsFromConfig(this.config);\n }\n\n async saveConfig(config: Config): Promise<{ saved: boolean; error?: string }> {\n try {\n await this.writeConfigAndReloadFromDisk(config);\n await this.syncChannelPluginsAfterPersist();\n return { saved: true };\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n log.error({ error }, 'Failed to save config');\n return { saved: false, error };\n }\n }\n\n /**\n * App store (phase 1): persist `extensions.enabled` / `extensions.disabled` for a bundled extension.\n * Extension modules are loaded at gateway startup; restart the gateway process to fully apply load/unload.\n */\n async setBundledExtensionActivationTarget(\n extensionId: string,\n wanted: boolean,\n ): Promise<{ ok: boolean; error?: string; requiresGatewayRestart: boolean }> {\n const loader = this.extensionLoader;\n if (!loader) {\n return { ok: false, error: 'Extension loader unavailable', requiresGatewayRestart: false };\n }\n const id = extensionId.trim();\n if (!id) {\n return { ok: false, error: 'Invalid extension id', requiresGatewayRestart: false };\n }\n const patch = computeBundledExtensionExtensionsPatch(loader, this.config, id, wanted);\n if (patch.ok === false) {\n return { ok: false, error: patch.error, requiresGatewayRestart: false };\n }\n const newConfig = { ...this.config, extensions: patch.extensions } as Config;\n const saved = await this.saveConfig(newConfig);\n if (!saved.saved) {\n return { ok: false, error: saved.error ?? 'Failed to save config', requiresGatewayRestart: false };\n }\n loader.setConfig(this.config as unknown as SurfaceConfig);\n return { ok: true, requiresGatewayRestart: true };\n }\n\n /**\n * Update configuration and persist to disk\n */\n async updateConfig(updates: Partial<Config>): Promise<{ updated: boolean; error?: string }> {\n try {\n log.debug('Updating configuration...');\n \n // Merge updates\n this.config = { ...this.config, ...updates };\n\n await this.writeConfigAndReloadFromDisk(this.config);\n await this.syncChannelPluginsAfterPersist();\n\n log.debug('Configuration updated successfully');\n return { updated: true };\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n log.error({ error }, 'Failed to update config');\n return { updated: false, error };\n }\n }\n\n /**\n * Run agent with a message and stream events\n */\n /**\n * @param runOptions.signal — When set (e.g. client disconnect), aborts in-flight generation and persists partial output.\n */\n async *runAgent(\n message: string,\n channel: string,\n chatId: string,\n attachments?: Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n }>,\n thinking?: string,\n runOptions?: { signal?: AbortSignal },\n ): AsyncGenerator<{ type: string; content?: string; status?: string; runId?: string }, { status: string; summary: string }, unknown> {\n const cappedAttachments =\n attachments && attachments.length > MAX_CHAT_ATTACHMENTS\n ? attachments.slice(0, MAX_CHAT_ATTACHMENTS)\n : attachments;\n if (attachments && cappedAttachments && attachments.length > cappedAttachments.length) {\n log.debug(\n { dropped: attachments.length - cappedAttachments.length, max: MAX_CHAT_ATTACHMENTS },\n 'Attachments capped for webchat',\n );\n }\n\n const runId = crypto.randomUUID();\n\n // For webchat, register the run in the relay before yielding the first event\n if (channel === 'webchat') {\n const parsedKey = parseSessionKey(chatId);\n const sessionKey = parsedKey ? chatId : buildSessionKey({\n agentId: getDefaultAgentId(this.config),\n source: 'webchat',\n accountId: 'default',\n peerKind: 'direct',\n peerId: chatId,\n });\n this.runRelay.ensureRun(runId, sessionKey);\n this.runAbortControllers.set(runId, new AbortController());\n }\n\n const statusEvent = { type: 'status', status: 'accepted', runId };\n if (channel === 'webchat') this.runRelay.publish(runId, statusEvent);\n yield statusEvent;\n\n try {\n // For 'webchat' channel (web UI), process through agent service\n if (channel === 'webchat') {\n // Determine session key: if chatId is already a valid session key, use it directly\n // Otherwise, build a new session key from the chatId\n const parsedKey = parseSessionKey(chatId);\n const sessionKey = parsedKey ? chatId : buildSessionKey({\n agentId: getDefaultAgentId(this.config),\n source: 'webchat',\n accountId: 'default',\n peerKind: 'direct',\n peerId: chatId,\n });\n\n const prepared = await this.agentService.prepareInboundAttachments(sessionKey, cappedAttachments);\n\n // Persist before streaming so a mid-turn refresh still sees text + attachment refs on disk.\n try {\n await this._saveUserMessage(sessionKey, message, prepared);\n } catch (err) {\n log.error({ err, sessionKey }, 'Failed to save user message');\n }\n\n const runAbort = this.runAbortControllers.get(runId);\n if (!runAbort) {\n throw new Error('run abort controller missing for webchat');\n }\n const mergedSignal = runOptions?.signal\n ? AbortSignal.any([runOptions.signal, runAbort.signal])\n : runAbort.signal;\n\n this.activeWebchatRunBySession.set(sessionKey, runId);\n try {\n this.emit('agent.stream', { sessionKey, event: statusEvent });\n const eventStream = this.agentService.processDirectStreaming(message, sessionKey, prepared, thinking, {\n signal: mergedSignal,\n });\n\n for await (const event of eventStream) {\n this.runRelay.publish(runId, event);\n this.emit('agent.stream', { sessionKey, event });\n yield event as { type: string; content?: string; status?: string; runId?: string };\n }\n\n this.runRelay.complete(runId);\n try {\n const metaAfter = await this.sessionManager.getSessionMetadata(sessionKey);\n if (metaAfter?.name) {\n this.emit('session.updated', { key: sessionKey, name: metaAfter.name });\n }\n } catch {\n /* ignore */\n }\n return {\n status: mergedSignal.aborted ? 'aborted' : 'ok',\n summary: mergedSignal.aborted ? 'Interrupted' : 'Message processed successfully',\n };\n } catch (error) {\n log.error({ error }, 'Agent processing failed');\n const errorEvent = { type: 'error', content: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` };\n this.runRelay.publish(runId, errorEvent);\n this.emit('agent.stream', { sessionKey, event: errorEvent });\n this.runRelay.complete(runId);\n yield errorEvent;\n return { status: 'error', summary: error instanceof Error ? error.message : 'Unknown error' };\n } finally {\n this.activeWebchatRunBySession.delete(sessionKey);\n this.runAbortControllers.delete(runId);\n }\n }\n\n // Send message through bus for other channels (telegram, etc.)\n const correlationMeta = inboundCorrelationMetadataFromAsyncLogContext();\n await this.bus.publishInbound({\n channel,\n sender_id: 'gateway',\n chat_id: chatId,\n content: message,\n ...(correlationMeta ? { metadata: correlationMeta } : {}),\n });\n\n // Wait for and collect response\n // This is simplified - in production we'd need proper session tracking\n yield { type: 'token', content: 'Processing...\\n' };\n\n // Simulate processing delay\n await new Promise((resolve) => setTimeout(resolve, 1000));\n\n yield { type: 'token', content: 'Done\\n' };\n\n return { status: 'ok', summary: 'Message processed' };\n } catch (error) {\n log.error({ error }, 'Agent run failed');\n throw error;\n }\n }\n\n /** Abort an in-flight webchat agent run (matches `runId` from SSE `status`). */\n abortAgentRun(runId: string): boolean {\n this.clarifyBridge.cancelForRun(runId);\n for (const [sk, id] of this.activeWebchatRunBySession) {\n if (id === runId) {\n this.activeWebchatRunBySession.delete(sk);\n }\n }\n const c = this.runAbortControllers.get(runId);\n if (!c) {\n return false;\n }\n c.abort();\n return true;\n }\n\n /**\n * Queue steering text for an active webchat run (`Agent.steer` / tool-boundary injection).\n * `chatId` is the same as `POST /api/agent` body (`sessionKey` or legacy peer id).\n */\n async steerWebchatAgent(\n chatId: string,\n message: string,\n ): Promise<{ ok: true } | { ok: false; code: 'BAD_REQUEST' | 'NO_ACTIVE_RUN' | 'STEER_FAILED' }> {\n const trimmed = message.trim();\n if (!trimmed) {\n return { ok: false, code: 'BAD_REQUEST' };\n }\n const parsedKey = parseSessionKey(chatId);\n const sessionKey = parsedKey\n ? chatId\n : buildSessionKey({\n agentId: getDefaultAgentId(this.config),\n source: 'webchat',\n accountId: 'default',\n peerKind: 'direct',\n peerId: chatId,\n });\n if (!this.activeWebchatRunBySession.has(sessionKey)) {\n return { ok: false, code: 'NO_ACTIVE_RUN' };\n }\n const steered = await this.agentService.steerWebchatSession(sessionKey, trimmed);\n if (!steered) {\n return { ok: false, code: 'STEER_FAILED' };\n }\n return { ok: true };\n }\n\n private async deliverTelegramClarify(ctx: {\n sessionKey: string;\n requestId: string;\n request: ClarifyBridgeRequest;\n }): Promise<void> {\n const parsed = parseSessionKey(ctx.sessionKey);\n if (!parsed || parsed.source !== 'telegram') {\n return;\n }\n\n let body = ctx.request.question;\n if (ctx.request.default) {\n body += `\\n\\nDefault if unsure: ${ctx.request.default}`;\n }\n\n const choices = ctx.request.choices;\n const buttonRows =\n choices && choices.length >= 2\n ? choices.map((c, i) => [\n {\n text: c.length > 64 ? `${c.slice(0, 61)}…` : c,\n callback_data: `clarify:${ctx.requestId}:${i}`,\n },\n ])\n : undefined;\n\n if (!buttonRows) {\n body += '\\n\\nReply with your answer in this chat.';\n }\n\n await this.channelManager.send({\n channel: 'telegram',\n chat_id: parsed.peerId,\n content: body,\n metadata: {\n accountId: parsed.accountId,\n ...(parsed.threadId ? { threadId: parsed.threadId } : {}),\n },\n buttons: buttonRows,\n });\n }\n\n /** Deliver a user's answer to a pending `clarify` tool call. */\n submitClarifyResponse(requestId: string, answer: string): boolean {\n return this.clarifyBridge.handleResponse(requestId, answer);\n }\n\n /**\n * Send message through a channel\n */\n async sendMessage(\n channel: string,\n chatId: string,\n content: string\n ): Promise<{ sent: boolean; messageId?: string }> {\n try {\n await this.channelManager.send({\n channel,\n chat_id: chatId,\n content,\n });\n const messageId = `msg_${Date.now()}`;\n this.emit('message.sent', { channel, chatId, messageId });\n return { sent: true, messageId };\n } catch (error) {\n log.error({ channel, chatId, error }, 'Failed to send message');\n throw error;\n }\n }\n\n /**\n * Get channel statuses\n */\n getChannelsStatus(): Array<{\n name: string;\n enabled: boolean;\n connected: boolean;\n }> {\n const runningChannels = new Set(this.channelManager.getRunningChannels());\n const channels = this.config.channels as Record<string, { enabled?: boolean } | undefined> | undefined;\n const builtinOrder = CHAT_CHANNEL_ORDER as readonly string[];\n\n const rows: Array<{ name: string; enabled: boolean; connected: boolean }> = CHAT_CHANNEL_ORDER.map(\n (name) => ({\n name,\n enabled: !!channels?.[name]?.enabled,\n connected: runningChannels.has(name),\n }),\n );\n\n const extReg = this.extensionLoader?.getRegistry();\n const extraIds = extReg?.channelPlugins.map((p) => p.id).filter((id) => !builtinOrder.includes(id)) ?? [];\n if (extraIds.length === 0) {\n return rows;\n }\n\n const seen = new Set(builtinOrder);\n for (const name of extraIds) {\n if (seen.has(name)) continue;\n seen.add(name);\n rows.push({\n name,\n enabled: channels?.[name]?.enabled !== false,\n connected: runningChannels.has(name),\n });\n }\n\n return rows;\n }\n\n /**\n * Request an immediate heartbeat run (coalesced like interval/cron wakes).\n */\n requestHeartbeatNow(opts?: { reason?: string }): void {\n this.heartbeatService.requestNow({ reason: opts?.reason ?? 'manual' });\n }\n\n /**\n * Get health status\n */\n getHealth(): {\n status: string;\n service: string;\n version: string;\n uptime: number;\n channels: { running: number; total: number };\n configPath: string;\n logs?: {\n dir: string;\n errors24h: number;\n stats: Record<string, number>;\n };\n } {\n const runningChannels = this.channelManager.getRunningChannels();\n const allChannels = this.channelManager.getAllChannels();\n const logStats = getLogStats();\n\n return {\n status: 'ok',\n service: 'xopc-gateway',\n version: PACKAGE_VERSION,\n uptime: Math.floor((Date.now() - this.startTime) / 1000),\n channels: {\n running: runningChannels.length,\n total: allChannels.length,\n },\n configPath: this.configPath,\n logs: {\n dir: getLogDir(),\n errors24h: logStats.errorsLast24h,\n stats: logStats.byLevel,\n },\n };\n }\n\n get isRunning(): boolean {\n return this.running;\n }\n\n /**\n * Get extension registry for external access (HTTP routes, gateway methods)\n */\n getExtensionRegistry() {\n return this.extensionLoader?.getRegistry();\n }\n\n /** Extension loader for discovery and frontend asset APIs (may be null if extensions failed to init). */\n getExtensionLoader(): ExtensionLoader | null {\n return this.extensionLoader;\n }\n\n /**\n * Get model registry for external access (HTTP routes)\n */\n getModelRegistry() {\n const { getModelRegistry } = require('../providers/index.js');\n return getModelRegistry();\n }\n\n /**\n * Invoke a gateway method registered by extensions\n */\n async invokeGatewayMethod(method: string, params: Record<string, unknown>): Promise<unknown> {\n const registry = this.getExtensionRegistry();\n if (!registry) {\n throw new Error('Extension registry not available');\n }\n\n const handler = registry.getGatewayMethod(method);\n if (!handler) {\n throw new Error(`Gateway method not found: ${method}`);\n }\n\n return await handler(params);\n }\n\n get currentConfig(): Config {\n return this.config;\n }\n\n get cronServiceInstance(): CronService {\n return this.cronService;\n }\n\n getSkillsApi(): { catalog: SkillCatalogEntry[]; managed: ManagedSkillListItem[] } {\n return {\n catalog: this.agentService.getSkillCatalog(),\n managed: listManagedSkillDirs(),\n };\n }\n\n getSkillMarkdownSource(skillName: string): { name: string; markdown: string } | null {\n return this.agentService.getSkillMarkdownSource(skillName);\n }\n\n deleteManagedSkill(skillId: string): void {\n removeSkillsLockEntry(skillId);\n deleteManagedSkillDir(skillId);\n this.agentService.refreshSkillsAfterDiskChange();\n }\n\n installManagedSkillZip(\n buffer: Buffer,\n opts: { skillId?: string; overwrite?: boolean },\n ): { skillId: string; path: string } {\n const result = installSkillFromZip(buffer, opts);\n removeSkillsLockEntry(result.skillId);\n this.agentService.refreshSkillsAfterDiskChange();\n return result;\n }\n\n async fetchSkillsMarketplaceCatalog(params: SkillsStoreListParams): Promise<SkillsStoreListResponse> {\n const base = resolveSkillsStoreBaseUrl(this.config);\n return listSkillPackages(base, params);\n }\n\n async fetchSkillsMarketplacePackageDetail(packageName: string): Promise<MarketplacePackageDetail> {\n const base = resolveSkillsStoreBaseUrl(this.config);\n return fetchMarketplacePackageDetail(base, packageName);\n }\n\n async installSkillFromMarketplace(opts: {\n name: string;\n version?: string;\n overwrite?: boolean;\n }): Promise<{ skillId: string; path: string }> {\n const base = resolveSkillsStoreBaseUrl(this.config);\n const { downloadUrl } = await resolveSkillZipDownloadUrl(base, opts.name, opts.version);\n const buf = await downloadSkillZipBuffer(base, downloadUrl);\n const skillId = skillIdForMarketplaceInstall(opts.name);\n return this.installManagedSkillZip(buf, { skillId, overwrite: opts.overwrite ?? false });\n }\n\n reloadSkillsFromDisk(): void {\n this.agentService.refreshSkillsAfterDiskChange();\n }\n\n patchSkillEnabled(skillName: string, enabled: boolean): void {\n createSkillConfigManager(resolveStateDir()).setSkillEnabled(skillName, enabled);\n this.agentService.refreshSkillsAfterSkillConfigChange();\n }\n\n get sessionManagerInstance(): SessionManager {\n return this.sessionManager;\n }\n\n async getSessionAgentConfig(sessionKey: string) {\n return this.agentService.getSessionAgentConfig(sessionKey);\n }\n\n /** Resolved markdown workspace for a session (after hydration / mkdir). Used by workspace file API when `sessionKey` is passed. */\n async getEffectiveWorkspacePathForSession(sessionKey: string): Promise<string> {\n return this.agentService.getEffectiveWorkspacePathForSession(sessionKey);\n }\n\n async patchSessionAgentConfig(sessionKey: string, body: {\n thinkingLevel?: string;\n model?: string | null;\n reasoningLevel?: string;\n workingDirectory?: string;\n }) {\n return this.agentService.patchSessionAgentConfig(sessionKey, body);\n }\n\n /**\n * Process a message directly through the agent (for CLI mode)\n */\n async processDirect(content: string, sessionKey = 'cli:direct'): Promise<string> {\n return this.agentService.processDirect(content, sessionKey);\n }\n\n // ========== SSE Event System ==========\n\n /**\n * Subscribe to server-pushed events.\n * Returns a cleanup function to unsubscribe.\n */\n subscribe(sessionId: string, listener: EventListener): () => void {\n this.subscribers.set(sessionId, listener);\n if (!this.eventBuffers.has(sessionId)) {\n this.eventBuffers.set(sessionId, []);\n }\n log.debug({ sessionId }, 'Event subscriber added');\n\n return () => {\n this.subscribers.delete(sessionId);\n // Keep buffer for a while in case they reconnect\n setTimeout(() => {\n if (!this.subscribers.has(sessionId)) {\n this.eventBuffers.delete(sessionId);\n }\n }, 5 * 60_000); // 5 min grace\n log.debug({ sessionId }, 'Event subscriber removed');\n };\n }\n\n /**\n * Emit an event to all subscribers.\n */\n emit(type: string, payload: unknown): void {\n const id = String(++this.eventCounter);\n const event: ServiceEvent = { id, type, payload };\n\n for (const [sessionId, listener] of this.subscribers) {\n // Buffer the event\n const buf = this.eventBuffers.get(sessionId) || [];\n buf.push(event);\n if (buf.length > EVENT_BUFFER_SIZE) buf.shift();\n this.eventBuffers.set(sessionId, buf);\n\n // Deliver\n try {\n listener(event);\n } catch (err) {\n log.warn({ sessionId, err }, 'Failed to deliver event to subscriber');\n }\n }\n }\n\n /**\n * Get events since a given event id (for Last-Event-ID reconnection).\n */\n getEventsSince(sessionId: string, lastEventId: string): ServiceEvent[] {\n const buf = this.eventBuffers.get(sessionId);\n if (!buf) return [];\n\n const idx = buf.findIndex((e) => e.id === lastEventId);\n if (idx === -1) return buf; // can't find cursor — send everything in buffer\n return buf.slice(idx + 1);\n }\n\n /**\n * Save user message to session for webchat (fire-and-forget).\n * Called at the start of runAgent to ensure message survives page refresh.\n */\n private async _saveUserMessage(\n sessionKey: string,\n message: string,\n attachments?: Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>,\n ): Promise<void> {\n // Load existing messages\n const existingMessages = await this.sessionManager.loadMessages(sessionKey);\n\n // Build user message (marker stripped in SessionStore.convertMessages for API clients)\n const userMessage = {\n role: 'user' as const,\n content: [{ type: 'text' as const, text: message }],\n attachments: attachments?.map((a) => ({\n type: a.type,\n mimeType: a.mimeType,\n name: a.name,\n size: a.size,\n workspaceRelativePath: a.workspaceRelativePath,\n // Note: we don't store data (base64) to keep session file small\n })),\n timestamp: Date.now(),\n /** Dropped before `agent.prompt` so we don't duplicate the turn; not exposed via GET session */\n webchatEarlySave: true as const,\n };\n\n // Append and save\n const updatedMessages = [...existingMessages, userMessage];\n await this.sessionManager.saveMessages(sessionKey, updatedMessages);\n log.debug({ sessionKey, messageCount: updatedMessages.length }, 'User message saved');\n }\n\n // ========== Session Management API ==========\n\n /**\n * List sessions with query filters\n */\n async listSessions(query?: SessionListQuery) {\n return this.sessionManager.listSessions(query);\n }\n\n /**\n * List all subagent sessions.\n * Subagent sessions have keys starting with 'subagent:'.\n */\n async listSubagents(query?: SessionListQuery) {\n return this.sessionManager.listSubagents(query);\n }\n\n /**\n * Get a single session by key\n */\n async getSession(key: string) {\n return this.sessionManager.getSession(key);\n }\n\n /**\n * Delete a session\n */\n async deleteSession(key: string): Promise<{ deleted: boolean }> {\n const result = await this.sessionManager.deleteSession(key);\n return { deleted: result };\n }\n\n /**\n * Delete multiple sessions\n */\n async deleteSessions(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n return this.sessionManager.deleteSessions(keys);\n }\n\n /**\n * Rename a session\n */\n async renameSession(key: string, name: string): Promise<{ renamed: boolean }> {\n await this.sessionManager.renameSession(key, name);\n return { renamed: true };\n }\n\n /**\n * Tag a session\n */\n async tagSession(key: string, tags: string[]): Promise<{ tagged: boolean }> {\n await this.sessionManager.tagSession(key, tags);\n return { tagged: true };\n }\n\n /**\n * Remove tags from a session\n */\n async untagSession(key: string, tags: string[]): Promise<{ untagged: boolean }> {\n await this.sessionManager.untagSession(key, tags);\n return { untagged: true };\n }\n\n /**\n * Archive a session\n */\n async archiveSession(key: string): Promise<{ archived: boolean }> {\n await this.sessionManager.archiveSession(key);\n return { archived: true };\n }\n\n /**\n * Unarchive a session\n */\n async unarchiveSession(key: string): Promise<{ unarchived: boolean }> {\n await this.sessionManager.unarchiveSession(key);\n return { unarchived: true };\n }\n\n /**\n * Pin a session\n */\n async pinSession(key: string): Promise<{ pinned: boolean }> {\n await this.sessionManager.pinSession(key);\n return { pinned: true };\n }\n\n /**\n * Unpin a session\n */\n async unpinSession(key: string): Promise<{ unpinned: boolean }> {\n await this.sessionManager.unpinSession(key);\n return { unpinned: true };\n }\n\n /**\n * Search sessions\n */\n async searchSessions(query: string) {\n return this.sessionManager.searchSessions(query);\n }\n\n /**\n * Search within a session\n */\n async searchInSession(key: string, keyword: string) {\n return this.sessionManager.searchInSession(key, keyword);\n }\n\n /**\n * Export a session\n */\n async exportSession(key: string, format: ExportFormat): Promise<{ content: string }> {\n const content = await this.sessionManager.exportSession(key, format);\n return { content };\n }\n\n /**\n * Get session statistics\n */\n async getSessionStats() {\n return this.sessionManager.getStats();\n }\n\n /**\n * Get unique chat IDs from sessions, grouped by channel\n * Returns a list of channel/chatId pairs for cron job configuration.\n * `chatId` is the session-store routing suffix (unique per bot account + peer).\n * When `routing` exists, `peerId` is the platform id (e.g. Telegram numeric chat id).\n */\n async getSessionChatIds(channel?: string): Promise<\n Array<{\n channel: string;\n chatId: string;\n lastActive: string;\n accountId?: string;\n peerKind?: string;\n peerId?: string;\n }>\n > {\n const result = await this.sessionManager.listSessions({\n limit: 1000,\n sortBy: 'lastAccessedAt',\n sortOrder: 'desc',\n ...(channel ? { channel } : {}),\n });\n\n // Group by channel:chatId to get unique pairs\n const seen = new Set<string>();\n const chatIds: Array<{\n channel: string;\n chatId: string;\n lastActive: string;\n accountId?: string;\n peerKind?: string;\n peerId?: string;\n }> = [];\n\n for (const session of result.items) {\n const key = `${session.sourceChannel}:${session.sourceChatId}`;\n if (!seen.has(key) && session.sourceChannel && session.sourceChatId) {\n seen.add(key);\n const r = session.routing;\n chatIds.push({\n channel: session.sourceChannel,\n chatId: session.sourceChatId,\n lastActive: session.lastAccessedAt,\n ...(r\n ? {\n accountId: r.accountId,\n peerKind: r.peerKind,\n peerId: r.peerId,\n }\n : {}),\n });\n }\n }\n\n return chatIds;\n }\n\n /**\n * Validate authentication token from request headers.\n * Returns true if auth is disabled (mode: 'none') or token is valid.\n */\n validateAuth(headers?: Record<string, string | string[] | undefined>): boolean {\n const token = extractToken(headers);\n return validateToken(this.auth, token);\n }\n\n /**\n * Get current auth mode.\n */\n getAuthMode(): 'none' | 'token' {\n return this.auth.mode;\n }\n\n /**\n * Get current auth token (for CLI server integration).\n * Returns undefined if mode is 'none'.\n */\n getAuthToken(): string | undefined {\n return this.auth.mode === 'token' ? this.auth.token : undefined;\n }\n\n /**\n * Refresh (regenerate) the gateway auth token.\n * Returns the new token.\n */\n async refreshAuthToken(): Promise<string> {\n if (this.auth.mode !== 'token') {\n throw new Error('Cannot refresh token: auth mode is not token');\n }\n\n // Generate new token\n const newToken = crypto.randomBytes(24).toString('hex');\n \n // Update in-memory auth\n this.auth.token = newToken;\n \n // Update config\n this.config = {\n ...this.config,\n gateway: {\n ...this.config.gateway,\n auth: {\n ...this.config.gateway?.auth,\n mode: 'token',\n token: newToken,\n },\n },\n };\n \n await this.writeConfigAndReloadFromDisk(this.config);\n\n log.info({ tokenPreview: `${newToken.slice(0, 8)}...` }, 'Gateway token refreshed');\n \n return newToken;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAQuD;gBAUE;aAM7B;YAOA;sBA4B4B;kBACqB;oBACb;AALhE,MAAM,MAAM,aAAa,iBAAiB;AAkB1C,MAAM,oBAAoB;AAO1B,IAAa,iBAAb,MAA4B;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA,kBAAkD;CAClD;CACA;CACA,UAAkB;CAClB,iBAAmD;CACnD,YAAoB,KAAK,KAAK;CAC9B;CAGA;CAGA,eAAuB;CACvB,8BAAsB,IAAI,KAA4B;CACtD,+BAAuB,IAAI,KAA6B;CAGxD,WAA2B,IAAI,eAAe;;CAG9C,sCAA8B,IAAI,KAA8B;CAEhE,gBAAiC,IAAI,eAAe;;CAGpD,4CAAoC,IAAI,KAAqB;CAE7D,YAAY,gBAA8C,EAAE,EAAE;AAA1C,OAAA,gBAAA;AAClB,OAAK,MAAM,IAAI,YAAY;AAC3B,OAAK,aAAa,cAAc,cAAc,mBAAmB;AACjE,OAAK,SAAS,WAAW,KAAK,WAAW;AAGzC,OAAK,OAAO,mBAAmB,EAC7B,YAAY,KAAK,OAAO,SAAS,MAClC,CAAC;AAGF,8BAA4B,KAAK,KAAK;AAGtC,MAAI,KAAK,KAAK,SAAS,SAAS;GAC9B,MAAM,eAAe,KAAK,KAAK,QAAQ,GAAG,KAAK,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO;AAC7E,OAAI,KAAK;IAAE,MAAM,KAAK,KAAK;IAAM,OAAO;IAAc,EAAE,4BAA4B;QAEpF,KAAI,KAAK,EAAE,MAAM,KAAK,KAAK,MAAM,EAAE,0BAA0B;AAI/D,OAAK,iBAAiB,IAAI,eAAe,KAAK,QAAQ,KAAK,IAAI;AAG/D,OAAK,gBAAgB,iBAAiB,KAAK,OAAO,IAAI;AACtD,OAAK,2BAA2B;EAGhC,MAAM,WAAW,kBAAkB;AACnC,MAAI,MAAM;GACR,YAAY,SAAS,QAAQ,CAAC;GAC9B,OAAO,SAAS,UAAU,IAAI;GAC/B,EAAE,4BAA4B;EAG/B,MAAM,cAAc,KAAK,OAAO,QAAQ,UAAU;EAClD,MAAM,UAAqC,EAAE;AAC7C,OAAK,eAAe,IAAI,aAAa,KAAK,KAAK;GAC7C,WAAW,KAAK;GAChB,OAAO,OAAO,gBAAgB,WAAW,cAAc,aAAa;GACpE,QAAQ,KAAK;GACb,mBAAmB,KAAK,iBAAiB,aAAa;GACtD,sBAAsB,QAAQ;GAC9B,gBAAgB,EACd,uBAAuB,YAAY,YAAY;IAC7C,MAAM,QAAQ,KAAK,0BAA0B,IAAI,WAAW;IAC5D,MAAM,aAAa,SACd,MAAkB;AACjB,UAAK,aAAa,uBAAuB,YAAY,EAAE;QAEzD,KAAA;IAEJ,MAAM,UADS,gBAAgB,WAAW,EAEhC,WAAW,aACf,OAAO,QAAkF;AACvF,WAAM,KAAK,uBAAuB,IAAI;QAExC,KAAA;AACN,QAAI,CAAC,SAAS,CAAC,QACb,QAAO,QAAQ,uBACb,IAAI,MAAM,4EAA4E,CACvF;AAEH,WAAO,KAAK,cAAc,aAAa;KACrC;KACA;KACA,OAAO,KAAK;KACZ;KACA;KACA;KACD,CAAC;MAEL;GACF,CAAC;AAIF,OAAK,aAAa,kBAAkB,KAAK,eAAe;AACxD,OAAK,eAAe,qBAAqB;GACvC,qBAAqB,OAAO,KAAK,aAAa,mBAAmB,GAAG;GACpE,wBAAwB,IAAI,OAAO,KAAK,aAAa,sBAAsB,IAAI,GAAG;GACnF,CAAC;AAGF,OAAK,cAAc,IAAI,YAAY;GACjC,UAAU,qBAAqB;GAC/B,cAAc,KAAK;GACnB,YAAY,KAAK;GAClB,CAAC;AACF,UAAQ,UAAU,KAAK;AAGvB,OAAK,iBAAiB,IAAI,eAAe,EACvC,QAAQ,KAAK,QACd,CAAC;AAEF,OAAK,mBAAmB,IAAI,iBAAiB;GAC3C,cAAc,KAAK;GACnB,YAAY,KAAK;GACjB,aAAa,KAAK;GAClB,cAAc,KAAK,eAAe,UAAU;GAC5C,iBAAiB,KAAK;GACvB,CAAC;AAEF,OAAK,YAAY,QAAQ;GACvB,cAAc,KAAK;GACnB,YAAY,KAAK;GACjB,kBAAkB,KAAK;GACvB,6BAA6B,kBAAkB,KAAK,OAAO;GAC5D,CAAC;;;;;CAMJ,4BAA0C;AACxC,MAAI;GACF,MAAM,MAAM,kBAAkB,KAAK,OAAO;AAC1C,QAAK,kBAAkB,IAAI,gBAAgB;IACzC,cAAc,KAAK;IACnB,eAAe,8BAA8B,KAAK,QAAQ,IAAI;IAC/D,CAAC;AACF,QAAK,gBAAgB,UAAU,KAAK,OAAsD;WACnF,OAAO;AACd,OAAI,KAAK,EAAE,OAAO,EAAE,wCAAwC;;;;;;CAOhE,MAAc,oCAAmD;AAC/D,MAAI,CAAC,KAAK,gBACR;AAEF,MAAI;AACF,SAAM,KAAK,gBAAgB,sBAAsB;GACjD,MAAM,MAAM,KAAK,gBAAgB,aAAa;AAC9C,QAAK,MAAM,UAAU,IAAI,eACvB,MAAK,eAAe,eAAe,OAAO;AAE5C,OAAI,MACF;IACE,kBAAkB,IAAI,WAAW;IACjC,gBAAgB,IAAI,eAAe;IACpC,EACD,mDACD;WACM,KAAK;AACZ,OAAI,KAAK,EAAE,KAAK,EAAE,4BAA4B;;;;;;CAOlD,MAAc,uBAAsC;AAClD,MAAI;AAEF,OAAI,CAAC,KAAK,OAAO,KAAK,SAAS;AAC7B,QAAI,MAAM,iCAAiC;AAC3C;;GAIF,MAAM,UAAU,6BAA6B,KAAK,IAAI;AACtD,6BAA0B,QAAQ;AAElC,OAAI,MAAM,EAAE,WAAW,QAAQ,IAAI,EAAE,iCAAiC;WAC/D,OAAO;AACd,OAAI,KAAK,EAAE,OAAO,EAAE,mCAAmC;;;CAI3D,MAAM,QAAuB;AAC3B,MAAI,KAAK,QAAS;AAElB,MAAI,MAAM,8BAA8B;AACxC,OAAK,YAAY,KAAK,KAAK;AAC3B,OAAK,UAAU;AAEf,wBAAsB,KAAK,cAAc;AAEzC,OAAK,eAAe,iBAAiB;GACnC,oBAAoB,IAAI,SAAS,YAC/B,KAAK,aAAa,6BAA6B,IAAI,SAAS,QAAQ;GACtE,iBAAiB,IAAI,SAAS,SAAS,OAAO,YAC5C,KAAK,aAAa,0BAA0B,IAAI,SAAS,SAAS,OAAO,QAAQ;GACpF,CAAC;AACF,OAAK,eAAe,0BAA0B,gBAAgB,KAAK,QAAQ,kBAAkB,KAAK,OAAO,CAAC,CAAC;AAE3G,MAAI,KAAK,gBACP,MAAK,gBAAgB,kBAAkB;GACrC,KAAK,KAAK;GACV,gBAAgB,KAAK;GACtB,CAAC;AAGJ,QAAM,KAAK,mCAAmC;AAG9C,QAAM,KAAK,eAAe,YAAY;AACtC,QAAM,KAAK,eAAe,OAAO;AACjC,QAAM,KAAK,eAAe,+BAA+B;AAGzD,QAAM,KAAK,eAAe,YAAY;AACtC,MAAI,MAAM,8BAA8B;AAExC,OAAK,YAAY,QAAQ;GACvB,cAAc,KAAK;GACnB,YAAY,KAAK;GACjB,kBAAkB,KAAK;GACvB,cAAc,KAAK,eAAe,UAAU;GAC5C,6BAA6B,kBAAkB,KAAK,OAAO;GAC5D,CAAC;AAEF,OAAK,eAAe,GAAG,mBAAmB,SAA0D;AAClG,QAAK,KAAK,mBAAmB;IAAE,KAAK,KAAK;IAAK,MAAM,KAAK;IAAM,MAAM,KAAK;IAAM,CAAC;IACjF;AAGF,QAAM,KAAK,sBAAsB;AAGjC,MAAI,KAAK,OAAO,MAAM,YAAY,MAChC,OAAM,KAAK,YAAY,YAAY;AAGrC,OAAK,iBAAiB,MAAM,gCAAgC,KAAK,OAAO,CAAC;AAGzE,OAAK,aAAa,OAAO,CAAC,OAAO,QAAQ;AACvC,OAAI,MAAM,EAAE,KAAK,EAAE,sBAAsB;IACzC;AAGF,OAAK,wBAAwB,CAAC,OAAO,QAAQ;AAC3C,OAAI,MAAM,EAAE,KAAK,EAAE,2BAA2B;IAC9C;AAGF,MAAI,KAAK,cAAc,oBAAoB,MACzC,MAAK,qBAAqB;AAG5B,MAAI,MAAM,0BAA0B;;CAGtC,MAAM,OAAsB;AAC1B,MAAI,CAAC,KAAK,QAAS;AAEnB,MAAI,MAAM,8BAA8B;AAGxC,MAAI,KAAK,gBAAgB;AACvB,SAAM,KAAK,eAAe,MAAM;AAChC,QAAK,iBAAiB;;AAIxB,OAAK,iBAAiB,MAAM;AAE5B,wBAAsB,KAAK;AAC3B,OAAK,cAAc,SAAS;AAC5B,OAAK,aAAa,MAAM;AAGxB,OAAK,UAAU;AACf,OAAK,IAAI,UAAU;AAEnB,QAAM,KAAK,eAAe,MAAM;AAGhC,QAAM,KAAK,YAAY,MAAM;AAE7B,MAAI,MAAM,0BAA0B;;;;;CAMtC,MAAc,yBAAwC;AACpD,MAAI,MAAM,sCAAsC;AAChD,SAAO,KAAK,QACV,KAAI;GACF,MAAM,MAAM,MAAM,KAAK,IAAI,iBAAiB;AAC5C,SAAM,KAAK,eAAe,KAAK,IAAI;WAC5B,OAAO;AACd,OAAI,iBAAiB,wBACnB;GAEF,MAAM,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACjE,OAAI,MACF;IAAE,KAAK;IAAO,cAAc;IAAI,OAAO;IAAoB,EAC3D,gDAAgD,KACjD;AACD,SAAM,IAAI,SAAS,YAAY,WAAW,SAAS,IAAK,CAAC;;;;;;CAQ/D,sBAAoC;AAClC,OAAK,iBAAiB,IAAI,kBACxB,KAAK,YACL,KAAK,QACL;GACE,iBAAiB,cAAc,KAAK,mBAAmB,UAAU;GACjE,wBAAwB,cAAc,KAAK,0BAA0B,UAAU;GAC/E,mBAAmB,cAAc,KAAK,qBAAqB,UAAU;GACrE,eAAe,cAAc,KAAK,iBAAiB,UAAU;GAC7D,oBAAoB,cAAc,KAAK,sBAAsB,UAAU;GACvE,gBAAgB,cAAc,KAAK,kBAAkB,UAAU;GAC/D,gBAAgB,cAAc;AAC5B,QAAI,KACF;KAAE,wBAAwB;KAAM,MAAM;KAAsE,EAC5G,qFACD;AACD,SAAK,SAAS;AACd,SAAK,KAAK,iBAAiB;KAAE,SAAS;KAAQ,iBAAiB;KAAM,CAAC;;GAEzE,EACD;GACE,YAAY;GACZ,SAAS,KAAK,cAAc,oBAAoB;GACjD,CACF;AACD,OAAK,eAAe,OAAO;;;;;CAM7B,mBAA2B,WAAyB;AAClD,MAAI,MAAM,6BAA6B;AACvC,OAAK,SAAS;AACd,oBAAkB,CAAC,SAAS;AAC5B,OAAK,KAAK,iBAAiB,EAAE,SAAS,UAAU,CAAC;AACjD,MAAI,MAAM,yBAAyB;;;;;CAMrC,0BAAkC,WAAyB;AACzD,MAAI,MAAM,8BAA8B;AACxC,OAAK,SAAS;AACd,OAAK,aAAa,6BAA6B,UAAU;AACzD,OAAK,KAAK,iBAAiB,EAAE,SAAS,UAAU,CAAC;AACjD,MAAI,MAAM,0BAA0B;;;;;;CAOtC,MAAc,qBAAqB,WAAkC;AACnE,MAAI,MAAM,+BAA+B;AACzC,OAAK,SAAS;AACd,QAAM,KAAK,eAAe,aAAa,UAAU;AACjD,OAAK,KAAK,iBAAiB,EAAE,SAAS,YAAY,CAAC;AACnD,OAAK,KAAK,mBAAmB,EAAE,UAAU,KAAK,mBAAmB,EAAE,CAAC;AACpE,MAAI,MAAM,2BAA2B;;;CAIvC,MAAc,iCAAgD;AAC5D,QAAM,KAAK,qBAAqB,KAAK,OAAO;AAC5C,OAAK,gBAAgB,kBAAkB,KAAK,OAAO;;;;;CAMrD,iBAAyB,WAAyB;AAChD,MAAI,MAAM,2BAA2B;AACrC,OAAK,SAAS;AACd,OAAK,YAAY,aAAa,UAAU;AACxC,OAAK,KAAK,iBAAiB,EAAE,SAAS,QAAQ,CAAC;AAC/C,MAAI,MAAM,uBAAuB;;;;;CAMnC,sBAA8B,WAAyB;AACrD,MAAI,MAAM,gCAAgC;AAC1C,OAAK,SAAS;AACd,OAAK,iBAAiB,aAAa,UAAU;AAC7C,OAAK,KAAK,iBAAiB,EAAE,SAAS,aAAa,CAAC;AACpD,MAAI,MAAM,4BAA4B;;;;;;CAOxC,mCAAyC;AACvC,OAAK,sBAAsB,KAAK,OAAO;;;;;CAMzC,kBAA0B,WAAyB;AACjD,MAAI,MAAM,4BAA4B;AACtC,OAAK,SAAS;AACd,OAAK,KAAK,iBAAiB,EAAE,SAAS,SAAS,CAAC;AAChD,MAAI,MAAM,wBAAwB;;;;;CAMpC,MAAM,eAA+D;AACnE,MAAI,CAAC,KAAK,eACR,QAAO;GAAE,UAAU;GAAO,OAAO;GAAmC;EAEtE,MAAM,SAAS,MAAM,KAAK,eAAe,eAAe;AACxD,SAAO;GAAE,UAAU,OAAO;GAAS,OAAO,OAAO;GAAO;;;;;;CAO1D,MAAM,kCAAiD;EACrD,MAAM,OAAO,WAAW,KAAK,WAAW;AACxC,OAAK,SAAS;AACd,OAAK,aAAa,6BAA6B,KAAK;AACpD,OAAK,gBAAgB,kBAAkB,KAAK;AAC5C,QAAM,KAAK,qBAAqB,KAAK;EACrC,MAAM,EAAE,iBAAiB,MAAM,OAAO;AACtC,QAAM,aAAa,yBAAyB,KAAK,QAAQ,KAAK,IAAI;AAClE,MAAI,KAAK,mDAAmD;;;;;;;;;CAU9D,MAAc,6BAA6B,eAAsC;AAC/E,QAAMA,WAAkB,eAAe,KAAK,WAAW;AACvD,OAAK,SAAS,WAAW,KAAK,WAAW;AACzC,OAAK,aAAa,6BAA6B,KAAK,OAAO;;CAG7D,MAAM,WAAW,QAA6D;AAC5E,MAAI;AACF,SAAM,KAAK,6BAA6B,OAAO;AAC/C,SAAM,KAAK,gCAAgC;AAC3C,UAAO,EAAE,OAAO,MAAM;WACf,KAAK;GACZ,MAAM,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC9D,OAAI,MAAM,EAAE,OAAO,EAAE,wBAAwB;AAC7C,UAAO;IAAE,OAAO;IAAO;IAAO;;;;;;;CAQlC,MAAM,oCACJ,aACA,QAC2E;EAC3E,MAAM,SAAS,KAAK;AACpB,MAAI,CAAC,OACH,QAAO;GAAE,IAAI;GAAO,OAAO;GAAgC,wBAAwB;GAAO;EAE5F,MAAM,KAAK,YAAY,MAAM;AAC7B,MAAI,CAAC,GACH,QAAO;GAAE,IAAI;GAAO,OAAO;GAAwB,wBAAwB;GAAO;EAEpF,MAAM,QAAQ,uCAAuC,QAAQ,KAAK,QAAQ,IAAI,OAAO;AACrF,MAAI,MAAM,OAAO,MACf,QAAO;GAAE,IAAI;GAAO,OAAO,MAAM;GAAO,wBAAwB;GAAO;EAEzE,MAAM,YAAY;GAAE,GAAG,KAAK;GAAQ,YAAY,MAAM;GAAY;EAClE,MAAM,QAAQ,MAAM,KAAK,WAAW,UAAU;AAC9C,MAAI,CAAC,MAAM,MACT,QAAO;GAAE,IAAI;GAAO,OAAO,MAAM,SAAS;GAAyB,wBAAwB;GAAO;AAEpG,SAAO,UAAU,KAAK,OAAmC;AACzD,SAAO;GAAE,IAAI;GAAM,wBAAwB;GAAM;;;;;CAMnD,MAAM,aAAa,SAAyE;AAC1F,MAAI;AACF,OAAI,MAAM,4BAA4B;AAGtC,QAAK,SAAS;IAAE,GAAG,KAAK;IAAQ,GAAG;IAAS;AAE5C,SAAM,KAAK,6BAA6B,KAAK,OAAO;AACpD,SAAM,KAAK,gCAAgC;AAE3C,OAAI,MAAM,qCAAqC;AAC/C,UAAO,EAAE,SAAS,MAAM;WACjB,KAAK;GACZ,MAAM,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC9D,OAAI,MAAM,EAAE,OAAO,EAAE,0BAA0B;AAC/C,UAAO;IAAE,SAAS;IAAO;IAAO;;;;;;;;;CAUpC,OAAO,SACL,SACA,SACA,QACA,aAOA,UACA,YACmI;EACnI,MAAM,oBACJ,eAAe,YAAY,SAAA,KACvB,YAAY,MAAM,GAAA,GAAwB,GAC1C;AACN,MAAI,eAAe,qBAAqB,YAAY,SAAS,kBAAkB,OAC7E,KAAI,MACF;GAAE,SAAS,YAAY,SAAS,kBAAkB;GAAQ,KAAA;GAA2B,EACrF,iCACD;EAGH,MAAM,QAAQ,OAAO,YAAY;AAGjC,MAAI,YAAY,WAAW;GAEzB,MAAM,aADY,gBAAgB,OAAO,GACV,SAAS,gBAAgB;IACtD,SAAS,kBAAkB,KAAK,OAAO;IACvC,QAAQ;IACR,WAAW;IACX,UAAU;IACV,QAAQ;IACT,CAAC;AACF,QAAK,SAAS,UAAU,OAAO,WAAW;AAC1C,QAAK,oBAAoB,IAAI,OAAO,IAAI,iBAAiB,CAAC;;EAG5D,MAAM,cAAc;GAAE,MAAM;GAAU,QAAQ;GAAY;GAAO;AACjE,MAAI,YAAY,UAAW,MAAK,SAAS,QAAQ,OAAO,YAAY;AACpE,QAAM;AAEN,MAAI;AAEF,OAAI,YAAY,WAAW;IAIzB,MAAM,aADY,gBAAgB,OAAO,GACV,SAAS,gBAAgB;KACtD,SAAS,kBAAkB,KAAK,OAAO;KACvC,QAAQ;KACR,WAAW;KACX,UAAU;KACV,QAAQ;KACT,CAAC;IAEF,MAAM,WAAW,MAAM,KAAK,aAAa,0BAA0B,YAAY,kBAAkB;AAGjG,QAAI;AACF,WAAM,KAAK,iBAAiB,YAAY,SAAS,SAAS;aACnD,KAAK;AACZ,SAAI,MAAM;MAAE;MAAK;MAAY,EAAE,8BAA8B;;IAG/D,MAAM,WAAW,KAAK,oBAAoB,IAAI,MAAM;AACpD,QAAI,CAAC,SACH,OAAM,IAAI,MAAM,2CAA2C;IAE7D,MAAM,eAAe,YAAY,SAC7B,YAAY,IAAI,CAAC,WAAW,QAAQ,SAAS,OAAO,CAAC,GACrD,SAAS;AAEb,SAAK,0BAA0B,IAAI,YAAY,MAAM;AACrD,QAAI;AACF,UAAK,KAAK,gBAAgB;MAAE;MAAY,OAAO;MAAa,CAAC;KAC7D,MAAM,cAAc,KAAK,aAAa,uBAAuB,SAAS,YAAY,UAAU,UAAU,EACpG,QAAQ,cACT,CAAC;AAEF,gBAAW,MAAM,SAAS,aAAa;AACrC,WAAK,SAAS,QAAQ,OAAO,MAAM;AACnC,WAAK,KAAK,gBAAgB;OAAE;OAAY;OAAO,CAAC;AAChD,YAAM;;AAGR,UAAK,SAAS,SAAS,MAAM;AAC7B,SAAI;MACF,MAAM,YAAY,MAAM,KAAK,eAAe,mBAAmB,WAAW;AAC1E,UAAI,WAAW,KACb,MAAK,KAAK,mBAAmB;OAAE,KAAK;OAAY,MAAM,UAAU;OAAM,CAAC;aAEnE;AAGR,YAAO;MACL,QAAQ,aAAa,UAAU,YAAY;MAC3C,SAAS,aAAa,UAAU,gBAAgB;MACjD;aACM,OAAO;AACd,SAAI,MAAM,EAAE,OAAO,EAAE,0BAA0B;KAC/C,MAAM,aAAa;MAAE,MAAM;MAAS,SAAS,UAAU,iBAAiB,QAAQ,MAAM,UAAU;MAAmB;AACnH,UAAK,SAAS,QAAQ,OAAO,WAAW;AACxC,UAAK,KAAK,gBAAgB;MAAE;MAAY,OAAO;MAAY,CAAC;AAC5D,UAAK,SAAS,SAAS,MAAM;AAC7B,WAAM;AACN,YAAO;MAAE,QAAQ;MAAS,SAAS,iBAAiB,QAAQ,MAAM,UAAU;MAAiB;cACrF;AACR,UAAK,0BAA0B,OAAO,WAAW;AACjD,UAAK,oBAAoB,OAAO,MAAM;;;GAK1C,MAAM,kBAAkB,+CAA+C;AACvE,SAAM,KAAK,IAAI,eAAe;IAC5B;IACA,WAAW;IACX,SAAS;IACT,SAAS;IACT,GAAI,kBAAkB,EAAE,UAAU,iBAAiB,GAAG,EAAE;IACzD,CAAC;AAIF,SAAM;IAAE,MAAM;IAAS,SAAS;IAAmB;AAGnD,SAAM,IAAI,SAAS,YAAY,WAAW,SAAS,IAAK,CAAC;AAEzD,SAAM;IAAE,MAAM;IAAS,SAAS;IAAU;AAE1C,UAAO;IAAE,QAAQ;IAAM,SAAS;IAAqB;WAC9C,OAAO;AACd,OAAI,MAAM,EAAE,OAAO,EAAE,mBAAmB;AACxC,SAAM;;;;CAKV,cAAc,OAAwB;AACpC,OAAK,cAAc,aAAa,MAAM;AACtC,OAAK,MAAM,CAAC,IAAI,OAAO,KAAK,0BAC1B,KAAI,OAAO,MACT,MAAK,0BAA0B,OAAO,GAAG;EAG7C,MAAM,IAAI,KAAK,oBAAoB,IAAI,MAAM;AAC7C,MAAI,CAAC,EACH,QAAO;AAET,IAAE,OAAO;AACT,SAAO;;;;;;CAOT,MAAM,kBACJ,QACA,SAC+F;EAC/F,MAAM,UAAU,QAAQ,MAAM;AAC9B,MAAI,CAAC,QACH,QAAO;GAAE,IAAI;GAAO,MAAM;GAAe;EAG3C,MAAM,aADY,gBAAgB,OAAO,GAErC,SACA,gBAAgB;GACd,SAAS,kBAAkB,KAAK,OAAO;GACvC,QAAQ;GACR,WAAW;GACX,UAAU;GACV,QAAQ;GACT,CAAC;AACN,MAAI,CAAC,KAAK,0BAA0B,IAAI,WAAW,CACjD,QAAO;GAAE,IAAI;GAAO,MAAM;GAAiB;AAG7C,MAAI,CADY,MAAM,KAAK,aAAa,oBAAoB,YAAY,QAAQ,CAE9E,QAAO;GAAE,IAAI;GAAO,MAAM;GAAgB;AAE5C,SAAO,EAAE,IAAI,MAAM;;CAGrB,MAAc,uBAAuB,KAInB;EAChB,MAAM,SAAS,gBAAgB,IAAI,WAAW;AAC9C,MAAI,CAAC,UAAU,OAAO,WAAW,WAC/B;EAGF,IAAI,OAAO,IAAI,QAAQ;AACvB,MAAI,IAAI,QAAQ,QACd,SAAQ,0BAA0B,IAAI,QAAQ;EAGhD,MAAM,UAAU,IAAI,QAAQ;EAC5B,MAAM,aACJ,WAAW,QAAQ,UAAU,IACzB,QAAQ,KAAK,GAAG,MAAM,CACpB;GACE,MAAM,EAAE,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,KAAK;GAC7C,eAAe,WAAW,IAAI,UAAU,GAAG;GAC5C,CACF,CAAC,GACF,KAAA;AAEN,MAAI,CAAC,WACH,SAAQ;AAGV,QAAM,KAAK,eAAe,KAAK;GAC7B,SAAS;GACT,SAAS,OAAO;GAChB,SAAS;GACT,UAAU;IACR,WAAW,OAAO;IAClB,GAAI,OAAO,WAAW,EAAE,UAAU,OAAO,UAAU,GAAG,EAAE;IACzD;GACD,SAAS;GACV,CAAC;;;CAIJ,sBAAsB,WAAmB,QAAyB;AAChE,SAAO,KAAK,cAAc,eAAe,WAAW,OAAO;;;;;CAM7D,MAAM,YACJ,SACA,QACA,SACgD;AAChD,MAAI;AACF,SAAM,KAAK,eAAe,KAAK;IAC7B;IACA,SAAS;IACT;IACD,CAAC;GACF,MAAM,YAAY,OAAO,KAAK,KAAK;AACnC,QAAK,KAAK,gBAAgB;IAAE;IAAS;IAAQ;IAAW,CAAC;AACzD,UAAO;IAAE,MAAM;IAAM;IAAW;WACzB,OAAO;AACd,OAAI,MAAM;IAAE;IAAS;IAAQ;IAAO,EAAE,yBAAyB;AAC/D,SAAM;;;;;;CAOV,oBAIG;EACD,MAAM,kBAAkB,IAAI,IAAI,KAAK,eAAe,oBAAoB,CAAC;EACzE,MAAM,WAAW,KAAK,OAAO;EAC7B,MAAM,eAAe;EAErB,MAAM,OAAsE,mBAAmB,KAC5F,UAAU;GACT;GACA,SAAS,CAAC,CAAC,WAAW,OAAO;GAC7B,WAAW,gBAAgB,IAAI,KAAK;GACrC,EACF;EAGD,MAAM,YADS,KAAK,iBAAiB,aAAa,GACzB,eAAe,KAAK,MAAM,EAAE,GAAG,CAAC,QAAQ,OAAO,CAAC,aAAa,SAAS,GAAG,CAAC,IAAI,EAAE;AACzG,MAAI,SAAS,WAAW,EACtB,QAAO;EAGT,MAAM,OAAO,IAAI,IAAI,aAAa;AAClC,OAAK,MAAM,QAAQ,UAAU;AAC3B,OAAI,KAAK,IAAI,KAAK,CAAE;AACpB,QAAK,IAAI,KAAK;AACd,QAAK,KAAK;IACR;IACA,SAAS,WAAW,OAAO,YAAY;IACvC,WAAW,gBAAgB,IAAI,KAAK;IACrC,CAAC;;AAGJ,SAAO;;;;;CAMT,oBAAoB,MAAkC;AACpD,OAAK,iBAAiB,WAAW,EAAE,QAAQ,MAAM,UAAU,UAAU,CAAC;;;;;CAMxE,YAYE;EACA,MAAM,kBAAkB,KAAK,eAAe,oBAAoB;EAChE,MAAM,cAAc,KAAK,eAAe,gBAAgB;EACxD,MAAM,WAAW,aAAa;AAE9B,SAAO;GACL,QAAQ;GACR,SAAS;GACT,SAAS;GACT,QAAQ,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK,aAAa,IAAK;GACxD,UAAU;IACR,SAAS,gBAAgB;IACzB,OAAO,YAAY;IACpB;GACD,YAAY,KAAK;GACjB,MAAM;IACJ,KAAK,WAAW;IAChB,WAAW,SAAS;IACpB,OAAO,SAAS;IACjB;GACF;;CAGH,IAAI,YAAqB;AACvB,SAAO,KAAK;;;;;CAMd,uBAAuB;AACrB,SAAO,KAAK,iBAAiB,aAAa;;;CAI5C,qBAA6C;AAC3C,SAAO,KAAK;;;;;CAMd,mBAAmB;EACjB,MAAM,EAAE,sBAAA,gBAAA,EAAA,aAAA,kBAAA;AACR,SAAO,kBAAkB;;;;;CAM3B,MAAM,oBAAoB,QAAgB,QAAmD;EAC3F,MAAM,WAAW,KAAK,sBAAsB;AAC5C,MAAI,CAAC,SACH,OAAM,IAAI,MAAM,mCAAmC;EAGrD,MAAM,UAAU,SAAS,iBAAiB,OAAO;AACjD,MAAI,CAAC,QACH,OAAM,IAAI,MAAM,6BAA6B,SAAS;AAGxD,SAAO,MAAM,QAAQ,OAAO;;CAG9B,IAAI,gBAAwB;AAC1B,SAAO,KAAK;;CAGd,IAAI,sBAAmC;AACrC,SAAO,KAAK;;CAGd,eAAkF;AAChF,SAAO;GACL,SAAS,KAAK,aAAa,iBAAiB;GAC5C,SAAS,sBAAsB;GAChC;;CAGH,uBAAuB,WAA8D;AACnF,SAAO,KAAK,aAAa,uBAAuB,UAAU;;CAG5D,mBAAmB,SAAuB;AACxC,wBAAsB,QAAQ;AAC9B,qBAAsB,QAAQ;AAC9B,OAAK,aAAa,8BAA8B;;CAGlD,uBACE,QACA,MACmC;EACnC,MAAM,SAAS,oBAAoB,QAAQ,KAAK;AAChD,wBAAsB,OAAO,QAAQ;AACrC,OAAK,aAAa,8BAA8B;AAChD,SAAO;;CAGT,MAAM,8BAA8B,QAAiE;AAEnG,SAAO,kBADM,0BAA0B,KAAK,OAAO,EACpB,OAAO;;CAGxC,MAAM,oCAAoC,aAAwD;AAEhG,SAAO,8BADM,0BAA0B,KAAK,OAAO,EACR,YAAY;;CAGzD,MAAM,4BAA4B,MAIa;EAC7C,MAAM,OAAO,0BAA0B,KAAK,OAAO;EACnD,MAAM,EAAE,gBAAgB,MAAM,2BAA2B,MAAM,KAAK,MAAM,KAAK,QAAQ;EACvF,MAAM,MAAM,MAAM,uBAAuB,MAAM,YAAY;EAC3D,MAAM,UAAU,6BAA6B,KAAK,KAAK;AACvD,SAAO,KAAK,uBAAuB,KAAK;GAAE;GAAS,WAAW,KAAK,aAAa;GAAO,CAAC;;CAG1F,uBAA6B;AAC3B,OAAK,aAAa,8BAA8B;;CAGlD,kBAAkB,WAAmB,SAAwB;AAC3D,2BAAyB,iBAAiB,CAAC,CAAC,gBAAgB,WAAW,QAAQ;AAC/E,OAAK,aAAa,qCAAqC;;CAGzD,IAAI,yBAAyC;AAC3C,SAAO,KAAK;;CAGd,MAAM,sBAAsB,YAAoB;AAC9C,SAAO,KAAK,aAAa,sBAAsB,WAAW;;;CAI5D,MAAM,oCAAoC,YAAqC;AAC7E,SAAO,KAAK,aAAa,oCAAoC,WAAW;;CAG1E,MAAM,wBAAwB,YAAoB,MAK/C;AACD,SAAO,KAAK,aAAa,wBAAwB,YAAY,KAAK;;;;;CAMpE,MAAM,cAAc,SAAiB,aAAa,cAA+B;AAC/E,SAAO,KAAK,aAAa,cAAc,SAAS,WAAW;;;;;;CAS7D,UAAU,WAAmB,UAAqC;AAChE,OAAK,YAAY,IAAI,WAAW,SAAS;AACzC,MAAI,CAAC,KAAK,aAAa,IAAI,UAAU,CACnC,MAAK,aAAa,IAAI,WAAW,EAAE,CAAC;AAEtC,MAAI,MAAM,EAAE,WAAW,EAAE,yBAAyB;AAElD,eAAa;AACX,QAAK,YAAY,OAAO,UAAU;AAElC,oBAAiB;AACf,QAAI,CAAC,KAAK,YAAY,IAAI,UAAU,CAClC,MAAK,aAAa,OAAO,UAAU;MAEpC,IAAI,IAAO;AACd,OAAI,MAAM,EAAE,WAAW,EAAE,2BAA2B;;;;;;CAOxD,KAAK,MAAc,SAAwB;EAEzC,MAAM,QAAsB;GAAE,IADnB,OAAO,EAAE,KAAK,aAAa;GACJ;GAAM;GAAS;AAEjD,OAAK,MAAM,CAAC,WAAW,aAAa,KAAK,aAAa;GAEpD,MAAM,MAAM,KAAK,aAAa,IAAI,UAAU,IAAI,EAAE;AAClD,OAAI,KAAK,MAAM;AACf,OAAI,IAAI,SAAS,kBAAmB,KAAI,OAAO;AAC/C,QAAK,aAAa,IAAI,WAAW,IAAI;AAGrC,OAAI;AACF,aAAS,MAAM;YACR,KAAK;AACZ,QAAI,KAAK;KAAE;KAAW;KAAK,EAAE,wCAAwC;;;;;;;CAQ3E,eAAe,WAAmB,aAAqC;EACrE,MAAM,MAAM,KAAK,aAAa,IAAI,UAAU;AAC5C,MAAI,CAAC,IAAK,QAAO,EAAE;EAEnB,MAAM,MAAM,IAAI,WAAW,MAAM,EAAE,OAAO,YAAY;AACtD,MAAI,QAAQ,GAAI,QAAO;AACvB,SAAO,IAAI,MAAM,MAAM,EAAE;;;;;;CAO3B,MAAc,iBACZ,YACA,SACA,aAQe;EAEf,MAAM,mBAAmB,MAAM,KAAK,eAAe,aAAa,WAAW;EAG3E,MAAM,cAAc;GAClB,MAAM;GACN,SAAS,CAAC;IAAE,MAAM;IAAiB,MAAM;IAAS,CAAC;GACnD,aAAa,aAAa,KAAK,OAAO;IACpC,MAAM,EAAE;IACR,UAAU,EAAE;IACZ,MAAM,EAAE;IACR,MAAM,EAAE;IACR,uBAAuB,EAAE;IAE1B,EAAE;GACH,WAAW,KAAK,KAAK;GAErB,kBAAkB;GACnB;EAGD,MAAM,kBAAkB,CAAC,GAAG,kBAAkB,YAAY;AAC1D,QAAM,KAAK,eAAe,aAAa,YAAY,gBAAgB;AACnE,MAAI,MAAM;GAAE;GAAY,cAAc,gBAAgB;GAAQ,EAAE,qBAAqB;;;;;CAQvF,MAAM,aAAa,OAA0B;AAC3C,SAAO,KAAK,eAAe,aAAa,MAAM;;;;;;CAOhD,MAAM,cAAc,OAA0B;AAC5C,SAAO,KAAK,eAAe,cAAc,MAAM;;;;;CAMjD,MAAM,WAAW,KAAa;AAC5B,SAAO,KAAK,eAAe,WAAW,IAAI;;;;;CAM5C,MAAM,cAAc,KAA4C;AAE9D,SAAO,EAAE,SADM,MAAM,KAAK,eAAe,cAAc,IAAI,EACjC;;;;;CAM5B,MAAM,eAAe,MAAkE;AACrF,SAAO,KAAK,eAAe,eAAe,KAAK;;;;;CAMjD,MAAM,cAAc,KAAa,MAA6C;AAC5E,QAAM,KAAK,eAAe,cAAc,KAAK,KAAK;AAClD,SAAO,EAAE,SAAS,MAAM;;;;;CAM1B,MAAM,WAAW,KAAa,MAA8C;AAC1E,QAAM,KAAK,eAAe,WAAW,KAAK,KAAK;AAC/C,SAAO,EAAE,QAAQ,MAAM;;;;;CAMzB,MAAM,aAAa,KAAa,MAAgD;AAC9E,QAAM,KAAK,eAAe,aAAa,KAAK,KAAK;AACjD,SAAO,EAAE,UAAU,MAAM;;;;;CAM3B,MAAM,eAAe,KAA6C;AAChE,QAAM,KAAK,eAAe,eAAe,IAAI;AAC7C,SAAO,EAAE,UAAU,MAAM;;;;;CAM3B,MAAM,iBAAiB,KAA+C;AACpE,QAAM,KAAK,eAAe,iBAAiB,IAAI;AAC/C,SAAO,EAAE,YAAY,MAAM;;;;;CAM7B,MAAM,WAAW,KAA2C;AAC1D,QAAM,KAAK,eAAe,WAAW,IAAI;AACzC,SAAO,EAAE,QAAQ,MAAM;;;;;CAMzB,MAAM,aAAa,KAA6C;AAC9D,QAAM,KAAK,eAAe,aAAa,IAAI;AAC3C,SAAO,EAAE,UAAU,MAAM;;;;;CAM3B,MAAM,eAAe,OAAe;AAClC,SAAO,KAAK,eAAe,eAAe,MAAM;;;;;CAMlD,MAAM,gBAAgB,KAAa,SAAiB;AAClD,SAAO,KAAK,eAAe,gBAAgB,KAAK,QAAQ;;;;;CAM1D,MAAM,cAAc,KAAa,QAAoD;AAEnF,SAAO,EAAE,SADO,MAAM,KAAK,eAAe,cAAc,KAAK,OAAO,EAClD;;;;;CAMpB,MAAM,kBAAkB;AACtB,SAAO,KAAK,eAAe,UAAU;;;;;;;;CASvC,MAAM,kBAAkB,SAStB;EACA,MAAM,SAAS,MAAM,KAAK,eAAe,aAAa;GACpD,OAAO;GACP,QAAQ;GACR,WAAW;GACX,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;GAC/B,CAAC;EAGF,MAAM,uBAAO,IAAI,KAAa;EAC9B,MAAM,UAOD,EAAE;AAEP,OAAK,MAAM,WAAW,OAAO,OAAO;GAClC,MAAM,MAAM,GAAG,QAAQ,cAAc,GAAG,QAAQ;AAChD,OAAI,CAAC,KAAK,IAAI,IAAI,IAAI,QAAQ,iBAAiB,QAAQ,cAAc;AACnE,SAAK,IAAI,IAAI;IACb,MAAM,IAAI,QAAQ;AAClB,YAAQ,KAAK;KACX,SAAS,QAAQ;KACjB,QAAQ,QAAQ;KAChB,YAAY,QAAQ;KACpB,GAAI,IACA;MACE,WAAW,EAAE;MACb,UAAU,EAAE;MACZ,QAAQ,EAAE;MACX,GACD,EAAE;KACP,CAAC;;;AAIN,SAAO;;;;;;CAOT,aAAa,SAAkE;EAC7E,MAAM,QAAQ,aAAa,QAAQ;AACnC,SAAO,cAAc,KAAK,MAAM,MAAM;;;;;CAMxC,cAAgC;AAC9B,SAAO,KAAK,KAAK;;;;;;CAOnB,eAAmC;AACjC,SAAO,KAAK,KAAK,SAAS,UAAU,KAAK,KAAK,QAAQ,KAAA;;;;;;CAOxD,MAAM,mBAAoC;AACxC,MAAI,KAAK,KAAK,SAAS,QACrB,OAAM,IAAI,MAAM,+CAA+C;EAIjE,MAAM,WAAW,OAAO,YAAY,GAAG,CAAC,SAAS,MAAM;AAGvD,OAAK,KAAK,QAAQ;AAGlB,OAAK,SAAS;GACZ,GAAG,KAAK;GACR,SAAS;IACP,GAAG,KAAK,OAAO;IACf,MAAM;KACJ,GAAG,KAAK,OAAO,SAAS;KACxB,MAAM;KACN,OAAO;KACR;IACF;GACF;AAED,QAAM,KAAK,6BAA6B,KAAK,OAAO;AAEpD,MAAI,KAAK,EAAE,cAAc,GAAG,SAAS,MAAM,GAAG,EAAE,CAAC,MAAM,EAAE,0BAA0B;AAEnF,SAAO"}
1
+ {"version":3,"file":"service.js","names":["writeConfigToDisk"],"sources":["../../../src/gateway/service.ts"],"sourcesContent":["import crypto from 'crypto';\nimport { AgentService } from '../agent/service.js';\nimport { ChannelManager } from '../channels/manager.js';\nimport { CHAT_CHANNEL_ORDER } from '../channels/registry.js';\nimport { MessageBus, MessageBusShutdownError } from '../infra/bus/index.js';\nimport type { Config as SurfaceConfig } from '../config/config-surface.js';\nimport { loadConfig, saveConfig as writeConfigToDisk } from '../config/index.js';\nimport { getWorkspacePath } from '../config/schema.js';\nimport { CronService } from '../cron/index.js';\nimport { computeBundledExtensionExtensionsPatch } from '../extensions/bundled-extension-activation.js';\nimport { ExtensionLoader } from '../extensions/index.js';\nimport { HeartbeatService, heartbeatRunnerConfigFromConfig } from './heartbeat/index.js';\nimport { ConfigHotReloader } from '../config/reload.js';\nimport { SessionManager } from '../session/index.js';\nimport type { Config } from '../config/schema.js';\nimport type { SessionListQuery, ExportFormat } from '../session/types.js';\nimport { resolveGatewayAuth, assertGatewayAuthConfigured, validateToken, extractToken, type ResolvedGatewayAuth } from './auth.js';\nimport { getModelRegistry } from '../providers/index.js';\nimport {\n createLogger,\n getLogDir,\n getLogStats,\n inboundCorrelationMetadataFromAsyncLogContext,\n} from '../utils/logger.js';\nimport {\n resolveConfigPath,\n resolveCronJobsPath,\n resolveStateDir,\n resolveAgentDir,\n resolveWorkspaceExtensionsDir,\n} from '../config/paths.js';\nimport { AgentRunRelay, type RelayEvent } from './agent-run-relay.js';\nimport { ClarifyBridge, type ClarifyBridgeRequest } from './clarify-bridge.js';\nimport { registerClarifyBridge } from './clarify-runtime.js';\nimport {\n deleteManagedSkill as deleteManagedSkillDir,\n installSkillFromZip,\n listManagedSkillDirs,\n} from '../agent/skills/managed-store.js';\nimport {\n downloadSkillZipBuffer,\n fetchMarketplacePackageDetail,\n listSkillPackages,\n resolveSkillZipDownloadUrl,\n resolveSkillsStoreBaseUrl,\n skillIdForMarketplaceInstall,\n type MarketplacePackageDetail,\n type SkillsStoreListParams,\n type SkillsStoreListResponse,\n} from '../agent/skills/skills-store-client.js';\nimport { createSkillConfigManager } from '../agent/skills/config.js';\nimport { removeSkillsLockEntry } from '../agent/skills/hub-lock.js';\nimport type { SkillCatalogEntry } from '../agent/agent-manager.js';\nimport type { ManagedSkillListItem } from '../agent/skills/managed-store.js';\n\nconst log = createLogger('GatewayService');\nimport { PACKAGE_VERSION } from '../package-version.js';\nimport { buildSessionKey, parseSessionKey } from '../routing/session-key.js';\nimport { getDefaultAgentId } from '../routing/resolve-route.js';\nimport { scheduleGatewayUpdateCheck } from '../infra/update-startup.js';\nimport { MAX_CHAT_ATTACHMENTS } from './chat-limits.js';\n\n// ========== SSE Event System ==========\n\nexport interface ServiceEvent {\n id: string;\n type: string;\n payload: unknown;\n}\n\ntype EventListener = (event: ServiceEvent) => Promise<void> | void;\n\nconst EVENT_BUFFER_SIZE = 200; // ring buffer per subscriber for Last-Event-ID replay\n\nexport interface GatewayServiceConfig {\n configPath?: string;\n enableHotReload?: boolean;\n}\n\nexport class GatewayService {\n private bus: MessageBus;\n private config: Config;\n private configPath: string;\n private agentService: AgentService;\n private channelManager: ChannelManager;\n private cronService: CronService;\n private extensionLoader: ExtensionLoader | null = null;\n private heartbeatService: HeartbeatService;\n private sessionManager: SessionManager;\n private running = false;\n private configReloader: ConfigHotReloader | null = null;\n private startTime = Date.now();\n private workspacePath: string;\n\n // Authentication\n private auth: ResolvedGatewayAuth;\n\n // SSE event system\n private eventCounter = 0;\n private subscribers = new Map<string, EventListener>();\n private eventBuffers = new Map<string, ServiceEvent[]>();\n\n // Agent run relay for resuming SSE streams\n public readonly runRelay = new AgentRunRelay();\n\n /** Per-run abort for webchat (POST /api/agent/abort or client disconnect). */\n private runAbortControllers = new Map<string, AbortController>();\n\n private stopGatewayUpdateCheck: (() => void) | null = null;\n\n private readonly clarifyBridge = new ClarifyBridge();\n\n /** Maps webchat session key → active `runId` for `clarify` tool routing. */\n private activeWebchatRunBySession = new Map<string, string>();\n\n constructor(private serviceConfig: GatewayServiceConfig = {}) {\n this.bus = new MessageBus();\n this.configPath = serviceConfig.configPath || resolveConfigPath();\n this.config = loadConfig(this.configPath);\n\n // Initialize authentication\n this.auth = resolveGatewayAuth({\n authConfig: this.config.gateway?.auth,\n });\n\n // Validate auth configuration\n assertGatewayAuthConfigured(this.auth);\n\n // Log token info (not the token itself)\n if (this.auth.mode === 'token') {\n const tokenPreview = this.auth.token ? `${this.auth.token.slice(0, 4)}***` : 'none';\n log.info({ mode: this.auth.mode, token: tokenPreview }, 'Authentication configured');\n } else {\n log.info({ mode: this.auth.mode }, 'Authentication disabled');\n }\n\n // Initialize channel manager\n this.channelManager = new ChannelManager(this.config, this.bus);\n\n // Initialize extension loader\n this.workspacePath = getWorkspacePath(this.config) || './workspace';\n this.initializeExtensionLoader();\n\n // Initialize ModelRegistry (loads from models.json)\n const registry = getModelRegistry();\n log.debug({ \n modelCount: registry.getAll().length, \n error: registry.getError() || 'none' \n }, 'ModelRegistry initialized');\n\n // Initialize agent service with extension registry\n const modelConfig = this.config.agents?.defaults?.model;\n const cronRef: { service?: CronService } = {};\n this.agentService = new AgentService(this.bus, {\n workspace: this.workspacePath,\n model: typeof modelConfig === 'string' ? modelConfig : modelConfig?.primary,\n config: this.config,\n extensionRegistry: this.extensionLoader?.getRegistry(),\n getCronService: () => cronRef.service,\n gatewayClarify: {\n requestClarification: (sessionKey, request) => {\n const runId = this.activeWebchatRunBySession.get(sessionKey);\n const publishSse = runId\n ? (e: RelayEvent) => {\n this.agentService.enqueueWebchatSseEvent(sessionKey, e);\n }\n : undefined;\n const parsed = parseSessionKey(sessionKey);\n const deliver =\n parsed?.source === 'telegram'\n ? async (ctx: { sessionKey: string; requestId: string; request: ClarifyBridgeRequest }) => {\n await this.deliverTelegramClarify(ctx);\n }\n : undefined;\n if (!runId && !deliver) {\n return Promise.reject(\n new Error('Clarify is not available for this session (use webchat, Telegram, or CLI)'),\n );\n }\n return this.clarifyBridge.startRequest({\n sessionKey,\n runId,\n relay: this.runRelay,\n publishSse,\n request,\n deliver,\n });\n },\n },\n });\n\n\n // Set channel manager reference for model switching\n this.agentService.setChannelManager(this.channelManager);\n this.channelManager.setSessionModelHooks({\n getModelForSession: (sk) => this.agentService.getModelForSession(sk),\n switchModelForSession: (sk, id) => this.agentService.switchModelForSession(sk, id),\n });\n\n // Initialize cron service\n this.cronService = new CronService({\n filePath: resolveCronJobsPath(),\n agentService: this.agentService,\n messageBus: this.bus,\n });\n cronRef.service = this.cronService;\n\n // Initialize session manager\n this.sessionManager = new SessionManager({\n config: this.config,\n });\n\n this.heartbeatService = new HeartbeatService({\n agentService: this.agentService,\n messageBus: this.bus,\n cronService: this.cronService,\n sessionStore: this.sessionManager.getStore(),\n getConfig: () => this.config,\n });\n\n this.cronService.setDeps({\n agentService: this.agentService,\n messageBus: this.bus,\n heartbeatService: this.heartbeatService,\n getDefaultCronAgentId: () => getDefaultAgentId(this.config),\n });\n }\n\n /**\n * Create extension loader and resolve configs (load runs in start() before channels).\n */\n private initializeExtensionLoader(): void {\n try {\n const aid = getDefaultAgentId(this.config);\n this.extensionLoader = new ExtensionLoader({\n workspaceDir: this.workspacePath,\n extensionsDir: resolveWorkspaceExtensionsDir(this.config, aid),\n });\n this.extensionLoader.setConfig(this.config as Parameters<ExtensionLoader['setConfig']>[0]);\n } catch (error) {\n log.warn({ error }, 'Failed to initialize extension loader');\n }\n }\n\n /**\n * Load extensions and register SDK / full ChannelPlugin instances with ChannelManager.\n */\n private async loadExtensionsAndRegisterChannels(): Promise<void> {\n if (!this.extensionLoader) {\n return;\n }\n try {\n await this.extensionLoader.loadByActivationPlan();\n const reg = this.extensionLoader.getRegistry();\n for (const plugin of reg.channelPlugins) {\n this.channelManager.registerPlugin(plugin);\n }\n log.debug(\n {\n extensionRecords: reg.extensions.size,\n channelPlugins: reg.channelPlugins.length,\n },\n 'Extensions loaded and channel plugins registered',\n );\n } catch (err) {\n log.warn({ err }, 'Failed to load extensions');\n }\n }\n\n async start(): Promise<void> {\n if (this.running) return;\n\n log.debug('Starting gateway service...');\n this.startTime = Date.now();\n this.running = true;\n\n registerClarifyBridge(this.clarifyBridge);\n\n this.channelManager.setOutboundHooks({\n runMessageSending: (to, content, channel) =>\n this.agentService.invokeOutboundMessageSending(to, content, channel),\n runMessageSent: (to, content, success, error, channel) =>\n this.agentService.invokeOutboundMessageSent(to, content, success, error, channel),\n });\n this.channelManager.enableOutboundPersistence(resolveAgentDir(this.config, getDefaultAgentId(this.config)));\n\n if (this.extensionLoader) {\n this.extensionLoader.setRuntimeContext({\n bus: this.bus,\n sessionManager: this.sessionManager,\n });\n }\n\n await this.loadExtensionsAndRegisterChannels();\n\n // Start channels (initialize first, then start)\n await this.channelManager.initialize();\n await this.channelManager.start();\n await this.channelManager.replayPendingOutboundMessages();\n\n // Initialize session manager\n await this.sessionManager.initialize();\n log.debug('Session manager initialized');\n\n this.cronService.setDeps({\n agentService: this.agentService,\n messageBus: this.bus,\n heartbeatService: this.heartbeatService,\n sessionStore: this.sessionManager.getStore(),\n getDefaultCronAgentId: () => getDefaultAgentId(this.config),\n });\n\n this.sessionManager.on('sessionUpdated', (data: { key: string; name?: string; tags?: string[] }) => {\n this.emit('session.updated', { key: data.key, name: data.name, tags: data.tags });\n });\n\n // Start cron service\n if (this.config.cron?.enabled !== false) {\n await this.cronService.initialize();\n }\n\n this.heartbeatService.start(heartbeatRunnerConfigFromConfig(this.config));\n\n // Start agent service (runs in background)\n this.agentService.start().catch((err) => {\n log.error({ err }, 'Agent service error');\n });\n\n // Start outbound message processor\n this.startOutboundProcessor().catch((err) => {\n log.error({ err }, 'Outbound processor error');\n });\n\n // Setup config hot reload\n if (this.serviceConfig.enableHotReload !== false) {\n this.setupConfigReloader();\n }\n\n this.stopGatewayUpdateCheck = scheduleGatewayUpdateCheck({\n config: this.config,\n onUpdateAvailableChange: (update) => {\n this.emit('update.available', update);\n },\n });\n\n log.debug('Gateway service started');\n }\n\n async stop(): Promise<void> {\n if (!this.running) return;\n\n log.debug('Stopping gateway service...');\n\n if (this.stopGatewayUpdateCheck) {\n this.stopGatewayUpdateCheck();\n this.stopGatewayUpdateCheck = null;\n }\n\n // Stop config reloader\n if (this.configReloader) {\n await this.configReloader.stop();\n this.configReloader = null;\n }\n\n // Stop heartbeat service\n this.heartbeatService.stop();\n\n registerClarifyBridge(null);\n this.clarifyBridge.dispose();\n this.agentService.stop();\n\n // Unblock `consumeOutbound()` / `consumeInbound()` waiters before stopping channels (CLI agent does the same).\n this.running = false;\n this.bus.shutdown();\n\n await this.channelManager.stop();\n\n // Stop cron service\n await this.cronService.stop();\n\n log.debug('Gateway service stopped');\n }\n\n /**\n * Start processing outbound messages and send through channels\n */\n private async startOutboundProcessor(): Promise<void> {\n log.debug('Starting outbound message processor');\n while (this.running) {\n try {\n const msg = await this.bus.consumeOutbound();\n await this.channelManager.send(msg);\n } catch (error) {\n if (error instanceof MessageBusShutdownError) {\n break;\n }\n const em = error instanceof Error ? error.message : String(error);\n log.error(\n { err: error, errorMessage: em, phase: 'outbound_consume' },\n `Outbound pipeline failed (will retry in 1s): ${em}`,\n );\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n }\n\n /**\n * Setup config hot reload using ConfigHotReloader\n */\n private setupConfigReloader(): void {\n this.configReloader = new ConfigHotReloader(\n this.configPath,\n this.config,\n {\n onModelsReload: (newConfig) => this.handleModelsReload(newConfig),\n onAgentDefaultsReload: (newConfig) => this.handleAgentDefaultsReload(newConfig),\n onChannelsReload: (newConfig) => this.handleChannelsReload(newConfig),\n onCronReload: (newConfig) => this.handleCronReload(newConfig),\n onHeartbeatReload: (newConfig) => this.handleHeartbeatReload(newConfig),\n onToolsReload: (newConfig) => this.handleToolsReload(newConfig),\n onFullRestart: (newConfig) => {\n log.warn(\n { requiresProcessRestart: true, hint: 'Restart the gateway process (hot reload cannot apply this change).' },\n 'Config reload: full gateway restart required — see prior \"restartPaths\" info log',\n );\n this.config = newConfig;\n this.emit('config.reload', { section: 'full', requiresRestart: true });\n },\n },\n {\n debounceMs: 300,\n enabled: this.serviceConfig.enableHotReload !== false,\n }\n );\n this.configReloader.start();\n }\n\n /**\n * Handle models config hot reload\n */\n private handleModelsReload(newConfig: Config): void {\n log.debug('Reloading models config...');\n this.config = newConfig;\n getModelRegistry().refresh();\n this.emit('config.reload', { section: 'models' });\n log.debug('Models config reloaded');\n }\n\n /**\n * Handle agent defaults config hot reload\n */\n private handleAgentDefaultsReload(newConfig: Config): void {\n log.debug('Reloading agent defaults...');\n this.config = newConfig;\n this.agentService.applyAgentDefaultsFromConfig(newConfig);\n this.emit('config.reload', { section: 'agents' });\n log.debug('Agent defaults reloaded');\n }\n\n /**\n * Apply `latest.channels` to every registered channel plugin (Telegram, Weixin, extensions).\n * Single runtime path for: file watcher hot reload, API saves, and Weixin QR follow-up.\n */\n private async handleChannelsReload(newConfig: Config): Promise<void> {\n log.debug('Reloading channels config...');\n this.config = newConfig;\n await this.channelManager.updateConfig(newConfig);\n this.emit('config.reload', { section: 'channels' });\n this.emit('channels.status', { channels: this.getChannelsStatus() });\n log.debug('Channels config reloaded');\n }\n\n /** After persisting config to disk: align plugins + debounced reload baseline (watchers skip duplicate diffs). */\n private async syncChannelPluginsAfterPersist(): Promise<void> {\n await this.handleChannelsReload(this.config);\n this.configReloader?.syncCurrentConfig(this.config);\n }\n\n /**\n * Handle cron config hot reload\n */\n private handleCronReload(newConfig: Config): void {\n log.debug('Reloading cron config...');\n this.config = newConfig;\n this.cronService.updateConfig(newConfig);\n this.emit('config.reload', { section: 'cron' });\n log.debug('Cron config reloaded');\n }\n\n /**\n * Handle heartbeat config hot reload\n */\n private handleHeartbeatReload(newConfig: Config): void {\n log.debug('Reloading heartbeat config...');\n this.config = newConfig;\n this.heartbeatService.updateConfig(newConfig);\n this.emit('config.reload', { section: 'heartbeat' });\n log.debug('Heartbeat config reloaded');\n }\n\n /**\n * Apply `gateway.heartbeat` from current config after PATCH /api/config (and when hot reload is off).\n * File watcher uses `handleHeartbeatReload` with the same effect when paths match.\n */\n reloadHeartbeatFromCurrentConfig(): void {\n this.handleHeartbeatReload(this.config);\n }\n\n /**\n * Handle tools config hot reload\n */\n private handleToolsReload(newConfig: Config): void {\n log.debug('Reloading tools config...');\n this.config = newConfig;\n this.emit('config.reload', { section: 'tools' });\n log.debug('Tools config reloaded');\n }\n\n /**\n * Reload configuration from disk (manual trigger)\n */\n async reloadConfig(): Promise<{ reloaded: boolean; error?: string }> {\n if (!this.configReloader) {\n return { reloaded: false, error: 'Config reloader not initialized' };\n }\n const result = await this.configReloader.triggerReload();\n return { reloaded: result.success, error: result.error };\n }\n\n /**\n * After Weixin QR login: token files may change without a `channels.weixin` JSON diff, so run the same\n * channel apply as an API save, then force Weixin long-poll restart (see `reloadMonitorsWithConfig`).\n */\n async afterWeixinCredentialsPersisted(): Promise<void> {\n const next = loadConfig(this.configPath);\n this.config = next;\n this.agentService.applyAgentDefaultsFromConfig(next);\n this.configReloader?.syncCurrentConfig(next);\n await this.handleChannelsReload(next);\n const { weixinPlugin } = await import('../channels/weixin/index.js');\n await weixinPlugin.reloadMonitorsWithConfig(this.config, this.bus);\n log.info('Weixin monitors restarted after credential login');\n }\n\n /**\n * Save current config to disk\n */\n /**\n * Persist and replace `this.config` with the validated file contents so runtime matches disk\n * (PATCH merge objects can drift from Zod-normalized output).\n */\n private async writeConfigAndReloadFromDisk(configToWrite: Config): Promise<void> {\n await writeConfigToDisk(configToWrite, this.configPath);\n this.config = loadConfig(this.configPath);\n this.agentService.applyAgentDefaultsFromConfig(this.config);\n }\n\n async saveConfig(config: Config): Promise<{ saved: boolean; error?: string }> {\n try {\n await this.writeConfigAndReloadFromDisk(config);\n await this.syncChannelPluginsAfterPersist();\n return { saved: true };\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n log.error({ error }, 'Failed to save config');\n return { saved: false, error };\n }\n }\n\n /**\n * App store (phase 1): persist `extensions.enabled` / `extensions.disabled` for a bundled extension.\n * Extension modules are loaded at gateway startup; restart the gateway process to fully apply load/unload.\n */\n async setBundledExtensionActivationTarget(\n extensionId: string,\n wanted: boolean,\n ): Promise<{ ok: boolean; error?: string; requiresGatewayRestart: boolean }> {\n const loader = this.extensionLoader;\n if (!loader) {\n return { ok: false, error: 'Extension loader unavailable', requiresGatewayRestart: false };\n }\n const id = extensionId.trim();\n if (!id) {\n return { ok: false, error: 'Invalid extension id', requiresGatewayRestart: false };\n }\n const patch = computeBundledExtensionExtensionsPatch(loader, this.config, id, wanted);\n if (patch.ok === false) {\n return { ok: false, error: patch.error, requiresGatewayRestart: false };\n }\n const newConfig = { ...this.config, extensions: patch.extensions } as Config;\n const saved = await this.saveConfig(newConfig);\n if (!saved.saved) {\n return { ok: false, error: saved.error ?? 'Failed to save config', requiresGatewayRestart: false };\n }\n loader.setConfig(this.config as unknown as SurfaceConfig);\n return { ok: true, requiresGatewayRestart: true };\n }\n\n /**\n * Update configuration and persist to disk\n */\n async updateConfig(updates: Partial<Config>): Promise<{ updated: boolean; error?: string }> {\n try {\n log.debug('Updating configuration...');\n \n // Merge updates\n this.config = { ...this.config, ...updates };\n\n await this.writeConfigAndReloadFromDisk(this.config);\n await this.syncChannelPluginsAfterPersist();\n\n log.debug('Configuration updated successfully');\n return { updated: true };\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n log.error({ error }, 'Failed to update config');\n return { updated: false, error };\n }\n }\n\n /**\n * Run agent with a message and stream events\n */\n /**\n * @param runOptions.signal — When set (e.g. client disconnect), aborts in-flight generation and persists partial output.\n */\n async *runAgent(\n message: string,\n channel: string,\n chatId: string,\n attachments?: Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n }>,\n thinking?: string,\n runOptions?: { signal?: AbortSignal },\n ): AsyncGenerator<{ type: string; content?: string; status?: string; runId?: string }, { status: string; summary: string }, unknown> {\n const cappedAttachments =\n attachments && attachments.length > MAX_CHAT_ATTACHMENTS\n ? attachments.slice(0, MAX_CHAT_ATTACHMENTS)\n : attachments;\n if (attachments && cappedAttachments && attachments.length > cappedAttachments.length) {\n log.debug(\n { dropped: attachments.length - cappedAttachments.length, max: MAX_CHAT_ATTACHMENTS },\n 'Attachments capped for webchat',\n );\n }\n\n const runId = crypto.randomUUID();\n\n // For webchat, register the run in the relay before yielding the first event\n if (channel === 'webchat') {\n const parsedKey = parseSessionKey(chatId);\n const sessionKey = parsedKey ? chatId : buildSessionKey({\n agentId: getDefaultAgentId(this.config),\n source: 'webchat',\n accountId: 'default',\n peerKind: 'direct',\n peerId: chatId,\n });\n this.runRelay.ensureRun(runId, sessionKey);\n this.runAbortControllers.set(runId, new AbortController());\n }\n\n const statusEvent = { type: 'status', status: 'accepted', runId };\n if (channel === 'webchat') this.runRelay.publish(runId, statusEvent);\n yield statusEvent;\n\n try {\n // For 'webchat' channel (web UI), process through agent service\n if (channel === 'webchat') {\n // Determine session key: if chatId is already a valid session key, use it directly\n // Otherwise, build a new session key from the chatId\n const parsedKey = parseSessionKey(chatId);\n const sessionKey = parsedKey ? chatId : buildSessionKey({\n agentId: getDefaultAgentId(this.config),\n source: 'webchat',\n accountId: 'default',\n peerKind: 'direct',\n peerId: chatId,\n });\n\n const prepared = await this.agentService.prepareInboundAttachments(sessionKey, cappedAttachments);\n\n // Persist before streaming so a mid-turn refresh still sees text + attachment refs on disk.\n try {\n await this._saveUserMessage(sessionKey, message, prepared);\n } catch (err) {\n log.error({ err, sessionKey }, 'Failed to save user message');\n }\n\n const runAbort = this.runAbortControllers.get(runId);\n if (!runAbort) {\n throw new Error('run abort controller missing for webchat');\n }\n const mergedSignal = runOptions?.signal\n ? AbortSignal.any([runOptions.signal, runAbort.signal])\n : runAbort.signal;\n\n this.activeWebchatRunBySession.set(sessionKey, runId);\n try {\n this.emit('agent.stream', { sessionKey, event: statusEvent });\n const eventStream = this.agentService.processDirectStreaming(message, sessionKey, prepared, thinking, {\n signal: mergedSignal,\n });\n\n for await (const event of eventStream) {\n this.runRelay.publish(runId, event);\n this.emit('agent.stream', { sessionKey, event });\n yield event as { type: string; content?: string; status?: string; runId?: string };\n }\n\n this.runRelay.complete(runId);\n try {\n const metaAfter = await this.sessionManager.getSessionMetadata(sessionKey);\n if (metaAfter?.name) {\n this.emit('session.updated', { key: sessionKey, name: metaAfter.name });\n }\n } catch {\n /* ignore */\n }\n return {\n status: mergedSignal.aborted ? 'aborted' : 'ok',\n summary: mergedSignal.aborted ? 'Interrupted' : 'Message processed successfully',\n };\n } catch (error) {\n log.error({ error }, 'Agent processing failed');\n const errorEvent = { type: 'error', content: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` };\n this.runRelay.publish(runId, errorEvent);\n this.emit('agent.stream', { sessionKey, event: errorEvent });\n this.runRelay.complete(runId);\n yield errorEvent;\n return { status: 'error', summary: error instanceof Error ? error.message : 'Unknown error' };\n } finally {\n this.activeWebchatRunBySession.delete(sessionKey);\n this.runAbortControllers.delete(runId);\n }\n }\n\n // Send message through bus for other channels (telegram, etc.)\n const correlationMeta = inboundCorrelationMetadataFromAsyncLogContext();\n await this.bus.publishInbound({\n channel,\n sender_id: 'gateway',\n chat_id: chatId,\n content: message,\n ...(correlationMeta ? { metadata: correlationMeta } : {}),\n });\n\n // Wait for and collect response\n // This is simplified - in production we'd need proper session tracking\n yield { type: 'token', content: 'Processing...\\n' };\n\n // Simulate processing delay\n await new Promise((resolve) => setTimeout(resolve, 1000));\n\n yield { type: 'token', content: 'Done\\n' };\n\n return { status: 'ok', summary: 'Message processed' };\n } catch (error) {\n log.error({ error }, 'Agent run failed');\n throw error;\n }\n }\n\n /** Abort an in-flight webchat agent run (matches `runId` from SSE `status`). */\n abortAgentRun(runId: string): boolean {\n this.clarifyBridge.cancelForRun(runId);\n for (const [sk, id] of this.activeWebchatRunBySession) {\n if (id === runId) {\n this.activeWebchatRunBySession.delete(sk);\n }\n }\n const c = this.runAbortControllers.get(runId);\n if (!c) {\n return false;\n }\n c.abort();\n return true;\n }\n\n /**\n * Queue steering text for an active webchat run (`Agent.steer` / tool-boundary injection).\n * `chatId` is the same as `POST /api/agent` body (`sessionKey` or legacy peer id).\n */\n async steerWebchatAgent(\n chatId: string,\n message: string,\n ): Promise<{ ok: true } | { ok: false; code: 'BAD_REQUEST' | 'NO_ACTIVE_RUN' | 'STEER_FAILED' }> {\n const trimmed = message.trim();\n if (!trimmed) {\n return { ok: false, code: 'BAD_REQUEST' };\n }\n const parsedKey = parseSessionKey(chatId);\n const sessionKey = parsedKey\n ? chatId\n : buildSessionKey({\n agentId: getDefaultAgentId(this.config),\n source: 'webchat',\n accountId: 'default',\n peerKind: 'direct',\n peerId: chatId,\n });\n if (!this.activeWebchatRunBySession.has(sessionKey)) {\n return { ok: false, code: 'NO_ACTIVE_RUN' };\n }\n const steered = await this.agentService.steerWebchatSession(sessionKey, trimmed);\n if (!steered) {\n return { ok: false, code: 'STEER_FAILED' };\n }\n return { ok: true };\n }\n\n private async deliverTelegramClarify(ctx: {\n sessionKey: string;\n requestId: string;\n request: ClarifyBridgeRequest;\n }): Promise<void> {\n const parsed = parseSessionKey(ctx.sessionKey);\n if (!parsed || parsed.source !== 'telegram') {\n return;\n }\n\n let body = ctx.request.question;\n if (ctx.request.default) {\n body += `\\n\\nDefault if unsure: ${ctx.request.default}`;\n }\n\n const choices = ctx.request.choices;\n const buttonRows =\n choices && choices.length >= 2\n ? choices.map((c, i) => [\n {\n text: c.length > 64 ? `${c.slice(0, 61)}…` : c,\n callback_data: `clarify:${ctx.requestId}:${i}`,\n },\n ])\n : undefined;\n\n if (!buttonRows) {\n body += '\\n\\nReply with your answer in this chat.';\n }\n\n await this.channelManager.send({\n channel: 'telegram',\n chat_id: parsed.peerId,\n content: body,\n metadata: {\n accountId: parsed.accountId,\n ...(parsed.threadId ? { threadId: parsed.threadId } : {}),\n },\n buttons: buttonRows,\n });\n }\n\n /** Deliver a user's answer to a pending `clarify` tool call. */\n submitClarifyResponse(requestId: string, answer: string): boolean {\n return this.clarifyBridge.handleResponse(requestId, answer);\n }\n\n /**\n * Send message through a channel\n */\n async sendMessage(\n channel: string,\n chatId: string,\n content: string\n ): Promise<{ sent: boolean; messageId?: string }> {\n try {\n await this.channelManager.send({\n channel,\n chat_id: chatId,\n content,\n });\n const messageId = `msg_${Date.now()}`;\n this.emit('message.sent', { channel, chatId, messageId });\n return { sent: true, messageId };\n } catch (error) {\n log.error({ channel, chatId, error }, 'Failed to send message');\n throw error;\n }\n }\n\n /**\n * Get channel statuses\n */\n getChannelsStatus(): Array<{\n name: string;\n enabled: boolean;\n connected: boolean;\n }> {\n const runningChannels = new Set(this.channelManager.getRunningChannels());\n const channels = this.config.channels as Record<string, { enabled?: boolean } | undefined> | undefined;\n const builtinOrder = CHAT_CHANNEL_ORDER as readonly string[];\n\n const rows: Array<{ name: string; enabled: boolean; connected: boolean }> = CHAT_CHANNEL_ORDER.map(\n (name) => ({\n name,\n enabled: !!channels?.[name]?.enabled,\n connected: runningChannels.has(name),\n }),\n );\n\n const extReg = this.extensionLoader?.getRegistry();\n const extraIds = extReg?.channelPlugins.map((p) => p.id).filter((id) => !builtinOrder.includes(id)) ?? [];\n if (extraIds.length === 0) {\n return rows;\n }\n\n const seen = new Set(builtinOrder);\n for (const name of extraIds) {\n if (seen.has(name)) continue;\n seen.add(name);\n rows.push({\n name,\n enabled: channels?.[name]?.enabled !== false,\n connected: runningChannels.has(name),\n });\n }\n\n return rows;\n }\n\n /**\n * Request an immediate heartbeat run (coalesced like interval/cron wakes).\n */\n requestHeartbeatNow(opts?: { reason?: string }): void {\n this.heartbeatService.requestNow({ reason: opts?.reason ?? 'manual' });\n }\n\n /**\n * Get health status\n */\n getHealth(): {\n status: string;\n service: string;\n version: string;\n uptime: number;\n channels: { running: number; total: number };\n configPath: string;\n logs?: {\n dir: string;\n errors24h: number;\n stats: Record<string, number>;\n };\n } {\n const runningChannels = this.channelManager.getRunningChannels();\n const allChannels = this.channelManager.getAllChannels();\n const logStats = getLogStats();\n\n return {\n status: 'ok',\n service: 'xopc-gateway',\n version: PACKAGE_VERSION,\n uptime: Math.floor((Date.now() - this.startTime) / 1000),\n channels: {\n running: runningChannels.length,\n total: allChannels.length,\n },\n configPath: this.configPath,\n logs: {\n dir: getLogDir(),\n errors24h: logStats.errorsLast24h,\n stats: logStats.byLevel,\n },\n };\n }\n\n get isRunning(): boolean {\n return this.running;\n }\n\n /**\n * Get extension registry for external access (HTTP routes, gateway methods)\n */\n getExtensionRegistry() {\n return this.extensionLoader?.getRegistry();\n }\n\n /** Extension loader for discovery and frontend asset APIs (may be null if extensions failed to init). */\n getExtensionLoader(): ExtensionLoader | null {\n return this.extensionLoader;\n }\n\n /**\n * Get model registry for external access (HTTP routes)\n */\n getModelRegistry() {\n const { getModelRegistry } = require('../providers/index.js');\n return getModelRegistry();\n }\n\n /**\n * Invoke a gateway method registered by extensions\n */\n async invokeGatewayMethod(method: string, params: Record<string, unknown>): Promise<unknown> {\n const registry = this.getExtensionRegistry();\n if (!registry) {\n throw new Error('Extension registry not available');\n }\n\n const handler = registry.getGatewayMethod(method);\n if (!handler) {\n throw new Error(`Gateway method not found: ${method}`);\n }\n\n return await handler(params);\n }\n\n get currentConfig(): Config {\n return this.config;\n }\n\n get cronServiceInstance(): CronService {\n return this.cronService;\n }\n\n getSkillsApi(): { catalog: SkillCatalogEntry[]; managed: ManagedSkillListItem[] } {\n return {\n catalog: this.agentService.getSkillCatalog(),\n managed: listManagedSkillDirs(),\n };\n }\n\n getSkillMarkdownSource(skillName: string): { name: string; markdown: string } | null {\n return this.agentService.getSkillMarkdownSource(skillName);\n }\n\n deleteManagedSkill(skillId: string): void {\n removeSkillsLockEntry(skillId);\n deleteManagedSkillDir(skillId);\n this.agentService.refreshSkillsAfterDiskChange();\n }\n\n installManagedSkillZip(\n buffer: Buffer,\n opts: { skillId?: string; overwrite?: boolean },\n ): { skillId: string; path: string } {\n const result = installSkillFromZip(buffer, opts);\n removeSkillsLockEntry(result.skillId);\n this.agentService.refreshSkillsAfterDiskChange();\n return result;\n }\n\n async fetchSkillsMarketplaceCatalog(params: SkillsStoreListParams): Promise<SkillsStoreListResponse> {\n const base = resolveSkillsStoreBaseUrl(this.config);\n return listSkillPackages(base, params);\n }\n\n async fetchSkillsMarketplacePackageDetail(packageName: string): Promise<MarketplacePackageDetail> {\n const base = resolveSkillsStoreBaseUrl(this.config);\n return fetchMarketplacePackageDetail(base, packageName);\n }\n\n async installSkillFromMarketplace(opts: {\n name: string;\n version?: string;\n overwrite?: boolean;\n }): Promise<{ skillId: string; path: string }> {\n const base = resolveSkillsStoreBaseUrl(this.config);\n const { downloadUrl } = await resolveSkillZipDownloadUrl(base, opts.name, opts.version);\n const buf = await downloadSkillZipBuffer(base, downloadUrl);\n const skillId = skillIdForMarketplaceInstall(opts.name);\n return this.installManagedSkillZip(buf, { skillId, overwrite: opts.overwrite ?? false });\n }\n\n reloadSkillsFromDisk(): void {\n this.agentService.refreshSkillsAfterDiskChange();\n }\n\n patchSkillEnabled(skillName: string, enabled: boolean): void {\n createSkillConfigManager(resolveStateDir()).setSkillEnabled(skillName, enabled);\n this.agentService.refreshSkillsAfterSkillConfigChange();\n }\n\n get sessionManagerInstance(): SessionManager {\n return this.sessionManager;\n }\n\n async getSessionAgentConfig(sessionKey: string) {\n return this.agentService.getSessionAgentConfig(sessionKey);\n }\n\n /** Resolved markdown workspace for a session (after hydration / mkdir). Used by workspace file API when `sessionKey` is passed. */\n async getEffectiveWorkspacePathForSession(sessionKey: string): Promise<string> {\n return this.agentService.getEffectiveWorkspacePathForSession(sessionKey);\n }\n\n async patchSessionAgentConfig(sessionKey: string, body: {\n thinkingLevel?: string;\n model?: string | null;\n reasoningLevel?: string;\n workingDirectory?: string;\n }) {\n return this.agentService.patchSessionAgentConfig(sessionKey, body);\n }\n\n /**\n * Process a message directly through the agent (for CLI mode)\n */\n async processDirect(content: string, sessionKey = 'cli:direct'): Promise<string> {\n return this.agentService.processDirect(content, sessionKey);\n }\n\n // ========== SSE Event System ==========\n\n /**\n * Subscribe to server-pushed events.\n * Returns a cleanup function to unsubscribe.\n */\n subscribe(sessionId: string, listener: EventListener): () => void {\n this.subscribers.set(sessionId, listener);\n if (!this.eventBuffers.has(sessionId)) {\n this.eventBuffers.set(sessionId, []);\n }\n log.debug({ sessionId }, 'Event subscriber added');\n\n return () => {\n this.subscribers.delete(sessionId);\n // Keep buffer for a while in case they reconnect\n setTimeout(() => {\n if (!this.subscribers.has(sessionId)) {\n this.eventBuffers.delete(sessionId);\n }\n }, 5 * 60_000); // 5 min grace\n log.debug({ sessionId }, 'Event subscriber removed');\n };\n }\n\n /**\n * Emit an event to all subscribers.\n */\n emit(type: string, payload: unknown): void {\n const id = String(++this.eventCounter);\n const event: ServiceEvent = { id, type, payload };\n\n for (const [sessionId, listener] of this.subscribers) {\n // Buffer the event\n const buf = this.eventBuffers.get(sessionId) || [];\n buf.push(event);\n if (buf.length > EVENT_BUFFER_SIZE) buf.shift();\n this.eventBuffers.set(sessionId, buf);\n\n // Deliver\n try {\n listener(event);\n } catch (err) {\n log.warn({ sessionId, err }, 'Failed to deliver event to subscriber');\n }\n }\n }\n\n /**\n * Get events since a given event id (for Last-Event-ID reconnection).\n */\n getEventsSince(sessionId: string, lastEventId: string): ServiceEvent[] {\n const buf = this.eventBuffers.get(sessionId);\n if (!buf) return [];\n\n const idx = buf.findIndex((e) => e.id === lastEventId);\n if (idx === -1) return buf; // can't find cursor — send everything in buffer\n return buf.slice(idx + 1);\n }\n\n /**\n * Save user message to session for webchat (fire-and-forget).\n * Called at the start of runAgent to ensure message survives page refresh.\n */\n private async _saveUserMessage(\n sessionKey: string,\n message: string,\n attachments?: Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>,\n ): Promise<void> {\n // Load existing messages\n const existingMessages = await this.sessionManager.loadMessages(sessionKey);\n\n // Build user message (marker stripped in SessionStore.convertMessages for API clients)\n const userMessage = {\n role: 'user' as const,\n content: [{ type: 'text' as const, text: message }],\n attachments: attachments?.map((a) => ({\n type: a.type,\n mimeType: a.mimeType,\n name: a.name,\n size: a.size,\n workspaceRelativePath: a.workspaceRelativePath,\n // Note: we don't store data (base64) to keep session file small\n })),\n timestamp: Date.now(),\n /** Dropped before `agent.prompt` so we don't duplicate the turn; not exposed via GET session */\n webchatEarlySave: true as const,\n };\n\n // Append and save\n const updatedMessages = [...existingMessages, userMessage];\n await this.sessionManager.saveMessages(sessionKey, updatedMessages);\n log.debug({ sessionKey, messageCount: updatedMessages.length }, 'User message saved');\n }\n\n // ========== Session Management API ==========\n\n /**\n * List sessions with query filters\n */\n async listSessions(query?: SessionListQuery) {\n return this.sessionManager.listSessions(query);\n }\n\n /**\n * List all subagent sessions.\n * Subagent sessions have keys starting with 'subagent:'.\n */\n async listSubagents(query?: SessionListQuery) {\n return this.sessionManager.listSubagents(query);\n }\n\n /**\n * Get a single session by key\n */\n async getSession(key: string) {\n return this.sessionManager.getSession(key);\n }\n\n /**\n * Delete a session\n */\n async deleteSession(key: string): Promise<{ deleted: boolean }> {\n const result = await this.sessionManager.deleteSession(key);\n return { deleted: result };\n }\n\n /**\n * Delete multiple sessions\n */\n async deleteSessions(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n return this.sessionManager.deleteSessions(keys);\n }\n\n /**\n * Rename a session\n */\n async renameSession(key: string, name: string): Promise<{ renamed: boolean }> {\n await this.sessionManager.renameSession(key, name);\n return { renamed: true };\n }\n\n /**\n * Tag a session\n */\n async tagSession(key: string, tags: string[]): Promise<{ tagged: boolean }> {\n await this.sessionManager.tagSession(key, tags);\n return { tagged: true };\n }\n\n /**\n * Remove tags from a session\n */\n async untagSession(key: string, tags: string[]): Promise<{ untagged: boolean }> {\n await this.sessionManager.untagSession(key, tags);\n return { untagged: true };\n }\n\n /**\n * Archive a session\n */\n async archiveSession(key: string): Promise<{ archived: boolean }> {\n await this.sessionManager.archiveSession(key);\n return { archived: true };\n }\n\n /**\n * Unarchive a session\n */\n async unarchiveSession(key: string): Promise<{ unarchived: boolean }> {\n await this.sessionManager.unarchiveSession(key);\n return { unarchived: true };\n }\n\n /**\n * Pin a session\n */\n async pinSession(key: string): Promise<{ pinned: boolean }> {\n await this.sessionManager.pinSession(key);\n return { pinned: true };\n }\n\n /**\n * Unpin a session\n */\n async unpinSession(key: string): Promise<{ unpinned: boolean }> {\n await this.sessionManager.unpinSession(key);\n return { unpinned: true };\n }\n\n /**\n * Search sessions\n */\n async searchSessions(query: string) {\n return this.sessionManager.searchSessions(query);\n }\n\n /**\n * Search within a session\n */\n async searchInSession(key: string, keyword: string) {\n return this.sessionManager.searchInSession(key, keyword);\n }\n\n /**\n * Export a session\n */\n async exportSession(key: string, format: ExportFormat): Promise<{ content: string }> {\n const content = await this.sessionManager.exportSession(key, format);\n return { content };\n }\n\n /**\n * Get session statistics\n */\n async getSessionStats() {\n return this.sessionManager.getStats();\n }\n\n /**\n * Get unique chat IDs from sessions, grouped by channel\n * Returns a list of channel/chatId pairs for cron job configuration.\n * `chatId` is the session-store routing suffix (unique per bot account + peer).\n * When `routing` exists, `peerId` is the platform id (e.g. Telegram numeric chat id).\n */\n async getSessionChatIds(channel?: string): Promise<\n Array<{\n channel: string;\n chatId: string;\n lastActive: string;\n accountId?: string;\n peerKind?: string;\n peerId?: string;\n }>\n > {\n const result = await this.sessionManager.listSessions({\n limit: 1000,\n sortBy: 'lastAccessedAt',\n sortOrder: 'desc',\n ...(channel ? { channel } : {}),\n });\n\n // Group by channel:chatId to get unique pairs\n const seen = new Set<string>();\n const chatIds: Array<{\n channel: string;\n chatId: string;\n lastActive: string;\n accountId?: string;\n peerKind?: string;\n peerId?: string;\n }> = [];\n\n for (const session of result.items) {\n const key = `${session.sourceChannel}:${session.sourceChatId}`;\n if (!seen.has(key) && session.sourceChannel && session.sourceChatId) {\n seen.add(key);\n const r = session.routing;\n chatIds.push({\n channel: session.sourceChannel,\n chatId: session.sourceChatId,\n lastActive: session.lastAccessedAt,\n ...(r\n ? {\n accountId: r.accountId,\n peerKind: r.peerKind,\n peerId: r.peerId,\n }\n : {}),\n });\n }\n }\n\n return chatIds;\n }\n\n /**\n * Validate authentication token from request headers.\n * Returns true if auth is disabled (mode: 'none') or token is valid.\n */\n validateAuth(headers?: Record<string, string | string[] | undefined>): boolean {\n const token = extractToken(headers);\n return validateToken(this.auth, token);\n }\n\n /**\n * Get current auth mode.\n */\n getAuthMode(): 'none' | 'token' {\n return this.auth.mode;\n }\n\n /**\n * Get current auth token (for CLI server integration).\n * Returns undefined if mode is 'none'.\n */\n getAuthToken(): string | undefined {\n return this.auth.mode === 'token' ? this.auth.token : undefined;\n }\n\n /**\n * Refresh (regenerate) the gateway auth token.\n * Returns the new token.\n */\n async refreshAuthToken(): Promise<string> {\n if (this.auth.mode !== 'token') {\n throw new Error('Cannot refresh token: auth mode is not token');\n }\n\n // Generate new token\n const newToken = crypto.randomBytes(24).toString('hex');\n \n // Update in-memory auth\n this.auth.token = newToken;\n \n // Update config\n this.config = {\n ...this.config,\n gateway: {\n ...this.config.gateway,\n auth: {\n ...this.config.gateway?.auth,\n mode: 'token',\n token: newToken,\n },\n },\n };\n \n await this.writeConfigAndReloadFromDisk(this.config);\n\n log.info({ tokenPreview: `${newToken.slice(0, 8)}...` }, 'Gateway token refreshed');\n \n return newToken;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Recursive workspace file list (relative POSIX paths). Used when ripgrep `--files` yields nothing
3
+ * (missing binary, spawn failure, or environment quirks) but the tree should still be searchable.
4
+ */
5
+ export declare function listWorkspaceRelativeFilesFsFallback(workspaceRootAbs: string, maxFiles: number): Promise<string[]>;
@@ -0,0 +1,56 @@
1
+ import { createLogger } from "../utils/logger/index.js";
2
+ import { init_logger } from "../utils/logger.js";
3
+ import { join, relative } from "node:path";
4
+ import { readdir } from "node:fs/promises";
5
+ //#region src/gateway/workspace-fs-file-list.ts
6
+ init_logger();
7
+ const log = createLogger("WorkspaceFsFileList");
8
+ /** Match ripgrep `--glob` excludes in `runRipgrepListFiles` (keep list aligned where possible). */
9
+ const SKIP_DIR_NAMES = new Set(["node_modules", ".git"]);
10
+ /**
11
+ * Recursive workspace file list (relative POSIX paths). Used when ripgrep `--files` yields nothing
12
+ * (missing binary, spawn failure, or environment quirks) but the tree should still be searchable.
13
+ */
14
+ async function listWorkspaceRelativeFilesFsFallback(workspaceRootAbs, maxFiles) {
15
+ const cap = Math.min(Math.max(maxFiles, 1), 2e5);
16
+ const out = [];
17
+ async function walk(dirAbs) {
18
+ if (out.length >= cap) return;
19
+ let entries;
20
+ try {
21
+ entries = await readdir(dirAbs, { withFileTypes: true });
22
+ } catch (err) {
23
+ log.debug({
24
+ err,
25
+ dirAbs
26
+ }, "fs list: readdir failed");
27
+ return;
28
+ }
29
+ for (const e of entries) {
30
+ if (out.length >= cap) return;
31
+ if (e.name.startsWith(".")) continue;
32
+ const full = join(dirAbs, e.name);
33
+ if (e.isDirectory()) {
34
+ if (SKIP_DIR_NAMES.has(e.name)) continue;
35
+ await walk(full);
36
+ } else if (e.isFile()) {
37
+ const rel = relative(workspaceRootAbs, full);
38
+ if (!rel || rel.startsWith("..")) continue;
39
+ out.push(rel.split("\\").join("/"));
40
+ }
41
+ }
42
+ }
43
+ try {
44
+ await walk(workspaceRootAbs);
45
+ } catch (err) {
46
+ log.warn({
47
+ err,
48
+ workspaceRootAbs
49
+ }, "fs list: walk failed");
50
+ }
51
+ return out;
52
+ }
53
+ //#endregion
54
+ export { listWorkspaceRelativeFilesFsFallback };
55
+
56
+ //# sourceMappingURL=workspace-fs-file-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-fs-file-list.js","names":[],"sources":["../../../src/gateway/workspace-fs-file-list.ts"],"sourcesContent":["import { readdir } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\n\nimport { createLogger } from '../utils/logger.js';\n\nconst log = createLogger('WorkspaceFsFileList');\n\n/** Match ripgrep `--glob` excludes in `runRipgrepListFiles` (keep list aligned where possible). */\nconst SKIP_DIR_NAMES = new Set(['node_modules', '.git']);\n\n/**\n * Recursive workspace file list (relative POSIX paths). Used when ripgrep `--files` yields nothing\n * (missing binary, spawn failure, or environment quirks) but the tree should still be searchable.\n */\nexport async function listWorkspaceRelativeFilesFsFallback(\n workspaceRootAbs: string,\n maxFiles: number,\n): Promise<string[]> {\n const cap = Math.min(Math.max(maxFiles, 1), 200_000);\n const out: string[] = [];\n\n async function walk(dirAbs: string): Promise<void> {\n if (out.length >= cap) return;\n let entries;\n try {\n entries = await readdir(dirAbs, { withFileTypes: true });\n } catch (err) {\n log.debug({ err, dirAbs }, 'fs list: readdir failed');\n return;\n }\n for (const e of entries) {\n if (out.length >= cap) return;\n if (e.name.startsWith('.')) continue;\n const full = join(dirAbs, e.name);\n if (e.isDirectory()) {\n if (SKIP_DIR_NAMES.has(e.name)) continue;\n await walk(full);\n } else if (e.isFile()) {\n const rel = relative(workspaceRootAbs, full);\n if (!rel || rel.startsWith('..')) continue;\n out.push(rel.split('\\\\').join('/'));\n }\n }\n }\n\n try {\n await walk(workspaceRootAbs);\n } catch (err) {\n log.warn({ err, workspaceRootAbs }, 'fs list: walk failed');\n }\n return out;\n}\n"],"mappings":";;;;;aAGkD;AAElD,MAAM,MAAM,aAAa,sBAAsB;;AAG/C,MAAM,iBAAiB,IAAI,IAAI,CAAC,gBAAgB,OAAO,CAAC;;;;;AAMxD,eAAsB,qCACpB,kBACA,UACmB;CACnB,MAAM,MAAM,KAAK,IAAI,KAAK,IAAI,UAAU,EAAE,EAAE,IAAQ;CACpD,MAAM,MAAgB,EAAE;CAExB,eAAe,KAAK,QAA+B;AACjD,MAAI,IAAI,UAAU,IAAK;EACvB,IAAI;AACJ,MAAI;AACF,aAAU,MAAM,QAAQ,QAAQ,EAAE,eAAe,MAAM,CAAC;WACjD,KAAK;AACZ,OAAI,MAAM;IAAE;IAAK;IAAQ,EAAE,0BAA0B;AACrD;;AAEF,OAAK,MAAM,KAAK,SAAS;AACvB,OAAI,IAAI,UAAU,IAAK;AACvB,OAAI,EAAE,KAAK,WAAW,IAAI,CAAE;GAC5B,MAAM,OAAO,KAAK,QAAQ,EAAE,KAAK;AACjC,OAAI,EAAE,aAAa,EAAE;AACnB,QAAI,eAAe,IAAI,EAAE,KAAK,CAAE;AAChC,UAAM,KAAK,KAAK;cACP,EAAE,QAAQ,EAAE;IACrB,MAAM,MAAM,SAAS,kBAAkB,KAAK;AAC5C,QAAI,CAAC,OAAO,IAAI,WAAW,KAAK,CAAE;AAClC,QAAI,KAAK,IAAI,MAAM,KAAK,CAAC,KAAK,IAAI,CAAC;;;;AAKzC,KAAI;AACF,QAAM,KAAK,iBAAiB;UACrB,KAAK;AACZ,MAAI,KAAK;GAAE;GAAK;GAAkB,EAAE,uBAAuB;;AAE7D,QAAO"}
@@ -1,6 +1,6 @@
1
1
  import { init_agent_scope, resolveAgentBootstrapDir, resolveDefaultAgentId } from "../agent/agent-scope.js";
2
- import { getWorkspacePath, init_schema } from "../config/schema.js";
3
2
  import { WORKSPACE_FILES, init_paths } from "../config/paths.js";
3
+ import { getWorkspacePath, init_schema } from "../config/schema.js";
4
4
  import { join } from "node:path";
5
5
  //#region src/gateway/workspace-heartbeat-path.ts
6
6
  init_schema();
@@ -7,3 +7,8 @@ export interface WorkspaceSearchHit {
7
7
  }
8
8
  /** Run ripgrep in a directory (absolute path). Returns empty array if rg fails to start. */
9
9
  export declare function runRipgrepInDirectory(query: string, dirAbsPath: string): Promise<WorkspaceSearchHit[]>;
10
+ /**
11
+ * List workspace-relative file paths via ripgrep `--files` (respects .gitignore; fast on large trees).
12
+ * Returns POSIX paths relative to `dirAbsPath`.
13
+ */
14
+ export declare function runRipgrepListFiles(dirAbsPath: string): Promise<string[]>;
@@ -1,11 +1,28 @@
1
+ import { createLogger } from "../utils/logger/index.js";
2
+ import { init_logger } from "../utils/logger.js";
3
+ import { existsSync } from "node:fs";
1
4
  import { spawn } from "node:child_process";
2
5
  //#region src/gateway/workspace-ripgrep.ts
6
+ init_logger();
7
+ const log = createLogger("WorkspaceRipgrep");
8
+ function isEnoent(err) {
9
+ return err !== null && typeof err === "object" && err.code === "ENOENT";
10
+ }
11
+ let cachedRipgrepBin;
12
+ /**
13
+ * Prefer `@vscode/ripgrep` when its postinstall placed `bin/rg`; otherwise use `rg` on PATH.
14
+ * (Bundled path can be ENOENT if postinstall was skipped or the binary was never downloaded.)
15
+ */
3
16
  async function resolveRipgrepBinary() {
17
+ if (cachedRipgrepBin) return cachedRipgrepBin;
18
+ let bin = "rg";
4
19
  try {
5
20
  const { rgPath } = await import("@vscode/ripgrep");
6
- if (typeof rgPath === "string" && rgPath.length > 0) return rgPath;
21
+ if (typeof rgPath === "string" && rgPath.length > 0 && existsSync(rgPath)) bin = rgPath;
22
+ else if (typeof rgPath === "string" && rgPath.length > 0) log.debug({ rgPath }, "@vscode/ripgrep binary not on disk; will try rg on PATH");
7
23
  } catch {}
8
- return "rg";
24
+ cachedRipgrepBin = bin;
25
+ return bin;
9
26
  }
10
27
  /** Run ripgrep in a directory (absolute path). Returns empty array if rg fails to start. */
11
28
  function runRipgrepInDirectory(query, dirAbsPath) {
@@ -52,11 +69,78 @@ function runRipgrepInDirectory(query, dirAbsPath) {
52
69
  }
53
70
  });
54
71
  rg.on("close", () => resolve(results));
55
- rg.on("error", () => resolve([]));
72
+ rg.on("error", (err) => {
73
+ if (!isEnoent(err)) log.warn({
74
+ err,
75
+ query,
76
+ dir: dirAbsPath,
77
+ rg: rgExecutable
78
+ }, "ripgrep in-directory: spawn failed");
79
+ else log.debug({
80
+ dir: dirAbsPath,
81
+ rg: rgExecutable
82
+ }, "ripgrep not on PATH; skipping in-directory search");
83
+ resolve([]);
84
+ });
85
+ });
86
+ })();
87
+ }
88
+ /**
89
+ * List workspace-relative file paths via ripgrep `--files` (respects .gitignore; fast on large trees).
90
+ * Returns POSIX paths relative to `dirAbsPath`.
91
+ */
92
+ function runRipgrepListFiles(dirAbsPath) {
93
+ return (async () => {
94
+ const rgExecutable = await resolveRipgrepBinary();
95
+ return await new Promise((resolve) => {
96
+ const rg = spawn(rgExecutable, [
97
+ "--files",
98
+ "--glob",
99
+ "!**/node_modules/**",
100
+ "--glob",
101
+ "!.git/**",
102
+ "."
103
+ ], {
104
+ shell: false,
105
+ cwd: dirAbsPath
106
+ });
107
+ const lines = [];
108
+ let buffer = "";
109
+ rg.stdout.on("data", (data) => {
110
+ buffer += data.toString();
111
+ const parts = buffer.split(/\r?\n/);
112
+ buffer = parts.pop() ?? "";
113
+ for (const line of parts) {
114
+ const t = line.trim();
115
+ if (t) lines.push(t.replace(/\\/g, "/"));
116
+ }
117
+ });
118
+ rg.on("close", (code) => {
119
+ const tail = buffer.trim();
120
+ if (tail) lines.push(tail.replace(/\\/g, "/"));
121
+ if (code !== 0 && lines.length === 0) log.debug({
122
+ code,
123
+ cwd: dirAbsPath,
124
+ rg: rgExecutable
125
+ }, "ripgrep --files: non-zero exit, no output (using fs fallback if any)");
126
+ resolve(lines);
127
+ });
128
+ rg.on("error", (err) => {
129
+ if (isEnoent(err)) log.debug({
130
+ cwd: dirAbsPath,
131
+ rg: rgExecutable
132
+ }, "ripgrep binary not found; workspace file search will use fs fallback when needed");
133
+ else log.warn({
134
+ err,
135
+ cwd: dirAbsPath,
136
+ rg: rgExecutable
137
+ }, "ripgrep --files failed to start");
138
+ resolve([]);
139
+ });
56
140
  });
57
141
  })();
58
142
  }
59
143
  //#endregion
60
- export { runRipgrepInDirectory };
144
+ export { runRipgrepInDirectory, runRipgrepListFiles };
61
145
 
62
146
  //# sourceMappingURL=workspace-ripgrep.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-ripgrep.js","names":[],"sources":["../../../src/gateway/workspace-ripgrep.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\n\nexport interface WorkspaceSearchHit {\n filePath: string;\n lineNumber: number;\n lineContent: string;\n matchStart: number;\n matchEnd: number;\n}\n\nasync function resolveRipgrepBinary(): Promise<string | null> {\n try {\n const { rgPath } = await import('@vscode/ripgrep');\n if (typeof rgPath === 'string' && rgPath.length > 0) {\n return rgPath;\n }\n } catch {\n // pnpm may skip @vscode/ripgrep postinstall; package dir can be missing.\n }\n return 'rg';\n}\n\n/** Run ripgrep in a directory (absolute path). Returns empty array if rg fails to start. */\nexport function runRipgrepInDirectory(query: string, dirAbsPath: string): Promise<WorkspaceSearchHit[]> {\n return (async () => {\n const rgExecutable = await resolveRipgrepBinary();\n\n return await new Promise<WorkspaceSearchHit[]>((resolve) => {\n const args = [\n '--json',\n '--smart-case',\n '--max-count',\n '50',\n '--glob',\n '*.md',\n '--glob',\n '*.txt',\n query,\n dirAbsPath,\n ];\n\n const rg = spawn(rgExecutable, args, { shell: false });\n const results: WorkspaceSearchHit[] = [];\n let buffer = '';\n\n rg.stdout.on('data', (data: Buffer) => {\n buffer += data.toString();\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const parsed = JSON.parse(line) as {\n type?: string;\n data?: {\n path?: { text?: string };\n lines?: { text?: string };\n line_number?: number;\n submatches?: Array<{ start?: number; end?: number }>;\n };\n };\n if (parsed.type === 'match' && parsed.data) {\n const d = parsed.data;\n const pathText = d.path?.text ?? '';\n const lineContent = d.lines?.text ?? '';\n const lineNumber = d.line_number ?? 0;\n const sm = d.submatches?.[0];\n results.push({\n filePath: pathText,\n lineNumber,\n lineContent: lineContent.trimEnd(),\n matchStart: sm?.start ?? 0,\n matchEnd: sm?.end ?? 0,\n });\n }\n } catch {\n /* skip */\n }\n }\n });\n\n rg.on('close', () => resolve(results));\n rg.on('error', () => resolve([]));\n });\n })();\n}\n"],"mappings":";;AAUA,eAAe,uBAA+C;AAC5D,KAAI;EACF,MAAM,EAAE,WAAW,MAAM,OAAO;AAChC,MAAI,OAAO,WAAW,YAAY,OAAO,SAAS,EAChD,QAAO;SAEH;AAGR,QAAO;;;AAIT,SAAgB,sBAAsB,OAAe,YAAmD;AACtG,SAAQ,YAAY;EAClB,MAAM,eAAe,MAAM,sBAAsB;AAEjD,SAAO,MAAM,IAAI,SAA+B,YAAY;GAc1D,MAAM,KAAK,MAAM,cAbJ;IACX;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,EAEoC,EAAE,OAAO,OAAO,CAAC;GACtD,MAAM,UAAgC,EAAE;GACxC,IAAI,SAAS;AAEb,MAAG,OAAO,GAAG,SAAS,SAAiB;AACrC,cAAU,KAAK,UAAU;IACzB,MAAM,QAAQ,OAAO,MAAM,KAAK;AAChC,aAAS,MAAM,KAAK,IAAI;AAExB,SAAK,MAAM,QAAQ,OAAO;AACxB,SAAI,CAAC,KAAK,MAAM,CAAE;AAClB,SAAI;MACF,MAAM,SAAS,KAAK,MAAM,KAAK;AAS/B,UAAI,OAAO,SAAS,WAAW,OAAO,MAAM;OAC1C,MAAM,IAAI,OAAO;OACjB,MAAM,WAAW,EAAE,MAAM,QAAQ;OACjC,MAAM,cAAc,EAAE,OAAO,QAAQ;OACrC,MAAM,aAAa,EAAE,eAAe;OACpC,MAAM,KAAK,EAAE,aAAa;AAC1B,eAAQ,KAAK;QACX,UAAU;QACV;QACA,aAAa,YAAY,SAAS;QAClC,YAAY,IAAI,SAAS;QACzB,UAAU,IAAI,OAAO;QACtB,CAAC;;aAEE;;KAIV;AAEF,MAAG,GAAG,eAAe,QAAQ,QAAQ,CAAC;AACtC,MAAG,GAAG,eAAe,QAAQ,EAAE,CAAC,CAAC;IACjC;KACA"}
1
+ {"version":3,"file":"workspace-ripgrep.js","names":[],"sources":["../../../src/gateway/workspace-ripgrep.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\n\nimport { createLogger } from '../utils/logger.js';\n\nconst log = createLogger('WorkspaceRipgrep');\n\nfunction isEnoent(err: unknown): boolean {\n return err !== null && typeof err === 'object' && (err as NodeJS.ErrnoException).code === 'ENOENT';\n}\n\nlet cachedRipgrepBin: string | undefined;\n\n/**\n * Prefer `@vscode/ripgrep` when its postinstall placed `bin/rg`; otherwise use `rg` on PATH.\n * (Bundled path can be ENOENT if postinstall was skipped or the binary was never downloaded.)\n */\nasync function resolveRipgrepBinary(): Promise<string> {\n if (cachedRipgrepBin) return cachedRipgrepBin;\n let bin = 'rg';\n try {\n const { rgPath } = await import('@vscode/ripgrep');\n if (typeof rgPath === 'string' && rgPath.length > 0 && existsSync(rgPath)) {\n bin = rgPath;\n } else if (typeof rgPath === 'string' && rgPath.length > 0) {\n log.debug({ rgPath }, '@vscode/ripgrep binary not on disk; will try rg on PATH');\n }\n } catch {\n // pnpm may skip @vscode/ripgrep postinstall; package dir can be missing.\n }\n cachedRipgrepBin = bin;\n return bin;\n}\n\nexport interface WorkspaceSearchHit {\n filePath: string;\n lineNumber: number;\n lineContent: string;\n matchStart: number;\n matchEnd: number;\n}\n\n/** Run ripgrep in a directory (absolute path). Returns empty array if rg fails to start. */\nexport function runRipgrepInDirectory(query: string, dirAbsPath: string): Promise<WorkspaceSearchHit[]> {\n return (async () => {\n const rgExecutable = await resolveRipgrepBinary();\n\n return await new Promise<WorkspaceSearchHit[]>((resolve) => {\n const args = [\n '--json',\n '--smart-case',\n '--max-count',\n '50',\n '--glob',\n '*.md',\n '--glob',\n '*.txt',\n query,\n dirAbsPath,\n ];\n\n const rg = spawn(rgExecutable, args, { shell: false });\n const results: WorkspaceSearchHit[] = [];\n let buffer = '';\n\n rg.stdout.on('data', (data: Buffer) => {\n buffer += data.toString();\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const parsed = JSON.parse(line) as {\n type?: string;\n data?: {\n path?: { text?: string };\n lines?: { text?: string };\n line_number?: number;\n submatches?: Array<{ start?: number; end?: number }>;\n };\n };\n if (parsed.type === 'match' && parsed.data) {\n const d = parsed.data;\n const pathText = d.path?.text ?? '';\n const lineContent = d.lines?.text ?? '';\n const lineNumber = d.line_number ?? 0;\n const sm = d.submatches?.[0];\n results.push({\n filePath: pathText,\n lineNumber,\n lineContent: lineContent.trimEnd(),\n matchStart: sm?.start ?? 0,\n matchEnd: sm?.end ?? 0,\n });\n }\n } catch {\n /* skip */\n }\n }\n });\n\n rg.on('close', () => resolve(results));\n rg.on('error', (err) => {\n if (!isEnoent(err)) {\n log.warn({ err, query, dir: dirAbsPath, rg: rgExecutable }, 'ripgrep in-directory: spawn failed');\n } else {\n log.debug({ dir: dirAbsPath, rg: rgExecutable }, 'ripgrep not on PATH; skipping in-directory search');\n }\n resolve([]);\n });\n });\n })();\n}\n\n/**\n * List workspace-relative file paths via ripgrep `--files` (respects .gitignore; fast on large trees).\n * Returns POSIX paths relative to `dirAbsPath`.\n */\nexport function runRipgrepListFiles(dirAbsPath: string): Promise<string[]> {\n return (async () => {\n const rgExecutable = await resolveRipgrepBinary();\n\n return await new Promise<string[]>((resolve) => {\n const args = ['--files', '--glob', '!**/node_modules/**', '--glob', '!.git/**', '.'];\n const rg = spawn(rgExecutable, args, { shell: false, cwd: dirAbsPath });\n const lines: string[] = [];\n let buffer = '';\n\n rg.stdout.on('data', (data: Buffer) => {\n buffer += data.toString();\n const parts = buffer.split(/\\r?\\n/);\n buffer = parts.pop() ?? '';\n for (const line of parts) {\n const t = line.trim();\n if (t) lines.push(t.replace(/\\\\/g, '/'));\n }\n });\n\n rg.on('close', (code) => {\n const tail = buffer.trim();\n if (tail) lines.push(tail.replace(/\\\\/g, '/'));\n if (code !== 0 && lines.length === 0) {\n log.debug({ code, cwd: dirAbsPath, rg: rgExecutable }, 'ripgrep --files: non-zero exit, no output (using fs fallback if any)');\n }\n resolve(lines);\n });\n rg.on('error', (err) => {\n if (isEnoent(err)) {\n log.debug(\n { cwd: dirAbsPath, rg: rgExecutable },\n 'ripgrep binary not found; workspace file search will use fs fallback when needed',\n );\n } else {\n log.warn({ err, cwd: dirAbsPath, rg: rgExecutable }, 'ripgrep --files failed to start');\n }\n resolve([]);\n });\n });\n })();\n}\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export type UpdateChannel = 'stable' | 'beta' | 'dev';
2
+ export declare const DEFAULT_PACKAGE_CHANNEL: UpdateChannel;
3
+ export declare const DEFAULT_GIT_CHANNEL: UpdateChannel;
4
+ /**
5
+ * Map update channel to the npm dist-tag used for querying registry.
6
+ * stable → latest, beta → beta, dev → dev
7
+ */
8
+ export declare function channelToNpmTag(channel: UpdateChannel): string;
9
+ /** Normalize a user-provided string to a valid UpdateChannel, or null. */
10
+ export declare function normalizeUpdateChannel(value?: string | null): UpdateChannel | null;
11
+ /** Return true if a version/tag string contains a beta prerelease identifier. */
12
+ export declare function isBetaVersion(version: string): boolean;
13
+ /** Return true if a version string is a stable release (no beta marker in this heuristic). */
14
+ export declare function isStableVersion(version: string): boolean;
@@ -0,0 +1,30 @@
1
+ //#region src/infra/update-channels.ts
2
+ const DEFAULT_PACKAGE_CHANNEL = "stable";
3
+ const DEFAULT_GIT_CHANNEL = "dev";
4
+ /**
5
+ * Map update channel to the npm dist-tag used for querying registry.
6
+ * stable → latest, beta → beta, dev → dev
7
+ */
8
+ function channelToNpmTag(channel) {
9
+ if (channel === "beta") return "beta";
10
+ if (channel === "dev") return "dev";
11
+ return "latest";
12
+ }
13
+ /** Normalize a user-provided string to a valid UpdateChannel, or null. */
14
+ function normalizeUpdateChannel(value) {
15
+ const normalized = value?.trim().toLowerCase();
16
+ if (normalized === "stable" || normalized === "beta" || normalized === "dev") return normalized;
17
+ return null;
18
+ }
19
+ /** Return true if a version/tag string contains a beta prerelease identifier. */
20
+ function isBetaVersion(version) {
21
+ return /(?:^|[.-])beta(?:[.-]|$)/i.test(version);
22
+ }
23
+ /** Return true if a version string is a stable release (no beta marker in this heuristic). */
24
+ function isStableVersion(version) {
25
+ return !isBetaVersion(version);
26
+ }
27
+ //#endregion
28
+ export { DEFAULT_GIT_CHANNEL, DEFAULT_PACKAGE_CHANNEL, channelToNpmTag, isBetaVersion, isStableVersion, normalizeUpdateChannel };
29
+
30
+ //# sourceMappingURL=update-channels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-channels.js","names":[],"sources":["../../../src/infra/update-channels.ts"],"sourcesContent":["// src/infra/update-channels.ts\n\nexport type UpdateChannel = 'stable' | 'beta' | 'dev';\n\nexport const DEFAULT_PACKAGE_CHANNEL: UpdateChannel = 'stable';\nexport const DEFAULT_GIT_CHANNEL: UpdateChannel = 'dev';\n\n/**\n * Map update channel to the npm dist-tag used for querying registry.\n * stable → latest, beta → beta, dev → dev\n */\nexport function channelToNpmTag(channel: UpdateChannel): string {\n if (channel === 'beta') return 'beta';\n if (channel === 'dev') return 'dev';\n return 'latest';\n}\n\n/** Normalize a user-provided string to a valid UpdateChannel, or null. */\nexport function normalizeUpdateChannel(value?: string | null): UpdateChannel | null {\n const normalized = value?.trim().toLowerCase();\n if (normalized === 'stable' || normalized === 'beta' || normalized === 'dev') {\n return normalized;\n }\n return null;\n}\n\n/** Return true if a version/tag string contains a beta prerelease identifier. */\nexport function isBetaVersion(version: string): boolean {\n return /(?:^|[.-])beta(?:[.-]|$)/i.test(version);\n}\n\n/** Return true if a version string is a stable release (no beta marker in this heuristic). */\nexport function isStableVersion(version: string): boolean {\n return !isBetaVersion(version);\n}\n"],"mappings":";AAIA,MAAa,0BAAyC;AACtD,MAAa,sBAAqC;;;;;AAMlD,SAAgB,gBAAgB,SAAgC;AAC9D,KAAI,YAAY,OAAQ,QAAO;AAC/B,KAAI,YAAY,MAAO,QAAO;AAC9B,QAAO;;;AAIT,SAAgB,uBAAuB,OAA6C;CAClF,MAAM,aAAa,OAAO,MAAM,CAAC,aAAa;AAC9C,KAAI,eAAe,YAAY,eAAe,UAAU,eAAe,MACrE,QAAO;AAET,QAAO;;;AAIT,SAAgB,cAAc,SAA0B;AACtD,QAAO,4BAA4B,KAAK,QAAQ;;;AAIlD,SAAgB,gBAAgB,SAA0B;AACxD,QAAO,CAAC,cAAc,QAAQ"}