@xopcai/xopc 0.0.241 → 0.0.243

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 (257) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{activity-store-CLnbVm9G.js → activity-store-CAah-_8h.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DuBewmbW.js → agent-avatar-dicebear-DDH4HgP7.js} +1 -1
  5. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-BDCfJZhh.js +1 -0
  6. package/dist/gateway/static/root/assets/agents-B0ZWgf-U.js +1 -0
  7. package/dist/gateway/static/root/assets/{api-DjBZe303.js → api-B2tKxbcz.js} +1 -1
  8. package/dist/gateway/static/root/assets/app-management-settings-panel-Blfwszzn.js +1 -0
  9. package/dist/gateway/static/root/assets/{appearance-settings-B-iul0RG.js → appearance-settings-C3V7Na75.js} +1 -1
  10. package/dist/gateway/static/root/assets/apps-page-BiEks0-F.js +1 -0
  11. package/dist/gateway/static/root/assets/{archive-plugin-BOx0EJZs.js → archive-plugin-BmSx7wzY.js} +1 -1
  12. package/dist/gateway/static/root/assets/{attachment-load-Cfk1lwBo.js → attachment-load-BS-S4LJ9.js} +1 -1
  13. package/dist/gateway/static/root/assets/{automations-page-BTSGvaeS.js → automations-page-C7Y3L30D.js} +1 -1
  14. package/dist/gateway/static/root/assets/automations-workspace-A3zPkPuo.js +6 -0
  15. package/dist/gateway/static/root/assets/{binary-plugins-ChZyZK45.js → binary-plugins-BM9B5kcm.js} +1 -1
  16. package/dist/gateway/static/root/assets/block-editor-BbnpkYP9.js +185 -0
  17. package/dist/gateway/static/root/assets/browser-settings-page-Dk4FQ7fY.js +8 -0
  18. package/dist/gateway/static/root/assets/{browser-workflow-inputs-jvfcgFG-.js → browser-workflow-inputs-CRnkgZHI.js} +1 -1
  19. package/dist/gateway/static/root/assets/{browser-workflows-page-DrHW7XNi.js → browser-workflows-page-B9qGL-it.js} +2 -2
  20. package/dist/gateway/static/root/assets/{button-CSEbl4zT.js → button-CtTvuElV.js} +1 -1
  21. package/dist/gateway/static/root/assets/capabilities-settings-panel--tR_El9e.js +7 -0
  22. package/dist/gateway/static/root/assets/channels-settings-D347vl_J.js +1 -0
  23. package/dist/gateway/static/root/assets/{channels-status-swr-CC9MFway.js → channels-status-swr-DuY4MGxe.js} +1 -1
  24. package/dist/gateway/static/root/assets/{chat-terminal-dock-DeeZrVbV.js → chat-terminal-dock-DABPvwBT.js} +2 -2
  25. package/dist/gateway/static/root/assets/connectors-page-B-tvJkRJ.js +2 -0
  26. package/dist/gateway/static/root/assets/{cron-expression-CiqQOeQ3.js → cron-expression-D3bsKbNd.js} +1 -1
  27. package/dist/gateway/static/root/assets/{date-picker-CGAMGPfU.js → date-picker-BsGM6Yty.js} +1 -1
  28. package/dist/gateway/static/root/assets/{dependency-picker-CxWeUTsc.js → dependency-picker-Dj3g6YXZ.js} +1 -1
  29. package/dist/gateway/static/root/assets/{desktop-pet-C3QmYO75.js → desktop-pet-C1QT0Sur.js} +1 -1
  30. package/dist/gateway/static/root/assets/{desktop-pet-settings-CAADkWYy.js → desktop-pet-settings-r6kAOnfY.js} +1 -1
  31. package/dist/gateway/static/root/assets/dist-CpzYSoz7.js +1 -0
  32. package/dist/gateway/static/root/assets/{dist-DaxPZj9e.js → dist-DBS3OB8K.js} +1 -1
  33. package/dist/gateway/static/root/assets/dist-DLmwHyAh.js +1 -0
  34. package/dist/gateway/static/root/assets/es2015-BvLh9GaE.js +41 -0
  35. package/dist/gateway/static/root/assets/{extension-debug-page-FQXjDJ8P.js → extension-debug-page-DOkf7nh9.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-page-BJE1D5Ji.js → extension-page-Cq2IA4Ho.js} +1 -1
  37. package/dist/gateway/static/root/assets/{extension-settings-page-5Ik2tNry.js → extension-settings-page-DQn4qjDZ.js} +1 -1
  38. package/dist/gateway/static/root/assets/{files-DQjhZjr2.js → files-D6vf3S30.js} +1 -1
  39. package/dist/gateway/static/root/assets/{gateway-config-api-cTj71t2a.js → gateway-config-api-DM3poL22.js} +1 -1
  40. package/dist/gateway/static/root/assets/{gateway-config-swr-Bxe_R-JH.js → gateway-config-swr-BC3pgb_P.js} +1 -1
  41. package/dist/gateway/static/root/assets/{gateway-settings-KXBUjNcx.js → gateway-settings-DPgH1hnh.js} +1 -1
  42. package/dist/gateway/static/root/assets/{gateway-startup-retry-DJG-I0wW.js → gateway-startup-retry-uMcSfLru.js} +1 -1
  43. package/dist/gateway/static/root/assets/{heartbeat-settings-C8cVzIcs.js → heartbeat-settings-Bk80-E3b.js} +1 -1
  44. package/dist/gateway/static/root/assets/home-page-DKo5JiMR.js +1 -0
  45. package/dist/gateway/static/root/assets/{image-generation-api-Dc_ojQuj.js → image-generation-api-D1qlQA-A.js} +1 -1
  46. package/dist/gateway/static/root/assets/index-B0WJL9N1.js +71 -0
  47. package/dist/gateway/static/root/assets/index-Dbh5SXyV.css +2 -0
  48. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-pOKl7blR.js → keyboard-shortcuts-settings-BEwvd8ZU.js} +1 -1
  49. package/dist/gateway/static/root/assets/local-app-workbench-page--eJ5rmNj.js +2 -0
  50. package/dist/gateway/static/root/assets/{local-apps-page-Bi6wwKgm.js → local-apps-page-CgkY9E-r.js} +1 -1
  51. package/dist/gateway/static/root/assets/{locale-store-C5wr3_pc.js → locale-store-CMZljTJx.js} +1 -1
  52. package/dist/gateway/static/root/assets/logs-page-Z49Td0J9.js +2 -0
  53. package/dist/gateway/static/root/assets/{management-settings-DGlVizWi.js → management-settings-Dv8NSJKt.js} +1 -1
  54. package/dist/gateway/static/root/assets/{markdown-split-CXP5zBx_.js → markdown-split-Cm0RBB-t.js} +1 -1
  55. package/dist/gateway/static/root/assets/{markdown-view-D6LxlDxr.js → markdown-view-PPCLt7fu.js} +3 -3
  56. package/dist/gateway/static/root/assets/{media-plugins-BsENGLr2.js → media-plugins-Bcr5sU1f.js} +1 -1
  57. package/dist/gateway/static/root/assets/messages-CTSgFlE4.js +3 -0
  58. package/dist/gateway/static/root/assets/mobile-pairing-wizard-kKDiXofY.js +1 -0
  59. package/dist/gateway/static/root/assets/{models-hub-cache-Y7GUjxHx.js → models-hub-cache-ZrbGRiGY.js} +1 -1
  60. package/dist/gateway/static/root/assets/{notes-page-D2EqpDyo.js → notes-page-CgsaRaGi.js} +1 -1
  61. package/dist/gateway/static/root/assets/notes-workbench-C1DEE09k.js +4 -0
  62. package/dist/gateway/static/root/assets/{oauth-api-C5NVcsKE.js → oauth-api-4VeMbkTJ.js} +1 -1
  63. package/dist/gateway/static/root/assets/popover-select-C-Hj-nXR.js +1 -0
  64. package/dist/gateway/static/root/assets/{product-delivery-dUJgmBQZ.js → product-delivery-CV4pDOtG.js} +1 -1
  65. package/dist/gateway/static/root/assets/product-open-page-BU0HiZtv.js +1 -0
  66. package/dist/gateway/static/root/assets/project-detail-page-isRapnp5.js +1 -0
  67. package/dist/gateway/static/root/assets/projects-page-zvsjg0mi.js +1 -0
  68. package/dist/gateway/static/root/assets/{registry-api-FXdFaoWM.js → registry-api-CWOtPFrE.js} +1 -1
  69. package/dist/gateway/static/root/assets/remote-access-hub-C9TGZ9Hc.js +1 -0
  70. package/dist/gateway/static/root/assets/runtime-tools-settings-panel-2zm9edMA.js +5 -0
  71. package/dist/gateway/static/root/assets/{schema-form-BUzdHWx6.js → schema-form-54A5gzkN.js} +1 -1
  72. package/dist/gateway/static/root/assets/{schemas-zIBW3wiV.js → schemas-BHS7bo06.js} +1 -1
  73. package/dist/gateway/static/root/assets/sessions-page-BP8RyiUn.js +1 -0
  74. package/dist/gateway/static/root/assets/settings-advanced-gate-D4EMxWAZ.js +1 -0
  75. package/dist/gateway/static/root/assets/{settings-page-C0OBLqpu.js → settings-page-DzDhoyxv.js} +1 -1
  76. package/dist/gateway/static/root/assets/{setup-status-panel-NpLQkFCh.js → setup-status-panel-uDicHU1w.js} +1 -1
  77. package/dist/gateway/static/root/assets/{share-preview-page-BtbPYaYC.js → share-preview-page-lOmLCWFS.js} +1 -1
  78. package/dist/gateway/static/root/assets/shares-settings-T4Yvouvv.js +1 -0
  79. package/dist/gateway/static/root/assets/skill-reload-api-DVb7nrXz.js +1 -0
  80. package/dist/gateway/static/root/assets/{skills-page-CYk8pN97.js → skills-page-CnyNH3To.js} +1 -1
  81. package/dist/gateway/static/root/assets/skills-page.utils-CgDtf2qs.js +1 -0
  82. package/dist/gateway/static/root/assets/{system-settings-panel-DmCRSvtd.js → system-settings-panel-C8yWfwtR.js} +1 -1
  83. package/dist/gateway/static/root/assets/task-detail-page-Tzfjlbf3.js +1 -0
  84. package/dist/gateway/static/root/assets/{theme-store-DUko1ZnE.js → theme-store-uxUwr10D.js} +1 -1
  85. package/dist/gateway/static/root/assets/tunnel-api-Bvg679no.js +1 -0
  86. package/dist/gateway/static/root/assets/{use-autosave-W_P8Kiv4.js → use-autosave-D88VGoyG.js} +1 -1
  87. package/dist/gateway/static/root/assets/user-context-page-RijPtyfO.js +1 -0
  88. package/dist/gateway/static/root/assets/{voice-api-key-field-BZD0j1jc.js → voice-api-key-field-CBHLsMcc.js} +1 -1
  89. package/dist/gateway/static/root/assets/voice-config-api-MqyI6cgq.js +1 -0
  90. package/dist/gateway/static/root/assets/work-discovery-overlay-GGe2TQ98.js +1 -0
  91. package/dist/gateway/static/root/assets/workflow-run-setup-panel-C0layUE9.js +3 -0
  92. package/dist/gateway/static/root/assets/workflows-page-DQXIzBAc.js +5 -0
  93. package/dist/gateway/static/root/index.html +21 -22
  94. package/dist/package.js +1 -1
  95. package/dist/packages/realtime-protocol/src/voice-audio.js +20 -0
  96. package/dist/packages/realtime-protocol/src/voice.js +208 -0
  97. package/dist/src/agent/agent-manager.js +1 -0
  98. package/dist/src/agent/embedded/run-for-session.d.ts +1 -0
  99. package/dist/src/agent/embedded/run-for-session.js +2 -1
  100. package/dist/src/agent/inbound/turn-dispatcher.d.ts +1 -0
  101. package/dist/src/agent/inbound/turn-dispatcher.js +2 -1
  102. package/dist/src/agent/prompt/voice-presentation.d.ts +2 -0
  103. package/dist/src/agent/prompt/voice-presentation.js +12 -0
  104. package/dist/src/agent/service/direct-turn-helpers.d.ts +1 -0
  105. package/dist/src/agent/service/direct-turn-helpers.js +1 -0
  106. package/dist/src/agent/service/process-direct-streaming.d.ts +1 -0
  107. package/dist/src/agent/service/process-direct-streaming.js +2 -1
  108. package/dist/src/agent/session/session-config-service.js +4 -2
  109. package/dist/src/agent/skills/installer.d.ts +1 -0
  110. package/dist/src/agent/skills/installer.js +9 -6
  111. package/dist/src/agent/tools/factory.d.ts +2 -0
  112. package/dist/src/agent/tools/factory.js +5 -1
  113. package/dist/src/automations/draft/automation-draft-service.js +1 -1
  114. package/dist/src/config/models-json.d.ts +8 -8
  115. package/dist/src/config/schema.d.ts +60 -0
  116. package/dist/src/config/schema.js +36 -4
  117. package/dist/src/extensions/install.js +1 -1
  118. package/dist/src/files/file-service.d.ts +1 -0
  119. package/dist/src/files/file-service.js +4 -0
  120. package/dist/src/gateway/chat-stream/thinking-delta-coalescer.js +8 -3
  121. package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -11
  122. package/dist/src/gateway/hono/lib/config-payload.js +2 -2
  123. package/dist/src/gateway/hono/lib/safe-voice-config.d.ts +14 -0
  124. package/dist/src/gateway/hono/lib/safe-voice-config.js +29 -1
  125. package/dist/src/gateway/hono/routes/config-patch/misc.js +2 -2
  126. package/dist/src/gateway/hono/routes/files.js +7 -0
  127. package/dist/src/gateway/hono/routes/lazy-fallback.js +18 -4
  128. package/dist/src/gateway/hono/routes/runtime-tools.js +7 -1
  129. package/dist/src/gateway/hono/routes/session-input-handler.js +14 -0
  130. package/dist/src/gateway/hono/routes/sessions.js +4 -1
  131. package/dist/src/gateway/hono/routes/voice.js +140 -3
  132. package/dist/src/gateway/server.js +1 -0
  133. package/dist/src/gateway/service/agent-runner.d.ts +1 -0
  134. package/dist/src/gateway/service/agent-runner.js +1 -6
  135. package/dist/src/gateway/service/run-gateway-agent.d.ts +1 -0
  136. package/dist/src/gateway/service/run-gateway-agent.js +19 -1
  137. package/dist/src/gateway/service.d.ts +2 -0
  138. package/dist/src/gateway/service.js +73 -0
  139. package/dist/src/media-understanding/types.d.ts +61 -0
  140. package/dist/src/runtime-tools/archive.js +17 -3
  141. package/dist/src/runtime-tools/command.d.ts +1 -0
  142. package/dist/src/runtime-tools/command.js +29 -8
  143. package/dist/src/runtime-tools/environment.d.ts +1 -0
  144. package/dist/src/runtime-tools/environment.js +11 -16
  145. package/dist/src/runtime-tools/install-lock.d.ts +1 -1
  146. package/dist/src/runtime-tools/install-lock.js +34 -21
  147. package/dist/src/runtime-tools/manager.d.ts +1 -1
  148. package/dist/src/runtime-tools/manager.js +99 -64
  149. package/dist/src/runtime-tools/manifest-store.d.ts +2 -0
  150. package/dist/src/runtime-tools/manifest-store.js +29 -8
  151. package/dist/src/runtime-tools/paths.d.ts +2 -1
  152. package/dist/src/runtime-tools/paths.js +19 -8
  153. package/dist/src/runtime-tools/probe.d.ts +2 -0
  154. package/dist/src/runtime-tools/probe.js +13 -1
  155. package/dist/src/runtime-tools/prune.js +21 -7
  156. package/dist/src/session/client-history.js +12 -0
  157. package/dist/src/session/manager.d.ts +1 -0
  158. package/dist/src/session/session-context-for-llm.js +8 -0
  159. package/dist/src/session/store.d.ts +1 -0
  160. package/dist/src/session/store.js +1 -0
  161. package/dist/src/session/stored-rows-to-file-entries.js +5 -1
  162. package/dist/src/session/voice-transcript.d.ts +9 -0
  163. package/dist/src/session/voice-transcript.js +45 -0
  164. package/dist/src/storage/sqlite/config-repository.js +9 -0
  165. package/dist/src/storage/sqlite/remote-context-repository.d.ts +3 -0
  166. package/dist/src/storage/sqlite/remote-context-repository.js +40 -0
  167. package/dist/src/storage/sqlite/session-repository.js +13 -0
  168. package/dist/src/storage/sqlite/user-context-repository.d.ts +3 -1
  169. package/dist/src/storage/sqlite/user-context-repository.js +50 -4
  170. package/dist/src/tui/tui.js +2 -1
  171. package/dist/src/user-context/automatic-context.d.ts +17 -0
  172. package/dist/src/user-context/automatic-context.js +85 -0
  173. package/dist/src/user-context/changes.d.ts +7 -0
  174. package/dist/src/user-context/changes.js +28 -0
  175. package/dist/src/user-context/planner.js +1 -24
  176. package/dist/src/user-context/retriever.js +10 -11
  177. package/dist/src/user-context/selection-policy.d.ts +20 -0
  178. package/dist/src/user-context/selection-policy.js +33 -0
  179. package/dist/src/user-context/sources/index.js +2 -2
  180. package/dist/src/user-context/sources/repository.d.ts +2 -1
  181. package/dist/src/user-context/sources/repository.js +26 -5
  182. package/dist/src/voice/dashscope/streaming-stt-session.d.ts +2 -0
  183. package/dist/src/voice/dashscope/streaming-stt-session.js +249 -0
  184. package/dist/src/voice/dashscope/streaming-tts-stream.d.ts +12 -0
  185. package/dist/src/voice/dashscope/streaming-tts-stream.js +153 -0
  186. package/dist/src/voice/realtime/agentEngine.d.ts +10 -0
  187. package/dist/src/voice/realtime/agentEngine.js +403 -0
  188. package/dist/src/voice/realtime/audio-playback-window.d.ts +9 -0
  189. package/dist/src/voice/realtime/audio-playback-window.js +49 -0
  190. package/dist/src/voice/realtime/conversation-context.d.ts +11 -0
  191. package/dist/src/voice/realtime/conversation-context.js +76 -0
  192. package/dist/src/voice/realtime/engine.d.ts +13 -0
  193. package/dist/src/voice/realtime/engine.js +1 -0
  194. package/dist/src/voice/realtime/memory-context.d.ts +17 -0
  195. package/dist/src/voice/realtime/memory-context.js +100 -0
  196. package/dist/src/voice/realtime/omniEngine.d.ts +18 -0
  197. package/dist/src/voice/realtime/omniEngine.js +443 -0
  198. package/dist/src/voice/realtime/omniRoute.d.ts +10 -0
  199. package/dist/src/voice/realtime/omniRoute.js +31 -0
  200. package/dist/src/voice/realtime/runtime.d.ts +99 -0
  201. package/dist/src/voice/realtime/runtime.js +622 -0
  202. package/dist/src/voice/realtime/speakable-segmenter.d.ts +10 -0
  203. package/dist/src/voice/realtime/speakable-segmenter.js +87 -0
  204. package/dist/src/voice/stt/list-providers.js +2 -1
  205. package/dist/src/voice/stt/providers/alibaba-transcription.js +13 -1
  206. package/dist/src/voice/stt/providers/xopc-cloud-transcription.js +23 -0
  207. package/dist/src/voice/tts/factory.js +1 -0
  208. package/dist/src/voice/tts/openai-compatible-speech.js +3 -1
  209. package/dist/src/voice/tts/providers/alibaba-speech.d.ts +1 -5
  210. package/dist/src/voice/tts/providers/alibaba-speech.js +25 -8
  211. package/dist/src/voice/tts/providers/edge-speech.js +21 -22
  212. package/dist/src/voice/tts/providers/minimax-speech.js +6 -3
  213. package/dist/src/voice/tts/providers/xopc-cloud-speech.js +26 -1
  214. package/dist/src/voice/tts/speak-core.d.ts +6 -2
  215. package/dist/src/voice/tts/speak-core.js +46 -34
  216. package/dist/src/voice/tts/speech-provider-types.d.ts +1 -0
  217. package/package.json +6 -6
  218. package/dist/gateway/static/root/assets/Combination-14nmIgxH.js +0 -41
  219. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-DbdgX6AA.js +0 -1
  220. package/dist/gateway/static/root/assets/agents-BX3uRVm4.js +0 -1
  221. package/dist/gateway/static/root/assets/app-chrome-BeB4KgOf.js +0 -1
  222. package/dist/gateway/static/root/assets/app-management-settings-panel-D8evZyFQ.js +0 -1
  223. package/dist/gateway/static/root/assets/apps-page-CoZd_cZu.js +0 -1
  224. package/dist/gateway/static/root/assets/automations-workspace-C8RRKT-a.js +0 -6
  225. package/dist/gateway/static/root/assets/block-editor-DCH9b2Ht.js +0 -184
  226. package/dist/gateway/static/root/assets/browser-settings-page-D1GPwIfx.js +0 -8
  227. package/dist/gateway/static/root/assets/capabilities-settings-panel-CrXB1Q-l.js +0 -7
  228. package/dist/gateway/static/root/assets/channels-settings-DWMSYaNZ.js +0 -1
  229. package/dist/gateway/static/root/assets/connectors-page-DXTl1fNB.js +0 -2
  230. package/dist/gateway/static/root/assets/dist-BPoKqzeH.js +0 -1
  231. package/dist/gateway/static/root/assets/dist-DqPZM2MH.js +0 -1
  232. package/dist/gateway/static/root/assets/home-page-NdWtBda5.js +0 -1
  233. package/dist/gateway/static/root/assets/index-7i2P3312.js +0 -70
  234. package/dist/gateway/static/root/assets/index-DTo3IcbW.css +0 -2
  235. package/dist/gateway/static/root/assets/local-app-workbench-page-C_j_od5f.js +0 -2
  236. package/dist/gateway/static/root/assets/logs-page-D6PDZEVP.js +0 -2
  237. package/dist/gateway/static/root/assets/messages-_tYhRAHD.js +0 -3
  238. package/dist/gateway/static/root/assets/mobile-pairing-wizard-DcEz7qcX.js +0 -1
  239. package/dist/gateway/static/root/assets/notes-workbench-Bu0a83Oj.js +0 -4
  240. package/dist/gateway/static/root/assets/popover-select-DrILF6mR.js +0 -1
  241. package/dist/gateway/static/root/assets/product-open-page-DMhf1rdz.js +0 -1
  242. package/dist/gateway/static/root/assets/project-detail-page-DDHi0Y4Q.js +0 -1
  243. package/dist/gateway/static/root/assets/projects-page-CVMFKpmu.js +0 -1
  244. package/dist/gateway/static/root/assets/remote-access-hub-B74aErBY.js +0 -1
  245. package/dist/gateway/static/root/assets/runtime-tools-settings-panel-Bv-akndY.js +0 -5
  246. package/dist/gateway/static/root/assets/sessions-page-D2wmGNZE.js +0 -1
  247. package/dist/gateway/static/root/assets/settings-advanced-gate-CYCCUMEt.js +0 -1
  248. package/dist/gateway/static/root/assets/shares-settings-rf8f1OGq.js +0 -1
  249. package/dist/gateway/static/root/assets/skill-reload-api-BkvclZ_o.js +0 -1
  250. package/dist/gateway/static/root/assets/skills-page.utils-DGjnz-NG.js +0 -1
  251. package/dist/gateway/static/root/assets/task-detail-page-6a2RdDl2.js +0 -1
  252. package/dist/gateway/static/root/assets/tunnel-api-Chfiba_x.js +0 -1
  253. package/dist/gateway/static/root/assets/user-context-page-SeOHP6XO.js +0 -1
  254. package/dist/gateway/static/root/assets/voice-config-api-BO6A8V3Z.js +0 -1
  255. package/dist/gateway/static/root/assets/work-discovery-overlay-BfwEoOGL.js +0 -1
  256. package/dist/gateway/static/root/assets/workflow-run-setup-panel-X7Z5_lRX.js +0 -3
  257. package/dist/gateway/static/root/assets/workflows-page-BklMzkg8.js +0 -5
@@ -0,0 +1,87 @@
1
+ import { stripMarkdown } from "../tts/preprocess.js";
2
+ //#region src/voice/realtime/speakable-segmenter.ts
3
+ const SENTENCE_END = /[。!?!?;;::\n]/u;
4
+ /** Streams spoken prose, keeping code and link targets out of synthesis. */
5
+ var SpeakableSegmenter = class {
6
+ pending = "";
7
+ fence = null;
8
+ constructor(maxCharacters = 120) {
9
+ this.maxCharacters = maxCharacters;
10
+ }
11
+ push(delta) {
12
+ this.pending += delta;
13
+ const phrases = [];
14
+ while (this.pending) {
15
+ if (this.fence) {
16
+ const end = this.pending.indexOf(this.fence);
17
+ if (end < 0) {
18
+ this.pending = this.pending.slice(-2);
19
+ break;
20
+ }
21
+ this.pending = this.pending.slice(end + this.fence.length);
22
+ this.fence = null;
23
+ continue;
24
+ }
25
+ const openingFence = this.pending.match(/^\s*(```|~~~)/);
26
+ if (openingFence) {
27
+ this.pending = this.pending.trimStart();
28
+ const newline = this.pending.indexOf("\n");
29
+ if (newline < 0) break;
30
+ this.fence = openingFence[1];
31
+ this.pending = this.pending.slice(newline + 1);
32
+ continue;
33
+ }
34
+ if (/^\s*\|/.test(this.pending)) {
35
+ const newline = this.pending.indexOf("\n", this.pending.search(/\|/));
36
+ if (newline < 0) break;
37
+ this.pending = this.pending.slice(newline + 1);
38
+ continue;
39
+ }
40
+ let boundary = -1;
41
+ let brackets = 0;
42
+ let parentheses = 0;
43
+ let inlineCode = false;
44
+ for (let index = 0; index < this.pending.length; index += 1) {
45
+ if (!parentheses && /^https?:\/\//.test(this.pending.slice(index))) {
46
+ const end = this.pending.slice(index).search(/\s/);
47
+ if (end < 0) break;
48
+ index += end - 1;
49
+ continue;
50
+ }
51
+ const char = this.pending[index];
52
+ if (char === "`") inlineCode = !inlineCode;
53
+ if (char === "[") brackets += 1;
54
+ if (char === "]") brackets = Math.max(0, brackets - 1);
55
+ if (char === "(") parentheses += 1;
56
+ if (char === ")") parentheses = Math.max(0, parentheses - 1);
57
+ if (inlineCode || brackets || parentheses || char === "]" && this.pending[index + 1] === "(") continue;
58
+ if (SENTENCE_END.test(char)) {
59
+ boundary = index + 1;
60
+ break;
61
+ }
62
+ if (index + 1 >= this.maxCharacters) {
63
+ const window = this.pending.slice(0, index + 1);
64
+ const whitespace = Math.max(window.lastIndexOf(" "), window.lastIndexOf(","), window.lastIndexOf(","));
65
+ boundary = whitespace > this.maxCharacters / 2 ? whitespace + 1 : index + 1;
66
+ break;
67
+ }
68
+ }
69
+ if (boundary < 0) break;
70
+ const phrase = this.toSpeech(this.pending.slice(0, boundary));
71
+ this.pending = this.pending.slice(boundary);
72
+ if (phrase) phrases.push(phrase);
73
+ }
74
+ return phrases;
75
+ }
76
+ flush() {
77
+ const phrase = this.fence || /^\s*(```|~~~)/.test(this.pending) ? "" : this.toSpeech(this.pending);
78
+ this.pending = "";
79
+ this.fence = null;
80
+ return phrase ? [phrase] : [];
81
+ }
82
+ toSpeech(text) {
83
+ return stripMarkdown(text).replace(/https?:\/\/\S+/g, "").replace(/[*_~]/g, "").replace(/^\s*\|.*\|\s*$/gm, "").trim();
84
+ }
85
+ };
86
+ //#endregion
87
+ export { SpeakableSegmenter };
@@ -1,4 +1,4 @@
1
- import { listMediaUnderstandingProviders } from "../../media-understanding/registry.js";
1
+ import { getMediaUnderstandingProvider, listMediaUnderstandingProviders } from "../../media-understanding/registry.js";
2
2
  import { resolveSTTProviderConfig } from "./factory.js";
3
3
  import { mergeSttConfigFromAppConfig } from "./config.js";
4
4
  import { getVoiceProviderMetadata } from "../metadata/registry.js";
@@ -35,6 +35,7 @@ function fallbackMetadata(providerId, aliases) {
35
35
  };
36
36
  }
37
37
  function isSttProviderConfigured(providerId, config) {
38
+ if (!getMediaUnderstandingProvider(providerId)?.transcribeAudio) return false;
38
39
  return resolveSTTProviderConfig(providerId, config) !== null;
39
40
  }
40
41
  /** List registered audio STT providers with configured state and UI metadata. */
@@ -3,10 +3,12 @@ import { init_logger } from "../../../utils/logger.js";
3
3
  import { postJsonRequest } from "../../../media-shared/http/provider-http.js";
4
4
  import "../../../media-shared/http/index.js";
5
5
  import { registerMediaUnderstandingProvider } from "../../../media-understanding/registry.js";
6
+ import { openDashScopeStreamingStt } from "../../dashscope/streaming-stt-session.js";
6
7
  //#region src/voice/stt/providers/alibaba-transcription.ts
7
8
  init_logger();
8
9
  const log = createLogger("STT:Alibaba");
9
10
  const DEFAULT_MODEL = "qwen-audio-3.0-asr-flash";
11
+ const DEFAULT_STREAMING_MODEL = "qwen-audio-3.0-asr-flash-streaming";
10
12
  const DEFAULT_BASE_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation";
11
13
  function audioFormat(request) {
12
14
  const extension = request.fileName.split(".").pop()?.toLowerCase();
@@ -85,7 +87,17 @@ const alibabaTranscriptionProvider = {
85
87
  envKey: "DASHSCOPE_API_KEY",
86
88
  defaultModels: { audio: DEFAULT_MODEL },
87
89
  autoPriority: { audio: 10 },
88
- transcribeAudio
90
+ transcribeAudio,
91
+ streamingAudio: {
92
+ inputSampleRates: [16e3],
93
+ turnDetection: ["server_vad"],
94
+ defaultModel: DEFAULT_STREAMING_MODEL,
95
+ models: [DEFAULT_STREAMING_MODEL]
96
+ },
97
+ openAudioStream: (request) => openDashScopeStreamingStt({
98
+ ...request,
99
+ model: request.model || DEFAULT_STREAMING_MODEL
100
+ })
89
101
  };
90
102
  registerMediaUnderstandingProvider(alibabaTranscriptionProvider);
91
103
  //#endregion
@@ -3,10 +3,12 @@ import { init_xopc_cloud_config, resolveXopcModelRouterUrl } from "../../../prov
3
3
  import { getProviderAuthService, init_provider_auth_service } from "../../../providers/provider-auth-service.js";
4
4
  import { compareCatalogModels } from "../../../providers/model-catalog-ranking.js";
5
5
  import { registerMediaUnderstandingProvider } from "../../../media-understanding/registry.js";
6
+ import { openDashScopeStreamingStt } from "../../dashscope/streaming-stt-session.js";
6
7
  //#region src/voice/stt/providers/xopc-cloud-transcription.ts
7
8
  init_model_catalog_store();
8
9
  init_provider_auth_service();
9
10
  init_xopc_cloud_config();
11
+ const DEFAULT_STREAMING_MODEL = "qwen-audio-3.0-asr-flash-streaming";
10
12
  function defaultModel() {
11
13
  const source = getModelCatalogStore().getSource("xopc-cloud");
12
14
  return source?.models.filter((model) => model.availability === "available" && model.kind === "stt").sort((left, right) => compareCatalogModels(left, right, source.recommended?.stt))[0]?.id;
@@ -44,6 +46,27 @@ const xopcCloudTranscriptionProvider = {
44
46
  model,
45
47
  ...request.language ? { language: request.language } : {}
46
48
  };
49
+ },
50
+ streamingAudio: {
51
+ inputSampleRates: [16e3],
52
+ turnDetection: ["server_vad"],
53
+ defaultModel: DEFAULT_STREAMING_MODEL,
54
+ models: [DEFAULT_STREAMING_MODEL]
55
+ },
56
+ openAudioStream: async (request) => {
57
+ const accessToken = await getProviderAuthService().resolveApiKey("xopc-cloud", request.signal);
58
+ if (!accessToken) throw new Error("XOPC Cloud authorization is unavailable");
59
+ const source = getModelCatalogStore().getSource("xopc-cloud");
60
+ const baseUrl = (request.baseUrl ?? source?.baseUrl ?? resolveXopcModelRouterUrl()).replace(/\/+$/, "");
61
+ const relayUrl = new URL(`${baseUrl}/audio/transcriptions/realtime`);
62
+ relayUrl.protocol = relayUrl.protocol === "https:" ? "wss:" : "ws:";
63
+ relayUrl.searchParams.set("model", request.model || DEFAULT_STREAMING_MODEL);
64
+ return openDashScopeStreamingStt({
65
+ ...request,
66
+ apiKey: accessToken,
67
+ baseUrl: relayUrl.toString(),
68
+ model: request.model || DEFAULT_STREAMING_MODEL
69
+ });
47
70
  }
48
71
  };
49
72
  registerMediaUnderstandingProvider(xopcCloudTranscriptionProvider);
@@ -100,6 +100,7 @@ function isTTSAvailable(config) {
100
100
  }
101
101
  }
102
102
  function isProviderConfigured(provider, config) {
103
+ if (!getSpeechProvider(provider)) return false;
103
104
  return resolveSpeechProvider(provider, config) !== null;
104
105
  }
105
106
  function getAvailableProviders(config) {
@@ -187,7 +187,8 @@ function createOpenAiCompatibleSpeechProvider(options) {
187
187
  response_format: responseFormat,
188
188
  ...speed === void 0 ? {} : { speed },
189
189
  ...buildExtraJsonBodyFields(config, options.extraJsonBodyFields)
190
- }
190
+ },
191
+ signal: req.signal
191
192
  });
192
193
  return {
193
194
  audioBuffer: Buffer.from(await response.arrayBuffer()),
@@ -216,6 +217,7 @@ function createOpenAiCompatibleSpeechProvider(options) {
216
217
  const response = await fetchWithTimeoutGuarded(`${baseUrl}/audio/speech`, {
217
218
  timeoutMs: req.timeoutMs,
218
219
  label: options.apiErrorLabel ?? `${options.label} TTS streaming`,
220
+ signal: req.signal,
219
221
  init: {
220
222
  method: "POST",
221
223
  headers: {
@@ -6,11 +6,6 @@
6
6
  * doesn't fit the OpenAI-compatible factory.
7
7
  *
8
8
  * Implementation notes (per docs/voice-rearchitecture.md §8.4.1):
9
- * - `synthesizeStream` is intentionally not implemented. Native qwen-tts
10
- * streaming uses a WebSocket protocol
11
- * (wss://dashscope.aliyuncs.com/api-ws/v1/inference); speak-core's stream
12
- * fallback wraps `synthesize` output as a single-chunk ReadableStream so
13
- * callers see the same shape.
14
9
  * - `maxTextLength = 512` (DashScope qwen-tts hard limit). Enforced upstream
15
10
  * via `truncateAtSentenceBoundary` before this provider sees the text.
16
11
  * - The audio URL returned by DashScope is hosted on Alibaba's CDN (variable
@@ -24,4 +19,5 @@
24
19
  * as a hardening pass.
25
20
  */
26
21
  import type { SpeechProviderPlugin } from '../speech-provider-types.js';
22
+ export declare const ALIBABA_REALTIME_TTS_MODEL = "qwen3-tts-flash-realtime";
27
23
  export declare const alibabaSpeechProvider: SpeechProviderPlugin;
@@ -4,6 +4,7 @@ import { ProviderHttpError } from "../../../media-shared/http/provider-http-erro
4
4
  import { fetchWithTimeoutGuarded, postJsonRequest } from "../../../media-shared/http/provider-http.js";
5
5
  import "../../../media-shared/http/index.js";
6
6
  import { registerSpeechProvider } from "../speech-registry.js";
7
+ import { openDashScopeStreamingTts } from "../../dashscope/streaming-tts-stream.js";
7
8
  //#region src/voice/tts/providers/alibaba-speech.ts
8
9
  /**
9
10
  * Alibaba DashScope TTS provider (qwen-tts model).
@@ -13,11 +14,6 @@ import { registerSpeechProvider } from "../speech-registry.js";
13
14
  * doesn't fit the OpenAI-compatible factory.
14
15
  *
15
16
  * Implementation notes (per docs/voice-rearchitecture.md §8.4.1):
16
- * - `synthesizeStream` is intentionally not implemented. Native qwen-tts
17
- * streaming uses a WebSocket protocol
18
- * (wss://dashscope.aliyuncs.com/api-ws/v1/inference); speak-core's stream
19
- * fallback wraps `synthesize` output as a single-chunk ReadableStream so
20
- * callers see the same shape.
21
17
  * - `maxTextLength = 512` (DashScope qwen-tts hard limit). Enforced upstream
22
18
  * via `truncateAtSentenceBoundary` before this provider sees the text.
23
19
  * - The audio URL returned by DashScope is hosted on Alibaba's CDN (variable
@@ -34,6 +30,7 @@ init_logger();
34
30
  const log = createLogger("SpeechProvider:Alibaba");
35
31
  const DEFAULT_BASE_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation";
36
32
  const DEFAULT_MODEL = "qwen-tts";
33
+ const ALIBABA_REALTIME_TTS_MODEL = "qwen3-tts-flash-realtime";
37
34
  const DEFAULT_VOICE = "longxiaochun";
38
35
  const ENV_KEY = "DASHSCOPE_API_KEY";
39
36
  const MAX_TEXT_LENGTH = 512;
@@ -135,7 +132,8 @@ const alibabaSpeechProvider = {
135
132
  model,
136
133
  input: { text: req.text },
137
134
  parameters: { voice }
138
- }
135
+ },
136
+ signal: req.signal
139
137
  })).json();
140
138
  if (data.output?.finish_reason === "null" && !data.output?.audio?.url) throw new Error(`Alibaba TTS API error: ${JSON.stringify(data)}`);
141
139
  let audioBuffer;
@@ -144,7 +142,8 @@ const alibabaSpeechProvider = {
144
142
  const audioResponse = await fetchWithTimeoutGuarded(audioUrl, {
145
143
  timeoutMs: req.timeoutMs,
146
144
  label: "Alibaba TTS audio download",
147
- allowPrivateNetwork: false
145
+ allowPrivateNetwork: false,
146
+ signal: req.signal
148
147
  });
149
148
  if (!audioResponse.ok) throw new ProviderHttpError({
150
149
  label: "Alibaba TTS audio download",
@@ -166,8 +165,26 @@ const alibabaSpeechProvider = {
166
165
  fileExtension: "wav",
167
166
  voiceCompatible: false
168
167
  };
168
+ },
169
+ synthesizeStream: async (req) => {
170
+ const config = readProviderConfig(req.providerConfig);
171
+ const apiKey = resolveApiKey(config);
172
+ if (!apiKey) throw new Error(`Alibaba TTS API key missing (set ${ENV_KEY} or messages.tts.providers.alibaba.apiKey)`);
173
+ const overrides = req.providerOverrides ?? {};
174
+ const voice = trimToUndefined(overrides.voice ?? overrides.voiceId) ?? config.voice;
175
+ const instructions = trimToUndefined(overrides.instructions);
176
+ return openDashScopeStreamingTts({
177
+ apiKey,
178
+ baseUrl: config.baseUrl,
179
+ model: ALIBABA_REALTIME_TTS_MODEL,
180
+ voice,
181
+ text: req.text,
182
+ ...instructions ? { instructions } : {},
183
+ signal: req.signal,
184
+ timeoutMs: req.timeoutMs
185
+ });
169
186
  }
170
187
  };
171
188
  registerSpeechProvider(alibabaSpeechProvider);
172
189
  //#endregion
173
- export { alibabaSpeechProvider };
190
+ export { ALIBABA_REALTIME_TTS_MODEL, alibabaSpeechProvider };
@@ -94,16 +94,31 @@ function parseDirectiveTokenInternal(ctx) {
94
94
  default: return { handled: false };
95
95
  }
96
96
  }
97
- async function waitForNonEmptyFile(filePath, timeoutMs) {
97
+ async function waitForNonEmptyFile(filePath, timeoutMs, signal) {
98
98
  const deadline = Date.now() + Math.min(Math.max(timeoutMs, 1e3), 5e3);
99
99
  while (Date.now() < deadline) {
100
+ if (signal.aborted) throw signal.reason;
100
101
  try {
101
102
  if (statSync(filePath).size > 0) return;
102
103
  } catch {}
103
104
  await new Promise((resolve) => setTimeout(resolve, 50));
104
105
  }
105
106
  }
106
- async function synthesizeToBuffer(text, config, voiceOverride, timeoutMs) {
107
+ async function withAbort(promise, signal) {
108
+ if (signal.aborted) throw signal.reason;
109
+ return new Promise((resolve, reject) => {
110
+ const onAbort = () => reject(signal.reason);
111
+ signal.addEventListener("abort", onAbort, { once: true });
112
+ promise.then((value) => {
113
+ signal.removeEventListener("abort", onAbort);
114
+ resolve(value);
115
+ }, (error) => {
116
+ signal.removeEventListener("abort", onAbort);
117
+ reject(error);
118
+ });
119
+ });
120
+ }
121
+ async function synthesizeToBuffer(text, config, voiceOverride, timeoutMs, signal) {
107
122
  const { EdgeTTS } = await import("node-edge-tts");
108
123
  const voice = voiceOverride ?? config.voice;
109
124
  const tempDir = mkdtempSync(path.join(tmpdir(), "tts-edge-"));
@@ -115,7 +130,7 @@ async function synthesizeToBuffer(text, config, voiceOverride, timeoutMs) {
115
130
  outputFormat: config.outputFormat,
116
131
  textLength: text.length
117
132
  }, "Calling Edge TTS");
118
- await new EdgeTTS({
133
+ await withAbort(new EdgeTTS({
119
134
  voice,
120
135
  lang: config.lang,
121
136
  outputFormat: config.outputFormat,
@@ -124,8 +139,8 @@ async function synthesizeToBuffer(text, config, voiceOverride, timeoutMs) {
124
139
  pitch: config.pitch,
125
140
  volume: config.volume,
126
141
  timeout: timeoutMs
127
- }).ttsPromise(text, outputPath);
128
- await waitForNonEmptyFile(outputPath, timeoutMs);
142
+ }).ttsPromise(text, outputPath), signal);
143
+ await waitForNonEmptyFile(outputPath, timeoutMs, signal);
129
144
  const buffer = readFileSync(outputPath);
130
145
  if (buffer.length === 0) throw new Error(`Edge TTS produced an empty ${format} file for voice ${voice}`);
131
146
  return {
@@ -163,29 +178,13 @@ const edgeSpeechProvider = {
163
178
  if (!config.enabled) throw new Error("Edge TTS is disabled in config (messages.tts.providers.edge.enabled = false)");
164
179
  const overrides = req.providerOverrides ?? {};
165
180
  const voiceOverride = trimToUndefined(overrides.voice ?? overrides.voiceId);
166
- const { buffer, outputFormat, ext } = await synthesizeToBuffer(req.text, config, voiceOverride, req.timeoutMs);
181
+ const { buffer, outputFormat, ext } = await synthesizeToBuffer(req.text, config, voiceOverride, req.timeoutMs, req.signal);
167
182
  return {
168
183
  audioBuffer: buffer,
169
184
  outputFormat,
170
185
  fileExtension: ext,
171
186
  voiceCompatible: outputFormat === "opus" || outputFormat === "ogg"
172
187
  };
173
- },
174
- synthesizeStream: async (req) => {
175
- const config = readProviderConfig(req.providerConfig);
176
- if (!config.enabled) throw new Error("Edge TTS is disabled in config (messages.tts.providers.edge.enabled = false)");
177
- const overrides = req.providerOverrides ?? {};
178
- const voiceOverride = trimToUndefined(overrides.voice ?? overrides.voiceId);
179
- const { buffer, outputFormat, ext } = await synthesizeToBuffer(req.text, config, voiceOverride, req.timeoutMs);
180
- return {
181
- audioStream: new ReadableStream({ start(controller) {
182
- controller.enqueue(new Uint8Array(buffer));
183
- controller.close();
184
- } }),
185
- outputFormat,
186
- fileExtension: ext,
187
- voiceCompatible: outputFormat === "opus" || outputFormat === "ogg"
188
- };
189
188
  }
190
189
  };
191
190
  registerSpeechProvider(edgeSpeechProvider);
@@ -268,19 +268,22 @@ const minimaxSpeechProvider = {
268
268
  model,
269
269
  voiceId: voice,
270
270
  text: req.text,
271
- timeoutMs: callTimeoutMs
271
+ timeoutMs: callTimeoutMs,
272
+ signal: req.signal
272
273
  });
273
274
  const fileId = await pollTask({
274
275
  baseUrl: config.baseUrl,
275
276
  apiKey,
276
277
  taskId,
277
- timeoutMs: callTimeoutMs
278
+ timeoutMs: callTimeoutMs,
279
+ signal: req.signal
278
280
  });
279
281
  const audioBuffer = await downloadAudio({
280
282
  baseUrl: config.baseUrl,
281
283
  apiKey,
282
284
  fileId,
283
- timeoutMs: callTimeoutMs
285
+ timeoutMs: callTimeoutMs,
286
+ signal: req.signal
284
287
  });
285
288
  log.debug({
286
289
  size: audioBuffer.length,
@@ -3,6 +3,7 @@ import { init_xopc_cloud_config, resolveXopcModelRouterUrl } from "../../../prov
3
3
  import { getProviderAuthService, init_provider_auth_service } from "../../../providers/provider-auth-service.js";
4
4
  import { compareCatalogModels } from "../../../providers/model-catalog-ranking.js";
5
5
  import { registerSpeechProvider } from "../speech-registry.js";
6
+ import { openDashScopeStreamingTts } from "../../dashscope/streaming-tts-stream.js";
6
7
  //#region src/voice/tts/providers/xopc-cloud-speech.ts
7
8
  init_model_catalog_store();
8
9
  init_provider_auth_service();
@@ -92,7 +93,7 @@ const xopcCloudSpeechProvider = {
92
93
  ...speed !== void 0 && catalogModel.tts?.speed ? { speed } : {},
93
94
  ...instructions && catalogModel.tts?.instructions ? { instructions } : {}
94
95
  }),
95
- signal: AbortSignal.timeout(request.timeoutMs),
96
+ signal: request.signal,
96
97
  redirect: "error"
97
98
  });
98
99
  if (!response.ok) {
@@ -108,6 +109,30 @@ const xopcCloudSpeechProvider = {
108
109
  fileExtension,
109
110
  voiceCompatible: fileExtension === "opus" || fileExtension === "ogg"
110
111
  };
112
+ },
113
+ synthesizeStream: async (request) => {
114
+ const config = readConfig(request.providerConfig);
115
+ const model = typeof request.providerOverrides?.model === "string" ? request.providerOverrides.model : config.model;
116
+ const voice = typeof request.providerOverrides?.voice === "string" ? request.providerOverrides.voice : config.voice;
117
+ if (!model || !voice) throw new Error("XOPC Cloud realtime TTS model or voice is unavailable");
118
+ const catalogModel = availableModels().find((entry) => entry.id === model);
119
+ if (!catalogModel?.tts?.streaming || !catalogModel.tts.outputFormats.includes("pcm")) throw new Error(`XOPC Cloud TTS model does not support PCM streaming: ${model}`);
120
+ const token = await getProviderAuthService().resolveApiKey("xopc-cloud", request.signal);
121
+ if (!token) throw new Error("XOPC Cloud authorization is unavailable");
122
+ const relayUrl = new URL(`${config.baseUrl.replace(/\/+$/, "")}/audio/speech/realtime`);
123
+ relayUrl.protocol = relayUrl.protocol === "https:" ? "wss:" : "ws:";
124
+ relayUrl.searchParams.set("model", model);
125
+ const instructions = typeof request.providerOverrides?.instructions === "string" ? request.providerOverrides.instructions.trim() : config.instructions;
126
+ return openDashScopeStreamingTts({
127
+ apiKey: token,
128
+ baseUrl: relayUrl.toString(),
129
+ model,
130
+ voice,
131
+ text: request.text,
132
+ ...instructions ? { instructions } : {},
133
+ signal: request.signal,
134
+ timeoutMs: request.timeoutMs
135
+ });
111
136
  }
112
137
  };
113
138
  registerSpeechProvider(xopcCloudSpeechProvider);
@@ -23,9 +23,13 @@ export interface SpeakOptions {
23
23
  parseDirectives?: boolean;
24
24
  modelOverrides?: TTSModelOverrideConfig;
25
25
  appConfig?: Config;
26
- /** Optional caller-supplied AbortSignal (currently passed through to providers via timeoutMs guard only). */
26
+ /** Optional caller-supplied cancellation signal. */
27
27
  signal?: AbortSignal;
28
28
  }
29
+ export interface SpeakStreamOptions extends SpeakOptions {
30
+ /** Disable provider/model fallback after a realtime route has been selected. */
31
+ allowFallback?: boolean;
32
+ }
29
33
  /**
30
34
  * Run the TTS chain (primary → fallback) and return the first successful result.
31
35
  */
@@ -51,4 +55,4 @@ export interface SpeakStreamResult {
51
55
  * Streaming TTS — tries the chain and returns the first provider's stream.
52
56
  * Providers without `synthesizeStream` are wrapped via wrapBufferAsStream.
53
57
  */
54
- export declare function speakStream(text: string, config: TTSConfig, options?: SpeakOptions): Promise<SpeakStreamResult>;
58
+ export declare function speakStream(text: string, config: TTSConfig, options?: SpeakStreamOptions): Promise<SpeakStreamResult>;
@@ -87,7 +87,7 @@ async function prepareTextForSynthesis(text, config, options) {
87
87
  };
88
88
  }
89
89
  /** Build the SpeechSynthesisRequest from a resolved provider + prepared text. */
90
- function buildSynthesisRequest(resolved, preparedText, ttsOptions, directiveOverrides, appConfig, target) {
90
+ function buildSynthesisRequest(resolved, preparedText, ttsOptions, directiveOverrides, appConfig, target, signal) {
91
91
  const providerOverrides = mergeOverrides(ttsOptions, pickProviderOverrides(directiveOverrides, resolved.providerId));
92
92
  const callTimeoutMs = ttsOptions?.timeoutMs ?? resolved.timeoutMs;
93
93
  return {
@@ -96,6 +96,7 @@ function buildSynthesisRequest(resolved, preparedText, ttsOptions, directiveOver
96
96
  providerConfig: resolved.providerConfig,
97
97
  target,
98
98
  timeoutMs: callTimeoutMs,
99
+ signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(callTimeoutMs)]) : AbortSignal.timeout(callTimeoutMs),
99
100
  ...providerOverrides ? { providerOverrides } : {}
100
101
  };
101
102
  }
@@ -145,7 +146,7 @@ async function speak(text, config, options) {
145
146
  textLength: prepared.text.length,
146
147
  provider: resolved.providerId
147
148
  }, "Converting text to speech");
148
- const request = buildSynthesisRequest(resolved, prepared.text, prepared.ttsOptions, prepared.directiveOverrides, options?.appConfig, "audio-file");
149
+ const request = buildSynthesisRequest(resolved, prepared.text, prepared.ttsOptions, prepared.directiveOverrides, options?.appConfig, "audio-file", options?.signal);
149
150
  const result = await resolved.plugin.synthesize(request);
150
151
  const durationSeconds = (Date.now() - startTime) / 1e3;
151
152
  attempts.push({
@@ -209,7 +210,7 @@ async function speakWithProvider(text, config, providerName, options) {
209
210
  if (typeof resolved.plugin.synthesize !== "function") throw new Error(`Provider '${providerName}' does not implement synthesize()`);
210
211
  const prepared = await prepareTextForSynthesis(text, config, options);
211
212
  const startTime = Date.now();
212
- const request = buildSynthesisRequest(resolved, prepared.text, prepared.ttsOptions, prepared.directiveOverrides, options?.appConfig, "audio-file");
213
+ const request = buildSynthesisRequest(resolved, prepared.text, prepared.ttsOptions, prepared.directiveOverrides, options?.appConfig, "audio-file", options?.signal);
213
214
  return {
214
215
  ...toTtsResult(resolved, await resolved.plugin.synthesize(request), (Date.now() - startTime) / 1e3),
215
216
  attempts: [{
@@ -229,41 +230,52 @@ async function speakWithProvider(text, config, providerName, options) {
229
230
  * Providers without `synthesizeStream` are wrapped via wrapBufferAsStream.
230
231
  */
231
232
  async function speakStream(text, config, options) {
233
+ options?.signal?.throwIfAborted();
232
234
  if (!config.enabled) throw new Error("TTS is not enabled");
233
235
  if (config.trigger === "tagged" && !/\[\[tts/.test(text)) throw new Error("TTS trigger is tagged but no [[tts]] directive found");
234
236
  const prepared = await prepareTextForSynthesis(text, config, options);
235
- const chain = resolveSpeechProviderChain(config);
237
+ options?.signal?.throwIfAborted();
238
+ const chain = options?.allowFallback === false ? [resolveSpeechProvider(prepared.selectedProvider, config)].filter((provider) => provider !== null) : resolveSpeechProviderChain(config);
239
+ if (chain.length === 0) throw new Error(`TTS provider "${prepared.selectedProvider}" is not available`);
236
240
  let lastError;
237
- for (const resolved of chain) try {
238
- const baseRequest = buildSynthesisRequest(resolved, prepared.text, prepared.ttsOptions, prepared.directiveOverrides, options?.appConfig, "voice-note");
239
- let streamResult;
240
- if (typeof resolved.plugin.synthesizeStream === "function") streamResult = await resolved.plugin.synthesizeStream(baseRequest);
241
- else if (typeof resolved.plugin.synthesize === "function") streamResult = wrapBufferAsStream(await resolved.plugin.synthesize(baseRequest));
242
- else throw new Error(`Provider "${resolved.providerId}" implements neither synthesize nor synthesizeStream`);
243
- log.info({
244
- provider: resolved.providerId,
245
- outputFormat: streamResult.outputFormat,
246
- voiceCompatible: streamResult.voiceCompatible,
247
- streamed: typeof resolved.plugin.synthesizeStream === "function"
248
- }, "TTS stream ready");
249
- const release = streamResult.release ?? (async () => {});
250
- return {
251
- audioStream: streamResult.audioStream,
252
- outputFormat: streamResult.outputFormat,
253
- fileExtension: streamResult.fileExtension,
254
- voiceCompatible: streamResult.voiceCompatible,
255
- provider: resolved.providerId,
256
- release,
257
- ttsText: prepared.text,
258
- wasPreprocessed: prepared.wasPreprocessed,
259
- wasSummarized: prepared.wasSummarized
260
- };
261
- } catch (error) {
262
- lastError = error instanceof Error ? error : new Error(String(error));
263
- log.warn({
264
- err: lastError,
265
- provider: resolved.providerId
266
- }, `TTS stream provider "${resolved.providerId}" failed, trying next`);
241
+ for (const [index, resolved] of chain.entries()) {
242
+ options?.signal?.throwIfAborted();
243
+ try {
244
+ const baseRequest = buildSynthesisRequest(resolved, prepared.text, prepared.ttsOptions, prepared.directiveOverrides, options?.appConfig, "voice-note", options?.signal);
245
+ let streamResult;
246
+ if (typeof resolved.plugin.synthesizeStream === "function") streamResult = await resolved.plugin.synthesizeStream(baseRequest);
247
+ else if (typeof resolved.plugin.synthesize === "function") streamResult = wrapBufferAsStream(await resolved.plugin.synthesize(baseRequest));
248
+ else throw new Error(`Provider "${resolved.providerId}" implements neither synthesize nor synthesizeStream`);
249
+ if (options?.signal?.aborted) {
250
+ await streamResult.release?.();
251
+ options.signal.throwIfAborted();
252
+ }
253
+ log.info({
254
+ provider: resolved.providerId,
255
+ outputFormat: streamResult.outputFormat,
256
+ voiceCompatible: streamResult.voiceCompatible,
257
+ streamed: typeof resolved.plugin.synthesizeStream === "function"
258
+ }, "TTS stream ready");
259
+ const release = streamResult.release ?? (async () => {});
260
+ return {
261
+ audioStream: streamResult.audioStream,
262
+ outputFormat: streamResult.outputFormat,
263
+ fileExtension: streamResult.fileExtension,
264
+ voiceCompatible: streamResult.voiceCompatible,
265
+ provider: resolved.providerId,
266
+ release,
267
+ ttsText: prepared.text,
268
+ wasPreprocessed: prepared.wasPreprocessed,
269
+ wasSummarized: prepared.wasSummarized
270
+ };
271
+ } catch (error) {
272
+ options?.signal?.throwIfAborted();
273
+ lastError = error instanceof Error ? error : new Error(String(error));
274
+ log.warn({
275
+ err: lastError,
276
+ provider: resolved.providerId
277
+ }, `TTS stream provider "${resolved.providerId}" failed${index < chain.length - 1 ? ", trying next" : ""}`);
278
+ }
267
279
  }
268
280
  throw lastError ?? /* @__PURE__ */ new Error("All TTS providers failed (streaming)");
269
281
  }
@@ -86,6 +86,7 @@ export interface SpeechSynthesisRequest {
86
86
  providerOverrides?: SpeechProviderOverrides;
87
87
  /** Hard timeout from the orchestrator (already merged with provider defaults). */
88
88
  timeoutMs: number;
89
+ signal: AbortSignal;
89
90
  }
90
91
  export type SpeechSynthesisStreamRequest = SpeechSynthesisRequest;
91
92
  /** Voice catalog entry returned by `listVoices`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xopcai/xopc",
3
- "version": "0.0.241",
3
+ "version": "0.0.243",
4
4
  "description": "Local-first personal AI assistant with reviewable user understanding, durable tasks, workflows, automations, and local or cloud models.",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -67,10 +67,10 @@
67
67
  "dependencies": {
68
68
  "@composio/core": "0.14.1",
69
69
  "@composio/experimental": "0.2.0",
70
- "@earendil-works/pi-agent-core": "0.84.4",
71
- "@earendil-works/pi-ai": "0.84.4",
72
- "@earendil-works/pi-coding-agent": "0.84.4",
73
- "@earendil-works/pi-tui": "0.84.4",
70
+ "@earendil-works/pi-agent-core": "0.85.1",
71
+ "@earendil-works/pi-ai": "0.85.1",
72
+ "@earendil-works/pi-coding-agent": "0.85.1",
73
+ "@earendil-works/pi-tui": "0.85.1",
74
74
  "@grammyjs/runner": "^2.0.3",
75
75
  "@hono/node-server": "^2.0.4",
76
76
  "@inquirer/prompts": "^8.5.0",
@@ -164,8 +164,8 @@
164
164
  "vitest": "4.1.5",
165
165
  "vue": "^3.5.35",
166
166
  "@xopcai/endpoint-tools-protocol": "0.0.0",
167
- "@xopcai/realtime-protocol": "0.0.0",
168
167
  "@xopcai/gateway-contract": "0.0.0",
168
+ "@xopcai/realtime-protocol": "0.0.0",
169
169
  "@xopcai/realtime-client": "0.0.0"
170
170
  },
171
171
  "scripts": {