@xopcai/xopc 0.0.264 → 0.0.266

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 (209) hide show
  1. package/dist/browser-ext/dist/assets/auth-CknJ00ae.js +5844 -0
  2. package/dist/browser-ext/dist/assets/sidepanel-C69P3v9u.css +113 -0
  3. package/dist/browser-ext/dist/background.js +200 -68
  4. package/dist/browser-ext/dist/sidepanel.html +14 -0
  5. package/dist/browser-ext/dist/sidepanel.js +18832 -0
  6. package/dist/browser-ext/manifest.json +20 -20
  7. package/dist/extensions/telegram/xopc.extension.json +1 -1
  8. package/dist/gateway/static/root/assets/{activity-store-BYBCpt7_.js → activity-store-Bhyy6ENz.js} +1 -1
  9. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-D20aBHPJ.js → agent-avatar-dicebear-Qrp5ALCF.js} +1 -1
  10. package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-BZvMqNrU.js → agent-defaults-settings-panel-DwinST48.js} +1 -1
  11. package/dist/gateway/static/root/assets/{agents-6foBELwt.js → agents-Dlr0YyNt.js} +1 -1
  12. package/dist/gateway/static/root/assets/{api-YbtjDbuG.js → api-B2y7ZjZk.js} +1 -1
  13. package/dist/gateway/static/root/assets/{app-management-settings-panel-4ps1RrDU.js → app-management-settings-panel-bLNvz93c.js} +1 -1
  14. package/dist/gateway/static/root/assets/{appearance-settings-C7nBHnkG.js → appearance-settings-D5qgIepF.js} +1 -1
  15. package/dist/gateway/static/root/assets/{apps-page-B_g-rL5b.js → apps-page-BQVlXhIb.js} +1 -1
  16. package/dist/gateway/static/root/assets/{archive-plugin-DfKK1iJO.js → archive-plugin-BEA7VAA4.js} +1 -1
  17. package/dist/gateway/static/root/assets/{attachment-load-BOU1pdnQ.js → attachment-load-B1RyF3uu.js} +1 -1
  18. package/dist/gateway/static/root/assets/{automations-page-BePzexAH.js → automations-page-BfH0FdKa.js} +1 -1
  19. package/dist/gateway/static/root/assets/{automations-workspace-7-m5pBEZ.js → automations-workspace-C37AlD3E.js} +1 -1
  20. package/dist/gateway/static/root/assets/{binary-plugins-Cly8XDyt.js → binary-plugins-EU884ZXi.js} +1 -1
  21. package/dist/gateway/static/root/assets/{block-editor-DzSu62js.js → block-editor-Bjr3naHb.js} +1 -1
  22. package/dist/gateway/static/root/assets/{browser-automation-inputs-8bHj7Fv2.js → browser-automation-inputs-s5I78Iko.js} +1 -1
  23. package/dist/gateway/static/root/assets/{browser-automations-page-CzL7Aj2k.js → browser-automations-page-CDrYrNGy.js} +1 -1
  24. package/dist/gateway/static/root/assets/{browser-settings-page-Dum3mTiz.js → browser-settings-page-DOmUkj2k.js} +1 -1
  25. package/dist/gateway/static/root/assets/{capabilities-settings-panel-Ci6qVS-O.js → capabilities-settings-panel-slt1o04n.js} +2 -2
  26. package/dist/gateway/static/root/assets/{channels-settings-BOGEZnSv.js → channels-settings-oHTTX6Jy.js} +1 -1
  27. package/dist/gateway/static/root/assets/{channels-status-swr-BR8oQ_eP.js → channels-status-swr-_OsWr_vS.js} +1 -1
  28. package/dist/gateway/static/root/assets/{chat-terminal-dock-BF9ZXa5Z.js → chat-terminal-dock-DEYQdv6n.js} +1 -1
  29. package/dist/gateway/static/root/assets/{connectors-page-DBvEvupa.js → connectors-page-olrbxHIT.js} +1 -1
  30. package/dist/gateway/static/root/assets/{cron-expression-BszT_gjK.js → cron-expression-DFrbDWpP.js} +1 -1
  31. package/dist/gateway/static/root/assets/{date-picker-D2MKBJ0G.js → date-picker-CYriYKpH.js} +1 -1
  32. package/dist/gateway/static/root/assets/{dependency-picker-DqigBx-G.js → dependency-picker-XY3qUErs.js} +1 -1
  33. package/dist/gateway/static/root/assets/{desktop-pet-CapspBGG.js → desktop-pet-Dsc__ynA.js} +1 -1
  34. package/dist/gateway/static/root/assets/{desktop-pet-settings-DMPk4CaK.js → desktop-pet-settings-EpuX3C08.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-debug-page-CXLsDtrW.js → extension-debug-page-B_NeLV4K.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-page-B2j87ROw.js → extension-page-D3JC6mLB.js} +1 -1
  37. package/dist/gateway/static/root/assets/{extension-settings-page-t1BApw-u.js → extension-settings-page-CXTQt1lp.js} +1 -1
  38. package/dist/gateway/static/root/assets/{files-BArWTfr7.js → files-Be5viEFY.js} +1 -1
  39. package/dist/gateway/static/root/assets/{gateway-config-swr-BFSesvAC.js → gateway-config-swr-OObCK8jM.js} +1 -1
  40. package/dist/gateway/static/root/assets/{gateway-settings-BFAecoGy.js → gateway-settings-yeYm_UPE.js} +1 -1
  41. package/dist/gateway/static/root/assets/{gateway-startup-retry-D1giYgI-.js → gateway-startup-retry-D_TxKN_A.js} +1 -1
  42. package/dist/gateway/static/root/assets/{heartbeat-settings-CyD9zDcU.js → heartbeat-settings-coh1LFsg.js} +1 -1
  43. package/dist/gateway/static/root/assets/{home-page-uItZMiBE.js → home-page-CAgoBZqp.js} +1 -1
  44. package/dist/gateway/static/root/assets/{image-generation-api-BeB-OPqO.js → image-generation-api-fPU9yGsw.js} +1 -1
  45. package/dist/gateway/static/root/assets/index-Cn__9Un2.js +74 -0
  46. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-Q_CM-ZCs.js → keyboard-shortcuts-settings-Cu1bI3Bg.js} +1 -1
  47. package/dist/gateway/static/root/assets/{local-app-workbench-page-DaiBWfOt.js → local-app-workbench-page-C9s2lFaI.js} +1 -1
  48. package/dist/gateway/static/root/assets/{local-apps-page-BALMNCSp.js → local-apps-page-CJ03Umjr.js} +1 -1
  49. package/dist/gateway/static/root/assets/{locale-store-RJHYFoq9.js → locale-store-DFQX9kTl.js} +1 -1
  50. package/dist/gateway/static/root/assets/{logs-page-C-TS7oFg.js → logs-page-CY_DQKJ2.js} +1 -1
  51. package/dist/gateway/static/root/assets/{management-settings-BajkJeYl.js → management-settings-YPEemeMo.js} +1 -1
  52. package/dist/gateway/static/root/assets/{markdown-view-BxoQJqHx.js → markdown-view-D-5jHRWg.js} +1 -1
  53. package/dist/gateway/static/root/assets/{media-plugins-C_8Hl6KG.js → media-plugins-BjT6jlrA.js} +1 -1
  54. package/dist/gateway/static/root/assets/{messages-Cf1h5WXB.js → messages-Dj8QuS6e.js} +2 -2
  55. package/dist/gateway/static/root/assets/{mobile-pairing-wizard-CsWIZrZW.js → mobile-pairing-wizard-A8eLtV6v.js} +1 -1
  56. package/dist/gateway/static/root/assets/{note-detail-panel-CCbPg_GP.js → note-detail-panel-DHT2QksJ.js} +3 -3
  57. package/dist/gateway/static/root/assets/{notes-page-BdxxcUKa.js → notes-page-BRFQiivM.js} +1 -1
  58. package/dist/gateway/static/root/assets/{page-tabs-BMYQef5D.js → page-tabs-DNUwulnl.js} +1 -1
  59. package/dist/gateway/static/root/assets/{preview-open-alternatives-CKZ-H5hJ.js → preview-open-alternatives-B33KsKkJ.js} +1 -1
  60. package/dist/gateway/static/root/assets/{product-delivery-CV4pDOtG.js → product-delivery-CCC4FJ3Y.js} +1 -1
  61. package/dist/gateway/static/root/assets/{product-open-page-zXJIptXs.js → product-open-page-CIGLPFm7.js} +1 -1
  62. package/dist/gateway/static/root/assets/{project-detail-page-DRhhxhZB.js → project-detail-page-CItEFVt9.js} +1 -1
  63. package/dist/gateway/static/root/assets/{projects-page-Wt7gF3Sx.js → projects-page-Bmy-c_lL.js} +1 -1
  64. package/dist/gateway/static/root/assets/{registry-api-BSfFaxVN.js → registry-api-CsZLatKo.js} +1 -1
  65. package/dist/gateway/static/root/assets/{remote-access-hub-B6de5Zrb.js → remote-access-hub-H6B1cA2V.js} +1 -1
  66. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DcUB5h1f.js → runtime-tools-settings-panel-BsCovC39.js} +1 -1
  67. package/dist/gateway/static/root/assets/{schema-form-CquWhe7r.js → schema-form-NsPb_Vam.js} +1 -1
  68. package/dist/gateway/static/root/assets/{schemas-BHS7bo06.js → schemas-BaskVM5g.js} +2 -2
  69. package/dist/gateway/static/root/assets/{sessions-page-BNWO2EVF.js → sessions-page-CyYLSXk_.js} +1 -1
  70. package/dist/gateway/static/root/assets/settings-advanced-gate-3T4rPk4A.js +1 -0
  71. package/dist/gateway/static/root/assets/{settings-form-section-DwswAefc.js → settings-form-section-BzPIPe1-.js} +1 -1
  72. package/dist/gateway/static/root/assets/{settings-page-CoAVmjjs.js → settings-page-4idpMhsw.js} +1 -1
  73. package/dist/gateway/static/root/assets/{setup-status-panel-C9EIcWtH.js → setup-status-panel-D2ds_TTD.js} +1 -1
  74. package/dist/gateway/static/root/assets/{share-preview-page-DWwy_4zh.js → share-preview-page-Djo3VTaC.js} +1 -1
  75. package/dist/gateway/static/root/assets/{shares-settings-CIUvq27-.js → shares-settings-fhRzNfRp.js} +1 -1
  76. package/dist/gateway/static/root/assets/skill-reload-api-Ct8tUju_.js +1 -0
  77. package/dist/gateway/static/root/assets/{skills-page-CqsgVtVh.js → skills-page-riM7FytZ.js} +1 -1
  78. package/dist/gateway/static/root/assets/{skills-page.utils-B-N73248.js → skills-page.utils-B3G8LBb-.js} +1 -1
  79. package/dist/gateway/static/root/assets/{system-settings-panel-BndjXuBZ.js → system-settings-panel-CW91jjnB.js} +1 -1
  80. package/dist/gateway/static/root/assets/{task-detail-page-CG6Ha9t7.js → task-detail-page-BBXKvl0k.js} +1 -1
  81. package/dist/gateway/static/root/assets/{theme-store-C5ssn64t.js → theme-store-zRT6vaZc.js} +1 -1
  82. package/dist/gateway/static/root/assets/{tunnel-api-BxZdcp3h.js → tunnel-api-DILER0Yb.js} +1 -1
  83. package/dist/gateway/static/root/assets/{use-autosave-mj9CUuz1.js → use-autosave-GxnP8vLO.js} +1 -1
  84. package/dist/gateway/static/root/assets/{user-model-page-DKeHMXI4.js → user-model-page-I1H4XRfA.js} +1 -1
  85. package/dist/gateway/static/root/assets/{voice-api-key-field-CzxVMYlr.js → voice-api-key-field-CI3sXyl4.js} +1 -1
  86. package/dist/gateway/static/root/assets/{voice-config-api-COA0V1cK.js → voice-config-api-DpJEu2RQ.js} +1 -1
  87. package/dist/gateway/static/root/assets/{work-discovery-overlay-DMiPK8dp.js → work-discovery-overlay-DZC9Exn3.js} +1 -1
  88. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-C82OXCJK.js → workflow-run-setup-panel-CaBaQER2.js} +1 -1
  89. package/dist/gateway/static/root/assets/{workflows-page-HLCUV3rQ.js → workflows-page-C-_Dxihw.js} +1 -1
  90. package/dist/gateway/static/root/index.html +12 -12
  91. package/dist/package.js +1 -1
  92. package/dist/packages/browser-control-contract/src/index.js +14 -0
  93. package/dist/packages/endpoint-tools-protocol/src/index.js +2 -1
  94. package/dist/packages/gateway-contract/src/browser-page-context.js +43 -0
  95. package/dist/packages/gateway-contract/src/browser-tab-binding.js +29 -0
  96. package/dist/packages/gateway-contract/src/device-pairing.js +18 -2
  97. package/dist/packages/realtime-protocol/src/index.js +1 -0
  98. package/dist/packages/realtime-protocol/src/voice-audio.js +70 -9
  99. package/dist/packages/realtime-protocol/src/voice.js +65 -17
  100. package/dist/src/agent/agent-manager.js +4 -4
  101. package/dist/src/agent/background-review/run-background-review.js +1 -1
  102. package/dist/src/agent/embedded/model-runtime.js +1 -1
  103. package/dist/src/agent/embedded/run-for-session.js +1 -1
  104. package/dist/src/agent/image/tool-model-config.js +1 -1
  105. package/dist/src/agent/mcp/oauth/mcp-oauth-store.js +1 -1
  106. package/dist/src/agent/service.js +2 -2
  107. package/dist/src/agent/session/session-inspector.js +1 -1
  108. package/dist/src/agent/skills/hub-lock.js +1 -1
  109. package/dist/src/agent/source-context/browser-page.d.ts +3 -0
  110. package/dist/src/agent/source-context/browser-page.js +39 -0
  111. package/dist/src/agent/source-context/injector.js +9 -2
  112. package/dist/src/agent/source-context/types.d.ts +7 -1
  113. package/dist/src/agent/source-context/types.js +4 -1
  114. package/dist/src/agent/tasks/task-judge-service.js +3 -3
  115. package/dist/src/agent/tools/factory.js +9 -1
  116. package/dist/src/ai-assist/text-assist.js +1 -1
  117. package/dist/src/auth/credentials.js +2 -2
  118. package/dist/src/auth/oauth/xopc-cloud.js +1 -1
  119. package/dist/src/auth/profiles/store.js +1 -1
  120. package/dist/src/auth/sync-provider-auth.js +2 -2
  121. package/dist/src/browser/automations/schema.d.ts +1 -1
  122. package/dist/src/browser/automations/service.d.ts +1 -1
  123. package/dist/src/browser/native-messaging-host.d.ts +15 -0
  124. package/dist/src/browser/native-messaging-host.js +105 -0
  125. package/dist/src/browser/providers/browser-ext-install.d.ts +17 -1
  126. package/dist/src/browser/providers/browser-ext-install.js +82 -16
  127. package/dist/src/browser/providers/extension.d.ts +4 -0
  128. package/dist/src/browser/providers/extension.js +104 -38
  129. package/dist/src/browser/runtime/browser-runtime.d.ts +2 -1
  130. package/dist/src/browser/runtime/browser-runtime.js +6 -2
  131. package/dist/src/cli/commands/browser.js +13 -3
  132. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  133. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  134. package/dist/src/cli/commands/models.js +1 -1
  135. package/dist/src/config/index.js +4 -4
  136. package/dist/src/config/loader.js +2 -2
  137. package/dist/src/config/models-json.js +1 -1
  138. package/dist/src/endpoint-tools/runtime.js +2 -0
  139. package/dist/src/extensions/lockfile.js +1 -1
  140. package/dist/src/gateway/agents-admin.js +1 -1
  141. package/dist/src/gateway/chat-stream/mapper.js +3 -2
  142. package/dist/src/gateway/file-path-classifier.js +1 -1
  143. package/dist/src/gateway/hono/app.js +19 -3
  144. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  145. package/dist/src/gateway/hono/middleware/auth.d.ts +4 -1
  146. package/dist/src/gateway/hono/middleware/auth.js +12 -1
  147. package/dist/src/gateway/hono/routes/browser-install.js +1 -1
  148. package/dist/src/gateway/hono/routes/browser.js +106 -7
  149. package/dist/src/gateway/hono/routes/devices.js +36 -3
  150. package/dist/src/gateway/hono/routes/endpoint-tools.js +15 -0
  151. package/dist/src/gateway/hono/routes/models.js +1 -1
  152. package/dist/src/gateway/hono/routes/session-input-handler.d.ts +20 -2
  153. package/dist/src/gateway/hono/routes/session-input-handler.js +18 -0
  154. package/dist/src/gateway/hono/routes/sessions.js +12 -6
  155. package/dist/src/gateway/hono/routes/voice.js +5 -4
  156. package/dist/src/gateway/security/gateway-scopes.d.ts +1 -0
  157. package/dist/src/gateway/security/gateway-scopes.js +10 -1
  158. package/dist/src/gateway/security/origin-check.d.ts +1 -0
  159. package/dist/src/gateway/security/origin-check.js +5 -1
  160. package/dist/src/gateway/service/agent-runner.js +2 -1
  161. package/dist/src/gateway/service/config-coordinator.js +1 -1
  162. package/dist/src/gateway/service/session-input-coordinator.d.ts +1 -0
  163. package/dist/src/gateway/service/session-input-coordinator.js +5 -4
  164. package/dist/src/gateway/service.js +22 -21
  165. package/dist/src/providers/auth-runtime/resolve-auth.js +1 -1
  166. package/dist/src/providers/index.js +2 -2
  167. package/dist/src/providers/model-registry.js +1 -1
  168. package/dist/src/providers/register-runtime-providers.js +1 -1
  169. package/dist/src/realtime/runtime.js +2 -1
  170. package/dist/src/session/client-history.d.ts +4 -1
  171. package/dist/src/session/client-history.js +6 -3
  172. package/dist/src/storage/sqlite/browser-tab-binding-repository.d.ts +7 -0
  173. package/dist/src/storage/sqlite/browser-tab-binding-repository.js +60 -0
  174. package/dist/src/storage/sqlite/device-access-repository.d.ts +3 -1
  175. package/dist/src/storage/sqlite/device-access-repository.js +4 -3
  176. package/dist/src/storage/sqlite/device-pairing-approval.js +6 -4
  177. package/dist/src/storage/sqlite/device-pairing-repository.d.ts +1 -1
  178. package/dist/src/storage/sqlite/index.d.ts +1 -0
  179. package/dist/src/storage/sqlite/index.js +2 -1
  180. package/dist/src/storage/sqlite/migrations/155_browser_extension.sql +120 -0
  181. package/dist/src/storage/sqlite/migrations/156_browser_tab_bindings.sql +14 -0
  182. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  183. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  184. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  185. package/dist/src/storage/sqlite/schema.js +1 -1
  186. package/dist/src/tui/tui-startup-resources.js +1 -1
  187. package/dist/src/tui/tui.js +1 -1
  188. package/dist/src/voice/realtime/agentBroker.d.ts +54 -0
  189. package/dist/src/voice/realtime/agentBroker.js +116 -0
  190. package/dist/src/voice/realtime/agentEngine.js +62 -15
  191. package/dist/src/voice/realtime/engine.d.ts +2 -1
  192. package/dist/src/voice/realtime/omniEngine.js +36 -7
  193. package/dist/src/voice/realtime/pcmFrameBuffer.d.ts +6 -0
  194. package/dist/src/voice/realtime/pcmFrameBuffer.js +29 -0
  195. package/dist/src/voice/realtime/runtime.js +97 -21
  196. package/dist/src/voice/realtime/runtime.types.d.ts +5 -21
  197. package/dist/src/voice/realtime/turnPolicy.d.ts +36 -0
  198. package/dist/src/voice/realtime/turnPolicy.js +134 -0
  199. package/dist/src/work-discovery/service.js +1 -1
  200. package/dist/src/workflows/service/workflow-run-service.js +1 -1
  201. package/package.json +2 -2
  202. package/dist/browser-ext/dist/content.js +0 -191
  203. package/dist/browser-ext/dist/popup.js +0 -32
  204. package/dist/browser-ext/popup.html +0 -37
  205. package/dist/gateway/static/root/assets/index-DQtjs2x3.js +0 -74
  206. package/dist/gateway/static/root/assets/settings-advanced-gate-DK1yTYE4.js +0 -1
  207. package/dist/gateway/static/root/assets/skill-reload-api-DfE3zFVw.js +0 -1
  208. package/dist/src/voice/realtime/conversationTurn.d.ts +0 -20
  209. package/dist/src/voice/realtime/conversationTurn.js +0 -83
@@ -1,6 +1,6 @@
1
1
  import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
2
2
  import { buildDevicePairingProof, devicePairingCompleteSchema, devicePairingDeviceSchema } from "../../../packages/gateway-contract/src/device-pairing.js";
3
- import { DEFAULT_MOBILE_SCOPES } from "../../gateway/security/gateway-scopes.js";
3
+ import { DEFAULT_BROWSER_EXTENSION_SCOPES, DEFAULT_MOBILE_SCOPES } from "../../gateway/security/gateway-scopes.js";
4
4
  import { createDevice, getDevice, registerInitialDeviceRefreshToken, revokeDevice } from "./device-access-repository.js";
5
5
  import { getOrCreateGatewayIdentity } from "./gateway-identity-repository.js";
6
6
  import crypto from "node:crypto";
@@ -19,6 +19,7 @@ const hash = (value) => crypto.createHash("sha256").update(value).digest("hex");
19
19
  const fail = (code, status) => {
20
20
  throw new DevicePairingError(code, status);
21
21
  };
22
+ const scopesForPlatform = (platform) => platform === "chrome" ? DEFAULT_BROWSER_EXTENSION_SCOPES : DEFAULT_MOBILE_SCOPES;
22
23
  function requestRow(id, now) {
23
24
  const db = getSqliteDatabase();
24
25
  db.prepare(`UPDATE device_pairing_requests SET status = 'expired', revision = revision + 1
@@ -133,9 +134,10 @@ function operateDevicePairingRequest(action, body, now = Date.now()) {
133
134
  return;
134
135
  }
135
136
  if (current.status !== "approved") fail("PAIRING_NOT_APPROVED");
137
+ const device = devicePairingDeviceSchema.parse(JSON.parse(current.device_json));
136
138
  const created = createDevice({
137
- ...devicePairingDeviceSchema.parse(JSON.parse(current.device_json)),
138
- scopes: DEFAULT_MOBILE_SCOPES,
139
+ ...device,
140
+ scopes: scopesForPlatform(device.platform),
139
141
  now
140
142
  });
141
143
  registerInitialDeviceRefreshToken(created.id, complete.initialRefreshToken, now);
@@ -147,7 +149,7 @@ function operateDevicePairingRequest(action, body, now = Date.now()) {
147
149
  return {
148
150
  request: statusFromRow(requestRow(row.request_id, now), now),
149
151
  routes: JSON.parse(setup.routes_json),
150
- ...action === "complete" ? { scopes: DEFAULT_MOBILE_SCOPES } : {}
152
+ ...action === "complete" ? { scopes: scopesForPlatform(devicePairingDeviceSchema.parse(JSON.parse(row.device_json)).platform) } : {}
151
153
  };
152
154
  }
153
155
  //#endregion
@@ -1,6 +1,6 @@
1
1
  export type DeviceRoute = {
2
2
  id: string;
3
- kind: 'xopc-secure-link' | 'tailscale' | 'custom-https';
3
+ kind: 'xopc-secure-link' | 'tailscale' | 'custom-https' | 'local-browser';
4
4
  url: string;
5
5
  };
6
6
  export type DevicePairingSetup = {
@@ -6,6 +6,7 @@ export { discoverSqlMigrations, listRegisteredMigrationTargets } from './migrati
6
6
  export { closeXopcDatabase, getXopcDatabase, isXopcDatabaseOpen, openXopcDatabase, requireXopcDatabase, resetXopcDatabaseSingletonForTest, type OpenXopcDatabaseOptions, type XopcDatabase, } from './connection.js';
7
7
  export { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';
8
8
  export { deleteEndpointSessionBinding, getEndpointSessionBinding, setEndpointSessionBinding, type StoredEndpointSessionBinding, } from './endpoint-session-binding-repository.js';
9
+ export { deleteBrowserTabBinding, deleteBrowserTabBindingsByEndpoint, deleteBrowserTabBindingsByPrincipal, getBrowserTabBinding, getBrowserTabBindingById, setBrowserTabBinding, } from './browser-tab-binding-repository.js';
9
10
  export { authenticateDeviceAccessToken, buildRefreshProofMessage, createDevice, getDevice, issueDeviceTokenPair, listDevices, revokeDevice, rotateDeviceRefreshToken, type DeviceAccessIdentity, type DevicePlatform, type DevicePublicKeyJwk, type DeviceRecord, type DeviceTokenPair, } from './device-access-repository.js';
10
11
  export { clearGatewayIdentityForTest, getOrCreateGatewayIdentity, signGatewayPayload, type GatewayIdentity, } from './gateway-identity-repository.js';
11
12
  export { consumeDevicePairingToken, createDevicePairingSetup, isDevicePairingSetupActive, type DevicePairingSetup, type DeviceRoute, type PairingConsumeResult, } from './device-pairing-repository.js';
@@ -7,6 +7,7 @@ import { ensureXopcDatabaseSchema } from "./schema.js";
7
7
  import { closeXopcDatabase, getXopcDatabase, isXopcDatabaseOpen, openXopcDatabase, requireXopcDatabase, resetXopcDatabaseSingletonForTest } from "./connection.js";
8
8
  import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
9
9
  import { deleteEndpointSessionBinding, getEndpointSessionBinding, setEndpointSessionBinding } from "./endpoint-session-binding-repository.js";
10
+ import { deleteBrowserTabBinding, deleteBrowserTabBindingsByEndpoint, deleteBrowserTabBindingsByPrincipal, getBrowserTabBinding, getBrowserTabBindingById, setBrowserTabBinding } from "./browser-tab-binding-repository.js";
10
11
  import { authenticateDeviceAccessToken, buildRefreshProofMessage, createDevice, getDevice, issueDeviceTokenPair, listDevices, revokeDevice, rotateDeviceRefreshToken } from "./device-access-repository.js";
11
12
  import { clearGatewayIdentityForTest, getOrCreateGatewayIdentity, signGatewayPayload } from "./gateway-identity-repository.js";
12
13
  import { consumeDevicePairingToken, createDevicePairingSetup, isDevicePairingSetupActive } from "./device-pairing-repository.js";
@@ -39,4 +40,4 @@ import { appendConnectorExecutionAudit, claimConnectorWebhookDelivery, completeC
39
40
  import { getConnectorAccount, listConnectorAccounts, reconcileConnectorAccount, refreshConnectorAccountCurrent } from "./connector-account-repository.js";
40
41
  import { getConnectorSyncPolicy, getConnectorSyncPolicyForConnection, listConnectorSyncPolicies, upsertConnectorSyncPolicy } from "./connector-sync-policy-repository.js";
41
42
  import { getUserTrustPolicy, setUserTrustPolicy } from "./user-trust-repository.js";
42
- export { COMPOSER_INPUT_HISTORY_LIMIT, DatabaseSchemaMigrationGapError, DatabaseSchemaTooNewError, SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_FILENAME, XOPC_DB_SCHEMA_VERSION, acknowledgeHomeAttention, appendCompactionBoundaryIfUnchanged, appendComposerInputHistory, appendConnectorExecutionAudit, appendTranscriptEntry, applyPendingMigrations, authenticateDeviceAccessToken, bindEndpointPrincipal, buildDefaultSessionMetadata, buildGlobalSessionStats, buildRefreshProofMessage, buildRelationshipPrompt, cancelQueuedSessionInput, claimConnectorWebhookDelivery, claimContextExtractionRun, claimKnowledgeSourceItems, claimNextConnectorLearningJob, claimNextSessionInput, classifyStoredRow, clearComposerInputHistory, clearGatewayIdentityForTest, closeTemporalAssertions, closeXopcDatabase, completeConnectorWebhookDelivery, completeKnowledgeSourceItemSynthesis, consumeClarificationApproval, consumeClarificationResume, consumeConnectorApproval, consumeDevicePairingToken, countKnowledgeSourceItems, createClarificationWait, createCollaborationRule, createConnectorApproval, createContextEvidence, createDevice, createDevicePairingSetup, createEndpointPrincipal, createObjectLinkRecord, createTemporalAssertion, decideConnectorApproval, deleteConnectorConnection, deleteConnectorInstallation, deleteEndpointSessionBinding, deleteKnowledgeSourceItems, deleteNoteAgentContextRecord, deleteNoteRecord, deleteObjectLinkRecord, deleteSessionConfig, deleteSessionRecord, discoverSqlMigrations, enqueueConnectorLearningJob, ensureSessionRecord, ensureXopcDatabaseSchema, estimateTokensFromMessages, extractFtsContent, findSessionInput, findSessionKeyBySessionId, finishContextExtractionRun, finishEndpointToolInvocationAudit, finishKnowledgeSyncRun, finishSessionInputRun, getActiveClarification, getActivityEventRecord, getCachedConnectorCatalogEntry, getClarification, getClarificationResumeInput, getClarificationSnapshot, getCollaborationRule, getConnectorAccount, getConnectorApproval, getConnectorConnection, getConnectorInstallation, getConnectorSyncPolicy, getConnectorSyncPolicyForConnection, getContextExtractionRun, getCurrentSessionId, getDevice, getEndpointPrincipal, getEndpointSessionBinding, getGlobalSessionStats, getInteractionState, getKnowledgeConsumerWatermark, getKnowledgeSourceCursor, getKnowledgeSourceItem, getNoteAgentContextRecord, getNoteRecord, getOrCreateGatewayIdentity, getRelationshipSettings, getSessionConfig, getSessionInputById, getSessionInputState, getSessionMetadata, getSessionPersistedLevels, getSessionTaskPlan, getSqliteDatabase, getUserPeopleIndexState, getUserPerson, getUserTrustPolicy, getXopcDatabase, hasIndependentExtractionOutput, hasSessionConfig, incrementSessionStatsOnAppend, insertSessionInput, inspectSchemaMigrationStatus, isClarificationSuspended, isDevicePairingSetupActive, isHomeAttentionAcknowledged, isProactiveSupportAllowed, isXopcDatabaseOpen, issueDeviceTokenPair, linkContextObjects, listActiveSessionInputRuns, listActivityRecords, listCachedConnectorCatalogEntries, listCollaborationRules, listCompactionBoundaries, listComposerInputHistory, listConnectorAccounts, listConnectorActionMetadata, listConnectorApprovals, listConnectorConnections, listConnectorExecutionAudit, listConnectorInstallations, listConnectorLearningJobs, listConnectorSyncPolicies, listContextExtractionOutputs, listContextExtractionRuns, listContextObjectRelations, listDevices, listEndpointPrincipals, listEndpointToolInvocationAudits, listKnowledgeSourceChanges, listKnowledgeSourceItems, listKnowledgeSyncRuns, listNoteRecords, listObjectActivityRecords, listObjectLinkRecords, listProjectActivityRecords, listRegisteredMigrationTargets, listSessionMetadata, listSessionsByAgent, listTemporalAssertions, listUserPeople, loadCompactionSourceSnapshot, loadLlmMessagesForSession, loadTranscriptHistoryRowsForSession, loadTranscriptRows, loadTranscriptRowsForSession, mergeUserPeople, metadataToSessionInsert, mutateQueuedSessionInput, openXopcDatabase, paginateTranscriptMessages, patchSessionMetadata, pruneBoundedKnowledgeSourceItems, readSchemaVersion, readSchemaVersionForTest, reconcileConnectorAccount, recordActivityEvent, recoverSessionInputState, recoverStaleConnectorLearningJobs, refreshConnectorAccountCurrent, releaseConnectorWebhookDelivery, replaceConnectorCatalogEntries, replaceLatestSessionTurnAndQueueInput, replaceTranscriptRows, replaceUserPeopleIndex, requireXopcDatabase, resetSessionRecord, resetXopcDatabaseSingletonForTest, resolveClarification, resolveMigrationsDir, resolveSessionAgentId, resolveXopcDatabasePath, restoreBeforeCompactionBoundary, revokeDevice, revokeEndpointPrincipal, rotateDeviceRefreshToken, runSqliteWriteTransaction, searchSessionTranscript, sessionConfigRowToConfig, sessionRowToMetadata, setCollaborationRuleStatus, setConnectorLearningPaused, setEndpointSessionBinding, setInteractionState, setKnowledgeConsumerWatermark, setKnowledgeSourceCursor, setKnowledgeSourceItemSynthesisStatus, setSessionConfig, setSessionInputStatus, setSessionTaskPlan, setUserTrustPolicy, signGatewayPayload, startEndpointToolInvocationAudit, startKnowledgeSyncRun, summarizeUserPeople, supersedeActiveClarification, touchEndpointPrincipal, transcriptEntryRowToStoredRow, updateConnectorLearningJob, updateInteractionStateFromMessage, updateRelationshipSettings, updateSessionConfig, updateSessionStats, updateUserPerson, upsertConnectorActionMetadata, upsertConnectorCatalogEntry, upsertConnectorConnection, upsertConnectorInstallation, upsertConnectorSyncPolicy, upsertKnowledgeSourceItems, upsertNoteAgentContextRecord, upsertNoteRecord, validateLatestSessionTurnTarget };
43
+ export { COMPOSER_INPUT_HISTORY_LIMIT, DatabaseSchemaMigrationGapError, DatabaseSchemaTooNewError, SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_FILENAME, XOPC_DB_SCHEMA_VERSION, acknowledgeHomeAttention, appendCompactionBoundaryIfUnchanged, appendComposerInputHistory, appendConnectorExecutionAudit, appendTranscriptEntry, applyPendingMigrations, authenticateDeviceAccessToken, bindEndpointPrincipal, buildDefaultSessionMetadata, buildGlobalSessionStats, buildRefreshProofMessage, buildRelationshipPrompt, cancelQueuedSessionInput, claimConnectorWebhookDelivery, claimContextExtractionRun, claimKnowledgeSourceItems, claimNextConnectorLearningJob, claimNextSessionInput, classifyStoredRow, clearComposerInputHistory, clearGatewayIdentityForTest, closeTemporalAssertions, closeXopcDatabase, completeConnectorWebhookDelivery, completeKnowledgeSourceItemSynthesis, consumeClarificationApproval, consumeClarificationResume, consumeConnectorApproval, consumeDevicePairingToken, countKnowledgeSourceItems, createClarificationWait, createCollaborationRule, createConnectorApproval, createContextEvidence, createDevice, createDevicePairingSetup, createEndpointPrincipal, createObjectLinkRecord, createTemporalAssertion, decideConnectorApproval, deleteBrowserTabBinding, deleteBrowserTabBindingsByEndpoint, deleteBrowserTabBindingsByPrincipal, deleteConnectorConnection, deleteConnectorInstallation, deleteEndpointSessionBinding, deleteKnowledgeSourceItems, deleteNoteAgentContextRecord, deleteNoteRecord, deleteObjectLinkRecord, deleteSessionConfig, deleteSessionRecord, discoverSqlMigrations, enqueueConnectorLearningJob, ensureSessionRecord, ensureXopcDatabaseSchema, estimateTokensFromMessages, extractFtsContent, findSessionInput, findSessionKeyBySessionId, finishContextExtractionRun, finishEndpointToolInvocationAudit, finishKnowledgeSyncRun, finishSessionInputRun, getActiveClarification, getActivityEventRecord, getBrowserTabBinding, getBrowserTabBindingById, getCachedConnectorCatalogEntry, getClarification, getClarificationResumeInput, getClarificationSnapshot, getCollaborationRule, getConnectorAccount, getConnectorApproval, getConnectorConnection, getConnectorInstallation, getConnectorSyncPolicy, getConnectorSyncPolicyForConnection, getContextExtractionRun, getCurrentSessionId, getDevice, getEndpointPrincipal, getEndpointSessionBinding, getGlobalSessionStats, getInteractionState, getKnowledgeConsumerWatermark, getKnowledgeSourceCursor, getKnowledgeSourceItem, getNoteAgentContextRecord, getNoteRecord, getOrCreateGatewayIdentity, getRelationshipSettings, getSessionConfig, getSessionInputById, getSessionInputState, getSessionMetadata, getSessionPersistedLevels, getSessionTaskPlan, getSqliteDatabase, getUserPeopleIndexState, getUserPerson, getUserTrustPolicy, getXopcDatabase, hasIndependentExtractionOutput, hasSessionConfig, incrementSessionStatsOnAppend, insertSessionInput, inspectSchemaMigrationStatus, isClarificationSuspended, isDevicePairingSetupActive, isHomeAttentionAcknowledged, isProactiveSupportAllowed, isXopcDatabaseOpen, issueDeviceTokenPair, linkContextObjects, listActiveSessionInputRuns, listActivityRecords, listCachedConnectorCatalogEntries, listCollaborationRules, listCompactionBoundaries, listComposerInputHistory, listConnectorAccounts, listConnectorActionMetadata, listConnectorApprovals, listConnectorConnections, listConnectorExecutionAudit, listConnectorInstallations, listConnectorLearningJobs, listConnectorSyncPolicies, listContextExtractionOutputs, listContextExtractionRuns, listContextObjectRelations, listDevices, listEndpointPrincipals, listEndpointToolInvocationAudits, listKnowledgeSourceChanges, listKnowledgeSourceItems, listKnowledgeSyncRuns, listNoteRecords, listObjectActivityRecords, listObjectLinkRecords, listProjectActivityRecords, listRegisteredMigrationTargets, listSessionMetadata, listSessionsByAgent, listTemporalAssertions, listUserPeople, loadCompactionSourceSnapshot, loadLlmMessagesForSession, loadTranscriptHistoryRowsForSession, loadTranscriptRows, loadTranscriptRowsForSession, mergeUserPeople, metadataToSessionInsert, mutateQueuedSessionInput, openXopcDatabase, paginateTranscriptMessages, patchSessionMetadata, pruneBoundedKnowledgeSourceItems, readSchemaVersion, readSchemaVersionForTest, reconcileConnectorAccount, recordActivityEvent, recoverSessionInputState, recoverStaleConnectorLearningJobs, refreshConnectorAccountCurrent, releaseConnectorWebhookDelivery, replaceConnectorCatalogEntries, replaceLatestSessionTurnAndQueueInput, replaceTranscriptRows, replaceUserPeopleIndex, requireXopcDatabase, resetSessionRecord, resetXopcDatabaseSingletonForTest, resolveClarification, resolveMigrationsDir, resolveSessionAgentId, resolveXopcDatabasePath, restoreBeforeCompactionBoundary, revokeDevice, revokeEndpointPrincipal, rotateDeviceRefreshToken, runSqliteWriteTransaction, searchSessionTranscript, sessionConfigRowToConfig, sessionRowToMetadata, setBrowserTabBinding, setCollaborationRuleStatus, setConnectorLearningPaused, setEndpointSessionBinding, setInteractionState, setKnowledgeConsumerWatermark, setKnowledgeSourceCursor, setKnowledgeSourceItemSynthesisStatus, setSessionConfig, setSessionInputStatus, setSessionTaskPlan, setUserTrustPolicy, signGatewayPayload, startEndpointToolInvocationAudit, startKnowledgeSyncRun, summarizeUserPeople, supersedeActiveClarification, touchEndpointPrincipal, transcriptEntryRowToStoredRow, updateConnectorLearningJob, updateInteractionStateFromMessage, updateRelationshipSettings, updateSessionConfig, updateSessionStats, updateUserPerson, upsertConnectorActionMetadata, upsertConnectorCatalogEntry, upsertConnectorConnection, upsertConnectorInstallation, upsertConnectorSyncPolicy, upsertKnowledgeSourceItems, upsertNoteAgentContextRecord, upsertNoteRecord, validateLatestSessionTurnTarget };
@@ -0,0 +1,120 @@
1
+ PRAGMA defer_foreign_keys = ON;
2
+
3
+ -- Rebuild the device table and every table that references it. SQLite rewrites
4
+ -- foreign-key targets on table rename, so rebuilding only `devices` would leave
5
+ -- references pointing at the temporary table name.
6
+ ALTER TABLE notification_deliveries RENAME TO notification_deliveries_v154;
7
+ ALTER TABLE device_pairing_requests RENAME TO device_pairing_requests_v154;
8
+ ALTER TABLE device_refresh_credentials RENAME TO device_refresh_credentials_v154;
9
+ ALTER TABLE device_access_sessions RENAME TO device_access_sessions_v154;
10
+ ALTER TABLE device_push_endpoints RENAME TO device_push_endpoints_v154;
11
+ ALTER TABLE devices RENAME TO devices_v154;
12
+
13
+ DROP INDEX idx_notification_deliveries_due;
14
+ DROP INDEX idx_device_pairing_requests_expiry;
15
+ DROP INDEX idx_device_refresh_credentials_device;
16
+ DROP INDEX idx_device_access_sessions_device;
17
+
18
+ CREATE TABLE devices (
19
+ device_id TEXT PRIMARY KEY,
20
+ display_name TEXT NOT NULL,
21
+ platform TEXT NOT NULL CHECK (platform IN ('ios', 'android', 'chrome')),
22
+ extension_id TEXT CHECK (extension_id IS NULL OR length(extension_id) = 32),
23
+ public_key_jwk TEXT NOT NULL,
24
+ scopes_json TEXT NOT NULL,
25
+ created_at INTEGER NOT NULL,
26
+ last_seen_at INTEGER,
27
+ revoked_at INTEGER,
28
+ CHECK ((platform = 'chrome') = (extension_id IS NOT NULL))
29
+ );
30
+
31
+ CREATE TABLE device_refresh_credentials (
32
+ credential_id TEXT PRIMARY KEY,
33
+ device_id TEXT NOT NULL REFERENCES devices(device_id) ON DELETE CASCADE,
34
+ token_hash TEXT NOT NULL,
35
+ expires_at INTEGER NOT NULL,
36
+ created_at INTEGER NOT NULL,
37
+ last_used_at INTEGER,
38
+ replaced_by TEXT REFERENCES device_refresh_credentials(credential_id),
39
+ rotation_request_id TEXT,
40
+ revoked_at INTEGER
41
+ );
42
+
43
+ CREATE TABLE device_access_sessions (
44
+ session_id TEXT PRIMARY KEY,
45
+ device_id TEXT NOT NULL REFERENCES devices(device_id) ON DELETE CASCADE,
46
+ token_hash TEXT NOT NULL,
47
+ expires_at INTEGER NOT NULL,
48
+ created_at INTEGER NOT NULL,
49
+ revoked_at INTEGER
50
+ );
51
+
52
+ CREATE TABLE device_push_endpoints (
53
+ device_id TEXT PRIMARY KEY REFERENCES devices(device_id) ON DELETE CASCADE,
54
+ platform TEXT NOT NULL CHECK (platform IN ('ios', 'android')),
55
+ push_token TEXT NOT NULL UNIQUE,
56
+ enabled INTEGER NOT NULL DEFAULT 1,
57
+ permissions TEXT NOT NULL CHECK (permissions IN ('granted', 'denied', 'unknown')),
58
+ preferences_json TEXT NOT NULL DEFAULT '{}',
59
+ locale TEXT NOT NULL CHECK (locale IN ('en', 'zh')),
60
+ app_version TEXT,
61
+ lease_expires_at INTEGER NOT NULL,
62
+ last_seen_at INTEGER NOT NULL,
63
+ created_at INTEGER NOT NULL,
64
+ updated_at INTEGER NOT NULL
65
+ );
66
+
67
+ CREATE TABLE device_pairing_requests (
68
+ request_id TEXT PRIMARY KEY,
69
+ pairing_id TEXT NOT NULL UNIQUE REFERENCES device_pairing_sessions(pairing_id) ON DELETE CASCADE,
70
+ device_json TEXT NOT NULL,
71
+ status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'completed', 'rejected', 'cancelled', 'expired')),
72
+ revision INTEGER NOT NULL DEFAULT 1,
73
+ confirmation_code TEXT NOT NULL,
74
+ expires_at INTEGER NOT NULL,
75
+ recovery_until INTEGER NOT NULL,
76
+ completion_key TEXT,
77
+ initial_token_hash TEXT,
78
+ device_id TEXT REFERENCES devices(device_id),
79
+ created_at INTEGER NOT NULL
80
+ );
81
+
82
+ CREATE TABLE notification_deliveries (
83
+ event_id TEXT NOT NULL,
84
+ device_id TEXT NOT NULL,
85
+ status TEXT NOT NULL CHECK (status IN ('pending', 'accepted', 'delivered', 'dead')),
86
+ attempts INTEGER NOT NULL DEFAULT 0,
87
+ next_attempt_at INTEGER NOT NULL,
88
+ provider_ticket_id TEXT,
89
+ last_error TEXT,
90
+ updated_at INTEGER NOT NULL,
91
+ PRIMARY KEY (event_id, device_id),
92
+ FOREIGN KEY (event_id) REFERENCES notification_events(event_id) ON DELETE CASCADE,
93
+ FOREIGN KEY (device_id) REFERENCES device_push_endpoints(device_id) ON DELETE CASCADE
94
+ );
95
+
96
+ INSERT INTO devices
97
+ SELECT device_id, display_name, platform, NULL, public_key_jwk, scopes_json,
98
+ created_at, last_seen_at, revoked_at
99
+ FROM devices_v154;
100
+ INSERT INTO device_refresh_credentials SELECT * FROM device_refresh_credentials_v154;
101
+ INSERT INTO device_access_sessions SELECT * FROM device_access_sessions_v154;
102
+ INSERT INTO device_push_endpoints SELECT * FROM device_push_endpoints_v154;
103
+ INSERT INTO device_pairing_requests SELECT * FROM device_pairing_requests_v154;
104
+ INSERT INTO notification_deliveries SELECT * FROM notification_deliveries_v154;
105
+
106
+ DROP TABLE notification_deliveries_v154;
107
+ DROP TABLE device_pairing_requests_v154;
108
+ DROP TABLE device_refresh_credentials_v154;
109
+ DROP TABLE device_access_sessions_v154;
110
+ DROP TABLE device_push_endpoints_v154;
111
+ DROP TABLE devices_v154;
112
+
113
+ CREATE INDEX idx_notification_deliveries_due
114
+ ON notification_deliveries(status, next_attempt_at);
115
+ CREATE INDEX idx_device_pairing_requests_expiry
116
+ ON device_pairing_requests(recovery_until);
117
+ CREATE INDEX idx_device_refresh_credentials_device
118
+ ON device_refresh_credentials(device_id, expires_at);
119
+ CREATE INDEX idx_device_access_sessions_device
120
+ ON device_access_sessions(device_id, expires_at);
@@ -0,0 +1,14 @@
1
+ CREATE TABLE browser_tab_bindings (
2
+ binding_id TEXT PRIMARY KEY,
3
+ session_key TEXT NOT NULL UNIQUE,
4
+ principal_id TEXT NOT NULL REFERENCES devices(device_id) ON DELETE CASCADE,
5
+ endpoint_id TEXT NOT NULL,
6
+ tab_id TEXT NOT NULL,
7
+ window_id TEXT NOT NULL,
8
+ document_id TEXT NOT NULL,
9
+ url_origin TEXT NOT NULL,
10
+ mode TEXT NOT NULL CHECK (mode IN ('read', 'act')),
11
+ created_at INTEGER NOT NULL,
12
+ expires_at INTEGER NOT NULL
13
+ );
14
+ CREATE INDEX idx_browser_tab_bindings_expiry ON browser_tab_bindings(expires_at);
@@ -3,7 +3,7 @@ import type { ApplyMigrationsOptions } from './types.js';
3
3
  /** Baseline schema version applied from schema.sql on first open. */
4
4
  export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
5
5
  /** Latest schema version this release supports (increment when adding migrations). */
6
- export declare const XOPC_DB_SCHEMA_VERSION = 154;
6
+ export declare const XOPC_DB_SCHEMA_VERSION = 156;
7
7
  export declare function resolveMigrationsDir(override?: string): string;
8
8
  /**
9
9
  * Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
@@ -12,7 +12,7 @@ const log = createLogger("Sqlite:Migrations");
12
12
  /** Baseline schema version applied from schema.sql on first open. */
13
13
  const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
14
14
  /** Latest schema version this release supports (increment when adding migrations). */
15
- const XOPC_DB_SCHEMA_VERSION = 154;
15
+ const XOPC_DB_SCHEMA_VERSION = 156;
16
16
  const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
17
17
  function backupBeforeTaskCutover(db, databasePath) {
18
18
  db.exec("PRAGMA wal_checkpoint(FULL)");
@@ -57,7 +57,7 @@ function applySingleMigration(db, migration) {
57
57
  * Each step runs in its own transaction and updates schema_meta on success.
58
58
  */
59
59
  function applyPendingMigrations(db, options = {}) {
60
- const targetVersion = options.targetVersion ?? 154;
60
+ const targetVersion = options.targetVersion ?? 156;
61
61
  let currentVersion = readSchemaVersion(db);
62
62
  if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
63
63
  if (currentVersion >= targetVersion) return currentVersion;
@@ -94,7 +94,7 @@ function applyPendingMigrations(db, options = {}) {
94
94
  }
95
95
  /** Inspect schema version without mutating the database. */
96
96
  function inspectSchemaMigrationStatus(db, options = {}) {
97
- const appVersion = options.targetVersion ?? 154;
97
+ const appVersion = options.targetVersion ?? 156;
98
98
  const dbVersion = readSchemaVersion(db);
99
99
  if (dbVersion > appVersion) return {
100
100
  dbVersion,
@@ -19,7 +19,7 @@ const log = createLogger('Sqlite:Migrations');
19
19
  export const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
20
20
 
21
21
  /** Latest schema version this release supports (increment when adding migrations). */
22
- export const XOPC_DB_SCHEMA_VERSION = 154;
22
+ export const XOPC_DB_SCHEMA_VERSION = 156;
23
23
 
24
24
  const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
25
25
 
@@ -32,7 +32,7 @@ function ensureXopcDatabaseSchema(db, options = {}) {
32
32
  currentVersion = 11;
33
33
  }
34
34
  applyPendingMigrations(db, {
35
- targetVersion: 154,
35
+ targetVersion: 156,
36
36
  databasePath: options.databasePath
37
37
  });
38
38
  }
@@ -1,6 +1,6 @@
1
1
  import { resolveStateDir } from "../config/paths-state.js";
2
- import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
3
2
  import { init_paths, resolveAgentProfileDir, resolveBundledSkillsDir } from "../config/paths.js";
3
+ import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
4
4
  import { resolveBootstrapFilesSync } from "../agent/bootstrap/bootstrap-files.js";
5
5
  import { createSkillConfigManager, isSkillEnabled } from "../agent/skills/config.js";
6
6
  import { loadSkills } from "../agent/skills/index.js";
@@ -2,10 +2,10 @@ import { version } from "../../package.js";
2
2
  import { resolveStateDir, resolveXopcDatabasePath } from "../config/paths-state.js";
3
3
  import { parseAgentSessionKey } from "../routing/session-key-utils.js";
4
4
  import { init_agent_session_key, normalizeAgentId } from "../routing/agent-session-key.js";
5
- import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
6
5
  import { init_paths } from "../config/paths.js";
7
6
  import { loadConfig } from "../config/loader.js";
8
7
  import { getAllModels, getAllProviders, getApiKey, init_providers, isProviderConfiguredSync } from "../providers/index.js";
8
+ import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
9
9
  import { ProjectTrustStore, hasTrustRequiringProjectResources } from "../project-trust/trust-store.js";
10
10
  import { renderWorkflowFinalSummary, renderWorkflowPanel } from "../agent/workflow/snapshot.js";
11
11
  import { ensureStarterAgentsInitialized } from "../agent/starter-agents.js";
@@ -0,0 +1,54 @@
1
+ import type { RealtimeEvent, RealtimeSubscriptionHandle } from '../../realtime/broker.js';
2
+ import type { SessionInput, SessionInputState } from '../../storage/sqlite/session-input-repository.js';
3
+ import type { SubmitSessionInput } from '../../gateway/service/session-input-coordinator.js';
4
+ export interface VoiceAgentEvent {
5
+ type: string;
6
+ payload?: Record<string, unknown>;
7
+ }
8
+ export interface VoiceAgentTask {
9
+ taskId: string;
10
+ runId: string;
11
+ events: AsyncIterable<VoiceAgentEvent>;
12
+ }
13
+ export interface VoiceAgentBroker {
14
+ delegate(input: {
15
+ sessionKey: string;
16
+ expectedSessionId: string;
17
+ turnId: string;
18
+ text: string;
19
+ signal: AbortSignal;
20
+ }): Promise<VoiceAgentTask>;
21
+ cancel(taskId: string): Promise<boolean>;
22
+ }
23
+ type SubmitResult = {
24
+ ok: true;
25
+ effectiveDelivery: 'next' | 'steer';
26
+ state: SessionInputState;
27
+ } | {
28
+ ok: false;
29
+ code: string;
30
+ };
31
+ type BrokerDependencies = {
32
+ submit(input: SubmitSessionInput): Promise<SubmitResult>;
33
+ find(sessionKey: string, clientMessageId: string): SessionInput | undefined;
34
+ snapshot(sessionKey: string): SessionInputState;
35
+ currentSequence(topic: string): number;
36
+ subscribe(topic: string, afterSeq: number, listener: (event: RealtimeEvent) => void): RealtimeSubscriptionHandle;
37
+ cancelRun(runId: string): Promise<void>;
38
+ };
39
+ /** Durable voice-to-Agent boundary. Playback and socket cancellation only detach event delivery. */
40
+ export declare class DurableVoiceAgentBroker implements VoiceAgentBroker {
41
+ private readonly deps;
42
+ private readonly runByTask;
43
+ constructor(deps: BrokerDependencies);
44
+ delegate(input: {
45
+ sessionKey: string;
46
+ expectedSessionId: string;
47
+ turnId: string;
48
+ text: string;
49
+ signal: AbortSignal;
50
+ }): Promise<VoiceAgentTask>;
51
+ cancel(taskId: string): Promise<boolean>;
52
+ private events;
53
+ }
54
+ export {};
@@ -0,0 +1,116 @@
1
+ //#region src/voice/realtime/agentBroker.ts
2
+ const RUN_ASSIGNMENT_TIMEOUT_MS = 5e3;
3
+ const RUN_ASSIGNMENT_POLL_MS = 20;
4
+ const MAX_TRACKED_TASKS = 256;
5
+ function sleep(ms, signal) {
6
+ return new Promise((resolve, reject) => {
7
+ const finish = () => {
8
+ signal.removeEventListener("abort", abort);
9
+ resolve();
10
+ };
11
+ const timer = setTimeout(finish, ms);
12
+ const abort = () => {
13
+ clearTimeout(timer);
14
+ reject(signal.reason ?? /* @__PURE__ */ new Error("Voice task subscription cancelled"));
15
+ };
16
+ signal.addEventListener("abort", abort, { once: true });
17
+ if (signal.aborted) abort();
18
+ });
19
+ }
20
+ function eventData(delivery) {
21
+ if (delivery.kind !== "realtime.event") return void 0;
22
+ const value = delivery.payload.data;
23
+ return value && typeof value === "object" && typeof value.type === "string" ? value : void 0;
24
+ }
25
+ /** Durable voice-to-Agent boundary. Playback and socket cancellation only detach event delivery. */
26
+ var DurableVoiceAgentBroker = class {
27
+ runByTask = /* @__PURE__ */ new Map();
28
+ constructor(deps) {
29
+ this.deps = deps;
30
+ }
31
+ async delegate(input) {
32
+ const clientMessageId = `voice:${input.expectedSessionId}:${input.turnId}`;
33
+ const activeRunId = this.deps.snapshot(input.sessionKey).activeRunId;
34
+ const activeRunCursor = activeRunId ? this.deps.currentSequence(`run:${activeRunId}`) : 0;
35
+ const result = await this.deps.submit({
36
+ sessionKey: input.sessionKey,
37
+ expectedSessionId: input.expectedSessionId,
38
+ clientMessageId,
39
+ delivery: activeRunId ? "steer" : "next",
40
+ content: input.text,
41
+ origin: {
42
+ type: "channel",
43
+ channel: "voice"
44
+ }
45
+ });
46
+ if (result.ok === false) throw new Error(`Voice task submission failed: ${result.code}`);
47
+ const deadline = Date.now() + RUN_ASSIGNMENT_TIMEOUT_MS;
48
+ let row = this.deps.find(input.sessionKey, clientMessageId);
49
+ while (!row?.runId && !row?.targetRunId) {
50
+ if (row && [
51
+ "completed",
52
+ "cancelled",
53
+ "failed",
54
+ "interrupted"
55
+ ].includes(row.status)) throw new Error(row.error ?? `Voice task ended before assignment: ${row.status}`);
56
+ if (Date.now() >= deadline) throw new Error("Voice task run assignment timed out");
57
+ await sleep(RUN_ASSIGNMENT_POLL_MS, input.signal);
58
+ row = this.deps.find(input.sessionKey, clientMessageId);
59
+ }
60
+ const runId = row.runId ?? row.targetRunId;
61
+ this.runByTask.set(row.id, runId);
62
+ if (this.runByTask.size > MAX_TRACKED_TASKS) this.runByTask.delete(this.runByTask.keys().next().value);
63
+ const afterSeq = result.effectiveDelivery === "steer" && runId === activeRunId ? activeRunCursor : 0;
64
+ return {
65
+ taskId: row.id,
66
+ runId,
67
+ events: this.events(row.id, runId, afterSeq, input.signal)
68
+ };
69
+ }
70
+ async cancel(taskId) {
71
+ const runId = this.runByTask.get(taskId);
72
+ if (!runId) return false;
73
+ await this.deps.cancelRun(runId);
74
+ this.runByTask.delete(taskId);
75
+ return true;
76
+ }
77
+ async *events(taskId, runId, afterSeq, signal) {
78
+ const queued = [];
79
+ let wake;
80
+ const push = (event) => {
81
+ queued.push(event);
82
+ wake?.();
83
+ wake = void 0;
84
+ };
85
+ const subscription = this.deps.subscribe(`run:${runId}`, afterSeq, push);
86
+ subscription.initial.forEach(push);
87
+ const abort = () => {
88
+ wake?.();
89
+ wake = void 0;
90
+ };
91
+ signal.addEventListener("abort", abort, { once: true });
92
+ try {
93
+ while (!signal.aborted) {
94
+ if (!queued.length) await new Promise((resolve) => {
95
+ wake = resolve;
96
+ });
97
+ if (signal.aborted) return;
98
+ const delivery = queued.shift();
99
+ if (!delivery) continue;
100
+ if (delivery.kind === "realtime.gap") throw new Error("Voice task event history is unavailable");
101
+ const event = eventData(delivery);
102
+ if (!event) continue;
103
+ yield event;
104
+ if (event.type === "stream_end") {
105
+ this.runByTask.delete(taskId);
106
+ return;
107
+ }
108
+ }
109
+ } finally {
110
+ signal.removeEventListener("abort", abort);
111
+ subscription.unsubscribe();
112
+ }
113
+ }
114
+ };
115
+ //#endregion
116
+ export { DurableVoiceAgentBroker };
@@ -2,8 +2,9 @@ import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
3
  import { speakStream } from "../tts/speak-core.js";
4
4
  import { AudioPlaybackWindow } from "./audio-playback-window.js";
5
- import { ConversationTurn } from "./conversationTurn.js";
5
+ import { TurnCoordinator } from "./turnPolicy.js";
6
6
  import { isLikelyPlaybackEcho } from "./playback-echo.js";
7
+ import { PcmFrameBuffer } from "./pcmFrameBuffer.js";
7
8
  import { SpeakableSegmenter } from "./speakable-segmenter.js";
8
9
  import crypto from "node:crypto";
9
10
  //#region src/voice/realtime/agentEngine.ts
@@ -34,8 +35,24 @@ function createAgentVoiceEngine(options) {
34
35
  let committing = false;
35
36
  const finalizedUtterances = /* @__PURE__ */ new Set();
36
37
  let pendingTurn;
37
- const turn = new ConversationTurn(claim.silenceDurationMs, (text) => {
38
+ const turn = new TurnCoordinator(claim.silenceDurationMs, (text, decision, turnId) => {
38
39
  if (closed || muted) return;
40
+ log.info({
41
+ sessionId: claim.sessionId,
42
+ turnId,
43
+ disposition: decision.disposition,
44
+ confidence: decision.confidence,
45
+ source: decision.source,
46
+ transcriptCharacters: text.length
47
+ }, "Realtime voice turn committed");
48
+ send("turn.decision", {
49
+ turnId,
50
+ disposition: decision.disposition,
51
+ confidence: decision.confidence,
52
+ source: decision.source,
53
+ committed: true
54
+ });
55
+ send("turn.committed", { turnId });
39
56
  if (queuedTurns >= 8) {
40
57
  send("session.error", {
41
58
  code: "INPUT_BACKPRESSURE",
@@ -48,13 +65,14 @@ function createAgentVoiceEngine(options) {
48
65
  queuedTurns += 1;
49
66
  const generation = inputGeneration;
50
67
  const pending = {
68
+ turnId,
51
69
  text,
52
70
  cancelled: false
53
71
  };
54
72
  pendingTurn = pending;
55
73
  conversationTail = conversationTail.then(() => {
56
74
  if (pendingTurn === pending) pendingTurn = void 0;
57
- if (generation === inputGeneration && !pending.cancelled) return runConversationTurn(text);
75
+ if (generation === inputGeneration && !pending.cancelled) return runAssistantTurn(text, turnId);
58
76
  }).finally(() => {
59
77
  if (generation === inputGeneration) queuedTurns -= 1;
60
78
  });
@@ -129,11 +147,10 @@ function createAgentVoiceEngine(options) {
129
147
  }
130
148
  });
131
149
  }
132
- for (let offset = 0; offset < item.value.byteLength; offset += 24e3) {
133
- const chunk = item.value.subarray(offset, offset + 24e3);
134
- await response.playback.reserve(chunk.byteLength, response.abortController.signal);
150
+ for (const frame of response.pcm.push(item.value)) {
151
+ await response.playback.reserve(frame.byteLength, response.abortController.signal);
135
152
  if (activeResponse !== response || closed) return;
136
- options.sendAudio(response.id, chunk);
153
+ options.sendAudio(response.id, frame);
137
154
  }
138
155
  }
139
156
  } finally {
@@ -204,7 +221,7 @@ function createAgentVoiceEngine(options) {
204
221
  if (response.speechWorker) prefetchNextSpeech(response);
205
222
  else startSpeechWorker(response);
206
223
  }
207
- async function runConversationTurn(text) {
224
+ async function runAssistantTurn(text, turnId) {
208
225
  if (!claim.tts || !claim.request.sessionKey || !text.trim() || closed) return;
209
226
  cancelActiveResponse("barge_in");
210
227
  const response = {
@@ -219,14 +236,28 @@ function createAgentVoiceEngine(options) {
219
236
  queuedSpeechCharacters: 0,
220
237
  startedAt: Date.now(),
221
238
  firstTextSeen: false,
222
- awaitingClarification: false
239
+ awaitingClarification: false,
240
+ pcm: new PcmFrameBuffer()
223
241
  };
224
242
  activeResponse = response;
225
243
  send("response.created", { responseId: response.id });
226
244
  try {
227
245
  await interruptionWrites;
228
246
  if (response.abortController.signal.aborted || closed) return;
229
- for await (const event of options.runtime.runAgent(text, claim.request.sessionKey, response.abortController.signal)) {
247
+ if (!claim.conversationSessionId) throw new Error("Conversation identity is unavailable");
248
+ const task = await options.runtime.agentBroker.delegate({
249
+ text,
250
+ turnId,
251
+ sessionKey: claim.request.sessionKey,
252
+ expectedSessionId: claim.conversationSessionId,
253
+ signal: response.abortController.signal
254
+ });
255
+ response.taskId = task.taskId;
256
+ send("task.created", {
257
+ responseId: response.id,
258
+ taskId: task.taskId
259
+ });
260
+ for await (const event of task.events) {
230
261
  if (activeResponse !== response || response.abortController.signal.aborted) return;
231
262
  if (event.type === "assistant_delta" || event.type === "tool_end" && event.payload?.toolName !== "clarify") response.awaitingClarification = false;
232
263
  if (event.type === "assistant_delta" && typeof event.payload?.delta === "string") {
@@ -247,12 +278,19 @@ function createAgentVoiceEngine(options) {
247
278
  queuePhrases(response, response.segmenter.push(event.payload.delta));
248
279
  }
249
280
  if (event.type === "tool_start") queuePhrases(response, response.segmenter.flush());
250
- if ((event.type === "tool_start" || event.type === "tool_end") && typeof event.payload?.toolCallId === "string" && typeof event.payload?.toolName === "string") send("response.activity", {
251
- responseId: response.id,
281
+ if ((event.type === "tool_start" || event.type === "tool_end") && typeof event.payload?.toolCallId === "string" && typeof event.payload?.toolName === "string") send("task.activity", {
282
+ taskId: task.taskId,
252
283
  toolCallId: event.payload.toolCallId.slice(0, 160),
253
284
  toolName: event.payload.toolName.slice(0, 256),
254
285
  status: event.type === "tool_start" ? "running" : event.payload.status === "error" ? "failed" : "completed"
255
286
  });
287
+ if (event.type === "stream_end") {
288
+ const status = event.payload?.status;
289
+ send("task.done", {
290
+ taskId: task.taskId,
291
+ status: status === "cancelled" ? "cancelled" : status === "suspended" ? "suspended" : status === "error" ? "failed" : "completed"
292
+ });
293
+ }
256
294
  if (event.type === "clarify_request" && typeof event.payload?.requestId === "string" && typeof event.payload?.question === "string") {
257
295
  response.awaitingClarification = true;
258
296
  send("response.clarification", {
@@ -274,6 +312,12 @@ function createAgentVoiceEngine(options) {
274
312
  send("response.text.done", { responseId: response.id });
275
313
  await waitForSpeech(response);
276
314
  if (response.speechError) throw response.speechError;
315
+ const finalFrame = response.pcm.finish();
316
+ if (finalFrame) {
317
+ await response.playback.reserve(finalFrame.byteLength, response.abortController.signal);
318
+ if (activeResponse !== response || closed) return;
319
+ options.sendAudio(response.id, finalFrame);
320
+ }
277
321
  await response.playback.drain(response.abortController.signal);
278
322
  if (activeResponse !== response || response.abortController.signal.aborted) return;
279
323
  if (response.audioStarted) send("response.audio.done", { responseId: response.id });
@@ -367,7 +411,7 @@ function createAgentVoiceEngine(options) {
367
411
  if (claim.request.purpose === "conversation") {
368
412
  if (pendingTurn) {
369
413
  pendingTurn.cancelled = true;
370
- turn.restore(pendingTurn.text);
414
+ turn.restore(pendingTurn.turnId, pendingTurn.text);
371
415
  pendingTurn = void 0;
372
416
  }
373
417
  turn.start(event.utteranceId);
@@ -500,8 +544,11 @@ function createAgentVoiceEngine(options) {
500
544
  });
501
545
  return cancelActiveResponse(reason);
502
546
  },
503
- acknowledge(responseId, playedBytes) {
504
- if (activeResponse?.id === responseId) activeResponse.playback.acknowledge(playedBytes);
547
+ cancelTask(taskId) {
548
+ return options.runtime.agentBroker.cancel(taskId);
549
+ },
550
+ acknowledge(responseId, playedDurationMs) {
551
+ if (activeResponse?.id === responseId) activeResponse.playback.acknowledge(playedDurationMs * 48);
505
552
  },
506
553
  close() {
507
554
  if (closing) return closing;
@@ -8,6 +8,7 @@ export interface VoiceEngine {
8
8
  setInputMuted(muted: boolean): void | Promise<void>;
9
9
  commit(): Promise<void>;
10
10
  cancel(responseId: string, reason: 'client_cancelled' | 'barge_in'): boolean;
11
- acknowledge(responseId: string, playedBytes: number): void;
11
+ cancelTask(taskId: string): Promise<boolean>;
12
+ acknowledge(responseId: string, playedDurationMs: number): void;
12
13
  close(): void | Promise<void>;
13
14
  }