@xopcai/xopc 0.0.242 → 0.0.243

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) 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-MU5XrVv8.js → activity-store-CAah-_8h.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-B_Wc0wuL.js → agent-avatar-dicebear-DDH4HgP7.js} +1 -1
  5. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-BDCfJZhh.js +1 -0
  6. package/dist/gateway/static/root/assets/agents-B0ZWgf-U.js +1 -0
  7. package/dist/gateway/static/root/assets/{api-DLyc8BQp.js → api-B2tKxbcz.js} +1 -1
  8. package/dist/gateway/static/root/assets/app-management-settings-panel-Blfwszzn.js +1 -0
  9. package/dist/gateway/static/root/assets/{appearance-settings-DB5gEQhN.js → appearance-settings-C3V7Na75.js} +1 -1
  10. package/dist/gateway/static/root/assets/apps-page-BiEks0-F.js +1 -0
  11. package/dist/gateway/static/root/assets/{archive-plugin-BOx0EJZs.js → archive-plugin-BmSx7wzY.js} +1 -1
  12. package/dist/gateway/static/root/assets/{attachment-load-CYnYIyLr.js → attachment-load-BS-S4LJ9.js} +1 -1
  13. package/dist/gateway/static/root/assets/{automations-page-ChTflpmA.js → automations-page-C7Y3L30D.js} +1 -1
  14. package/dist/gateway/static/root/assets/automations-workspace-A3zPkPuo.js +6 -0
  15. package/dist/gateway/static/root/assets/{binary-plugins-BjbLfNwG.js → binary-plugins-BM9B5kcm.js} +1 -1
  16. package/dist/gateway/static/root/assets/block-editor-BbnpkYP9.js +185 -0
  17. package/dist/gateway/static/root/assets/browser-settings-page-Dk4FQ7fY.js +8 -0
  18. package/dist/gateway/static/root/assets/{browser-workflow-inputs-DGQxD0vD.js → browser-workflow-inputs-CRnkgZHI.js} +1 -1
  19. package/dist/gateway/static/root/assets/{browser-workflows-page-B9gfdd7L.js → browser-workflows-page-B9qGL-it.js} +2 -2
  20. package/dist/gateway/static/root/assets/{button-CSEbl4zT.js → button-CtTvuElV.js} +1 -1
  21. package/dist/gateway/static/root/assets/capabilities-settings-panel--tR_El9e.js +7 -0
  22. package/dist/gateway/static/root/assets/channels-settings-D347vl_J.js +1 -0
  23. package/dist/gateway/static/root/assets/{channels-status-swr-TkU42f-W.js → channels-status-swr-DuY4MGxe.js} +1 -1
  24. package/dist/gateway/static/root/assets/{chat-terminal-dock-CWMPcnoN.js → chat-terminal-dock-DABPvwBT.js} +2 -2
  25. package/dist/gateway/static/root/assets/connectors-page-B-tvJkRJ.js +2 -0
  26. package/dist/gateway/static/root/assets/{cron-expression-3h3rR0cf.js → cron-expression-D3bsKbNd.js} +1 -1
  27. package/dist/gateway/static/root/assets/{date-picker-Bz1sLBEx.js → date-picker-BsGM6Yty.js} +1 -1
  28. package/dist/gateway/static/root/assets/{dependency-picker-p9DWR8uU.js → dependency-picker-Dj3g6YXZ.js} +1 -1
  29. package/dist/gateway/static/root/assets/{desktop-pet-D1TDB_gR.js → desktop-pet-C1QT0Sur.js} +1 -1
  30. package/dist/gateway/static/root/assets/{desktop-pet-settings-DWPZpoB7.js → desktop-pet-settings-r6kAOnfY.js} +1 -1
  31. package/dist/gateway/static/root/assets/dist-CpzYSoz7.js +1 -0
  32. package/dist/gateway/static/root/assets/{dist-DaxPZj9e.js → dist-DBS3OB8K.js} +1 -1
  33. package/dist/gateway/static/root/assets/dist-DLmwHyAh.js +1 -0
  34. package/dist/gateway/static/root/assets/es2015-BvLh9GaE.js +41 -0
  35. package/dist/gateway/static/root/assets/{extension-debug-page-BrRbK2rJ.js → extension-debug-page-DOkf7nh9.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-page-CD65czfS.js → extension-page-Cq2IA4Ho.js} +1 -1
  37. package/dist/gateway/static/root/assets/{extension-settings-page-BRDDKhpi.js → extension-settings-page-DQn4qjDZ.js} +1 -1
  38. package/dist/gateway/static/root/assets/{files-DQjhZjr2.js → files-D6vf3S30.js} +1 -1
  39. package/dist/gateway/static/root/assets/{gateway-config-api-CjbBsr5M.js → gateway-config-api-DM3poL22.js} +1 -1
  40. package/dist/gateway/static/root/assets/{gateway-config-swr-HT7sLeO9.js → gateway-config-swr-BC3pgb_P.js} +1 -1
  41. package/dist/gateway/static/root/assets/{gateway-settings-BQjmfYD4.js → gateway-settings-DPgH1hnh.js} +1 -1
  42. package/dist/gateway/static/root/assets/{gateway-startup-retry-iBCtaAuT.js → gateway-startup-retry-uMcSfLru.js} +1 -1
  43. package/dist/gateway/static/root/assets/{heartbeat-settings-BzZxLgrw.js → heartbeat-settings-Bk80-E3b.js} +1 -1
  44. package/dist/gateway/static/root/assets/home-page-DKo5JiMR.js +1 -0
  45. package/dist/gateway/static/root/assets/{image-generation-api-dj9TNEn2.js → image-generation-api-D1qlQA-A.js} +1 -1
  46. package/dist/gateway/static/root/assets/index-B0WJL9N1.js +71 -0
  47. package/dist/gateway/static/root/assets/index-Dbh5SXyV.css +2 -0
  48. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BEnuEPD3.js → keyboard-shortcuts-settings-BEwvd8ZU.js} +1 -1
  49. package/dist/gateway/static/root/assets/local-app-workbench-page--eJ5rmNj.js +2 -0
  50. package/dist/gateway/static/root/assets/{local-apps-page-DXQtppWq.js → local-apps-page-CgkY9E-r.js} +1 -1
  51. package/dist/gateway/static/root/assets/{locale-store-DVzTJSj2.js → locale-store-CMZljTJx.js} +1 -1
  52. package/dist/gateway/static/root/assets/logs-page-Z49Td0J9.js +2 -0
  53. package/dist/gateway/static/root/assets/{management-settings-DIpBMWrA.js → management-settings-Dv8NSJKt.js} +1 -1
  54. package/dist/gateway/static/root/assets/{markdown-split-BFHyGwNF.js → markdown-split-Cm0RBB-t.js} +1 -1
  55. package/dist/gateway/static/root/assets/{markdown-view-DR3yVwtV.js → markdown-view-PPCLt7fu.js} +3 -3
  56. package/dist/gateway/static/root/assets/{media-plugins-BjjMxC5_.js → media-plugins-Bcr5sU1f.js} +1 -1
  57. package/dist/gateway/static/root/assets/messages-CTSgFlE4.js +3 -0
  58. package/dist/gateway/static/root/assets/mobile-pairing-wizard-kKDiXofY.js +1 -0
  59. package/dist/gateway/static/root/assets/models-hub-cache-ZrbGRiGY.js +1 -0
  60. package/dist/gateway/static/root/assets/{notes-page-BbvtelJa.js → notes-page-CgsaRaGi.js} +1 -1
  61. package/dist/gateway/static/root/assets/notes-workbench-C1DEE09k.js +4 -0
  62. package/dist/gateway/static/root/assets/{oauth-api-BB-VYJ5j.js → oauth-api-4VeMbkTJ.js} +1 -1
  63. package/dist/gateway/static/root/assets/popover-select-C-Hj-nXR.js +1 -0
  64. package/dist/gateway/static/root/assets/{product-delivery-dUJgmBQZ.js → product-delivery-CV4pDOtG.js} +1 -1
  65. package/dist/gateway/static/root/assets/product-open-page-BU0HiZtv.js +1 -0
  66. package/dist/gateway/static/root/assets/project-detail-page-isRapnp5.js +1 -0
  67. package/dist/gateway/static/root/assets/projects-page-zvsjg0mi.js +1 -0
  68. package/dist/gateway/static/root/assets/{registry-api-DXsR8oGN.js → registry-api-CWOtPFrE.js} +1 -1
  69. package/dist/gateway/static/root/assets/remote-access-hub-C9TGZ9Hc.js +1 -0
  70. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DoEfLjPB.js → runtime-tools-settings-panel-2zm9edMA.js} +2 -2
  71. package/dist/gateway/static/root/assets/{schema-form-BsqpbBDv.js → schema-form-54A5gzkN.js} +1 -1
  72. package/dist/gateway/static/root/assets/{schemas-zIBW3wiV.js → schemas-BHS7bo06.js} +1 -1
  73. package/dist/gateway/static/root/assets/sessions-page-BP8RyiUn.js +1 -0
  74. package/dist/gateway/static/root/assets/settings-advanced-gate-D4EMxWAZ.js +1 -0
  75. package/dist/gateway/static/root/assets/{settings-page-CE7ZcCTK.js → settings-page-DzDhoyxv.js} +1 -1
  76. package/dist/gateway/static/root/assets/{setup-status-panel-CGlne9i0.js → setup-status-panel-uDicHU1w.js} +1 -1
  77. package/dist/gateway/static/root/assets/{share-preview-page-Bn0qMX8V.js → share-preview-page-lOmLCWFS.js} +1 -1
  78. package/dist/gateway/static/root/assets/shares-settings-T4Yvouvv.js +1 -0
  79. package/dist/gateway/static/root/assets/skill-reload-api-DVb7nrXz.js +1 -0
  80. package/dist/gateway/static/root/assets/{skills-page-dRkeYh5P.js → skills-page-CnyNH3To.js} +1 -1
  81. package/dist/gateway/static/root/assets/skills-page.utils-CgDtf2qs.js +1 -0
  82. package/dist/gateway/static/root/assets/{system-settings-panel-C0ZCuCM4.js → system-settings-panel-C8yWfwtR.js} +1 -1
  83. package/dist/gateway/static/root/assets/task-detail-page-Tzfjlbf3.js +1 -0
  84. package/dist/gateway/static/root/assets/{theme-store-Cth-eChE.js → theme-store-uxUwr10D.js} +1 -1
  85. package/dist/gateway/static/root/assets/tunnel-api-Bvg679no.js +1 -0
  86. package/dist/gateway/static/root/assets/{use-autosave-PSV89QuQ.js → use-autosave-D88VGoyG.js} +1 -1
  87. package/dist/gateway/static/root/assets/user-context-page-RijPtyfO.js +1 -0
  88. package/dist/gateway/static/root/assets/{voice-api-key-field-DXWdJ8cR.js → voice-api-key-field-CBHLsMcc.js} +1 -1
  89. package/dist/gateway/static/root/assets/voice-config-api-MqyI6cgq.js +1 -0
  90. package/dist/gateway/static/root/assets/work-discovery-overlay-GGe2TQ98.js +1 -0
  91. package/dist/gateway/static/root/assets/workflow-run-setup-panel-C0layUE9.js +3 -0
  92. package/dist/gateway/static/root/assets/workflows-page-DQXIzBAc.js +5 -0
  93. package/dist/gateway/static/root/index.html +21 -22
  94. package/dist/package.js +1 -1
  95. package/dist/packages/realtime-protocol/src/voice-audio.js +20 -0
  96. package/dist/packages/realtime-protocol/src/voice.js +208 -0
  97. package/dist/src/agent/embedded/run-for-session.d.ts +1 -0
  98. package/dist/src/agent/embedded/run-for-session.js +2 -1
  99. package/dist/src/agent/inbound/turn-dispatcher.d.ts +1 -0
  100. package/dist/src/agent/inbound/turn-dispatcher.js +2 -1
  101. package/dist/src/agent/prompt/voice-presentation.d.ts +2 -0
  102. package/dist/src/agent/prompt/voice-presentation.js +12 -0
  103. package/dist/src/agent/service/direct-turn-helpers.d.ts +1 -0
  104. package/dist/src/agent/service/direct-turn-helpers.js +1 -0
  105. package/dist/src/agent/service/process-direct-streaming.d.ts +1 -0
  106. package/dist/src/agent/service/process-direct-streaming.js +2 -1
  107. package/dist/src/agent/session/session-config-service.js +4 -2
  108. package/dist/src/automations/draft/automation-draft-service.js +1 -1
  109. package/dist/src/config/models-json.d.ts +8 -8
  110. package/dist/src/config/schema.d.ts +60 -0
  111. package/dist/src/config/schema.js +31 -1
  112. package/dist/src/files/file-service.d.ts +1 -0
  113. package/dist/src/files/file-service.js +4 -0
  114. package/dist/src/gateway/chat-stream/thinking-delta-coalescer.js +8 -3
  115. package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -11
  116. package/dist/src/gateway/hono/lib/config-payload.js +2 -2
  117. package/dist/src/gateway/hono/lib/safe-voice-config.d.ts +14 -0
  118. package/dist/src/gateway/hono/lib/safe-voice-config.js +29 -1
  119. package/dist/src/gateway/hono/routes/config-patch/misc.js +2 -2
  120. package/dist/src/gateway/hono/routes/files.js +7 -0
  121. package/dist/src/gateway/hono/routes/lazy-fallback.js +18 -4
  122. package/dist/src/gateway/hono/routes/session-input-handler.js +14 -0
  123. package/dist/src/gateway/hono/routes/sessions.js +4 -1
  124. package/dist/src/gateway/hono/routes/voice.js +139 -2
  125. package/dist/src/gateway/server.js +1 -0
  126. package/dist/src/gateway/service/agent-runner.d.ts +1 -0
  127. package/dist/src/gateway/service/agent-runner.js +1 -6
  128. package/dist/src/gateway/service/run-gateway-agent.d.ts +1 -0
  129. package/dist/src/gateway/service/run-gateway-agent.js +19 -1
  130. package/dist/src/gateway/service.d.ts +2 -0
  131. package/dist/src/gateway/service.js +73 -0
  132. package/dist/src/media-understanding/types.d.ts +61 -0
  133. package/dist/src/session/client-history.js +12 -0
  134. package/dist/src/session/manager.d.ts +1 -0
  135. package/dist/src/session/session-context-for-llm.js +8 -0
  136. package/dist/src/session/store.d.ts +1 -0
  137. package/dist/src/session/store.js +1 -0
  138. package/dist/src/session/stored-rows-to-file-entries.js +5 -1
  139. package/dist/src/session/voice-transcript.d.ts +9 -0
  140. package/dist/src/session/voice-transcript.js +45 -0
  141. package/dist/src/storage/sqlite/config-repository.js +9 -0
  142. package/dist/src/storage/sqlite/remote-context-repository.d.ts +3 -0
  143. package/dist/src/storage/sqlite/remote-context-repository.js +40 -0
  144. package/dist/src/storage/sqlite/session-repository.js +13 -0
  145. package/dist/src/storage/sqlite/user-context-repository.d.ts +3 -1
  146. package/dist/src/storage/sqlite/user-context-repository.js +50 -4
  147. package/dist/src/tui/tui.js +2 -1
  148. package/dist/src/user-context/automatic-context.d.ts +17 -0
  149. package/dist/src/user-context/automatic-context.js +85 -0
  150. package/dist/src/user-context/changes.d.ts +7 -0
  151. package/dist/src/user-context/changes.js +28 -0
  152. package/dist/src/user-context/planner.js +1 -24
  153. package/dist/src/user-context/retriever.js +10 -11
  154. package/dist/src/user-context/selection-policy.d.ts +20 -0
  155. package/dist/src/user-context/selection-policy.js +33 -0
  156. package/dist/src/user-context/sources/index.js +2 -2
  157. package/dist/src/user-context/sources/repository.d.ts +2 -1
  158. package/dist/src/user-context/sources/repository.js +26 -5
  159. package/dist/src/voice/dashscope/streaming-stt-session.d.ts +2 -0
  160. package/dist/src/voice/dashscope/streaming-stt-session.js +249 -0
  161. package/dist/src/voice/dashscope/streaming-tts-stream.d.ts +12 -0
  162. package/dist/src/voice/dashscope/streaming-tts-stream.js +153 -0
  163. package/dist/src/voice/realtime/agentEngine.d.ts +10 -0
  164. package/dist/src/voice/realtime/agentEngine.js +403 -0
  165. package/dist/src/voice/realtime/audio-playback-window.d.ts +9 -0
  166. package/dist/src/voice/realtime/audio-playback-window.js +49 -0
  167. package/dist/src/voice/realtime/conversation-context.d.ts +11 -0
  168. package/dist/src/voice/realtime/conversation-context.js +76 -0
  169. package/dist/src/voice/realtime/engine.d.ts +13 -0
  170. package/dist/src/voice/realtime/engine.js +1 -0
  171. package/dist/src/voice/realtime/memory-context.d.ts +17 -0
  172. package/dist/src/voice/realtime/memory-context.js +100 -0
  173. package/dist/src/voice/realtime/omniEngine.d.ts +18 -0
  174. package/dist/src/voice/realtime/omniEngine.js +443 -0
  175. package/dist/src/voice/realtime/omniRoute.d.ts +10 -0
  176. package/dist/src/voice/realtime/omniRoute.js +31 -0
  177. package/dist/src/voice/realtime/runtime.d.ts +99 -0
  178. package/dist/src/voice/realtime/runtime.js +622 -0
  179. package/dist/src/voice/realtime/speakable-segmenter.d.ts +10 -0
  180. package/dist/src/voice/realtime/speakable-segmenter.js +87 -0
  181. package/dist/src/voice/stt/list-providers.js +2 -1
  182. package/dist/src/voice/stt/providers/alibaba-transcription.js +13 -1
  183. package/dist/src/voice/stt/providers/xopc-cloud-transcription.js +23 -0
  184. package/dist/src/voice/tts/factory.js +1 -0
  185. package/dist/src/voice/tts/openai-compatible-speech.js +3 -1
  186. package/dist/src/voice/tts/providers/alibaba-speech.d.ts +1 -5
  187. package/dist/src/voice/tts/providers/alibaba-speech.js +25 -8
  188. package/dist/src/voice/tts/providers/edge-speech.js +21 -22
  189. package/dist/src/voice/tts/providers/minimax-speech.js +6 -3
  190. package/dist/src/voice/tts/providers/xopc-cloud-speech.js +26 -1
  191. package/dist/src/voice/tts/speak-core.d.ts +6 -2
  192. package/dist/src/voice/tts/speak-core.js +46 -34
  193. package/dist/src/voice/tts/speech-provider-types.d.ts +1 -0
  194. package/package.json +5 -5
  195. package/dist/gateway/static/root/assets/Combination-14nmIgxH.js +0 -41
  196. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-Cf7U2aUi.js +0 -1
  197. package/dist/gateway/static/root/assets/agents-Ce9oOnwE.js +0 -1
  198. package/dist/gateway/static/root/assets/app-chrome-BeB4KgOf.js +0 -1
  199. package/dist/gateway/static/root/assets/app-management-settings-panel-CACOOS-a.js +0 -1
  200. package/dist/gateway/static/root/assets/apps-page-BaTFOCmR.js +0 -1
  201. package/dist/gateway/static/root/assets/automations-workspace-DK6EreCr.js +0 -6
  202. package/dist/gateway/static/root/assets/block-editor-DgAs5aJc.js +0 -184
  203. package/dist/gateway/static/root/assets/browser-settings-page-CHStw5UW.js +0 -8
  204. package/dist/gateway/static/root/assets/capabilities-settings-panel-JGH6Kfkr.js +0 -7
  205. package/dist/gateway/static/root/assets/channels-settings-YkUaaWkY.js +0 -1
  206. package/dist/gateway/static/root/assets/connectors-page-BI0FG3gA.js +0 -2
  207. package/dist/gateway/static/root/assets/dist-BPoKqzeH.js +0 -1
  208. package/dist/gateway/static/root/assets/dist-DqPZM2MH.js +0 -1
  209. package/dist/gateway/static/root/assets/home-page--9fKq0mL.js +0 -1
  210. package/dist/gateway/static/root/assets/index-D499B1qS.css +0 -2
  211. package/dist/gateway/static/root/assets/index-J3FsIvTs.js +0 -70
  212. package/dist/gateway/static/root/assets/local-app-workbench-page-FqG00zS1.js +0 -2
  213. package/dist/gateway/static/root/assets/logs-page-c6CfZ8PA.js +0 -2
  214. package/dist/gateway/static/root/assets/messages-_tYhRAHD.js +0 -3
  215. package/dist/gateway/static/root/assets/mobile-pairing-wizard-CHnXZTwb.js +0 -1
  216. package/dist/gateway/static/root/assets/models-hub-cache-DiXjKuuY.js +0 -1
  217. package/dist/gateway/static/root/assets/notes-workbench-DIR37b-K.js +0 -4
  218. package/dist/gateway/static/root/assets/popover-select-DrILF6mR.js +0 -1
  219. package/dist/gateway/static/root/assets/product-open-page-CRm9Q66h.js +0 -1
  220. package/dist/gateway/static/root/assets/project-detail-page-DHiLTiRW.js +0 -1
  221. package/dist/gateway/static/root/assets/projects-page-God7NCcI.js +0 -1
  222. package/dist/gateway/static/root/assets/remote-access-hub-YpF0-mx2.js +0 -1
  223. package/dist/gateway/static/root/assets/sessions-page-CP8O1TxR.js +0 -1
  224. package/dist/gateway/static/root/assets/settings-advanced-gate-DzdbK3j9.js +0 -1
  225. package/dist/gateway/static/root/assets/shares-settings-B7NeVl4g.js +0 -1
  226. package/dist/gateway/static/root/assets/skill-reload-api-DDTmi4a7.js +0 -1
  227. package/dist/gateway/static/root/assets/skills-page.utils--q8p7MAn.js +0 -1
  228. package/dist/gateway/static/root/assets/task-detail-page-yiMzii2b.js +0 -1
  229. package/dist/gateway/static/root/assets/tunnel-api-CC3jOB4n.js +0 -1
  230. package/dist/gateway/static/root/assets/user-context-page-Vtwg7fQc.js +0 -1
  231. package/dist/gateway/static/root/assets/voice-config-api-BDoeXgww.js +0 -1
  232. package/dist/gateway/static/root/assets/work-discovery-overlay-BV_Jj5iD.js +0 -1
  233. package/dist/gateway/static/root/assets/workflow-run-setup-panel-DNHbWGPq.js +0 -3
  234. package/dist/gateway/static/root/assets/workflows-page-BkFIbMrz.js +0 -5
@@ -112,6 +112,65 @@ export interface AudioTranscriptionResult {
112
112
  /** Audio duration in seconds, when reported. */
113
113
  durationSeconds?: number;
114
114
  }
115
+ export interface PcmAudioFormat {
116
+ encoding: 'pcm_s16le';
117
+ sampleRate: 16_000 | 24_000;
118
+ channels: 1;
119
+ }
120
+ export interface StreamingSttCapabilities {
121
+ inputSampleRates: readonly number[];
122
+ turnDetection: readonly 'server_vad'[];
123
+ defaultModel: string;
124
+ models: readonly string[];
125
+ }
126
+ export type StreamingSttEvent = {
127
+ type: 'ready';
128
+ } | {
129
+ type: 'speech_started';
130
+ utteranceId: string;
131
+ } | {
132
+ type: 'speech_stopped';
133
+ utteranceId: string;
134
+ } | {
135
+ type: 'transcript_delta';
136
+ utteranceId: string;
137
+ revision: number;
138
+ text: string;
139
+ } | {
140
+ type: 'transcript_final';
141
+ utteranceId: string;
142
+ revision: number;
143
+ text: string;
144
+ language?: string;
145
+ } | {
146
+ type: 'usage';
147
+ inputAudioMs: number;
148
+ } | {
149
+ type: 'error';
150
+ error: Error;
151
+ };
152
+ export interface StreamingSttOpenRequest {
153
+ model: string;
154
+ inputFormat: PcmAudioFormat;
155
+ apiKey?: string;
156
+ baseUrl?: string;
157
+ headers?: Record<string, string>;
158
+ language?: string;
159
+ prompt?: string;
160
+ turnDetection: {
161
+ mode: 'server_vad';
162
+ silenceDurationMs: number;
163
+ };
164
+ timeoutMs: number;
165
+ signal: AbortSignal;
166
+ onEvent: (event: StreamingSttEvent) => void;
167
+ }
168
+ export interface StreamingSttSession {
169
+ appendAudio(chunk: Uint8Array): void;
170
+ commit(): Promise<void>;
171
+ close(): Promise<void>;
172
+ abort(reason: string): void;
173
+ }
115
174
  export interface ImageDescriptionRequest {
116
175
  buffer: Buffer;
117
176
  fileName: string;
@@ -170,6 +229,8 @@ export interface MediaUnderstandingProvider {
170
229
  /** True when the provider can natively ingest PDF (no OCR pre-step needed). */
171
230
  nativeDocumentInputs?: ReadonlyArray<'pdf'>;
172
231
  transcribeAudio?: (req: AudioTranscriptionRequest) => Promise<AudioTranscriptionResult>;
232
+ streamingAudio?: StreamingSttCapabilities;
233
+ openAudioStream?: (req: StreamingSttOpenRequest) => Promise<StreamingSttSession>;
173
234
  describeImage?: (req: ImageDescriptionRequest) => Promise<ImageDescriptionResult>;
174
235
  describeVideo?: (req: VideoDescriptionRequest) => Promise<VideoDescriptionResult>;
175
236
  }
@@ -267,6 +267,18 @@ function customRowToClientHistory(row) {
267
267
  if (r.role !== "custom" && r.type !== "custom_message") return null;
268
268
  const customType = optionalString(r.customType)?.trim();
269
269
  if (!customType) return null;
270
+ if (customType === "voice_omni_transcript") {
271
+ const details = r.details;
272
+ if (details?.role !== "user" && details?.role !== "assistant") return null;
273
+ const content = typeof r.content === "string" ? r.content : "";
274
+ return {
275
+ role: details.role,
276
+ kind: "message",
277
+ content,
278
+ rawContent: content,
279
+ timestamp: parseTimestampValue(typeof r.timestamp === "string" || typeof r.timestamp === "number" ? r.timestamp : void 0)
280
+ };
281
+ }
270
282
  const content = Array.isArray(r.content) || typeof r.content === "string" ? flattenMessageContent(r.content) : "";
271
283
  const timestamp = parseTimestampValue(typeof r.timestamp === "string" || typeof r.timestamp === "number" ? r.timestamp : void 0);
272
284
  return {
@@ -149,6 +149,7 @@ export declare class SessionIndex extends EventEmitter {
149
149
  data?: unknown;
150
150
  }): Promise<void>;
151
151
  appendTranscriptCustomMessageEntry(key: string, entry: {
152
+ expectedSessionId?: string;
152
153
  customType: string;
153
154
  content?: string | unknown[];
154
155
  display?: boolean;
@@ -1,3 +1,4 @@
1
+ import { voiceTranscriptMessage } from "./voice-transcript.js";
1
2
  import { isCompactionAudit, isCompactionHandover } from "./compaction-types.js";
2
3
  //#region src/session/session-context-for-llm.ts
3
4
  function isTranscriptContextEntry(x) {
@@ -245,6 +246,12 @@ function buildSessionContextForLlm(rows) {
245
246
  continue;
246
247
  }
247
248
  if (isTranscriptCustomMessageEntry(r)) {
249
+ if (r.customType === "voice_call") continue;
250
+ if (r.customType === "voice_omni_transcript") {
251
+ const message = voiceTranscriptMessage(r);
252
+ if (message) out.push(message);
253
+ continue;
254
+ }
248
255
  out.push(customMessageRowToLlmMessage(r));
249
256
  continue;
250
257
  }
@@ -257,6 +264,7 @@ function buildSessionDisplayMessages(rows) {
257
264
  const out = [];
258
265
  for (const r of rows) {
259
266
  if (isTranscriptCustomMessageEntry(r)) {
267
+ if (r.customType === "voice_call") continue;
260
268
  if (r.display === false) continue;
261
269
  out.push(customMessageRowToLlmMessage(r));
262
270
  continue;
@@ -124,6 +124,7 @@ export declare class SessionStore {
124
124
  data?: unknown;
125
125
  }): Promise<void>;
126
126
  appendTranscriptCustomMessageEntry(key: string, entry: {
127
+ expectedSessionId?: string;
127
128
  customType: string;
128
129
  content?: string | unknown[];
129
130
  display?: boolean;
@@ -386,6 +386,7 @@ var SessionStore = class {
386
386
  async appendTranscriptCustomMessageEntry(key, entry) {
387
387
  return this.runStoreMutation(async () => {
388
388
  requireXopcDatabase();
389
+ if (entry.expectedSessionId && getSessionMetadata(key)?.sessionId !== entry.expectedSessionId) throw new Error("Voice conversation session changed");
389
390
  ensureSessionRecord(key, this.resolveWorkspaceCwd(key));
390
391
  appendTranscriptEntry(key, {
391
392
  role: "custom",
@@ -1,3 +1,4 @@
1
+ import { voiceTranscriptMessage } from "./voice-transcript.js";
1
2
  import { isTranscriptContextEntry, isTranscriptCustomMessageEntry, isTranscriptCustomStateEntry } from "./session-context-for-llm.js";
2
3
  import { randomUUID } from "node:crypto";
3
4
  import { CURRENT_SESSION_VERSION } from "@earendil-works/pi-coding-agent";
@@ -100,7 +101,10 @@ function storedRowsToFileEntries(params) {
100
101
  continue;
101
102
  }
102
103
  if (isTranscriptCustomMessageEntry(row)) {
103
- const entry = customMessageRowToEntry(row, parentId, byId);
104
+ if (row.customType === "voice_call") continue;
105
+ const voiceMessage = row.customType === "voice_omni_transcript" ? voiceTranscriptMessage(row) : null;
106
+ if (row.customType === "voice_omni_transcript" && !voiceMessage) continue;
107
+ const entry = voiceMessage ? agentMessageToEntry(voiceMessage, parentId, byId) : customMessageRowToEntry(row, parentId, byId);
104
108
  byId.add(entry.id);
105
109
  entries.push(entry);
106
110
  parentId = entry.id;
@@ -0,0 +1,9 @@
1
+ import type { AgentMessage } from '@earendil-works/pi-agent-core';
2
+ export declare const VOICE_CALL_TYPE = "voice_call";
3
+ export declare const VOICE_TRANSCRIPT_TYPE = "voice_omni_transcript";
4
+ /** One projection for both direct LLM loads and the embedded SessionManager. */
5
+ export declare function voiceTranscriptMessage(row: {
6
+ content?: unknown;
7
+ details?: unknown;
8
+ timestamp?: string | number;
9
+ }): AgentMessage | null;
@@ -0,0 +1,45 @@
1
+ //#region src/session/voice-transcript.ts
2
+ const VOICE_CALL_TYPE = "voice_call";
3
+ const VOICE_TRANSCRIPT_TYPE = "voice_omni_transcript";
4
+ /** One projection for both direct LLM loads and the embedded SessionManager. */
5
+ function voiceTranscriptMessage(row) {
6
+ const details = row.details && typeof row.details === "object" ? row.details : {};
7
+ if (details.role !== "user" && details.role !== "assistant" || typeof row.content !== "string" || !row.content.trim()) return null;
8
+ const text = details.role === "assistant" && details.interrupted === true ? "[Voice reply was interrupted. Its generated text is omitted because the heard portion is unknown.]" : row.content;
9
+ const parsed = typeof row.timestamp === "number" ? row.timestamp : Date.parse(row.timestamp ?? "");
10
+ const timestamp = Number.isFinite(parsed) ? parsed : 0;
11
+ const content = [{
12
+ type: "text",
13
+ text
14
+ }];
15
+ if (details.role === "user") return {
16
+ role: "user",
17
+ content,
18
+ timestamp
19
+ };
20
+ return {
21
+ role: "assistant",
22
+ content,
23
+ timestamp,
24
+ api: "openai-completions",
25
+ provider: "xopc-voice",
26
+ model: "voice-transcript",
27
+ stopReason: "stop",
28
+ usage: {
29
+ input: 0,
30
+ output: 0,
31
+ cacheRead: 0,
32
+ cacheWrite: 0,
33
+ totalTokens: 0,
34
+ cost: {
35
+ input: 0,
36
+ output: 0,
37
+ cacheRead: 0,
38
+ cacheWrite: 0,
39
+ total: 0
40
+ }
41
+ }
42
+ };
43
+ }
44
+ //#endregion
45
+ export { VOICE_CALL_TYPE, VOICE_TRANSCRIPT_TYPE, voiceTranscriptMessage };
@@ -1,4 +1,5 @@
1
1
  import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
2
+ import { notifyUserContextChange } from "../../user-context/changes.js";
2
3
  import { sessionConfigRowToConfig } from "./row-mappers.js";
3
4
  import { ensureSessionInTransaction } from "./session-repository.js";
4
5
  //#region src/storage/sqlite/config-repository.ts
@@ -21,6 +22,10 @@ function listSessionWorkspaceOverrides() {
21
22
  `).all();
22
23
  }
23
24
  function setSessionConfig(sessionKey, config, cwd) {
25
+ notifyUserContextChange({
26
+ kind: "session",
27
+ id: sessionKey
28
+ });
24
29
  return runSqliteWriteTransaction((db) => {
25
30
  ensureSessionInTransaction(db, sessionKey, cwd);
26
31
  const updatedAt = Math.max(Date.now(), (readConfigRow(db, sessionKey)?.updated_at ?? 0) + 1);
@@ -54,6 +59,10 @@ function updateSessionConfig(sessionKey, partial, cwd) {
54
59
  }, cwd);
55
60
  }
56
61
  function deleteSessionConfig(sessionKey) {
62
+ notifyUserContextChange({
63
+ kind: "session",
64
+ id: sessionKey
65
+ });
57
66
  runSqliteWriteTransaction((db) => {
58
67
  db.prepare(`DELETE FROM session_config WHERE session_key = ?`).run(sessionKey);
59
68
  });
@@ -0,0 +1,3 @@
1
+ export type RemoteContextKind = 'understanding' | 'focus';
2
+ /** Check all provenance, including older evidence, without loading private excerpts. */
3
+ export declare function remoteContextEligibleIds(kind: RemoteContextKind, ids: string[]): Set<string>;
@@ -0,0 +1,40 @@
1
+ import { getSqliteDatabase } from "./transaction.js";
2
+ import { USER_CONTEXT_PRINCIPAL_ID } from "../../user-context/domain.js";
3
+ //#region src/storage/sqlite/remote-context-repository.ts
4
+ /** Check all provenance, including older evidence, without loading private excerpts. */
5
+ function remoteContextEligibleIds(kind, ids) {
6
+ if (!ids.length) return /* @__PURE__ */ new Set();
7
+ if (ids.length > 200) throw new Error("Context candidate limit exceeded");
8
+ const focus = kind === "focus";
9
+ const table = focus ? "user_focuses" : "user_understandings";
10
+ const versions = focus ? "user_focus_versions" : "user_understanding_versions";
11
+ const links = focus ? "user_focus_evidence_links" : "understanding_evidence_links";
12
+ const id = focus ? "focus_id" : "understanding_id";
13
+ const rows = getSqliteDatabase().prepare(`
14
+ SELECT u.${id} AS id, v.created_by, u.explicitness,
15
+ COUNT(e.evidence_id) AS evidence_count,
16
+ MAX(CASE WHEN e.evidence_id IS NOT NULL AND (
17
+ (e.processing_policy IS NOT NULL AND e.processing_policy <> 'remote_allowed')
18
+ OR e.trust_level = 'untrusted' OR e.principal_id <> u.principal_id
19
+ OR (e.source_run_id IS NOT NULL AND (g.grant_id IS NULL OR g.status <> 'active' OR g.processing_policy <> 'remote_allowed'))
20
+ OR (e.source_type = 'connector' AND (e.processing_policy IS NULL OR e.source_run_id IS NULL))
21
+ OR (e.processing_policy IS NULL AND e.source_type <> 'user' AND e.trust_level <> 'owner')
22
+ ) THEN 1 ELSE 0 END) AS blocked
23
+ FROM ${table} u
24
+ JOIN ${versions} v ON v.version_id = u.current_version_id
25
+ LEFT JOIN ${versions} history ON history.${id} = u.${id}
26
+ LEFT JOIN ${links} l ON l.version_id = history.version_id
27
+ LEFT JOIN context_evidence e ON e.evidence_id = l.evidence_id
28
+ LEFT JOIN understanding_source_runs r ON r.run_id = e.source_run_id
29
+ LEFT JOIN understanding_source_grants g ON g.grant_id = r.grant_id
30
+ WHERE u.principal_id = ? AND u.${id} IN (${ids.map(() => "?").join(",")})
31
+ ${focus ? `AND (u.source_run_id IS NULL OR EXISTS (
32
+ SELECT 1 FROM understanding_source_runs fr JOIN understanding_source_grants fg ON fg.grant_id = fr.grant_id
33
+ WHERE fr.run_id = u.source_run_id AND fg.status = 'active' AND fg.processing_policy = 'remote_allowed'
34
+ ))` : ""}
35
+ GROUP BY u.${id}
36
+ `).all(USER_CONTEXT_PRINCIPAL_ID, ...ids);
37
+ return new Set(rows.filter((row) => !row.blocked && (row.evidence_count > 0 || row.explicitness === "explicit" && row.created_by === "user")).map((row) => row.id));
38
+ }
39
+ //#endregion
40
+ export { remoteContextEligibleIds };
@@ -1,4 +1,5 @@
1
1
  import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
2
+ import { notifyUserContextChange } from "../../user-context/changes.js";
2
3
  import { buildFts5SearchQuery } from "./fts.js";
3
4
  import { validateSessionId } from "../../session/session-id.js";
4
5
  import { buildDefaultSessionMetadata } from "./session-metadata.js";
@@ -175,6 +176,10 @@ function sessionSortColumn(sortBy) {
175
176
  }
176
177
  }
177
178
  function patchSessionMetadata(sessionKey, updates) {
179
+ if ("projectId" in updates) notifyUserContextChange({
180
+ kind: "session-reset",
181
+ id: sessionKey
182
+ });
178
183
  return runSqliteWriteTransaction((db) => {
179
184
  const existing = readSessionRow(db, sessionKey);
180
185
  if (!existing) throw new Error(`Session not found: ${sessionKey}`);
@@ -242,6 +247,10 @@ function incrementSessionStatsOnAppend(sessionKey, tokenDelta = 0) {
242
247
  });
243
248
  }
244
249
  function resetSessionRecord(sessionKey, cwd) {
250
+ notifyUserContextChange({
251
+ kind: "session-reset",
252
+ id: sessionKey
253
+ });
245
254
  return runSqliteWriteTransaction((db) => {
246
255
  const existing = readSessionRow(db, sessionKey);
247
256
  if (!existing) return null;
@@ -267,6 +276,10 @@ function resetSessionRecord(sessionKey, cwd) {
267
276
  });
268
277
  }
269
278
  function deleteSessionRecord(sessionKey) {
279
+ notifyUserContextChange({
280
+ kind: "session-reset",
281
+ id: sessionKey
282
+ });
270
283
  return runSqliteWriteTransaction((db) => {
271
284
  const existing = readSessionRow(db, sessionKey);
272
285
  if (!existing) return false;
@@ -18,7 +18,7 @@ export type CreateUnderstandingInput = Omit<UserUnderstanding, 'id' | 'versionId
18
18
  };
19
19
  export declare function createUnderstanding(input: CreateUnderstandingInput, principalId?: string): UserUnderstanding;
20
20
  export declare function getUnderstanding(id: string): UserUnderstanding | undefined;
21
- export declare function listUnderstandings(statuses?: UnderstandingStatus[], principalId?: string): UserUnderstanding[];
21
+ export declare function listUnderstandings(statuses?: UnderstandingStatus[], principalId?: string, limit?: number): UserUnderstanding[];
22
22
  export declare function searchActiveUnderstandings(query: string, limit?: number): Array<{
23
23
  understanding: UserUnderstanding;
24
24
  score: number;
@@ -52,6 +52,8 @@ export declare function createContextEvidence(input: Omit<ContextEvidence, 'id'
52
52
  }, principalId?: string): ContextEvidence;
53
53
  export declare function linkUnderstandingEvidence(versionId: string, evidenceId: string, relation: 'supports' | 'contradicts' | 'supersedes', confidence: number): void;
54
54
  export declare function listUnderstandingEvidence(understandingId: string, relation?: 'supports' | 'contradicts' | 'supersedes'): ContextEvidence[];
55
+ /** Latest source labels for ranking, without fetching evidence bodies per candidate. */
56
+ export declare function understandingSourceBuckets(ids: string[]): Map<string, string>;
55
57
  export declare function recordContextRun(input: {
56
58
  turnId: string;
57
59
  sessionKey: string;
@@ -1,5 +1,6 @@
1
1
  import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
2
2
  import { USER_CONTEXT_PRINCIPAL_ID } from "../../user-context/domain.js";
3
+ import { notifyUserContextChange } from "../../user-context/changes.js";
3
4
  import { retrievalQueryAuditValue } from "../../retrieval/audit.js";
4
5
  import { buildFts5SearchQuery, fts5RankToScore } from "./fts.js";
5
6
  import { createHash, randomUUID } from "node:crypto";
@@ -108,6 +109,10 @@ function getUserProfile(principalId = USER_CONTEXT_PRINCIPAL_ID) {
108
109
  };
109
110
  }
110
111
  function updateUserProfile(patch, principalId = USER_CONTEXT_PRINCIPAL_ID) {
112
+ notifyUserContextChange({
113
+ kind: "profile",
114
+ id: "profile"
115
+ });
111
116
  const current = getUserProfile(principalId);
112
117
  const now = Date.now();
113
118
  const next = {
@@ -163,11 +168,12 @@ function getUnderstanding(id) {
163
168
  const row = getSqliteDatabase().prepare(`${UNDERSTANDING_SELECT} WHERE u.understanding_id = ?`).get(id);
164
169
  return row ? understandingFromRow(row) : void 0;
165
170
  }
166
- function listUnderstandings(statuses, principalId = USER_CONTEXT_PRINCIPAL_ID) {
171
+ function listUnderstandings(statuses, principalId = USER_CONTEXT_PRINCIPAL_ID, limit) {
167
172
  const db = getSqliteDatabase();
168
- if (!statuses?.length) return db.prepare(`${UNDERSTANDING_SELECT} WHERE u.principal_id = ? ORDER BY u.updated_at DESC`).all(principalId).map(understandingFromRow);
173
+ const maxRows = limit === void 0 ? -1 : Math.max(1, Math.min(500, Math.floor(limit)));
174
+ if (!statuses?.length) return db.prepare(`${UNDERSTANDING_SELECT} WHERE u.principal_id = ? ORDER BY u.updated_at DESC LIMIT ?`).all(principalId, maxRows).map(understandingFromRow);
169
175
  const placeholders = statuses.map(() => "?").join(", ");
170
- return db.prepare(`${UNDERSTANDING_SELECT} WHERE u.principal_id = ? AND u.status IN (${placeholders}) ORDER BY u.updated_at DESC`).all(principalId, ...statuses).map(understandingFromRow);
176
+ return db.prepare(`${UNDERSTANDING_SELECT} WHERE u.principal_id = ? AND u.status IN (${placeholders}) ORDER BY u.updated_at DESC LIMIT ?`).all(principalId, ...statuses, maxRows).map(understandingFromRow);
171
177
  }
172
178
  function searchActiveUnderstandings(query, limit = 50) {
173
179
  const ftsQuery = buildFts5SearchQuery(query);
@@ -193,6 +199,10 @@ function searchActiveUnderstandings(query, limit = 50) {
193
199
  }));
194
200
  }
195
201
  function reviseUnderstanding(id, statement, options) {
202
+ notifyUserContextChange({
203
+ kind: "understanding",
204
+ id
205
+ });
196
206
  const current = getUnderstanding(id);
197
207
  if (!current) throw new Error(`User understanding not found: ${id}`);
198
208
  const versionId = randomUUID();
@@ -210,6 +220,10 @@ function reviseUnderstanding(id, statement, options) {
210
220
  return getUnderstanding(id);
211
221
  }
212
222
  function setUnderstandingStatus(id, status, options) {
223
+ notifyUserContextChange({
224
+ kind: "understanding",
225
+ id
226
+ });
213
227
  const current = getUnderstanding(id);
214
228
  if (!current) throw new Error(`User understanding not found: ${id}`);
215
229
  const now = Date.now();
@@ -237,6 +251,10 @@ function setUnderstandingStatus(id, status, options) {
237
251
  return result;
238
252
  }
239
253
  function closeUnderstandingValidity(id, validTo = Date.now()) {
254
+ notifyUserContextChange({
255
+ kind: "understanding",
256
+ id
257
+ });
240
258
  getSqliteDatabase().prepare(`UPDATE user_understandings SET valid_to = ?, updated_at = ?
241
259
  WHERE understanding_id = ?`).run(validTo, validTo, id);
242
260
  const result = getUnderstanding(id);
@@ -244,6 +262,10 @@ function closeUnderstandingValidity(id, validTo = Date.now()) {
244
262
  return result;
245
263
  }
246
264
  function rejectUnderstanding(id, reason, actorType = "runtime") {
265
+ notifyUserContextChange({
266
+ kind: "understanding",
267
+ id
268
+ });
247
269
  const current = getUnderstanding(id);
248
270
  if (!current) throw new Error(`User understanding not found: ${id}`);
249
271
  const now = Date.now();
@@ -277,6 +299,10 @@ function listUnderstandingStatusEvents(id) {
277
299
  }));
278
300
  }
279
301
  function deleteUnderstanding(id) {
302
+ notifyUserContextChange({
303
+ kind: "understanding",
304
+ id
305
+ });
280
306
  return runSqliteWriteTransaction((db) => {
281
307
  db.prepare("DELETE FROM user_understanding_fts WHERE understanding_id = ?").run(id);
282
308
  return Number(db.prepare("DELETE FROM user_understandings WHERE understanding_id = ?").run(id).changes) > 0;
@@ -342,6 +368,7 @@ function createContextEvidence(input, principalId = USER_CONTEXT_PRINCIPAL_ID) {
342
368
  AND COALESCE(source_instance_id, '') = COALESCE(?, '') AND source_ref = ?`).get(principalId, input.sourceType, input.sourceInstanceId ?? null, input.sourceRef);
343
369
  const derivedHash = input.contentHash ?? (input.redactedExcerpt ? createHash("sha256").update(input.redactedExcerpt).digest("hex") : void 0);
344
370
  if (existing) {
371
+ notifyUserContextChange({ kind: "policy" });
345
372
  getSqliteDatabase().prepare(`UPDATE context_evidence SET
346
373
  source_run_id = COALESCE(source_run_id, ?), source_item_id = COALESCE(source_item_id, ?),
347
374
  session_id = COALESCE(session_id, ?), turn_id = COALESCE(turn_id, ?),
@@ -373,6 +400,11 @@ function createContextEvidence(input, principalId = USER_CONTEXT_PRINCIPAL_ID) {
373
400
  return evidence;
374
401
  }
375
402
  function linkUnderstandingEvidence(versionId, evidenceId, relation, confidence) {
403
+ const owner = getSqliteDatabase().prepare("SELECT understanding_id AS id FROM user_understanding_versions WHERE version_id = ?").get(versionId);
404
+ if (owner) notifyUserContextChange({
405
+ kind: "understanding",
406
+ id: owner.id
407
+ });
376
408
  getSqliteDatabase().prepare(`INSERT INTO understanding_evidence_links (
377
409
  version_id, evidence_id, relation, confidence
378
410
  ) VALUES (?, ?, ?, ?)
@@ -385,6 +417,20 @@ function listUnderstandingEvidence(understandingId, relation) {
385
417
  WHERE v.understanding_id = ? AND (? IS NULL OR l.relation = ?)
386
418
  ORDER BY e.observed_at DESC`).all(understandingId, relation ?? null, relation ?? null).map(evidenceFromRow);
387
419
  }
420
+ /** Latest source labels for ranking, without fetching evidence bodies per candidate. */
421
+ function understandingSourceBuckets(ids) {
422
+ if (!ids.length) return /* @__PURE__ */ new Map();
423
+ const rows = getSqliteDatabase().prepare(`
424
+ SELECT understanding_id, source_type, source_instance_id, source_ref FROM (
425
+ SELECT v.understanding_id, e.source_type, e.source_instance_id, e.source_ref,
426
+ ROW_NUMBER() OVER (PARTITION BY v.understanding_id ORDER BY e.observed_at DESC) AS rank
427
+ FROM context_evidence e JOIN understanding_evidence_links l ON l.evidence_id = e.evidence_id
428
+ JOIN user_understanding_versions v ON v.version_id = l.version_id
429
+ WHERE v.understanding_id IN (${ids.map(() => "?").join(",")})
430
+ ) WHERE rank = 1
431
+ `).all(...ids);
432
+ return new Map(rows.map((row) => [row.understanding_id, `${row.source_type}:${row.source_instance_id ?? row.source_ref.split(":entry:")[0]}`]));
433
+ }
388
434
  function recordContextRun(input) {
389
435
  const auditedQuery = retrievalQueryAuditValue(input.query);
390
436
  const existing = getSqliteDatabase().prepare("SELECT context_run_id FROM context_runs WHERE turn_id = ?").get(input.turnId);
@@ -486,4 +532,4 @@ function decideContextConsent(id, decision) {
486
532
  return Number(result.changes) > 0;
487
533
  }
488
534
  //#endregion
489
- export { closeUnderstandingValidity, consumeContextConsent, createCollaborationRule, createContextEvidence, createUnderstanding, decideContextConsent, deleteCollaborationRule, deleteUnderstanding, ensureContextConsent, getCollaborationRule, getTurnPersonalization, getUnderstanding, getUserProfile, hasContextConsent, isUnderstandingSuppressed, linkUnderstandingEvidence, listCollaborationRules, listUnderstandingEvidence, listUnderstandingStatusEvents, listUnderstandings, recordContextFeedback, recordContextRun, rejectUnderstanding, reviseCollaborationRule, reviseUnderstanding, searchActiveUnderstandings, setCollaborationRuleStatus, setUnderstandingStatus, updateUserProfile };
535
+ export { closeUnderstandingValidity, consumeContextConsent, createCollaborationRule, createContextEvidence, createUnderstanding, decideContextConsent, deleteCollaborationRule, deleteUnderstanding, ensureContextConsent, getCollaborationRule, getTurnPersonalization, getUnderstanding, getUserProfile, hasContextConsent, isUnderstandingSuppressed, linkUnderstandingEvidence, listCollaborationRules, listUnderstandingEvidence, listUnderstandingStatusEvents, listUnderstandings, recordContextFeedback, recordContextRun, rejectUnderstanding, reviseCollaborationRule, reviseUnderstanding, searchActiveUnderstandings, setCollaborationRuleStatus, setUnderstandingStatus, understandingSourceBuckets, updateUserProfile };
@@ -477,7 +477,8 @@ async function runTui(opts) {
477
477
  isHidden: () => hidden,
478
478
  focus: () => tui.setFocus(component),
479
479
  unfocus: () => tui.setFocus(editor),
480
- isFocused: () => false
480
+ isFocused: () => false,
481
+ getBounds: () => void 0
481
482
  };
482
483
  };
483
484
  const closeCommandOverlay = () => {
@@ -0,0 +1,17 @@
1
+ import { type RemoteContextKind } from '../storage/sqlite/remote-context-repository.js';
2
+ import type { UserContextScopeTarget } from './scope.js';
3
+ export interface AutomaticContextReference {
4
+ kind: RemoteContextKind | 'profile';
5
+ id: string;
6
+ version: string;
7
+ }
8
+ export interface AutomaticContextSelection {
9
+ block: string;
10
+ references: AutomaticContextReference[];
11
+ }
12
+ /** Read-only background selection. Never consumes consent or starts a model turn. */
13
+ export declare function selectAutomaticContext(input: UserContextScopeTarget & {
14
+ query: string;
15
+ maxChars: number;
16
+ deadline: number;
17
+ }): AutomaticContextSelection;
@@ -0,0 +1,85 @@
1
+ import { getUserProfile, listUnderstandings } from "../storage/sqlite/user-context-repository.js";
2
+ import { listUserFocuses } from "./sources/repository.js";
3
+ import { UserUnderstandingRetriever } from "./retriever.js";
4
+ import { allowsAutomaticDisclosure, focusRejectionReason, focusScore, rejectionReason } from "./selection-policy.js";
5
+ import { remoteContextEligibleIds } from "../storage/sqlite/remote-context-repository.js";
6
+ //#region src/user-context/automatic-context.ts
7
+ /** Read-only background selection. Never consumes consent or starts a model turn. */
8
+ function selectAutomaticContext(input) {
9
+ const references = [];
10
+ const facts = [];
11
+ const profile = {};
12
+ const render = () => JSON.stringify({ backgroundMemory: {
13
+ profile,
14
+ facts
15
+ } });
16
+ const withinDeadline = () => performance.now() < input.deadline;
17
+ if (input.maxChars < 128 || !withinDeadline()) return {
18
+ block: "",
19
+ references
20
+ };
21
+ const user = getUserProfile();
22
+ for (const key of [
23
+ "callName",
24
+ "locale",
25
+ "timezone",
26
+ "pronouns",
27
+ "role",
28
+ "primaryGoal"
29
+ ]) {
30
+ if (!user[key] || user[key].length > 300) continue;
31
+ profile[key] = user[key];
32
+ if (render().length > input.maxChars) delete profile[key];
33
+ }
34
+ if (Object.keys(profile).length) references.push({
35
+ kind: "profile",
36
+ id: "profile",
37
+ version: JSON.stringify(user)
38
+ });
39
+ const query = input.query.trim();
40
+ const candidates = !withinDeadline() ? [] : query ? new UserUnderstandingRetriever().retrieve({
41
+ ...input,
42
+ maxCandidates: 50
43
+ }) : listUnderstandings(["active"], void 0, 200).filter((item) => item.explicitness === "explicit" && ["preference", "boundary"].includes(item.kind) && item.durability === "durable").map((understanding) => ({
44
+ understanding,
45
+ score: 1
46
+ }));
47
+ const now = Date.now();
48
+ const eligible = candidates.filter(({ understanding: item, score }) => item.status === "active" && allowsAutomaticDisclosure(item) && !rejectionReason(item, input, now, []) && score > .25);
49
+ const allowed = withinDeadline() ? remoteContextEligibleIds("understanding", eligible.map(({ understanding }) => understanding.id)) : /* @__PURE__ */ new Set();
50
+ const append = (text, origin, reference) => {
51
+ if (!withinDeadline() || facts.length >= 6 || !text.trim()) return;
52
+ facts.push({
53
+ text,
54
+ origin
55
+ });
56
+ if (render().length > input.maxChars) {
57
+ facts.pop();
58
+ return;
59
+ }
60
+ references.push(reference);
61
+ };
62
+ for (const { understanding: item } of eligible) if (allowed.has(item.id)) append(item.statement, item.explicitness, {
63
+ kind: "understanding",
64
+ id: item.id,
65
+ version: item.versionId
66
+ });
67
+ if (query && withinDeadline() && facts.length < 6) {
68
+ const focuses = listUserFocuses(["active"], 50).filter((item) => allowsAutomaticDisclosure(item) && !focusRejectionReason(item, input, now)).map((item) => ({
69
+ item,
70
+ score: focusScore(item, query, input)
71
+ })).filter(({ score }) => score > .25).sort((a, b) => b.score - a.score);
72
+ const allowedFocuses = remoteContextEligibleIds("focus", focuses.map(({ item }) => item.id));
73
+ for (const { item } of focuses) if (allowedFocuses.has(item.id)) append(`${item.title}: ${item.summary}`, item.explicitness, {
74
+ kind: "focus",
75
+ id: item.id,
76
+ version: item.versionId ?? String(item.updatedAt)
77
+ });
78
+ }
79
+ return {
80
+ block: references.length ? render() : "",
81
+ references
82
+ };
83
+ }
84
+ //#endregion
85
+ export { selectAutomaticContext };
@@ -0,0 +1,7 @@
1
+ export interface UserContextChange {
2
+ kind: 'profile' | 'understanding' | 'focus' | 'policy' | 'session' | 'session-reset';
3
+ id?: string;
4
+ }
5
+ export declare function onUserContextChange(listener: (change: UserContextChange) => void): () => void;
6
+ /** Dispatch after the synchronous write/outer transaction has left the stack. */
7
+ export declare function notifyUserContextChange(change: UserContextChange): void;
@@ -0,0 +1,28 @@
1
+ import { createLogger } from "../utils/logger/index.js";
2
+ import { init_logger } from "../utils/logger.js";
3
+ //#region src/user-context/changes.ts
4
+ init_logger();
5
+ const listeners = /* @__PURE__ */ new Set();
6
+ const log = createLogger("UserContextChanges");
7
+ function onUserContextChange(listener) {
8
+ listeners.add(listener);
9
+ return () => {
10
+ listeners.delete(listener);
11
+ };
12
+ }
13
+ /** Dispatch after the synchronous write/outer transaction has left the stack. */
14
+ function notifyUserContextChange(change) {
15
+ if (!listeners.size) return;
16
+ queueMicrotask(() => {
17
+ for (const listener of listeners) try {
18
+ listener(change);
19
+ } catch (err) {
20
+ log.error({
21
+ err,
22
+ kind: change.kind
23
+ }, "User context change listener failed");
24
+ }
25
+ });
26
+ }
27
+ //#endregion
28
+ export { notifyUserContextChange, onUserContextChange };