@xopcai/xopc 0.0.373 → 0.0.375

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 (323) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  3. package/dist/extensions/telegram/xopc.extension.json +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear--kDkSTqC.js → agent-avatar-dicebear-B2Popl59.js} +1 -1
  5. package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-z4_2-Kn2.js → agent-defaults-settings-panel-BnF3HrHY.js} +1 -1
  6. package/dist/gateway/static/root/assets/{agent-plugin-dialog-BYndK83K.js → agent-plugin-dialog-CwP6OrUs.js} +1 -1
  7. package/dist/gateway/static/root/assets/{agents-CVo96cx1.js → agents-n3IremKl.js} +3 -3
  8. package/dist/gateway/static/root/assets/{app-management-settings-panel-D9oOJKiH.js → app-management-settings-panel-DNkXIPxK.js} +1 -1
  9. package/dist/gateway/static/root/assets/{appearance-settings-B0-8Cdyv.js → appearance-settings-p1_NpH8a.js} +1 -1
  10. package/dist/gateway/static/root/assets/{apps-page-DasVc6i4.js → apps-page-CP2u5iEh.js} +1 -1
  11. package/dist/gateway/static/root/assets/{automations-page-BlCNqy2i.js → automations-page-CjYiGOov.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automations-workspace-C8ipDP05.js → automations-workspace-BAHiUQbe.js} +1 -1
  13. package/dist/gateway/static/root/assets/{block-editor-Cktlez6g.js → block-editor-DiOou1HO.js} +1 -1
  14. package/dist/gateway/static/root/assets/{browser-automations-page-KRfDl8ej.js → browser-automations-page-nArl2uwX.js} +1 -1
  15. package/dist/gateway/static/root/assets/{browser-settings-page-BraQ4wnh.js → browser-settings-page-Bd8tF-gg.js} +1 -1
  16. package/dist/gateway/static/root/assets/{capabilities-page-B3m8xGzd.js → capabilities-page-Cx4cdgse.js} +2 -2
  17. package/dist/gateway/static/root/assets/{capabilities-settings-panel-VQ4fHuBV.js → capabilities-settings-panel-DpWceNd0.js} +1 -1
  18. package/dist/gateway/static/root/assets/{capability-header-actions-f5Ido4Eg.js → capability-header-actions-AeBubNQK.js} +1 -1
  19. package/dist/gateway/static/root/assets/{channels-N4Vc2nEG.js → channels-Cd1Rf0Vk.js} +1 -1
  20. package/dist/gateway/static/root/assets/{chat-terminal-dock-BwvTKwsn.js → chat-terminal-dock-DrVlbq_P.js} +1 -1
  21. package/dist/gateway/static/root/assets/{computer-settings-page-CAwUsKpw.js → computer-settings-page-TUzZnurj.js} +1 -1
  22. package/dist/gateway/static/root/assets/{connector-service-page-C0HuAMuH.js → connector-service-page-BlpwjGW7.js} +1 -1
  23. package/dist/gateway/static/root/assets/{connectors-page-BcBmW8JT.js → connectors-page-j8VFMD4o.js} +1 -1
  24. package/dist/gateway/static/root/assets/{date-picker-DiQxRpe-.js → date-picker-CjKRpA8X.js} +1 -1
  25. package/dist/gateway/static/root/assets/{dependency-picker-Ds0_BJbN.js → dependency-picker-J9_0obij.js} +1 -1
  26. package/dist/gateway/static/root/assets/{desktop-pet-ZMEOYwrR.js → desktop-pet-DySwgreF.js} +1 -1
  27. package/dist/gateway/static/root/assets/{desktop-pet-settings-DkyTqD07.js → desktop-pet-settings-B1-oyKBG.js} +1 -1
  28. package/dist/gateway/static/root/assets/{device-pairing-wizard-CvaI5ZJZ.js → device-pairing-wizard-C5rjPAyc.js} +1 -1
  29. package/dist/gateway/static/root/assets/{directory-picker-path-field-Bfv93eK1.js → directory-picker-path-field-BY7MwiZd.js} +1 -1
  30. package/dist/gateway/static/root/assets/{extension-page-JXKGtIdM.js → extension-page-JoSFhfb6.js} +1 -1
  31. package/dist/gateway/static/root/assets/{extension-settings-page-lsQ2xvbr.js → extension-settings-page-B7LtTNx7.js} +1 -1
  32. package/dist/gateway/static/root/assets/{fallback-models-7bG0AND1.js → fallback-models-DKSWcBt_.js} +1 -1
  33. package/dist/gateway/static/root/assets/{gateway-settings-BpbcoPx9.js → gateway-settings-CGm-lbsX.js} +1 -1
  34. package/dist/gateway/static/root/assets/{home-page-BxctC0aJ.js → home-page-CX3uWkyH.js} +1 -1
  35. package/dist/gateway/static/root/assets/{imports-page-Bq8CazCb.js → imports-page-DS_RFtWf.js} +1 -1
  36. package/dist/gateway/static/root/assets/{index-B40-KDN8.js → index-CgvOIDy_.js} +6 -6
  37. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-kkAevkl2.js → keyboard-shortcuts-settings-BxwXR2qA.js} +1 -1
  38. package/dist/gateway/static/root/assets/{local-app-workbench-page-abLaf1hW.js → local-app-workbench-page-BWd4RMBE.js} +1 -1
  39. package/dist/gateway/static/root/assets/{local-apps-page-nwsnwYjK.js → local-apps-page-BsgcA6-N.js} +1 -1
  40. package/dist/gateway/static/root/assets/{logs-page-BEgYS-Xg.js → logs-page-CLP5pu2j.js} +1 -1
  41. package/dist/gateway/static/root/assets/management-settings-CZAZwCpE.js +1 -0
  42. package/dist/gateway/static/root/assets/{note-detail-panel-CCnfurBR.js → note-detail-panel-BeAHgdtw.js} +3 -3
  43. package/dist/gateway/static/root/assets/{notes-page-D9u_znxM.js → notes-page-MP21Zf_R.js} +1 -1
  44. package/dist/gateway/static/root/assets/{page-context-capture-button-BO3FoRej.js → page-context-capture-button-BZADmKcV.js} +1 -1
  45. package/dist/gateway/static/root/assets/personal-page-BbaTGmE-.js +1 -0
  46. package/dist/gateway/static/root/assets/{product-open-page-Dso2_hyl.js → product-open-page-CBnKsW4p.js} +1 -1
  47. package/dist/gateway/static/root/assets/{product-preview-content-CkvVb3EA.js → product-preview-content-B0gGexdG.js} +1 -1
  48. package/dist/gateway/static/root/assets/{project-detail-page-UjrrzE3H.js → project-detail-page-ZjQSv_NG.js} +1 -1
  49. package/dist/gateway/static/root/assets/{projects-page-C1h63P2M.js → projects-page-Bf7Sd_Ck.js} +1 -1
  50. package/dist/gateway/static/root/assets/{remote-access-hub-BJifH8AB.js → remote-access-hub-Bl8aPuXU.js} +1 -1
  51. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-vG1wqcEb.js → runtime-tools-settings-panel-CQZ1c-re.js} +1 -1
  52. package/dist/gateway/static/root/assets/{scenes-page-CwoAoYMX.js → scenes-page-1xGRBOJO.js} +1 -1
  53. package/dist/gateway/static/root/assets/{schema-form-BIsHdyz7.js → schema-form-BYqugMox.js} +1 -1
  54. package/dist/gateway/static/root/assets/{sessions-page-BcCLp3yU.js → sessions-page-DjMSVXU3.js} +1 -1
  55. package/dist/gateway/static/root/assets/settings-advanced-gate-BJuMTtyI.js +1 -0
  56. package/dist/gateway/static/root/assets/{settings-page-z9Jlk0BL.js → settings-page-CSqi5fmN.js} +1 -1
  57. package/dist/gateway/static/root/assets/{setup-status-panel-CrpXECqe.js → setup-status-panel-D5bM0TvA.js} +1 -1
  58. package/dist/gateway/static/root/assets/{shares-settings-D9XpTJXP.js → shares-settings-C-V9WllF.js} +1 -1
  59. package/dist/gateway/static/root/assets/{skill-api-DtPOf1aW.js → skill-api-CFtEHy5e.js} +1 -1
  60. package/dist/gateway/static/root/assets/{skills-page-Bq9-2b9U.js → skills-page-B-olXUVH.js} +1 -1
  61. package/dist/gateway/static/root/assets/{system-settings-panel-P7T729uD.js → system-settings-panel-DBMR9ks_.js} +1 -1
  62. package/dist/gateway/static/root/assets/{task-detail-page-CsB9AFx6.js → task-detail-page-CGe34vxR.js} +1 -1
  63. package/dist/gateway/static/root/assets/{tunnel-api-CuowqIZ7.js → tunnel-api-CzNIlgdJ.js} +1 -1
  64. package/dist/gateway/static/root/assets/{usage-page-avX3msoZ.js → usage-page-O3abwewp.js} +1 -1
  65. package/dist/gateway/static/root/assets/user-model-page-Bx2JlCpj.js +2 -0
  66. package/dist/gateway/static/root/assets/{work-discovery-overlay-BgJNz2tH.js → work-discovery-overlay-D967-fVj.js} +1 -1
  67. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-1I2q2J_U.js → workflow-run-setup-panel-BJ945m7G.js} +1 -1
  68. package/dist/gateway/static/root/assets/{workflows-page-DhJl9pnO.js → workflows-page-DUpVeIuw.js} +1 -1
  69. package/dist/gateway/static/root/assets/{xopc-cloud-account-card-CnPBP1V_.js → xopc-cloud-account-card-Bq9ubffP.js} +1 -1
  70. package/dist/gateway/static/root/index.html +1 -1
  71. package/dist/package.js +1 -1
  72. package/dist/packages/realtime-protocol/src/voice.js +11 -0
  73. package/dist/src/agent/agent-manager.d.ts +2 -7
  74. package/dist/src/agent/agent-manager.js +21 -50
  75. package/dist/src/agent/background-review/coordinator.js +1 -1
  76. package/dist/src/agent/background-review/prompts.d.ts +6 -0
  77. package/dist/src/agent/background-review/prompts.js +7 -1
  78. package/dist/src/agent/background-review/run-background-review.d.ts +3 -0
  79. package/dist/src/agent/background-review/run-background-review.js +185 -17
  80. package/dist/src/agent/context/coordinator.d.ts +1 -0
  81. package/dist/src/agent/context/coordinator.js +11 -2
  82. package/dist/src/agent/context/execution-context.js +40 -4
  83. package/dist/src/agent/context/project-context.js +11 -3
  84. package/dist/src/agent/embedded/run-for-session.js +2 -2
  85. package/dist/src/agent/embedded/run-turn.js +4 -4
  86. package/dist/src/agent/external-tools/endpoint-provider.js +2 -2
  87. package/dist/src/agent/image/tool-model-config.js +1 -1
  88. package/dist/src/agent/inbound/attachment-pipeline.js +1 -1
  89. package/dist/src/agent/memory/compaction-promotion.d.ts +2 -0
  90. package/dist/src/agent/memory/compaction-promotion.js +33 -2
  91. package/dist/src/agent/service/process-direct-streaming.js +1 -1
  92. package/dist/src/agent/service.js +5 -5
  93. package/dist/src/agent/session/session-inspector.js +1 -1
  94. package/dist/src/agent/skills/index.js +1 -1
  95. package/dist/src/agent/skills/managed-store.js +1 -1
  96. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  97. package/dist/src/agent/tasks/task-judge-service.js +2 -2
  98. package/dist/src/agent/tools/factory.js +1 -1
  99. package/dist/src/agent/tools/knowledge-memory-tool.js +14 -6
  100. package/dist/src/agent/tools/xopc-use-tool.js +2 -2
  101. package/dist/src/agent/workspace-runtime/registry.js +1 -1
  102. package/dist/src/agent-catalog/capabilities.js +2 -2
  103. package/dist/src/agent-config/schema.d.ts +9 -0
  104. package/dist/src/agent-config/schema.js +1 -0
  105. package/dist/src/ai-assist/text-assist.js +1 -1
  106. package/dist/src/auth/sync-provider-auth.js +1 -1
  107. package/dist/src/automations/capabilities/drafts.js +1 -1
  108. package/dist/src/automations/draft/automation-draft-service.js +1 -1
  109. package/dist/src/automations/service/action-executor.js +1 -1
  110. package/dist/src/bootstrap/application-state.js +2 -2
  111. package/dist/src/capabilities/runtime/dispatcher.js +1 -1
  112. package/dist/src/capabilities/runtime/product.js +1 -1
  113. package/dist/src/chat-commands/agent-edit.js +1 -1
  114. package/dist/src/chat-commands/context.js +1 -1
  115. package/dist/src/cli/commands/agent.js +2 -2
  116. package/dist/src/cli/commands/agents.js +1 -1
  117. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  118. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  119. package/dist/src/cli/commands/gateway/shared.js +2 -1
  120. package/dist/src/cli/commands/models.js +1 -1
  121. package/dist/src/cli/commands/onboard/model.js +1 -1
  122. package/dist/src/cli/extension-cli-register.js +1 -1
  123. package/dist/src/cli/utils/init-workspace-core.js +1 -1
  124. package/dist/src/computer/runtime.js +1 -1
  125. package/dist/src/config/agent-model-intents.js +1 -1
  126. package/dist/src/config/agent-profile.js +2 -2
  127. package/dist/src/config/gateway-bind.js +2 -1
  128. package/dist/src/config/index.js +4 -4
  129. package/dist/src/config/loader.js +1 -1
  130. package/dist/src/config/models-json.d.ts +8 -8
  131. package/dist/src/config/models-json.js +1 -1
  132. package/dist/src/config/schema.d.ts +18 -12
  133. package/dist/src/config/schema.js +12 -8
  134. package/dist/src/connectors/connected-source-ingestion.js +45 -7
  135. package/dist/src/connectors/connected-source-understanding.d.ts +1 -0
  136. package/dist/src/connectors/connected-source-understanding.js +59 -67
  137. package/dist/src/connectors/content-enrichment.js +1 -1
  138. package/dist/src/connectors/learning-coordinator.js +2 -2
  139. package/dist/src/daemon/constants.js +14 -9
  140. package/dist/src/daemon/index.js +4 -1
  141. package/dist/src/daemon/install-plan.js +2 -1
  142. package/dist/src/daemon/launchd.js +2 -1
  143. package/dist/src/daemon/schtasks.js +2 -1
  144. package/dist/src/daemon/service-audit.js +2 -1
  145. package/dist/src/daemon/service.js +2 -1
  146. package/dist/src/daemon/systemd.js +2 -1
  147. package/dist/src/extensions/agent-plugins/validation.js +1 -1
  148. package/dist/src/gateway/agents-admin.js +2 -2
  149. package/dist/src/gateway/client-ip.js +2 -1
  150. package/dist/src/gateway/file-path-classifier.js +1 -1
  151. package/dist/src/gateway/hono/app.js +2 -1
  152. package/dist/src/gateway/hono/lib/config-payload.d.ts +5 -0
  153. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  154. package/dist/src/gateway/hono/middleware/auth.d.ts +3 -5
  155. package/dist/src/gateway/hono/middleware/auth.js +56 -14
  156. package/dist/src/gateway/hono/middleware/logger.js +2 -1
  157. package/dist/src/gateway/hono/middleware/public-device-rate-limit.d.ts +16 -0
  158. package/dist/src/gateway/hono/middleware/public-device-rate-limit.js +43 -0
  159. package/dist/src/gateway/hono/middleware/strict-rate-limit.js +3 -2
  160. package/dist/src/gateway/hono/oauth-async.js +2 -1
  161. package/dist/src/gateway/hono/routes/browser.js +2 -1
  162. package/dist/src/gateway/hono/routes/config-patch/misc.js +5 -5
  163. package/dist/src/gateway/hono/routes/config.js +2 -1
  164. package/dist/src/gateway/hono/routes/connectors.js +1 -1
  165. package/dist/src/gateway/hono/routes/context-sources.js +2 -2
  166. package/dist/src/gateway/hono/routes/device-pairing-approval.d.ts +3 -8
  167. package/dist/src/gateway/hono/routes/device-pairing-approval.js +5 -20
  168. package/dist/src/gateway/hono/routes/devices.js +25 -9
  169. package/dist/src/gateway/hono/routes/endpoint-tools.js +1 -0
  170. package/dist/src/gateway/hono/routes/lazy-bundles.js +1 -0
  171. package/dist/src/gateway/hono/routes/mcp.js +1 -1
  172. package/dist/src/gateway/hono/routes/memory-learning.d.ts +3 -0
  173. package/dist/src/gateway/hono/routes/memory-learning.js +76 -0
  174. package/dist/src/gateway/hono/routes/models.js +1 -1
  175. package/dist/src/gateway/hono/routes/notes.js +2 -2
  176. package/dist/src/gateway/hono/routes/personal-agent.js +3 -2
  177. package/dist/src/gateway/hono/routes/projects.js +3 -3
  178. package/dist/src/gateway/hono/routes/public-gateway.js +6 -6
  179. package/dist/src/gateway/hono/routes/session-input-handler.js +1 -1
  180. package/dist/src/gateway/hono/routes/shares.js +2 -1
  181. package/dist/src/gateway/hono/routes/tasks.js +1 -1
  182. package/dist/src/gateway/hono/routes/user-model-refresh.js +1 -1
  183. package/dist/src/gateway/hono/routes/user-model.js +5 -3
  184. package/dist/src/gateway/hono/routes/voice.js +1 -0
  185. package/dist/src/gateway/host.js +9 -4
  186. package/dist/src/gateway/image-generation-setup.js +2 -2
  187. package/dist/src/gateway/index.js +8 -4
  188. package/dist/src/gateway/listen.js +2 -1
  189. package/dist/src/gateway/lock.js +1 -1
  190. package/dist/src/gateway/rate-limit/auth-policy.js +7 -11
  191. package/dist/src/gateway/rate-limit/buckets.d.ts +6 -0
  192. package/dist/src/gateway/rate-limit/buckets.js +211 -189
  193. package/dist/src/gateway/rate-limit/env-flags.js +4 -1
  194. package/dist/src/gateway/rate-limit/index.js +13 -4
  195. package/dist/src/gateway/runtime-config.js +5 -3
  196. package/dist/src/gateway/security/audit.js +3 -2
  197. package/dist/src/gateway/security/browser-session.js +2 -1
  198. package/dist/src/gateway/security/device-request-proof.d.ts +28 -0
  199. package/dist/src/gateway/security/device-request-proof.js +122 -0
  200. package/dist/src/gateway/security/loopback.js +7 -6
  201. package/dist/src/gateway/service/session-command-service.js +1 -1
  202. package/dist/src/gateway/service.js +18 -16
  203. package/dist/src/gateway/tailscale-auth.js +2 -1
  204. package/dist/src/gateway/ws-security.js +2 -1
  205. package/dist/src/gateway/xopc-cloud-capability-setup.js +1 -1
  206. package/dist/src/home-intelligence/generator.js +2 -2
  207. package/dist/src/home-intelligence/host.js +2 -2
  208. package/dist/src/imports/inventory.js +1 -1
  209. package/dist/src/infra/rate-limit/failure-limiter.js +78 -90
  210. package/dist/src/infra/rate-limit/index.js +11 -3
  211. package/dist/src/infra/rate-limit/keyed-store.js +52 -47
  212. package/dist/src/infra/rate-limit/rate-limiter.js +55 -58
  213. package/dist/src/infra/update-service-refresh.js +2 -1
  214. package/dist/src/knowledge/chunks.d.ts +10 -0
  215. package/dist/src/knowledge/chunks.js +28 -0
  216. package/dist/src/knowledge/connected-knowledge-pipeline.js +67 -33
  217. package/dist/src/knowledge/index.js +1 -1
  218. package/dist/src/knowledge/ingestion-service.js +5 -1
  219. package/dist/src/knowledge/local-folder-adapter.d.ts +2 -1
  220. package/dist/src/knowledge/local-folder-adapter.js +22 -13
  221. package/dist/src/knowledge-memory/domain.d.ts +4 -0
  222. package/dist/src/knowledge-memory/index.d.ts +1 -0
  223. package/dist/src/knowledge-memory/index.js +2 -1
  224. package/dist/src/knowledge-memory/repair-evidence.d.ts +2 -0
  225. package/dist/src/knowledge-memory/repair-evidence.js +57 -0
  226. package/dist/src/knowledge-memory/repository.d.ts +8 -0
  227. package/dist/src/knowledge-memory/repository.js +48 -27
  228. package/dist/src/knowledge-memory/search-text.d.ts +3 -0
  229. package/dist/src/knowledge-memory/search-text.js +32 -0
  230. package/dist/src/knowledge-memory/usage-policy.d.ts +6 -0
  231. package/dist/src/knowledge-memory/usage-policy.js +61 -0
  232. package/dist/src/local-apps/service.js +1 -1
  233. package/dist/src/memory-learning/repository.d.ts +24 -0
  234. package/dist/src/memory-learning/repository.js +96 -0
  235. package/dist/src/memory-learning/service.d.ts +17 -0
  236. package/dist/src/memory-learning/service.js +105 -0
  237. package/dist/src/memory-learning/source-learning.d.ts +7 -0
  238. package/dist/src/memory-learning/source-learning.js +235 -0
  239. package/dist/src/memory-learning/task-receipts.d.ts +3 -0
  240. package/dist/src/memory-learning/task-receipts.js +95 -0
  241. package/dist/src/memory-learning/work-capture.d.ts +51 -0
  242. package/dist/src/memory-learning/work-capture.js +140 -0
  243. package/dist/src/memory-maintenance/service.js +5 -5
  244. package/dist/src/personal-agent/proactivity/service.js +2 -2
  245. package/dist/src/personal-agent/prompt-context.js +4 -4
  246. package/dist/src/personal-agent/reply-composer.js +1 -1
  247. package/dist/src/personal-agent/repository.d.ts +3 -0
  248. package/dist/src/personal-agent/repository.js +1 -0
  249. package/dist/src/personal-agent/service.d.ts +6 -2
  250. package/dist/src/personal-agent/service.js +6 -5
  251. package/dist/src/platform/client.d.ts +1 -1
  252. package/dist/src/platform/contracts.d.ts +1 -1
  253. package/dist/src/providers/local-model.d.ts +2 -0
  254. package/dist/src/providers/local-model.js +16 -0
  255. package/dist/src/providers/model-catalog-persistence.js +1 -1
  256. package/dist/src/realtime/runtime.js +6 -0
  257. package/dist/src/realtime/tickets.d.ts +1 -0
  258. package/dist/src/realtime/tickets.js +4 -0
  259. package/dist/src/remote-access/exposure-guards.js +2 -1
  260. package/dist/src/routing/resolve-route.js +1 -1
  261. package/dist/src/routing/resolve-tui-session-key.js +1 -1
  262. package/dist/src/scenes/taskFollowUp/service.js +1 -1
  263. package/dist/src/session/session-title.js +1 -1
  264. package/dist/src/session/store.js +2 -1
  265. package/dist/src/share/share-rate-limit.js +2 -1
  266. package/dist/src/share/share-url.js +2 -1
  267. package/dist/src/share/site-share-router.js +2 -1
  268. package/dist/src/storage/sqlite/device-access-repository.d.ts +7 -0
  269. package/dist/src/storage/sqlite/device-access-repository.js +22 -4
  270. package/dist/src/storage/sqlite/knowledge-repository.js +6 -0
  271. package/dist/src/storage/sqlite/migrations/243_voice_opening_greetings.sql +12 -0
  272. package/dist/src/storage/sqlite/migrations/244_device_request_proofs.sql +8 -0
  273. package/dist/src/storage/sqlite/migrations/245_memory_learning.sql +125 -0
  274. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  275. package/dist/src/storage/sqlite/migrations/runner.js +6 -3
  276. package/dist/src/storage/sqlite/schema.js +1 -1
  277. package/dist/src/tasks/capabilities/read.js +2 -2
  278. package/dist/src/tasks/task-handoff-service.js +1 -1
  279. package/dist/src/tasks/task-main-update-decision-service.js +1 -1
  280. package/dist/src/tui/backends/embedded-backend.js +4 -4
  281. package/dist/src/tui/extension-host/load-extensions.js +1 -1
  282. package/dist/src/tui/tui-commands.js +1 -1
  283. package/dist/src/tui/tui-skills-autocomplete.js +1 -1
  284. package/dist/src/tui/tui-startup-resources.js +2 -2
  285. package/dist/src/tui/tui.js +2 -2
  286. package/dist/src/tunnel/broker-client.js +2 -0
  287. package/dist/src/tunnel/tunnel-config.js +2 -2
  288. package/dist/src/tunnel/tunnel-rate-limit.js +2 -1
  289. package/dist/src/user-context/access-policy.js +2 -1
  290. package/dist/src/user-context/config.d.ts +10 -0
  291. package/dist/src/user-context/config.js +5 -0
  292. package/dist/src/user-context/extraction/registry.d.ts +1 -1
  293. package/dist/src/user-context/extraction/registry.js +10 -0
  294. package/dist/src/user-context/identity-bindings.d.ts +10 -0
  295. package/dist/src/user-context/identity-bindings.js +24 -0
  296. package/dist/src/user-context/sources/consent-fields.d.ts +2 -0
  297. package/dist/src/user-context/sources/consent-fields.js +66 -0
  298. package/dist/src/user-model/bootstrap.js +1 -1
  299. package/dist/src/user-model/capture/executor.js +1 -1
  300. package/dist/src/user-model/capture/semantic.d.ts +3 -1
  301. package/dist/src/user-model/capture/semantic.js +14 -6
  302. package/dist/src/user-model/index.js +1 -1
  303. package/dist/src/user-model/usage-policy.js +34 -0
  304. package/dist/src/utils/logger/redact.js +4 -1
  305. package/dist/src/voice/platform-streams.d.ts +6 -0
  306. package/dist/src/voice/platform-streams.js +44 -4
  307. package/dist/src/voice/realtime/agentEngine.js +138 -83
  308. package/dist/src/voice/realtime/engine.d.ts +6 -0
  309. package/dist/src/voice/realtime/memory-context.js +19 -5
  310. package/dist/src/voice/realtime/opening-greeting.d.ts +12 -0
  311. package/dist/src/voice/realtime/opening-greeting.js +22 -0
  312. package/dist/src/voice/realtime/persona-context.js +1 -1
  313. package/dist/src/voice/realtime/runtime.js +90 -1
  314. package/dist/src/voice/realtime/runtime.types.d.ts +4 -0
  315. package/dist/src/work-discovery/incremental-repository.js +1 -1
  316. package/dist/src/work-discovery/service.js +6 -6
  317. package/dist/src/workflows/service/workflow-run-service.js +2 -2
  318. package/dist/src/workflows/service/workflow-session-bridge.js +38 -10
  319. package/package.json +3 -3
  320. package/dist/gateway/static/root/assets/management-settings-D_JQ6_X0.js +0 -1
  321. package/dist/gateway/static/root/assets/personal-page-BJn6UHAF.js +0 -1
  322. package/dist/gateway/static/root/assets/settings-advanced-gate-D114UdRI.js +0 -1
  323. package/dist/gateway/static/root/assets/user-model-page-DGDBWw7P.js +0 -2
@@ -1,5 +1,6 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
+ import { PlatformVoiceError } from "../platform-streams.js";
3
4
  import { prepareSpeechStreamSession, speakStream } from "../tts/speak-core.js";
4
5
  import { AudioPlaybackWindow } from "./audio-playback-window.js";
5
6
  import { TurnCoordinator } from "./turnPolicy.js";
@@ -43,6 +44,7 @@ function createAgentVoiceEngine(options) {
43
44
  let turnReceivedAtMs;
44
45
  let taskUpdateQueued = false;
45
46
  let lastUserSpeechAt = 0;
47
+ let openingInputSeen = false;
46
48
  const turn = new TurnCoordinator(claim.silenceDurationMs, (text, decision, turnId) => {
47
49
  if (closed || muted) return;
48
50
  const receivedAtMs = turnReceivedAtMs ?? Date.now();
@@ -103,13 +105,15 @@ function createAgentVoiceEngine(options) {
103
105
  };
104
106
  activeResponse = void 0;
105
107
  clearTimeout(response.firstPhraseTimer);
108
+ clearTimeout(response.openingTimer);
109
+ if (response.opening && response.opening.result !== "expired") response.opening.result = "interrupted";
106
110
  response.abortController.abort(reason);
107
111
  send("response.cancelled", {
108
112
  responseId: response.id,
109
113
  reason
110
114
  });
111
115
  const conversationId = claim.request.conversationId;
112
- if (conversationId && reason !== "session_closed") interruptionWrites = interruptionWrites.then(() => options.runtime.recordInterruption({
116
+ if (conversationId && !response.opening && reason !== "session_closed") interruptionWrites = interruptionWrites.then(() => options.runtime.recordInterruption({
113
117
  conversationId,
114
118
  responseId: response.id,
115
119
  reason,
@@ -221,6 +225,12 @@ function createAgentVoiceEngine(options) {
221
225
  for (const frame of response.pcm.push(item.value)) {
222
226
  await response.playback.reserve(frame.byteLength, response.abortController.signal);
223
227
  if (activeResponse !== response || closed) return;
228
+ if (response.opening && options.runtime.sessionBusy(claim.request.conversationId)) {
229
+ cancelActiveResponse("barge_in");
230
+ return;
231
+ }
232
+ response.firstFrameSent = true;
233
+ clearTimeout(response.openingTimer);
224
234
  options.sendAudio(response.id, frame);
225
235
  }
226
236
  }
@@ -304,17 +314,19 @@ function createAgentVoiceEngine(options) {
304
314
  if (response.speechWorker) prefetchNextSpeech(response);
305
315
  else startSpeechWorker(response);
306
316
  }
307
- async function runAssistantTurn(text, turnId, updateClientMessageId, receivedAtMs = Date.now(), committedAtMs = receivedAtMs) {
317
+ async function runAssistantTurn(text, turnId, updateClientMessageId, receivedAtMs = Date.now(), committedAtMs = receivedAtMs, opening) {
308
318
  if (!claim.tts || !claim.request.conversationId || !text.trim() || closed) return false;
309
319
  cancelActiveResponse("barge_in");
310
320
  const response = {
311
- id: `resp_${crypto.randomUUID()}`,
321
+ id: opening?.responseId ?? `resp_${crypto.randomUUID()}`,
322
+ ...opening ? { opening } : {},
312
323
  playback: new AudioPlaybackWindow(),
313
324
  abortController: new AbortController(),
314
325
  segmenter: new SpeakableSegmenter(AGENT_TTS_MAX_SEGMENT_CHARACTERS, AGENT_TTS_MIN_SEGMENT_CHARACTERS, 0),
315
326
  text: "",
316
327
  audibleText: "",
317
328
  audioStarted: false,
329
+ firstFrameSent: false,
318
330
  speechQueue: [],
319
331
  queuedSpeechCharacters: 0,
320
332
  startedAt: Date.now(),
@@ -328,13 +340,19 @@ function createAgentVoiceEngine(options) {
328
340
  const taskUpdateExposed = () => Boolean(updateClientMessageId && response.text.trim());
329
341
  activeResponse = response;
330
342
  send("response.created", { responseId: response.id });
343
+ if (opening) response.openingTimer = setTimeout(() => {
344
+ if (activeResponse === response) {
345
+ opening.result = "expired";
346
+ cancelActiveResponse("client_cancelled");
347
+ }
348
+ }, Math.max(0, opening.deadlineAt - Date.now()));
331
349
  try {
332
- const opening = prepareSpeechStreamSession(claim.tts.config, {
350
+ const speechOpening = prepareSpeechStreamSession(claim.tts.config, {
333
351
  appConfig: claim.config,
334
352
  signal: response.abortController.signal,
335
353
  allowFallback: false
336
354
  });
337
- if (opening) response.preparedSpeech = opening.then((session) => {
355
+ if (speechOpening) response.preparedSpeech = speechOpening.then((session) => {
338
356
  log.info({
339
357
  sessionId: claim.sessionId,
340
358
  responseId: response.id,
@@ -346,86 +364,96 @@ function createAgentVoiceEngine(options) {
346
364
  }, (error) => ({ error }));
347
365
  await interruptionWrites;
348
366
  if (response.abortController.signal.aborted || closed) return taskUpdateExposed();
349
- if (!claim.conversationSessionId) throw new Error("Conversation identity is unavailable");
350
- const task = await options.runtime.agentBroker.delegate({
351
- text,
352
- turnId,
353
- conversationId: claim.request.conversationId,
354
- expectedTranscriptId: claim.conversationSessionId,
355
- signal: response.abortController.signal,
356
- clientMessageId: updateClientMessageId ?? `voice:${claim.conversationSessionId}:${claim.sessionId}:${inputGeneration}:${turnId}`,
357
- ...updateClientMessageId ? { origin: {
358
- type: "system",
359
- source: "task_update"
360
- } } : {}
361
- });
362
- response.taskId = task.taskId;
363
- send("task.created", {
364
- responseId: response.id,
365
- taskId: task.taskId
366
- });
367
- for await (const event of task.events) {
368
- if (activeResponse !== response || response.abortController.signal.aborted) return taskUpdateExposed();
369
- if (event.type === "assistant_delta" || event.type === "tool_end" && event.payload?.toolName !== "clarify") response.awaitingClarification = false;
370
- if (event.type === "assistant_delta" && typeof event.payload?.delta === "string") {
371
- if (response.text.length + event.payload.delta.length > 32e3) throw new Error("Voice response text limit reached");
372
- if (!response.firstTextSeen && event.payload.delta.trim()) {
373
- response.firstTextSeen = true;
374
- response.firstTextAtMs = Date.now();
375
- log.info({
376
- sessionId: claim.sessionId,
367
+ if (opening) {
368
+ response.text = text;
369
+ response.firstTextAtMs = Date.now();
370
+ send("response.text.delta", {
371
+ responseId: response.id,
372
+ delta: text
373
+ });
374
+ queuePhrases(response, [text]);
375
+ } else {
376
+ if (!claim.conversationSessionId) throw new Error("Conversation identity is unavailable");
377
+ const task = await options.runtime.agentBroker.delegate({
378
+ text,
379
+ turnId,
380
+ conversationId: claim.request.conversationId,
381
+ expectedTranscriptId: claim.conversationSessionId,
382
+ signal: response.abortController.signal,
383
+ clientMessageId: updateClientMessageId ?? `voice:${claim.conversationSessionId}:${claim.sessionId}:${inputGeneration}:${turnId}`,
384
+ ...updateClientMessageId ? { origin: {
385
+ type: "system",
386
+ source: "task_update"
387
+ } } : {}
388
+ });
389
+ response.taskId = task.taskId;
390
+ send("task.created", {
391
+ responseId: response.id,
392
+ taskId: task.taskId
393
+ });
394
+ for await (const event of task.events) {
395
+ if (activeResponse !== response || response.abortController.signal.aborted) return taskUpdateExposed();
396
+ if (event.type === "assistant_delta" || event.type === "tool_end" && event.payload?.toolName !== "clarify") response.awaitingClarification = false;
397
+ if (event.type === "assistant_delta" && typeof event.payload?.delta === "string") {
398
+ if (response.text.length + event.payload.delta.length > 32e3) throw new Error("Voice response text limit reached");
399
+ if (!response.firstTextSeen && event.payload.delta.trim()) {
400
+ response.firstTextSeen = true;
401
+ response.firstTextAtMs = Date.now();
402
+ log.info({
403
+ sessionId: claim.sessionId,
404
+ responseId: response.id,
405
+ conversationId: claim.request.conversationId,
406
+ phase: "first_response_text",
407
+ latencyMs: Date.now() - response.startedAt,
408
+ inputToTextMs: Date.now() - response.inputReceivedAtMs,
409
+ turnDecisionMs: Math.max(0, response.turnCommittedAtMs - response.inputReceivedAtMs),
410
+ queueMs: Math.max(0, response.startedAt - response.turnCommittedAtMs)
411
+ }, "Realtime voice first response text ready");
412
+ }
413
+ response.text += event.payload.delta;
414
+ send("response.text.delta", {
377
415
  responseId: response.id,
378
- conversationId: claim.request.conversationId,
379
- phase: "first_response_text",
380
- latencyMs: Date.now() - response.startedAt,
381
- inputToTextMs: Date.now() - response.inputReceivedAtMs,
382
- turnDecisionMs: Math.max(0, response.turnCommittedAtMs - response.inputReceivedAtMs),
383
- queueMs: Math.max(0, response.startedAt - response.turnCommittedAtMs)
384
- }, "Realtime voice first response text ready");
416
+ delta: event.payload.delta
417
+ });
418
+ queuePhrases(response, response.segmenter.push(event.payload.delta));
419
+ if (response.firstPhraseAtMs === void 0 && response.firstPhraseTimer === void 0) response.firstPhraseTimer = setTimeout(() => {
420
+ response.firstPhraseTimer = void 0;
421
+ if (activeResponse === response && !response.abortController.signal.aborted) queuePhrases(response, response.segmenter.flushFirstPhrase());
422
+ }, FIRST_PHRASE_WAIT_MS);
385
423
  }
386
- response.text += event.payload.delta;
387
- send("response.text.delta", {
388
- responseId: response.id,
389
- delta: event.payload.delta
424
+ if (event.type === "tool_start") queuePhrases(response, response.segmenter.flush());
425
+ if ((event.type === "tool_start" || event.type === "tool_end") && typeof event.payload?.toolCallId === "string" && typeof event.payload?.toolName === "string") send("task.activity", {
426
+ taskId: task.taskId,
427
+ toolCallId: event.payload.toolCallId.slice(0, 160),
428
+ toolName: event.payload.toolName.slice(0, 256),
429
+ status: event.type === "tool_start" ? "running" : event.payload.status === "error" ? "failed" : "completed"
390
430
  });
391
- queuePhrases(response, response.segmenter.push(event.payload.delta));
392
- if (response.firstPhraseAtMs === void 0 && response.firstPhraseTimer === void 0) response.firstPhraseTimer = setTimeout(() => {
393
- response.firstPhraseTimer = void 0;
394
- if (activeResponse === response && !response.abortController.signal.aborted) queuePhrases(response, response.segmenter.flushFirstPhrase());
395
- }, FIRST_PHRASE_WAIT_MS);
396
- }
397
- if (event.type === "tool_start") queuePhrases(response, response.segmenter.flush());
398
- if ((event.type === "tool_start" || event.type === "tool_end") && typeof event.payload?.toolCallId === "string" && typeof event.payload?.toolName === "string") send("task.activity", {
399
- taskId: task.taskId,
400
- toolCallId: event.payload.toolCallId.slice(0, 160),
401
- toolName: event.payload.toolName.slice(0, 256),
402
- status: event.type === "tool_start" ? "running" : event.payload.status === "error" ? "failed" : "completed"
403
- });
404
- if (event.type === "run_end" || event.type === "stream_end") {
405
- const status = event.payload?.status;
406
- if (!response.taskDone) {
407
- response.taskDone = true;
408
- send("task.done", {
409
- taskId: task.taskId,
410
- status: status === "cancelled" ? "cancelled" : status === "suspended" ? "suspended" : status === "error" ? "failed" : "completed"
431
+ if (event.type === "run_end" || event.type === "stream_end") {
432
+ const status = event.payload?.status;
433
+ if (!response.taskDone) {
434
+ response.taskDone = true;
435
+ send("task.done", {
436
+ taskId: task.taskId,
437
+ status: status === "cancelled" ? "cancelled" : status === "suspended" ? "suspended" : status === "error" ? "failed" : "completed"
438
+ });
439
+ }
440
+ }
441
+ if (event.type === "clarify_request" && typeof event.payload?.requestId === "string" && typeof event.payload?.question === "string") {
442
+ response.awaitingClarification = true;
443
+ send("response.clarification", {
444
+ responseId: response.id,
445
+ requestId: event.payload.requestId.slice(0, 160),
446
+ kind: event.payload.kind === "approval" ? "approval" : "input",
447
+ question: event.payload.question.slice(0, 8e3),
448
+ ...Array.isArray(event.payload.choices) ? { choices: event.payload.choices.filter((choice) => typeof choice === "string").slice(0, 20).map((choice) => choice.slice(0, 1e3)) } : {},
449
+ ...typeof event.payload.suggestedAnswer === "string" ? { suggestedAnswer: event.payload.suggestedAnswer.slice(0, 1e3) } : {},
450
+ version: typeof event.payload.version === "number" ? event.payload.version : 1,
451
+ createdAt: typeof event.payload.createdAt === "number" ? event.payload.createdAt : Date.now(),
452
+ ...typeof event.payload.expiresAt === "number" ? { expiresAt: event.payload.expiresAt } : {}
411
453
  });
412
454
  }
455
+ if (event.type === "error") throw new Error(typeof event.payload?.message === "string" ? event.payload.message : "Agent response failed");
413
456
  }
414
- if (event.type === "clarify_request" && typeof event.payload?.requestId === "string" && typeof event.payload?.question === "string") {
415
- response.awaitingClarification = true;
416
- send("response.clarification", {
417
- responseId: response.id,
418
- requestId: event.payload.requestId.slice(0, 160),
419
- kind: event.payload.kind === "approval" ? "approval" : "input",
420
- question: event.payload.question.slice(0, 8e3),
421
- ...Array.isArray(event.payload.choices) ? { choices: event.payload.choices.filter((choice) => typeof choice === "string").slice(0, 20).map((choice) => choice.slice(0, 1e3)) } : {},
422
- ...typeof event.payload.suggestedAnswer === "string" ? { suggestedAnswer: event.payload.suggestedAnswer.slice(0, 1e3) } : {},
423
- version: typeof event.payload.version === "number" ? event.payload.version : 1,
424
- createdAt: typeof event.payload.createdAt === "number" ? event.payload.createdAt : Date.now(),
425
- ...typeof event.payload.expiresAt === "number" ? { expiresAt: event.payload.expiresAt } : {}
426
- });
427
- }
428
- if (event.type === "error") throw new Error(typeof event.payload?.message === "string" ? event.payload.message : "Agent response failed");
429
457
  }
430
458
  if (activeResponse !== response || response.abortController.signal.aborted || closed) return taskUpdateExposed();
431
459
  queuePhrases(response, response.segmenter.flush());
@@ -436,6 +464,12 @@ function createAgentVoiceEngine(options) {
436
464
  if (finalFrame) {
437
465
  await response.playback.reserve(finalFrame.byteLength, response.abortController.signal);
438
466
  if (activeResponse !== response || closed) return taskUpdateExposed();
467
+ if (response.opening && options.runtime.sessionBusy(claim.request.conversationId)) {
468
+ cancelActiveResponse("barge_in");
469
+ return false;
470
+ }
471
+ response.firstFrameSent = true;
472
+ clearTimeout(response.openingTimer);
439
473
  options.sendAudio(response.id, finalFrame);
440
474
  }
441
475
  await response.playback.drain(response.abortController.signal);
@@ -451,6 +485,7 @@ function createAgentVoiceEngine(options) {
451
485
  finishReason: response.audioStarted ? "completed" : "text_only",
452
486
  audio: response.audioStarted
453
487
  });
488
+ if (opening) opening.result = response.audioStarted ? "completed" : "failed";
454
489
  activeResponse = void 0;
455
490
  return !updateClientMessageId || taskUpdateExposed();
456
491
  } catch (error) {
@@ -461,7 +496,7 @@ function createAgentVoiceEngine(options) {
461
496
  responseId: response.id,
462
497
  ...updateClientMessageId ? { clientMessageId: updateClientMessageId } : {}
463
498
  }, updateClientMessageId ? "Proactive voice task update deferred" : "Realtime voice response failed");
464
- if (!updateClientMessageId) send("session.error", {
499
+ if (!updateClientMessageId && !opening) send("session.error", {
465
500
  code: "RESPONSE_FAILED",
466
501
  message: "Voice response failed",
467
502
  recoverable: true
@@ -483,6 +518,7 @@ function createAgentVoiceEngine(options) {
483
518
  if (activeResponse === response) activeResponse = void 0;
484
519
  return taskUpdateExposed();
485
520
  } finally {
521
+ clearTimeout(response.openingTimer);
486
522
  clearTimeout(response.firstPhraseTimer);
487
523
  await waitForSpeech(response);
488
524
  response.abortController.abort("response_finished");
@@ -538,14 +574,15 @@ function createAgentVoiceEngine(options) {
538
574
  if (event.type === "error") {
539
575
  turn.reset();
540
576
  echoCandidates.clear();
577
+ const message = event.error instanceof PlatformVoiceError ? event.error.clientMessage : "Streaming transcription failed";
541
578
  log.warn({
542
579
  err: event.error,
543
580
  sessionId: claim.sessionId,
544
581
  provider: stt.route.provider
545
- }, "Realtime STT failed");
582
+ }, `Realtime STT failed: ${message}`);
546
583
  send("session.error", {
547
584
  code: "PROVIDER_ERROR",
548
- message: "Streaming transcription failed",
585
+ message,
549
586
  recoverable: false
550
587
  });
551
588
  options.onClose("provider_error", true);
@@ -553,6 +590,8 @@ function createAgentVoiceEngine(options) {
553
590
  }
554
591
  if (muted || finalizedUtterances.has(event.utteranceId)) return;
555
592
  if (event.type === "speech_started") {
593
+ openingInputSeen = true;
594
+ if (activeResponse?.opening && !activeResponse.firstFrameSent) cancelActiveResponse("barge_in");
556
595
  lastUserSpeechAt = Date.now();
557
596
  echoCandidates.remember(event.utteranceId, activeResponse?.audioStarted ? activeResponse.audibleText : recentPlayback && recentPlayback.expiresAt >= Date.now() ? recentPlayback.text : void 0);
558
597
  if (claim.request.purpose === "conversation") {
@@ -578,6 +617,7 @@ function createAgentVoiceEngine(options) {
578
617
  if (event.type === "transcript_final") {
579
618
  lastUserSpeechAt = Date.now();
580
619
  const text = event.text.trim();
620
+ if (text) openingInputSeen = true;
581
621
  if (!text) {
582
622
  if (claim.request.purpose === "conversation") bufferFinal(event.utteranceId, "");
583
623
  return;
@@ -613,7 +653,7 @@ function createAgentVoiceEngine(options) {
613
653
  turn.reset();
614
654
  return;
615
655
  }
616
- if (claim.config.voice?.realtime?.bargeIn) cancelActiveResponse("barge_in");
656
+ if (activeResponse?.opening || claim.config.voice?.realtime?.bargeIn) cancelActiveResponse("barge_in");
617
657
  bufferFinal(event.utteranceId, text);
618
658
  }
619
659
  }
@@ -654,6 +694,21 @@ function createAgentVoiceEngine(options) {
654
694
  }
655
695
  return {
656
696
  isConversationIdle,
697
+ async offerOpeningGreeting(input) {
698
+ if (openingInputSeen || !canOfferTaskUpdateInput() || options.runtime.sessionBusy(claim.request.conversationId)) return "skipped";
699
+ const opening = {
700
+ ...input,
701
+ result: "failed"
702
+ };
703
+ const attempt = conversationTail.then(async () => {
704
+ if (Date.now() >= input.deadlineAt) return "expired";
705
+ if (openingInputSeen || !canOfferTaskUpdateInput() || options.runtime.sessionBusy(claim.request.conversationId)) return "skipped";
706
+ await runAssistantTurn(input.text, input.responseId, void 0, input.deadlineAt - 2e3, Date.now(), opening);
707
+ return opening.result;
708
+ });
709
+ conversationTail = attempt.then(() => {}, () => {});
710
+ return attempt;
711
+ },
657
712
  canOfferTaskUpdate() {
658
713
  return !taskUpdateQueued && canOfferTaskUpdateInput();
659
714
  },
@@ -1,9 +1,15 @@
1
+ import type { OpeningGreetingResult } from './opening-greeting.js';
1
2
  import type { VoiceServerEvent } from '@xopcai/realtime-protocol/voice';
2
3
  export type VoiceEventSink = <T extends VoiceServerEvent['type']>(type: T, payload: Extract<VoiceServerEvent, {
3
4
  type: T;
4
5
  }>['payload']) => void;
5
6
  export interface VoiceEngine {
6
7
  start(): Promise<void>;
8
+ offerOpeningGreeting?(input: {
9
+ responseId: string;
10
+ text: string;
11
+ deadlineAt: number;
12
+ }): Promise<OpeningGreetingResult>;
7
13
  appendAudio(bytes: Uint8Array): void;
8
14
  setInputMuted(muted: boolean): void | Promise<void>;
9
15
  commit(): Promise<void>;
@@ -3,16 +3,20 @@ import { init_logger } from "../../utils/logger.js";
3
3
  import { getSessionMetadata } from "../../storage/sqlite/session-read-repository.js";
4
4
  import { init_session_repository } from "../../storage/sqlite/session-repository.js";
5
5
  import { getSessionConfig, init_config_repository } from "../../storage/sqlite/config-repository.js";
6
+ import { canUseKnowledge } from "../../knowledge-memory/usage-policy.js";
7
+ import { getKnowledgeItem } from "../../knowledge-memory/repository.js";
8
+ import { init_config, resolveKnowledgeReadPolicy } from "../../user-context/config.js";
6
9
  import { getConversationRouting } from "../../storage/sqlite/conversation-repository.js";
7
10
  import { getConversation } from "../../routing/agent-session-key.js";
8
11
  import { init_session_key } from "../../routing/session-key.js";
9
- import { getKnowledgeItem } from "../../knowledge-memory/repository.js";
12
+ import { resolveUserContextSessionAccess } from "../../user-context/access-policy.js";
10
13
  import "../../knowledge-memory/index.js";
14
+ import { stripRuntimeContextFromUserMessage } from "../../session/user-message-display.js";
11
15
  import { getUserAssertion } from "../../user-model/repository.js";
12
16
  import "../../user-model/index.js";
13
- import { stripRuntimeContextFromUserMessage } from "../../session/user-message-display.js";
14
17
  import { buildExecutionContext } from "../../agent/context/execution-context.js";
15
18
  //#region src/voice/realtime/memory-context.ts
19
+ init_config();
16
20
  init_session_key();
17
21
  init_config_repository();
18
22
  init_session_repository();
@@ -35,7 +39,7 @@ function buildVoiceMemoryContext(input) {
35
39
  const started = performance.now();
36
40
  try {
37
41
  const config = input.getConfig();
38
- if (!voiceMemoryEnabled(config, input.conversationId) || input.maxChars < 128) return;
42
+ if (!resolveUserContextSessionAccess(config, input.conversationId).userModel || !voiceMemoryEnabled(config, input.conversationId) || input.maxChars < 128) return;
39
43
  const session = getSessionMetadata(input.conversationId);
40
44
  const context = buildExecutionContext({
41
45
  query: voiceMemoryQuery(input.history),
@@ -44,7 +48,9 @@ function buildVoiceMemoryContext(input) {
44
48
  projectId: input.projectId,
45
49
  conversationId: input.conversationId,
46
50
  maxAssertions: 6,
47
- maxKnowledge: 3
51
+ maxKnowledge: 3,
52
+ includeKnowledge: resolveUserContextSessionAccess(config, input.conversationId).knowledge,
53
+ knowledgePolicy: resolveKnowledgeReadPolicy(config.userContext.knowledgeMemory)
48
54
  });
49
55
  const block = JSON.stringify({ backgroundMemory: {
50
56
  userFacts: context.assertions.map((item) => item.assertion.statement),
@@ -64,7 +70,15 @@ function buildVoiceMemoryContext(input) {
64
70
  const configVersion = JSON.stringify(config.userContext);
65
71
  const isCurrent = () => {
66
72
  if (!voiceMemoryEnabled(input.getConfig(), input.conversationId) || JSON.stringify(input.getConfig().userContext) !== configVersion) return false;
67
- return references.every((reference) => reference.kind === "assertion" ? getUserAssertion(reference.id)?.recordedAt === reference.updatedAt : getKnowledgeItem(reference.id)?.updatedAt === reference.updatedAt);
73
+ return references.every((reference) => reference.kind === "assertion" ? getUserAssertion(reference.id)?.recordedAt === reference.updatedAt : (() => {
74
+ const item = getKnowledgeItem(reference.id);
75
+ return Boolean(item && item.updatedAt === reference.updatedAt && canUseKnowledge(item, {
76
+ agentId: session?.routing?.agentId ?? "main",
77
+ workspaceId: input.workspaceId,
78
+ projectId: input.projectId,
79
+ conversationId: input.conversationId
80
+ }, resolveKnowledgeReadPolicy(input.getConfig().userContext.knowledgeMemory)));
81
+ })());
68
82
  };
69
83
  log.debug({
70
84
  conversationId: input.conversationId,
@@ -0,0 +1,12 @@
1
+ export type OpeningGreetingResult = 'completed' | 'skipped' | 'interrupted' | 'failed' | 'expired';
2
+ export interface OpeningGreetingIdentity {
3
+ principalId: string;
4
+ conversationId: string;
5
+ logicalCallId: string;
6
+ responseId: string;
7
+ }
8
+ /** Only an explicitly provided name is used; never infer identity from an account ID. */
9
+ export declare function openingGreetingText(callName: string | null | undefined, language: 'zh' | 'en'): string;
10
+ /** Claim before output: crashes and interrupted calls must never replay the greeting. */
11
+ export declare function claimOpeningGreeting(identity: OpeningGreetingIdentity, now?: number): boolean;
12
+ export declare function finishOpeningGreeting(identity: OpeningGreetingIdentity, state: OpeningGreetingResult, reason?: string): void;
@@ -0,0 +1,22 @@
1
+ import { getSqliteDatabase, init_transaction } from "../../storage/sqlite/transaction.js";
2
+ //#region src/voice/realtime/opening-greeting.ts
3
+ init_transaction();
4
+ /** Only an explicitly provided name is used; never infer identity from an account ID. */
5
+ function openingGreetingText(callName, language) {
6
+ const name = callName?.replace(/[\p{Cc}\p{Cf}]/gu, "").replace(/\s+/g, " ").trim().slice(0, 40);
7
+ return language === "en" ? name ? `Hi, ${name}. I'm here.` : "Hi, I'm here." : name ? `嗨,${name},我在。` : "嗨,我在。";
8
+ }
9
+ /** Claim before output: crashes and interrupted calls must never replay the greeting. */
10
+ function claimOpeningGreeting(identity, now = Date.now()) {
11
+ const db = getSqliteDatabase();
12
+ db.prepare("DELETE FROM voice_opening_greetings WHERE created_at < ?").run(now - 864e5);
13
+ return db.prepare(`INSERT OR IGNORE INTO voice_opening_greetings
14
+ (principal_id, conversation_id, logical_call_id, response_id, state, created_at, updated_at)
15
+ VALUES (?, ?, ?, ?, 'claimed', ?, ?)`).run(identity.principalId, identity.conversationId, identity.logicalCallId, identity.responseId, now, now).changes === 1;
16
+ }
17
+ function finishOpeningGreeting(identity, state, reason) {
18
+ getSqliteDatabase().prepare(`UPDATE voice_opening_greetings SET state = ?, reason = ?, updated_at = ?
19
+ WHERE principal_id = ? AND conversation_id = ? AND logical_call_id = ? AND state = 'claimed'`).run(state, reason ?? null, Date.now(), identity.principalId, identity.conversationId, identity.logicalCallId);
20
+ }
21
+ //#endregion
22
+ export { claimOpeningGreeting, finishOpeningGreeting, openingGreetingText };
@@ -1,6 +1,6 @@
1
1
  import { init_agent_scope, resolveAgentProfileDir } from "../../agent/agent-scope.js";
2
- import { DEFAULT_SOUL_FILENAME } from "../../agent/context/workspace.js";
3
2
  import { resolveEffectiveAgentProfileForSession } from "../../config/agent-profile.js";
3
+ import { DEFAULT_SOUL_FILENAME } from "../../agent/context/workspace.js";
4
4
  import { loadProfileBootstrapFiles } from "../../agent/bootstrap/load-bootstrap-files.js";
5
5
  import { join } from "node:path";
6
6
  import { statSync } from "node:fs";
@@ -12,7 +12,9 @@ import { ALIBABA_REALTIME_TTS_MODEL } from "../tts/providers/alibaba-speech.js";
12
12
  import { resolveSpeechProviderChain } from "../tts/factory.js";
13
13
  import { mergeTtsConfigFromAppConfig } from "../tts/merge-config.js";
14
14
  import { voiceConversationInstructions } from "./conversation-context.js";
15
+ import { verifyDeviceConnectionProof } from "../../gateway/security/device-request-proof.js";
15
16
  import { createPreauthConnectionBudget } from "../../gateway/security/preauth-connection-budget.js";
17
+ import { claimOpeningGreeting, finishOpeningGreeting, openingGreetingText } from "./opening-greeting.js";
16
18
  import { createAgentVoiceEngine } from "./agentEngine.js";
17
19
  import { resolveOmniRoute } from "./omniRoute.js";
18
20
  import { createOmniVoiceEngine } from "./omniEngine.js";
@@ -225,12 +227,18 @@ var VoiceRealtimeRuntime = class {
225
227
  async createSession(request, principalId, now = Date.now()) {
226
228
  const { request: resolvedRequest, config, omni, conversationSessionId, stt, tts, inputMode } = await this.prepareSession(request, now);
227
229
  request = resolvedRequest;
230
+ const personal = request.conversationId ? getPersonalAgentByConversation(request.conversationId) : null;
231
+ const openingGreeting = request.openingGreeting && request.openingGreeting.initiation === "user" && request.purpose === "conversation" && request.mode === "assistant" && personal?.state === "ready" && personal.voiceCallPreferences.openingGreetingEnabled ? {
232
+ responseId: `greeting_${crypto.randomUUID()}`,
233
+ text: openingGreetingText(personal.userCallName, request.language ?? "zh")
234
+ } : void 0;
228
235
  const sessionId = crypto.randomUUID();
229
236
  const ticket = crypto.randomBytes(32).toString("base64url");
230
237
  const connectionEpoch = crypto.randomInt(1, 4294967296);
231
238
  const maxSessionMs = request.purpose === "conversation" ? Math.min(config.voice.realtime.maxConversationMs, omni?.maxSessionSeconds ? omni.maxSessionSeconds * 1e3 : Number.MAX_SAFE_INTEGER) : config.voice.realtime.maxDictationMs;
232
239
  const claim = {
233
240
  conversationSessionId,
241
+ openingGreeting,
234
242
  sessionId,
235
243
  connectionEpoch,
236
244
  principalId,
@@ -250,6 +258,10 @@ var VoiceRealtimeRuntime = class {
250
258
  this.tickets.set(ticketKey(ticket), claim);
251
259
  if (request.purpose === "conversation" && request.conversationId) this.conversationReservations.set(request.conversationId, sessionId);
252
260
  return {
261
+ ...request.openingGreeting ? { openingGreeting: {
262
+ accepted: Boolean(openingGreeting),
263
+ ...openingGreeting ? { greetingResponseId: openingGreeting.responseId } : {}
264
+ } } : {},
253
265
  sessionId,
254
266
  ticket,
255
267
  ticketExpiresAt: new Date(claim.expiresAt).toISOString(),
@@ -394,6 +406,20 @@ var VoiceRealtimeRuntime = class {
394
406
  let context;
395
407
  let unsubscribeMemory;
396
408
  let unsubscribeSession;
409
+ let openingTimer;
410
+ let outputReady = false;
411
+ let pendingGreeting;
412
+ const finishGreeting = (identity, result, reason) => {
413
+ try {
414
+ finishOpeningGreeting(identity, result, reason);
415
+ } catch (err) {
416
+ log.warn({
417
+ err,
418
+ ...identity,
419
+ phase: "opening_greeting_audit"
420
+ }, "Opening greeting audit failed");
421
+ }
422
+ };
397
423
  let inputMutationTail = Promise.resolve();
398
424
  const send = (type, payload) => {
399
425
  if (!claim || socket.readyState !== WebSocketState.OPEN) return;
@@ -423,6 +449,11 @@ var VoiceRealtimeRuntime = class {
423
449
  if (shutdownCompletion) return shutdownCompletion;
424
450
  if (closed) return Promise.resolve();
425
451
  closed = true;
452
+ clearTimeout(openingTimer);
453
+ if (pendingGreeting) {
454
+ finishGreeting(pendingGreeting, "skipped", "session_closed");
455
+ pendingGreeting = void 0;
456
+ }
426
457
  shutdownCompletion = (async () => {
427
458
  unsubscribeMemory?.();
428
459
  unsubscribeSession?.();
@@ -476,6 +507,11 @@ var VoiceRealtimeRuntime = class {
476
507
  shutdown("context_changed", true);
477
508
  };
478
509
  const authenticate = async (message) => {
510
+ const pendingClaim = this.tickets.get(ticketKey(message.payload.ticket));
511
+ if (pendingClaim && !verifyDeviceConnectionProof(pendingClaim.principalId, message.payload.ticket, new URL(request.url, "http://localhost").pathname + new URL(request.url, "http://localhost").search, typeof request.headers["x-xopc-proof"] === "string" ? request.headers["x-xopc-proof"] : void 0)) {
512
+ socket.close(4401, "Invalid voice session proof");
513
+ return;
514
+ }
479
515
  const consumed = this.consumeTicket(message.payload.ticket, message.payload.sessionId);
480
516
  if (!consumed || this.options.isPrincipalActive?.(consumed.principalId) === false) {
481
517
  if (consumed) this.releaseConversationReservation(consumed);
@@ -697,7 +733,60 @@ var VoiceRealtimeRuntime = class {
697
733
  return;
698
734
  }
699
735
  if (message.type === "session.ping") send("session.pong", {});
700
- else if (message.type === "session.stop") shutdown(message.payload.reason, true);
736
+ else if (message.type === "session.output_ready") {
737
+ if (!ready || !claim.request.openingGreeting || message.payload.connectionEpoch !== claim.connectionEpoch) {
738
+ socket.close(4400, "Invalid output readiness");
739
+ return;
740
+ }
741
+ if (outputReady) return;
742
+ outputReady = true;
743
+ const greeting = claim.openingGreeting;
744
+ if (!greeting || !engine?.offerOpeningGreeting) return;
745
+ const identity = {
746
+ principalId: claim.principalId,
747
+ conversationId: claim.request.conversationId,
748
+ logicalCallId: claim.request.openingGreeting.logicalCallId,
749
+ responseId: greeting.responseId
750
+ };
751
+ try {
752
+ if (!claimOpeningGreeting(identity)) return;
753
+ } catch (err) {
754
+ log.warn({
755
+ err,
756
+ sessionId: claim.sessionId
757
+ }, "Opening greeting claim failed");
758
+ return;
759
+ }
760
+ const deadlineAt = Date.now() + 2e3;
761
+ pendingGreeting = identity;
762
+ openingTimer = setTimeout(() => {
763
+ openingTimer = void 0;
764
+ pendingGreeting = void 0;
765
+ if (closed) {
766
+ finishGreeting(identity, "skipped", "session_closed");
767
+ return;
768
+ }
769
+ engine.offerOpeningGreeting({
770
+ ...greeting,
771
+ deadlineAt
772
+ }).then((result) => {
773
+ finishGreeting(identity, result);
774
+ log.info({
775
+ ...identity,
776
+ sessionId: claim?.sessionId,
777
+ phase: "opening_greeting",
778
+ result
779
+ }, "Opening greeting finished");
780
+ }).catch((err) => {
781
+ finishGreeting(identity, "failed");
782
+ log.warn({
783
+ err,
784
+ ...identity,
785
+ phase: "opening_greeting"
786
+ }, "Opening greeting failed");
787
+ });
788
+ }, 300);
789
+ } else if (message.type === "session.stop") shutdown(message.payload.reason, true);
701
790
  else if (message.type === "session.metric") {
702
791
  const { responseId, metric, durationMs } = message.payload;
703
792
  const key = `${responseId}:${metric}`;
@@ -18,6 +18,10 @@ export interface ResolvedStreamingStt {
18
18
  route: VoiceProviderRoute;
19
19
  }
20
20
  export interface VoiceTicketClaim {
21
+ openingGreeting?: {
22
+ responseId: string;
23
+ text: string;
24
+ };
21
25
  sessionId: string;
22
26
  connectionEpoch: number;
23
27
  principalId: string;
@@ -1,5 +1,5 @@
1
- import { getWorkDiscoveryRun } from "./repository.js";
2
1
  import { createUnderstandingSourceRun, listUnderstandingSourceGrants, listUnderstandingSourceRuns, updateUnderstandingSourceRun } from "../user-context/sources/repository.js";
2
+ import { getWorkDiscoveryRun } from "./repository.js";
3
3
  import { workDiscoveryFingerprintsEqual } from "./incremental.js";
4
4
  //#region src/work-discovery/incremental-repository.ts
5
5
  function refreshFromRun(run) {