@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
@@ -1,19 +1,54 @@
1
1
  import { listMediaUnderstandingProviders } from "../../media-understanding/registry.js";
2
2
  import { resolveSTTProviderConfig } from "./factory.js";
3
3
  import { mergeSttConfigFromAppConfig } from "../../channels/attachments/voice-stt-webchat.js";
4
+ import { getVoiceProviderMetadata } from "../metadata/registry.js";
5
+ import "../metadata/index.js";
4
6
  //#region src/voice/stt/list-providers.ts
7
+ function fallbackMetadata(providerId, aliases) {
8
+ return {
9
+ id: providerId,
10
+ capability: "stt",
11
+ displayName: providerId,
12
+ aliases: [...aliases],
13
+ fields: [
14
+ {
15
+ key: "apiKey",
16
+ label: "API Key",
17
+ type: "password",
18
+ secret: true
19
+ },
20
+ {
21
+ key: "model",
22
+ label: "Model",
23
+ type: "string"
24
+ },
25
+ {
26
+ key: "baseUrl",
27
+ label: "Base URL",
28
+ type: "string"
29
+ }
30
+ ],
31
+ diagnostics: {
32
+ requiresApiKey: true,
33
+ configPath: `tools.media.audio.providers.${providerId}`
34
+ }
35
+ };
36
+ }
5
37
  function isSttProviderConfigured(providerId, config) {
6
38
  return resolveSTTProviderConfig(providerId, config) !== null;
7
39
  }
8
- /** List registered audio STT providers with configured state for the current app config. */
40
+ /** List registered audio STT providers with configured state and UI metadata. */
9
41
  function listSttProvidersForApi(config) {
10
42
  const sttConfig = mergeSttConfigFromAppConfig(config?.tools?.media?.audio, config?.tools?.media);
11
43
  return {
12
- providers: listMediaUnderstandingProviders().filter((plugin) => plugin.capabilities?.includes("audio") && typeof plugin.transcribeAudio === "function").map((plugin) => ({
13
- id: plugin.id,
14
- aliases: [...plugin.aliases ?? []],
15
- configured: isSttProviderConfigured(plugin.id, sttConfig)
16
- })),
44
+ providers: listMediaUnderstandingProviders().filter((plugin) => plugin.capabilities?.includes("audio") && typeof plugin.transcribeAudio === "function").map((plugin) => {
45
+ const metadata = getVoiceProviderMetadata("stt", plugin.id) ?? fallbackMetadata(plugin.id, plugin.aliases ?? []);
46
+ return {
47
+ ...metadata,
48
+ aliases: [...metadata.aliases ?? plugin.aliases ?? []],
49
+ configured: isSttProviderConfigured(plugin.id, sttConfig)
50
+ };
51
+ }),
17
52
  active: sttConfig.provider
18
53
  };
19
54
  }
@@ -1 +1 @@
1
- {"version":3,"file":"list-providers.js","names":[],"sources":["../../../../src/voice/stt/list-providers.ts"],"sourcesContent":["/**\n * Registry-driven STT provider listing for gateway / Web UI discovery.\n */\n\nimport type { Config } from '../../config/schema.js';\nimport { mergeSttConfigFromAppConfig } from '../../channels/attachments/voice-stt-webchat.js';\n\nimport { resolveSTTProviderConfig } from './factory.js';\nimport type { STTConfig } from './types.js';\nimport { listMediaUnderstandingProviders } from '../../media-understanding/registry.js';\n\nexport interface SttProviderListEntry {\n id: string;\n aliases: string[];\n configured: boolean;\n}\n\nexport interface SttProvidersPayload {\n providers: SttProviderListEntry[];\n active: string;\n}\n\nexport function isSttProviderConfigured(providerId: string, config: STTConfig): boolean {\n return resolveSTTProviderConfig(providerId, config) !== null;\n}\n\n/** List registered audio STT providers with configured state for the current app config. */\nexport function listSttProvidersForApi(config: Config | undefined): SttProvidersPayload {\n const sttConfig = mergeSttConfigFromAppConfig(config?.tools?.media?.audio, config?.tools?.media);\n const providers: SttProviderListEntry[] = listMediaUnderstandingProviders()\n .filter(\n (plugin) =>\n plugin.capabilities?.includes('audio') && typeof plugin.transcribeAudio === 'function',\n )\n .map((plugin) => ({\n id: plugin.id,\n aliases: [...(plugin.aliases ?? [])],\n configured: isSttProviderConfigured(plugin.id, sttConfig),\n }));\n\n return {\n providers,\n active: sttConfig.provider,\n };\n}\n"],"mappings":";;;;AAsBA,SAAgB,wBAAwB,YAAoB,QAA4B;AACtF,QAAO,yBAAyB,YAAY,OAAO,KAAK;;;AAI1D,SAAgB,uBAAuB,QAAiD;CACtF,MAAM,YAAY,4BAA4B,QAAQ,OAAO,OAAO,OAAO,QAAQ,OAAO,MAAM;AAYhG,QAAO;EACL,WAZwC,iCAAiC,CACxE,QACE,WACC,OAAO,cAAc,SAAS,QAAQ,IAAI,OAAO,OAAO,oBAAoB,WAC/E,CACA,KAAK,YAAY;GAChB,IAAI,OAAO;GACX,SAAS,CAAC,GAAI,OAAO,WAAW,EAAE,CAAE;GACpC,YAAY,wBAAwB,OAAO,IAAI,UAAU;GAC1D,EAGQ;EACT,QAAQ,UAAU;EACnB"}
1
+ {"version":3,"file":"list-providers.js","names":[],"sources":["../../../../src/voice/stt/list-providers.ts"],"sourcesContent":["/**\n * Registry-driven STT provider listing for gateway / Web UI discovery.\n */\n\nimport type { Config } from '../../config/schema.js';\nimport { mergeSttConfigFromAppConfig } from '../../channels/attachments/voice-stt-webchat.js';\nimport { listMediaUnderstandingProviders } from '../../media-understanding/registry.js';\nimport { getVoiceProviderMetadata, type VoiceProviderMetadata } from '../metadata/index.js';\n\nimport { resolveSTTProviderConfig } from './factory.js';\nimport type { STTConfig } from './types.js';\n\nexport interface SttProviderListEntry extends VoiceProviderMetadata {\n aliases: string[];\n configured: boolean;\n}\n\nexport interface SttProvidersPayload {\n providers: SttProviderListEntry[];\n active: string;\n}\n\nfunction fallbackMetadata(providerId: string, aliases: readonly string[]): VoiceProviderMetadata {\n return {\n id: providerId,\n capability: 'stt',\n displayName: providerId,\n aliases: [...aliases],\n fields: [\n { key: 'apiKey', label: 'API Key', type: 'password', secret: true },\n { key: 'model', label: 'Model', type: 'string' },\n { key: 'baseUrl', label: 'Base URL', type: 'string' },\n ],\n diagnostics: {\n requiresApiKey: true,\n configPath: `tools.media.audio.providers.${providerId}`,\n },\n };\n}\n\nexport function isSttProviderConfigured(providerId: string, config: STTConfig): boolean {\n return resolveSTTProviderConfig(providerId, config) !== null;\n}\n\n/** List registered audio STT providers with configured state and UI metadata. */\nexport function listSttProvidersForApi(config: Config | undefined): SttProvidersPayload {\n const sttConfig = mergeSttConfigFromAppConfig(config?.tools?.media?.audio, config?.tools?.media);\n const providers: SttProviderListEntry[] = listMediaUnderstandingProviders()\n .filter(\n (plugin) =>\n plugin.capabilities?.includes('audio') && typeof plugin.transcribeAudio === 'function',\n )\n .map((plugin) => {\n const metadata = getVoiceProviderMetadata('stt', plugin.id) ?? fallbackMetadata(plugin.id, plugin.aliases ?? []);\n return {\n ...metadata,\n aliases: [...(metadata.aliases ?? plugin.aliases ?? [])],\n configured: isSttProviderConfigured(plugin.id, sttConfig),\n };\n });\n\n return {\n providers,\n active: sttConfig.provider,\n };\n}\n"],"mappings":";;;;;;AAsBA,SAAS,iBAAiB,YAAoB,SAAmD;AAC/F,QAAO;EACL,IAAI;EACJ,YAAY;EACZ,aAAa;EACb,SAAS,CAAC,GAAG,QAAQ;EACrB,QAAQ;GACN;IAAE,KAAK;IAAU,OAAO;IAAW,MAAM;IAAY,QAAQ;IAAM;GACnE;IAAE,KAAK;IAAS,OAAO;IAAS,MAAM;IAAU;GAChD;IAAE,KAAK;IAAW,OAAO;IAAY,MAAM;IAAU;GACtD;EACD,aAAa;GACX,gBAAgB;GAChB,YAAY,+BAA+B;GAC5C;EACF;;AAGH,SAAgB,wBAAwB,YAAoB,QAA4B;AACtF,QAAO,yBAAyB,YAAY,OAAO,KAAK;;;AAI1D,SAAgB,uBAAuB,QAAiD;CACtF,MAAM,YAAY,4BAA4B,QAAQ,OAAO,OAAO,OAAO,QAAQ,OAAO,MAAM;AAehG,QAAO;EACL,WAfwC,iCAAiC,CACxE,QACE,WACC,OAAO,cAAc,SAAS,QAAQ,IAAI,OAAO,OAAO,oBAAoB,WAC/E,CACA,KAAK,WAAW;GACf,MAAM,WAAW,yBAAyB,OAAO,OAAO,GAAG,IAAI,iBAAiB,OAAO,IAAI,OAAO,WAAW,EAAE,CAAC;AAChH,UAAO;IACL,GAAG;IACH,SAAS,CAAC,GAAI,SAAS,WAAW,OAAO,WAAW,EAAE,CAAE;IACxD,YAAY,wBAAwB,OAAO,IAAI,UAAU;IAC1D;IAIM;EACT,QAAQ,UAAU;EACnB"}
@@ -27,27 +27,11 @@ import type { MediaUnderstandingModelEntry } from '../../media-understanding/typ
27
27
  export interface STTConfig {
28
28
  enabled: boolean;
29
29
  provider: string;
30
- alibaba?: {
31
- apiKey?: string;
32
- model?: string;
33
- baseUrl?: string;
34
- headers?: Record<string, string>;
35
- language?: string;
36
- prompt?: string;
37
- };
38
- openai?: {
39
- apiKey?: string;
40
- model?: string;
41
- baseUrl?: string;
42
- headers?: Record<string, string>;
43
- language?: string;
44
- prompt?: string;
45
- };
46
30
  /** Capability-local model chain (`tools.media.audio.models`). */
47
31
  models?: MediaUnderstandingModelEntry[];
48
32
  /** Shared model entries merged from `tools.media.models` at runtime. */
49
33
  sharedModels?: MediaUnderstandingModelEntry[];
50
- /** OpenClaw-aligned provider settings map (`tools.media.audio.providers.<id>`). */
34
+ /** Provider settings map (`tools.media.audio.providers.<id>`). */
51
35
  providers?: Record<string, Record<string, unknown>>;
52
36
  fallback?: {
53
37
  enabled: boolean;
@@ -55,7 +39,6 @@ export interface STTConfig {
55
39
  };
56
40
  /** Hard timeout per provider call (ms). Default 60s. */
57
41
  timeoutMs?: number;
58
- [key: string]: unknown;
59
42
  }
60
43
  export type STTProviderAttemptOutcome = 'success' | 'skipped' | 'failed';
61
44
  export type STTProviderFailureReason = 'success' | 'not_configured' | 'timeout' | 'provider_error' | 'unsupported_format' | 'unknown';
@@ -2,8 +2,10 @@
2
2
  const DEFAULT_STT_CONFIG = {
3
3
  enabled: false,
4
4
  provider: "alibaba",
5
- alibaba: { model: "paraformer-v2" },
6
- openai: { model: "whisper-1" },
5
+ providers: {
6
+ alibaba: { model: "paraformer-v2" },
7
+ openai: { model: "whisper-1" }
8
+ },
7
9
  fallback: {
8
10
  enabled: true,
9
11
  order: ["alibaba", "openai"]
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../../../../src/voice/stt/types.ts"],"sourcesContent":["/**\n * STT public types — data shapes consumed by downstream code (channels,\n * schema validation, gateway payloads).\n *\n * Provider implementations live behind `MediaUnderstandingProvider.transcribeAudio`\n * (see src/media-understanding/types.ts).\n */\n\nexport interface STTResult {\n /** Transcribed text. */\n text: string;\n /** Provider that performed the transcription (e.g. \"alibaba\", \"openai\"). */\n provider: string;\n /** Audio duration in seconds (if reported). */\n duration?: number;\n /** Detected language (if reported). */\n language?: string;\n}\n\nexport interface STTOptions {\n /** Language hint (e.g. 'zh', 'en'). */\n language?: string;\n /** Model id (provider-specific). */\n model?: string;\n}\n\nexport type { MediaUnderstandingModelEntry } from '../../media-understanding/types.js';\nimport type { MediaUnderstandingModelEntry } from '../../media-understanding/types.js';\n\n/** STTConfig consumed by the schema and downstream wiring. */\nexport interface STTConfig {\n enabled: boolean;\n provider: string;\n alibaba?: {\n apiKey?: string;\n model?: string;\n baseUrl?: string;\n headers?: Record<string, string>;\n language?: string;\n prompt?: string;\n };\n openai?: {\n apiKey?: string;\n model?: string;\n baseUrl?: string;\n headers?: Record<string, string>;\n language?: string;\n prompt?: string;\n };\n /** Capability-local model chain (`tools.media.audio.models`). */\n models?: MediaUnderstandingModelEntry[];\n /** Shared model entries merged from `tools.media.models` at runtime. */\n sharedModels?: MediaUnderstandingModelEntry[];\n /** OpenClaw-aligned provider settings map (`tools.media.audio.providers.<id>`). */\n providers?: Record<string, Record<string, unknown>>;\n fallback?: {\n enabled: boolean;\n order: string[];\n };\n /** Hard timeout per provider call (ms). Default 60s. */\n timeoutMs?: number;\n [key: string]: unknown;\n}\n\nexport type STTProviderAttemptOutcome = 'success' | 'skipped' | 'failed';\n\nexport type STTProviderFailureReason =\n | 'success'\n | 'not_configured'\n | 'timeout'\n | 'provider_error'\n | 'unsupported_format'\n | 'unknown';\n\nexport interface STTProviderAttempt {\n provider: string;\n outcome: STTProviderAttemptOutcome;\n reasonCode: STTProviderFailureReason;\n latencyMs: number;\n error?: string;\n}\n\nexport interface STTResultWithTracking extends STTResult {\n attempts: STTProviderAttempt[];\n fallbackFrom?: string;\n attemptedProviders: string[];\n}\n\nexport const DEFAULT_STT_CONFIG: STTConfig = {\n enabled: false,\n provider: 'alibaba',\n alibaba: {\n model: 'paraformer-v2',\n },\n openai: {\n model: 'whisper-1',\n },\n fallback: {\n enabled: true,\n order: ['alibaba', 'openai'],\n },\n timeoutMs: 60_000,\n};\n"],"mappings":";AAwFA,MAAa,qBAAgC;CAC3C,SAAS;CACT,UAAU;CACV,SAAS,EACP,OAAO,iBACR;CACD,QAAQ,EACN,OAAO,aACR;CACD,UAAU;EACR,SAAS;EACT,OAAO,CAAC,WAAW,SAAS;EAC7B;CACD,WAAW;CACZ"}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../../../src/voice/stt/types.ts"],"sourcesContent":["/**\n * STT public types — data shapes consumed by downstream code (channels,\n * schema validation, gateway payloads).\n *\n * Provider implementations live behind `MediaUnderstandingProvider.transcribeAudio`\n * (see src/media-understanding/types.ts).\n */\n\nexport interface STTResult {\n /** Transcribed text. */\n text: string;\n /** Provider that performed the transcription (e.g. \"alibaba\", \"openai\"). */\n provider: string;\n /** Audio duration in seconds (if reported). */\n duration?: number;\n /** Detected language (if reported). */\n language?: string;\n}\n\nexport interface STTOptions {\n /** Language hint (e.g. 'zh', 'en'). */\n language?: string;\n /** Model id (provider-specific). */\n model?: string;\n}\n\nexport type { MediaUnderstandingModelEntry } from '../../media-understanding/types.js';\nimport type { MediaUnderstandingModelEntry } from '../../media-understanding/types.js';\n\n/** STTConfig consumed by the schema and downstream wiring. */\nexport interface STTConfig {\n enabled: boolean;\n provider: string;\n /** Capability-local model chain (`tools.media.audio.models`). */\n models?: MediaUnderstandingModelEntry[];\n /** Shared model entries merged from `tools.media.models` at runtime. */\n sharedModels?: MediaUnderstandingModelEntry[];\n /** Provider settings map (`tools.media.audio.providers.<id>`). */\n providers?: Record<string, Record<string, unknown>>;\n fallback?: {\n enabled: boolean;\n order: string[];\n };\n /** Hard timeout per provider call (ms). Default 60s. */\n timeoutMs?: number;\n}\n\nexport type STTProviderAttemptOutcome = 'success' | 'skipped' | 'failed';\n\nexport type STTProviderFailureReason =\n | 'success'\n | 'not_configured'\n | 'timeout'\n | 'provider_error'\n | 'unsupported_format'\n | 'unknown';\n\nexport interface STTProviderAttempt {\n provider: string;\n outcome: STTProviderAttemptOutcome;\n reasonCode: STTProviderFailureReason;\n latencyMs: number;\n error?: string;\n}\n\nexport interface STTResultWithTracking extends STTResult {\n attempts: STTProviderAttempt[];\n fallbackFrom?: string;\n attemptedProviders: string[];\n}\n\nexport const DEFAULT_STT_CONFIG: STTConfig = {\n enabled: false,\n provider: 'alibaba',\n providers: {\n alibaba: { model: 'paraformer-v2' },\n openai: { model: 'whisper-1' },\n },\n fallback: {\n enabled: true,\n order: ['alibaba', 'openai'],\n },\n timeoutMs: 60_000,\n};\n"],"mappings":";AAuEA,MAAa,qBAAgC;CAC3C,SAAS;CACT,UAAU;CACV,WAAW;EACT,SAAS,EAAE,OAAO,iBAAiB;EACnC,QAAQ,EAAE,OAAO,aAAa;EAC/B;CACD,UAAU;EACR,SAAS;EACT,OAAO,CAAC,WAAW,SAAS;EAC7B;CACD,WAAW;CACZ"}
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
- import { join } from "path";
4
3
  import { unlink, writeFile } from "fs/promises";
4
+ import { join } from "path";
5
5
  import { spawn } from "child_process";
6
6
  import { tmpdir } from "os";
7
7
  //#region src/voice/tts/audio.ts
@@ -1,19 +1,15 @@
1
1
  /**
2
2
  * TTS config slice resolution — maps persisted config to per-provider raw config.
3
3
  *
4
- * Supports OpenClaw-aligned `messages.tts.providers.<id>` plus legacy flat keys
5
- * (`messages.tts.openai`, `messages.tts.tts-local-cli`, …).
4
+ * Reads `messages.tts.providers.<id>` only — there is no legacy flat-key form.
6
5
  */
7
6
  import type { TTSConfig } from './types.js';
8
- /** Top-level `messages.tts` keys that are not provider config buckets. */
9
- export declare const TTS_CONFIG_RESERVED_KEYS: Set<string>;
10
- /** Collect provider-id → raw config entries from providers map + legacy flat keys. */
7
+ /** Collect provider-id raw config entries from the `providers` map. */
11
8
  export declare function collectTtsProviderConfigEntries(config: Partial<TTSConfig> | Record<string, unknown> | undefined): Record<string, Record<string, unknown>>;
12
9
  /** Resolve the raw config slice for one provider id. */
13
10
  export declare function resolveTtsProviderConfigSlice(providerId: string, config: Partial<TTSConfig> | Record<string, unknown> | undefined): Record<string, unknown>;
14
11
  /**
15
12
  * Build the `rawConfig` object passed into `SpeechProviderPlugin.resolveConfig`.
16
- * Includes the full TTS block, normalized `providers` map, and a top-level slice
17
- * for the active provider (matches built-in `rawConfig[id] ?? rawConfig` reads).
13
+ * Includes the full TTS block plus a normalized `providers` map.
18
14
  */
19
15
  export declare function buildTtsResolveRawConfig(providerId: string, config: Partial<TTSConfig> | Record<string, unknown>): Record<string, unknown>;
@@ -1,42 +1,13 @@
1
1
  //#region src/voice/tts/config-slice.ts
2
- /** Top-level `messages.tts` keys that are not provider config buckets. */
3
- const TTS_CONFIG_RESERVED_KEYS = new Set([
4
- "auto",
5
- "enabled",
6
- "maxTextLength",
7
- "mode",
8
- "modelOverrides",
9
- "persona",
10
- "personas",
11
- "prefsPath",
12
- "provider",
13
- "providers",
14
- "summaryModel",
15
- "timeoutMs",
16
- "trigger",
17
- "fallback",
18
- "summarization"
19
- ]);
20
2
  function asRecord(value) {
21
3
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
22
4
  }
23
- function asProviderConfig(value) {
24
- return asRecord(value) ?? {};
25
- }
26
- /** Collect provider-id → raw config entries from providers map + legacy flat keys. */
5
+ /** Collect provider-id → raw config entries from the `providers` map. */
27
6
  function collectTtsProviderConfigEntries(config) {
28
- const raw = config ?? {};
7
+ const providers = asRecord((config ?? {}).providers);
8
+ if (!providers) return {};
29
9
  const entries = {};
30
- const providers = asRecord(raw.providers);
31
- if (providers) for (const [providerId, value] of Object.entries(providers)) entries[providerId] = {
32
- ...entries[providerId],
33
- ...asProviderConfig(value)
34
- };
35
- for (const [key, value] of Object.entries(raw)) {
36
- if (TTS_CONFIG_RESERVED_KEYS.has(key)) continue;
37
- if (typeof value !== "object" || value === null || Array.isArray(value)) continue;
38
- if (entries[key] === void 0) entries[key] = asProviderConfig(value);
39
- }
10
+ for (const [providerId, value] of Object.entries(providers)) entries[providerId] = { ...asRecord(value) ?? {} };
40
11
  return entries;
41
12
  }
42
13
  /** Resolve the raw config slice for one provider id. */
@@ -45,19 +16,17 @@ function resolveTtsProviderConfigSlice(providerId, config) {
45
16
  }
46
17
  /**
47
18
  * Build the `rawConfig` object passed into `SpeechProviderPlugin.resolveConfig`.
48
- * Includes the full TTS block, normalized `providers` map, and a top-level slice
49
- * for the active provider (matches built-in `rawConfig[id] ?? rawConfig` reads).
19
+ * Includes the full TTS block plus a normalized `providers` map.
50
20
  */
51
21
  function buildTtsResolveRawConfig(providerId, config) {
52
22
  const entries = collectTtsProviderConfigEntries(config);
53
- const slice = entries[providerId] ?? {};
54
23
  return {
55
24
  ...config,
56
25
  providers: entries,
57
- [providerId]: slice
26
+ [providerId]: entries[providerId] ?? {}
58
27
  };
59
28
  }
60
29
  //#endregion
61
- export { TTS_CONFIG_RESERVED_KEYS, buildTtsResolveRawConfig, collectTtsProviderConfigEntries, resolveTtsProviderConfigSlice };
30
+ export { buildTtsResolveRawConfig, collectTtsProviderConfigEntries, resolveTtsProviderConfigSlice };
62
31
 
63
32
  //# sourceMappingURL=config-slice.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-slice.js","names":[],"sources":["../../../../src/voice/tts/config-slice.ts"],"sourcesContent":["/**\n * TTS config slice resolution — maps persisted config to per-provider raw config.\n *\n * Supports OpenClaw-aligned `messages.tts.providers.<id>` plus legacy flat keys\n * (`messages.tts.openai`, `messages.tts.tts-local-cli`, …).\n */\n\nimport type { TTSConfig } from './types.js';\n\n/** Top-level `messages.tts` keys that are not provider config buckets. */\nexport const TTS_CONFIG_RESERVED_KEYS = new Set([\n 'auto',\n 'enabled',\n 'maxTextLength',\n 'mode',\n 'modelOverrides',\n 'persona',\n 'personas',\n 'prefsPath',\n 'provider',\n 'providers',\n 'summaryModel',\n 'timeoutMs',\n 'trigger',\n 'fallback',\n 'summarization',\n]);\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction asProviderConfig(value: unknown): Record<string, unknown> {\n return asRecord(value) ?? {};\n}\n\n/** Collect provider-id → raw config entries from providers map + legacy flat keys. */\nexport function collectTtsProviderConfigEntries(\n config: Partial<TTSConfig> | Record<string, unknown> | undefined,\n): Record<string, Record<string, unknown>> {\n const raw = (config ?? {}) as Record<string, unknown>;\n const entries: Record<string, Record<string, unknown>> = {};\n\n const providers = asRecord(raw.providers);\n if (providers) {\n for (const [providerId, value] of Object.entries(providers)) {\n entries[providerId] = { ...entries[providerId], ...asProviderConfig(value) };\n }\n }\n\n for (const [key, value] of Object.entries(raw)) {\n if (TTS_CONFIG_RESERVED_KEYS.has(key)) {\n continue;\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n continue;\n }\n if (entries[key] === undefined) {\n entries[key] = asProviderConfig(value);\n }\n }\n\n return entries;\n}\n\n/** Resolve the raw config slice for one provider id. */\nexport function resolveTtsProviderConfigSlice(\n providerId: string,\n config: Partial<TTSConfig> | Record<string, unknown> | undefined,\n): Record<string, unknown> {\n const entries = collectTtsProviderConfigEntries(config);\n return entries[providerId] ?? {};\n}\n\n/**\n * Build the `rawConfig` object passed into `SpeechProviderPlugin.resolveConfig`.\n * Includes the full TTS block, normalized `providers` map, and a top-level slice\n * for the active provider (matches built-in `rawConfig[id] ?? rawConfig` reads).\n */\nexport function buildTtsResolveRawConfig(\n providerId: string,\n config: Partial<TTSConfig> | Record<string, unknown>,\n): Record<string, unknown> {\n const entries = collectTtsProviderConfigEntries(config);\n const slice = entries[providerId] ?? {};\n return {\n ...(config as Record<string, unknown>),\n providers: entries,\n [providerId]: slice,\n };\n}\n"],"mappings":";;AAUA,MAAa,2BAA2B,IAAI,IAAI;CAC9C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,SAAS,SAAS,OAAqD;AACrE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM,GACtE,QACD,KAAA;;AAGN,SAAS,iBAAiB,OAAyC;AACjE,QAAO,SAAS,MAAM,IAAI,EAAE;;;AAI9B,SAAgB,gCACd,QACyC;CACzC,MAAM,MAAO,UAAU,EAAE;CACzB,MAAM,UAAmD,EAAE;CAE3D,MAAM,YAAY,SAAS,IAAI,UAAU;AACzC,KAAI,UACF,MAAK,MAAM,CAAC,YAAY,UAAU,OAAO,QAAQ,UAAU,CACzD,SAAQ,cAAc;EAAE,GAAG,QAAQ;EAAa,GAAG,iBAAiB,MAAM;EAAE;AAIhF,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,EAAE;AAC9C,MAAI,yBAAyB,IAAI,IAAI,CACnC;AAEF,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,MAAM,CACrE;AAEF,MAAI,QAAQ,SAAS,KAAA,EACnB,SAAQ,OAAO,iBAAiB,MAAM;;AAI1C,QAAO;;;AAIT,SAAgB,8BACd,YACA,QACyB;AAEzB,QADgB,gCAAgC,OAClC,CAAC,eAAe,EAAE;;;;;;;AAQlC,SAAgB,yBACd,YACA,QACyB;CACzB,MAAM,UAAU,gCAAgC,OAAO;CACvD,MAAM,QAAQ,QAAQ,eAAe,EAAE;AACvC,QAAO;EACL,GAAI;EACJ,WAAW;GACV,aAAa;EACf"}
1
+ {"version":3,"file":"config-slice.js","names":[],"sources":["../../../../src/voice/tts/config-slice.ts"],"sourcesContent":["/**\n * TTS config slice resolution — maps persisted config to per-provider raw config.\n *\n * Reads `messages.tts.providers.<id>` only there is no legacy flat-key form.\n */\n\nimport type { TTSConfig } from './types.js';\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\n/** Collect provider-id → raw config entries from the `providers` map. */\nexport function collectTtsProviderConfigEntries(\n config: Partial<TTSConfig> | Record<string, unknown> | undefined,\n): Record<string, Record<string, unknown>> {\n const raw = (config ?? {}) as Record<string, unknown>;\n const providers = asRecord(raw.providers);\n if (!providers) return {};\n const entries: Record<string, Record<string, unknown>> = {};\n for (const [providerId, value] of Object.entries(providers)) {\n entries[providerId] = { ...(asRecord(value) ?? {}) };\n }\n return entries;\n}\n\n/** Resolve the raw config slice for one provider id. */\nexport function resolveTtsProviderConfigSlice(\n providerId: string,\n config: Partial<TTSConfig> | Record<string, unknown> | undefined,\n): Record<string, unknown> {\n return collectTtsProviderConfigEntries(config)[providerId] ?? {};\n}\n\n/**\n * Build the `rawConfig` object passed into `SpeechProviderPlugin.resolveConfig`.\n * Includes the full TTS block plus a normalized `providers` map.\n */\nexport function buildTtsResolveRawConfig(\n providerId: string,\n config: Partial<TTSConfig> | Record<string, unknown>,\n): Record<string, unknown> {\n const entries = collectTtsProviderConfigEntries(config);\n return {\n ...(config as Record<string, unknown>),\n providers: entries,\n [providerId]: entries[providerId] ?? {},\n };\n}\n"],"mappings":";AAQA,SAAS,SAAS,OAAqD;AACrE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM,GACtE,QACD,KAAA;;;AAIN,SAAgB,gCACd,QACyC;CAEzC,MAAM,YAAY,UADL,UAAU,EAAE,EACM,UAAU;AACzC,KAAI,CAAC,UAAW,QAAO,EAAE;CACzB,MAAM,UAAmD,EAAE;AAC3D,MAAK,MAAM,CAAC,YAAY,UAAU,OAAO,QAAQ,UAAU,CACzD,SAAQ,cAAc,EAAE,GAAI,SAAS,MAAM,IAAI,EAAE,EAAG;AAEtD,QAAO;;;AAIT,SAAgB,8BACd,YACA,QACyB;AACzB,QAAO,gCAAgC,OAAO,CAAC,eAAe,EAAE;;;;;;AAOlE,SAAgB,yBACd,YACA,QACyB;CACzB,MAAM,UAAU,gCAAgC,OAAO;AACvD,QAAO;EACL,GAAI;EACJ,WAAW;GACV,aAAa,QAAQ,eAAe,EAAE;EACxC"}
@@ -2,8 +2,8 @@
2
2
  * Registry-driven TTS provider listing for gateway / Web UI discovery.
3
3
  */
4
4
  import type { Config } from '../../config/schema.js';
5
- export interface TtsProviderListEntry {
6
- id: string;
5
+ import { type VoiceProviderMetadata } from '../metadata/index.js';
6
+ export interface TtsProviderListEntry extends VoiceProviderMetadata {
7
7
  aliases: string[];
8
8
  configured: boolean;
9
9
  }
@@ -11,5 +11,5 @@ export interface TtsProvidersPayload {
11
11
  providers: TtsProviderListEntry[];
12
12
  active: string;
13
13
  }
14
- /** List registered speech providers with configured state for the current app config. */
14
+ /** List registered speech providers with configured state and UI metadata. */
15
15
  export declare function listTtsProvidersForApi(config: Config | undefined): TtsProvidersPayload;
@@ -1,16 +1,51 @@
1
1
  import { listSpeechProviders } from "./speech-registry.js";
2
2
  import { resolveSpeechProvider } from "./factory.js";
3
3
  import { mergeTtsConfigFromAppConfig } from "./merge-config.js";
4
+ import { getVoiceProviderMetadata } from "../metadata/registry.js";
5
+ import "../metadata/index.js";
4
6
  //#region src/voice/tts/list-providers.ts
5
- /** List registered speech providers with configured state for the current app config. */
7
+ function fallbackMetadata(providerId, aliases) {
8
+ return {
9
+ id: providerId,
10
+ capability: "tts",
11
+ displayName: providerId,
12
+ aliases: [...aliases],
13
+ fields: [
14
+ {
15
+ key: "apiKey",
16
+ label: "API Key",
17
+ type: "password",
18
+ secret: true
19
+ },
20
+ {
21
+ key: "model",
22
+ label: "Model",
23
+ type: "string"
24
+ },
25
+ {
26
+ key: "voice",
27
+ label: "Voice",
28
+ type: "string"
29
+ }
30
+ ],
31
+ diagnostics: {
32
+ requiresApiKey: true,
33
+ configPath: `messages.tts.providers.${providerId}`
34
+ }
35
+ };
36
+ }
37
+ /** List registered speech providers with configured state and UI metadata. */
6
38
  function listTtsProvidersForApi(config) {
7
39
  const ttsConfig = mergeTtsConfigFromAppConfig(config?.messages?.tts);
8
40
  return {
9
- providers: listSpeechProviders().map((plugin) => ({
10
- id: plugin.id,
11
- aliases: [...plugin.aliases ?? []],
12
- configured: resolveSpeechProvider(plugin.id, ttsConfig) !== null
13
- })),
41
+ providers: listSpeechProviders().map((plugin) => {
42
+ const metadata = getVoiceProviderMetadata("tts", plugin.id) ?? fallbackMetadata(plugin.id, plugin.aliases ?? []);
43
+ return {
44
+ ...metadata,
45
+ aliases: [...metadata.aliases ?? plugin.aliases ?? []],
46
+ configured: resolveSpeechProvider(plugin.id, ttsConfig) !== null
47
+ };
48
+ }),
14
49
  active: ttsConfig.provider
15
50
  };
16
51
  }
@@ -1 +1 @@
1
- {"version":3,"file":"list-providers.js","names":[],"sources":["../../../../src/voice/tts/list-providers.ts"],"sourcesContent":["/**\n * Registry-driven TTS provider listing for gateway / Web UI discovery.\n */\n\nimport type { Config } from '../../config/schema.js';\n\nimport { resolveSpeechProvider } from './factory.js';\nimport { mergeTtsConfigFromAppConfig } from './merge-config.js';\nimport { listSpeechProviders } from './speech-registry.js';\n\nexport interface TtsProviderListEntry {\n id: string;\n aliases: string[];\n configured: boolean;\n}\n\nexport interface TtsProvidersPayload {\n providers: TtsProviderListEntry[];\n active: string;\n}\n\n/** List registered speech providers with configured state for the current app config. */\nexport function listTtsProvidersForApi(config: Config | undefined): TtsProvidersPayload {\n const ttsConfig = mergeTtsConfigFromAppConfig(config?.messages?.tts);\n const providers: TtsProviderListEntry[] = listSpeechProviders().map((plugin) => ({\n id: plugin.id,\n aliases: [...(plugin.aliases ?? [])],\n configured: resolveSpeechProvider(plugin.id, ttsConfig) !== null,\n }));\n\n return {\n providers,\n active: ttsConfig.provider,\n };\n}\n"],"mappings":";;;;;AAsBA,SAAgB,uBAAuB,QAAiD;CACtF,MAAM,YAAY,4BAA4B,QAAQ,UAAU,IAAI;AAOpE,QAAO;EACL,WAPwC,qBAAqB,CAAC,KAAK,YAAY;GAC/E,IAAI,OAAO;GACX,SAAS,CAAC,GAAI,OAAO,WAAW,EAAE,CAAE;GACpC,YAAY,sBAAsB,OAAO,IAAI,UAAU,KAAK;GAC7D,EAGU;EACT,QAAQ,UAAU;EACnB"}
1
+ {"version":3,"file":"list-providers.js","names":[],"sources":["../../../../src/voice/tts/list-providers.ts"],"sourcesContent":["/**\n * Registry-driven TTS provider listing for gateway / Web UI discovery.\n */\n\nimport type { Config } from '../../config/schema.js';\nimport { getVoiceProviderMetadata, type VoiceProviderMetadata } from '../metadata/index.js';\n\nimport { resolveSpeechProvider } from './factory.js';\nimport { mergeTtsConfigFromAppConfig } from './merge-config.js';\nimport { listSpeechProviders } from './speech-registry.js';\n\nexport interface TtsProviderListEntry extends VoiceProviderMetadata {\n aliases: string[];\n configured: boolean;\n}\n\nexport interface TtsProvidersPayload {\n providers: TtsProviderListEntry[];\n active: string;\n}\n\nfunction fallbackMetadata(providerId: string, aliases: readonly string[]): VoiceProviderMetadata {\n return {\n id: providerId,\n capability: 'tts',\n displayName: providerId,\n aliases: [...aliases],\n fields: [\n { key: 'apiKey', label: 'API Key', type: 'password', secret: true },\n { key: 'model', label: 'Model', type: 'string' },\n { key: 'voice', label: 'Voice', type: 'string' },\n ],\n diagnostics: {\n requiresApiKey: true,\n configPath: `messages.tts.providers.${providerId}`,\n },\n };\n}\n\n/** List registered speech providers with configured state and UI metadata. */\nexport function listTtsProvidersForApi(config: Config | undefined): TtsProvidersPayload {\n const ttsConfig = mergeTtsConfigFromAppConfig(config?.messages?.tts);\n const providers: TtsProviderListEntry[] = listSpeechProviders().map((plugin) => {\n const metadata = getVoiceProviderMetadata('tts', plugin.id) ?? fallbackMetadata(plugin.id, plugin.aliases ?? []);\n return {\n ...metadata,\n aliases: [...(metadata.aliases ?? plugin.aliases ?? [])],\n configured: resolveSpeechProvider(plugin.id, ttsConfig) !== null,\n };\n });\n\n return {\n providers,\n active: ttsConfig.provider,\n };\n}\n"],"mappings":";;;;;;AAqBA,SAAS,iBAAiB,YAAoB,SAAmD;AAC/F,QAAO;EACL,IAAI;EACJ,YAAY;EACZ,aAAa;EACb,SAAS,CAAC,GAAG,QAAQ;EACrB,QAAQ;GACN;IAAE,KAAK;IAAU,OAAO;IAAW,MAAM;IAAY,QAAQ;IAAM;GACnE;IAAE,KAAK;IAAS,OAAO;IAAS,MAAM;IAAU;GAChD;IAAE,KAAK;IAAS,OAAO;IAAS,MAAM;IAAU;GACjD;EACD,aAAa;GACX,gBAAgB;GAChB,YAAY,0BAA0B;GACvC;EACF;;;AAIH,SAAgB,uBAAuB,QAAiD;CACtF,MAAM,YAAY,4BAA4B,QAAQ,UAAU,IAAI;AAUpE,QAAO;EACL,WAVwC,qBAAqB,CAAC,KAAK,WAAW;GAC9E,MAAM,WAAW,yBAAyB,OAAO,OAAO,GAAG,IAAI,iBAAiB,OAAO,IAAI,OAAO,WAAW,EAAE,CAAC;AAChH,UAAO;IACL,GAAG;IACH,SAAS,CAAC,GAAI,SAAS,WAAW,OAAO,WAAW,EAAE,CAAE;IACxD,YAAY,sBAAsB,OAAO,IAAI,UAAU,KAAK;IAC7D;IAIQ;EACT,QAAQ,UAAU;EACnB"}
@@ -1,4 +1,3 @@
1
- import { TTS_CONFIG_RESERVED_KEYS, collectTtsProviderConfigEntries } from "./config-slice.js";
2
1
  import { isTTSAvailable } from "./factory.js";
3
2
  import { DEFAULT_TTS_CONFIG } from "./types.js";
4
3
  //#region src/voice/tts/merge-config.ts
@@ -23,51 +22,9 @@ function mergeProviderEntries(base, patch) {
23
22
  };
24
23
  return merged;
25
24
  }
26
- function mergeKnownFlatProviderSlices(merged, patch) {
27
- return {
28
- ...merged,
29
- alibaba: {
30
- ...DEFAULT_TTS_CONFIG.alibaba,
31
- ...patch.alibaba
32
- },
33
- openai: {
34
- ...DEFAULT_TTS_CONFIG.openai,
35
- ...patch.openai
36
- },
37
- edge: {
38
- ...DEFAULT_TTS_CONFIG.edge,
39
- ...patch.edge
40
- },
41
- minimax: {
42
- ...DEFAULT_TTS_CONFIG.minimax,
43
- ...patch.minimax
44
- }
45
- };
46
- }
47
- function mergeExtensionFlatProviderSlices(merged, patch) {
48
- const next = { ...merged };
49
- for (const [key, value] of Object.entries(patch)) {
50
- if (TTS_CONFIG_RESERVED_KEYS.has(key)) continue;
51
- if ([
52
- "alibaba",
53
- "openai",
54
- "edge",
55
- "minimax"
56
- ].includes(key)) continue;
57
- if (typeof value !== "object" || value === null || Array.isArray(value)) continue;
58
- const existing = next[key];
59
- next[key] = typeof existing === "object" && existing !== null && !Array.isArray(existing) ? {
60
- ...existing,
61
- ...value
62
- } : value;
63
- }
64
- return next;
65
- }
66
25
  function mergeTtsConfigFromAppConfig(tts) {
67
26
  const p = tts ?? {};
68
- const defaultEntries = collectTtsProviderConfigEntries(DEFAULT_TTS_CONFIG);
69
- const patchEntries = collectTtsProviderConfigEntries(p);
70
- let merged = {
27
+ return {
71
28
  ...DEFAULT_TTS_CONFIG,
72
29
  ...p,
73
30
  enabled: p.enabled ?? DEFAULT_TTS_CONFIG.enabled,
@@ -81,15 +38,12 @@ function mergeTtsConfigFromAppConfig(tts) {
81
38
  ...DEFAULT_TTS_CONFIG.modelOverrides,
82
39
  ...p.modelOverrides
83
40
  },
84
- providers: mergeProviderEntries(defaultEntries, patchEntries),
41
+ providers: mergeProviderEntries(DEFAULT_TTS_CONFIG.providers, p.providers),
85
42
  summarization: {
86
43
  ...DEFAULT_TTS_CONFIG.summarization,
87
44
  ...p.summarization
88
45
  }
89
46
  };
90
- merged = mergeKnownFlatProviderSlices(merged, p);
91
- merged = mergeExtensionFlatProviderSlices(merged, p);
92
- return merged;
93
47
  }
94
48
  /**
95
49
  * User-facing hint when TTS is enabled in settings but no provider can run.
@@ -1 +1 @@
1
- {"version":3,"file":"merge-config.js","names":[],"sources":["../../../../src/voice/tts/merge-config.ts"],"sourcesContent":["import type { Config } from '../../config/schema.js';\nimport {\n collectTtsProviderConfigEntries,\n TTS_CONFIG_RESERVED_KEYS,\n} from './config-slice.js';\nimport { DEFAULT_TTS_CONFIG, type TTSConfig } from './types.js';\nimport { isTTSAvailable } from './factory.js';\n\n/**\n * Merge persisted app config `tts` with defaults to a full {@link TTSConfig}\n * for validation (provider chain, env-based keys, etc.).\n */\nfunction normalizeTtsTrigger(raw: unknown): TTSConfig['trigger'] {\n const t = typeof raw === 'string' ? raw.toLowerCase() : '';\n if (t === 'off' || t === 'always' || t === 'inbound' || t === 'tagged') return t;\n return DEFAULT_TTS_CONFIG.trigger;\n}\n\nfunction normalizeTtsProvider(raw: unknown): string {\n return typeof raw === 'string' && raw.trim().length > 0 ? raw.trim() : DEFAULT_TTS_CONFIG.provider;\n}\n\nfunction mergeProviderEntries(\n base: Record<string, Record<string, unknown>> | undefined,\n patch: Record<string, Record<string, unknown>> | undefined,\n): Record<string, Record<string, unknown>> | undefined {\n if (!base && !patch) {\n return undefined;\n }\n const merged: Record<string, Record<string, unknown>> = { ...(base ?? {}) };\n for (const [providerId, slice] of Object.entries(patch ?? {})) {\n merged[providerId] = { ...(merged[providerId] ?? {}), ...slice };\n }\n return merged;\n}\n\nfunction mergeKnownFlatProviderSlices(\n merged: TTSConfig,\n patch: Partial<TTSConfig>,\n): TTSConfig {\n return {\n ...merged,\n alibaba: { ...DEFAULT_TTS_CONFIG.alibaba, ...patch.alibaba },\n openai: { ...DEFAULT_TTS_CONFIG.openai, ...patch.openai },\n edge: { ...DEFAULT_TTS_CONFIG.edge, ...patch.edge },\n minimax: { ...DEFAULT_TTS_CONFIG.minimax, ...patch.minimax },\n };\n}\n\nfunction mergeExtensionFlatProviderSlices(\n merged: TTSConfig,\n patch: Record<string, unknown>,\n): TTSConfig {\n const next = { ...merged } as Record<string, unknown>;\n for (const [key, value] of Object.entries(patch)) {\n if (TTS_CONFIG_RESERVED_KEYS.has(key)) {\n continue;\n }\n if (['alibaba', 'openai', 'edge', 'minimax'].includes(key)) {\n continue;\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n continue;\n }\n const existing = next[key];\n next[key] =\n typeof existing === 'object' && existing !== null && !Array.isArray(existing)\n ? { ...(existing as Record<string, unknown>), ...(value as Record<string, unknown>) }\n : value;\n }\n return next as TTSConfig;\n}\n\nexport function mergeTtsConfigFromAppConfig(tts: Partial<TTSConfig> | undefined): TTSConfig {\n const p = (tts ?? {}) as Partial<TTSConfig> & Record<string, unknown>;\n const defaultEntries = collectTtsProviderConfigEntries(DEFAULT_TTS_CONFIG);\n const patchEntries = collectTtsProviderConfigEntries(p);\n\n let merged: TTSConfig = {\n ...DEFAULT_TTS_CONFIG,\n ...p,\n enabled: p.enabled ?? DEFAULT_TTS_CONFIG.enabled,\n provider: normalizeTtsProvider(p.provider),\n trigger: normalizeTtsTrigger(p.trigger ?? DEFAULT_TTS_CONFIG.trigger),\n fallback: {\n ...DEFAULT_TTS_CONFIG.fallback!,\n ...p.fallback,\n },\n modelOverrides: {\n ...DEFAULT_TTS_CONFIG.modelOverrides!,\n ...p.modelOverrides,\n },\n providers: mergeProviderEntries(defaultEntries, patchEntries),\n summarization: {\n ...DEFAULT_TTS_CONFIG.summarization,\n ...p.summarization,\n },\n };\n\n merged = mergeKnownFlatProviderSlices(merged, p);\n merged = mergeExtensionFlatProviderSlices(merged, p);\n return merged;\n}\n\n/**\n * User-facing hint when TTS is enabled in settings but no provider can run.\n */\nexport function formatTtsSetupHint(): string {\n return (\n `⚠️ *TTS is on, but no provider can run yet.*\\n\\n` +\n `Configure one of the following in \\`~/.xopc/xopc.json\\` (or env):\\n` +\n `• *OpenAI*: \\`OPENAI_API_KEY\\` or \\`messages.tts.providers.openai.apiKey\\`\\n` +\n `• *Alibaba*: \\`DASHSCOPE_API_KEY\\` or \\`messages.tts.providers.alibaba.apiKey\\`\\n` +\n `• *MiniMax*: \\`MINIMAX_API_KEY\\` or \\`messages.tts.providers.minimax.apiKey\\`\\n` +\n `• *Edge* (no key): ensure \\`messages.tts.providers.edge.enabled\\` is not \\`false\\`\\n` +\n `• *Local CLI*: \\`messages.tts.providers.tts-local-cli.command\\`\\n\\n` +\n `You can also use the gateway Web UI → Settings → Voice.`\n );\n}\n\n/**\n * Append readiness / setup guidance when TTS is enabled but unavailable.\n */\nexport function appendTtsReadinessNote(content: string, appConfig: Config | undefined): string {\n const effective = mergeTtsConfigFromAppConfig(appConfig?.messages?.tts);\n if (!effective.enabled) {\n return content;\n }\n if (isTTSAvailable(effective)) {\n return content;\n }\n return `${content}\\n\\n${formatTtsSetupHint()}`;\n}\n"],"mappings":";;;;;;;;AAYA,SAAS,oBAAoB,KAAoC;CAC/D,MAAM,IAAI,OAAO,QAAQ,WAAW,IAAI,aAAa,GAAG;AACxD,KAAI,MAAM,SAAS,MAAM,YAAY,MAAM,aAAa,MAAM,SAAU,QAAO;AAC/E,QAAO,mBAAmB;;AAG5B,SAAS,qBAAqB,KAAsB;AAClD,QAAO,OAAO,QAAQ,YAAY,IAAI,MAAM,CAAC,SAAS,IAAI,IAAI,MAAM,GAAG,mBAAmB;;AAG5F,SAAS,qBACP,MACA,OACqD;AACrD,KAAI,CAAC,QAAQ,CAAC,MACZ;CAEF,MAAM,SAAkD,EAAE,GAAI,QAAQ,EAAE,EAAG;AAC3E,MAAK,MAAM,CAAC,YAAY,UAAU,OAAO,QAAQ,SAAS,EAAE,CAAC,CAC3D,QAAO,cAAc;EAAE,GAAI,OAAO,eAAe,EAAE;EAAG,GAAG;EAAO;AAElE,QAAO;;AAGT,SAAS,6BACP,QACA,OACW;AACX,QAAO;EACL,GAAG;EACH,SAAS;GAAE,GAAG,mBAAmB;GAAS,GAAG,MAAM;GAAS;EAC5D,QAAQ;GAAE,GAAG,mBAAmB;GAAQ,GAAG,MAAM;GAAQ;EACzD,MAAM;GAAE,GAAG,mBAAmB;GAAM,GAAG,MAAM;GAAM;EACnD,SAAS;GAAE,GAAG,mBAAmB;GAAS,GAAG,MAAM;GAAS;EAC7D;;AAGH,SAAS,iCACP,QACA,OACW;CACX,MAAM,OAAO,EAAE,GAAG,QAAQ;AAC1B,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,EAAE;AAChD,MAAI,yBAAyB,IAAI,IAAI,CACnC;AAEF,MAAI;GAAC;GAAW;GAAU;GAAQ;GAAU,CAAC,SAAS,IAAI,CACxD;AAEF,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,MAAM,CACrE;EAEF,MAAM,WAAW,KAAK;AACtB,OAAK,OACH,OAAO,aAAa,YAAY,aAAa,QAAQ,CAAC,MAAM,QAAQ,SAAS,GACzE;GAAE,GAAI;GAAsC,GAAI;GAAmC,GACnF;;AAER,QAAO;;AAGT,SAAgB,4BAA4B,KAAgD;CAC1F,MAAM,IAAK,OAAO,EAAE;CACpB,MAAM,iBAAiB,gCAAgC,mBAAmB;CAC1E,MAAM,eAAe,gCAAgC,EAAE;CAEvD,IAAI,SAAoB;EACtB,GAAG;EACH,GAAG;EACH,SAAS,EAAE,WAAW,mBAAmB;EACzC,UAAU,qBAAqB,EAAE,SAAS;EAC1C,SAAS,oBAAoB,EAAE,WAAW,mBAAmB,QAAQ;EACrE,UAAU;GACR,GAAG,mBAAmB;GACtB,GAAG,EAAE;GACN;EACD,gBAAgB;GACd,GAAG,mBAAmB;GACtB,GAAG,EAAE;GACN;EACD,WAAW,qBAAqB,gBAAgB,aAAa;EAC7D,eAAe;GACb,GAAG,mBAAmB;GACtB,GAAG,EAAE;GACN;EACF;AAED,UAAS,6BAA6B,QAAQ,EAAE;AAChD,UAAS,iCAAiC,QAAQ,EAAE;AACpD,QAAO;;;;;AAMT,SAAgB,qBAA6B;AAC3C,QACE;;;;;AAcJ,SAAgB,uBAAuB,SAAiB,WAAuC;CAC7F,MAAM,YAAY,4BAA4B,WAAW,UAAU,IAAI;AACvE,KAAI,CAAC,UAAU,QACb,QAAO;AAET,KAAI,eAAe,UAAU,CAC3B,QAAO;AAET,QAAO,GAAG,QAAQ,MAAM,oBAAoB"}
1
+ {"version":3,"file":"merge-config.js","names":[],"sources":["../../../../src/voice/tts/merge-config.ts"],"sourcesContent":["import type { Config } from '../../config/schema.js';\nimport { DEFAULT_TTS_CONFIG, type TTSConfig } from './types.js';\nimport { isTTSAvailable } from './factory.js';\n\n/**\n * Merge persisted app config `tts` with defaults to a full {@link TTSConfig}\n * for validation (provider chain, env-based keys, etc.).\n */\nfunction normalizeTtsTrigger(raw: unknown): TTSConfig['trigger'] {\n const t = typeof raw === 'string' ? raw.toLowerCase() : '';\n if (t === 'off' || t === 'always' || t === 'inbound' || t === 'tagged') return t;\n return DEFAULT_TTS_CONFIG.trigger;\n}\n\nfunction normalizeTtsProvider(raw: unknown): string {\n return typeof raw === 'string' && raw.trim().length > 0 ? raw.trim() : DEFAULT_TTS_CONFIG.provider;\n}\n\nfunction mergeProviderEntries(\n base: Record<string, Record<string, unknown>> | undefined,\n patch: Record<string, Record<string, unknown>> | undefined,\n): Record<string, Record<string, unknown>> | undefined {\n if (!base && !patch) {\n return undefined;\n }\n const merged: Record<string, Record<string, unknown>> = { ...(base ?? {}) };\n for (const [providerId, slice] of Object.entries(patch ?? {})) {\n merged[providerId] = { ...(merged[providerId] ?? {}), ...slice };\n }\n return merged;\n}\n\nexport function mergeTtsConfigFromAppConfig(tts: Partial<TTSConfig> | undefined): TTSConfig {\n const p = (tts ?? {}) as Partial<TTSConfig>;\n return {\n ...DEFAULT_TTS_CONFIG,\n ...p,\n enabled: p.enabled ?? DEFAULT_TTS_CONFIG.enabled,\n provider: normalizeTtsProvider(p.provider),\n trigger: normalizeTtsTrigger(p.trigger ?? DEFAULT_TTS_CONFIG.trigger),\n fallback: {\n ...DEFAULT_TTS_CONFIG.fallback!,\n ...p.fallback,\n },\n modelOverrides: {\n ...DEFAULT_TTS_CONFIG.modelOverrides!,\n ...p.modelOverrides,\n },\n providers: mergeProviderEntries(DEFAULT_TTS_CONFIG.providers, p.providers),\n summarization: {\n ...DEFAULT_TTS_CONFIG.summarization,\n ...p.summarization,\n },\n };\n}\n\n/**\n * User-facing hint when TTS is enabled in settings but no provider can run.\n */\nexport function formatTtsSetupHint(): string {\n return (\n `⚠️ *TTS is on, but no provider can run yet.*\\n\\n` +\n `Configure one of the following in \\`~/.xopc/xopc.json\\` (or env):\\n` +\n `• *OpenAI*: \\`OPENAI_API_KEY\\` or \\`messages.tts.providers.openai.apiKey\\`\\n` +\n `• *Alibaba*: \\`DASHSCOPE_API_KEY\\` or \\`messages.tts.providers.alibaba.apiKey\\`\\n` +\n `• *MiniMax*: \\`MINIMAX_API_KEY\\` or \\`messages.tts.providers.minimax.apiKey\\`\\n` +\n `• *Edge* (no key): ensure \\`messages.tts.providers.edge.enabled\\` is not \\`false\\`\\n` +\n `• *Local CLI*: \\`messages.tts.providers.tts-local-cli.command\\`\\n\\n` +\n `You can also use the gateway Web UI → Settings → Voice.`\n );\n}\n\n/**\n * Append readiness / setup guidance when TTS is enabled but unavailable.\n */\nexport function appendTtsReadinessNote(content: string, appConfig: Config | undefined): string {\n const effective = mergeTtsConfigFromAppConfig(appConfig?.messages?.tts);\n if (!effective.enabled) {\n return content;\n }\n if (isTTSAvailable(effective)) {\n return content;\n }\n return `${content}\\n\\n${formatTtsSetupHint()}`;\n}\n"],"mappings":";;;;;;;AAQA,SAAS,oBAAoB,KAAoC;CAC/D,MAAM,IAAI,OAAO,QAAQ,WAAW,IAAI,aAAa,GAAG;AACxD,KAAI,MAAM,SAAS,MAAM,YAAY,MAAM,aAAa,MAAM,SAAU,QAAO;AAC/E,QAAO,mBAAmB;;AAG5B,SAAS,qBAAqB,KAAsB;AAClD,QAAO,OAAO,QAAQ,YAAY,IAAI,MAAM,CAAC,SAAS,IAAI,IAAI,MAAM,GAAG,mBAAmB;;AAG5F,SAAS,qBACP,MACA,OACqD;AACrD,KAAI,CAAC,QAAQ,CAAC,MACZ;CAEF,MAAM,SAAkD,EAAE,GAAI,QAAQ,EAAE,EAAG;AAC3E,MAAK,MAAM,CAAC,YAAY,UAAU,OAAO,QAAQ,SAAS,EAAE,CAAC,CAC3D,QAAO,cAAc;EAAE,GAAI,OAAO,eAAe,EAAE;EAAG,GAAG;EAAO;AAElE,QAAO;;AAGT,SAAgB,4BAA4B,KAAgD;CAC1F,MAAM,IAAK,OAAO,EAAE;AACpB,QAAO;EACL,GAAG;EACH,GAAG;EACH,SAAS,EAAE,WAAW,mBAAmB;EACzC,UAAU,qBAAqB,EAAE,SAAS;EAC1C,SAAS,oBAAoB,EAAE,WAAW,mBAAmB,QAAQ;EACrE,UAAU;GACR,GAAG,mBAAmB;GACtB,GAAG,EAAE;GACN;EACD,gBAAgB;GACd,GAAG,mBAAmB;GACtB,GAAG,EAAE;GACN;EACD,WAAW,qBAAqB,mBAAmB,WAAW,EAAE,UAAU;EAC1E,eAAe;GACb,GAAG,mBAAmB;GACtB,GAAG,EAAE;GACN;EACF;;;;;AAMH,SAAgB,qBAA6B;AAC3C,QACE;;;;;AAcJ,SAAgB,uBAAuB,SAAiB,WAAuC;CAC7F,MAAM,YAAY,4BAA4B,WAAW,UAAU,IAAI;AACvE,KAAI,CAAC,UAAU,QACb,QAAO;AAET,KAAI,eAAe,UAAU,CAC3B,QAAO;AAET,QAAO,GAAG,QAAQ,MAAM,oBAAoB"}
@@ -56,7 +56,7 @@ function trimToUndefined(value) {
56
56
  return trimmed.length > 0 ? trimmed : void 0;
57
57
  }
58
58
  function normalizeConfig(rawConfig) {
59
- const raw = asObject(rawConfig.alibaba) ?? rawConfig;
59
+ const raw = asObject(rawConfig.alibaba) ?? {};
60
60
  return {
61
61
  apiKey: trimToUndefined(raw.apiKey),
62
62
  baseUrl: trimToUndefined(raw.baseUrl) ?? DEFAULT_BASE_URL,
@@ -1 +1 @@
1
- {"version":3,"file":"alibaba-speech.js","names":[],"sources":["../../../../../src/voice/tts/providers/alibaba-speech.ts"],"sourcesContent":["/**\n * Alibaba DashScope TTS provider (qwen-tts model).\n *\n * Implements the SpeechProviderPlugin contract directly because DashScope's\n * response shape (output.audio.url for the audio binary, no direct stream)\n * doesn't fit the OpenAI-compatible factory.\n *\n * Implementation notes (per docs/voice-rearchitecture.md §8.4.1):\n * - `synthesizeStream` is intentionally not implemented. Native qwen-tts\n * streaming uses a WebSocket protocol\n * (wss://dashscope.aliyuncs.com/api-ws/v1/inference); speak-core's stream\n * fallback wraps `synthesize` output as a single-chunk ReadableStream so\n * callers see the same shape.\n * - `maxTextLength = 512` (DashScope qwen-tts hard limit). Enforced upstream\n * via `truncateAtSentenceBoundary` before this provider sees the text.\n * - The audio URL returned by DashScope is hosted on Alibaba's CDN (variable\n * hostname). We do not SSRF-guard the audio fetch because:\n * (a) the URL is provided by the same provider we already trust for the\n * initial synthesis call,\n * (b) it's HTTPS-only and short-lived (~5 min TTL),\n * (c) maintaining a hostname allowlist breaks every time Alibaba rotates\n * CDN domains.\n * A dedicated `assertSafeUrl` for trusted-CDN responses can be added later\n * as a hardening pass.\n */\n\nimport {\n ProviderHttpError,\n fetchWithTimeoutGuarded,\n postJsonRequest,\n} from '../../../media-shared/http/index.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport { registerSpeechProvider } from '../speech-registry.js';\nimport type {\n SpeechDirectiveTokenParseContext,\n SpeechDirectiveTokenParseResult,\n SpeechProviderConfig,\n SpeechProviderConfiguredContext,\n SpeechProviderPlugin,\n SpeechProviderResolveConfigContext,\n SpeechSynthesisRequest,\n SpeechSynthesisResult,\n} from '../speech-provider-types.js';\n\nconst log = createLogger('SpeechProvider:Alibaba');\n\nconst DEFAULT_BASE_URL =\n 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation';\nconst DEFAULT_MODEL = 'qwen-tts';\nconst DEFAULT_VOICE = 'longxiaochun';\nconst ENV_KEY = 'DASHSCOPE_API_KEY';\nconst MAX_TEXT_LENGTH = 512;\nconst ALIBABA_VOICES = [\n 'Cherry',\n 'Ethan',\n 'Serena',\n 'Chelsie',\n 'longxiaochun',\n 'longxiaobai',\n 'longwan',\n 'longcheng',\n] as const;\n\ninterface AlibabaTtsConfig extends Record<string, unknown> {\n apiKey?: string;\n baseUrl: string;\n model: string;\n voice: string;\n}\n\ninterface CosyVoiceResponse {\n output: {\n audio?: { url?: string; data?: string };\n speech?: string;\n speech_url?: string;\n finish_reason?: string;\n };\n usage?: { characters?: number };\n request_id?: string;\n}\n\nfunction asObject(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction trimToUndefined(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction normalizeConfig(rawConfig: Record<string, unknown>): AlibabaTtsConfig {\n const raw = asObject(rawConfig.alibaba) ?? rawConfig;\n return {\n apiKey: trimToUndefined(raw.apiKey),\n baseUrl: trimToUndefined(raw.baseUrl) ?? DEFAULT_BASE_URL,\n model: trimToUndefined(raw.model ?? raw.modelId) ?? DEFAULT_MODEL,\n voice: trimToUndefined(raw.voice ?? raw.voiceId) ?? DEFAULT_VOICE,\n };\n}\n\nfunction readProviderConfig(config: SpeechProviderConfig): AlibabaTtsConfig {\n return {\n apiKey: trimToUndefined(config.apiKey),\n baseUrl: trimToUndefined(config.baseUrl) ?? DEFAULT_BASE_URL,\n model: trimToUndefined(config.model ?? config.modelId) ?? DEFAULT_MODEL,\n voice: trimToUndefined(config.voice ?? config.voiceId) ?? DEFAULT_VOICE,\n };\n}\n\nfunction resolveApiKey(config: AlibabaTtsConfig): string | undefined {\n return config.apiKey ?? trimToUndefined(process.env[ENV_KEY]);\n}\n\nfunction parseDirectiveTokenInternal(\n ctx: SpeechDirectiveTokenParseContext,\n): SpeechDirectiveTokenParseResult {\n switch (ctx.key) {\n case 'voice':\n case 'voice_id':\n case 'voiceid':\n case 'alibaba_voice':\n case 'alibabavoice':\n if (!ctx.policy.allowVoice) {\n return { handled: true };\n }\n return { handled: true, overrides: { voice: ctx.value } };\n case 'model':\n case 'model_id':\n case 'modelid':\n case 'alibaba_model':\n case 'alibabamodel':\n if (!ctx.policy.allowModelId) {\n return { handled: true };\n }\n return { handled: true, overrides: { model: ctx.value } };\n default:\n return { handled: false };\n }\n}\n\nexport const alibabaSpeechProvider: SpeechProviderPlugin = {\n id: 'alibaba',\n aliases: ['dashscope', 'qwen-tts'],\n autoSelectOrder: 25,\n\n resolveConfig: (ctx: SpeechProviderResolveConfigContext) => normalizeConfig(ctx.rawConfig),\n\n isConfigured: (ctx: SpeechProviderConfiguredContext) =>\n Boolean(resolveApiKey(readProviderConfig(ctx.providerConfig))),\n\n parseDirectiveToken: parseDirectiveTokenInternal,\n\n listVoices: async () => ALIBABA_VOICES.map((id) => ({ id, name: id })),\n\n synthesize: async (req: SpeechSynthesisRequest): Promise<SpeechSynthesisResult> => {\n const config = readProviderConfig(req.providerConfig);\n const apiKey = resolveApiKey(config);\n if (!apiKey) {\n throw new Error(\n `Alibaba TTS API key missing (set ${ENV_KEY} or messages.tts.providers.alibaba.apiKey)`,\n );\n }\n if (req.text.length > MAX_TEXT_LENGTH) {\n throw new Error(\n `Alibaba TTS text exceeds ${MAX_TEXT_LENGTH} char limit (got ${req.text.length})`,\n );\n }\n\n const overrides = req.providerOverrides ?? {};\n const model = trimToUndefined(overrides.model ?? overrides.modelId) ?? config.model;\n const voice = trimToUndefined(overrides.voice ?? overrides.voiceId) ?? config.voice;\n\n log.debug({ model, voice, textLength: req.text.length }, 'Calling Alibaba TTS');\n\n const response = await postJsonRequest(config.baseUrl, {\n timeoutMs: req.timeoutMs,\n label: 'Alibaba TTS',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'X-DashScope-DataInspection': 'disable',\n },\n body: {\n model,\n input: { text: req.text },\n parameters: { voice },\n },\n });\n\n const data = (await response.json()) as CosyVoiceResponse;\n\n // DashScope occasionally returns `finish_reason: \"null\"` (string) on failure.\n if (data.output?.finish_reason === 'null' && !data.output?.audio?.url) {\n throw new Error(`Alibaba TTS API error: ${JSON.stringify(data)}`);\n }\n\n let audioBuffer: Buffer;\n const audioUrl = data.output?.audio?.url ?? data.output?.speech_url;\n if (audioUrl) {\n // CDN URL — see file-level DECISION on why we skip SSRF here.\n const audioResponse = await fetchWithTimeoutGuarded(audioUrl, {\n timeoutMs: req.timeoutMs,\n label: 'Alibaba TTS audio download',\n allowPrivateNetwork: false,\n });\n if (!audioResponse.ok) {\n throw new ProviderHttpError({\n label: 'Alibaba TTS audio download',\n status: audioResponse.status,\n detail: audioResponse.statusText,\n });\n }\n audioBuffer = Buffer.from(await audioResponse.arrayBuffer());\n } else if (data.output?.speech) {\n audioBuffer = Buffer.from(data.output.speech, 'base64');\n } else if (data.output?.audio?.data) {\n audioBuffer = Buffer.from(data.output.audio.data, 'base64');\n } else {\n throw new Error('No audio returned from Alibaba TTS');\n }\n\n log.debug(\n { size: audioBuffer.length, characters: data.usage?.characters, requestId: data.request_id },\n 'Alibaba TTS completed',\n );\n\n return {\n audioBuffer,\n outputFormat: 'wav',\n fileExtension: 'wav',\n // wav is NOT a Telegram voice-note format → ffmpeg compresses downstream.\n voiceCompatible: false,\n };\n },\n\n // synthesizeStream intentionally omitted — see file-level DECISION.\n};\n\nregisterSpeechProvider(alibabaSpeechProvider);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA+BwD;AAaxD,MAAM,MAAM,aAAa,yBAAyB;AAElD,MAAM,mBACJ;AACF,MAAM,gBAAgB;AACtB,MAAM,gBAAgB;AACtB,MAAM,UAAU;AAChB,MAAM,kBAAkB;AACxB,MAAM,iBAAiB;CACrB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAoBD,SAAS,SAAS,OAAqD;AACrE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM,GACtE,QACD,KAAA;;AAGN,SAAS,gBAAgB,OAAoC;AAC3D,KAAI,OAAO,UAAU,SACnB;CAEF,MAAM,UAAU,MAAM,MAAM;AAC5B,QAAO,QAAQ,SAAS,IAAI,UAAU,KAAA;;AAGxC,SAAS,gBAAgB,WAAsD;CAC7E,MAAM,MAAM,SAAS,UAAU,QAAQ,IAAI;AAC3C,QAAO;EACL,QAAQ,gBAAgB,IAAI,OAAO;EACnC,SAAS,gBAAgB,IAAI,QAAQ,IAAI;EACzC,OAAO,gBAAgB,IAAI,SAAS,IAAI,QAAQ,IAAI;EACpD,OAAO,gBAAgB,IAAI,SAAS,IAAI,QAAQ,IAAI;EACrD;;AAGH,SAAS,mBAAmB,QAAgD;AAC1E,QAAO;EACL,QAAQ,gBAAgB,OAAO,OAAO;EACtC,SAAS,gBAAgB,OAAO,QAAQ,IAAI;EAC5C,OAAO,gBAAgB,OAAO,SAAS,OAAO,QAAQ,IAAI;EAC1D,OAAO,gBAAgB,OAAO,SAAS,OAAO,QAAQ,IAAI;EAC3D;;AAGH,SAAS,cAAc,QAA8C;AACnE,QAAO,OAAO,UAAU,gBAAgB,QAAQ,IAAI,SAAS;;AAG/D,SAAS,4BACP,KACiC;AACjC,SAAQ,IAAI,KAAZ;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;AACH,OAAI,CAAC,IAAI,OAAO,WACd,QAAO,EAAE,SAAS,MAAM;AAE1B,UAAO;IAAE,SAAS;IAAM,WAAW,EAAE,OAAO,IAAI,OAAO;IAAE;EAC3D,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;AACH,OAAI,CAAC,IAAI,OAAO,aACd,QAAO,EAAE,SAAS,MAAM;AAE1B,UAAO;IAAE,SAAS;IAAM,WAAW,EAAE,OAAO,IAAI,OAAO;IAAE;EAC3D,QACE,QAAO,EAAE,SAAS,OAAO;;;AAI/B,MAAa,wBAA8C;CACzD,IAAI;CACJ,SAAS,CAAC,aAAa,WAAW;CAClC,iBAAiB;CAEjB,gBAAgB,QAA4C,gBAAgB,IAAI,UAAU;CAE1F,eAAe,QACb,QAAQ,cAAc,mBAAmB,IAAI,eAAe,CAAC,CAAC;CAEhE,qBAAqB;CAErB,YAAY,YAAY,eAAe,KAAK,QAAQ;EAAE;EAAI,MAAM;EAAI,EAAE;CAEtE,YAAY,OAAO,QAAgE;EACjF,MAAM,SAAS,mBAAmB,IAAI,eAAe;EACrD,MAAM,SAAS,cAAc,OAAO;AACpC,MAAI,CAAC,OACH,OAAM,IAAI,MACR,oCAAoC,QAAQ,4CAC7C;AAEH,MAAI,IAAI,KAAK,SAAS,gBACpB,OAAM,IAAI,MACR,4BAA4B,gBAAgB,mBAAmB,IAAI,KAAK,OAAO,GAChF;EAGH,MAAM,YAAY,IAAI,qBAAqB,EAAE;EAC7C,MAAM,QAAQ,gBAAgB,UAAU,SAAS,UAAU,QAAQ,IAAI,OAAO;EAC9E,MAAM,QAAQ,gBAAgB,UAAU,SAAS,UAAU,QAAQ,IAAI,OAAO;AAE9E,MAAI,MAAM;GAAE;GAAO;GAAO,YAAY,IAAI,KAAK;GAAQ,EAAE,sBAAsB;EAgB/E,MAAM,OAAQ,OAAM,MAdG,gBAAgB,OAAO,SAAS;GACrD,WAAW,IAAI;GACf,OAAO;GACP,SAAS;IACP,eAAe,UAAU;IACzB,8BAA8B;IAC/B;GACD,MAAM;IACJ;IACA,OAAO,EAAE,MAAM,IAAI,MAAM;IACzB,YAAY,EAAE,OAAO;IACtB;GACF,CAAC,EAE2B,MAAM;AAGnC,MAAI,KAAK,QAAQ,kBAAkB,UAAU,CAAC,KAAK,QAAQ,OAAO,IAChE,OAAM,IAAI,MAAM,0BAA0B,KAAK,UAAU,KAAK,GAAG;EAGnE,IAAI;EACJ,MAAM,WAAW,KAAK,QAAQ,OAAO,OAAO,KAAK,QAAQ;AACzD,MAAI,UAAU;GAEZ,MAAM,gBAAgB,MAAM,wBAAwB,UAAU;IAC5D,WAAW,IAAI;IACf,OAAO;IACP,qBAAqB;IACtB,CAAC;AACF,OAAI,CAAC,cAAc,GACjB,OAAM,IAAI,kBAAkB;IAC1B,OAAO;IACP,QAAQ,cAAc;IACtB,QAAQ,cAAc;IACvB,CAAC;AAEJ,iBAAc,OAAO,KAAK,MAAM,cAAc,aAAa,CAAC;aACnD,KAAK,QAAQ,OACtB,eAAc,OAAO,KAAK,KAAK,OAAO,QAAQ,SAAS;WAC9C,KAAK,QAAQ,OAAO,KAC7B,eAAc,OAAO,KAAK,KAAK,OAAO,MAAM,MAAM,SAAS;MAE3D,OAAM,IAAI,MAAM,qCAAqC;AAGvD,MAAI,MACF;GAAE,MAAM,YAAY;GAAQ,YAAY,KAAK,OAAO;GAAY,WAAW,KAAK;GAAY,EAC5F,wBACD;AAED,SAAO;GACL;GACA,cAAc;GACd,eAAe;GAEf,iBAAiB;GAClB;;CAIJ;AAED,uBAAuB,sBAAsB"}
1
+ {"version":3,"file":"alibaba-speech.js","names":[],"sources":["../../../../../src/voice/tts/providers/alibaba-speech.ts"],"sourcesContent":["/**\n * Alibaba DashScope TTS provider (qwen-tts model).\n *\n * Implements the SpeechProviderPlugin contract directly because DashScope's\n * response shape (output.audio.url for the audio binary, no direct stream)\n * doesn't fit the OpenAI-compatible factory.\n *\n * Implementation notes (per docs/voice-rearchitecture.md §8.4.1):\n * - `synthesizeStream` is intentionally not implemented. Native qwen-tts\n * streaming uses a WebSocket protocol\n * (wss://dashscope.aliyuncs.com/api-ws/v1/inference); speak-core's stream\n * fallback wraps `synthesize` output as a single-chunk ReadableStream so\n * callers see the same shape.\n * - `maxTextLength = 512` (DashScope qwen-tts hard limit). Enforced upstream\n * via `truncateAtSentenceBoundary` before this provider sees the text.\n * - The audio URL returned by DashScope is hosted on Alibaba's CDN (variable\n * hostname). We do not SSRF-guard the audio fetch because:\n * (a) the URL is provided by the same provider we already trust for the\n * initial synthesis call,\n * (b) it's HTTPS-only and short-lived (~5 min TTL),\n * (c) maintaining a hostname allowlist breaks every time Alibaba rotates\n * CDN domains.\n * A dedicated `assertSafeUrl` for trusted-CDN responses can be added later\n * as a hardening pass.\n */\n\nimport {\n ProviderHttpError,\n fetchWithTimeoutGuarded,\n postJsonRequest,\n} from '../../../media-shared/http/index.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport { registerSpeechProvider } from '../speech-registry.js';\nimport type {\n SpeechDirectiveTokenParseContext,\n SpeechDirectiveTokenParseResult,\n SpeechProviderConfig,\n SpeechProviderConfiguredContext,\n SpeechProviderPlugin,\n SpeechProviderResolveConfigContext,\n SpeechSynthesisRequest,\n SpeechSynthesisResult,\n} from '../speech-provider-types.js';\n\nconst log = createLogger('SpeechProvider:Alibaba');\n\nconst DEFAULT_BASE_URL =\n 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation';\nconst DEFAULT_MODEL = 'qwen-tts';\nconst DEFAULT_VOICE = 'longxiaochun';\nconst ENV_KEY = 'DASHSCOPE_API_KEY';\nconst MAX_TEXT_LENGTH = 512;\nconst ALIBABA_VOICES = [\n 'Cherry',\n 'Ethan',\n 'Serena',\n 'Chelsie',\n 'longxiaochun',\n 'longxiaobai',\n 'longwan',\n 'longcheng',\n] as const;\n\ninterface AlibabaTtsConfig extends Record<string, unknown> {\n apiKey?: string;\n baseUrl: string;\n model: string;\n voice: string;\n}\n\ninterface CosyVoiceResponse {\n output: {\n audio?: { url?: string; data?: string };\n speech?: string;\n speech_url?: string;\n finish_reason?: string;\n };\n usage?: { characters?: number };\n request_id?: string;\n}\n\nfunction asObject(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction trimToUndefined(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction normalizeConfig(rawConfig: Record<string, unknown>): AlibabaTtsConfig {\n const raw = asObject(rawConfig.alibaba) ?? {};\n return {\n apiKey: trimToUndefined(raw.apiKey),\n baseUrl: trimToUndefined(raw.baseUrl) ?? DEFAULT_BASE_URL,\n model: trimToUndefined(raw.model ?? raw.modelId) ?? DEFAULT_MODEL,\n voice: trimToUndefined(raw.voice ?? raw.voiceId) ?? DEFAULT_VOICE,\n };\n}\n\nfunction readProviderConfig(config: SpeechProviderConfig): AlibabaTtsConfig {\n return {\n apiKey: trimToUndefined(config.apiKey),\n baseUrl: trimToUndefined(config.baseUrl) ?? DEFAULT_BASE_URL,\n model: trimToUndefined(config.model ?? config.modelId) ?? DEFAULT_MODEL,\n voice: trimToUndefined(config.voice ?? config.voiceId) ?? DEFAULT_VOICE,\n };\n}\n\nfunction resolveApiKey(config: AlibabaTtsConfig): string | undefined {\n return config.apiKey ?? trimToUndefined(process.env[ENV_KEY]);\n}\n\nfunction parseDirectiveTokenInternal(\n ctx: SpeechDirectiveTokenParseContext,\n): SpeechDirectiveTokenParseResult {\n switch (ctx.key) {\n case 'voice':\n case 'voice_id':\n case 'voiceid':\n case 'alibaba_voice':\n case 'alibabavoice':\n if (!ctx.policy.allowVoice) {\n return { handled: true };\n }\n return { handled: true, overrides: { voice: ctx.value } };\n case 'model':\n case 'model_id':\n case 'modelid':\n case 'alibaba_model':\n case 'alibabamodel':\n if (!ctx.policy.allowModelId) {\n return { handled: true };\n }\n return { handled: true, overrides: { model: ctx.value } };\n default:\n return { handled: false };\n }\n}\n\nexport const alibabaSpeechProvider: SpeechProviderPlugin = {\n id: 'alibaba',\n aliases: ['dashscope', 'qwen-tts'],\n autoSelectOrder: 25,\n\n resolveConfig: (ctx: SpeechProviderResolveConfigContext) => normalizeConfig(ctx.rawConfig),\n\n isConfigured: (ctx: SpeechProviderConfiguredContext) =>\n Boolean(resolveApiKey(readProviderConfig(ctx.providerConfig))),\n\n parseDirectiveToken: parseDirectiveTokenInternal,\n\n listVoices: async () => ALIBABA_VOICES.map((id) => ({ id, name: id })),\n\n synthesize: async (req: SpeechSynthesisRequest): Promise<SpeechSynthesisResult> => {\n const config = readProviderConfig(req.providerConfig);\n const apiKey = resolveApiKey(config);\n if (!apiKey) {\n throw new Error(\n `Alibaba TTS API key missing (set ${ENV_KEY} or messages.tts.providers.alibaba.apiKey)`,\n );\n }\n if (req.text.length > MAX_TEXT_LENGTH) {\n throw new Error(\n `Alibaba TTS text exceeds ${MAX_TEXT_LENGTH} char limit (got ${req.text.length})`,\n );\n }\n\n const overrides = req.providerOverrides ?? {};\n const model = trimToUndefined(overrides.model ?? overrides.modelId) ?? config.model;\n const voice = trimToUndefined(overrides.voice ?? overrides.voiceId) ?? config.voice;\n\n log.debug({ model, voice, textLength: req.text.length }, 'Calling Alibaba TTS');\n\n const response = await postJsonRequest(config.baseUrl, {\n timeoutMs: req.timeoutMs,\n label: 'Alibaba TTS',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'X-DashScope-DataInspection': 'disable',\n },\n body: {\n model,\n input: { text: req.text },\n parameters: { voice },\n },\n });\n\n const data = (await response.json()) as CosyVoiceResponse;\n\n // DashScope occasionally returns `finish_reason: \"null\"` (string) on failure.\n if (data.output?.finish_reason === 'null' && !data.output?.audio?.url) {\n throw new Error(`Alibaba TTS API error: ${JSON.stringify(data)}`);\n }\n\n let audioBuffer: Buffer;\n const audioUrl = data.output?.audio?.url ?? data.output?.speech_url;\n if (audioUrl) {\n // CDN URL — see file-level DECISION on why we skip SSRF here.\n const audioResponse = await fetchWithTimeoutGuarded(audioUrl, {\n timeoutMs: req.timeoutMs,\n label: 'Alibaba TTS audio download',\n allowPrivateNetwork: false,\n });\n if (!audioResponse.ok) {\n throw new ProviderHttpError({\n label: 'Alibaba TTS audio download',\n status: audioResponse.status,\n detail: audioResponse.statusText,\n });\n }\n audioBuffer = Buffer.from(await audioResponse.arrayBuffer());\n } else if (data.output?.speech) {\n audioBuffer = Buffer.from(data.output.speech, 'base64');\n } else if (data.output?.audio?.data) {\n audioBuffer = Buffer.from(data.output.audio.data, 'base64');\n } else {\n throw new Error('No audio returned from Alibaba TTS');\n }\n\n log.debug(\n { size: audioBuffer.length, characters: data.usage?.characters, requestId: data.request_id },\n 'Alibaba TTS completed',\n );\n\n return {\n audioBuffer,\n outputFormat: 'wav',\n fileExtension: 'wav',\n // wav is NOT a Telegram voice-note format → ffmpeg compresses downstream.\n voiceCompatible: false,\n };\n },\n\n // synthesizeStream intentionally omitted — see file-level DECISION.\n};\n\nregisterSpeechProvider(alibabaSpeechProvider);\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import { createLogger } from "../../../utils/logger/index.js";
2
2
  import { init_logger } from "../../../utils/logger.js";
3
3
  import { registerSpeechProvider } from "../speech-registry.js";
4
- import { tmpdir } from "node:os";
5
- import path from "node:path";
6
4
  import { mkdtempSync, readFileSync, rmSync } from "node:fs";
5
+ import path from "node:path";
6
+ import { tmpdir } from "node:os";
7
7
  //#region src/voice/tts/providers/edge-speech.ts
8
8
  /**
9
9
  * Edge TTS provider — wraps the `node-edge-tts` package. Requires no API key
@@ -72,36 +72,8 @@ export interface TTSConfig {
72
72
  summarization?: TTSSummarizationConfig;
73
73
  /** Allow model to override TTS parameters */
74
74
  modelOverrides?: TTSModelOverrideConfig;
75
- /** OpenClaw-aligned provider settings map (`messages.tts.providers.<id>`). */
75
+ /** Provider settings map (`messages.tts.providers.<id>`). */
76
76
  providers?: Record<string, Record<string, unknown>>;
77
- alibaba?: {
78
- apiKey?: string;
79
- model?: string;
80
- voice?: string;
81
- };
82
- openai?: {
83
- apiKey?: string;
84
- model?: string;
85
- voice?: string;
86
- };
87
- edge?: {
88
- enabled?: boolean;
89
- voice?: string;
90
- lang?: string;
91
- outputFormat?: string;
92
- pitch?: string;
93
- rate?: string;
94
- volume?: string;
95
- proxy?: string;
96
- timeoutMs?: number;
97
- };
98
- minimax?: {
99
- apiKey?: string;
100
- model?: string;
101
- voice?: string;
102
- };
103
- /** Extension / legacy flat provider config (e.g. `tts-local-cli`). */
104
- [providerId: string]: unknown;
105
77
  }
106
78
  export interface TTSSummarizationConfig {
107
79
  /** When true (default), long text is summarized via LLM before TTS */
@@ -25,23 +25,25 @@ const DEFAULT_TTS_CONFIG = {
25
25
  allowNormalization: false,
26
26
  allowSeed: false
27
27
  },
28
- alibaba: {
29
- model: "qwen-tts",
30
- voice: "Cherry"
31
- },
32
- openai: {
33
- model: "tts-1",
34
- voice: "alloy"
35
- },
36
- edge: {
37
- enabled: true,
38
- voice: "en-US-MichelleNeural",
39
- lang: "en-US",
40
- outputFormat: "audio-24khz-48kbitrate-mono-mp3"
41
- },
42
- minimax: {
43
- model: "speech-2.8-hd",
44
- voice: "male-qn-qingse"
28
+ providers: {
29
+ alibaba: {
30
+ model: "qwen-tts",
31
+ voice: "Cherry"
32
+ },
33
+ openai: {
34
+ model: "tts-1",
35
+ voice: "alloy"
36
+ },
37
+ edge: {
38
+ enabled: true,
39
+ voice: "en-US-MichelleNeural",
40
+ lang: "en-US",
41
+ outputFormat: "audio-24khz-48kbitrate-mono-mp3"
42
+ },
43
+ minimax: {
44
+ model: "speech-2.8-hd",
45
+ voice: "male-qn-qingse"
46
+ }
45
47
  }
46
48
  };
47
49
  //#endregion