@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,622 @@
1
+ import { createLogger } from "../../utils/logger/index.js";
2
+ import { init_logger } from "../../utils/logger.js";
3
+ import { onUserContextChange } from "../../user-context/changes.js";
4
+ import { getModelCatalogStore, init_model_catalog_store } from "../../providers/model-catalog-store.js";
5
+ import { ALIBABA_REALTIME_TTS_MODEL } from "../tts/providers/alibaba-speech.js";
6
+ import { resolveSpeechProviderChain } from "../tts/factory.js";
7
+ import { mergeTtsConfigFromAppConfig } from "../tts/merge-config.js";
8
+ import { getMediaUnderstandingProvider } from "../../media-understanding/registry.js";
9
+ import { resolveSTTProviderChain } from "../stt/factory.js";
10
+ import { mergeSttConfigFromAppConfig } from "../stt/config.js";
11
+ import { voiceConversationInstructions } from "./conversation-context.js";
12
+ import { createPreauthConnectionBudget } from "../../gateway/security/preauth-connection-budget.js";
13
+ import { encodeVoiceAudioFrame } from "../../../packages/realtime-protocol/src/voice-audio.js";
14
+ import { VOICE_REALTIME_HEARTBEAT_INTERVAL_MS, VOICE_REALTIME_MAX_BINARY_FRAME_BYTES, VOICE_REALTIME_START_TIMEOUT_MS, VOICE_REALTIME_WS_PATH, parseVoiceClientJsonFrame, parseVoiceClientMessage } from "../../../packages/realtime-protocol/src/voice.js";
15
+ import { createAgentVoiceEngine } from "./agentEngine.js";
16
+ import { resolveOmniRoute } from "./omniRoute.js";
17
+ import { createOmniVoiceEngine } from "./omniEngine.js";
18
+ import { createRequire } from "node:module";
19
+ import crypto from "node:crypto";
20
+ //#region src/voice/realtime/runtime.ts
21
+ init_logger();
22
+ init_model_catalog_store();
23
+ const { WebSocket: WebSocketState, WebSocketServer } = createRequire(import.meta.url)("ws");
24
+ const log = createLogger("Voice:Realtime");
25
+ const TICKET_TTL_MS = 6e4;
26
+ const MAX_OUTSTANDING_TICKETS = 200;
27
+ const MAX_CONNECTIONS = 50;
28
+ const MAX_CONNECTIONS_PER_PRINCIPAL = 2;
29
+ const MAX_CLOCK_SKEW_MS = 6e4;
30
+ var VoiceSessionCreationError = class extends Error {
31
+ constructor(code, message, status) {
32
+ super(message);
33
+ this.code = code;
34
+ this.status = status;
35
+ this.name = "VoiceSessionCreationError";
36
+ }
37
+ };
38
+ function ticketKey(ticket) {
39
+ return crypto.createHash("sha256").update(ticket).digest("hex");
40
+ }
41
+ function isStreamingProvider(provider) {
42
+ return Boolean(provider?.streamingAudio && provider.openAudioStream);
43
+ }
44
+ function resolveStreamingStt(config, language) {
45
+ const sttConfig = mergeSttConfigFromAppConfig(config.tools?.media?.audio, config.tools?.media);
46
+ for (const entry of resolveSTTProviderChain(sttConfig)) {
47
+ const plugin = getMediaUnderstandingProvider(entry.id);
48
+ if (!isStreamingProvider(plugin)) continue;
49
+ const model = plugin.streamingAudio.defaultModel;
50
+ return {
51
+ plugin,
52
+ model,
53
+ ...entry.apiKey ? { apiKey: entry.apiKey } : {},
54
+ ...entry.baseUrl ? { baseUrl: entry.baseUrl } : {},
55
+ ...entry.headers ? { headers: entry.headers } : {},
56
+ ...language ?? entry.language ? { language: language ?? entry.language } : {},
57
+ ...entry.prompt ? { prompt: entry.prompt } : {},
58
+ route: {
59
+ provider: plugin.id,
60
+ model,
61
+ managed: plugin.id === "xopc-cloud"
62
+ }
63
+ };
64
+ }
65
+ }
66
+ function resolveStreamingTts(config) {
67
+ let effective = mergeTtsConfigFromAppConfig(config.messages?.tts);
68
+ const selection = config.voice?.realtime?.tts;
69
+ if (selection) {
70
+ const slice = effective.providers?.[selection.provider] ?? {};
71
+ const apiKey = (config.tools?.media?.audio?.providers?.[selection.provider])?.apiKey || (selection.provider === "alibaba" ? process.env.DASHSCOPE_API_KEY : void 0) || slice.apiKey;
72
+ effective = {
73
+ ...effective,
74
+ enabled: true,
75
+ provider: selection.provider,
76
+ managedAuto: false,
77
+ fallback: {
78
+ enabled: false,
79
+ order: []
80
+ },
81
+ providers: { [selection.provider]: {
82
+ ...apiKey ? { apiKey } : {},
83
+ ...slice.baseUrl ? { baseUrl: slice.baseUrl } : {},
84
+ ...selection.voice ? { voice: selection.voice } : selection.provider === "alibaba" ? { voice: "Cherry" } : {}
85
+ } }
86
+ };
87
+ }
88
+ if (!effective.enabled) return void 0;
89
+ let chain;
90
+ try {
91
+ chain = resolveSpeechProviderChain(effective);
92
+ } catch {
93
+ return;
94
+ }
95
+ for (const provider of chain) {
96
+ if (typeof provider.plugin.synthesizeStream !== "function") continue;
97
+ let model;
98
+ if (provider.plugin.id === "alibaba") model = ALIBABA_REALTIME_TTS_MODEL;
99
+ else if (provider.plugin.id === "xopc-cloud") {
100
+ const candidate = typeof provider.providerConfig.model === "string" ? provider.providerConfig.model : void 0;
101
+ const catalogModel = getModelCatalogStore().getSource("xopc-cloud")?.models.find((entry) => entry.id === candidate);
102
+ if (!catalogModel?.tts?.streaming || !catalogModel.tts.outputFormats.includes("pcm")) continue;
103
+ model = candidate;
104
+ } else continue;
105
+ if (!model) continue;
106
+ const rawSlice = effective.providers?.[provider.plugin.id] ?? {};
107
+ return {
108
+ provider,
109
+ config: {
110
+ ...effective,
111
+ enabled: true,
112
+ provider: provider.plugin.id,
113
+ managedAuto: false,
114
+ trigger: "always",
115
+ fallback: {
116
+ enabled: false,
117
+ order: []
118
+ },
119
+ summarization: { enabled: false },
120
+ modelOverrides: { enabled: false },
121
+ providers: { [provider.plugin.id]: {
122
+ ...rawSlice,
123
+ model
124
+ } }
125
+ },
126
+ route: {
127
+ provider: provider.plugin.id,
128
+ model,
129
+ managed: provider.plugin.id === "xopc-cloud"
130
+ }
131
+ };
132
+ }
133
+ }
134
+ function rawText(data) {
135
+ if (typeof data === "string") return data;
136
+ if (data instanceof ArrayBuffer) return Buffer.from(data).toString("utf8");
137
+ if (Array.isArray(data)) return Buffer.concat(data).toString("utf8");
138
+ return data.toString("utf8");
139
+ }
140
+ var VoiceRealtimeRuntime = class {
141
+ wss = new WebSocketServer({
142
+ noServer: true,
143
+ maxPayload: VOICE_REALTIME_MAX_BINARY_FRAME_BYTES,
144
+ perMessageDeflate: false
145
+ });
146
+ tickets = /* @__PURE__ */ new Map();
147
+ preauthBudget = createPreauthConnectionBudget();
148
+ socketsByPrincipal = /* @__PURE__ */ new Map();
149
+ conversationReservations = /* @__PURE__ */ new Map();
150
+ closed = false;
151
+ constructor(options) {
152
+ this.options = options;
153
+ this.wss.on("connection", (socket, request) => this.handleConnection(socket, request));
154
+ this.wss.on("error", (err) => log.error({ err }, "Voice WebSocket server failed"));
155
+ }
156
+ async prepareSession(request, now = Date.now()) {
157
+ const config = structuredClone(this.options.getConfig());
158
+ this.pruneTickets(now);
159
+ if (!config.voice?.realtime?.enabled) throw new VoiceSessionCreationError("VOICE_DISABLED", "Realtime voice is disabled", 503);
160
+ request = request.purpose === "conversation" ? {
161
+ ...request,
162
+ engine: request.engine ?? config.voice.realtime.defaultEngine
163
+ } : request;
164
+ let omni;
165
+ if (request.engine === "omni") try {
166
+ omni = await resolveOmniRoute(config);
167
+ } catch {
168
+ throw new VoiceSessionCreationError("PROVIDER_UNAVAILABLE", "Natural conversation is unavailable. Check its model, endpoint and credentials.", 503);
169
+ }
170
+ if (omni && (!this.options.recordOmniTranscript || !this.options.getConversationContext)) throw new VoiceSessionCreationError("PROVIDER_UNAVAILABLE", "Natural conversation storage is unavailable", 503);
171
+ const conversationSessionId = request.purpose === "conversation" && request.sessionKey ? await this.options.getSessionIdentity?.(request.sessionKey) : void 0;
172
+ if (omni && !conversationSessionId) throw new VoiceSessionCreationError("SESSION_NOT_FOUND", "Conversation session was not found", 404);
173
+ const stt = omni ? void 0 : resolveStreamingStt(config, request.language);
174
+ if (!omni && !stt) throw new VoiceSessionCreationError("PROVIDER_UNAVAILABLE", "No configured streaming speech-to-text provider is available", 503);
175
+ const inputMode = "server_vad";
176
+ if (stt && !stt.plugin.streamingAudio.turnDetection.includes(inputMode)) throw new VoiceSessionCreationError("PROVIDER_UNAVAILABLE", `Streaming speech-to-text provider does not support ${inputMode}`, 503);
177
+ const tts = request.engine === "agent" ? resolveStreamingTts(config) : void 0;
178
+ if (request.purpose === "conversation") {
179
+ if (!request.sessionKey || !await this.options.sessionExists(request.sessionKey)) throw new VoiceSessionCreationError("SESSION_NOT_FOUND", "Conversation session was not found", 404);
180
+ if (this.options.sessionBusy(request.sessionKey)) throw new VoiceSessionCreationError("SESSION_CONFLICT", "Conversation session already has an active response", 409);
181
+ if (this.conversationReservations.has(request.sessionKey)) throw new VoiceSessionCreationError("SESSION_CONFLICT", "Conversation session already has a voice connection", 409);
182
+ if (!omni && !tts) throw new VoiceSessionCreationError("PROVIDER_UNAVAILABLE", "No configured PCM streaming text-to-speech provider is available", 503);
183
+ }
184
+ if (this.tickets.size >= MAX_OUTSTANDING_TICKETS) throw new VoiceSessionCreationError("SESSION_LIMIT", "Too many outstanding realtime voice tickets", 429);
185
+ return {
186
+ request,
187
+ config,
188
+ omni,
189
+ conversationSessionId,
190
+ stt,
191
+ tts,
192
+ inputMode
193
+ };
194
+ }
195
+ async preflight(request) {
196
+ await this.prepareSession(request);
197
+ }
198
+ async createSession(request, principalId, now = Date.now()) {
199
+ const { request: resolvedRequest, config, omni, conversationSessionId, stt, tts, inputMode } = await this.prepareSession(request, now);
200
+ request = resolvedRequest;
201
+ const sessionId = crypto.randomUUID();
202
+ const ticket = crypto.randomBytes(32).toString("base64url");
203
+ const maxSessionMs = request.purpose === "conversation" ? Math.min(config.voice.realtime.maxConversationMs, omni?.route.managed ? 30 * 6e4 : Number.MAX_SAFE_INTEGER) : config.voice.realtime.maxDictationMs;
204
+ const claim = {
205
+ conversationSessionId,
206
+ sessionId,
207
+ principalId,
208
+ request,
209
+ inputMode,
210
+ idleTimeoutMs: config.voice.realtime.idleTimeoutMs,
211
+ maxSessionMs,
212
+ silenceDurationMs: config.voice.realtime.silenceDurationMs,
213
+ stt,
214
+ omni,
215
+ ...tts ? { tts } : {},
216
+ config,
217
+ createdAt: now,
218
+ expiresAt: now + TICKET_TTL_MS
219
+ };
220
+ this.tickets.set(ticketKey(ticket), claim);
221
+ if (request.purpose === "conversation" && request.sessionKey) this.conversationReservations.set(request.sessionKey, sessionId);
222
+ return {
223
+ sessionId,
224
+ ticket,
225
+ ticketExpiresAt: new Date(claim.expiresAt).toISOString(),
226
+ websocketPath: VOICE_REALTIME_WS_PATH,
227
+ protocolVersion: 2,
228
+ purpose: request.purpose,
229
+ inputMode,
230
+ bargeIn: config.voice.realtime.bargeIn,
231
+ inputFormat: {
232
+ encoding: "pcm_s16le",
233
+ sampleRate: 16e3,
234
+ channels: 1
235
+ },
236
+ limits: {
237
+ maxBinaryFrameBytes: VOICE_REALTIME_MAX_BINARY_FRAME_BYTES,
238
+ maxSessionMs,
239
+ idleTimeoutMs: claim.idleTimeoutMs
240
+ },
241
+ route: omni ? {
242
+ engine: "omni",
243
+ omni: omni.route
244
+ } : tts ? {
245
+ engine: "agent",
246
+ stt: stt.route,
247
+ tts: tts.route
248
+ } : {
249
+ engine: "dictation",
250
+ stt: stt.route
251
+ }
252
+ };
253
+ }
254
+ handleUpgrade(req, socket, head) {
255
+ if (new URL(req.url ?? "/", "http://gateway.local").pathname !== "/api/voice/realtime/v2/ws") return false;
256
+ if (this.closed || this.wss.clients.size >= MAX_CONNECTIONS) {
257
+ socket.destroy();
258
+ return true;
259
+ }
260
+ const clientIp = req.socket.remoteAddress;
261
+ if (!this.preauthBudget.acquire(clientIp)) {
262
+ socket.destroy();
263
+ return true;
264
+ }
265
+ try {
266
+ this.wss.handleUpgrade(req, socket, head, (ws) => this.wss.emit("connection", ws, req));
267
+ } catch (error) {
268
+ this.preauthBudget.release(clientIp);
269
+ throw error;
270
+ }
271
+ return true;
272
+ }
273
+ hasConversation(sessionKey) {
274
+ this.pruneTickets(Date.now());
275
+ return this.conversationReservations.has(sessionKey);
276
+ }
277
+ close() {
278
+ if (this.closed) return;
279
+ this.closed = true;
280
+ for (const client of this.wss.clients) client.close(1001, "Gateway stopping");
281
+ this.tickets.clear();
282
+ this.wss.close();
283
+ this.socketsByPrincipal.clear();
284
+ this.conversationReservations.clear();
285
+ }
286
+ releaseConversationReservation(claim) {
287
+ const sessionKey = claim.request.sessionKey;
288
+ if (sessionKey && this.conversationReservations.get(sessionKey) === claim.sessionId) this.conversationReservations.delete(sessionKey);
289
+ }
290
+ consumeTicket(ticket, sessionId, now = Date.now()) {
291
+ const key = ticketKey(ticket);
292
+ const claim = this.tickets.get(key);
293
+ this.tickets.delete(key);
294
+ if (!claim) return void 0;
295
+ if (claim.expiresAt < now || claim.sessionId !== sessionId) {
296
+ this.releaseConversationReservation(claim);
297
+ return;
298
+ }
299
+ return claim;
300
+ }
301
+ pruneTickets(now) {
302
+ for (const [key, claim] of this.tickets) if (claim.expiresAt < now) {
303
+ this.tickets.delete(key);
304
+ this.releaseConversationReservation(claim);
305
+ }
306
+ }
307
+ handleConnection(socket, request) {
308
+ const clientIp = request.socket.remoteAddress;
309
+ let claim;
310
+ let engine;
311
+ let ready = false;
312
+ let seq = 0;
313
+ let audioSeq = 0;
314
+ let closed = false;
315
+ let lastActivityAt = Date.now();
316
+ const abortController = new AbortController();
317
+ const seenMessageIds = /* @__PURE__ */ new Set();
318
+ let context;
319
+ let unsubscribeMemory;
320
+ let unsubscribeSession;
321
+ const send = (type, payload) => {
322
+ if (!claim || socket.readyState !== WebSocketState.OPEN) return;
323
+ const event = {
324
+ protocolVersion: 2,
325
+ eventId: crypto.randomUUID(),
326
+ seq: ++seq,
327
+ type,
328
+ sentAt: Date.now(),
329
+ sessionId: claim.sessionId,
330
+ payload
331
+ };
332
+ socket.send(JSON.stringify(event));
333
+ };
334
+ const detachPrincipal = () => {
335
+ if (!claim) return;
336
+ const sockets = this.socketsByPrincipal.get(claim.principalId);
337
+ sockets?.delete(socket);
338
+ if (sockets?.size === 0) this.socketsByPrincipal.delete(claim.principalId);
339
+ };
340
+ const shutdown = async (reason, notify) => {
341
+ if (closed) return;
342
+ closed = true;
343
+ unsubscribeMemory?.();
344
+ unsubscribeSession?.();
345
+ clearTimeout(startTimer);
346
+ clearInterval(lifecycleTimer);
347
+ abortController.abort(reason);
348
+ try {
349
+ await engine?.close();
350
+ } catch (err) {
351
+ log.error({
352
+ err,
353
+ sessionId: claim?.sessionId
354
+ }, "Voice cleanup failed");
355
+ }
356
+ if (notify) send("session.closed", { reason });
357
+ detachPrincipal();
358
+ if (claim) this.releaseConversationReservation(claim);
359
+ this.preauthBudget.release(clientIp);
360
+ if (socket.readyState === WebSocketState.OPEN) socket.close(1e3, reason.slice(0, 120));
361
+ };
362
+ const startTimer = setTimeout(() => socket.close(4401, "Voice session start timeout"), VOICE_REALTIME_START_TIMEOUT_MS);
363
+ const lifecycleTimer = setInterval(() => {
364
+ if (!claim) return;
365
+ if (context?.memory && !context.memory.isCurrent()) {
366
+ invalidateContext();
367
+ return;
368
+ }
369
+ const now = Date.now();
370
+ if (now - lastActivityAt > claim.idleTimeoutMs) shutdown("idle_timeout", true);
371
+ else if (now - claim.createdAt > claim.maxSessionMs) shutdown("session_limit", true);
372
+ }, 1e3);
373
+ const invalidateContext = () => {
374
+ if (closed) return;
375
+ send("session.error", {
376
+ code: "CONTEXT_CHANGED",
377
+ message: "Conversation context changed. Reconnect to use the updated information.",
378
+ recoverable: false
379
+ });
380
+ shutdown("context_changed", true);
381
+ };
382
+ const authenticate = async (message) => {
383
+ const consumed = this.consumeTicket(message.payload.ticket, message.payload.sessionId);
384
+ if (!consumed) {
385
+ socket.close(4401, "Invalid voice session ticket");
386
+ return;
387
+ }
388
+ const principalSockets = this.socketsByPrincipal.get(consumed.principalId) ?? /* @__PURE__ */ new Set();
389
+ if (principalSockets.size >= MAX_CONNECTIONS_PER_PRINCIPAL) {
390
+ this.releaseConversationReservation(consumed);
391
+ socket.close(4429, "Voice session concurrency limit");
392
+ return;
393
+ }
394
+ claim = consumed;
395
+ principalSockets.add(socket);
396
+ this.socketsByPrincipal.set(consumed.principalId, principalSockets);
397
+ clearTimeout(startTimer);
398
+ let setupStage = "context";
399
+ try {
400
+ if (consumed.omni) {
401
+ unsubscribeSession = onUserContextChange((change) => {
402
+ if (change.kind === "session-reset" && change.id === consumed.request.sessionKey) invalidateContext();
403
+ });
404
+ context = await this.options.getConversationContext(consumed.request.sessionKey, consumed.conversationSessionId);
405
+ if (closed) return;
406
+ if (context.memory) {
407
+ unsubscribeMemory = context.memory.subscribe(invalidateContext);
408
+ if (!context.memory.isCurrent()) {
409
+ invalidateContext();
410
+ return;
411
+ }
412
+ }
413
+ consumed.omni = {
414
+ ...consumed.omni,
415
+ instructions: voiceConversationInstructions(consumed.omni.instructions, context)
416
+ };
417
+ }
418
+ setupStage = "engine";
419
+ const sendAudio = (responseId, bytes) => {
420
+ if (socket.bufferedAmount > 1024 * 1024) throw new Error("Voice client audio backpressure limit exceeded");
421
+ if (!closed && socket.readyState === WebSocketState.OPEN) socket.send(encodeVoiceAudioFrame({
422
+ responseId,
423
+ seq: ++audioSeq,
424
+ audio: bytes
425
+ }), { binary: true });
426
+ };
427
+ engine = consumed.omni ? createOmniVoiceEngine({
428
+ callId: consumed.sessionId,
429
+ route: consumed.omni,
430
+ silenceDurationMs: consumed.silenceDurationMs,
431
+ bargeIn: consumed.config.voice?.realtime?.bargeIn ?? true,
432
+ send,
433
+ sendAudio,
434
+ record: (entry) => this.options.recordOmniTranscript(consumed.request.sessionKey, consumed.sessionId, entry, consumed.conversationSessionId),
435
+ onClose: shutdown
436
+ }) : createAgentVoiceEngine({
437
+ claim: consumed,
438
+ runtime: this.options,
439
+ signal: abortController.signal,
440
+ send,
441
+ sendAudio,
442
+ onClose: shutdown
443
+ });
444
+ await engine.start();
445
+ if (closed) return;
446
+ ready = true;
447
+ log.info({
448
+ sessionId: consumed.sessionId,
449
+ purpose: consumed.request.purpose,
450
+ provider: consumed.omni?.route.provider ?? consumed.stt?.route.provider,
451
+ model: consumed.omni?.route.model ?? consumed.stt?.route.model,
452
+ latencyMs: Date.now() - consumed.createdAt
453
+ }, "Realtime voice session ready");
454
+ send("session.ready", {
455
+ purpose: consumed.request.purpose,
456
+ inputMode: consumed.inputMode,
457
+ inputFormat: {
458
+ encoding: "pcm_s16le",
459
+ sampleRate: 16e3,
460
+ channels: 1
461
+ },
462
+ route: consumed.omni ? {
463
+ engine: "omni",
464
+ omni: consumed.omni.route
465
+ } : consumed.tts ? {
466
+ engine: "agent",
467
+ stt: consumed.stt.route,
468
+ tts: consumed.tts.route
469
+ } : {
470
+ engine: "dictation",
471
+ stt: consumed.stt.route
472
+ },
473
+ heartbeatIntervalMs: VOICE_REALTIME_HEARTBEAT_INTERVAL_MS
474
+ });
475
+ } catch (error) {
476
+ if (closed) return;
477
+ log.warn({
478
+ err: error,
479
+ sessionId: consumed.sessionId,
480
+ provider: consumed.omni?.route.provider ?? consumed.stt?.route.provider
481
+ }, "Realtime voice setup failed");
482
+ send("session.error", {
483
+ code: "PROVIDER_UNAVAILABLE",
484
+ message: setupStage === "context" ? "Conversation context could not be restored. Check voice and agent instructions." : "Voice engine is unavailable",
485
+ recoverable: false
486
+ });
487
+ await shutdown("provider_unavailable", true);
488
+ }
489
+ };
490
+ const clientMetrics = /* @__PURE__ */ new Set();
491
+ socket.on("message", (data, isBinary) => {
492
+ lastActivityAt = Date.now();
493
+ if (isBinary) {
494
+ if (!claim) {
495
+ socket.close(4401, "Voice session is not ready");
496
+ return;
497
+ }
498
+ const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : Array.isArray(data) ? Buffer.concat(data) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
499
+ if (bytes.byteLength === 0 || bytes.byteLength % 2 !== 0 || bytes.byteLength > 65536) {
500
+ send("session.error", {
501
+ code: "INVALID_AUDIO",
502
+ message: "Invalid PCM audio frame",
503
+ recoverable: false
504
+ });
505
+ shutdown("invalid_audio", true);
506
+ return;
507
+ }
508
+ if (!ready || !engine) {
509
+ socket.close(4401, "Voice session is not ready");
510
+ return;
511
+ }
512
+ try {
513
+ engine.appendAudio(bytes);
514
+ } catch (error) {
515
+ log.warn({
516
+ err: error,
517
+ sessionId: claim.sessionId
518
+ }, "Realtime voice engine rejected audio");
519
+ send("session.error", {
520
+ code: "AUDIO_BACKPRESSURE",
521
+ message: "Audio stream cannot keep up",
522
+ recoverable: false
523
+ });
524
+ shutdown("audio_backpressure", true);
525
+ }
526
+ return;
527
+ }
528
+ let message;
529
+ try {
530
+ message = parseVoiceClientMessage(parseVoiceClientJsonFrame(rawText(data)));
531
+ if (Math.abs(Date.now() - message.sentAt) > MAX_CLOCK_SKEW_MS) throw new Error("Clock skew");
532
+ if (seenMessageIds.has(message.messageId)) return;
533
+ if (seenMessageIds.size >= 256) seenMessageIds.clear();
534
+ seenMessageIds.add(message.messageId);
535
+ } catch (error) {
536
+ log.debug({ err: error }, "Voice client sent an invalid control frame");
537
+ socket.close(4400, "Invalid voice protocol frame");
538
+ return;
539
+ }
540
+ if (!claim) {
541
+ if (message.type !== "session.start") {
542
+ socket.close(4401, "First voice message must be session.start");
543
+ return;
544
+ }
545
+ authenticate(message);
546
+ return;
547
+ }
548
+ if (message.type === "session.ping") send("session.pong", {});
549
+ else if (message.type === "session.stop") shutdown(message.payload.reason, true);
550
+ else if (message.type === "session.metric") {
551
+ const { responseId, metric, durationMs } = message.payload;
552
+ const key = `${responseId}:${metric}`;
553
+ if (!clientMetrics.has(key) && clientMetrics.size < 512) {
554
+ clientMetrics.add(key);
555
+ log.info({
556
+ sessionId: claim.sessionId,
557
+ responseId,
558
+ metric,
559
+ durationMs,
560
+ source: "client",
561
+ engine: claim.request.engine
562
+ }, "Voice client timing sample");
563
+ }
564
+ } else if (message.type === "response.cancel") {
565
+ if (!engine?.cancel(message.payload.responseId, "client_cancelled")) send("session.error", {
566
+ code: "NO_ACTIVE_RESPONSE",
567
+ message: "No matching response is active",
568
+ recoverable: true
569
+ });
570
+ } else if (message.type === "response.audio.played") try {
571
+ engine?.acknowledge(message.payload.responseId, message.payload.playedBytes);
572
+ } catch {
573
+ socket.close(4400, "Invalid playback acknowledgement");
574
+ }
575
+ else if (message.type === "input.mute") {
576
+ if (!ready || !engine || claim.request.purpose !== "conversation") return;
577
+ Promise.resolve().then(() => engine.setInputMuted(message.payload.muted)).catch((error) => {
578
+ log.warn({
579
+ err: error,
580
+ sessionId: claim?.sessionId
581
+ }, "Voice input reset failed");
582
+ send("session.error", {
583
+ code: "PROVIDER_ERROR",
584
+ message: "Could not resume microphone input",
585
+ recoverable: false
586
+ });
587
+ shutdown("provider_error", true);
588
+ });
589
+ } else if (message.type === "input.commit") {
590
+ if (!ready || !engine || claim.request.purpose !== "dictation") {
591
+ send("session.error", {
592
+ code: "INVALID_STATE",
593
+ message: "Input cannot be committed",
594
+ recoverable: true
595
+ });
596
+ return;
597
+ }
598
+ ready = false;
599
+ engine.commit().catch(async (error) => {
600
+ log.warn({
601
+ err: error,
602
+ sessionId: claim?.sessionId
603
+ }, "Voice input commit failed");
604
+ send("session.error", {
605
+ code: "PROVIDER_ERROR",
606
+ message: "Voice input commit failed",
607
+ recoverable: false
608
+ });
609
+ await shutdown("provider_error", true);
610
+ });
611
+ } else send("session.error", {
612
+ code: "INVALID_STATE",
613
+ message: "Voice session is already started",
614
+ recoverable: true
615
+ });
616
+ });
617
+ socket.once("close", () => void shutdown("disconnected", false));
618
+ socket.once("error", (err) => log.debug({ err }, "Voice client socket failed"));
619
+ }
620
+ };
621
+ //#endregion
622
+ export { VoiceRealtimeRuntime, VoiceSessionCreationError, resolveStreamingStt, resolveStreamingTts };
@@ -0,0 +1,10 @@
1
+ /** Streams spoken prose, keeping code and link targets out of synthesis. */
2
+ export declare class SpeakableSegmenter {
3
+ private readonly maxCharacters;
4
+ private pending;
5
+ private fence;
6
+ constructor(maxCharacters?: number);
7
+ push(delta: string): string[];
8
+ flush(): string[];
9
+ private toSpeech;
10
+ }