@xopcai/xopc 0.0.143 → 0.0.145

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 (259) 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/Combination-DolwnuKi.js +41 -0
  4. package/dist/gateway/static/root/assets/{activity-center-store-jITiX2vJ.js → activity-center-store-3l9ahUqb.js} +1 -1
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DiME9u_P.js → agent-avatar-dicebear-BEUxdhZx.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-1bpT0xRO.js +2 -0
  7. package/dist/gateway/static/root/assets/app-management-settings-panel-CRHSZJWi.js +1 -0
  8. package/dist/gateway/static/root/assets/{appearance-settings-DUfdNQqk.js → appearance-settings-BunADYJb.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-Cc4JgVUQ.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-B-Qk6cGr.js → archive-plugin-B6vTkH2A.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-Di6EEbQX.js → attachment-load-BjZXzaNv.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automation-suggestion-card-DGxvRxtO.js → automation-suggestion-card-ZDA-Stg1.js} +1 -1
  13. package/dist/gateway/static/root/assets/automations-page-mb0fdVpq.js +6 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-ZaOZDH2I.js → binary-plugins-DYZfLvPz.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-BTFhjWfg.js → block-editor-C3MUlYC9.js} +46 -46
  16. package/dist/gateway/static/root/assets/browser-settings-page-Bb0xhaMN.js +8 -0
  17. package/dist/gateway/static/root/assets/capability-presets-api-BV76yN9a.js +1 -0
  18. package/dist/gateway/static/root/assets/capability-presets-settings-panel-CUcGKZqC.js +2 -0
  19. package/dist/gateway/static/root/assets/channel-recipient-api-fTlTnWB4.js +1 -0
  20. package/dist/gateway/static/root/assets/channels-settings-BlSDMGdy.js +1 -0
  21. package/dist/gateway/static/root/assets/channels-status-swr-CJauF1CW.js +1 -0
  22. package/dist/gateway/static/root/assets/{code-editor--tMO38Tr.js → code-editor-D4_k-6QN.js} +1 -1
  23. package/dist/gateway/static/root/assets/connector-logo-BVFNT2zW.js +1 -0
  24. package/dist/gateway/static/root/assets/connectors-page-CMgAFGvb.js +2 -0
  25. package/dist/gateway/static/root/assets/copy-to-clipboard-HOJ79TWv.js +1 -0
  26. package/dist/gateway/static/root/assets/{desktop-pet---yzVxVR.js → desktop-pet-_jUmoXd3.js} +1 -1
  27. package/dist/gateway/static/root/assets/{desktop-pet-settings-Dc3gqBJE.js → desktop-pet-settings-DwGOy4AQ.js} +1 -1
  28. package/dist/gateway/static/root/assets/{directory-picker-path-field-DCmOGLtK.js → directory-picker-path-field-akvF0F-u.js} +1 -1
  29. package/dist/gateway/static/root/assets/dist-C99RGfAH.js +1 -0
  30. package/dist/gateway/static/root/assets/dreaming-settings-BLetQiYY.js +3 -0
  31. package/dist/gateway/static/root/assets/{extension-debug-page-ByaKTTq4.js → extension-debug-page-Cof2ncxE.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-page-BmRzHAZr.js → extension-page-CwWktRNz.js} +1 -1
  33. package/dist/gateway/static/root/assets/extension-settings-page-CuggWOIl.js +1 -0
  34. package/dist/gateway/static/root/assets/gateway-config-api-B7sxIbza.js +1 -0
  35. package/dist/gateway/static/root/assets/gateway-settings-DIv0eKMf.js +1 -0
  36. package/dist/gateway/static/root/assets/gateway-startup-retry-DtoLihQE.js +1 -0
  37. package/dist/gateway/static/root/assets/goal-create-dialog-C4Kw0mfS.js +1 -0
  38. package/dist/gateway/static/root/assets/goal-detail-page-BMmzYpZL.js +4 -0
  39. package/dist/gateway/static/root/assets/goals-config-api-B0jm5JaA.js +1 -0
  40. package/dist/gateway/static/root/assets/goals-page-CGeT77PC.js +2 -0
  41. package/dist/gateway/static/root/assets/goals-settings-N5_iL7es.js +1 -0
  42. package/dist/gateway/static/root/assets/heartbeat-settings-XzH3lbRR.js +1 -0
  43. package/dist/gateway/static/root/assets/{html-workspace-editor-COqmtESs.js → html-workspace-editor-C3DKAkMc.js} +1 -1
  44. package/dist/gateway/static/root/assets/index-CSLF84-Q.css +1 -0
  45. package/dist/gateway/static/root/assets/index-I-ZCenVw.js +259 -0
  46. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-x_v0nrIi.js +1 -0
  47. package/dist/gateway/static/root/assets/locale-store-CiM-D9jA.js +1 -0
  48. package/dist/gateway/static/root/assets/logs-page-BSh1t8cD.js +7 -0
  49. package/dist/gateway/static/root/assets/markdown-editor-BvUebh3C.js +2 -0
  50. package/dist/gateway/static/root/assets/{markdown-split-Dw-yE2-y.js → markdown-split-5rJx5flj.js} +1 -1
  51. package/dist/gateway/static/root/assets/{media-plugins-D9NNNUgE.js → media-plugins-DYXccwAd.js} +1 -1
  52. package/dist/gateway/static/root/assets/messages-Cb1VMeqP.js +5 -0
  53. package/dist/gateway/static/root/assets/models-hub-panel-BNpTenAI.js +2 -0
  54. package/dist/gateway/static/root/assets/{notes-page-CRgbJGgk.js → notes-page-BgLyndCO.js} +1 -1
  55. package/dist/gateway/static/root/assets/notes-workbench-teklkhcE.js +4 -0
  56. package/dist/gateway/static/root/assets/{page-tabs-D4hkWVt_.js → page-tabs-6Al9_sGz.js} +1 -1
  57. package/dist/gateway/static/root/assets/{preview-open-alternatives-CIckv0uy.js → preview-open-alternatives-Cm3G72Hv.js} +1 -1
  58. package/dist/gateway/static/root/assets/project-detail-page-CCB_dKoh.js +1 -0
  59. package/dist/gateway/static/root/assets/projects-page-VOAFAHYj.js +1 -0
  60. package/dist/gateway/static/root/assets/remote-access-hub-USYu64Ru.js +1 -0
  61. package/dist/gateway/static/root/assets/{save-bar-controls-B0b42kB2.js → save-bar-controls-BYtfzu_F.js} +1 -1
  62. package/dist/gateway/static/root/assets/{schedule-field-BtjEkczK.js → schedule-field-CvIRc6yK.js} +1 -1
  63. package/dist/gateway/static/root/assets/{schema-form-Bf0bk3i6.js → schema-form-CQuKx0n7.js} +1 -1
  64. package/dist/gateway/static/root/assets/sessions-page-oGC8O5h_.js +1 -0
  65. package/dist/gateway/static/root/assets/settings-advanced-gate-D4rP66R1.js +1 -0
  66. package/dist/gateway/static/root/assets/{settings-collapsible-section-BTKTdTNX.js → settings-collapsible-section-CF3HHM-t.js} +1 -1
  67. package/dist/gateway/static/root/assets/{settings-form-section-DL4P0sAj.js → settings-form-section-CwWjoRN6.js} +1 -1
  68. package/dist/gateway/static/root/assets/{settings-loading-skeleton-BV6KFNj4.js → settings-loading-skeleton-aUFmJa13.js} +1 -1
  69. package/dist/gateway/static/root/assets/{settings-page-B_4DsX5W.js → settings-page-B2UUU9TF.js} +1 -1
  70. package/dist/gateway/static/root/assets/{settings-page-layout-CyGlrInB.js → settings-page-layout-DqpbTWqI.js} +1 -1
  71. package/dist/gateway/static/root/assets/setup-status-panel-Ck9keROI.js +1 -0
  72. package/dist/gateway/static/root/assets/share-preview-page-BOhlPsS1.js +2 -0
  73. package/dist/gateway/static/root/assets/shares-settings-BYLsCuqj.js +1 -0
  74. package/dist/gateway/static/root/assets/skill-reload-api-BNHUyVML.js +1 -0
  75. package/dist/gateway/static/root/assets/skills-page-CpXbQH4n.js +2 -0
  76. package/dist/gateway/static/root/assets/{system-settings-panel-D85fBBuI.js → system-settings-panel-CWxSvuwD.js} +1 -1
  77. package/dist/gateway/static/root/assets/{theme-store-Bt6vAkSB.js → theme-store-C9Dxcppm.js} +1 -1
  78. package/dist/gateway/static/root/assets/{typed-models-lib-CwEuYrHX.js → typed-models-lib-qROAvqTw.js} +1 -1
  79. package/dist/gateway/static/root/assets/use-channel-catalog-DE9cpn4G.js +1 -0
  80. package/dist/gateway/static/root/assets/user-context-page-DbNEK4Yp.js +1 -0
  81. package/dist/gateway/static/root/assets/user-profile-fields-editor-CWxc0ghP.js +1 -0
  82. package/dist/gateway/static/root/assets/{user-profile-settings-panel-CWM0fEpb.js → user-profile-settings-panel-DW-Up6vt.js} +1 -1
  83. package/dist/gateway/static/root/assets/{utils--BO6xT3u.js → utils-D53uRXVr.js} +1 -1
  84. package/dist/gateway/static/root/assets/{vendor-codemirror-C1WKjCk5.js → vendor-codemirror--gTq3YAI.js} +17 -17
  85. package/dist/gateway/static/root/assets/voice-api-key-field-Cs7MOrg0.js +1 -0
  86. package/dist/gateway/static/root/assets/work-item-detail-page-D1ziG1Pp.js +1 -0
  87. package/dist/gateway/static/root/assets/workflow-run-setup-panel-D_x9ELJp.js +3 -0
  88. package/dist/gateway/static/root/assets/workflows-page-DHGV_n6i.js +11 -0
  89. package/dist/gateway/static/root/index.html +10 -10
  90. package/dist/package.js +1 -1
  91. package/dist/src/agent/embedded/session-runner.js +3 -3
  92. package/dist/src/agent/embedded/session-runner.js.map +1 -1
  93. package/dist/src/agent/embedded/xopc-auth-storage.d.ts +3 -1
  94. package/dist/src/agent/embedded/xopc-auth-storage.js +11 -1
  95. package/dist/src/agent/embedded/xopc-auth-storage.js.map +1 -1
  96. package/dist/src/agent/memory/dreaming/config.js +1 -1
  97. package/dist/src/agent-runtime/index.js +1 -1
  98. package/dist/src/agent-runtime/runtime-profile.js +1 -1
  99. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  100. package/dist/src/config/agent-profile.js +1 -1
  101. package/dist/src/config/index.js +2 -2
  102. package/dist/src/config/schema.d.ts +60 -5
  103. package/dist/src/config/schema.js +30 -9
  104. package/dist/src/config/schema.js.map +1 -1
  105. package/dist/src/connectors/composio-catalog.js +11 -2
  106. package/dist/src/connectors/composio-catalog.js.map +1 -1
  107. package/dist/src/connectors/connector-memory-sync.js +135 -26
  108. package/dist/src/connectors/connector-memory-sync.js.map +1 -1
  109. package/dist/src/gateway/agents-admin.js +1 -1
  110. package/dist/src/gateway/heartbeat/service.js +1 -1
  111. package/dist/src/gateway/hono/app.js +1 -1
  112. package/dist/src/gateway/hono/app.js.map +1 -1
  113. package/dist/src/gateway/hono/lib/config-payload.d.ts +11 -0
  114. package/dist/src/gateway/hono/lib/config-payload.js +1 -0
  115. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  116. package/dist/src/gateway/hono/routes/config-patch/misc.js +6 -1
  117. package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
  118. package/dist/src/gateway/hono/routes/memory.js +11 -1
  119. package/dist/src/gateway/hono/routes/memory.js.map +1 -1
  120. package/dist/src/gateway/hono/routes/status.js +3 -0
  121. package/dist/src/gateway/hono/routes/status.js.map +1 -1
  122. package/dist/src/gateway/hono/routes/voice.d.ts +5 -5
  123. package/dist/src/gateway/hono/routes/voice.js +288 -62
  124. package/dist/src/gateway/hono/routes/voice.js.map +1 -1
  125. package/dist/src/gateway/hono/routes/you.js +5 -1
  126. package/dist/src/gateway/hono/routes/you.js.map +1 -1
  127. package/dist/src/gateway/service.d.ts +8 -0
  128. package/dist/src/gateway/service.js +41 -11
  129. package/dist/src/gateway/service.js.map +1 -1
  130. package/dist/src/heartbeat/index.js +1 -1
  131. package/dist/src/knowledge/connected-knowledge-pipeline.d.ts +21 -0
  132. package/dist/src/knowledge/connected-knowledge-pipeline.js +267 -0
  133. package/dist/src/knowledge/connected-knowledge-pipeline.js.map +1 -0
  134. package/dist/src/knowledge/coordinator.d.ts +10 -0
  135. package/dist/src/knowledge/coordinator.js +52 -0
  136. package/dist/src/knowledge/coordinator.js.map +1 -0
  137. package/dist/src/knowledge/index.d.ts +2 -0
  138. package/dist/src/knowledge/index.js +3 -1
  139. package/dist/src/knowledge/ingestion-service.d.ts +7 -1
  140. package/dist/src/knowledge/ingestion-service.js +28 -0
  141. package/dist/src/knowledge/ingestion-service.js.map +1 -1
  142. package/dist/src/knowledge/types.d.ts +18 -0
  143. package/dist/src/media-understanding/audio-transcription-runner.js +5 -3
  144. package/dist/src/media-understanding/audio-transcription-runner.js.map +1 -1
  145. package/dist/src/media-understanding/runner.js +8 -3
  146. package/dist/src/media-understanding/runner.js.map +1 -1
  147. package/dist/src/media-understanding/types.d.ts +7 -0
  148. package/dist/src/storage/sqlite/index.d.ts +1 -1
  149. package/dist/src/storage/sqlite/index.js +2 -2
  150. package/dist/src/storage/sqlite/knowledge-repository.d.ts +26 -1
  151. package/dist/src/storage/sqlite/knowledge-repository.js +120 -6
  152. package/dist/src/storage/sqlite/knowledge-repository.js.map +1 -1
  153. package/dist/src/storage/sqlite/migrations/037_connected_knowledge_pipeline.sql +47 -0
  154. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  155. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  156. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  157. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  158. package/dist/src/storage/sqlite/schema.js +1 -1
  159. package/dist/src/user-context/projection.d.ts +13 -4
  160. package/dist/src/user-context/projection.js +19 -7
  161. package/dist/src/user-context/projection.js.map +1 -1
  162. package/dist/src/voice/language-profile.d.ts +20 -0
  163. package/dist/src/voice/language-profile.js +156 -0
  164. package/dist/src/voice/language-profile.js.map +1 -0
  165. package/dist/src/voice/local/model-files.d.ts +14 -0
  166. package/dist/src/voice/local/model-files.js +94 -0
  167. package/dist/src/voice/local/model-files.js.map +1 -0
  168. package/dist/src/voice/local/model-manager.d.ts +18 -0
  169. package/dist/src/voice/local/model-manager.js +135 -0
  170. package/dist/src/voice/local/model-manager.js.map +1 -0
  171. package/dist/src/voice/local/models.d.ts +28 -0
  172. package/dist/src/voice/local/models.js +101 -0
  173. package/dist/src/voice/local/models.js.map +1 -0
  174. package/dist/src/voice/local/runtime-client.d.ts +26 -0
  175. package/dist/src/voice/local/runtime-client.js +166 -0
  176. package/dist/src/voice/local/runtime-client.js.map +1 -0
  177. package/dist/src/voice/local/runtime-dispatcher.d.ts +9 -0
  178. package/dist/src/voice/local/runtime-dispatcher.js +22 -0
  179. package/dist/src/voice/local/runtime-dispatcher.js.map +1 -0
  180. package/dist/src/voice/local/runtime-worker.d.ts +1 -0
  181. package/dist/src/voice/local/runtime-worker.js +249 -0
  182. package/dist/src/voice/local/runtime-worker.js.map +1 -0
  183. package/dist/src/voice/local/wav.d.ts +6 -0
  184. package/dist/src/voice/local/wav.js +69 -0
  185. package/dist/src/voice/local/wav.js.map +1 -0
  186. package/dist/src/voice/metadata/builtin.js +46 -0
  187. package/dist/src/voice/metadata/builtin.js.map +1 -1
  188. package/dist/src/voice/stt/factory.js +8 -2
  189. package/dist/src/voice/stt/factory.js.map +1 -1
  190. package/dist/src/voice/stt/providers/alibaba-transcription.js +14 -8
  191. package/dist/src/voice/stt/providers/alibaba-transcription.js.map +1 -1
  192. package/dist/src/voice/stt/providers/index.d.ts +1 -0
  193. package/dist/src/voice/stt/providers/index.js +2 -1
  194. package/dist/src/voice/stt/providers/local-transcription.d.ts +2 -0
  195. package/dist/src/voice/stt/providers/local-transcription.js +51 -0
  196. package/dist/src/voice/stt/providers/local-transcription.js.map +1 -0
  197. package/dist/src/voice/stt/providers/openai-transcription.js +5 -3
  198. package/dist/src/voice/stt/providers/openai-transcription.js.map +1 -1
  199. package/dist/src/voice/stt/transcribe-core.js +5 -4
  200. package/dist/src/voice/stt/transcribe-core.js.map +1 -1
  201. package/dist/src/voice/stt/types.d.ts +4 -0
  202. package/dist/src/voice/stt/types.js +5 -4
  203. package/dist/src/voice/stt/types.js.map +1 -1
  204. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  205. package/dist/src/voice/tts/providers/edge-speech.js.map +1 -1
  206. package/dist/src/voice/tts/types.js +3 -3
  207. package/dist/src/voice/tts/types.js.map +1 -1
  208. package/package.json +4 -2
  209. package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +0 -41
  210. package/dist/gateway/static/root/assets/agents-DzBXhZXO.js +0 -2
  211. package/dist/gateway/static/root/assets/app-management-settings-panel-Cuo7SbUP.js +0 -1
  212. package/dist/gateway/static/root/assets/apps-page-CKuKK5AU.js +0 -1
  213. package/dist/gateway/static/root/assets/automations-page-D-FF_WKV.js +0 -4
  214. package/dist/gateway/static/root/assets/browser-settings-page-BvKLdEQt.js +0 -8
  215. package/dist/gateway/static/root/assets/capability-presets-api-BU-pVZiZ.js +0 -1
  216. package/dist/gateway/static/root/assets/capability-presets-settings-panel-DgyeEwzh.js +0 -2
  217. package/dist/gateway/static/root/assets/channel-recipient-api-DuulnhIj.js +0 -1
  218. package/dist/gateway/static/root/assets/channels-settings-BbVsdD_I.js +0 -1
  219. package/dist/gateway/static/root/assets/channels-status-swr-B5lVrUsN.js +0 -1
  220. package/dist/gateway/static/root/assets/connectors-page-B8QqVy3I.js +0 -2
  221. package/dist/gateway/static/root/assets/dist-Mys7eFJ5.js +0 -1
  222. package/dist/gateway/static/root/assets/dreaming-settings-BgT5KZVe.js +0 -3
  223. package/dist/gateway/static/root/assets/extension-settings-page-JUtwIhbF.js +0 -1
  224. package/dist/gateway/static/root/assets/fetch-8QNN25Sh.js +0 -1
  225. package/dist/gateway/static/root/assets/gateway-config-api-CxicF50_.js +0 -1
  226. package/dist/gateway/static/root/assets/gateway-settings-Bz2I_EO5.js +0 -1
  227. package/dist/gateway/static/root/assets/gateway-startup-retry-5YBnS9Fj.js +0 -1
  228. package/dist/gateway/static/root/assets/goal-create-dialog-VcoyuZGN.js +0 -1
  229. package/dist/gateway/static/root/assets/goal-detail-page-Cilkap2G.js +0 -4
  230. package/dist/gateway/static/root/assets/goals-config-api-CwcHRdC0.js +0 -1
  231. package/dist/gateway/static/root/assets/goals-page-C6eoni4z.js +0 -2
  232. package/dist/gateway/static/root/assets/goals-settings-o4i-qAOS.js +0 -1
  233. package/dist/gateway/static/root/assets/heartbeat-settings-DQQkETX8.js +0 -1
  234. package/dist/gateway/static/root/assets/index-BKRqeRHm.js +0 -257
  235. package/dist/gateway/static/root/assets/index-CphxClbs.css +0 -1
  236. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-LT6qB-ua.js +0 -1
  237. package/dist/gateway/static/root/assets/locale-store-Ba72bFmP.js +0 -1
  238. package/dist/gateway/static/root/assets/logs-page-Dli2e3fS.js +0 -7
  239. package/dist/gateway/static/root/assets/markdown-editor-jD1LPiP-.js +0 -2
  240. package/dist/gateway/static/root/assets/messages-CzKxM6I4.js +0 -5
  241. package/dist/gateway/static/root/assets/models-hub-panel-DVn2Q1Ik.js +0 -2
  242. package/dist/gateway/static/root/assets/notes-workbench-BxZRHLWx.js +0 -2
  243. package/dist/gateway/static/root/assets/project-detail-page-Doz53xLn.js +0 -1
  244. package/dist/gateway/static/root/assets/projects-page-DoMtcRLg.js +0 -1
  245. package/dist/gateway/static/root/assets/remote-access-hub-BeHVEgrm.js +0 -1
  246. package/dist/gateway/static/root/assets/sessions-page-Bk4bQsBe.js +0 -1
  247. package/dist/gateway/static/root/assets/settings-advanced-gate-DdFyaRMm.js +0 -1
  248. package/dist/gateway/static/root/assets/setup-status-panel-DJ1_MLgX.js +0 -1
  249. package/dist/gateway/static/root/assets/share-preview-page-BQBV7YrV.js +0 -2
  250. package/dist/gateway/static/root/assets/shares-settings-0VuFLBeU.js +0 -1
  251. package/dist/gateway/static/root/assets/skill-reload-api-DO87Ckeo.js +0 -1
  252. package/dist/gateway/static/root/assets/skills-page-D3VdtxAn.js +0 -2
  253. package/dist/gateway/static/root/assets/use-channel-catalog-COXIfXcl.js +0 -1
  254. package/dist/gateway/static/root/assets/user-context-page-m9JVQ73F.js +0 -1
  255. package/dist/gateway/static/root/assets/user-profile-fields-editor-DtQiuY7E.js +0 -1
  256. package/dist/gateway/static/root/assets/voice-api-key-field-LoT3wgZ9.js +0 -1
  257. package/dist/gateway/static/root/assets/work-item-detail-page-DSTMz_mO.js +0 -1
  258. package/dist/gateway/static/root/assets/workflow-run-setup-panel-B35lJzvm.js +0 -3
  259. package/dist/gateway/static/root/assets/workflows-page-BfSm9oR8.js +0 -11
@@ -19,7 +19,7 @@ const log = createLogger('Sqlite:Migrations');
19
19
  export const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
20
20
 
21
21
  /** Latest schema version this release supports (increment when adding migrations). */
22
- export const XOPC_DB_SCHEMA_VERSION = 36;
22
+ export const XOPC_DB_SCHEMA_VERSION = 37;
23
23
 
24
24
  const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
25
25
 
@@ -31,7 +31,7 @@ function ensureXopcDatabaseSchema(db) {
31
31
  bootstrapFreshDatabase(db);
32
32
  currentVersion = 11;
33
33
  }
34
- applyPendingMigrations(db, { targetVersion: 36 });
34
+ applyPendingMigrations(db, { targetVersion: 37 });
35
35
  }
36
36
  //#endregion
37
37
  export { SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_SCHEMA_VERSION, ensureSchemaMetaTable, ensureXopcDatabaseSchema, readSchemaVersion, readSchemaVersionForTest, setSchemaVersion };
@@ -1,5 +1,6 @@
1
1
  import type { MemoryKind, MemoryRecord } from '../agent/memory/types.js';
2
2
  import type { ConnectorDefinition, ConnectorInstance } from '../connectors/types.js';
3
+ import type { KnowledgeSourceItem, KnowledgeSyncRun } from '../knowledge/types.js';
3
4
  export type UserContextFacet = 'basics' | 'collaboration' | 'boundaries' | 'priorities' | 'people' | 'current';
4
5
  export type UserContextOrigin = 'told_by_you' | 'observed' | 'inferred' | 'connected_source';
5
6
  export declare function isUserContextRecord(record: MemoryRecord): boolean;
@@ -29,10 +30,10 @@ export declare function projectUserContextRecord(record: MemoryRecord): {
29
30
  };
30
31
  export declare function isPersonalContextConnector(definition: ConnectorDefinition): boolean;
31
32
  export declare function recordsDerivedFromPersonalContextSource(records: MemoryRecord[], agentId: string, connectorId: string): MemoryRecord[];
32
- export declare function projectPersonalContextSources(definitions: ConnectorDefinition[], instances: ConnectorInstance[], records?: MemoryRecord[]): {
33
- id: string;
34
- displayName: string;
35
- description: string;
33
+ export declare function projectPersonalContextSources(definitions: ConnectorDefinition[], instances: ConnectorInstance[], records?: MemoryRecord[], knowledge?: {
34
+ sourceItems?: KnowledgeSourceItem[];
35
+ syncRuns?: KnowledgeSyncRun[];
36
+ }): {
36
37
  category: import("../connectors/types.js").ConnectorCategory;
37
38
  capabilities: ("context" | "tools" | "events" | "memory_source")[];
38
39
  access: {
@@ -51,4 +52,12 @@ export declare function projectPersonalContextSources(definitions: ConnectorDefi
51
52
  lastHealthStatus: import("../connectors/types.js").ConnectorHealthStatus;
52
53
  lastActivityAt: string;
53
54
  derivedUnderstandingCount: number;
55
+ knowledgeItemCount: number;
56
+ lastSyncAt: string;
57
+ lastSyncStatus: import("../knowledge/types.js").KnowledgeSyncRunStatus;
58
+ lastSyncError: string;
59
+ branding?: import("../connectors/types.js").ConnectorBranding;
60
+ id: string;
61
+ displayName: string;
62
+ description: string;
54
63
  }[];
@@ -70,7 +70,7 @@ function isPersonalContextConnector(definition) {
70
70
  function recordsDerivedFromPersonalContextSource(records, agentId, connectorId) {
71
71
  return records.filter((record) => record.scope.agentId === agentId && record.source.provider === connectorId && isUserContextRecord(record));
72
72
  }
73
- function projectPersonalContextSources(definitions, instances, records = []) {
73
+ function projectPersonalContextSources(definitions, instances, records = [], knowledge = {}) {
74
74
  const instanceByConnector = /* @__PURE__ */ new Map();
75
75
  for (const instance of instances) {
76
76
  const current = instanceByConnector.get(instance.connectorId) ?? [];
@@ -80,6 +80,9 @@ function projectPersonalContextSources(definitions, instances, records = []) {
80
80
  return definitions.filter(isPersonalContextConnector).map((definition) => {
81
81
  const connected = instanceByConnector.get(definition.id) ?? [];
82
82
  const relatedRecords = records.filter((record) => record.source.provider === definition.id);
83
+ const sourceItems = (knowledge.sourceItems ?? []).filter((item) => item.metadata.connectorId === definition.id);
84
+ const sourceInstanceIds = new Set(sourceItems.map((item) => item.sourceInstanceId));
85
+ const latestSync = (knowledge.syncRuns ?? []).filter((run) => sourceInstanceIds.has(run.sourceInstanceId) || run.sourceInstanceId.startsWith(`composio:${definition.id}:`) || run.sourceInstanceId.startsWith(`${definition.id}:`)).sort((left, right) => Date.parse(right.startedAt) - Date.parse(left.startedAt))[0];
83
86
  const latestHealth = connected.filter((instance) => instance.usage.lastHealthCheckAt && instance.usage.lastHealthStatus).sort((left, right) => Date.parse(right.usage.lastHealthCheckAt) - Date.parse(left.usage.lastHealthCheckAt))[0]?.usage;
84
87
  const permissionDetails = definition.permissions?.data ?? [];
85
88
  const canWrite = permissionDetails.some((permission) => /(?:^|[.:/_-])(write|create|update|delete|manage|send|admin)(?:$|[.:/_-])/i.test(permission));
@@ -87,6 +90,7 @@ function projectPersonalContextSources(definitions, instances, records = []) {
87
90
  id: definition.id,
88
91
  displayName: definition.displayName,
89
92
  description: definition.description,
93
+ ...definition.branding ? { branding: definition.branding } : {},
90
94
  category: definition.category,
91
95
  capabilities: definition.capabilities.filter((capability) => capability === "context" || capability === "memory_source" || capability === "tools" || capability === "events"),
92
96
  access: {
@@ -103,12 +107,20 @@ function projectPersonalContextSources(definitions, instances, records = []) {
103
107
  lastConnectedAt: connected.map((instance) => instance.lastConnectedAt).filter((value) => Boolean(value)).sort().at(-1),
104
108
  lastHealthCheckAt: latestHealth?.lastHealthCheckAt,
105
109
  lastHealthStatus: latestHealth?.lastHealthStatus,
106
- lastActivityAt: connected.flatMap((instance) => [
107
- instance.lastConnectedAt,
108
- instance.usage.lastHealthCheckAt,
109
- ...instance.audit.map((entry) => entry.at)
110
- ]).filter((value) => Boolean(value)).sort().at(-1),
111
- derivedUnderstandingCount: relatedRecords.length
110
+ lastActivityAt: [
111
+ ...connected.flatMap((instance) => [
112
+ instance.lastConnectedAt,
113
+ instance.usage.lastHealthCheckAt,
114
+ ...instance.audit.map((entry) => entry.at)
115
+ ]),
116
+ latestSync?.finishedAt,
117
+ latestSync?.startedAt
118
+ ].filter((value) => Boolean(value)).sort().at(-1),
119
+ derivedUnderstandingCount: relatedRecords.length,
120
+ knowledgeItemCount: sourceItems.filter((item) => !item.deletedAt).length,
121
+ lastSyncAt: latestSync?.finishedAt ?? latestSync?.startedAt,
122
+ lastSyncStatus: latestSync?.status,
123
+ lastSyncError: latestSync?.error
112
124
  };
113
125
  }).sort((left, right) => {
114
126
  if (left.installed !== right.installed) return left.installed ? -1 : 1;
@@ -1 +1 @@
1
- {"version":3,"file":"projection.js","names":[],"sources":["../../../src/user-context/projection.ts"],"sourcesContent":["import type { MemoryKind, MemoryRecord } from '../agent/memory/types.js';\nimport { effectiveMemoryStatus, resolveMemoryStability } from '../agent/memory/lifecycle.js';\nimport { classifyMemoryContextOrigin } from '../agent/memory/source-origin.js';\nimport type { ConnectorDefinition, ConnectorInstance } from '../connectors/types.js';\n\nexport type UserContextFacet =\n | 'basics'\n | 'collaboration'\n | 'boundaries'\n | 'priorities'\n | 'people'\n | 'current';\n\nexport type UserContextOrigin = 'told_by_you' | 'observed' | 'inferred' | 'connected_source';\n\nconst ALWAYS_PERSONAL_KINDS = new Set<MemoryKind>([\n 'user_profile',\n 'preference',\n 'boundary',\n 'relationship',\n 'commitment',\n 'routine',\n 'personal_logistics',\n 'current_state',\n 'tool_preference',\n 'long_term_goal',\n]);\n\nconst CONTEXTUAL_USER_KINDS = new Set<MemoryKind>([\n 'project_context',\n 'open_question',\n 'milestone',\n 'derived_insight',\n 'task_lesson',\n]);\n\nexport function isUserContextRecord(record: MemoryRecord): boolean {\n if (ALWAYS_PERSONAL_KINDS.has(record.kind)) return true;\n return CONTEXTUAL_USER_KINDS.has(record.kind) && record.tags?.includes('user-understanding') === true;\n}\n\nexport function isUserContextMemoryKind(value: unknown): value is MemoryKind {\n return typeof value === 'string'\n && (ALWAYS_PERSONAL_KINDS.has(value as MemoryKind) || CONTEXTUAL_USER_KINDS.has(value as MemoryKind));\n}\n\nexport function facetForMemoryKind(kind: MemoryKind): UserContextFacet {\n if (kind === 'user_profile' || kind === 'personal_logistics') return 'basics';\n if (kind === 'preference' || kind === 'routine' || kind === 'tool_preference' || kind === 'task_lesson') {\n return 'collaboration';\n }\n if (kind === 'boundary') return 'boundaries';\n if (kind === 'relationship') return 'people';\n if (kind === 'current_state' || kind === 'open_question') return 'current';\n return 'priorities';\n}\n\nexport function originForMemoryRecord(record: MemoryRecord): UserContextOrigin {\n const origin = classifyMemoryContextOrigin(record);\n return origin === 'told_by_user' ? 'told_by_you' : origin;\n}\n\nexport function projectUserContextRecord(record: MemoryRecord) {\n const lifecycle = resolveMemoryStability(record);\n const latestEvidenceAt = record.evidence\n ?.map((evidence) => evidence.observedAt)\n .filter((value): value is string => Boolean(value))\n .sort()\n .at(-1);\n return {\n id: record.id,\n statement: record.content,\n facet: facetForMemoryKind(record.kind),\n kind: record.kind,\n status: effectiveMemoryStatus(record),\n origin: originForMemoryRecord(record),\n sourceName: record.source.provider ?? 'local',\n updatedAt: record.updatedAt,\n sensitivity: record.sensitivity ?? 'normal',\n explicitness: record.explicitness,\n durability: record.durability,\n canReference: record.disclosurePolicy !== 'silent',\n stability: lifecycle.band,\n stabilityScore: lifecycle.score,\n reviewAt: lifecycle.reviewAt,\n reviewDue: lifecycle.reviewDue,\n evidenceCount: record.evidence?.length ?? 0,\n sourcePath: record.source.path,\n latestEvidenceAt,\n };\n}\n\nexport function isPersonalContextConnector(definition: ConnectorDefinition): boolean {\n return definition.capabilities.includes('context') || definition.capabilities.includes('memory_source');\n}\n\nexport function recordsDerivedFromPersonalContextSource(\n records: MemoryRecord[],\n agentId: string,\n connectorId: string,\n): MemoryRecord[] {\n return records.filter((record) => (\n record.scope.agentId === agentId\n && record.source.provider === connectorId\n && isUserContextRecord(record)\n ));\n}\n\nexport function projectPersonalContextSources(\n definitions: ConnectorDefinition[],\n instances: ConnectorInstance[],\n records: MemoryRecord[] = [],\n) {\n const instanceByConnector = new Map<string, ConnectorInstance[]>();\n for (const instance of instances) {\n const current = instanceByConnector.get(instance.connectorId) ?? [];\n current.push(instance);\n instanceByConnector.set(instance.connectorId, current);\n }\n return definitions\n .filter(isPersonalContextConnector)\n .map((definition) => {\n const connected = instanceByConnector.get(definition.id) ?? [];\n const relatedRecords = records.filter((record) => record.source.provider === definition.id);\n const latestHealth = connected\n .filter((instance) => instance.usage.lastHealthCheckAt && instance.usage.lastHealthStatus)\n .sort((left, right) => (\n Date.parse(right.usage.lastHealthCheckAt!) - Date.parse(left.usage.lastHealthCheckAt!)\n ))[0]?.usage;\n const permissionDetails = definition.permissions?.data ?? [];\n const canWrite = permissionDetails.some((permission) => (\n /(?:^|[.:/_-])(write|create|update|delete|manage|send|admin)(?:$|[.:/_-])/i.test(permission)\n ));\n return {\n id: definition.id,\n displayName: definition.displayName,\n description: definition.description,\n category: definition.category,\n capabilities: definition.capabilities.filter((capability) => (\n capability === 'context' || capability === 'memory_source' || capability === 'tools' || capability === 'events'\n )),\n access: {\n context: definition.capabilities.includes('context'),\n memory: definition.capabilities.includes('memory_source'),\n read: definition.capabilities.includes('context')\n || definition.capabilities.includes('resources')\n || definition.capabilities.includes('tools'),\n write: canWrite,\n },\n permissionDetails,\n installed: connected.length > 0,\n enabled: connected.some((instance) => instance.enabled),\n status: connected[0]?.status ?? 'not_installed',\n instanceId: connected[0]?.instanceId,\n lastConnectedAt: connected\n .map((instance) => instance.lastConnectedAt)\n .filter((value): value is string => Boolean(value))\n .sort()\n .at(-1),\n lastHealthCheckAt: latestHealth?.lastHealthCheckAt,\n lastHealthStatus: latestHealth?.lastHealthStatus,\n lastActivityAt: connected\n .flatMap((instance) => [\n instance.lastConnectedAt,\n instance.usage.lastHealthCheckAt,\n ...instance.audit.map((entry) => entry.at),\n ])\n .filter((value): value is string => Boolean(value))\n .sort()\n .at(-1),\n derivedUnderstandingCount: relatedRecords.length,\n };\n })\n .sort((left, right) => {\n if (left.installed !== right.installed) return left.installed ? -1 : 1;\n return left.displayName.localeCompare(right.displayName);\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"projection.js","names":[],"sources":["../../../src/user-context/projection.ts"],"sourcesContent":["import type { MemoryKind, MemoryRecord } from '../agent/memory/types.js';\nimport { effectiveMemoryStatus, resolveMemoryStability } from '../agent/memory/lifecycle.js';\nimport { classifyMemoryContextOrigin } from '../agent/memory/source-origin.js';\nimport type { ConnectorDefinition, ConnectorInstance } from '../connectors/types.js';\nimport type { KnowledgeSourceItem, KnowledgeSyncRun } from '../knowledge/types.js';\n\nexport type UserContextFacet =\n | 'basics'\n | 'collaboration'\n | 'boundaries'\n | 'priorities'\n | 'people'\n | 'current';\n\nexport type UserContextOrigin = 'told_by_you' | 'observed' | 'inferred' | 'connected_source';\n\nconst ALWAYS_PERSONAL_KINDS = new Set<MemoryKind>([\n 'user_profile',\n 'preference',\n 'boundary',\n 'relationship',\n 'commitment',\n 'routine',\n 'personal_logistics',\n 'current_state',\n 'tool_preference',\n 'long_term_goal',\n]);\n\nconst CONTEXTUAL_USER_KINDS = new Set<MemoryKind>([\n 'project_context',\n 'open_question',\n 'milestone',\n 'derived_insight',\n 'task_lesson',\n]);\n\nexport function isUserContextRecord(record: MemoryRecord): boolean {\n if (ALWAYS_PERSONAL_KINDS.has(record.kind)) return true;\n return CONTEXTUAL_USER_KINDS.has(record.kind) && record.tags?.includes('user-understanding') === true;\n}\n\nexport function isUserContextMemoryKind(value: unknown): value is MemoryKind {\n return typeof value === 'string'\n && (ALWAYS_PERSONAL_KINDS.has(value as MemoryKind) || CONTEXTUAL_USER_KINDS.has(value as MemoryKind));\n}\n\nexport function facetForMemoryKind(kind: MemoryKind): UserContextFacet {\n if (kind === 'user_profile' || kind === 'personal_logistics') return 'basics';\n if (kind === 'preference' || kind === 'routine' || kind === 'tool_preference' || kind === 'task_lesson') {\n return 'collaboration';\n }\n if (kind === 'boundary') return 'boundaries';\n if (kind === 'relationship') return 'people';\n if (kind === 'current_state' || kind === 'open_question') return 'current';\n return 'priorities';\n}\n\nexport function originForMemoryRecord(record: MemoryRecord): UserContextOrigin {\n const origin = classifyMemoryContextOrigin(record);\n return origin === 'told_by_user' ? 'told_by_you' : origin;\n}\n\nexport function projectUserContextRecord(record: MemoryRecord) {\n const lifecycle = resolveMemoryStability(record);\n const latestEvidenceAt = record.evidence\n ?.map((evidence) => evidence.observedAt)\n .filter((value): value is string => Boolean(value))\n .sort()\n .at(-1);\n return {\n id: record.id,\n statement: record.content,\n facet: facetForMemoryKind(record.kind),\n kind: record.kind,\n status: effectiveMemoryStatus(record),\n origin: originForMemoryRecord(record),\n sourceName: record.source.provider ?? 'local',\n updatedAt: record.updatedAt,\n sensitivity: record.sensitivity ?? 'normal',\n explicitness: record.explicitness,\n durability: record.durability,\n canReference: record.disclosurePolicy !== 'silent',\n stability: lifecycle.band,\n stabilityScore: lifecycle.score,\n reviewAt: lifecycle.reviewAt,\n reviewDue: lifecycle.reviewDue,\n evidenceCount: record.evidence?.length ?? 0,\n sourcePath: record.source.path,\n latestEvidenceAt,\n };\n}\n\nexport function isPersonalContextConnector(definition: ConnectorDefinition): boolean {\n return definition.capabilities.includes('context') || definition.capabilities.includes('memory_source');\n}\n\nexport function recordsDerivedFromPersonalContextSource(\n records: MemoryRecord[],\n agentId: string,\n connectorId: string,\n): MemoryRecord[] {\n return records.filter((record) => (\n record.scope.agentId === agentId\n && record.source.provider === connectorId\n && isUserContextRecord(record)\n ));\n}\n\nexport function projectPersonalContextSources(\n definitions: ConnectorDefinition[],\n instances: ConnectorInstance[],\n records: MemoryRecord[] = [],\n knowledge: { sourceItems?: KnowledgeSourceItem[]; syncRuns?: KnowledgeSyncRun[] } = {},\n) {\n const instanceByConnector = new Map<string, ConnectorInstance[]>();\n for (const instance of instances) {\n const current = instanceByConnector.get(instance.connectorId) ?? [];\n current.push(instance);\n instanceByConnector.set(instance.connectorId, current);\n }\n return definitions\n .filter(isPersonalContextConnector)\n .map((definition) => {\n const connected = instanceByConnector.get(definition.id) ?? [];\n const relatedRecords = records.filter((record) => record.source.provider === definition.id);\n const sourceItems = (knowledge.sourceItems ?? []).filter((item) => item.metadata.connectorId === definition.id);\n const sourceInstanceIds = new Set(sourceItems.map((item) => item.sourceInstanceId));\n const latestSync = (knowledge.syncRuns ?? [])\n .filter((run) => (\n sourceInstanceIds.has(run.sourceInstanceId)\n || run.sourceInstanceId.startsWith(`composio:${definition.id}:`)\n || run.sourceInstanceId.startsWith(`${definition.id}:`)\n ))\n .sort((left, right) => Date.parse(right.startedAt) - Date.parse(left.startedAt))[0];\n const latestHealth = connected\n .filter((instance) => instance.usage.lastHealthCheckAt && instance.usage.lastHealthStatus)\n .sort((left, right) => (\n Date.parse(right.usage.lastHealthCheckAt!) - Date.parse(left.usage.lastHealthCheckAt!)\n ))[0]?.usage;\n const permissionDetails = definition.permissions?.data ?? [];\n const canWrite = permissionDetails.some((permission) => (\n /(?:^|[.:/_-])(write|create|update|delete|manage|send|admin)(?:$|[.:/_-])/i.test(permission)\n ));\n return {\n id: definition.id,\n displayName: definition.displayName,\n description: definition.description,\n ...(definition.branding ? { branding: definition.branding } : {}),\n category: definition.category,\n capabilities: definition.capabilities.filter((capability) => (\n capability === 'context' || capability === 'memory_source' || capability === 'tools' || capability === 'events'\n )),\n access: {\n context: definition.capabilities.includes('context'),\n memory: definition.capabilities.includes('memory_source'),\n read: definition.capabilities.includes('context')\n || definition.capabilities.includes('resources')\n || definition.capabilities.includes('tools'),\n write: canWrite,\n },\n permissionDetails,\n installed: connected.length > 0,\n enabled: connected.some((instance) => instance.enabled),\n status: connected[0]?.status ?? 'not_installed',\n instanceId: connected[0]?.instanceId,\n lastConnectedAt: connected\n .map((instance) => instance.lastConnectedAt)\n .filter((value): value is string => Boolean(value))\n .sort()\n .at(-1),\n lastHealthCheckAt: latestHealth?.lastHealthCheckAt,\n lastHealthStatus: latestHealth?.lastHealthStatus,\n lastActivityAt: [\n ...connected.flatMap((instance) => [\n instance.lastConnectedAt,\n instance.usage.lastHealthCheckAt,\n ...instance.audit.map((entry) => entry.at),\n ]),\n latestSync?.finishedAt,\n latestSync?.startedAt,\n ]\n .filter((value): value is string => Boolean(value))\n .sort()\n .at(-1),\n derivedUnderstandingCount: relatedRecords.length,\n knowledgeItemCount: sourceItems.filter((item) => !item.deletedAt).length,\n lastSyncAt: latestSync?.finishedAt ?? latestSync?.startedAt,\n lastSyncStatus: latestSync?.status,\n lastSyncError: latestSync?.error,\n };\n })\n .sort((left, right) => {\n if (left.installed !== right.installed) return left.installed ? -1 : 1;\n return left.displayName.localeCompare(right.displayName);\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { Config } from '../config/schema.js';
2
+ export type ProductLanguage = 'en' | 'zh';
3
+ export type VoiceLanguageMode = 'auto' | 'manual';
4
+ export interface VoiceLanguageProfile {
5
+ language: ProductLanguage;
6
+ sttLanguage: 'auto' | 'en';
7
+ edgeLanguage: 'en-US' | 'zh-CN';
8
+ edgeVoice: 'en-US-MichelleNeural' | 'zh-CN-XiaoxiaoNeural';
9
+ }
10
+ export declare function resolveVoiceLanguageProfile(language: ProductLanguage): VoiceLanguageProfile;
11
+ export declare function inferProductLanguageFromEnvironment(): ProductLanguage;
12
+ /**
13
+ * Install safe product defaults while preserving every explicit on/off choice.
14
+ * Older configs with a persisted language or Edge voice are treated as manual.
15
+ */
16
+ export declare function initializeVoiceDefaults(config: Config, language?: ProductLanguage): boolean;
17
+ /** Apply product language only to providers whose language mapping is known. */
18
+ export declare function applyAutomaticVoiceLanguage(config: Config, language: ProductLanguage): boolean;
19
+ /** Start the model download without delaying gateway readiness. */
20
+ export declare function prepareConfiguredLocalVoiceModel(config: Config): void;
@@ -0,0 +1,156 @@
1
+ import { createLogger } from "../utils/logger/index.js";
2
+ import { init_logger } from "../utils/logger.js";
3
+ import { DEFAULT_LOCAL_VOICE_MODEL_ID, isLocalVoiceModelInstalled } from "./local/models.js";
4
+ import { startLocalVoiceModelInstall } from "./local/model-manager.js";
5
+ //#region src/voice/language-profile.ts
6
+ init_logger();
7
+ const log = createLogger("Voice:Language");
8
+ function resolveVoiceLanguageProfile(language) {
9
+ return language === "zh" ? {
10
+ language,
11
+ sttLanguage: "auto",
12
+ edgeLanguage: "zh-CN",
13
+ edgeVoice: "zh-CN-XiaoxiaoNeural"
14
+ } : {
15
+ language,
16
+ sttLanguage: "en",
17
+ edgeLanguage: "en-US",
18
+ edgeVoice: "en-US-MichelleNeural"
19
+ };
20
+ }
21
+ function inferProductLanguageFromEnvironment() {
22
+ return [
23
+ process.env.LC_ALL,
24
+ process.env.LC_MESSAGES,
25
+ process.env.LANG,
26
+ Intl.DateTimeFormat().resolvedOptions().locale
27
+ ].find((value) => typeof value === "string" && value.trim())?.trim().toLowerCase().startsWith("zh") ? "zh" : "en";
28
+ }
29
+ function hasLegacyManualLanguageSelection(config) {
30
+ const local = config.tools?.media?.audio?.providers?.["xopc-local"];
31
+ const edge = config.messages?.tts?.providers?.edge;
32
+ return typeof local?.language === "string" && local.language.trim().length > 0 || typeof edge?.voice === "string" && edge.voice.trim().length > 0 || typeof edge?.lang === "string" && edge.lang.trim().length > 0;
33
+ }
34
+ /**
35
+ * Install safe product defaults while preserving every explicit on/off choice.
36
+ * Older configs with a persisted language or Edge voice are treated as manual.
37
+ */
38
+ function initializeVoiceDefaults(config, language = inferProductLanguageFromEnvironment()) {
39
+ const before = JSON.stringify({
40
+ voice: config.voice,
41
+ tools: config.tools?.media?.audio,
42
+ tts: config.messages?.tts
43
+ });
44
+ const languageMode = config.voice?.languageMode ?? (hasLegacyManualLanguageSelection(config) ? "manual" : "auto");
45
+ config.voice = {
46
+ ...config.voice,
47
+ languageMode,
48
+ language: config.voice?.language ?? language
49
+ };
50
+ config.tools = config.tools ?? {};
51
+ config.tools.media = config.tools.media ?? {};
52
+ const audio = config.tools.media.audio;
53
+ config.tools.media.audio = {
54
+ ...audio,
55
+ enabled: audio?.enabled ?? true,
56
+ provider: audio?.provider ?? "xopc-local",
57
+ fallback: audio?.fallback ?? {
58
+ enabled: false,
59
+ order: ["xopc-local"]
60
+ },
61
+ providers: {
62
+ ...audio?.providers ?? {},
63
+ "xopc-local": {
64
+ model: DEFAULT_LOCAL_VOICE_MODEL_ID,
65
+ ...audio?.providers?.["xopc-local"] ?? {}
66
+ }
67
+ }
68
+ };
69
+ config.messages = config.messages ?? {};
70
+ const tts = config.messages.tts;
71
+ config.messages.tts = {
72
+ ...tts,
73
+ enabled: tts?.enabled ?? true,
74
+ provider: tts?.provider ?? "edge",
75
+ trigger: tts?.trigger ?? "inbound",
76
+ providers: {
77
+ ...tts?.providers ?? {},
78
+ edge: {
79
+ enabled: true,
80
+ ...tts?.providers?.edge ?? {}
81
+ }
82
+ }
83
+ };
84
+ if (languageMode === "auto") applyAutomaticVoiceLanguage(config, language);
85
+ return before !== JSON.stringify({
86
+ voice: config.voice,
87
+ tools: config.tools?.media?.audio,
88
+ tts: config.messages?.tts
89
+ });
90
+ }
91
+ /** Apply product language only to providers whose language mapping is known. */
92
+ function applyAutomaticVoiceLanguage(config, language) {
93
+ if ((config.voice?.languageMode ?? "auto") !== "auto") return false;
94
+ const before = JSON.stringify({
95
+ voice: config.voice,
96
+ tools: config.tools?.media?.audio,
97
+ tts: config.messages?.tts
98
+ });
99
+ const profile = resolveVoiceLanguageProfile(language);
100
+ config.voice = {
101
+ ...config.voice,
102
+ languageMode: "auto",
103
+ language
104
+ };
105
+ const audio = config.tools?.media?.audio;
106
+ if (audio?.provider === "xopc-local") audio.providers = {
107
+ ...audio.providers ?? {},
108
+ "xopc-local": {
109
+ ...audio.providers?.["xopc-local"] ?? {},
110
+ model: DEFAULT_LOCAL_VOICE_MODEL_ID,
111
+ language: profile.sttLanguage
112
+ }
113
+ };
114
+ const tts = config.messages?.tts;
115
+ if (tts?.provider === "edge") tts.providers = {
116
+ ...tts.providers ?? {},
117
+ edge: {
118
+ enabled: true,
119
+ ...tts.providers?.edge ?? {},
120
+ voice: profile.edgeVoice,
121
+ lang: profile.edgeLanguage
122
+ }
123
+ };
124
+ return before !== JSON.stringify({
125
+ voice: config.voice,
126
+ tools: config.tools?.media?.audio,
127
+ tts: config.messages?.tts
128
+ });
129
+ }
130
+ /** Start the model download without delaying gateway readiness. */
131
+ function prepareConfiguredLocalVoiceModel(config) {
132
+ const audio = config.tools?.media?.audio;
133
+ if (audio?.enabled === false || (audio?.provider ?? "xopc-local") !== "xopc-local") return;
134
+ const configuredModel = audio?.providers?.["xopc-local"]?.model;
135
+ const modelId = typeof configuredModel === "string" && configuredModel.trim() ? configuredModel.trim() : DEFAULT_LOCAL_VOICE_MODEL_ID;
136
+ if (isLocalVoiceModelInstalled(modelId)) return;
137
+ try {
138
+ startLocalVoiceModelInstall(modelId);
139
+ log.info({
140
+ modelId,
141
+ phase: "startup_prepare"
142
+ }, "Local voice model preparation started");
143
+ } catch (err) {
144
+ const errorMessage = err instanceof Error ? err.message : String(err);
145
+ log.warn({
146
+ err,
147
+ modelId,
148
+ errorMessage,
149
+ phase: "startup_prepare"
150
+ }, `Local voice model preparation failed: ${errorMessage}`);
151
+ }
152
+ }
153
+ //#endregion
154
+ export { applyAutomaticVoiceLanguage, inferProductLanguageFromEnvironment, initializeVoiceDefaults, prepareConfiguredLocalVoiceModel, resolveVoiceLanguageProfile };
155
+
156
+ //# sourceMappingURL=language-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-profile.js","names":[],"sources":["../../../src/voice/language-profile.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport { createLogger } from '../utils/logger.js';\nimport { startLocalVoiceModelInstall } from './local/model-manager.js';\nimport {\n DEFAULT_LOCAL_VOICE_MODEL_ID,\n isLocalVoiceModelInstalled,\n} from './local/models.js';\n\nconst log = createLogger('Voice:Language');\n\nexport type ProductLanguage = 'en' | 'zh';\nexport type VoiceLanguageMode = 'auto' | 'manual';\n\nexport interface VoiceLanguageProfile {\n language: ProductLanguage;\n sttLanguage: 'auto' | 'en';\n edgeLanguage: 'en-US' | 'zh-CN';\n edgeVoice: 'en-US-MichelleNeural' | 'zh-CN-XiaoxiaoNeural';\n}\n\nexport function resolveVoiceLanguageProfile(language: ProductLanguage): VoiceLanguageProfile {\n return language === 'zh'\n ? {\n language,\n sttLanguage: 'auto',\n edgeLanguage: 'zh-CN',\n edgeVoice: 'zh-CN-XiaoxiaoNeural',\n }\n : {\n language,\n sttLanguage: 'en',\n edgeLanguage: 'en-US',\n edgeVoice: 'en-US-MichelleNeural',\n };\n}\n\nexport function inferProductLanguageFromEnvironment(): ProductLanguage {\n const locale = [\n process.env.LC_ALL,\n process.env.LC_MESSAGES,\n process.env.LANG,\n Intl.DateTimeFormat().resolvedOptions().locale,\n ].find((value) => typeof value === 'string' && value.trim());\n return locale?.trim().toLowerCase().startsWith('zh') ? 'zh' : 'en';\n}\n\nfunction hasLegacyManualLanguageSelection(config: Config): boolean {\n const local = config.tools?.media?.audio?.providers?.['xopc-local'];\n const edge = config.messages?.tts?.providers?.edge;\n return (\n (typeof local?.language === 'string' && local.language.trim().length > 0) ||\n (typeof edge?.voice === 'string' && edge.voice.trim().length > 0) ||\n (typeof edge?.lang === 'string' && edge.lang.trim().length > 0)\n );\n}\n\n/**\n * Install safe product defaults while preserving every explicit on/off choice.\n * Older configs with a persisted language or Edge voice are treated as manual.\n */\nexport function initializeVoiceDefaults(\n config: Config,\n language: ProductLanguage = inferProductLanguageFromEnvironment(),\n): boolean {\n const before = JSON.stringify({ voice: config.voice, tools: config.tools?.media?.audio, tts: config.messages?.tts });\n const existingMode = config.voice?.languageMode;\n const languageMode: VoiceLanguageMode =\n existingMode ?? (hasLegacyManualLanguageSelection(config) ? 'manual' : 'auto');\n\n config.voice = {\n ...config.voice,\n languageMode,\n language: config.voice?.language ?? language,\n };\n\n config.tools = config.tools ?? {};\n config.tools.media = config.tools.media ?? {};\n const audio = config.tools.media.audio;\n config.tools.media.audio = {\n ...audio,\n enabled: audio?.enabled ?? true,\n provider: audio?.provider ?? 'xopc-local',\n fallback: audio?.fallback ?? { enabled: false, order: ['xopc-local'] },\n providers: {\n ...(audio?.providers ?? {}),\n 'xopc-local': {\n model: DEFAULT_LOCAL_VOICE_MODEL_ID,\n ...(audio?.providers?.['xopc-local'] ?? {}),\n },\n },\n };\n\n config.messages = config.messages ?? {};\n const tts = config.messages.tts;\n config.messages.tts = {\n ...tts,\n enabled: tts?.enabled ?? true,\n provider: tts?.provider ?? 'edge',\n trigger: tts?.trigger ?? 'inbound',\n providers: {\n ...(tts?.providers ?? {}),\n edge: {\n enabled: true,\n ...(tts?.providers?.edge ?? {}),\n },\n },\n };\n\n if (languageMode === 'auto') {\n applyAutomaticVoiceLanguage(config, language);\n }\n return before !== JSON.stringify({ voice: config.voice, tools: config.tools?.media?.audio, tts: config.messages?.tts });\n}\n\n/** Apply product language only to providers whose language mapping is known. */\nexport function applyAutomaticVoiceLanguage(config: Config, language: ProductLanguage): boolean {\n if ((config.voice?.languageMode ?? 'auto') !== 'auto') return false;\n const before = JSON.stringify({ voice: config.voice, tools: config.tools?.media?.audio, tts: config.messages?.tts });\n const profile = resolveVoiceLanguageProfile(language);\n\n config.voice = { ...config.voice, languageMode: 'auto', language };\n const audio = config.tools?.media?.audio;\n if (audio?.provider === 'xopc-local') {\n audio.providers = {\n ...(audio.providers ?? {}),\n 'xopc-local': {\n ...(audio.providers?.['xopc-local'] ?? {}),\n model: DEFAULT_LOCAL_VOICE_MODEL_ID,\n language: profile.sttLanguage,\n },\n };\n }\n const tts = config.messages?.tts;\n if (tts?.provider === 'edge') {\n tts.providers = {\n ...(tts.providers ?? {}),\n edge: {\n enabled: true,\n ...(tts.providers?.edge ?? {}),\n voice: profile.edgeVoice,\n lang: profile.edgeLanguage,\n },\n };\n }\n return before !== JSON.stringify({ voice: config.voice, tools: config.tools?.media?.audio, tts: config.messages?.tts });\n}\n\n/** Start the model download without delaying gateway readiness. */\nexport function prepareConfiguredLocalVoiceModel(config: Config): void {\n const audio = config.tools?.media?.audio;\n if (audio?.enabled === false || (audio?.provider ?? 'xopc-local') !== 'xopc-local') return;\n const configuredModel = audio?.providers?.['xopc-local']?.model;\n const modelId = typeof configuredModel === 'string' && configuredModel.trim()\n ? configuredModel.trim()\n : DEFAULT_LOCAL_VOICE_MODEL_ID;\n if (isLocalVoiceModelInstalled(modelId)) return;\n try {\n startLocalVoiceModelInstall(modelId);\n log.info({ modelId, phase: 'startup_prepare' }, 'Local voice model preparation started');\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, modelId, errorMessage, phase: 'startup_prepare' }, `Local voice model preparation failed: ${errorMessage}`);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { LocalVoiceModelDefinition } from './models.js';
2
+ export interface LocalVoiceFileProgress {
3
+ status: 'download';
4
+ file: string;
5
+ loaded: number;
6
+ total: number;
7
+ progress: number;
8
+ }
9
+ export declare function downloadLocalVoiceModelFiles(options: {
10
+ model: LocalVoiceModelDefinition;
11
+ destinationDir: string;
12
+ remoteHosts: readonly string[];
13
+ onProgress?: (progress: LocalVoiceFileProgress) => void;
14
+ }): Promise<void>;
@@ -0,0 +1,94 @@
1
+ import { dirname, join } from "node:path";
2
+ import { createReadStream, createWriteStream } from "node:fs";
3
+ import { createHash } from "node:crypto";
4
+ import { mkdir, rm, stat } from "node:fs/promises";
5
+ import { Readable } from "node:stream";
6
+ import { pipeline } from "node:stream/promises";
7
+ //#region src/voice/local/model-files.ts
8
+ function resolveModelFileUrl(remoteHost, model, file) {
9
+ const base = remoteHost.endsWith("/") ? remoteHost : `${remoteHost}/`;
10
+ const path = file.path.split("/").map(encodeURIComponent).join("/");
11
+ return new URL(`${model.repository}/resolve/${model.revision}/${path}`, base).toString();
12
+ }
13
+ async function sha256File(path) {
14
+ const hash = createHash("sha256");
15
+ for await (const chunk of createReadStream(path)) hash.update(chunk);
16
+ return hash.digest("hex");
17
+ }
18
+ async function downloadFile(url, destination, expectedBytes, onBytes) {
19
+ const existing = await stat(destination).catch(() => null);
20
+ let offset = existing?.isFile() ? existing.size : 0;
21
+ if (offset > expectedBytes) {
22
+ await rm(destination, { force: true });
23
+ offset = 0;
24
+ }
25
+ if (offset === expectedBytes) {
26
+ onBytes(offset);
27
+ return;
28
+ }
29
+ const response = await fetch(url, {
30
+ redirect: "follow",
31
+ headers: offset > 0 ? { Range: `bytes=${offset}-` } : void 0
32
+ });
33
+ if (!response.ok || !response.body) throw new Error(`Model download failed with HTTP ${response.status}`);
34
+ const isResume = offset > 0 && response.status === 206;
35
+ if (offset > 0 && !isResume) offset = 0;
36
+ const declaredBytes = Number(response.headers.get("content-length"));
37
+ const expectedResponseBytes = expectedBytes - offset;
38
+ if (Number.isFinite(declaredBytes) && declaredBytes > 0 && declaredBytes !== expectedResponseBytes) throw new Error(`Model download size mismatch: expected ${expectedResponseBytes}, received ${declaredBytes}`);
39
+ await mkdir(dirname(destination), { recursive: true });
40
+ let loaded = offset;
41
+ let lastReportedBytes = offset;
42
+ let lastReportedAt = 0;
43
+ const source = Readable.fromWeb(response.body);
44
+ source.on("data", (chunk) => {
45
+ loaded += chunk.length;
46
+ const now = Date.now();
47
+ if (loaded === expectedBytes || loaded - lastReportedBytes >= 256 * 1024 || now - lastReportedAt >= 250) {
48
+ lastReportedBytes = loaded;
49
+ lastReportedAt = now;
50
+ onBytes(loaded);
51
+ }
52
+ });
53
+ await pipeline(source, createWriteStream(destination, {
54
+ flags: isResume ? "a" : "w",
55
+ mode: 384
56
+ }));
57
+ if (loaded !== expectedBytes) throw new Error(`Model download was incomplete: expected ${expectedBytes}, received ${loaded}`);
58
+ }
59
+ async function verifyModelFile(path, file) {
60
+ if ((await stat(path)).size !== file.bytes) throw new Error(`Model file size mismatch for ${file.path}`);
61
+ if (await sha256File(path) !== file.sha256) throw new Error(`Model file checksum mismatch for ${file.path}`);
62
+ }
63
+ async function downloadLocalVoiceModelFiles(options) {
64
+ const files = options.model.files;
65
+ if (!files?.length) throw new Error(`No downloadable files declared for ${options.model.id}`);
66
+ const total = files.reduce((sum, file) => sum + file.bytes, 0);
67
+ let completedBytes = 0;
68
+ for (const file of files) {
69
+ const destination = join(options.destinationDir, file.path);
70
+ let lastError;
71
+ for (const host of options.remoteHosts) try {
72
+ await downloadFile(resolveModelFileUrl(host, options.model, file), destination, file.bytes, (fileBytes) => options.onProgress?.({
73
+ status: "download",
74
+ file: file.path,
75
+ loaded: completedBytes + fileBytes,
76
+ total,
77
+ progress: (completedBytes + fileBytes) / total
78
+ }));
79
+ await verifyModelFile(destination, file);
80
+ lastError = void 0;
81
+ break;
82
+ } catch (error) {
83
+ lastError = error;
84
+ const message = error instanceof Error ? error.message : String(error);
85
+ if (message.includes("checksum mismatch") || message.includes("size mismatch")) await rm(destination, { force: true });
86
+ }
87
+ if (lastError) throw lastError;
88
+ completedBytes += file.bytes;
89
+ }
90
+ }
91
+ //#endregion
92
+ export { downloadLocalVoiceModelFiles };
93
+
94
+ //# sourceMappingURL=model-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-files.js","names":[],"sources":["../../../../src/voice/local/model-files.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { createReadStream, createWriteStream } from 'node:fs';\nimport { mkdir, rm, stat } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\n\nimport type { LocalVoiceModelDefinition, LocalVoiceModelFile } from './models.js';\n\nexport interface LocalVoiceFileProgress {\n status: 'download';\n file: string;\n loaded: number;\n total: number;\n progress: number;\n}\n\nfunction resolveModelFileUrl(\n remoteHost: string,\n model: LocalVoiceModelDefinition,\n file: LocalVoiceModelFile,\n): string {\n const base = remoteHost.endsWith('/') ? remoteHost : `${remoteHost}/`;\n const path = file.path.split('/').map(encodeURIComponent).join('/');\n return new URL(`${model.repository}/resolve/${model.revision}/${path}`, base).toString();\n}\n\nasync function sha256File(path: string): Promise<string> {\n const hash = createHash('sha256');\n for await (const chunk of createReadStream(path)) hash.update(chunk as Buffer);\n return hash.digest('hex');\n}\n\nasync function downloadFile(\n url: string,\n destination: string,\n expectedBytes: number,\n onBytes: (bytes: number) => void,\n): Promise<void> {\n const existing = await stat(destination).catch(() => null);\n let offset = existing?.isFile() ? existing.size : 0;\n if (offset > expectedBytes) {\n await rm(destination, { force: true });\n offset = 0;\n }\n if (offset === expectedBytes) {\n onBytes(offset);\n return;\n }\n const response = await fetch(url, {\n redirect: 'follow',\n headers: offset > 0 ? { Range: `bytes=${offset}-` } : undefined,\n });\n if (!response.ok || !response.body) {\n throw new Error(`Model download failed with HTTP ${response.status}`);\n }\n const isResume = offset > 0 && response.status === 206;\n if (offset > 0 && !isResume) offset = 0;\n const declaredBytes = Number(response.headers.get('content-length'));\n const expectedResponseBytes = expectedBytes - offset;\n if (Number.isFinite(declaredBytes) && declaredBytes > 0 && declaredBytes !== expectedResponseBytes) {\n throw new Error(`Model download size mismatch: expected ${expectedResponseBytes}, received ${declaredBytes}`);\n }\n\n await mkdir(dirname(destination), { recursive: true });\n let loaded = offset;\n let lastReportedBytes = offset;\n let lastReportedAt = 0;\n const source = Readable.fromWeb(response.body as never);\n source.on('data', (chunk: Buffer) => {\n loaded += chunk.length;\n const now = Date.now();\n if (loaded === expectedBytes || loaded - lastReportedBytes >= 256 * 1024 || now - lastReportedAt >= 250) {\n lastReportedBytes = loaded;\n lastReportedAt = now;\n onBytes(loaded);\n }\n });\n await pipeline(source, createWriteStream(destination, { flags: isResume ? 'a' : 'w', mode: 0o600 }));\n if (loaded !== expectedBytes) {\n throw new Error(`Model download was incomplete: expected ${expectedBytes}, received ${loaded}`);\n }\n}\n\nasync function verifyModelFile(path: string, file: LocalVoiceModelFile): Promise<void> {\n const info = await stat(path);\n if (info.size !== file.bytes) {\n throw new Error(`Model file size mismatch for ${file.path}`);\n }\n const actual = await sha256File(path);\n if (actual !== file.sha256) {\n throw new Error(`Model file checksum mismatch for ${file.path}`);\n }\n}\n\nexport async function downloadLocalVoiceModelFiles(options: {\n model: LocalVoiceModelDefinition;\n destinationDir: string;\n remoteHosts: readonly string[];\n onProgress?: (progress: LocalVoiceFileProgress) => void;\n}): Promise<void> {\n const files = options.model.files;\n if (!files?.length) throw new Error(`No downloadable files declared for ${options.model.id}`);\n const total = files.reduce((sum, file) => sum + file.bytes, 0);\n let completedBytes = 0;\n\n for (const file of files) {\n const destination = join(options.destinationDir, file.path);\n let lastError: unknown;\n for (const host of options.remoteHosts) {\n try {\n await downloadFile(\n resolveModelFileUrl(host, options.model, file),\n destination,\n file.bytes,\n (fileBytes) => options.onProgress?.({\n status: 'download',\n file: file.path,\n loaded: completedBytes + fileBytes,\n total,\n progress: (completedBytes + fileBytes) / total,\n }),\n );\n await verifyModelFile(destination, file);\n lastError = undefined;\n break;\n } catch (error) {\n lastError = error;\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes('checksum mismatch') || message.includes('size mismatch')) {\n await rm(destination, { force: true });\n }\n }\n }\n if (lastError) {\n throw lastError;\n }\n completedBytes += file.bytes;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export type LocalVoiceModelState = 'not_installed' | 'downloading' | 'ready' | 'error';
2
+ export interface LocalVoiceModelStatus {
3
+ id: string;
4
+ name: string;
5
+ description: string;
6
+ approximateBytes: number;
7
+ engine: string;
8
+ languages: readonly string[];
9
+ recommended?: boolean;
10
+ state: LocalVoiceModelState;
11
+ progress?: number;
12
+ downloadedBytes?: number;
13
+ totalBytes?: number;
14
+ error?: string;
15
+ }
16
+ export declare function listLocalVoiceModelStatuses(): Promise<LocalVoiceModelStatus[]>;
17
+ export declare function startLocalVoiceModelInstall(modelId: string): LocalVoiceModelStatus;
18
+ export declare function removeLocalVoiceModel(modelId: string): Promise<void>;
@@ -0,0 +1,135 @@
1
+ import { createLogger } from "../../utils/logger/index.js";
2
+ import { init_logger } from "../../utils/logger.js";
3
+ import { LOCAL_VOICE_MODELS, getLocalVoiceModel, isLocalVoiceModelInstalled, resolveLocalVoiceModelDir, resolveLocalVoiceModelMarkerPath } from "./models.js";
4
+ import { getLocalVoiceRuntimeClient } from "./runtime-client.js";
5
+ import { join } from "node:path";
6
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
7
+ //#region src/voice/local/model-manager.ts
8
+ init_logger();
9
+ const log = createLogger("LocalVoice:Models");
10
+ const jobs = /* @__PURE__ */ new Map();
11
+ function progressFraction(progress) {
12
+ if (typeof progress.progress === "number" && Number.isFinite(progress.progress)) return progress.progress > 1 ? progress.progress / 100 : progress.progress;
13
+ if (typeof progress.loaded === "number" && typeof progress.total === "number" && progress.total > 0) return progress.loaded / progress.total;
14
+ }
15
+ async function listLocalVoiceModelStatuses() {
16
+ return Promise.all(LOCAL_VOICE_MODELS.map(async (model) => {
17
+ const active = jobs.get(model.id);
18
+ if (active) return { ...active };
19
+ if (!isLocalVoiceModelInstalled(model.id)) return {
20
+ id: model.id,
21
+ name: model.name,
22
+ description: model.description,
23
+ approximateBytes: model.approximateBytes,
24
+ engine: model.engine,
25
+ languages: model.languages,
26
+ recommended: model.recommended,
27
+ state: "not_installed"
28
+ };
29
+ let installedAt;
30
+ try {
31
+ installedAt = JSON.parse(await readFile(resolveLocalVoiceModelMarkerPath(model.id), "utf8")).installedAt;
32
+ } catch {}
33
+ return {
34
+ id: model.id,
35
+ name: model.name,
36
+ description: installedAt ? `${model.description} Installed ${installedAt}.` : model.description,
37
+ approximateBytes: model.approximateBytes,
38
+ engine: model.engine,
39
+ languages: model.languages,
40
+ recommended: model.recommended,
41
+ state: "ready"
42
+ };
43
+ }));
44
+ }
45
+ function startLocalVoiceModelInstall(modelId) {
46
+ const model = getLocalVoiceModel(modelId);
47
+ const existing = jobs.get(model.id);
48
+ if (existing?.state === "downloading") return { ...existing };
49
+ const status = {
50
+ id: model.id,
51
+ name: model.name,
52
+ description: model.description,
53
+ approximateBytes: model.approximateBytes,
54
+ engine: model.engine,
55
+ languages: model.languages,
56
+ recommended: model.recommended,
57
+ state: "downloading",
58
+ progress: 0
59
+ };
60
+ jobs.set(model.id, status);
61
+ (async () => {
62
+ const modelDir = resolveLocalVoiceModelDir(model.id);
63
+ const stagingDir = `${modelDir}.download`;
64
+ try {
65
+ if (model.engine === "transformers.js") await rm(stagingDir, {
66
+ recursive: true,
67
+ force: true
68
+ });
69
+ await mkdir(stagingDir, { recursive: true });
70
+ await getLocalVoiceRuntimeClient().request("model.install", {
71
+ modelId: model.id,
72
+ cacheDir: stagingDir
73
+ }, { onProgress: (progress) => {
74
+ const current = jobs.get(model.id);
75
+ if (!current) return;
76
+ current.progress = progressFraction(progress) ?? current.progress;
77
+ current.downloadedBytes = progress.loaded;
78
+ current.totalBytes = progress.total;
79
+ } });
80
+ await writeFile(join(stagingDir, "installed.json"), `${JSON.stringify({
81
+ modelId: model.id,
82
+ repository: model.repository,
83
+ revision: model.revision,
84
+ engine: model.engine,
85
+ ...model.dtype ? { dtype: model.dtype } : {},
86
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
87
+ }, null, 2)}\n`, { mode: 384 });
88
+ await rm(modelDir, {
89
+ recursive: true,
90
+ force: true
91
+ });
92
+ await rename(stagingDir, modelDir);
93
+ jobs.delete(model.id);
94
+ log.info({ modelId: model.id }, "Local voice model installed");
95
+ } catch (error) {
96
+ if (model.engine === "transformers.js") await rm(stagingDir, {
97
+ recursive: true,
98
+ force: true
99
+ }).catch(() => {});
100
+ const message = error instanceof Error ? error.message : String(error);
101
+ jobs.set(model.id, {
102
+ ...status,
103
+ state: "error",
104
+ error: message
105
+ });
106
+ log.error({
107
+ err: error,
108
+ modelId: model.id
109
+ }, `Local voice model installation failed: ${message}`);
110
+ }
111
+ })();
112
+ return { ...status };
113
+ }
114
+ async function removeLocalVoiceModel(modelId) {
115
+ const model = getLocalVoiceModel(modelId);
116
+ if (jobs.get(model.id)?.state === "downloading") throw new Error("Cannot remove a model while it is downloading");
117
+ try {
118
+ await getLocalVoiceRuntimeClient().request("model.unload", { modelId: model.id }, { timeoutMs: 1e4 });
119
+ } catch (error) {
120
+ log.warn({
121
+ err: error,
122
+ modelId: model.id
123
+ }, "Local voice runtime unload failed; removing model files");
124
+ }
125
+ await rm(resolveLocalVoiceModelDir(model.id), {
126
+ recursive: true,
127
+ force: true
128
+ });
129
+ jobs.delete(model.id);
130
+ log.info({ modelId: model.id }, "Local voice model removed");
131
+ }
132
+ //#endregion
133
+ export { listLocalVoiceModelStatuses, removeLocalVoiceModel, startLocalVoiceModelInstall };
134
+
135
+ //# sourceMappingURL=model-manager.js.map