@xopcai/xopc 0.0.389 → 0.0.391

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 (321) 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-dvik6t7Z.js → activity-store-DWQCDi8M.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-bWMd5TDC.js → agent-avatar-dicebear-CzS0TUv9.js} +1 -1
  5. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-DE910MyU.js +1 -0
  6. package/dist/gateway/static/root/assets/{agent-plugin-dialog-UunIxc1q.js → agent-plugin-dialog-0SlvsYl7.js} +1 -1
  7. package/dist/gateway/static/root/assets/agents-BZh28Qn1.js +77 -0
  8. package/dist/gateway/static/root/assets/{agents-admin-api-BlNGyXpm.js → agents-admin-api-Bb3Kvz3X.js} +1 -1
  9. package/dist/gateway/static/root/assets/app-management-settings-panel-9A7JN5V5.js +1 -0
  10. package/dist/gateway/static/root/assets/{appearance-settings-CUAdx44v.js → appearance-settings-BPdLEo6L.js} +1 -1
  11. package/dist/gateway/static/root/assets/{apps-page-D5zDB6gp.js → apps-page-Cz7LypV9.js} +1 -1
  12. package/dist/gateway/static/root/assets/{archive-plugin-Du6dJNcl.js → archive-plugin-DFnHPNie.js} +1 -1
  13. package/dist/gateway/static/root/assets/{automations-page-BO8Ig5Z4.js → automations-page-CnBLPVNh.js} +1 -1
  14. package/dist/gateway/static/root/assets/automations-workspace-BBjS-sk-.js +6 -0
  15. package/dist/gateway/static/root/assets/{binary-plugins-D7VJjwnr.js → binary-plugins-BQC0Tzoi.js} +1 -1
  16. package/dist/gateway/static/root/assets/{block-editor-CaJlWTAn.js → block-editor-DSh5019z.js} +4 -4
  17. package/dist/gateway/static/root/assets/{browser-automation-inputs-iyMBJmQX.js → browser-automation-inputs-C5tIrIRL.js} +1 -1
  18. package/dist/gateway/static/root/assets/browser-automations-page-CFCMNfJN.js +3 -0
  19. package/dist/gateway/static/root/assets/browser-settings-page-C__tyPwu.js +3 -0
  20. package/dist/gateway/static/root/assets/{capabilities-CEH1AnXc.js → capabilities-D6RJG4QY.js} +1 -1
  21. package/dist/gateway/static/root/assets/{capabilities-MCKRMZDK.js → capabilities-DGEmBQnF.js} +1 -1
  22. package/dist/gateway/static/root/assets/capabilities-page-B5AZa7ir.js +2 -0
  23. package/dist/gateway/static/root/assets/capabilities-settings-panel-Dskz2_bh.js +8 -0
  24. package/dist/gateway/static/root/assets/{capability-header-actions-BIA0Lwt5.js → capability-header-actions-xzjWZVbg.js} +1 -1
  25. package/dist/gateway/static/root/assets/channels-DRse_dD3.js +1 -0
  26. package/dist/gateway/static/root/assets/chat-terminal-dock-CQF0M4kE.js +36 -0
  27. package/dist/gateway/static/root/assets/{computer-settings-page-DvY3zgyY.js → computer-settings-page-ZfzCTS0N.js} +1 -1
  28. package/dist/gateway/static/root/assets/{connector-service-page-St2-Fj3y.js → connector-service-page-CwAeWVLK.js} +1 -1
  29. package/dist/gateway/static/root/assets/connectors-page-CanakKaj.js +1 -0
  30. package/dist/gateway/static/root/assets/{date-picker-C0-D80ny.js → date-picker-hmJlEAQZ.js} +1 -1
  31. package/dist/gateway/static/root/assets/{dependency-picker-CNd7mwMU.js → dependency-picker-Us_Juz6I.js} +1 -1
  32. package/dist/gateway/static/root/assets/{desktop-pet-C-2wDANn.js → desktop-pet-CvOTI4uw.js} +1 -1
  33. package/dist/gateway/static/root/assets/{desktop-pet-settings-DyhxLrdJ.js → desktop-pet-settings-DYg-Gb8t.js} +1 -1
  34. package/dist/gateway/static/root/assets/device-pairing-wizard-B1FbZ8C1.js +1 -0
  35. package/dist/gateway/static/root/assets/{directory-picker-path-field-CNivVIPT.js → directory-picker-path-field-DZN9EDvM.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-debug-page-CGYO3JPw.js → extension-debug-page-YIibp6Kk.js} +1 -1
  37. package/dist/gateway/static/root/assets/{extension-page-Dc51ALAg.js → extension-page-DkcGyb28.js} +1 -1
  38. package/dist/gateway/static/root/assets/{extension-provider-BXPILdlw.js → extension-provider-B1PQGTtY.js} +1 -1
  39. package/dist/gateway/static/root/assets/extension-settings-page-C_rJiSKo.js +1 -0
  40. package/dist/gateway/static/root/assets/{gateway-config-swr-Bc2MytsF.js → gateway-config-swr-Dv_A5FMu.js} +1 -1
  41. package/dist/gateway/static/root/assets/{gateway-realtime-BFK-vlME.js → gateway-realtime-DghCGfY_.js} +1 -1
  42. package/dist/gateway/static/root/assets/gateway-realtime-store-CMJVSnvi.js +1 -0
  43. package/dist/gateway/static/root/assets/gateway-settings-CjCWPHNJ.js +1 -0
  44. package/dist/gateway/static/root/assets/{gateway-startup-retry-BDPMttPx.js → gateway-startup-retry-CkTVSESs.js} +1 -1
  45. package/dist/gateway/static/root/assets/{highlighted-body-KQOG7T2V-DAlRGMge.js → highlighted-body-KQOG7T2V-DV2wNqBn.js} +1 -1
  46. package/dist/gateway/static/root/assets/home-page-fAYsd7kG.js +2 -0
  47. package/dist/gateway/static/root/assets/image-generation-api-B98iXt6A.js +1 -0
  48. package/dist/gateway/static/root/assets/imports-page-DoBCBN8G.js +1 -0
  49. package/dist/gateway/static/root/assets/index-DFgXwLsv.js +260 -0
  50. package/dist/gateway/static/root/assets/index-a6ZvhclR.css +1 -0
  51. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BYYC9Qmw.js → keyboard-shortcuts-settings-DXVu4lqV.js} +1 -1
  52. package/dist/gateway/static/root/assets/local-app-workbench-page-B3fz6uRi.js +1 -0
  53. package/dist/gateway/static/root/assets/{local-apps-page-BU8pUsVM.js → local-apps-page-BC6xhmUh.js} +1 -1
  54. package/dist/gateway/static/root/assets/{local-session-drafts-kFc_MAA9.js → local-session-drafts-DFnZQu9A.js} +1 -1
  55. package/dist/gateway/static/root/assets/{locale-store-Dr43FCty.js → locale-store-BpK9AiDq.js} +1 -1
  56. package/dist/gateway/static/root/assets/logs-page-CSqSFDRU.js +2 -0
  57. package/dist/gateway/static/root/assets/management-settings-gq5L0THs.js +1 -0
  58. package/dist/gateway/static/root/assets/{markdown-view-CdKlPjJM.js → markdown-view-IsYTotu6.js} +18 -18
  59. package/dist/gateway/static/root/assets/{media-plugins-DE9zt88i.js → media-plugins-oSChWbcP.js} +1 -1
  60. package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-C_QKwqDN.js +1 -0
  61. package/dist/gateway/static/root/assets/messages-CKTo_sIF.js +3 -0
  62. package/dist/gateway/static/root/assets/models-hub-cache-TjpL7hZ5.js +1 -0
  63. package/dist/gateway/static/root/assets/note-detail-panel-CdpNXre-.js +5 -0
  64. package/dist/gateway/static/root/assets/{notes-api-BHouNL6-.js → notes-api-CBb944yw.js} +1 -1
  65. package/dist/gateway/static/root/assets/notes-page-EGvGw8MN.js +8 -0
  66. package/dist/gateway/static/root/assets/{page-context-capture-button-TQr8NnSc.js → page-context-capture-button-CoWb7BYP.js} +1 -1
  67. package/dist/gateway/static/root/assets/personal-page-DYzbNTyN.js +1 -0
  68. package/dist/gateway/static/root/assets/{product-open-page-BEl6syFp.js → product-open-page-CTyy7H7-.js} +1 -1
  69. package/dist/gateway/static/root/assets/{product-preview-content-BxSHnbqz.js → product-preview-content-D4xqTaEy.js} +1 -1
  70. package/dist/gateway/static/root/assets/project-detail-page-DIVCw0QQ.js +1 -0
  71. package/dist/gateway/static/root/assets/projects-page-B4L9VnBN.js +1 -0
  72. package/dist/gateway/static/root/assets/{registry-api-BD5IgDOA.js → registry-api-Dsa2LvRt.js} +1 -1
  73. package/dist/gateway/static/root/assets/remote-access-hub-B8PvgLVz.js +1 -0
  74. package/dist/gateway/static/root/assets/{runtime-tools-api-CjTRoQWT.js → runtime-tools-api-C2Uw89eY.js} +1 -1
  75. package/dist/gateway/static/root/assets/runtime-tools-settings-panel-Bqnp3tyt.js +1 -0
  76. package/dist/gateway/static/root/assets/scenes-page-D50g4GM2.js +2 -0
  77. package/dist/gateway/static/root/assets/{schema-form-ByG-R6N2.js → schema-form-h_RCNfiO.js} +1 -1
  78. package/dist/gateway/static/root/assets/{send-session-input-UAVStisp.js → send-session-input-DypbVTT7.js} +1 -1
  79. package/dist/gateway/static/root/assets/{session-api-DvgEiNRS.js → session-api-ga-SUOq_.js} +1 -1
  80. package/dist/gateway/static/root/assets/{session-manager-BOb1tC_Z.js → session-manager-C_OiKblh.js} +8 -8
  81. package/dist/gateway/static/root/assets/sessions-page-BsqDIbIr.js +1 -0
  82. package/dist/gateway/static/root/assets/settings-advanced-gate-D3zJAW4p.js +1 -0
  83. package/dist/gateway/static/root/assets/{settings-page-C1e9m23j.js → settings-page-DPCgrICd.js} +1 -1
  84. package/dist/gateway/static/root/assets/setup-status-panel-jjSYJpDd.js +1 -0
  85. package/dist/gateway/static/root/assets/{share-link-dialog-kE_6v5Dp.js → share-link-dialog-BGicglAS.js} +1 -1
  86. package/dist/gateway/static/root/assets/{share-preview-page-BT-_yR1h.js → share-preview-page-BORe9rLB.js} +1 -1
  87. package/dist/gateway/static/root/assets/{shares-api-Dg_SqHQE.js → shares-api-Dbx3z6-1.js} +1 -1
  88. package/dist/gateway/static/root/assets/shares-settings-C13FgfWk.js +1 -0
  89. package/dist/gateway/static/root/assets/{skill-api-BslUYMnX.js → skill-api-Cq6O1DZK.js} +1 -1
  90. package/dist/gateway/static/root/assets/{skill-reload-api-BQ2rTOP1.js → skill-reload-api-Cq7QDjMA.js} +1 -1
  91. package/dist/gateway/static/root/assets/skills-page-BM1ShEjx.js +2 -0
  92. package/dist/gateway/static/root/assets/{system-settings-panel-GpbPfWwo.js → system-settings-panel-BNCGFO1e.js} +1 -1
  93. package/dist/gateway/static/root/assets/task-detail-page-4VG82wcF.js +2 -0
  94. package/dist/gateway/static/root/assets/{task-expandable-content-DJrrEBB4.js → task-expandable-content-DAZGz5YC.js} +1 -1
  95. package/dist/gateway/static/root/assets/{theme-store-BKpYnggg.js → theme-store-Db7hDESt.js} +1 -1
  96. package/dist/gateway/static/root/assets/{tracing-settings-page-D71w7dwV.js → tracing-settings-page-C6p2Mu92.js} +1 -1
  97. package/dist/gateway/static/root/assets/{tunnel-api-BV46CvS8.js → tunnel-api-Dw15EJp0.js} +1 -1
  98. package/dist/gateway/static/root/assets/usage-page-DDrIqgGe.js +1 -0
  99. package/dist/gateway/static/root/assets/{use-autosave-CzyqRffr.js → use-autosave-DDjIdW4U.js} +1 -1
  100. package/dist/gateway/static/root/assets/{use-composer-attachments-Dviq-1f6.js → use-composer-attachments-DNl7O3Vb.js} +1 -1
  101. package/dist/gateway/static/root/assets/{use-file-preview-expanded-D5Kk7Hon.js → use-file-preview-expanded-DMHehhwG.js} +2 -2
  102. package/dist/gateway/static/root/assets/user-model-page-CaS9dOIr.js +2 -0
  103. package/dist/gateway/static/root/assets/{voice-api-key-field-DWtbmacd.js → voice-api-key-field-ByAxHE4P.js} +1 -1
  104. package/dist/gateway/static/root/assets/{voice-config-api-DnIjZm_9.js → voice-config-api-Dl39b27p.js} +1 -1
  105. package/dist/gateway/static/root/assets/voice-transcribe-api-CVaU83Jc.js +1 -0
  106. package/dist/gateway/static/root/assets/{work-discovery-overlay-ChEG71hz.js → work-discovery-overlay-BvVvPA1T.js} +1 -1
  107. package/dist/gateway/static/root/assets/workflow-run-setup-panel-BcFm9QcC.js +3 -0
  108. package/dist/gateway/static/root/assets/workflows-page-DMec_XZB.js +6 -0
  109. package/dist/gateway/static/root/assets/{workspace-api-CZDvu7uA.js → workspace-api-BpfKsrtP.js} +2 -2
  110. package/dist/gateway/static/root/assets/{xopc-cloud-account-card-CbUgg1SK.js → xopc-cloud-account-card-CU5lzzE9.js} +1 -1
  111. package/dist/gateway/static/root/index.html +28 -35
  112. package/dist/package.js +1 -1
  113. package/dist/packages/gateway-contract/src/discussions.js +3 -2
  114. package/dist/packages/gateway-contract/src/index.js +4 -0
  115. package/dist/packages/gateway-contract/src/provider-setup.js +6 -0
  116. package/dist/packages/gateway-contract/src/recording-outcome.js +6 -0
  117. package/dist/packages/image-providers/src/catalog.js +10 -0
  118. package/dist/packages/image-providers/src/execute.js +5 -1
  119. package/dist/packages/image-providers/src/validation.js +19 -1
  120. package/dist/src/agent/agent-manager.d.ts +1 -0
  121. package/dist/src/agent/agent-manager.js +4 -1
  122. package/dist/src/agent/background-review/run-background-review.d.ts +0 -1
  123. package/dist/src/agent/background-review/run-background-review.js +10 -2
  124. package/dist/src/agent/context/execution-scope.js +1 -1
  125. package/dist/src/agent/embedded/execution-tool-policy.d.ts +2 -0
  126. package/dist/src/agent/embedded/execution-tool-policy.js +8 -3
  127. package/dist/src/agent/embedded/run-for-session.js +26 -3
  128. package/dist/src/agent/embedded/run-turn.js +14 -1
  129. package/dist/src/agent/embedded/session-runner.js +1 -1
  130. package/dist/src/agent/embedded/xopc-stream-bridge.js +3 -0
  131. package/dist/src/agent/image/generation/runtime.js +12 -14
  132. package/dist/src/agent/image/understanding/runtime.js +5 -0
  133. package/dist/src/agent/inbound/inbound-loop.d.ts +1 -15
  134. package/dist/src/agent/inbound/inbound-loop.js +5 -0
  135. package/dist/src/agent/mcp/connection-errors.d.ts +3 -0
  136. package/dist/src/agent/mcp/connection-errors.js +32 -0
  137. package/dist/src/agent/mcp/connection-setup.d.ts +2 -0
  138. package/dist/src/agent/mcp/connection-setup.js +38 -18
  139. package/dist/src/agent/memory/compaction.js +5 -1
  140. package/dist/src/agent/memory/idle-compaction-scheduler.js +3 -1
  141. package/dist/src/agent/models/manager.d.ts +1 -0
  142. package/dist/src/agent/models/manager.js +4 -2
  143. package/dist/src/agent/orchestration/agent-orchestrator.d.ts +1 -6
  144. package/dist/src/agent/orchestration/agent-orchestrator.js +5 -0
  145. package/dist/src/agent/service/process-direct-one-shot.js +5 -0
  146. package/dist/src/agent/service/process-direct-streaming.d.ts +1 -0
  147. package/dist/src/agent/service/process-direct-streaming.js +17 -1
  148. package/dist/src/agent/tools/factory.js +14 -12
  149. package/dist/src/agent/tools/image-generate-tool.js +5 -1
  150. package/dist/src/agent/tools/personal-read-tool.js +4 -0
  151. package/dist/src/agent/tools/task-collaboration-tool.js +1 -1
  152. package/dist/src/agent-config/resolver.js +5 -0
  153. package/dist/src/agent-config/schema.d.ts +26 -6
  154. package/dist/src/agent-config/schema.js +2 -0
  155. package/dist/src/ai-assist/text-assist.js +5 -16
  156. package/dist/src/automations/service/action-executor.js +3 -1
  157. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  158. package/dist/src/config/agent-model-intents.js +4 -1
  159. package/dist/src/config/schema.d.ts +11 -5
  160. package/dist/src/config/schema.js +11 -3
  161. package/dist/src/discussions/analyzer.d.ts +2 -0
  162. package/dist/src/discussions/analyzer.js +38 -9
  163. package/dist/src/discussions/audio-clip.d.ts +3 -0
  164. package/dist/src/discussions/audio-clip.js +72 -0
  165. package/dist/src/discussions/live-worker.js +3 -1
  166. package/dist/src/discussions/long-review.d.ts +17 -0
  167. package/dist/src/discussions/long-review.js +278 -0
  168. package/dist/src/discussions/media-timeline.d.ts +21 -0
  169. package/dist/src/discussions/media-timeline.js +134 -0
  170. package/dist/src/discussions/organizer-worker.d.ts +4 -1
  171. package/dist/src/discussions/organizer-worker.js +17 -12
  172. package/dist/src/discussions/organizer.js +2 -0
  173. package/dist/src/discussions/processing.d.ts +43 -0
  174. package/dist/src/discussions/processing.js +125 -0
  175. package/dist/src/discussions/recording.js +202 -42
  176. package/dist/src/discussions/recordingJobs.js +2 -0
  177. package/dist/src/discussions/repository.js +4 -2
  178. package/dist/src/discussions/review.d.ts +8 -0
  179. package/dist/src/discussions/review.js +183 -0
  180. package/dist/src/discussions/sealer.d.ts +7 -1
  181. package/dist/src/discussions/sealer.js +163 -87
  182. package/dist/src/discussions/service.d.ts +7 -1
  183. package/dist/src/discussions/service.js +51 -15
  184. package/dist/src/discussions/types.d.ts +2 -1
  185. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  186. package/dist/src/gateway/global-defaults-admin.d.ts +3 -1
  187. package/dist/src/gateway/global-defaults-admin.js +14 -2
  188. package/dist/src/gateway/hono/lib/config-payload.d.ts +11 -2
  189. package/dist/src/gateway/hono/lib/config-payload.js +2 -0
  190. package/dist/src/gateway/hono/lib/voice-settings-revisions.d.ts +5 -0
  191. package/dist/src/gateway/hono/lib/voice-settings-revisions.js +24 -0
  192. package/dist/src/gateway/hono/routes/commands-skills.js +15 -0
  193. package/dist/src/gateway/hono/routes/config-patch/misc.js +8 -1
  194. package/dist/src/gateway/hono/routes/config.js +12 -0
  195. package/dist/src/gateway/hono/routes/discussions.js +63 -1
  196. package/dist/src/gateway/hono/routes/global-defaults.js +8 -1
  197. package/dist/src/gateway/hono/routes/models.js +12 -4
  198. package/dist/src/gateway/hono/routes/provider-setup.d.ts +3 -0
  199. package/dist/src/gateway/hono/routes/provider-setup.js +106 -0
  200. package/dist/src/gateway/hono/routes/status.js +2 -0
  201. package/dist/src/gateway/hono/routes/voice.js +23 -10
  202. package/dist/src/gateway/provider-model-setup.d.ts +4 -0
  203. package/dist/src/gateway/provider-model-setup.js +56 -0
  204. package/dist/src/gateway/service/config-coordinator.d.ts +2 -2
  205. package/dist/src/gateway/service.js +2 -0
  206. package/dist/src/media-shared/http/provider-http.d.ts +0 -6
  207. package/dist/src/media-shared/http/provider-http.js +5 -4
  208. package/dist/src/media-understanding/runner.d.ts +0 -28
  209. package/dist/src/media-understanding/runner.js +4 -0
  210. package/dist/src/platform/client.d.ts +1 -1
  211. package/dist/src/platform/contracts.d.ts +1 -1
  212. package/dist/src/providers/env-keys.js +3 -0
  213. package/dist/src/providers/extension-stream-bridge.js +3 -0
  214. package/dist/src/providers/index.js +15 -0
  215. package/dist/src/providers/model-call.d.ts +5 -2
  216. package/dist/src/providers/model-call.js +13 -2
  217. package/dist/src/providers/model-reference-auditor.js +1 -0
  218. package/dist/src/providers/model-response.js +1 -1
  219. package/dist/src/providers/model-usage.d.ts +15 -0
  220. package/dist/src/providers/model-usage.js +50 -0
  221. package/dist/src/providers/presentation.js +8 -0
  222. package/dist/src/providers/setup/engine.d.ts +37 -0
  223. package/dist/src/providers/setup/engine.js +177 -0
  224. package/dist/src/providers/setup/profiles.d.ts +4 -0
  225. package/dist/src/providers/setup/profiles.js +78 -0
  226. package/dist/src/providers/setup/voice.d.ts +23 -0
  227. package/dist/src/providers/setup/voice.js +234 -0
  228. package/dist/src/scenes/agentExecutor.d.ts +1 -0
  229. package/dist/src/scenes/agentExecutor.js +5 -0
  230. package/dist/src/storage/sqlite/migrations/253_discussion_processing_recovery.sql +20 -0
  231. package/dist/src/storage/sqlite/migrations/254_discussion_annotations.sql +17 -0
  232. package/dist/src/storage/sqlite/migrations/255_discussion_chapter_progress.sql +10 -0
  233. package/dist/src/storage/sqlite/migrations/256_task_main_update_retries.sql +21 -0
  234. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  235. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  236. package/dist/src/storage/sqlite/schema.js +1 -1
  237. package/dist/src/tasks/task-collaboration-repository.d.ts +2 -0
  238. package/dist/src/tasks/task-collaboration-repository.js +7 -1
  239. package/dist/src/tasks/task-main-update-delivery.js +11 -4
  240. package/dist/src/tasks/task-main-update-input.js +2 -1
  241. package/dist/src/tasks/task-main-update-retry.d.ts +5 -0
  242. package/dist/src/tasks/task-main-update-retry.js +15 -0
  243. package/dist/src/usage/recorder.js +2 -1
  244. package/dist/src/usage/scenario-registry.js +128 -123
  245. package/dist/src/voice/dashscope/streaming-stt-session.js +3 -0
  246. package/dist/src/voice/dashscope/streaming-tts-stream.js +3 -0
  247. package/dist/src/voice/dictation.d.ts +12 -0
  248. package/dist/src/voice/dictation.js +22 -0
  249. package/dist/src/voice/metadata/builtin.js +54 -0
  250. package/dist/src/voice/platform-streams.js +3 -0
  251. package/dist/src/voice/realtime/omniEngine.js +3 -0
  252. package/dist/src/voice/realtime/omniRoute.js +3 -2
  253. package/dist/src/voice/realtime/runtime.js +4 -2
  254. package/dist/src/voice/stt/factory.js +9 -3
  255. package/dist/src/voice/stt/providers/bailian-plan-transcription.d.ts +2 -0
  256. package/dist/src/voice/stt/providers/bailian-plan-transcription.js +32 -0
  257. package/dist/src/voice/stt/providers/index.d.ts +1 -0
  258. package/dist/src/voice/stt/providers/index.js +2 -1
  259. package/dist/src/voice/token-plan.d.ts +8 -0
  260. package/dist/src/voice/token-plan.js +11 -0
  261. package/dist/src/voice/tts/factory.js +4 -1
  262. package/dist/src/voice/tts/providers/bailian-plan-speech.d.ts +2 -0
  263. package/dist/src/voice/tts/providers/bailian-plan-speech.js +84 -0
  264. package/dist/src/voice/tts/providers/index.d.ts +1 -0
  265. package/dist/src/voice/tts/providers/index.js +2 -1
  266. package/dist/src/voice/tts/speak-core.js +10 -1
  267. package/package.json +2 -2
  268. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-Ds0Ue2FB.js +0 -1
  269. package/dist/gateway/static/root/assets/agents-BLRM4o-U.js +0 -77
  270. package/dist/gateway/static/root/assets/app-management-settings-panel-Dmy828Pe.js +0 -1
  271. package/dist/gateway/static/root/assets/automations-workspace-UcCneEs3.js +0 -6
  272. package/dist/gateway/static/root/assets/bot-DjLkhyZL.js +0 -1
  273. package/dist/gateway/static/root/assets/browser-automations-page-BjvQnYfO.js +0 -3
  274. package/dist/gateway/static/root/assets/browser-settings-page-D5ZghI28.js +0 -3
  275. package/dist/gateway/static/root/assets/capabilities-page-MC8qIrXo.js +0 -2
  276. package/dist/gateway/static/root/assets/capabilities-settings-panel-Blz3hEzp.js +0 -8
  277. package/dist/gateway/static/root/assets/channels-DSg_iCVC.js +0 -1
  278. package/dist/gateway/static/root/assets/chat-terminal-dock-C97SJEwd.js +0 -36
  279. package/dist/gateway/static/root/assets/circle-pause-CN9jYH2s.js +0 -1
  280. package/dist/gateway/static/root/assets/clock-3-DnrAnuyZ.js +0 -1
  281. package/dist/gateway/static/root/assets/connectors-page-DXk-GAsd.js +0 -1
  282. package/dist/gateway/static/root/assets/device-pairing-wizard-BeSuq__I.js +0 -1
  283. package/dist/gateway/static/root/assets/extension-settings-page-jYu6qpY2.js +0 -1
  284. package/dist/gateway/static/root/assets/fallback-models-CoyzvDo7.js +0 -1
  285. package/dist/gateway/static/root/assets/gateway-realtime-store-C3vH-Iup.js +0 -1
  286. package/dist/gateway/static/root/assets/gateway-settings-JlT1dlSj.js +0 -1
  287. package/dist/gateway/static/root/assets/git-branch-Dh9uxTot.js +0 -1
  288. package/dist/gateway/static/root/assets/home-page-DOVl8s-1.js +0 -2
  289. package/dist/gateway/static/root/assets/image-generation-api-BZBiYDmV.js +0 -1
  290. package/dist/gateway/static/root/assets/imports-page-DgdBTp_0.js +0 -1
  291. package/dist/gateway/static/root/assets/index-CmC8JkSM.js +0 -260
  292. package/dist/gateway/static/root/assets/index-Dgzc0oBu.css +0 -1
  293. package/dist/gateway/static/root/assets/local-app-workbench-page-BKZfn-MS.js +0 -1
  294. package/dist/gateway/static/root/assets/logs-page-BSepiACn.js +0 -2
  295. package/dist/gateway/static/root/assets/management-settings-dE_ykPNY.js +0 -1
  296. package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-65SE6j-_.js +0 -1
  297. package/dist/gateway/static/root/assets/message-square-fVE7Ghj1.js +0 -1
  298. package/dist/gateway/static/root/assets/messages-DlTNQI92.js +0 -3
  299. package/dist/gateway/static/root/assets/models-hub-cache-C7FAsEe_.js +0 -1
  300. package/dist/gateway/static/root/assets/note-detail-panel-D9oqhdyk.js +0 -5
  301. package/dist/gateway/static/root/assets/notes-page-Bd4_7ph8.js +0 -8
  302. package/dist/gateway/static/root/assets/pause-DuPxgSxk.js +0 -1
  303. package/dist/gateway/static/root/assets/personal-page-BvWzq4Wp.js +0 -1
  304. package/dist/gateway/static/root/assets/play-kxZvpBg8.js +0 -1
  305. package/dist/gateway/static/root/assets/project-detail-page-D-qkIXVT.js +0 -1
  306. package/dist/gateway/static/root/assets/projects-page-BQtw1Dfn.js +0 -1
  307. package/dist/gateway/static/root/assets/remote-access-hub-CYFNz0Jc.js +0 -1
  308. package/dist/gateway/static/root/assets/runtime-tools-settings-panel-Qq_NyYx_.js +0 -1
  309. package/dist/gateway/static/root/assets/scenes-page-hGr-ppYk.js +0 -2
  310. package/dist/gateway/static/root/assets/sessions-page-CpX1Rluw.js +0 -1
  311. package/dist/gateway/static/root/assets/settings-advanced-gate-D2s_MlxR.js +0 -1
  312. package/dist/gateway/static/root/assets/settings-loading-skeleton-BTqvr_Fw.js +0 -1
  313. package/dist/gateway/static/root/assets/setup-status-panel-BiHRYDHq.js +0 -1
  314. package/dist/gateway/static/root/assets/shares-settings-DfcAu08T.js +0 -1
  315. package/dist/gateway/static/root/assets/skills-page-CvKc5nBN.js +0 -2
  316. package/dist/gateway/static/root/assets/sparkles-DHlfQFAF.js +0 -1
  317. package/dist/gateway/static/root/assets/task-detail-page-5B5ZVAu0.js +0 -2
  318. package/dist/gateway/static/root/assets/usage-page-rMuwNpJi.js +0 -1
  319. package/dist/gateway/static/root/assets/user-model-page-Dhqw3v_u.js +0 -2
  320. package/dist/gateway/static/root/assets/workflow-run-setup-panel-pdiRKLxP.js +0 -3
  321. package/dist/gateway/static/root/assets/workflows-page-D9LMxINt.js +0 -6
@@ -0,0 +1,183 @@
1
+ import { getSqliteDatabase, init_transaction, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
2
+ import { DiscussionServiceError } from "./errors.js";
3
+ import { getDiscussionCapture, listDiscussionTranscriptSegments } from "./repository.js";
4
+ import { readTranscriptRevision, saveTranscriptRevision } from "./revisions.js";
5
+ import { pcmEpoch, playbackRanges, pointToPcm, probeMediaEpoch } from "./media-timeline.js";
6
+ import { z } from "zod";
7
+ //#region src/discussions/review.ts
8
+ init_transaction();
9
+ const anchorSchema = z.object({
10
+ type: z.enum(["time", "transcript"]),
11
+ capturedAt: z.number().int().nonnegative(),
12
+ epoch: z.number().int().min(0).max(100).optional(),
13
+ coordinate: z.enum(["encoded", "pcm"]).optional(),
14
+ offsetMs: z.number().min(0).max(8e6).optional(),
15
+ positionMs: z.number().min(0).max(8e6).optional(),
16
+ transcriptRevision: z.number().int().nonnegative().optional(),
17
+ segmentIds: z.array(z.number().int().nonnegative()).min(1).max(100).optional()
18
+ }).strict().refine((anchor) => anchor.type === "transcript" ? anchor.transcriptRevision !== void 0 && !!anchor.segmentIds?.length && anchor.offsetMs === void 0 && anchor.positionMs === void 0 : anchor.transcriptRevision === void 0 && anchor.segmentIds === void 0 && !(anchor.offsetMs !== void 0 && anchor.positionMs !== void 0) && (anchor.offsetMs === void 0 || anchor.epoch !== void 0));
19
+ const inputSchema = z.object({
20
+ id: z.string().regex(/^[a-zA-Z0-9_-]{1,100}$/),
21
+ kind: z.enum([
22
+ "highlight",
23
+ "question",
24
+ "note"
25
+ ]),
26
+ text: z.string().max(4e3),
27
+ anchor: anchorSchema,
28
+ expectedRevision: z.number().int().nonnegative(),
29
+ deleted: z.boolean().optional()
30
+ });
31
+ const map = (row) => ({
32
+ id: row.id,
33
+ kind: row.kind,
34
+ text: row.text,
35
+ anchor: JSON.parse(row.anchor_json),
36
+ revision: row.revision,
37
+ deleted: !!row.deleted,
38
+ createdAt: row.created_at,
39
+ updatedAt: row.updated_at
40
+ });
41
+ const requireCapture = (id) => {
42
+ const capture = getDiscussionCapture(id);
43
+ if (!capture) throw new DiscussionServiceError("not_found", "Discussion not found");
44
+ return capture;
45
+ };
46
+ function listAnnotations(id) {
47
+ requireCapture(id);
48
+ return getSqliteDatabase().prepare("SELECT * FROM discussion_annotations WHERE discussion_id=? ORDER BY created_at,id").all(id).map(map).sort((a, b) => a.anchor.capturedAt - b.anchor.capturedAt || a.id.localeCompare(b.id));
49
+ }
50
+ function putAnnotation(id, value) {
51
+ const parsed = inputSchema.safeParse(value);
52
+ if (!parsed.success) throw new DiscussionServiceError("invalid_input", "Invalid annotation");
53
+ const input = parsed.data;
54
+ return runSqliteWriteTransaction((db) => {
55
+ const capture = requireCapture(id);
56
+ if (input.anchor.type === "transcript") {
57
+ if (input.anchor.transcriptRevision === capture.transcriptRevision) saveTranscriptRevision(id);
58
+ const segments = readTranscriptRevision(id, input.anchor.transcriptRevision);
59
+ if (!segments || input.anchor.segmentIds.some((sequence) => !segments.some((segment) => segment.sequence === sequence && segment.status === "confirmed"))) throw new DiscussionServiceError("conflict", "Transcript evidence is no longer available");
60
+ }
61
+ const row = db.prepare("SELECT * FROM discussion_annotations WHERE discussion_id=? AND id=?").get(id, input.id);
62
+ const current = row ? map(row) : void 0;
63
+ const anchor = JSON.stringify(input.anchor);
64
+ if (current && current.kind === input.kind && current.text === input.text && current.deleted === !!input.deleted && JSON.stringify(current.anchor) === anchor) return current;
65
+ if ((current?.revision ?? 0) !== input.expectedRevision) throw new DiscussionServiceError("conflict", "Annotation changed; reload before saving");
66
+ if (current && JSON.stringify(current.anchor) !== anchor) {
67
+ const previous = current.anchor;
68
+ if (!(previous.type === "time" && previous.offsetMs === void 0 && previous.positionMs === void 0 && input.anchor.type === "time" && previous.capturedAt === input.anchor.capturedAt && previous.epoch === input.anchor.epoch && previous.coordinate === input.anchor.coordinate)) throw new DiscussionServiceError("conflict", "Original annotation anchor is immutable");
69
+ }
70
+ if (!current && Number(db.prepare("SELECT COUNT(*) n FROM discussion_annotations WHERE discussion_id=?").get(id).n) >= 1e3) throw new DiscussionServiceError("invalid_input", "Annotation limit reached");
71
+ const now = Date.now();
72
+ const revision = (current?.revision ?? 0) + 1;
73
+ db.prepare(`INSERT INTO discussion_annotations(discussion_id,id,kind,text,anchor_json,revision,deleted,created_at,updated_at)
74
+ VALUES (?,?,?,?,?,?,?,?,?) ON CONFLICT(discussion_id,id) DO UPDATE SET kind=excluded.kind,text=excluded.text,anchor_json=excluded.anchor_json,revision=excluded.revision,deleted=excluded.deleted,updated_at=excluded.updated_at`).run(id, input.id, input.kind, input.text, anchor, revision, input.deleted ? 1 : 0, current?.createdAt ?? now, now);
75
+ return {
76
+ id: input.id,
77
+ kind: input.kind,
78
+ text: input.text,
79
+ anchor: input.anchor,
80
+ revision,
81
+ deleted: !!input.deleted,
82
+ createdAt: current?.createdAt ?? now,
83
+ updatedAt: now
84
+ };
85
+ });
86
+ }
87
+ const timingAttempts = /* @__PURE__ */ new Map();
88
+ const timingInFlight = /* @__PURE__ */ new Map();
89
+ async function recordingTimeline(id, notes) {
90
+ const capture = requireCapture(id);
91
+ if (!capture.audioAttachmentId || capture.audioDeletedAt || !capture.durationMs) return;
92
+ const key = `${id}:${capture.audioSha256}`;
93
+ if (timingInFlight.has(key)) return timingInFlight.get(key);
94
+ const cached = getSqliteDatabase().prepare("SELECT timeline_json FROM discussion_media_timeline WHERE discussion_id=? AND audio_sha256=?").get(id, capture.audioSha256 ?? "");
95
+ let previous;
96
+ try {
97
+ previous = cached ? JSON.parse(cached.timeline_json) : void 0;
98
+ } catch {}
99
+ if (previous && Array.isArray(previous.epochs) && (previous.primary === "pcm" || previous.epochs.every((epoch) => epoch.verified))) return previous;
100
+ if (capture.mimeType?.includes("wav")) return {
101
+ primary: "pcm",
102
+ epochs: [pcmEpoch(capture.durationMs)]
103
+ };
104
+ if (previous && Date.now() - (timingAttempts.get(key) ?? 0) < 3e4) return previous;
105
+ timingAttempts.set(key, Date.now());
106
+ if (timingAttempts.size > 256) timingAttempts.delete(timingAttempts.keys().next().value);
107
+ const pending = (async () => {
108
+ const attachment = await notes.getAttachmentPath(capture.noteId, capture.audioAttachmentId);
109
+ if (!attachment) return;
110
+ const timeline = {
111
+ primary: "encoded",
112
+ epochs: [await probeMediaEpoch(attachment.filePath, capture.durationMs, 0, AbortSignal.timeout(35e3))]
113
+ };
114
+ if (getDiscussionCapture(id)?.audioSha256 === capture.audioSha256) getSqliteDatabase().prepare("INSERT OR REPLACE INTO discussion_media_timeline(discussion_id,audio_sha256,timeline_json) VALUES(?,?,?)").run(id, capture.audioSha256 ?? "", JSON.stringify(timeline));
115
+ return timeline;
116
+ })().finally(() => timingInFlight.delete(key));
117
+ timingInFlight.set(key, pending);
118
+ return pending;
119
+ }
120
+ async function evidenceFor(id, revision, ids, notes) {
121
+ const capture = requireCapture(id);
122
+ if (!Number.isSafeInteger(revision) || revision < 0 || !ids.length || ids.length > 100 || ids.some((id) => !Number.isSafeInteger(id) || id < 0)) throw new DiscussionServiceError("invalid_input", "Invalid evidence reference");
123
+ if (capture.transcriptRevision === revision) saveTranscriptRevision(id);
124
+ const source = readTranscriptRevision(id, revision);
125
+ if (!source || ids.some((id) => !source.some((segment) => segment.sequence === id && segment.status === "confirmed"))) throw new DiscussionServiceError("not_found", "Referenced transcript revision or segment is unavailable");
126
+ const attachment = capture.audioAttachmentId && !capture.audioDeletedAt ? await notes.getAttachmentPath(capture.noteId, capture.audioAttachmentId) : null;
127
+ const timeline = attachment ? await recordingTimeline(id, notes) : void 0;
128
+ return {
129
+ transcriptRevision: revision,
130
+ stale: revision !== getDiscussionCapture(id)?.transcriptRevision,
131
+ audioAvailable: !!attachment,
132
+ timingVerified: !!timeline && (timeline.primary === "pcm" || timeline.epochs.every((epoch) => epoch.verified)),
133
+ segments: source.filter((segment) => ids.includes(segment.sequence)).map((segment) => ({
134
+ sequence: segment.sequence,
135
+ startedAtMs: segment.startedAtMs,
136
+ endedAtMs: segment.endedAtMs,
137
+ text: segment.displayText ?? segment.rawText ?? "",
138
+ speakerLabel: segment.speakerLabel,
139
+ playback: timeline ? playbackRanges(timeline, segment.startedAtMs, segment.endedAtMs) : []
140
+ }))
141
+ };
142
+ }
143
+ async function resolvedAnnotations(id, notes) {
144
+ const capture = requireCapture(id);
145
+ const annotations = listAnnotations(id);
146
+ const timeline = await recordingTimeline(id, notes);
147
+ const current = listDiscussionTranscriptSegments(id).filter((segment) => segment.status === "confirmed");
148
+ return Promise.all(annotations.map(async (annotation) => {
149
+ const anchor = annotation.anchor;
150
+ if (annotation.deleted) return {
151
+ annotation,
152
+ state: "unavailable",
153
+ playback: []
154
+ };
155
+ if (anchor.type === "transcript") {
156
+ const evidence = await evidenceFor(id, anchor.transcriptRevision, anchor.segmentIds, notes).catch(() => void 0);
157
+ return {
158
+ annotation,
159
+ state: evidence ? "located" : "unavailable",
160
+ evidence,
161
+ playback: evidence?.segments.flatMap((segment) => segment.playback) ?? []
162
+ };
163
+ }
164
+ const epoch = timeline?.epochs[anchor.epoch ?? 0];
165
+ const position = anchor.positionMs ?? (epoch && anchor.offsetMs !== void 0 ? anchor.coordinate === "pcm" ? anchor.offsetMs < epoch.durationMs ? epoch.baseMs + anchor.offsetMs : void 0 : pointToPcm(epoch, anchor.offsetMs) : void 0);
166
+ if (!(!capture.audioDeletedAt && !!capture.audioAttachmentId && position !== void 0 && position >= 0 && position < (capture.durationMs ?? 0))) return {
167
+ annotation,
168
+ state: !capture.audioAttachmentId && !capture.audioDeletedAt ? "pending" : "unavailable",
169
+ playback: []
170
+ };
171
+ const refs = current.filter((segment) => segment.startedAtMs <= position && segment.endedAtMs > position).map((segment) => segment.sequence);
172
+ const evidence = refs.length ? await evidenceFor(id, capture.transcriptRevision, refs, notes) : void 0;
173
+ return {
174
+ annotation,
175
+ state: "located",
176
+ positionMs: position,
177
+ playback: timeline ? playbackRanges(timeline, Math.max(0, position - 2e3), Math.min(capture.durationMs, position + 8e3)) : [],
178
+ evidence
179
+ };
180
+ }));
181
+ }
182
+ //#endregion
183
+ export { evidenceFor, listAnnotations, putAnnotation, recordingTimeline, resolvedAnnotations };
@@ -1,5 +1,6 @@
1
1
  import type { Config } from '../config/schema.js';
2
2
  import type { NotesService } from '../notes/service.js';
3
+ import type { STTResult } from '../voice/stt/types.js';
3
4
  import type { DiscussionCapture } from './types.js';
4
5
  export interface DiscussionSealerDeps {
5
6
  processRecordingJob?: () => Promise<void>;
@@ -9,18 +10,23 @@ export interface DiscussionSealerDeps {
9
10
  text: string;
10
11
  language?: string;
11
12
  }>;
13
+ transcribeSegment?: (audio: Buffer, signal: AbortSignal) => Promise<STTResult>;
14
+ requestTimeoutMs?: number;
12
15
  onUpdated?: (capture: DiscussionCapture) => void;
13
16
  }
14
17
  export declare class DiscussionSealer {
15
18
  private readonly deps;
16
19
  private readonly intervalMs;
20
+ private readonly owner;
17
21
  private timer?;
18
22
  private running;
23
+ private recordingRunning;
24
+ private stopping;
25
+ private controller?;
19
26
  constructor(deps: DiscussionSealerDeps, intervalMs?: number);
20
27
  start(): void;
21
28
  stop(): Promise<void>;
22
29
  tick(now?: number): Promise<void>;
23
30
  private sealWhenReady;
24
31
  private transcribeFullRecording;
25
- private fail;
26
32
  }
@@ -1,113 +1,189 @@
1
1
  import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
+ import { init_transaction, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
4
+ import { DISCUSSION_MAX_SAVED_DURATION_MS } from "../../packages/gateway-contract/src/discussions.js";
5
+ import { init_src } from "../../packages/gateway-contract/src/index.js";
6
+ import { init_model_usage, withModelUsageOrigin } from "../providers/model-usage.js";
3
7
  import { transcribe } from "../voice/stt/transcribe-core.js";
4
8
  import { isSTTAvailable } from "../voice/stt/availability.js";
5
9
  import { mergeSttConfigFromAppConfig } from "../voice/stt/config.js";
6
10
  import "../voice/stt/index.js";
7
- import { getDiscussionCapture, listDiscussionCaptures, listDiscussionTranscriptSegments, updateDiscussionCapture } from "./repository.js";
8
- import { assembleDiscussionTranscript } from "./transcript.js";
11
+ import { listDiscussionTranscriptSegments, updateDiscussionCapture } from "./repository.js";
12
+ import { appendTranscriptWithoutOverlap, assembleDiscussionTranscript } from "./transcript.js";
9
13
  import { replaceTranscriptSegments, saveTranscriptRevision } from "./revisions.js";
14
+ import { assertProcessingRun, claimSealingCapture, ensureProcessingRun, failOrRetryProcessing, getProcessingRun, readCheckpoint, releaseSealingLease, renewSealingLease, resetProcessingRetry, saveCheckpoint, withProcessingDeadline } from "./processing.js";
10
15
  import { missingAudioRanges, slicePcmWav } from "./audio-repair.js";
11
16
  import { decodeWavToMonoFloat32 } from "../voice/audio/wav.js";
12
17
  import { forEachNormalizedAudioSegment } from "../voice/audio/normalize.js";
13
18
  import { getRecordingJob } from "./recordingJobs.js";
14
- import { createHash } from "node:crypto";
19
+ import { createReadStream } from "node:fs";
20
+ import { createHash, randomUUID } from "node:crypto";
15
21
  //#region src/discussions/sealer.ts
22
+ init_src();
23
+ init_model_usage();
24
+ init_transaction();
16
25
  init_logger();
17
26
  const log = createLogger("DiscussionSealer");
18
27
  const UPLOAD_GRACE_MS = 2 * 6e4;
19
28
  var DiscussionSealer = class {
29
+ owner = randomUUID();
20
30
  timer;
21
31
  running = false;
32
+ recordingRunning = false;
33
+ stopping = false;
34
+ controller;
22
35
  constructor(deps, intervalMs = 1e3) {
23
36
  this.deps = deps;
24
37
  this.intervalMs = intervalMs;
25
38
  }
26
39
  start() {
27
40
  if (this.timer) return;
28
- this.timer = setInterval(() => void this.tick(), this.intervalMs);
41
+ this.stopping = false;
42
+ this.timer = setInterval(() => void this.tick().catch((error) => log.error({ err: error }, "Discussion processing tick failed")), this.intervalMs);
29
43
  this.timer.unref();
30
- this.tick();
44
+ this.tick().catch((error) => log.error({ err: error }, "Discussion processing tick failed"));
31
45
  }
32
46
  async stop() {
47
+ this.stopping = true;
33
48
  if (this.timer) clearInterval(this.timer);
34
49
  this.timer = void 0;
35
- while (this.running) await new Promise((resolve) => setTimeout(resolve, 10));
50
+ this.controller?.abort(/* @__PURE__ */ new Error("Discussion processing stopped"));
51
+ while (this.running || this.recordingRunning) await new Promise((resolve) => setTimeout(resolve, 10));
36
52
  }
37
53
  async tick(now = Date.now()) {
38
- if (this.running) return;
54
+ if (this.stopping) return;
55
+ if (!this.recordingRunning) {
56
+ this.recordingRunning = true;
57
+ try {
58
+ await this.deps.processRecordingJob?.();
59
+ } finally {
60
+ this.recordingRunning = false;
61
+ }
62
+ }
63
+ if (this.running || this.stopping) return;
39
64
  this.running = true;
65
+ const controller = new AbortController();
66
+ this.controller = controller;
67
+ let capture = null;
68
+ let renewal;
40
69
  try {
41
- await this.deps.processRecordingJob?.();
42
- const capture = [...listDiscussionCaptures({
43
- status: "sealing",
44
- limit: 1
45
- }).items, ...listDiscussionCaptures({
46
- status: "stopping",
47
- limit: 1
48
- }).items][0];
70
+ capture = claimSealingCapture(this.owner, now);
49
71
  if (!capture) return;
50
- await this.sealWhenReady(capture, now);
72
+ const id = capture.id;
73
+ renewal = setInterval(() => {
74
+ if (!renewSealingLease(id, this.owner)) controller.abort(/* @__PURE__ */ new Error("Transcription lease lost"));
75
+ }, 15e3);
76
+ renewal.unref();
77
+ await this.sealWhenReady(capture, now, controller.signal);
51
78
  } finally {
79
+ if (renewal) clearInterval(renewal);
80
+ if (capture) releaseSealingLease(capture.id, this.owner);
81
+ this.controller = void 0;
52
82
  this.running = false;
53
83
  }
54
84
  }
55
- async sealWhenReady(capture, now) {
85
+ async sealWhenReady(capture, now, signal) {
56
86
  const ageMs = now - (capture.recordingStoppedAt ?? now);
57
87
  if (!capture.audioAttachmentId) {
58
88
  const job = getRecordingJob(capture.id);
59
89
  if (job && (job.state === "queued" || job.state === "running")) return;
60
- if (ageMs >= UPLOAD_GRACE_MS) this.fail(capture, "audio_upload", "recording_missing", "Original recording was not uploaded");
90
+ if (ageMs >= UPLOAD_GRACE_MS) {
91
+ const updated = updateDiscussionCapture(capture.id, {
92
+ status: "needs_attention",
93
+ failureStage: "audio_upload",
94
+ failureCode: "recording_missing",
95
+ failureMessage: "Original recording was not uploaded"
96
+ }, ["stopping", "sealing"]);
97
+ if (updated) this.deps.onUpdated?.(updated);
98
+ }
99
+ if (ageMs < UPLOAD_GRACE_MS) updateDiscussionCapture(capture.id, { updatedAt: Date.now() }, [capture.status]);
61
100
  return;
62
101
  }
63
102
  const segments = listDiscussionTranscriptSegments(capture.id);
64
103
  const expected = capture.expectedLastSequence ?? -1;
65
- const bySequence = new Map(segments.map((segment) => [segment.sequence, segment]));
66
- const missing = Array.from({ length: expected + 1 }, (_, sequence) => sequence).filter((sequence) => !bySequence.has(sequence));
104
+ const missing = expected >= 0 && Array.from({ length: expected + 1 }, (_, sequence) => sequence).some((sequence) => !segments.some((segment) => segment.sequence === sequence));
67
105
  const pending = segments.some((segment) => segment.status === "uploaded" || segment.status === "transcribing");
68
- if ((missing.length > 0 || pending) && ageMs < UPLOAD_GRACE_MS) return;
106
+ const run = ensureProcessingRun(capture);
107
+ if (!run.force_audio && (missing || pending) && ageMs < UPLOAD_GRACE_MS) {
108
+ updateDiscussionCapture(capture.id, { updatedAt: Date.now() }, [capture.status]);
109
+ return;
110
+ }
69
111
  const sealing = capture.status === "sealing" ? capture : updateDiscussionCapture(capture.id, { status: "sealing" }, ["stopping"]);
70
112
  if (!sealing) return;
71
113
  try {
72
114
  const confirmed = segments.filter((segment) => segment.status === "confirmed");
73
- const result = expected >= 0 && missing.length === 0 && !pending && confirmed.length === expected + 1 ? { text: assembleDiscussionTranscript(confirmed) } : await this.transcribeFullRecording(sealing);
115
+ const result = !run.force_audio && expected >= 0 && !missing && !pending && confirmed.length === expected + 1 ? { text: assembleDiscussionTranscript(confirmed) } : await withModelUsageOrigin("interactive", () => this.transcribeFullRecording(sealing, run, signal));
74
116
  const text = result.text.trim();
75
- saveTranscriptRevision(capture.id);
76
117
  if (!text) throw new Error("Discussion transcription produced no text");
77
- const updated = updateDiscussionCapture(capture.id, {
78
- status: "organizing",
79
- canonicalTranscript: text,
80
- canonicalTranscriptSha256: createHash("sha256").update(text).digest("hex"),
81
- transcriptLanguage: result.language,
82
- failureStage: void 0,
83
- failureCode: void 0,
84
- failureMessage: void 0
85
- }, ["sealing"]);
86
- if (!updated) throw new Error("Discussion changed while sealing transcript");
87
- this.deps.onUpdated?.(updated);
118
+ const updated = runSqliteWriteTransaction(() => {
119
+ assertProcessingRun(run, this.owner, signal);
120
+ if (result.segments) replaceTranscriptSegments(capture.id, result.segments);
121
+ saveTranscriptRevision(capture.id);
122
+ resetProcessingRetry(capture.id);
123
+ return updateDiscussionCapture(capture.id, {
124
+ status: "organizing",
125
+ canonicalTranscript: text,
126
+ canonicalTranscriptSha256: createHash("sha256").update(text).digest("hex"),
127
+ transcriptLanguage: result.language,
128
+ failureStage: void 0,
129
+ failureCode: void 0,
130
+ failureMessage: void 0
131
+ }, ["sealing"]);
132
+ });
133
+ if (updated) this.deps.onUpdated?.(updated);
88
134
  } catch (error) {
89
- const message = error instanceof Error ? error.message : String(error);
90
- this.fail(sealing, "transcript_sealing", "transcript_sealing_failed", message);
135
+ if (signal.aborted) return;
136
+ if (getProcessingRun(capture.id)?.run_id !== run.run_id || !renewSealingLease(capture.id, this.owner)) return;
137
+ const updated = failOrRetryProcessing(sealing, "transcript_sealing", error);
138
+ if (updated) this.deps.onUpdated?.(updated);
139
+ log.warn({
140
+ err: error,
141
+ discussionId: capture.id,
142
+ phase: "transcript_sealing"
143
+ }, "Discussion transcription failed; saved original retained");
91
144
  }
92
145
  }
93
- async transcribeFullRecording(capture) {
146
+ async transcribeFullRecording(capture, run, signal) {
94
147
  const attachment = await this.deps.notes.getAttachmentPath(capture.noteId, capture.audioAttachmentId);
95
148
  if (!attachment) throw new Error("Discussion audio file is missing");
149
+ const assertCurrent = () => assertProcessingRun(run, this.owner, signal);
150
+ assertCurrent();
151
+ if (capture.audioSha256) {
152
+ const hash = createHash("sha256");
153
+ for await (const chunk of createReadStream(attachment.filePath)) {
154
+ signal.throwIfAborted();
155
+ hash.update(chunk);
156
+ }
157
+ if (hash.digest("hex") !== capture.audioSha256) throw new Error("Saved original audio checksum mismatch");
158
+ }
159
+ assertCurrent();
96
160
  if (this.deps.transcribeRecording) {
97
- const result = await this.deps.transcribeRecording(attachment.filePath, capture);
98
- replaceTranscriptSegments(capture.id, [{
99
- text: result.text,
100
- startedAtMs: 0,
101
- endedAtMs: capture.durationMs ?? 1e3
102
- }]);
103
- return result;
161
+ if (listDiscussionTranscriptSegments(capture.id).some((segment) => segment.correctedByUser)) throw new Error("Segment transcription is required to preserve manual corrections");
162
+ const cached = readCheckpoint(run.run_id, 0, capture.durationMs ?? 1e3);
163
+ const result = cached ?? await withProcessingDeadline(async (requestSignal) => {
164
+ const response = await this.deps.transcribeRecording(attachment.filePath, capture, requestSignal);
165
+ return {
166
+ segments: [{
167
+ text: response.text,
168
+ startedAtMs: 0,
169
+ endedAtMs: capture.durationMs ?? 1e3
170
+ }],
171
+ language: response.language
172
+ };
173
+ }, signal, this.deps.requestTimeoutMs ?? 12e4);
174
+ assertCurrent();
175
+ if (!cached) saveCheckpoint(run.run_id, 0, capture.durationMs ?? 1e3, result);
176
+ return {
177
+ text: result.segments.map((segment) => segment.text).join("\n"),
178
+ language: result.language,
179
+ segments: result.segments
180
+ };
104
181
  }
105
182
  const config = this.deps.getConfig();
106
183
  const sttConfig = mergeSttConfigFromAppConfig(config.tools?.media?.audio, config.tools?.media);
107
- if (!isSTTAvailable(sttConfig)) throw new Error("STT is not configured");
108
- let offset = 0;
109
- const confirmed = listDiscussionTranscriptSegments(capture.id).filter((segment) => segment.status === "confirmed");
110
- const segments = confirmed.map((segment) => ({
184
+ if (!this.deps.transcribeSegment && !isSTTAvailable(sttConfig)) throw new Error("STT is not configured");
185
+ const confirmed = listDiscussionTranscriptSegments(capture.id).filter((segment) => segment.status === "confirmed" && (!run.force_audio || segment.correctedByUser));
186
+ const output = confirmed.map((segment) => ({
111
187
  text: segment.displayText ?? segment.rawText ?? "",
112
188
  rawText: segment.rawText,
113
189
  startedAtMs: segment.startedAtMs,
@@ -115,61 +191,61 @@ var DiscussionSealer = class {
115
191
  speakerLabel: segment.speakerLabel,
116
192
  correctedByUser: segment.correctedByUser
117
193
  }));
194
+ let offset = 0;
118
195
  let language;
119
196
  await forEachNormalizedAudioSegment({
120
197
  filePath: attachment.filePath,
121
- maxDurationSeconds: 7200
198
+ maxDurationSeconds: DISCUSSION_MAX_SAVED_DURATION_MS / 1e3,
199
+ signal
122
200
  }, async (buffer, index) => {
201
+ assertCurrent();
123
202
  const decoded = decodeWavToMonoFloat32(buffer);
124
203
  const durationMs = decoded.durationSeconds * 1e3;
125
204
  const gaps = missingAudioRanges(offset, offset + durationMs, confirmed);
126
205
  for (const [gapIndex, gap] of gaps.entries()) {
127
- const audio = gaps.length === 1 && gap.startedAtMs === offset && gap.endedAtMs === offset + durationMs ? buffer : slicePcmWav(decoded, gap.startedAtMs - offset, gap.endedAtMs - offset);
128
- if (audio.length <= 44) continue;
129
- const result = await transcribe(audio, sttConfig, {
130
- mime: "audio/wav",
131
- fileName: `discussion-recovery-${index}-${gapIndex}.wav`
132
- });
133
- if (result.segments?.length) for (const part of result.segments) {
134
- if (!Number.isFinite(part.startMs) || !Number.isFinite(part.endMs) || part.startMs < 0 || part.endMs <= part.startMs || part.endMs > gap.endedAtMs - gap.startedAtMs + 100) throw new Error("Provider returned an invalid transcript time range");
135
- segments.push({
136
- text: part.text,
137
- startedAtMs: gap.startedAtMs + part.startMs,
138
- endedAtMs: Math.min(gap.endedAtMs, gap.startedAtMs + part.endMs),
139
- ...part.speaker ? { speakerLabel: `${index + 1}:${gapIndex + 1}:${part.speaker}` } : {}
206
+ assertCurrent();
207
+ let checkpoint = readCheckpoint(run.run_id, gap.startedAtMs, gap.endedAtMs);
208
+ if (!checkpoint) {
209
+ const audio = gaps.length === 1 && gap.startedAtMs === offset && gap.endedAtMs === offset + durationMs ? buffer : slicePcmWav(decoded, gap.startedAtMs - offset, gap.endedAtMs - offset);
210
+ if (audio.length <= 44) continue;
211
+ const result = await withProcessingDeadline((requestSignal) => this.deps.transcribeSegment ? this.deps.transcribeSegment(audio, requestSignal) : transcribe(audio, sttConfig, {
212
+ mime: "audio/wav",
213
+ fileName: `discussion-recovery-${index}-${gapIndex}.wav`,
214
+ signal: requestSignal
215
+ }), signal, this.deps.requestTimeoutMs ?? 12e4);
216
+ const recovered = [];
217
+ if (result.segments?.length) for (const part of result.segments) {
218
+ if (!Number.isFinite(part.startMs) || !Number.isFinite(part.endMs) || part.startMs < 0 || part.endMs <= part.startMs || part.endMs > gap.endedAtMs - gap.startedAtMs + 100) throw new Error("Provider returned an invalid transcript time range");
219
+ recovered.push({
220
+ text: part.text,
221
+ startedAtMs: gap.startedAtMs + part.startMs,
222
+ endedAtMs: Math.min(gap.endedAtMs, gap.startedAtMs + part.endMs),
223
+ ...part.speaker ? { speakerLabel: `${index + 1}:${gapIndex + 1}:${part.speaker}` } : {}
224
+ });
225
+ }
226
+ else if (result.text.trim()) recovered.push({
227
+ text: result.text,
228
+ ...gap
140
229
  });
230
+ checkpoint = {
231
+ segments: recovered,
232
+ language: result.language
233
+ };
234
+ assertCurrent();
235
+ saveCheckpoint(run.run_id, gap.startedAtMs, gap.endedAtMs, checkpoint);
141
236
  }
142
- else if (result.text.trim()) segments.push({
143
- text: result.text,
144
- ...gap
145
- });
146
- language ??= result.language;
237
+ output.push(...checkpoint.segments);
238
+ language ??= checkpoint.language;
147
239
  }
148
240
  offset += durationMs;
149
241
  });
150
- segments.sort((a, b) => a.startedAtMs - b.startedAtMs);
151
- replaceTranscriptSegments(capture.id, segments);
242
+ output.sort((a, b) => a.startedAtMs - b.startedAtMs);
152
243
  return {
153
- text: assembleDiscussionTranscript(listDiscussionTranscriptSegments(capture.id)),
154
- ...language ? { language } : {}
244
+ text: output.reduce((text, segment) => appendTranscriptWithoutOverlap(text, segment.text), ""),
245
+ language,
246
+ segments: output
155
247
  };
156
248
  }
157
- fail(capture, stage, code, message) {
158
- const current = getDiscussionCapture(capture.id);
159
- if (!current || current.status !== "stopping" && current.status !== "sealing") return;
160
- const updated = updateDiscussionCapture(capture.id, {
161
- status: "needs_attention",
162
- failureStage: stage,
163
- failureCode: code,
164
- failureMessage: message.slice(0, 1e3)
165
- }, [current.status]);
166
- if (updated) this.deps.onUpdated?.(updated);
167
- log.warn({
168
- discussionId: capture.id,
169
- stage,
170
- code
171
- }, `Discussion sealing failed: ${message}`);
172
- }
173
249
  };
174
250
  //#endregion
175
251
  export { DiscussionSealer };
@@ -1,3 +1,4 @@
1
+ import { longRecordingReview } from './long-review.js';
1
2
  import { type DiscussionRecordingManifest } from '@xopcai/gateway-contract';
2
3
  import type { NotesService } from '../notes/service.js';
3
4
  import type { ProjectService } from '../projects/project-service.js';
@@ -15,6 +16,10 @@ export declare class DiscussionService {
15
16
  private readonly recordingOwner;
16
17
  private readonly recordingControllers;
17
18
  constructor(notes: NotesService, projects: ProjectService, emit?: (type: string, payload: unknown) => void);
19
+ audioClip(id: string, start: number, end: number, signal: AbortSignal): Promise<Buffer<ArrayBufferLike>>;
20
+ annotations(id: string): Promise<import("@xopcai/gateway-contract").DiscussionResolvedAnnotation[]>;
21
+ saveAnnotation(id: string, input: import('@xopcai/gateway-contract').DiscussionAnnotationInput): import("@xopcai/gateway-contract").DiscussionAnnotation;
22
+ evidence(id: string, revision: number, ids: number[]): Promise<import("@xopcai/gateway-contract").DiscussionEvidence>;
18
23
  settings(): DiscussionCaptureSettings;
19
24
  acknowledgeConsent(policyVersion: number): DiscussionCaptureSettings;
20
25
  create(input: CreateDiscussionInput): Promise<DiscussionDetail>;
@@ -44,10 +49,11 @@ export declare class DiscussionService {
44
49
  sealRecording(id: string, input: DiscussionRecordingManifest): Promise<import("@xopcai/gateway-contract").DiscussionRecordingJob>;
45
50
  /** Runs inside the existing sealer lifecycle; unfinished leases survive process restart. */
46
51
  processRecordingJob(): Promise<void>;
47
- retry(id: string): Promise<DiscussionDetail | null>;
52
+ retry(id: string, mode?: import('./types.js').DiscussionRetryMode): Promise<DiscussionDetail | null>;
48
53
  cancel(id: string): Promise<DiscussionDetail | null>;
49
54
  deleteAudio(id: string): Promise<DiscussionDetail | null>;
50
55
  unlinkInferredProject(id: string): Promise<DiscussionDetail | null>;
56
+ review(id: string, options?: Parameters<typeof longRecordingReview>[2]): Promise<import("@xopcai/gateway-contract").DiscussionReview>;
51
57
  private detail;
52
58
  private enqueueMutation;
53
59
  }