@xopcai/xopc 0.0.86 → 0.0.88

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 (658) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/adapters/cli-login.js +3 -3
  3. package/dist/extensions/feishu/src/adapters/cli-login.js.map +1 -1
  4. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  5. package/dist/extensions/feishu/src/workflow-progress.js +1 -1
  6. package/dist/extensions/telegram/src/delivery-chat-id.d.ts +1 -1
  7. package/dist/extensions/telegram/src/delivery-chat-id.js +1 -1
  8. package/dist/extensions/telegram/src/delivery-chat-id.js.map +1 -1
  9. package/dist/extensions/telegram/src/plugin.js +1 -1
  10. package/dist/extensions/telegram/src/routing-integration.js +3 -2
  11. package/dist/extensions/telegram/src/routing-integration.js.map +1 -1
  12. package/dist/extensions/telegram/src/workflow-progress.js +1 -1
  13. package/dist/extensions/telegram/xopc.extension.json +1 -1
  14. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js +2 -2
  15. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js.map +1 -1
  16. package/dist/extensions/weixin/src/api/api.js +3 -3
  17. package/dist/extensions/weixin/src/api/api.js.map +1 -1
  18. package/dist/extensions/weixin/src/auth/accounts.js +12 -12
  19. package/dist/extensions/weixin/src/auth/accounts.js.map +1 -1
  20. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  21. package/dist/extensions/weixin/src/delivery-to.js +2 -2
  22. package/dist/extensions/weixin/src/delivery-to.js.map +1 -1
  23. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  24. package/dist/extensions/weixin/src/messaging/debug-mode.js +5 -5
  25. package/dist/extensions/weixin/src/messaging/debug-mode.js.map +1 -1
  26. package/dist/extensions/weixin/src/messaging/inbound.js +11 -11
  27. package/dist/extensions/weixin/src/messaging/inbound.js.map +1 -1
  28. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  29. package/dist/extensions/weixin/src/plugin.js +1 -1
  30. package/dist/extensions/weixin/src/storage/sync-buf.js +4 -4
  31. package/dist/extensions/weixin/src/storage/sync-buf.js.map +1 -1
  32. package/dist/extensions/weixin/src/workflow-progress.d.ts +1 -1
  33. package/dist/extensions/weixin/src/workflow-progress.js +1 -1
  34. package/dist/extensions/weixin/src/workflow-progress.js.map +1 -1
  35. package/dist/gateway/static/root/assets/agents-CRxETUZx.js +222 -0
  36. package/dist/gateway/static/root/assets/{apps-page-DrfytjOb.js → apps-page-wKWf3l57.js} +1 -1
  37. package/dist/gateway/static/root/assets/channels-settings-DDbqVNkx.js +1 -0
  38. package/dist/gateway/static/root/assets/{channels-status-swr-Bs5kMCMI.js → channels-status-swr-DIsl75Y3.js} +1 -1
  39. package/dist/gateway/static/root/assets/copy-SxMW6Xpc.js +1 -0
  40. package/dist/gateway/static/root/assets/{cron-api-BuVcZ5zR.js → cron-api-N9hvuRrn.js} +1 -1
  41. package/dist/gateway/static/root/assets/{cron-page-BMrloeFH.js → cron-page-tlNGNxhP.js} +1 -1
  42. package/dist/gateway/static/root/assets/{dist-CKU1OOTf.js → dist-CJwfHYvT.js} +1 -1
  43. package/dist/gateway/static/root/assets/{extension-debug-page-BdW_46sN.js → extension-debug-page-BVJohZoZ.js} +1 -1
  44. package/dist/gateway/static/root/assets/{extension-page-DW47KI82.js → extension-page-BT2tmElC.js} +1 -1
  45. package/dist/gateway/static/root/assets/extension-settings-page-BSS47c2j.js +1 -0
  46. package/dist/gateway/static/root/assets/{fetch-B2MYHbWg.js → fetch-BaFNUtkE.js} +1 -1
  47. package/dist/gateway/static/root/assets/{field-primitives-DPG-oJmx.js → field-primitives-QwYEq6Hz.js} +1 -1
  48. package/dist/gateway/static/root/assets/{heartbeat-config-api-C8dNts9i.js → heartbeat-config-api-BVSidEDJ.js} +1 -1
  49. package/dist/gateway/static/root/assets/index-CqZzHNEg.css +1 -0
  50. package/dist/gateway/static/root/assets/{index-BmVYculr.js → index-qNrVJp-y.js} +97 -95
  51. package/dist/gateway/static/root/assets/{logs-page-sTsVWz0X.js → logs-page-DDonPVLn.js} +1 -1
  52. package/dist/gateway/static/root/assets/sessions-page-DKt-Wmib.js +1 -0
  53. package/dist/gateway/static/root/assets/{settings-form-section-DuvRQW--.js → settings-form-section-B8N3A3Zo.js} +1 -1
  54. package/dist/gateway/static/root/assets/settings-page-DcJjvvw4.js +3 -0
  55. package/dist/gateway/static/root/assets/{share-preview-page-BtG2kLDh.js → share-preview-page-Q7KqkO-u.js} +1 -1
  56. package/dist/gateway/static/root/assets/skills-page-DuJ4BTO3.js +2 -0
  57. package/dist/gateway/static/root/assets/{theme-store-DryYl3qD.js → theme-store-BbRc5ugR.js} +1 -1
  58. package/dist/gateway/static/root/assets/url-D6jvVYIA.js +7 -0
  59. package/dist/gateway/static/root/assets/{utils-BY7bU1DT.js → utils-CxDGduqK.js} +1 -1
  60. package/dist/gateway/static/root/assets/voice-api-key-field-CTyHz7L_.js +1 -0
  61. package/dist/gateway/static/root/assets/workflows-page-GacJ41Fv.js +27 -0
  62. package/dist/gateway/static/root/index.html +6 -5
  63. package/dist/package.js +1 -1
  64. package/dist/src/agent/agent-manager.js +7 -7
  65. package/dist/src/agent/agent-scope.d.ts +4 -0
  66. package/dist/src/agent/agent-scope.js +53 -10
  67. package/dist/src/agent/agent-scope.js.map +1 -1
  68. package/dist/src/agent/bootstrap/filter-bootstrap-files.js +2 -1
  69. package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
  70. package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
  71. package/dist/src/agent/child-agent-factory.d.ts +15 -0
  72. package/dist/src/agent/child-agent-factory.js +35 -2
  73. package/dist/src/agent/child-agent-factory.js.map +1 -1
  74. package/dist/src/agent/client-error-format.d.ts +20 -0
  75. package/dist/src/agent/client-error-format.js +97 -0
  76. package/dist/src/agent/client-error-format.js.map +1 -0
  77. package/dist/src/agent/context/workspace-seed.js +2 -2
  78. package/dist/src/agent/embedded/run-turn.js +23 -4
  79. package/dist/src/agent/embedded/run-turn.js.map +1 -1
  80. package/dist/src/agent/embedded/session-tool-result-guard.js +2 -1
  81. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  82. package/dist/src/agent/embedded/tool-result-truncation.js +2 -1
  83. package/dist/src/agent/embedded/tool-result-truncation.js.map +1 -1
  84. package/dist/src/agent/fallback/candidates.js +2 -2
  85. package/dist/src/agent/fallback/candidates.js.map +1 -1
  86. package/dist/src/agent/goals/goal-locale.d.ts +1 -1
  87. package/dist/src/agent/goals/goal-run-store.js +4 -4
  88. package/dist/src/agent/goals/persistent-goal-apis.d.ts +0 -2
  89. package/dist/src/agent/goals/persistent-goal-service.js +1 -2
  90. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
  91. package/dist/src/agent/goals/post-turn.js +2 -2
  92. package/dist/src/agent/image/generation/normalization.js +2 -12
  93. package/dist/src/agent/image/generation/normalization.js.map +1 -1
  94. package/dist/src/agent/image/generation/provider-registry.d.ts +4 -8
  95. package/dist/src/agent/image/generation/provider-registry.js.map +1 -1
  96. package/dist/src/agent/image/generation/runtime.d.ts +2 -2
  97. package/dist/src/agent/image/generation/runtime.js.map +1 -1
  98. package/dist/src/agent/image/generation/types.d.ts +0 -18
  99. package/dist/src/agent/image/image-helpers.js +6 -1
  100. package/dist/src/agent/image/image-helpers.js.map +1 -1
  101. package/dist/src/agent/image/index.d.ts +1 -1
  102. package/dist/src/agent/image/load-image-media.js +2 -2
  103. package/dist/src/agent/inbound/inbound-loop.d.ts +5 -0
  104. package/dist/src/agent/inbound/inbound-loop.js +41 -10
  105. package/dist/src/agent/inbound/inbound-loop.js.map +1 -1
  106. package/dist/src/agent/inbound/turn-dispatcher.d.ts +4 -0
  107. package/dist/src/agent/inbound/turn-dispatcher.js +7 -5
  108. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  109. package/dist/src/agent/ipc/bus.js +1 -1
  110. package/dist/src/agent/ipc/inbox.js +2 -2
  111. package/dist/src/agent/ipc/socket.js +1 -1
  112. package/dist/src/agent/mcp/bundle-mcp-materialize.js +2 -1
  113. package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
  114. package/dist/src/agent/mcp/bundle-mcp-names.js +2 -1
  115. package/dist/src/agent/mcp/bundle-mcp-names.js.map +1 -1
  116. package/dist/src/agent/mcp/bundle-mcp-runtime.js +2 -1
  117. package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
  118. package/dist/src/agent/mcp/mcp-transport-config.js +2 -1
  119. package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
  120. package/dist/src/agent/mcp/mcp-transport.js +2 -1
  121. package/dist/src/agent/mcp/mcp-transport.js.map +1 -1
  122. package/dist/src/agent/media-generation/runtime-shared.js +2 -9
  123. package/dist/src/agent/media-generation/runtime-shared.js.map +1 -1
  124. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  125. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  126. package/dist/src/agent/memory/dreaming/events.js +1 -1
  127. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  128. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  129. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  130. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  131. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  132. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  133. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  134. package/dist/src/agent/messaging/command-handler.d.ts +6 -0
  135. package/dist/src/agent/messaging/command-handler.js +5 -0
  136. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  137. package/dist/src/agent/models/manager.js +1 -1
  138. package/dist/src/agent/orchestration/llm-turn-retry.d.ts +2 -0
  139. package/dist/src/agent/orchestration/llm-turn-retry.js +9 -1
  140. package/dist/src/agent/orchestration/llm-turn-retry.js.map +1 -1
  141. package/dist/src/agent/prompt/safety.d.ts +0 -7
  142. package/dist/src/agent/prompt/safety.js +1 -20
  143. package/dist/src/agent/prompt/safety.js.map +1 -1
  144. package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
  145. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  146. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  147. package/dist/src/agent/sandbox/path-policy.js +2 -2
  148. package/dist/src/agent/service/build-direct-message-content.js +2 -2
  149. package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
  150. package/dist/src/agent/service/direct-turn-helpers.d.ts +3 -1
  151. package/dist/src/agent/service/direct-turn-helpers.js +6 -1
  152. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  153. package/dist/src/agent/service/process-direct-one-shot.d.ts +4 -0
  154. package/dist/src/agent/service/process-direct-one-shot.js +15 -2
  155. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  156. package/dist/src/agent/service/process-direct-streaming.d.ts +4 -0
  157. package/dist/src/agent/service/process-direct-streaming.js +53 -7
  158. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  159. package/dist/src/agent/service/webchat-tts.d.ts +1 -2
  160. package/dist/src/agent/service/webchat-tts.js +2 -2
  161. package/dist/src/agent/service/webchat-tts.js.map +1 -1
  162. package/dist/src/agent/service.d.ts +8 -0
  163. package/dist/src/agent/service.js +25 -5
  164. package/dist/src/agent/service.js.map +1 -1
  165. package/dist/src/agent/session/session-inspector.js +1 -1
  166. package/dist/src/agent/skills/config.js +1 -1
  167. package/dist/src/agent/skills/hub-hash.js +2 -2
  168. package/dist/src/agent/skills/hub-lock.js +1 -1
  169. package/dist/src/agent/skills/hub-pull.js +2 -2
  170. package/dist/src/agent/skills/index.js +1 -1
  171. package/dist/src/agent/skills/managed-store.js +1 -1
  172. package/dist/src/agent/skills/scanner.js +1 -1
  173. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  174. package/dist/src/agent/skills/skill-manager.js +1 -1
  175. package/dist/src/agent/tools/create-share-tool.js +27 -20
  176. package/dist/src/agent/tools/create-share-tool.js.map +1 -1
  177. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  178. package/dist/src/agent/tools/factory.js +2 -2
  179. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  180. package/dist/src/agent/tools/index.d.ts +0 -1
  181. package/dist/src/agent/tools/index.js +4 -5
  182. package/dist/src/agent/tools/send-media.js +1 -1
  183. package/dist/src/agent/tools/shell.js +0 -13
  184. package/dist/src/agent/tools/shell.js.map +1 -1
  185. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  186. package/dist/src/agent/tools/workflow-tool.js +70 -16
  187. package/dist/src/agent/tools/workflow-tool.js.map +1 -1
  188. package/dist/src/agent/tools/write.js +1 -1
  189. package/dist/src/agent/workflow/agent-progress.d.ts +5 -0
  190. package/dist/src/agent/workflow/agent-progress.js +65 -0
  191. package/dist/src/agent/workflow/agent-progress.js.map +1 -0
  192. package/dist/src/agent/workflow/builtins/audit-repo.d.ts +1 -1
  193. package/dist/src/agent/workflow/builtins/audit-repo.js +14 -0
  194. package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -1
  195. package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
  196. package/dist/src/agent/workflow/builtins/debug-incident.js +14 -0
  197. package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
  198. package/dist/src/agent/workflow/builtins/implementation-plan.d.ts +12 -0
  199. package/dist/src/agent/workflow/builtins/implementation-plan.js +175 -0
  200. package/dist/src/agent/workflow/builtins/implementation-plan.js.map +1 -0
  201. package/dist/src/agent/workflow/builtins/index.d.ts +3 -1
  202. package/dist/src/agent/workflow/builtins/index.js +11 -1
  203. package/dist/src/agent/workflow/builtins/index.js.map +1 -1
  204. package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +1 -1
  205. package/dist/src/agent/workflow/builtins/multi-perspective-review.js +14 -0
  206. package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -1
  207. package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
  208. package/dist/src/agent/workflow/builtins/pr-review.js +14 -0
  209. package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
  210. package/dist/src/agent/workflow/builtins/release-check.d.ts +11 -0
  211. package/dist/src/agent/workflow/builtins/release-check.js +165 -0
  212. package/dist/src/agent/workflow/builtins/release-check.js.map +1 -0
  213. package/dist/src/agent/workflow/builtins/research.d.ts +1 -1
  214. package/dist/src/agent/workflow/builtins/research.js +14 -0
  215. package/dist/src/agent/workflow/builtins/research.js.map +1 -1
  216. package/dist/src/agent/workflow/catalog.js +1 -1
  217. package/dist/src/agent/workflow/channel-capability.d.ts +3 -3
  218. package/dist/src/agent/workflow/index.d.ts +2 -1
  219. package/dist/src/agent/workflow/index.js +3 -2
  220. package/dist/src/agent/workflow/lint.d.ts +38 -0
  221. package/dist/src/agent/workflow/lint.js +74 -0
  222. package/dist/src/agent/workflow/lint.js.map +1 -0
  223. package/dist/src/agent/workflow/meta-locale.d.ts +12 -0
  224. package/dist/src/agent/workflow/meta-locale.js +62 -0
  225. package/dist/src/agent/workflow/meta-locale.js.map +1 -0
  226. package/dist/src/agent/workflow/parser.js +7 -1
  227. package/dist/src/agent/workflow/parser.js.map +1 -1
  228. package/dist/src/agent/workflow/runtime.d.ts +4 -1
  229. package/dist/src/agent/workflow/runtime.js +88 -8
  230. package/dist/src/agent/workflow/runtime.js.map +1 -1
  231. package/dist/src/agent/workflow/snapshot.js +2 -12
  232. package/dist/src/agent/workflow/snapshot.js.map +1 -1
  233. package/dist/src/agent/workflow/step-labels.d.ts +8 -0
  234. package/dist/src/agent/workflow/step-labels.js +48 -0
  235. package/dist/src/agent/workflow/step-labels.js.map +1 -0
  236. package/dist/src/agent/workflow/subagent-runner.js +46 -1
  237. package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
  238. package/dist/src/agent/workflow/types.d.ts +76 -1
  239. package/dist/src/auth/credentials.d.ts +5 -0
  240. package/dist/src/auth/credentials.js +12 -3
  241. package/dist/src/auth/credentials.js.map +1 -1
  242. package/dist/src/auth/profiles/store.js +1 -1
  243. package/dist/src/auth/sync-provider-auth.js +1 -1
  244. package/dist/src/browser/cache-dir-policy.js +1 -1
  245. package/dist/src/browser/cdp-local-launcher.js +2 -2
  246. package/dist/src/browser/index.js +4 -4
  247. package/dist/src/browser/manager.d.ts +1 -3
  248. package/dist/src/browser/manager.js +0 -6
  249. package/dist/src/browser/manager.js.map +1 -1
  250. package/dist/src/browser/providers/browser-ext-install.d.ts +4 -4
  251. package/dist/src/browser/providers/browser-ext-install.js +41 -88
  252. package/dist/src/browser/providers/browser-ext-install.js.map +1 -1
  253. package/dist/src/browser/providers/cloakbrowser.d.ts +0 -5
  254. package/dist/src/browser/providers/cloakbrowser.js +6 -59
  255. package/dist/src/browser/providers/cloakbrowser.js.map +1 -1
  256. package/dist/src/browser/providers/playwright-doctor.js +1 -1
  257. package/dist/src/browser/stealth.js +1 -1
  258. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  259. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  260. package/dist/src/channels/attachments/voice-stt-webchat.js +10 -8
  261. package/dist/src/channels/attachments/voice-stt-webchat.js.map +1 -1
  262. package/dist/src/channels/outbound/persist-store.js +1 -1
  263. package/dist/src/channels/pairing/allow-from-file.js +9 -9
  264. package/dist/src/channels/pairing/allow-from-file.js.map +1 -1
  265. package/dist/src/channels/pairing/pairing-store.js +7 -7
  266. package/dist/src/channels/pairing/pairing-store.js.map +1 -1
  267. package/dist/src/chat-commands/builtins/config.js +2 -2
  268. package/dist/src/chat-commands/builtins/session.js +1 -1
  269. package/dist/src/chat-commands/builtins/session.js.map +1 -1
  270. package/dist/src/chat-commands/builtins/tts.js +2 -2
  271. package/dist/src/chat-commands/builtins/tts.js.map +1 -1
  272. package/dist/src/chat-commands/context.d.ts +3 -0
  273. package/dist/src/chat-commands/context.js +22 -4
  274. package/dist/src/chat-commands/context.js.map +1 -1
  275. package/dist/src/chat-commands/session-key.d.ts +4 -37
  276. package/dist/src/chat-commands/session-key.js +49 -85
  277. package/dist/src/chat-commands/session-key.js.map +1 -1
  278. package/dist/src/chat-commands/types.d.ts +2 -0
  279. package/dist/src/cli/commands/agent/interactive.js +2 -2
  280. package/dist/src/cli/commands/agent/interactive.js.map +1 -1
  281. package/dist/src/cli/commands/agent/sessions.js +2 -2
  282. package/dist/src/cli/commands/agent/sessions.js.map +1 -1
  283. package/dist/src/cli/commands/agent.js +4 -5
  284. package/dist/src/cli/commands/agent.js.map +1 -1
  285. package/dist/src/cli/commands/channels.js +1 -5
  286. package/dist/src/cli/commands/channels.js.map +1 -1
  287. package/dist/src/cli/commands/config.js +1 -1
  288. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  289. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  290. package/dist/src/cli/commands/doctor/checks/session-integrity.js +1 -1
  291. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  292. package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
  293. package/dist/src/cli/commands/extension-dev.js +1 -1
  294. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  295. package/dist/src/cli/commands/extension-pack.js +1 -1
  296. package/dist/src/cli/commands/gateway/lifecycle-core.js +1 -1
  297. package/dist/src/cli/commands/gateway/lifecycle-core.js.map +1 -1
  298. package/dist/src/cli/commands/gateway/logs.d.ts +9 -0
  299. package/dist/src/cli/commands/gateway/logs.js +50 -17
  300. package/dist/src/cli/commands/gateway/logs.js.map +1 -1
  301. package/dist/src/cli/commands/image.js +23 -22
  302. package/dist/src/cli/commands/image.js.map +1 -1
  303. package/dist/src/cli/commands/init.js +4 -4
  304. package/dist/src/cli/commands/onboard.js +1 -1
  305. package/dist/src/cli/commands/session/utils.js +2 -2
  306. package/dist/src/cli/commands/session/utils.js.map +1 -1
  307. package/dist/src/cli/commands/update.js +26 -46
  308. package/dist/src/cli/commands/update.js.map +1 -1
  309. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  310. package/dist/src/cli/utils/session.d.ts +0 -5
  311. package/dist/src/cli/utils/session.js +1 -6
  312. package/dist/src/cli/utils/session.js.map +1 -1
  313. package/dist/src/commands/agents.config.js +1 -1
  314. package/dist/src/commands/agents.config.js.map +1 -1
  315. package/dist/src/config/agent-profile.js +6 -28
  316. package/dist/src/config/agent-profile.js.map +1 -1
  317. package/dist/src/config/agent-typed-models.d.ts +18 -0
  318. package/dist/src/config/agent-typed-models.js +53 -0
  319. package/dist/src/config/agent-typed-models.js.map +1 -0
  320. package/dist/src/config/gateway-bind.js +1 -1
  321. package/dist/src/config/index.js +6 -6
  322. package/dist/src/config/loader.js +2 -2
  323. package/dist/src/config/model-input.js +2 -5
  324. package/dist/src/config/model-input.js.map +1 -1
  325. package/dist/src/config/models-json.js +2 -2
  326. package/dist/src/config/paths-state.js +1 -1
  327. package/dist/src/config/profile.js +2 -2
  328. package/dist/src/config/schema.d.ts +253 -217
  329. package/dist/src/config/schema.js +91 -40
  330. package/dist/src/config/schema.js.map +1 -1
  331. package/dist/src/config/voice.d.ts +3 -28
  332. package/dist/src/config/voice.js +27 -261
  333. package/dist/src/config/voice.js.map +1 -1
  334. package/dist/src/config/workspace-path-helpers.d.ts +1 -2
  335. package/dist/src/config/workspace-path-helpers.js.map +1 -1
  336. package/dist/src/config/workspace-path.js +1 -1
  337. package/dist/src/cron/executor.js +2 -2
  338. package/dist/src/cron/persistence.js +1 -1
  339. package/dist/src/cron/run-log-store.js +1 -1
  340. package/dist/src/daemon/constants.js +1 -1
  341. package/dist/src/daemon/install-plan.js +27 -3
  342. package/dist/src/daemon/install-plan.js.map +1 -1
  343. package/dist/src/daemon/launchd.d.ts +8 -0
  344. package/dist/src/daemon/launchd.js +7 -14
  345. package/dist/src/daemon/launchd.js.map +1 -1
  346. package/dist/src/daemon/schtasks.d.ts +25 -0
  347. package/dist/src/daemon/schtasks.js +168 -48
  348. package/dist/src/daemon/schtasks.js.map +1 -1
  349. package/dist/src/daemon/service.js +5 -4
  350. package/dist/src/daemon/service.js.map +1 -1
  351. package/dist/src/daemon/systemd.d.ts +6 -0
  352. package/dist/src/daemon/systemd.js +20 -5
  353. package/dist/src/daemon/systemd.js.map +1 -1
  354. package/dist/src/extensions/activation-context.js +0 -1
  355. package/dist/src/extensions/activation-context.js.map +1 -1
  356. package/dist/src/extensions/bundle-mcp.js +1 -1
  357. package/dist/src/extensions/discover-extensions.js +1 -1
  358. package/dist/src/extensions/health.js +1 -1
  359. package/dist/src/extensions/loader.js +1 -1
  360. package/dist/src/extensions/lockfile.js +2 -2
  361. package/dist/src/extensions/normalize-manifest.js +0 -1
  362. package/dist/src/extensions/normalize-manifest.js.map +1 -1
  363. package/dist/src/extensions/types/manifest.d.ts +0 -2
  364. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  365. package/dist/src/gateway/agent-builtin-tools.js +1 -0
  366. package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
  367. package/dist/src/gateway/agents-admin.d.ts +9 -0
  368. package/dist/src/gateway/agents-admin.js +28 -4
  369. package/dist/src/gateway/agents-admin.js.map +1 -1
  370. package/dist/src/gateway/config-tools-web.js +3 -2
  371. package/dist/src/gateway/config-tools-web.js.map +1 -1
  372. package/dist/src/gateway/file-path-classifier.js +2 -2
  373. package/dist/src/gateway/heartbeat/service.js +2 -2
  374. package/dist/src/gateway/heartbeat/service.js.map +1 -1
  375. package/dist/src/gateway/hono/app.js +1 -1
  376. package/dist/src/gateway/hono/lib/agent-model.d.ts +25 -10
  377. package/dist/src/gateway/hono/lib/agent-model.js +60 -36
  378. package/dist/src/gateway/hono/lib/agent-model.js.map +1 -1
  379. package/dist/src/gateway/hono/lib/config-payload.js +29 -6
  380. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  381. package/dist/src/gateway/hono/lib/extension-store.js +2 -2
  382. package/dist/src/gateway/hono/lib/mask-secret-length.d.ts +6 -0
  383. package/dist/src/gateway/hono/lib/mask-secret-length.js +16 -0
  384. package/dist/src/gateway/hono/lib/mask-secret-length.js.map +1 -0
  385. package/dist/src/gateway/hono/lib/safe-providers-config.d.ts +1 -1
  386. package/dist/src/gateway/hono/lib/safe-providers-config.js +2 -1
  387. package/dist/src/gateway/hono/lib/safe-providers-config.js.map +1 -1
  388. package/dist/src/gateway/hono/lib/safe-voice-config.js +16 -54
  389. package/dist/src/gateway/hono/lib/safe-voice-config.js.map +1 -1
  390. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  391. package/dist/src/gateway/hono/oauth.js +1 -1
  392. package/dist/src/gateway/hono/routes/agents.js +2 -2
  393. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  394. package/dist/src/gateway/hono/routes/config-patch/agents.js +25 -7
  395. package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -1
  396. package/dist/src/gateway/hono/routes/config-patch/channels.js +0 -11
  397. package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -1
  398. package/dist/src/gateway/hono/routes/config-patch/gateway.js +3 -2
  399. package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
  400. package/dist/src/gateway/hono/routes/config-patch/misc.js +8 -3
  401. package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
  402. package/dist/src/gateway/hono/routes/config.js +59 -0
  403. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  404. package/dist/src/gateway/hono/routes/dreaming.js +1 -1
  405. package/dist/src/gateway/hono/routes/goals.js +1 -1
  406. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  407. package/dist/src/gateway/hono/routes/host-fs.js +2 -2
  408. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  409. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  410. package/dist/src/gateway/hono/routes/models.js +75 -12
  411. package/dist/src/gateway/hono/routes/models.js.map +1 -1
  412. package/dist/src/gateway/hono/routes/sessions.js +28 -7
  413. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  414. package/dist/src/gateway/hono/routes/shares.js +15 -13
  415. package/dist/src/gateway/hono/routes/shares.js.map +1 -1
  416. package/dist/src/gateway/hono/routes/tunnel.js +1 -1
  417. package/dist/src/gateway/hono/routes/update.js +4 -2
  418. package/dist/src/gateway/hono/routes/update.js.map +1 -1
  419. package/dist/src/gateway/hono/routes/voice.js +75 -0
  420. package/dist/src/gateway/hono/routes/voice.js.map +1 -1
  421. package/dist/src/gateway/hono/routes/workflows.d.ts +3 -0
  422. package/dist/src/gateway/hono/routes/workflows.js +347 -0
  423. package/dist/src/gateway/hono/routes/workflows.js.map +1 -0
  424. package/dist/src/gateway/hono/routes/workspace.js +4 -4
  425. package/dist/src/gateway/hono/sse.js +16 -33
  426. package/dist/src/gateway/hono/sse.js.map +1 -1
  427. package/dist/src/gateway/lock.js +11 -11
  428. package/dist/src/gateway/lock.js.map +1 -1
  429. package/dist/src/gateway/ports.js +6 -6
  430. package/dist/src/gateway/ports.js.map +1 -1
  431. package/dist/src/gateway/resolve-webchat-session-key.d.ts +19 -0
  432. package/dist/src/gateway/resolve-webchat-session-key.js +46 -0
  433. package/dist/src/gateway/resolve-webchat-session-key.js.map +1 -0
  434. package/dist/src/gateway/service/agent-runner.js +2 -2
  435. package/dist/src/gateway/service/marketplace-service.js +2 -2
  436. package/dist/src/gateway/service/run-gateway-agent.js +9 -11
  437. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  438. package/dist/src/gateway/service/sessions-api.d.ts +3 -0
  439. package/dist/src/gateway/service/sessions-api.js +8 -0
  440. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  441. package/dist/src/gateway/service.d.ts +3 -2
  442. package/dist/src/gateway/service.js +9 -8
  443. package/dist/src/gateway/service.js.map +1 -1
  444. package/dist/src/gateway/session-reset-service.d.ts +20 -0
  445. package/dist/src/gateway/session-reset-service.js +54 -0
  446. package/dist/src/gateway/session-reset-service.js.map +1 -0
  447. package/dist/src/gateway/startup-readiness.d.ts +1 -1
  448. package/dist/src/gateway/startup-readiness.js +1 -0
  449. package/dist/src/gateway/startup-readiness.js.map +1 -1
  450. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  451. package/dist/src/heartbeat/index.js +1 -1
  452. package/dist/src/infra/gateway-processes.js +2 -2
  453. package/dist/src/infra/gateway-processes.js.map +1 -1
  454. package/dist/src/infra/restart.js +2 -2
  455. package/dist/src/infra/run-command.d.ts +16 -0
  456. package/dist/src/infra/run-command.js +67 -0
  457. package/dist/src/infra/run-command.js.map +1 -0
  458. package/dist/src/infra/update-check.js +1 -1
  459. package/dist/src/infra/update-global.d.ts +45 -0
  460. package/dist/src/infra/update-global.js +224 -0
  461. package/dist/src/infra/update-global.js.map +1 -0
  462. package/dist/src/infra/update-lock.js +3 -3
  463. package/dist/src/infra/update-runner.js +1 -1
  464. package/dist/src/infra/update-startup.js +2 -2
  465. package/dist/src/infra/write-file-atomic.js +2 -2
  466. package/dist/src/mcp/channel-shared.js +2 -1
  467. package/dist/src/mcp/channel-shared.js.map +1 -1
  468. package/dist/src/providers/auth-runtime/auth-profile-store.js +2 -2
  469. package/dist/src/providers/auth-runtime/auth-profile-store.js.map +1 -1
  470. package/dist/src/providers/auth-runtime/resolve-auth.js +1 -12
  471. package/dist/src/providers/auth-runtime/resolve-auth.js.map +1 -1
  472. package/dist/src/providers/auth-runtime/types.d.ts +6 -12
  473. package/dist/src/providers/index.js +2 -2
  474. package/dist/src/providers/model-registry.js +1 -1
  475. package/dist/src/routing/agent-session-key.d.ts +58 -0
  476. package/dist/src/routing/agent-session-key.js +164 -0
  477. package/dist/src/routing/agent-session-key.js.map +1 -0
  478. package/dist/src/routing/index.d.ts +1 -1
  479. package/dist/src/routing/index.js +4 -2
  480. package/dist/src/routing/index.js.map +1 -1
  481. package/dist/src/routing/resolve-route.d.ts +15 -0
  482. package/dist/src/routing/resolve-route.js +41 -20
  483. package/dist/src/routing/resolve-route.js.map +1 -1
  484. package/dist/src/routing/resolve-tui-session-key.d.ts +25 -0
  485. package/dist/src/routing/resolve-tui-session-key.js +54 -0
  486. package/dist/src/routing/resolve-tui-session-key.js.map +1 -0
  487. package/dist/src/routing/session-key-utils.d.ts +24 -0
  488. package/dist/src/routing/session-key-utils.js +92 -0
  489. package/dist/src/routing/session-key-utils.js.map +1 -0
  490. package/dist/src/routing/session-key.d.ts +19 -49
  491. package/dist/src/routing/session-key.js +143 -116
  492. package/dist/src/routing/session-key.js.map +1 -1
  493. package/dist/src/session/config-store.js +2 -2
  494. package/dist/src/session/index.d.ts +6 -0
  495. package/dist/src/session/index.js +7 -1
  496. package/dist/src/session/init-session-turn.d.ts +30 -0
  497. package/dist/src/session/init-session-turn.js +102 -0
  498. package/dist/src/session/init-session-turn.js.map +1 -0
  499. package/dist/src/session/lifecycle-timestamps.d.ts +8 -0
  500. package/dist/src/session/lifecycle-timestamps.js +16 -0
  501. package/dist/src/session/lifecycle-timestamps.js.map +1 -0
  502. package/dist/src/session/manager.d.ts +7 -1
  503. package/dist/src/session/manager.js +8 -1
  504. package/dist/src/session/manager.js.map +1 -1
  505. package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
  506. package/dist/src/session/parity/sessions-json-file.js +1 -1
  507. package/dist/src/session/parity/transcript-file-lock.js +2 -2
  508. package/dist/src/session/parity/transcript-paths.js +2 -2
  509. package/dist/src/session/parity/transcript-paths.js.map +1 -1
  510. package/dist/src/session/parity/xopc-session-disk-entry.d.ts +6 -0
  511. package/dist/src/session/reset-policy.d.ts +32 -0
  512. package/dist/src/session/reset-policy.js +65 -0
  513. package/dist/src/session/reset-policy.js.map +1 -0
  514. package/dist/src/session/reset-triggers.d.ts +20 -0
  515. package/dist/src/session/reset-triggers.js +63 -0
  516. package/dist/src/session/reset-triggers.js.map +1 -0
  517. package/dist/src/session/reset-type.d.ts +12 -0
  518. package/dist/src/session/reset-type.js +25 -0
  519. package/dist/src/session/reset-type.js.map +1 -0
  520. package/dist/src/session/resolve-session.d.ts +30 -0
  521. package/dist/src/session/resolve-session.js +93 -0
  522. package/dist/src/session/resolve-session.js.map +1 -0
  523. package/dist/src/session/search-index-cache.js +1 -1
  524. package/dist/src/session/search-index.js +1 -1
  525. package/dist/src/session/session-title.js +3 -2
  526. package/dist/src/session/session-title.js.map +1 -1
  527. package/dist/src/session/store.d.ts +11 -4
  528. package/dist/src/session/store.js +62 -11
  529. package/dist/src/session/store.js.map +1 -1
  530. package/dist/src/session/transcript-events.js +2 -1
  531. package/dist/src/session/transcript-events.js.map +1 -1
  532. package/dist/src/share/share-auto.js +2 -2
  533. package/dist/src/share/share-store.js +3 -3
  534. package/dist/src/share/share-thumbnail.js +2 -2
  535. package/dist/src/share/share-url.d.ts +33 -0
  536. package/dist/src/share/share-url.js +56 -14
  537. package/dist/src/share/share-url.js.map +1 -1
  538. package/dist/src/share/share-zip.js +1 -1
  539. package/dist/src/share/site-share-store.js +3 -3
  540. package/dist/src/share/site-static-serve.js +1 -1
  541. package/dist/src/tui/backends/embedded-backend.js +4 -9
  542. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  543. package/dist/src/tui/backends/gateway-sse-backend.js +1 -1
  544. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  545. package/dist/src/tui/clipboard-image.js +3 -3
  546. package/dist/src/tui/components/chat-log.js +3 -3
  547. package/dist/src/tui/components/chat-log.js.map +1 -1
  548. package/dist/src/tui/theme-manager.js +1 -1
  549. package/dist/src/tui/theme.d.ts +0 -2
  550. package/dist/src/tui/theme.js +1 -3
  551. package/dist/src/tui/theme.js.map +1 -1
  552. package/dist/src/tui/tui-agent-events.js +2 -1
  553. package/dist/src/tui/tui-agent-events.js.map +1 -1
  554. package/dist/src/tui/tui-commands.d.ts +3 -0
  555. package/dist/src/tui/tui-commands.js +45 -10
  556. package/dist/src/tui/tui-commands.js.map +1 -1
  557. package/dist/src/tui/tui-keybindings-file.js +2 -22
  558. package/dist/src/tui/tui-keybindings-file.js.map +1 -1
  559. package/dist/src/tui/tui-scoped-models.js +2 -2
  560. package/dist/src/tui/tui-session-actions.d.ts +28 -0
  561. package/dist/src/tui/tui-session-actions.js +88 -0
  562. package/dist/src/tui/tui-session-actions.js.map +1 -0
  563. package/dist/src/tui/tui-settings.js +1 -1
  564. package/dist/src/tui/tui.js +54 -49
  565. package/dist/src/tui/tui.js.map +1 -1
  566. package/dist/src/tunnel/frpc-binary.js +3 -3
  567. package/dist/src/tunnel/frpc-config.js +1 -1
  568. package/dist/src/tunnel/frpc-extract.js +1 -1
  569. package/dist/src/tunnel/tunnel-state.js +1 -1
  570. package/dist/src/utils/logger/audit.js +1 -1
  571. package/dist/src/utils/logger/log-store.js +1 -1
  572. package/dist/src/utils/logger/rotation.js +1 -1
  573. package/dist/src/utils/string-coerce.d.ts +2 -0
  574. package/dist/src/utils/string-coerce.js +10 -1
  575. package/dist/src/utils/string-coerce.js.map +1 -1
  576. package/dist/src/voice/metadata/builtin.d.ts +2 -0
  577. package/dist/src/voice/metadata/builtin.js +420 -0
  578. package/dist/src/voice/metadata/builtin.js.map +1 -0
  579. package/dist/src/voice/metadata/index.d.ts +4 -0
  580. package/dist/src/voice/metadata/index.js +3 -0
  581. package/dist/src/voice/metadata/registry.d.ts +5 -0
  582. package/dist/src/voice/metadata/registry.js +34 -0
  583. package/dist/src/voice/metadata/registry.js.map +1 -0
  584. package/dist/src/voice/metadata/types.d.ts +41 -0
  585. package/dist/src/voice/metadata/types.js +1 -0
  586. package/dist/src/voice/stt/config-slice.d.ts +2 -5
  587. package/dist/src/voice/stt/config-slice.js +5 -26
  588. package/dist/src/voice/stt/config-slice.js.map +1 -1
  589. package/dist/src/voice/stt/list-providers.d.ts +3 -3
  590. package/dist/src/voice/stt/list-providers.js +41 -6
  591. package/dist/src/voice/stt/list-providers.js.map +1 -1
  592. package/dist/src/voice/stt/types.d.ts +1 -18
  593. package/dist/src/voice/stt/types.js +4 -2
  594. package/dist/src/voice/stt/types.js.map +1 -1
  595. package/dist/src/voice/tts/audio.js +1 -1
  596. package/dist/src/voice/tts/config-slice.d.ts +3 -7
  597. package/dist/src/voice/tts/config-slice.js +7 -38
  598. package/dist/src/voice/tts/config-slice.js.map +1 -1
  599. package/dist/src/voice/tts/list-providers.d.ts +3 -3
  600. package/dist/src/voice/tts/list-providers.js +41 -6
  601. package/dist/src/voice/tts/list-providers.js.map +1 -1
  602. package/dist/src/voice/tts/merge-config.js +2 -48
  603. package/dist/src/voice/tts/merge-config.js.map +1 -1
  604. package/dist/src/voice/tts/providers/alibaba-speech.js +1 -1
  605. package/dist/src/voice/tts/providers/alibaba-speech.js.map +1 -1
  606. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  607. package/dist/src/voice/tts/types.d.ts +1 -29
  608. package/dist/src/voice/tts/types.js +19 -17
  609. package/dist/src/voice/tts/types.js.map +1 -1
  610. package/dist/src/workflows/domain/command.d.ts +18 -0
  611. package/dist/src/workflows/domain/command.js +1 -0
  612. package/dist/src/workflows/domain/definition.d.ts +62 -0
  613. package/dist/src/workflows/domain/definition.js +1 -0
  614. package/dist/src/workflows/domain/event.d.ts +67 -0
  615. package/dist/src/workflows/domain/event.js +1 -0
  616. package/dist/src/workflows/domain/index.d.ts +5 -0
  617. package/dist/src/workflows/domain/index.js +2 -0
  618. package/dist/src/workflows/domain/result.d.ts +65 -0
  619. package/dist/src/workflows/domain/result.js +1 -0
  620. package/dist/src/workflows/domain/run.d.ts +120 -0
  621. package/dist/src/workflows/domain/run.js +14 -0
  622. package/dist/src/workflows/domain/run.js.map +1 -0
  623. package/dist/src/workflows/engine/index.d.ts +2 -0
  624. package/dist/src/workflows/engine/index.js +3 -0
  625. package/dist/src/workflows/engine/projector.d.ts +3 -0
  626. package/dist/src/workflows/engine/projector.js +205 -0
  627. package/dist/src/workflows/engine/projector.js.map +1 -0
  628. package/dist/src/workflows/engine/workflow-engine.d.ts +31 -0
  629. package/dist/src/workflows/engine/workflow-engine.js +188 -0
  630. package/dist/src/workflows/engine/workflow-engine.js.map +1 -0
  631. package/dist/src/workflows/index.d.ts +6 -0
  632. package/dist/src/workflows/index.js +11 -0
  633. package/dist/src/workflows/runtime/index.d.ts +1 -0
  634. package/dist/src/workflows/runtime/index.js +4 -0
  635. package/dist/src/workflows/runtime/script-runtime.d.ts +3 -0
  636. package/dist/src/workflows/runtime/script-runtime.js +3 -0
  637. package/dist/src/workflows/store/event-store.d.ts +17 -0
  638. package/dist/src/workflows/store/event-store.js +83 -0
  639. package/dist/src/workflows/store/event-store.js.map +1 -0
  640. package/dist/src/workflows/store/paths.d.ts +7 -0
  641. package/dist/src/workflows/store/paths.js +26 -0
  642. package/dist/src/workflows/store/paths.js.map +1 -0
  643. package/dist/src/workflows/store/run-store.d.ts +13 -0
  644. package/dist/src/workflows/store/run-store.js +68 -0
  645. package/dist/src/workflows/store/run-store.js.map +1 -0
  646. package/package.json +5 -8
  647. package/dist/gateway/static/root/assets/agents-mS3_HpRI.js +0 -222
  648. package/dist/gateway/static/root/assets/channels-settings-BG6b9KrW.js +0 -1
  649. package/dist/gateway/static/root/assets/extension-settings-page-B-W4x2xP.js +0 -1
  650. package/dist/gateway/static/root/assets/index-ew_2L2We.css +0 -1
  651. package/dist/gateway/static/root/assets/sessions-page-FaG_Vlkb.js +0 -1
  652. package/dist/gateway/static/root/assets/settings-page-Bet1OerL.js +0 -3
  653. package/dist/gateway/static/root/assets/skills-page-DhUO235y.js +0 -2
  654. package/dist/gateway/static/root/assets/url-BwNL6Rgk.js +0 -3
  655. package/dist/gateway/static/root/assets/voice-api-key-field-CGEydndO.js +0 -1
  656. package/dist/src/agent/tools/browser-legacy-tools.d.ts +0 -17
  657. package/dist/src/agent/tools/browser-legacy-tools.js +0 -766
  658. package/dist/src/agent/tools/browser-legacy-tools.js.map +0 -1
@@ -120,6 +120,7 @@ function registerUpdateRoutes(authenticated, deps) {
120
120
  message: String(parsed.message ?? "Git checkout — use git pull instead.")
121
121
  }, 400);
122
122
  if (!result.ok) {
123
+ const installMessage = typeof parsed?.message === "string" ? parsed.message : typeof parsed?.stderrTail === "string" ? parsed.stderrTail : void 0;
123
124
  log.warn({
124
125
  channel,
125
126
  exitCode: result.exitCode,
@@ -128,7 +129,7 @@ function registerUpdateRoutes(authenticated, deps) {
128
129
  return c.json({
129
130
  ok: false,
130
131
  error: "update-failed",
131
- message: result.stderr?.trim() || result.reason || `Update exited with code ${result.exitCode ?? "unknown"}`,
132
+ message: installMessage || result.stderr?.trim() || result.reason || `Update exited with code ${result.exitCode ?? "unknown"}`,
132
133
  result: parsed
133
134
  });
134
135
  }
@@ -199,12 +200,13 @@ function registerUpdateRoutes(authenticated, deps) {
199
200
  return;
200
201
  }
201
202
  if (!result.ok) {
203
+ const installMessage = typeof parsed?.message === "string" ? parsed.message : typeof parsed?.stderrTail === "string" ? parsed.stderrTail : void 0;
202
204
  await stream.writeSSE({
203
205
  event: "result",
204
206
  data: JSON.stringify({
205
207
  ok: false,
206
208
  error: "update-failed",
207
- message: result.stderr?.trim() || result.reason || `Update exited with code ${result.exitCode ?? "unknown"}`,
209
+ message: installMessage || result.stderr?.trim() || result.reason || `Update exited with code ${result.exitCode ?? "unknown"}`,
208
210
  result: parsed,
209
211
  exitCode: result.exitCode,
210
212
  reason: result.reason
@@ -1 +1 @@
1
- {"version":3,"file":"update.js","names":[],"sources":["../../../../../src/gateway/hono/routes/update.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\n\nimport { loadConfig } from '../../../config/index.js';\nimport { acquireUpdateLock } from '../../../infra/update-lock.js';\nimport { detectInstallKind, resolvePackageRoot } from '../../../infra/update-check.js';\nimport {\n DEFAULT_PACKAGE_CHANNEL,\n normalizeUpdateChannel,\n type UpdateChannel,\n} from '../../../infra/update-channels.js';\nimport { runAutoUpdateCommand, runAutoUpdateCommandWithProgress } from '../../../infra/update-runner.js';\nimport { getUpdateAvailable, runGatewayUpdateCheck } from '../../../infra/update-startup.js';\nimport { PACKAGE_VERSION } from '../../../package-version.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst log = createLogger('GatewayUpdate');\n\nfunction parseUpdateCliJson(stdout: string): Record<string, unknown> | null {\n const t = stdout.trim();\n if (!t) return null;\n try {\n const parsed = JSON.parse(t) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : null;\n } catch {\n const lines = t.split('\\n').filter(Boolean);\n for (let i = lines.length - 1; i >= 0; i--) {\n const line = lines[i]!.trim();\n if (!line.startsWith('{')) continue;\n try {\n const parsed = JSON.parse(line) as unknown;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // try previous line\n }\n }\n }\n return null;\n}\n\ntype PreconditionOk = {\n ok: true;\n channel: UpdateChannel;\n root: string | null;\n};\n\nfunction isPreconditionFail(\n x: PreconditionOk | { ok: false; status: 400; body: Record<string, unknown> },\n): x is { ok: false; status: 400; body: Record<string, unknown> } {\n return !x.ok;\n}\n\nasync function npmUpdatePreconditions(\n service: AuthenticatedRouteDeps['service'],\n): Promise<PreconditionOk | { ok: false; status: 400; body: Record<string, unknown> }> {\n const config = loadConfig(service.getHealth().configPath);\n const channel = normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n\n const root = await resolvePackageRoot();\n if (root) {\n const kind = await detectInstallKind(root);\n if (kind === 'git') {\n return {\n ok: false,\n status: 400,\n body: {\n ok: false,\n error: 'git-checkout',\n message:\n 'Running from a git checkout. Use `git pull` in the repo, or install from npm to use one-click update.',\n },\n };\n }\n }\n\n return { ok: true, channel, root };\n}\n\nexport function registerUpdateRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { strictRateLimitMiddleware, service } = deps;\n\n /**\n * GET /api/update/status\n */\n authenticated.get('/api/update/status', (c) => {\n const update = getUpdateAvailable();\n return c.json({\n ok: true,\n payload: {\n currentVersion: PACKAGE_VERSION,\n updateAvailable: update !== null,\n latestVersion: update?.latestVersion ?? null,\n channel: update?.channel ?? null,\n },\n });\n });\n\n /**\n * POST /api/update/check\n */\n authenticated.post('/api/update/check', strictRateLimitMiddleware, async (c) => {\n const config = loadConfig(service.getHealth().configPath);\n await runGatewayUpdateCheck({\n config,\n force: true,\n onUpdateAvailableChange: (update) => {\n service.emit('update.available', update);\n },\n });\n const result = getUpdateAvailable();\n return c.json({\n ok: true,\n payload: {\n currentVersion: PACKAGE_VERSION,\n updateAvailable: result !== null,\n latestVersion: result?.latestVersion ?? null,\n channel: result?.channel ?? null,\n },\n });\n });\n\n /**\n * POST /api/update/run — one-click npm install (OpenClaw-style). Rejects git checkouts.\n */\n authenticated.post('/api/update/run', strictRateLimitMiddleware, async (c) => {\n const pre = await npmUpdatePreconditions(service);\n if (isPreconditionFail(pre)) {\n return c.json(pre.body, pre.status);\n }\n\n const lock = await acquireUpdateLock('gateway');\n if (!lock) {\n return c.json(\n {\n ok: false,\n error: 'busy',\n message: 'Another update is already in progress.',\n },\n 409,\n );\n }\n\n const { channel, root } = pre;\n try {\n log.info({ channel }, 'Gateway: starting one-click npm update');\n const result = await runAutoUpdateCommand({ channel, root });\n const parsed = parseUpdateCliJson(result.stdout ?? '');\n\n if (result.ok && parsed?.status === 'skipped' && parsed?.reason === 'git-checkout') {\n return c.json(\n {\n ok: false,\n error: 'git-checkout',\n message: String(parsed.message ?? 'Git checkout — use git pull instead.'),\n },\n 400,\n );\n }\n\n if (!result.ok) {\n log.warn(\n { channel, exitCode: result.exitCode, reason: result.reason },\n 'Gateway: one-click npm update failed',\n );\n return c.json({\n ok: false,\n error: 'update-failed',\n message: result.stderr?.trim() || result.reason || `Update exited with code ${result.exitCode ?? 'unknown'}`,\n result: parsed,\n });\n }\n\n log.info({ channel }, 'Gateway: one-click npm update finished');\n return c.json({ ok: true, result: parsed });\n } catch (err) {\n log.error({ err, channel }, 'Gateway: one-click npm update threw');\n return c.json(\n {\n ok: false,\n error: 'internal',\n message: err instanceof Error ? err.message : String(err),\n },\n 500,\n );\n } finally {\n await lock.release();\n }\n });\n\n /**\n * POST /api/update/run/stream — SSE-streamed npm update with progress lines.\n */\n authenticated.post('/api/update/run/stream', strictRateLimitMiddleware, async (c) => {\n const pre = await npmUpdatePreconditions(service);\n if (isPreconditionFail(pre)) {\n return c.json(pre.body, pre.status);\n }\n\n const { channel, root } = pre;\n\n return streamSSE(c, async (stream) => {\n const lock = await acquireUpdateLock('gateway');\n if (!lock) {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'busy',\n message: 'Another update is already in progress.',\n }),\n });\n return;\n }\n\n try {\n log.info({ channel }, 'Gateway: starting streamed one-click npm update');\n const result = await runAutoUpdateCommandWithProgress({\n channel,\n root,\n onProgress: async (line, source) => {\n await stream.writeSSE({\n event: 'progress',\n data: JSON.stringify({ line, source }),\n });\n },\n });\n\n const parsed = parseUpdateCliJson(result.stdout ?? '');\n\n if (result.ok && parsed?.status === 'skipped' && parsed?.reason === 'git-checkout') {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'git-checkout',\n message: String(parsed.message ?? 'Git checkout — use git pull instead.'),\n }),\n });\n return;\n }\n\n if (!result.ok) {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'update-failed',\n message:\n result.stderr?.trim() || result.reason || `Update exited with code ${result.exitCode ?? 'unknown'}`,\n result: parsed,\n exitCode: result.exitCode,\n reason: result.reason,\n }),\n });\n return;\n }\n\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({ ok: true, result: parsed }),\n });\n } catch (err) {\n log.error({ err, channel }, 'Gateway: streamed npm update threw');\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'internal',\n message: err instanceof Error ? err.message : String(err),\n }),\n });\n } finally {\n await lock.release();\n }\n });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"update.js","names":[],"sources":["../../../../../src/gateway/hono/routes/update.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\n\nimport { loadConfig } from '../../../config/index.js';\nimport { acquireUpdateLock } from '../../../infra/update-lock.js';\nimport { detectInstallKind, resolvePackageRoot } from '../../../infra/update-check.js';\nimport {\n DEFAULT_PACKAGE_CHANNEL,\n normalizeUpdateChannel,\n type UpdateChannel,\n} from '../../../infra/update-channels.js';\nimport { runAutoUpdateCommand, runAutoUpdateCommandWithProgress } from '../../../infra/update-runner.js';\nimport { getUpdateAvailable, runGatewayUpdateCheck } from '../../../infra/update-startup.js';\nimport { PACKAGE_VERSION } from '../../../package-version.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst log = createLogger('GatewayUpdate');\n\nfunction parseUpdateCliJson(stdout: string): Record<string, unknown> | null {\n const t = stdout.trim();\n if (!t) return null;\n try {\n const parsed = JSON.parse(t) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : null;\n } catch {\n const lines = t.split('\\n').filter(Boolean);\n for (let i = lines.length - 1; i >= 0; i--) {\n const line = lines[i]!.trim();\n if (!line.startsWith('{')) continue;\n try {\n const parsed = JSON.parse(line) as unknown;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // try previous line\n }\n }\n }\n return null;\n}\n\ntype PreconditionOk = {\n ok: true;\n channel: UpdateChannel;\n root: string | null;\n};\n\nfunction isPreconditionFail(\n x: PreconditionOk | { ok: false; status: 400; body: Record<string, unknown> },\n): x is { ok: false; status: 400; body: Record<string, unknown> } {\n return !x.ok;\n}\n\nasync function npmUpdatePreconditions(\n service: AuthenticatedRouteDeps['service'],\n): Promise<PreconditionOk | { ok: false; status: 400; body: Record<string, unknown> }> {\n const config = loadConfig(service.getHealth().configPath);\n const channel = normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n\n const root = await resolvePackageRoot();\n if (root) {\n const kind = await detectInstallKind(root);\n if (kind === 'git') {\n return {\n ok: false,\n status: 400,\n body: {\n ok: false,\n error: 'git-checkout',\n message:\n 'Running from a git checkout. Use `git pull` in the repo, or install from npm to use one-click update.',\n },\n };\n }\n }\n\n return { ok: true, channel, root };\n}\n\nexport function registerUpdateRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { strictRateLimitMiddleware, service } = deps;\n\n /**\n * GET /api/update/status\n */\n authenticated.get('/api/update/status', (c) => {\n const update = getUpdateAvailable();\n return c.json({\n ok: true,\n payload: {\n currentVersion: PACKAGE_VERSION,\n updateAvailable: update !== null,\n latestVersion: update?.latestVersion ?? null,\n channel: update?.channel ?? null,\n },\n });\n });\n\n /**\n * POST /api/update/check\n */\n authenticated.post('/api/update/check', strictRateLimitMiddleware, async (c) => {\n const config = loadConfig(service.getHealth().configPath);\n await runGatewayUpdateCheck({\n config,\n force: true,\n onUpdateAvailableChange: (update) => {\n service.emit('update.available', update);\n },\n });\n const result = getUpdateAvailable();\n return c.json({\n ok: true,\n payload: {\n currentVersion: PACKAGE_VERSION,\n updateAvailable: result !== null,\n latestVersion: result?.latestVersion ?? null,\n channel: result?.channel ?? null,\n },\n });\n });\n\n /**\n * POST /api/update/run — one-click npm install (OpenClaw-style). Rejects git checkouts.\n */\n authenticated.post('/api/update/run', strictRateLimitMiddleware, async (c) => {\n const pre = await npmUpdatePreconditions(service);\n if (isPreconditionFail(pre)) {\n return c.json(pre.body, pre.status);\n }\n\n const lock = await acquireUpdateLock('gateway');\n if (!lock) {\n return c.json(\n {\n ok: false,\n error: 'busy',\n message: 'Another update is already in progress.',\n },\n 409,\n );\n }\n\n const { channel, root } = pre;\n try {\n log.info({ channel }, 'Gateway: starting one-click npm update');\n const result = await runAutoUpdateCommand({ channel, root });\n const parsed = parseUpdateCliJson(result.stdout ?? '');\n\n if (result.ok && parsed?.status === 'skipped' && parsed?.reason === 'git-checkout') {\n return c.json(\n {\n ok: false,\n error: 'git-checkout',\n message: String(parsed.message ?? 'Git checkout — use git pull instead.'),\n },\n 400,\n );\n }\n\n if (!result.ok) {\n const installMessage =\n typeof parsed?.message === 'string'\n ? parsed.message\n : typeof parsed?.stderrTail === 'string'\n ? parsed.stderrTail\n : undefined;\n log.warn(\n { channel, exitCode: result.exitCode, reason: result.reason },\n 'Gateway: one-click npm update failed',\n );\n return c.json({\n ok: false,\n error: 'update-failed',\n message:\n installMessage ||\n result.stderr?.trim() ||\n result.reason ||\n `Update exited with code ${result.exitCode ?? 'unknown'}`,\n result: parsed,\n });\n }\n\n log.info({ channel }, 'Gateway: one-click npm update finished');\n return c.json({ ok: true, result: parsed });\n } catch (err) {\n log.error({ err, channel }, 'Gateway: one-click npm update threw');\n return c.json(\n {\n ok: false,\n error: 'internal',\n message: err instanceof Error ? err.message : String(err),\n },\n 500,\n );\n } finally {\n await lock.release();\n }\n });\n\n /**\n * POST /api/update/run/stream — SSE-streamed npm update with progress lines.\n */\n authenticated.post('/api/update/run/stream', strictRateLimitMiddleware, async (c) => {\n const pre = await npmUpdatePreconditions(service);\n if (isPreconditionFail(pre)) {\n return c.json(pre.body, pre.status);\n }\n\n const { channel, root } = pre;\n\n return streamSSE(c, async (stream) => {\n const lock = await acquireUpdateLock('gateway');\n if (!lock) {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'busy',\n message: 'Another update is already in progress.',\n }),\n });\n return;\n }\n\n try {\n log.info({ channel }, 'Gateway: starting streamed one-click npm update');\n const result = await runAutoUpdateCommandWithProgress({\n channel,\n root,\n onProgress: async (line, source) => {\n await stream.writeSSE({\n event: 'progress',\n data: JSON.stringify({ line, source }),\n });\n },\n });\n\n const parsed = parseUpdateCliJson(result.stdout ?? '');\n\n if (result.ok && parsed?.status === 'skipped' && parsed?.reason === 'git-checkout') {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'git-checkout',\n message: String(parsed.message ?? 'Git checkout — use git pull instead.'),\n }),\n });\n return;\n }\n\n if (!result.ok) {\n const installMessage =\n typeof parsed?.message === 'string'\n ? parsed.message\n : typeof parsed?.stderrTail === 'string'\n ? parsed.stderrTail\n : undefined;\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'update-failed',\n message:\n installMessage ||\n result.stderr?.trim() ||\n result.reason ||\n `Update exited with code ${result.exitCode ?? 'unknown'}`,\n result: parsed,\n exitCode: result.exitCode,\n reason: result.reason,\n }),\n });\n return;\n }\n\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({ ok: true, result: parsed }),\n });\n } catch (err) {\n log.error({ err, channel }, 'Gateway: streamed npm update threw');\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'internal',\n message: err instanceof Error ? err.message : String(err),\n }),\n });\n } finally {\n await lock.release();\n }\n });\n });\n}\n"],"mappings":"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"}
@@ -2,12 +2,16 @@ import { createLogger } from "../../../utils/logger/index.js";
2
2
  import { init_logger } from "../../../utils/logger.js";
3
3
  import { getDefaultModelSync, init_providers, resolveModel } from "../../../providers/index.js";
4
4
  import { resolveTtsProviderConfigSlice } from "../../../voice/tts/config-slice.js";
5
+ import { isTTSAvailable } from "../../../voice/tts/factory.js";
6
+ import { speak } from "../../../voice/tts/speak-core.js";
7
+ import { mergeTtsConfigFromAppConfig } from "../../../voice/tts/merge-config.js";
5
8
  import { resolveSttProviderConfigSlice } from "../../../voice/stt/config-slice.js";
6
9
  import { transcribe } from "../../../voice/stt/transcribe-core.js";
7
10
  import { isSTTAvailable } from "../../../voice/stt/availability.js";
8
11
  import "../../../voice/stt/index.js";
9
12
  import { mergeSttConfigFromAppConfig } from "../../../channels/attachments/voice-stt-webchat.js";
10
13
  import { listSttProvidersForApi } from "../../../voice/stt/list-providers.js";
14
+ import "../../../voice/tts/index.js";
11
15
  import { listTtsProvidersForApi } from "../../../voice/tts/list-providers.js";
12
16
  import { complete } from "@earendil-works/pi-ai";
13
17
  //#region src/gateway/hono/routes/voice.ts
@@ -145,6 +149,77 @@ function registerVoiceRoutes(authenticated, deps) {
145
149
  });
146
150
  });
147
151
  /**
152
+ * POST /api/voice/tts-test
153
+ *
154
+ * Body: { text: string, provider?: string, model?: string, voice?: string }
155
+ * Response: { ok: true, payload: { audio: string, format: string, provider: string } }
156
+ */
157
+ authenticated.post("/api/voice/tts-test", strictRateLimitMiddleware, async (c) => {
158
+ let body = {};
159
+ try {
160
+ body = await c.req.json();
161
+ } catch {
162
+ return c.json({
163
+ ok: false,
164
+ error: { message: "Invalid JSON body" }
165
+ }, 400);
166
+ }
167
+ const text = typeof body.text === "string" ? body.text.trim() : "";
168
+ if (!text) return c.json({
169
+ ok: false,
170
+ error: { message: "text is required" }
171
+ }, 400);
172
+ if (text.length > 1e3) return c.json({
173
+ ok: false,
174
+ error: { message: "text exceeds 1000 characters" }
175
+ }, 400);
176
+ const config = service.currentConfig;
177
+ const baseTtsConfig = mergeTtsConfigFromAppConfig(config.messages?.tts);
178
+ const provider = typeof body.provider === "string" && body.provider.trim() ? body.provider.trim() : baseTtsConfig.provider;
179
+ const ttsConfig = {
180
+ ...baseTtsConfig,
181
+ enabled: true,
182
+ provider,
183
+ fallback: {
184
+ enabled: false,
185
+ order: [provider]
186
+ }
187
+ };
188
+ if (!isTTSAvailable(ttsConfig)) return c.json({
189
+ ok: false,
190
+ error: { message: `TTS provider "${provider}" is not configured.` }
191
+ }, 503);
192
+ try {
193
+ const result = await speak(text, ttsConfig, {
194
+ appConfig: config,
195
+ parseDirectives: false,
196
+ tts: {
197
+ ...typeof body.model === "string" && body.model.trim() ? { model: body.model.trim() } : {},
198
+ ...typeof body.voice === "string" && body.voice.trim() ? { voice: body.voice.trim() } : {}
199
+ }
200
+ });
201
+ return c.json({
202
+ ok: true,
203
+ payload: {
204
+ audio: result.audio.toString("base64"),
205
+ format: result.format,
206
+ provider: result.provider,
207
+ ...result.duration !== void 0 ? { duration: result.duration } : {}
208
+ }
209
+ });
210
+ } catch (error) {
211
+ const msg = error instanceof Error ? error.message : String(error);
212
+ log.error({
213
+ errorMessage: msg,
214
+ provider
215
+ }, "Voice TTS test failed");
216
+ return c.json({
217
+ ok: false,
218
+ error: { message: `TTS test failed: ${msg}` }
219
+ }, 502);
220
+ }
221
+ });
222
+ /**
148
223
  * POST /api/voice/transcribe
149
224
  *
150
225
  * Body: { audio: string (base64), mimeType: string, language?: string }
@@ -1 +1 @@
1
- {"version":3,"file":"voice.js","names":[],"sources":["../../../../../src/gateway/hono/routes/voice.ts"],"sourcesContent":["/**\n * Voice routes — POST /api/voice/transcribe\n *\n * Single endpoint that:\n * 1. Runs STT (Whisper preferred for low latency, Alibaba fallback)\n * 2. Optionally runs LLM refine on the raw transcript\n * 3. Returns { raw, refined?, language }\n *\n * LLM refine is auto-applied when a model is resolvable; gracefully degrades\n * to raw-only when no LLM is configured.\n */\n\nimport type { Hono } from 'hono';\nimport { complete, type UserMessage } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../../config/schema.js';\nimport { getDefaultModelSync, resolveModel } from '../../../providers/index.js';\nimport { isSTTAvailable, transcribe } from '../../../voice/stt/index.js';\nimport { listTtsProvidersForApi } from '../../../voice/tts/list-providers.js';\nimport { listSttProvidersForApi } from '../../../voice/stt/list-providers.js';\nimport { mergeSttConfigFromAppConfig } from '../../../channels/attachments/voice-stt-webchat.js';\nimport { resolveSttProviderConfigSlice } from '../../../voice/stt/config-slice.js';\nimport { resolveTtsProviderConfigSlice } from '../../../voice/tts/config-slice.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst log = createLogger('Gateway:Voice');\n\nfunction readVoiceApiKeyFromConfigFileOnly(\n cfg: Config,\n kind: 'stt' | 'tts',\n providerId: string,\n): string | undefined {\n const id = providerId.trim();\n if (!id) return undefined;\n const slice =\n kind === 'stt'\n ? resolveSttProviderConfigSlice(id, cfg.tools?.media?.audio)\n : resolveTtsProviderConfigSlice(id, cfg.messages?.tts);\n const key = slice.apiKey;\n return typeof key === 'string' && key.trim() ? key.trim() : undefined;\n}\n\nconst REFINE_TIMEOUT_MS = 15_000;\nconst MAX_AUDIO_BYTES = 25 * 1024 * 1024; // 25 MB\n\nconst REFINE_SYSTEM_PROMPT = `你是语音转文字后处理助手。将语音转写原文整理为高质量文本输入。\n\n规则:\n1. 修正明显的语音识别错误\n2. 添加正确的标点符号\n3. 去除口语赘词(嗯、啊、那个、就是说、然后就是)\n4. 保持原意不变,不要扩写或改变语义\n5. 如果原文已经很好,原样输出\n6. 只输出整理后的文字,不要解释`;\n\nfunction resolveRefineModel(config: Config | undefined): ReturnType<typeof resolveModel> | null {\n const envRef = process.env.XOPC_VOICE_REFINE_MODEL?.trim();\n if (envRef) {\n try {\n return resolveModel(envRef);\n } catch { /* fall through */ }\n }\n for (const candidate of ['openai/gpt-4o-mini', 'google/gemini-2.0-flash']) {\n try {\n return resolveModel(candidate);\n } catch { /* next */ }\n }\n try {\n return resolveModel(getDefaultModelSync(config));\n } catch {\n return null;\n }\n}\n\nasync function refineTranscript(\n raw: string,\n config: Config | undefined,\n signal?: AbortSignal,\n): Promise<string | undefined> {\n if (!raw.trim()) return undefined;\n\n const model = resolveRefineModel(config);\n if (!model) {\n log.debug('No LLM model available for voice refine; returning raw only');\n return undefined;\n }\n\n try {\n const userMsg: UserMessage = {\n role: 'user',\n content: `${REFINE_SYSTEM_PROMPT}\\n\\n原文:${raw}`,\n timestamp: Date.now(),\n };\n\n const timeoutSignal =\n typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function'\n ? AbortSignal.timeout(REFINE_TIMEOUT_MS)\n : undefined;\n const mergedSignal =\n signal && timeoutSignal && typeof AbortSignal.any === 'function'\n ? AbortSignal.any([signal, timeoutSignal])\n : signal ?? timeoutSignal;\n\n const result = await complete(\n model,\n { messages: [userMsg] },\n {\n maxTokens: Math.min(raw.length * 3, 4096),\n temperature: 0.2,\n signal: mergedSignal as AbortSignal,\n },\n );\n\n let out = '';\n if (Array.isArray(result.content)) {\n for (const c of result.content) {\n if (c && typeof c === 'object' && (c as { type?: string }).type === 'text') {\n out += String((c as { text?: string }).text || '');\n }\n }\n }\n\n const refined = out.trim();\n if (!refined || refined === raw.trim()) return undefined;\n return refined;\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n log.warn({ errorMessage: msg }, 'Voice refine failed; returning raw only');\n return undefined;\n }\n}\n\nexport function registerVoiceRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service, strictRateLimitMiddleware } = deps;\n\n /**\n * GET /api/voice/providers\n *\n * Lists registered SpeechProviderPlugin ids with configured state for the\n * current gateway config (OpenClaw `tts.providers` equivalent).\n */\n authenticated.get('/api/voice/providers', (c) => {\n const config = service.currentConfig as Config;\n const payload = listTtsProvidersForApi(config);\n return c.json({ ok: true, payload });\n });\n\n /**\n * GET /api/voice/stt-providers\n *\n * Lists registered MediaUnderstandingProvider ids with configured state for\n * the current gateway config (OpenClaw `tools.media.audio.providers` equivalent).\n */\n authenticated.get('/api/voice/stt-providers', (c) => {\n const config = service.currentConfig as Config;\n const payload = listSttProvidersForApi(config);\n return c.json({ ok: true, payload });\n });\n\n /**\n * POST /api/voice/reveal-api-key — return plaintext voice provider apiKey from config file only.\n * Body: `{ kind: 'stt' | 'tts', provider: string }`\n */\n authenticated.post('/api/voice/reveal-api-key', strictRateLimitMiddleware, async (c) => {\n let body: { kind?: unknown; provider?: unknown } = {};\n try {\n body = (await c.req.json()) as typeof body;\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON body' } }, 400);\n }\n const kind = body.kind === 'stt' || body.kind === 'tts' ? body.kind : null;\n const provider = typeof body.provider === 'string' ? body.provider.trim() : '';\n if (!kind) {\n return c.json({ ok: false, error: { message: 'kind must be \"stt\" or \"tts\"' } }, 400);\n }\n if (!provider) {\n return c.json({ ok: false, error: { message: 'provider is required' } }, 400);\n }\n\n const cfg = service.currentConfig as Config;\n const apiKey = readVoiceApiKeyFromConfigFileOnly(cfg, kind, provider);\n return c.json({\n ok: true,\n payload: {\n kind,\n provider,\n apiKey: apiKey ?? null,\n source: apiKey ? ('config' as const) : ('none' as const),\n },\n });\n });\n\n /**\n * POST /api/voice/transcribe\n *\n * Body: { audio: string (base64), mimeType: string, language?: string }\n * Response: { ok: true, payload: { raw: string, refined?: string, language?: string } }\n */\n authenticated.post('/api/voice/transcribe', async (c) => {\n let body: { audio?: string; mimeType?: string; language?: string } = {};\n try {\n body = (await c.req.json()) as typeof body;\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON body' } }, 400);\n }\n\n const { audio, mimeType, language } = body;\n if (!audio || typeof audio !== 'string') {\n return c.json({ ok: false, error: { message: 'Missing required field: audio (base64)' } }, 400);\n }\n if (!mimeType || typeof mimeType !== 'string') {\n return c.json({ ok: false, error: { message: 'Missing required field: mimeType' } }, 400);\n }\n\n // Decode base64 audio\n let audioBuffer: Buffer;\n try {\n audioBuffer = Buffer.from(audio, 'base64');\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid base64 audio data' } }, 400);\n }\n\n if (audioBuffer.length === 0) {\n return c.json({ ok: false, error: { message: 'Empty audio data' } }, 400);\n }\n if (audioBuffer.length > MAX_AUDIO_BYTES) {\n return c.json({ ok: false, error: { message: 'Audio data exceeds 25 MB limit' } }, 400);\n }\n\n // Resolve STT config from app config\n const config = service.currentConfig as Config;\n const sttConfigRaw = config.tools?.media?.audio;\n const sttConfig = mergeSttConfigFromAppConfig(sttConfigRaw, config.tools?.media);\n\n if (!isSTTAvailable(sttConfig)) {\n return c.json({\n ok: false,\n error: { message: 'STT is not configured. Enable STT in gateway config (tools.media.audio).' },\n }, 503);\n }\n\n // Run STT\n let raw: string;\n let detectedLanguage: string | undefined;\n try {\n const result = await transcribe(audioBuffer, sttConfig, {\n language: language || (sttConfig.provider === 'alibaba' ? 'zh' : undefined),\n });\n raw = result.text;\n detectedLanguage = result.language ?? language;\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n log.error({ errorMessage: msg }, 'Voice transcription failed');\n return c.json({ ok: false, error: { message: `Transcription failed: ${msg}` } }, 502);\n }\n\n if (!raw.trim()) {\n return c.json({\n ok: true,\n payload: { raw: '', language: detectedLanguage },\n });\n }\n\n // Run LLM refine (auto, best-effort)\n const refined = await refineTranscript(raw, config);\n\n return c.json({\n ok: true,\n payload: {\n raw,\n ...(refined ? { refined } : {}),\n ...(detectedLanguage ? { language: detectedLanguage } : {}),\n },\n });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"voice.js","names":[],"sources":["../../../../../src/gateway/hono/routes/voice.ts"],"sourcesContent":["/**\n * Voice routes — POST /api/voice/transcribe\n *\n * Single endpoint that:\n * 1. Runs STT (Whisper preferred for low latency, Alibaba fallback)\n * 2. Optionally runs LLM refine on the raw transcript\n * 3. Returns { raw, refined?, language }\n *\n * LLM refine is auto-applied when a model is resolvable; gracefully degrades\n * to raw-only when no LLM is configured.\n */\n\nimport type { Hono } from 'hono';\nimport { complete, type UserMessage } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../../config/schema.js';\nimport { getDefaultModelSync, resolveModel } from '../../../providers/index.js';\nimport { isSTTAvailable, transcribe } from '../../../voice/stt/index.js';\nimport { isTTSAvailable, mergeTtsConfigFromAppConfig, speak } from '../../../voice/tts/index.js';\nimport { listTtsProvidersForApi } from '../../../voice/tts/list-providers.js';\nimport { listSttProvidersForApi } from '../../../voice/stt/list-providers.js';\nimport { mergeSttConfigFromAppConfig } from '../../../channels/attachments/voice-stt-webchat.js';\nimport { resolveSttProviderConfigSlice } from '../../../voice/stt/config-slice.js';\nimport { resolveTtsProviderConfigSlice } from '../../../voice/tts/config-slice.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst log = createLogger('Gateway:Voice');\n\nfunction readVoiceApiKeyFromConfigFileOnly(\n cfg: Config,\n kind: 'stt' | 'tts',\n providerId: string,\n): string | undefined {\n const id = providerId.trim();\n if (!id) return undefined;\n const slice =\n kind === 'stt'\n ? resolveSttProviderConfigSlice(id, cfg.tools?.media?.audio)\n : resolveTtsProviderConfigSlice(id, cfg.messages?.tts);\n const key = slice.apiKey;\n return typeof key === 'string' && key.trim() ? key.trim() : undefined;\n}\n\nconst REFINE_TIMEOUT_MS = 15_000;\nconst MAX_AUDIO_BYTES = 25 * 1024 * 1024; // 25 MB\n\nconst REFINE_SYSTEM_PROMPT = `你是语音转文字后处理助手。将语音转写原文整理为高质量文本输入。\n\n规则:\n1. 修正明显的语音识别错误\n2. 添加正确的标点符号\n3. 去除口语赘词(嗯、啊、那个、就是说、然后就是)\n4. 保持原意不变,不要扩写或改变语义\n5. 如果原文已经很好,原样输出\n6. 只输出整理后的文字,不要解释`;\n\nfunction resolveRefineModel(config: Config | undefined): ReturnType<typeof resolveModel> | null {\n const envRef = process.env.XOPC_VOICE_REFINE_MODEL?.trim();\n if (envRef) {\n try {\n return resolveModel(envRef);\n } catch { /* fall through */ }\n }\n for (const candidate of ['openai/gpt-4o-mini', 'google/gemini-2.0-flash']) {\n try {\n return resolveModel(candidate);\n } catch { /* next */ }\n }\n try {\n return resolveModel(getDefaultModelSync(config));\n } catch {\n return null;\n }\n}\n\nasync function refineTranscript(\n raw: string,\n config: Config | undefined,\n signal?: AbortSignal,\n): Promise<string | undefined> {\n if (!raw.trim()) return undefined;\n\n const model = resolveRefineModel(config);\n if (!model) {\n log.debug('No LLM model available for voice refine; returning raw only');\n return undefined;\n }\n\n try {\n const userMsg: UserMessage = {\n role: 'user',\n content: `${REFINE_SYSTEM_PROMPT}\\n\\n原文:${raw}`,\n timestamp: Date.now(),\n };\n\n const timeoutSignal =\n typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function'\n ? AbortSignal.timeout(REFINE_TIMEOUT_MS)\n : undefined;\n const mergedSignal =\n signal && timeoutSignal && typeof AbortSignal.any === 'function'\n ? AbortSignal.any([signal, timeoutSignal])\n : signal ?? timeoutSignal;\n\n const result = await complete(\n model,\n { messages: [userMsg] },\n {\n maxTokens: Math.min(raw.length * 3, 4096),\n temperature: 0.2,\n signal: mergedSignal as AbortSignal,\n },\n );\n\n let out = '';\n if (Array.isArray(result.content)) {\n for (const c of result.content) {\n if (c && typeof c === 'object' && (c as { type?: string }).type === 'text') {\n out += String((c as { text?: string }).text || '');\n }\n }\n }\n\n const refined = out.trim();\n if (!refined || refined === raw.trim()) return undefined;\n return refined;\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n log.warn({ errorMessage: msg }, 'Voice refine failed; returning raw only');\n return undefined;\n }\n}\n\nexport function registerVoiceRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service, strictRateLimitMiddleware } = deps;\n\n /**\n * GET /api/voice/providers\n *\n * Lists registered SpeechProviderPlugin ids with configured state for the\n * current gateway config (OpenClaw `tts.providers` equivalent).\n */\n authenticated.get('/api/voice/providers', (c) => {\n const config = service.currentConfig as Config;\n const payload = listTtsProvidersForApi(config);\n return c.json({ ok: true, payload });\n });\n\n /**\n * GET /api/voice/stt-providers\n *\n * Lists registered MediaUnderstandingProvider ids with configured state for\n * the current gateway config (OpenClaw `tools.media.audio.providers` equivalent).\n */\n authenticated.get('/api/voice/stt-providers', (c) => {\n const config = service.currentConfig as Config;\n const payload = listSttProvidersForApi(config);\n return c.json({ ok: true, payload });\n });\n\n /**\n * POST /api/voice/reveal-api-key — return plaintext voice provider apiKey from config file only.\n * Body: `{ kind: 'stt' | 'tts', provider: string }`\n */\n authenticated.post('/api/voice/reveal-api-key', strictRateLimitMiddleware, async (c) => {\n let body: { kind?: unknown; provider?: unknown } = {};\n try {\n body = (await c.req.json()) as typeof body;\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON body' } }, 400);\n }\n const kind = body.kind === 'stt' || body.kind === 'tts' ? body.kind : null;\n const provider = typeof body.provider === 'string' ? body.provider.trim() : '';\n if (!kind) {\n return c.json({ ok: false, error: { message: 'kind must be \"stt\" or \"tts\"' } }, 400);\n }\n if (!provider) {\n return c.json({ ok: false, error: { message: 'provider is required' } }, 400);\n }\n\n const cfg = service.currentConfig as Config;\n const apiKey = readVoiceApiKeyFromConfigFileOnly(cfg, kind, provider);\n return c.json({\n ok: true,\n payload: {\n kind,\n provider,\n apiKey: apiKey ?? null,\n source: apiKey ? ('config' as const) : ('none' as const),\n },\n });\n });\n\n /**\n * POST /api/voice/tts-test\n *\n * Body: { text: string, provider?: string, model?: string, voice?: string }\n * Response: { ok: true, payload: { audio: string, format: string, provider: string } }\n */\n authenticated.post('/api/voice/tts-test', strictRateLimitMiddleware, async (c) => {\n let body: { text?: unknown; provider?: unknown; model?: unknown; voice?: unknown } = {};\n try {\n body = (await c.req.json()) as typeof body;\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON body' } }, 400);\n }\n\n const text = typeof body.text === 'string' ? body.text.trim() : '';\n if (!text) {\n return c.json({ ok: false, error: { message: 'text is required' } }, 400);\n }\n if (text.length > 1000) {\n return c.json({ ok: false, error: { message: 'text exceeds 1000 characters' } }, 400);\n }\n\n const config = service.currentConfig as Config;\n const baseTtsConfig = mergeTtsConfigFromAppConfig(config.messages?.tts);\n const provider = typeof body.provider === 'string' && body.provider.trim() ? body.provider.trim() : baseTtsConfig.provider;\n const ttsConfig = {\n ...baseTtsConfig,\n enabled: true,\n provider,\n fallback: { enabled: false, order: [provider] },\n };\n\n if (!isTTSAvailable(ttsConfig)) {\n return c.json({\n ok: false,\n error: { message: `TTS provider \"${provider}\" is not configured.` },\n }, 503);\n }\n\n try {\n const result = await speak(text, ttsConfig, {\n appConfig: config,\n parseDirectives: false,\n tts: {\n ...(typeof body.model === 'string' && body.model.trim() ? { model: body.model.trim() } : {}),\n ...(typeof body.voice === 'string' && body.voice.trim() ? { voice: body.voice.trim() } : {}),\n },\n });\n return c.json({\n ok: true,\n payload: {\n audio: result.audio.toString('base64'),\n format: result.format,\n provider: result.provider,\n ...(result.duration !== undefined ? { duration: result.duration } : {}),\n },\n });\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n log.error({ errorMessage: msg, provider }, 'Voice TTS test failed');\n return c.json({ ok: false, error: { message: `TTS test failed: ${msg}` } }, 502);\n }\n });\n\n /**\n * POST /api/voice/transcribe\n *\n * Body: { audio: string (base64), mimeType: string, language?: string }\n * Response: { ok: true, payload: { raw: string, refined?: string, language?: string } }\n */\n authenticated.post('/api/voice/transcribe', async (c) => {\n let body: { audio?: string; mimeType?: string; language?: string } = {};\n try {\n body = (await c.req.json()) as typeof body;\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON body' } }, 400);\n }\n\n const { audio, mimeType, language } = body;\n if (!audio || typeof audio !== 'string') {\n return c.json({ ok: false, error: { message: 'Missing required field: audio (base64)' } }, 400);\n }\n if (!mimeType || typeof mimeType !== 'string') {\n return c.json({ ok: false, error: { message: 'Missing required field: mimeType' } }, 400);\n }\n\n // Decode base64 audio\n let audioBuffer: Buffer;\n try {\n audioBuffer = Buffer.from(audio, 'base64');\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid base64 audio data' } }, 400);\n }\n\n if (audioBuffer.length === 0) {\n return c.json({ ok: false, error: { message: 'Empty audio data' } }, 400);\n }\n if (audioBuffer.length > MAX_AUDIO_BYTES) {\n return c.json({ ok: false, error: { message: 'Audio data exceeds 25 MB limit' } }, 400);\n }\n\n // Resolve STT config from app config\n const config = service.currentConfig as Config;\n const sttConfigRaw = config.tools?.media?.audio;\n const sttConfig = mergeSttConfigFromAppConfig(sttConfigRaw, config.tools?.media);\n\n if (!isSTTAvailable(sttConfig)) {\n return c.json({\n ok: false,\n error: { message: 'STT is not configured. Enable STT in gateway config (tools.media.audio).' },\n }, 503);\n }\n\n // Run STT\n let raw: string;\n let detectedLanguage: string | undefined;\n try {\n const result = await transcribe(audioBuffer, sttConfig, {\n language: language || (sttConfig.provider === 'alibaba' ? 'zh' : undefined),\n });\n raw = result.text;\n detectedLanguage = result.language ?? language;\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n log.error({ errorMessage: msg }, 'Voice transcription failed');\n return c.json({ ok: false, error: { message: `Transcription failed: ${msg}` } }, 502);\n }\n\n if (!raw.trim()) {\n return c.json({\n ok: true,\n payload: { raw: '', language: detectedLanguage },\n });\n }\n\n // Run LLM refine (auto, best-effort)\n const refined = await refineTranscript(raw, config);\n\n return c.json({\n ok: true,\n payload: {\n raw,\n ...(refined ? { refined } : {}),\n ...(detectedLanguage ? { language: detectedLanguage } : {}),\n },\n });\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AuthenticatedRouteDeps } from './deps.js';
3
+ export declare function registerWorkflowRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void;
@@ -0,0 +1,347 @@
1
+ import { init_agent_scope, resolveDefaultAgentId } from "../../../agent/agent-scope.js";
2
+ import { extractProfileAgentId } from "../../../config/agent-profile.js";
3
+ import { init_providers, resolveModel } from "../../../providers/index.js";
4
+ import { createWorkflowCatalog } from "../../../agent/workflow/catalog.js";
5
+ import { DelegateSubagentRunner } from "../../../agent/workflow/subagent-runner.js";
6
+ import { resolveModelRef } from "../../../config/agent-typed-models.js";
7
+ import { AgentToolsFactory } from "../../../agent/tools/factory.js";
8
+ import { isTerminalWorkflowRunStatus } from "../../../workflows/domain/run.js";
9
+ import { WorkflowEngine } from "../../../workflows/engine/workflow-engine.js";
10
+ import { WorkflowEventStore } from "../../../workflows/store/event-store.js";
11
+ import { WorkflowRunStore } from "../../../workflows/store/run-store.js";
12
+ import "../../../workflows/index.js";
13
+ import { randomUUID } from "node:crypto";
14
+ //#region src/gateway/hono/routes/workflows.ts
15
+ init_agent_scope();
16
+ init_providers();
17
+ const DEFAULT_WORKFLOW_CONCURRENCY = 4;
18
+ const DEFAULT_WORKFLOW_TIMEOUT_SEC = 1800;
19
+ const DEFAULT_WORKFLOW_MAX_SUBAGENTS = 100;
20
+ const activeWorkflowRuns = /* @__PURE__ */ new Map();
21
+ function registerWorkflowRoutes(authenticated, deps) {
22
+ const { service } = deps;
23
+ authenticated.get("/api/workflows/definitions", (c) => {
24
+ const catalog = createWorkflowCatalog();
25
+ const definitions = catalog.list().map((entry) => {
26
+ try {
27
+ return toWorkflowDefinition(catalog.load(entry.name));
28
+ } catch {
29
+ return null;
30
+ }
31
+ }).filter((definition) => Boolean(definition));
32
+ return c.json({ definitions });
33
+ });
34
+ authenticated.get("/api/workflows/definitions/:id", (c) => {
35
+ const id = c.req.param("id");
36
+ const catalog = createWorkflowCatalog();
37
+ try {
38
+ const definition = toWorkflowDefinition(catalog.load(id));
39
+ return c.json({ definition });
40
+ } catch (err) {
41
+ return c.json({ error: err instanceof Error ? err.message : "Workflow definition not found" }, 404);
42
+ }
43
+ });
44
+ authenticated.post("/api/workflows/definitions", async (c) => {
45
+ const body = await readJsonBody(c.req.raw);
46
+ const name = body.name?.trim();
47
+ const script = body.script?.trim();
48
+ if (!name) return c.json({ error: "name is required" }, 400);
49
+ if (!script) return c.json({ error: "script is required" }, 400);
50
+ const catalog = createWorkflowCatalog();
51
+ try {
52
+ catalog.save(name, script);
53
+ const definition = toWorkflowDefinition(catalog.load(name));
54
+ return c.json({ definition }, 201);
55
+ } catch (err) {
56
+ return c.json({ error: err instanceof Error ? err.message : "Failed to save workflow" }, 400);
57
+ }
58
+ });
59
+ authenticated.delete("/api/workflows/definitions/:id", (c) => {
60
+ const id = c.req.param("id").trim();
61
+ if (!id) return c.json({ error: "id is required" }, 400);
62
+ const catalog = createWorkflowCatalog();
63
+ try {
64
+ if (!catalog.remove(id)) return c.json({ error: "User workflow not found or cannot delete built-in workflow" }, 404);
65
+ return c.json({ removed: true });
66
+ } catch (err) {
67
+ return c.json({ error: err instanceof Error ? err.message : "Failed to delete workflow" }, 400);
68
+ }
69
+ });
70
+ authenticated.get("/api/workflows/stats", async (c) => {
71
+ const agentId = getAgentId(c.req.query("agentId"), service.currentConfig);
72
+ const runs = await createRunStore(service.currentConfig, agentId).listRunSummaries(500);
73
+ return c.json({ stats: buildWorkflowStats(runs) });
74
+ });
75
+ authenticated.post("/api/workflows/runs", async (c) => {
76
+ const body = await readJsonBody(c.req.raw);
77
+ const definitionId = body.definitionId?.trim();
78
+ if (!definitionId) return c.json({ error: "definitionId is required" }, 400);
79
+ const agentId = getAgentId(body.agentId ?? c.req.query("agentId"), service.currentConfig);
80
+ const runId = await queueWorkflowRun({
81
+ deps,
82
+ agentId,
83
+ sessionKey: body.sessionKey?.trim() || `workflow:${agentId}`,
84
+ definitionId,
85
+ input: body.input,
86
+ goal: body.goal,
87
+ source: body.source ?? { kind: "webui" },
88
+ concurrency: normalizePositiveInteger(body.concurrency),
89
+ maxSubagents: normalizePositiveInteger(body.maxSubagents),
90
+ tokenBudget: body.tokenBudget
91
+ });
92
+ if (!runId) return c.json({ error: "Workflow definition not found" }, 404);
93
+ return c.json({ runId }, 202);
94
+ });
95
+ authenticated.get("/api/workflows/runs", async (c) => {
96
+ const agentId = getAgentId(c.req.query("agentId"), service.currentConfig);
97
+ const rawLimit = c.req.query("limit");
98
+ const limit = rawLimit ? Number.parseInt(rawLimit, 10) : 50;
99
+ const runs = await createRunStore(service.currentConfig, agentId).listRunSummaries(Number.isFinite(limit) ? limit : 50);
100
+ return c.json({ runs });
101
+ });
102
+ authenticated.post("/api/workflows/runs/:runId/cancel", async (c) => {
103
+ const runId = c.req.param("runId");
104
+ const agentId = getAgentId(c.req.query("agentId"), service.currentConfig);
105
+ const runStore = createRunStore(service.currentConfig, agentId);
106
+ const controller = activeWorkflowRuns.get(runId);
107
+ if (controller) {
108
+ controller.abort();
109
+ activeWorkflowRuns.delete(runId);
110
+ return c.json({ cancelled: true });
111
+ }
112
+ const view = await runStore.readRunView(runId);
113
+ if (!view) return c.json({ error: "Workflow run not found" }, 404);
114
+ if (isTerminalWorkflowRunStatus(view.run.status)) return c.json({
115
+ cancelled: true,
116
+ alreadyFinished: true
117
+ });
118
+ await new WorkflowEventStore(service.currentConfig, agentId).append({
119
+ runId,
120
+ type: "run_cancelled",
121
+ payload: { reason: "Cancelled by user" }
122
+ });
123
+ const updated = await runStore.rebuildRunView(runId);
124
+ if (updated) service.emit("workflow.run.updated", {
125
+ runId,
126
+ view: updated
127
+ });
128
+ return c.json({ cancelled: true });
129
+ });
130
+ authenticated.get("/api/workflows/runs/:runId", async (c) => {
131
+ const agentId = getAgentId(c.req.query("agentId"), service.currentConfig);
132
+ const runId = c.req.param("runId");
133
+ const view = await createRunStore(service.currentConfig, agentId).readRunView(runId);
134
+ if (!view) return c.json({ error: "Workflow run not found" }, 404);
135
+ return c.json({ view });
136
+ });
137
+ authenticated.post("/api/workflows/runs/:runId/rebuild", async (c) => {
138
+ const agentId = getAgentId(c.req.query("agentId"), service.currentConfig);
139
+ const runId = c.req.param("runId");
140
+ const view = await createRunStore(service.currentConfig, agentId).rebuildRunView(runId);
141
+ if (!view) return c.json({ error: "Workflow run not found" }, 404);
142
+ service.emit("workflow.run.updated", {
143
+ runId,
144
+ view
145
+ });
146
+ return c.json({ view });
147
+ });
148
+ authenticated.post("/api/workflows/runs/:runId/retry", async (c) => {
149
+ const agentId = getAgentId(c.req.query("agentId"), service.currentConfig);
150
+ const runId = c.req.param("runId");
151
+ const existing = await createRunStore(service.currentConfig, agentId).readRunView(runId);
152
+ if (!existing) return c.json({ error: "Workflow run not found" }, 404);
153
+ const newRunId = await queueWorkflowRun({
154
+ deps,
155
+ agentId,
156
+ sessionKey: `workflow:${agentId}`,
157
+ definitionId: existing.run.definitionId,
158
+ input: existing.run.input,
159
+ goal: existing.run.goal,
160
+ source: { kind: "webui" }
161
+ });
162
+ if (!newRunId) return c.json({ error: "Workflow definition not found" }, 404);
163
+ return c.json({ runId: newRunId }, 202);
164
+ });
165
+ }
166
+ function createRunStore(config, agentId) {
167
+ return new WorkflowRunStore(config, agentId, new WorkflowEventStore(config, agentId));
168
+ }
169
+ function getAgentId(rawAgentId, config) {
170
+ const trimmed = rawAgentId?.trim();
171
+ if (trimmed) return trimmed;
172
+ return resolveDefaultAgentId(config);
173
+ }
174
+ function toWorkflowDefinition(loaded) {
175
+ const nowMs = Date.now();
176
+ const phases = loaded.meta.phases?.map((phase, index) => ({
177
+ id: normalizeId(phase.title) || `phase-${index + 1}`,
178
+ title: phase.title,
179
+ description: phase.detail
180
+ })) ?? [];
181
+ return {
182
+ id: loaded.name,
183
+ name: loaded.name,
184
+ title: toTitle(loaded.name),
185
+ description: loaded.meta.description,
186
+ version: "1.0.0",
187
+ phases,
188
+ runtime: {
189
+ kind: "script",
190
+ source: loaded.script
191
+ },
192
+ defaults: {
193
+ concurrency: DEFAULT_WORKFLOW_CONCURRENCY,
194
+ timeoutSec: DEFAULT_WORKFLOW_TIMEOUT_SEC,
195
+ maxSubagents: loaded.meta.estimatedAgents?.max ?? DEFAULT_WORKFLOW_MAX_SUBAGENTS
196
+ },
197
+ metadata: {
198
+ tags: loaded.meta.tags ?? [],
199
+ builtIn: loaded.source === "builtin",
200
+ source: loaded.source,
201
+ whenToUse: loaded.meta.whenToUse,
202
+ estimatedAgents: loaded.meta.estimatedAgents,
203
+ examplePrompts: loaded.meta.examplePrompts,
204
+ i18n: loaded.meta.i18n,
205
+ createdAtMs: nowMs,
206
+ updatedAtMs: nowMs
207
+ }
208
+ };
209
+ }
210
+ async function queueWorkflowRun(params) {
211
+ const { deps, agentId, sessionKey, definitionId } = params;
212
+ const { service } = deps;
213
+ const catalog = createWorkflowCatalog();
214
+ let definition;
215
+ try {
216
+ definition = toWorkflowDefinition(catalog.load(definitionId));
217
+ } catch {
218
+ return null;
219
+ }
220
+ const eventStore = new WorkflowEventStore(service.currentConfig, agentId);
221
+ const runStore = new WorkflowRunStore(service.currentConfig, agentId, eventStore);
222
+ const runId = randomUUID();
223
+ const abortController = new AbortController();
224
+ const engine = createWorkflowEngine({
225
+ deps,
226
+ eventStore,
227
+ runStore,
228
+ sessionKey
229
+ });
230
+ activeWorkflowRuns.set(runId, abortController);
231
+ engine.startRun(definition, {
232
+ runId,
233
+ input: params.input,
234
+ goal: params.goal,
235
+ source: params.source,
236
+ signal: abortController.signal,
237
+ concurrency: params.concurrency,
238
+ maxSubagents: params.maxSubagents,
239
+ tokenBudget: params.tokenBudget
240
+ }).catch((err) => {
241
+ service.emit("workflow.run.error", {
242
+ runId,
243
+ error: err instanceof Error ? err.message : String(err)
244
+ });
245
+ }).finally(() => {
246
+ activeWorkflowRuns.delete(runId);
247
+ });
248
+ return runId;
249
+ }
250
+ function buildWorkflowStats(runs) {
251
+ const activeStatuses = new Set(["queued", "running"]);
252
+ const succeededStatuses = new Set(["succeeded"]);
253
+ const failedStatuses = new Set([
254
+ "failed",
255
+ "timeout",
256
+ "cancelled"
257
+ ]);
258
+ let durationTotal = 0;
259
+ let durationCount = 0;
260
+ const definitionCounts = /* @__PURE__ */ new Map();
261
+ for (const run of runs) {
262
+ definitionCounts.set(run.definitionId, (definitionCounts.get(run.definitionId) ?? 0) + 1);
263
+ if (run.metrics.durationMs != null && Number.isFinite(run.metrics.durationMs)) {
264
+ durationTotal += run.metrics.durationMs;
265
+ durationCount += 1;
266
+ }
267
+ }
268
+ const topDefinitions = [...definitionCounts.entries()].sort((left, right) => right[1] - left[1]).slice(0, 5).map(([definitionId, count]) => ({
269
+ definitionId,
270
+ count
271
+ }));
272
+ return {
273
+ totalRuns: runs.length,
274
+ activeRuns: runs.filter((run) => activeStatuses.has(run.status)).length,
275
+ succeededRuns: runs.filter((run) => succeededStatuses.has(run.status)).length,
276
+ failedRuns: runs.filter((run) => failedStatuses.has(run.status)).length,
277
+ averageDurationMs: durationCount > 0 ? Math.round(durationTotal / durationCount) : null,
278
+ topDefinitions
279
+ };
280
+ }
281
+ function createWorkflowEngine(params) {
282
+ const { service } = params.deps;
283
+ const runner = new DelegateSubagentRunner({
284
+ workspace: service.currentWorkspacePath,
285
+ bus: service.messageBusInstance,
286
+ getDefaultModel: () => resolveModel(service.agentService.getModelForSession(params.sessionKey)),
287
+ getConfig: () => service.currentConfig,
288
+ buildChildTools: (childOptions) => buildWorkflowChildTools(childOptions)
289
+ });
290
+ return new WorkflowEngine({
291
+ cwd: service.currentWorkspacePath,
292
+ eventStore: params.eventStore,
293
+ runStore: params.runStore,
294
+ runner,
295
+ resolveModelId: (modelId) => {
296
+ const agentId = extractProfileAgentId(params.sessionKey, service.currentConfig);
297
+ return resolveModel(resolveModelRef(service.currentConfig, agentId, modelId));
298
+ },
299
+ onEventAppended: (event) => {
300
+ service.emit("workflow.event.appended", {
301
+ runId: event.runId,
302
+ event
303
+ });
304
+ },
305
+ onRunViewUpdated: (view) => {
306
+ service.emit("workflow.run.updated", {
307
+ runId: view.run.id,
308
+ view
309
+ });
310
+ }
311
+ });
312
+ }
313
+ function buildWorkflowChildTools(childOptions) {
314
+ return new AgentToolsFactory({
315
+ workspace: childOptions.workspace,
316
+ bus: childOptions.bus,
317
+ getCurrentContext: () => null,
318
+ getConfig: childOptions.getConfig,
319
+ getPrimaryModel: () => childOptions.model,
320
+ toolExecutorConfig: childOptions.toolExecutorConfig
321
+ }).createAllTools({
322
+ workspace: childOptions.workspace,
323
+ getPrimaryModel: () => childOptions.model,
324
+ disabledTools: new Set(["extensions"])
325
+ });
326
+ }
327
+ async function readJsonBody(request) {
328
+ try {
329
+ return await request.json();
330
+ } catch {
331
+ return {};
332
+ }
333
+ }
334
+ function normalizePositiveInteger(value) {
335
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 1) return;
336
+ return Math.floor(value);
337
+ }
338
+ function normalizeId(value) {
339
+ return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
340
+ }
341
+ function toTitle(value) {
342
+ return value.split(/[_-]+/g).filter(Boolean).map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
343
+ }
344
+ //#endregion
345
+ export { registerWorkflowRoutes };
346
+
347
+ //# sourceMappingURL=workflows.js.map