@vellumai/assistant 0.12.3-staging.1 → 0.12.3-staging.2

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 (560) hide show
  1. package/ARCHITECTURE.md +43 -29
  2. package/Dockerfile +63 -0
  3. package/docs/architecture/security.md +10 -5
  4. package/docs/architecture/turn-actor.md +2 -0
  5. package/docs/desktop-browser-cli.md +45 -4
  6. package/docs/live-voice-task-outcomes.md +100 -0
  7. package/docs/mode-sessions.md +183 -0
  8. package/docs/voice-input-diagnostics.md +58 -0
  9. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  10. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  12. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  13. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  14. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  15. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -0
  16. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -1
  17. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +38 -7
  18. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +5 -6
  19. package/node_modules/@vellumai/service-contracts/package.json +2 -1
  20. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  21. package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  22. package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
  23. package/openapi.yaml +665 -14
  24. package/package.json +1 -1
  25. package/scripts/smoke-desktop-browser-cli.ts +10 -26
  26. package/scripts/smoke-desktop-window-theme.ts +2 -3
  27. package/src/__tests__/agent-loop-output-hooks.test.ts +150 -1
  28. package/src/__tests__/agent-loop-provider-error-recording.test.ts +87 -19
  29. package/src/__tests__/annotate-activity-metadata.test.ts +3 -1
  30. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  31. package/src/__tests__/answered-question-persistence.test.ts +2 -0
  32. package/src/__tests__/anthropic-gateway-shared.test.ts +22 -0
  33. package/src/__tests__/approval-message-composer.test.ts +1 -118
  34. package/src/__tests__/approval-routes-http.test.ts +3 -0
  35. package/src/__tests__/assistant-event-hub-connection-history.test.ts +111 -0
  36. package/src/__tests__/assistant-event-hub-targeted.test.ts +50 -0
  37. package/src/__tests__/assistant-event-targeting.test.ts +186 -0
  38. package/src/__tests__/assistant-stream-state.test.ts +29 -9
  39. package/src/__tests__/browser-fill-credential.test.ts +11 -22
  40. package/src/__tests__/btw-routes.test.ts +4 -1
  41. package/src/__tests__/build-persisted-content.test.ts +1 -1
  42. package/src/__tests__/call-conversation-messages.test.ts +5 -0
  43. package/src/__tests__/call-recovery.test.ts +7 -2
  44. package/src/__tests__/call-routes-http.test.ts +12 -0
  45. package/src/__tests__/call-store.test.ts +7 -2
  46. package/src/__tests__/channel-retry-sweep.test.ts +9 -0
  47. package/src/__tests__/chat-reveal-guard-priming.test.ts +2 -0
  48. package/src/__tests__/checker.test.ts +11 -3
  49. package/src/__tests__/client-os-metadata-persistence.test.ts +35 -1
  50. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -25
  51. package/src/__tests__/computer-use-tools.test.ts +1 -233
  52. package/src/__tests__/config-model-image-provider.test.ts +39 -0
  53. package/src/__tests__/config-schema.test.ts +16 -0
  54. package/src/__tests__/config-watcher.test.ts +24 -0
  55. package/src/__tests__/context-search-agent-runner.test.ts +260 -1
  56. package/src/__tests__/context-search-fanout.test.ts +1 -1
  57. package/src/__tests__/context-search-types.test.ts +7 -7
  58. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +8 -2
  59. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +2 -0
  60. package/src/__tests__/conversation-agent-loop.test.ts +546 -16
  61. package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
  62. package/src/__tests__/conversation-evictor.test.ts +62 -37
  63. package/src/__tests__/conversation-history-boundary-repair-failure.test.ts +174 -0
  64. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +2 -0
  65. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  66. package/src/__tests__/conversation-retry-route.test.ts +22 -0
  67. package/src/__tests__/conversation-routes-disk-view.test.ts +2 -0
  68. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +2 -0
  69. package/src/__tests__/conversation-routes-guardian-reply.test.ts +10 -0
  70. package/src/__tests__/conversation-routes-hidden-queue.test.ts +2 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
  72. package/src/__tests__/conversation-runtime-assembly.test.ts +29 -0
  73. package/src/__tests__/conversation-surface-snapshots.test.ts +543 -0
  74. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +151 -0
  75. package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +63 -1
  76. package/src/__tests__/conversation-tool-setup-attribution.test.ts +23 -0
  77. package/src/__tests__/conversation-undo.test.ts +61 -0
  78. package/src/__tests__/credential-execution-client.test.ts +6 -6
  79. package/src/__tests__/credential-prompt-route.test.ts +35 -3
  80. package/src/__tests__/cu-unified-flow.test.ts +82 -1
  81. package/src/__tests__/db-mode-sessions-migration.test.ts +175 -0
  82. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -0
  83. package/src/__tests__/dm-backfill.test.ts +91 -1
  84. package/src/__tests__/document-create-route.test.ts +88 -0
  85. package/src/__tests__/document-sync-tags.test.ts +27 -0
  86. package/src/__tests__/emit-signal-routing-intent.test.ts +142 -2
  87. package/src/__tests__/events-tail-route.test.ts +25 -0
  88. package/src/__tests__/evict-conversations-for-reload.test.ts +57 -8
  89. package/src/__tests__/guardian-action-sweep.test.ts +16 -0
  90. package/src/__tests__/guardian-dispatch.test.ts +13 -0
  91. package/src/__tests__/headless-browser-interactions.test.ts +64 -20
  92. package/src/__tests__/heartbeat-service.test.ts +2 -59
  93. package/src/__tests__/helpers/mock-conversation.ts +21 -0
  94. package/src/__tests__/helpers/native-web-search-harness.ts +2 -0
  95. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +5 -0
  96. package/src/__tests__/host-cu-proxy.test.ts +46 -2
  97. package/src/__tests__/http-user-message-parity.test.ts +2 -0
  98. package/src/__tests__/image-credentials.test.ts +49 -0
  99. package/src/__tests__/image-service-dispatcher.test.ts +45 -0
  100. package/src/__tests__/inference-profile-session-handler.test.ts +1 -1
  101. package/src/__tests__/interrupt-on-send.test.ts +57 -43
  102. package/src/__tests__/interrupt-turn-note.test.ts +5 -6
  103. package/src/__tests__/llm-catalog-parity.test.ts +2 -2
  104. package/src/__tests__/media-generate-image.test.ts +49 -1
  105. package/src/__tests__/message-provenance-contact-id.test.ts +69 -0
  106. package/src/__tests__/migration-export-to-gcs.test.ts +142 -0
  107. package/src/__tests__/mode-session-history.test.ts +245 -0
  108. package/src/__tests__/mode-session-recovery.test.ts +222 -0
  109. package/src/__tests__/mode-session-wire.test.ts +141 -0
  110. package/src/__tests__/notification-guardian-path.test.ts +7 -0
  111. package/src/__tests__/notification-telegram-adapter.test.ts +2 -2
  112. package/src/__tests__/notification-vellum-adapter.test.ts +34 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +12 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  115. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  116. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  117. package/src/__tests__/plugin-api-provider.test.ts +5 -0
  118. package/src/__tests__/plugin-api-resolve-credential.test.ts +97 -0
  119. package/src/__tests__/plugin-import-boundary-guard.test.ts +9 -0
  120. package/src/__tests__/process-message-display-content.test.ts +56 -0
  121. package/src/__tests__/reaction-persistence.test.ts +5 -0
  122. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +25 -9
  123. package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
  124. package/src/__tests__/scripted-turn-metadata-persistence.test.ts +73 -0
  125. package/src/__tests__/secret-ingress-http.test.ts +2 -0
  126. package/src/__tests__/secure-keys.test.ts +43 -1
  127. package/src/__tests__/send-endpoint-busy.test.ts +163 -1
  128. package/src/__tests__/server-history-render.test.ts +91 -1
  129. package/src/__tests__/settings-routes.test.ts +50 -1
  130. package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
  131. package/src/__tests__/subagent-fork-notifications.test.ts +1 -2
  132. package/src/__tests__/subagent-manager-notify.test.ts +1 -2
  133. package/src/__tests__/subagent-notify-parent.test.ts +146 -39
  134. package/src/__tests__/subagent-notify-stale-rebuild.test.ts +2 -4
  135. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +2 -0
  136. package/src/__tests__/thread-backfill.test.ts +53 -0
  137. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  138. package/src/__tests__/tool-result-metadata-plumbing.test.ts +3 -1
  139. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  140. package/src/__tests__/twilio-routes.test.ts +1 -0
  141. package/src/__tests__/web-search-backend-failure.test.ts +1 -1
  142. package/src/activation/progress-store.test.ts +12 -7
  143. package/src/agent/loop.ts +99 -41
  144. package/src/api/events/assistant-turn-start.ts +3 -0
  145. package/src/api/events/generation-handoff.ts +2 -0
  146. package/src/api/events/message-complete.ts +2 -0
  147. package/src/api/events/notification-conversation-created.ts +14 -16
  148. package/src/api/events/notification-intent.ts +4 -2
  149. package/src/api/events/tool-result.ts +85 -0
  150. package/src/api/events/tool-use-start.ts +3 -0
  151. package/src/api/events/user-message-echo.ts +3 -0
  152. package/src/api/index.ts +31 -6
  153. package/src/api/mode-session.test.ts +131 -0
  154. package/src/api/mode-session.ts +148 -0
  155. package/src/api/remember-tool.ts +15 -0
  156. package/src/api/responses/conversation-message.ts +6 -0
  157. package/src/approvals/AGENTS.md +4 -2
  158. package/src/browser/__tests__/operations.test.ts +29 -0
  159. package/src/browser/operation-meta.ts +2 -1
  160. package/src/browser/operations.ts +29 -0
  161. package/src/browser/virtual-desktop-target.ts +2 -8
  162. package/src/bundler/bundle-signer.ts +1 -3
  163. package/src/calls/__tests__/barge-in-guard.test.ts +3 -0
  164. package/src/calls/__tests__/call-controller.test.ts +94 -0
  165. package/src/calls/__tests__/call-funnel-telemetry.test.ts +204 -0
  166. package/src/calls/__tests__/voice-control-protocol.test.ts +15 -0
  167. package/src/calls/__tests__/voice-escalation-judge.test.ts +209 -0
  168. package/src/calls/__tests__/voice-leg-coordinator.test.ts +168 -0
  169. package/src/{live-voice/__tests__/live-voice-metrics.test.ts → calls/__tests__/voice-metrics.test.ts} +32 -38
  170. package/src/calls/__tests__/voice-session-bridge.test.ts +576 -122
  171. package/src/calls/barge-in-guard.ts +5 -0
  172. package/src/calls/call-controller.ts +196 -1
  173. package/src/calls/call-domain.ts +4 -0
  174. package/src/calls/call-store.ts +87 -2
  175. package/src/calls/guardian-action-sweep.ts +22 -8
  176. package/src/calls/media-stream-audio-transcode.ts +1 -3
  177. package/src/calls/typesafe-noul.ts +156 -0
  178. package/src/calls/voice-control-protocol.ts +6 -0
  179. package/src/calls/voice-escalation-judge.ts +145 -0
  180. package/src/calls/voice-escalation-target.ts +10 -0
  181. package/src/calls/voice-leg-coordinator.ts +121 -3
  182. package/src/{live-voice/live-voice-metrics.ts → calls/voice-metrics.ts} +94 -87
  183. package/src/calls/voice-session-bridge.ts +256 -112
  184. package/src/calls/voice-triage-escalate.ts +7 -0
  185. package/src/cli/__tests__/roadmap-cli.test.ts +4 -41
  186. package/src/cli/commands/__tests__/notifications.test.ts +35 -10
  187. package/src/cli/commands/browser.help.ts +4 -5
  188. package/src/cli/commands/browser.ts +0 -1
  189. package/src/cli/commands/clients.help.ts +46 -0
  190. package/src/cli/commands/clients.ts +116 -0
  191. package/src/cli/commands/credentials.help.ts +6 -1
  192. package/src/cli/commands/notifications.help.ts +16 -2
  193. package/src/cli/commands/notifications.ts +30 -8
  194. package/src/cli/commands/roadmap.help.ts +18 -21
  195. package/src/cli/commands/roadmap.ts +4 -21
  196. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
  197. package/src/cli/lib/bundled-marketplace.json +84 -34
  198. package/src/cli/lib/plugin-marketplace.ts +8 -0
  199. package/src/cli/lib/publish-plugin.ts +1 -2
  200. package/src/config/__tests__/default-provider.test.ts +3 -1
  201. package/src/config/__tests__/memory-retrospective-schema.test.ts +14 -1
  202. package/src/config/bundled-skills/computer-use/SKILL.md +1 -1
  203. package/src/config/bundled-skills/computer-use/TOOLS.json +89 -1
  204. package/src/config/bundled-skills/image-studio/SKILL.md +17 -2
  205. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +7 -11
  206. package/src/config/bundled-skills/sequences/TOOLS.json +1 -5
  207. package/src/config/call-site-defaults.ts +9 -0
  208. package/src/config/feature-flag-registry.json +16 -0
  209. package/src/config/profile-text-generation.test.ts +2 -2
  210. package/src/config/schemas/__tests__/tools.test.ts +31 -0
  211. package/src/config/schemas/call-site-catalog.ts +14 -0
  212. package/src/config/schemas/channels.ts +0 -18
  213. package/src/config/schemas/llm.ts +3 -1
  214. package/src/config/schemas/mcp.ts +2 -1
  215. package/src/config/schemas/memory-retrospective.ts +11 -2
  216. package/src/config/schemas/services.ts +15 -6
  217. package/src/config/schemas/tools.ts +10 -0
  218. package/src/config/session-groups-gate.ts +21 -0
  219. package/src/config/skills.ts +2 -6
  220. package/src/conversations/__tests__/message-consolidation.test.ts +149 -0
  221. package/src/conversations/message-consolidation.ts +102 -5
  222. package/src/credential-execution/ces-connect.test.ts +69 -0
  223. package/src/credential-execution/ces-connect.ts +115 -0
  224. package/src/credential-execution/ces-runtime.ts +55 -93
  225. package/src/credential-execution/executable-discovery.ts +1 -11
  226. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
  227. package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
  228. package/src/daemon/browser-mode-session.test.ts +402 -0
  229. package/src/daemon/browser-mode-session.ts +186 -0
  230. package/src/daemon/computer-use-mode-session.test.ts +210 -0
  231. package/src/daemon/computer-use-mode-session.ts +105 -0
  232. package/src/daemon/config-watcher.ts +10 -0
  233. package/src/daemon/conversation-agent-loop-handlers.ts +58 -10
  234. package/src/daemon/conversation-agent-loop.ts +219 -25
  235. package/src/daemon/conversation-evictor.ts +5 -24
  236. package/src/daemon/conversation-history.ts +10 -0
  237. package/src/daemon/conversation-interrupt-repair.ts +4 -43
  238. package/src/daemon/conversation-interrupt.ts +7 -11
  239. package/src/daemon/conversation-messaging.ts +58 -5
  240. package/src/daemon/conversation-mode-session.test.ts +1667 -0
  241. package/src/daemon/conversation-mode-session.ts +1008 -0
  242. package/src/daemon/conversation-process.ts +5 -0
  243. package/src/daemon/conversation-runtime-assembly.ts +10 -20
  244. package/src/daemon/conversation-skill-tools.ts +1 -0
  245. package/src/daemon/conversation-store.ts +4 -4
  246. package/src/daemon/conversation-surface-snapshots.ts +135 -0
  247. package/src/daemon/conversation-surfaces.ts +113 -5
  248. package/src/daemon/conversation-tool-setup.ts +8 -3
  249. package/src/daemon/conversation.ts +143 -41
  250. package/src/daemon/handlers/config-model.ts +7 -5
  251. package/src/daemon/handlers/shared.ts +55 -16
  252. package/src/daemon/host-cu-proxy.ts +51 -22
  253. package/src/daemon/lifecycle.ts +30 -13
  254. package/src/daemon/message-protocol.ts +0 -1
  255. package/src/daemon/message-provenance.ts +28 -4
  256. package/src/daemon/mode-session-startup-recovery.ts +23 -0
  257. package/src/daemon/mode-session-tracking.ts +54 -0
  258. package/src/daemon/process-message.ts +13 -4
  259. package/src/daemon/virtual-desktop-context.ts +21 -0
  260. package/src/desktop/__tests__/fake-desktop.ts +1 -0
  261. package/src/desktop/desktop-accessibility-script.ts +88 -0
  262. package/src/desktop/desktop-accessibility.test.ts +160 -0
  263. package/src/desktop/desktop-accessibility.ts +169 -0
  264. package/src/desktop/desktop-automation-lease.test.ts +76 -68
  265. package/src/desktop/desktop-automation-lease.ts +41 -50
  266. package/src/desktop/desktop-browser-endpoint.ts +4 -0
  267. package/src/desktop/desktop-computer-use-routing.test.ts +330 -0
  268. package/src/desktop/desktop-computer-use.test.ts +449 -0
  269. package/src/desktop/desktop-computer-use.ts +502 -0
  270. package/src/desktop/desktop-dependencies.ts +47 -293
  271. package/src/desktop/desktop-display.ts +2 -2
  272. package/src/desktop/desktop-panel-config.test.ts +13 -3
  273. package/src/desktop/desktop-panel-config.ts +109 -15
  274. package/src/desktop/desktop-session-manager.test.ts +69 -21
  275. package/src/desktop/desktop-session-manager.ts +76 -22
  276. package/src/desktop/desktop-stream-bridge.test.ts +11 -122
  277. package/src/desktop/desktop-stream-bridge.ts +6 -8
  278. package/src/desktop/desktop-window-theme.ts +5 -25
  279. package/src/desktop/virtual-desktop-feature.ts +52 -2
  280. package/src/desktop/virtual-desktop-platform.test.ts +5 -9
  281. package/src/documents/document-store.ts +23 -0
  282. package/src/heartbeat/__tests__/heartbeat-service.test.ts +22 -26
  283. package/src/heartbeat/heartbeat-service.ts +5 -33
  284. package/src/ipc/__tests__/browser-ipc.test.ts +215 -6
  285. package/src/ipc/assistant-server.ts +2 -0
  286. package/src/ipc/routes/__tests__/channel-reply-ipc-routes.test.ts +111 -0
  287. package/src/ipc/routes/channel-reply-ipc-routes.ts +73 -0
  288. package/src/live-voice/__tests__/continuation-judge.test.ts +151 -0
  289. package/src/live-voice/__tests__/live-activity-reporter.test.ts +154 -15
  290. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +743 -31
  291. package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +191 -10
  292. package/src/live-voice/__tests__/live-voice-session-groups-gate.test.ts +87 -0
  293. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +5 -1
  294. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +41 -0
  295. package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +1017 -9
  296. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +224 -1
  297. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +101 -0
  298. package/src/live-voice/__tests__/live-voice-tts.test.ts +44 -0
  299. package/src/live-voice/__tests__/live-voice-vad.test.ts +939 -75
  300. package/src/live-voice/__tests__/microphone-idle-gate.test.ts +87 -0
  301. package/src/live-voice/__tests__/session-controls.test.ts +23 -0
  302. package/src/live-voice/__tests__/task-outcomes.test.ts +117 -0
  303. package/src/live-voice/__tests__/voice-input-diagnostics.test.ts +117 -0
  304. package/src/live-voice/camera-mode-session.test.ts +265 -0
  305. package/src/live-voice/camera-mode-session.ts +277 -0
  306. package/src/live-voice/continuation-judge.ts +123 -0
  307. package/src/live-voice/live-activity-reporter.ts +115 -6
  308. package/src/live-voice/live-voice-manager.ts +36 -1
  309. package/src/live-voice/live-voice-photo.ts +88 -7
  310. package/src/live-voice/live-voice-session-manager.ts +15 -0
  311. package/src/live-voice/live-voice-session.ts +1443 -747
  312. package/src/live-voice/live-voice-tts.ts +22 -4
  313. package/src/live-voice/microphone-idle-gate.ts +56 -0
  314. package/src/live-voice/protocol.ts +171 -4
  315. package/src/live-voice/session-controls.ts +11 -3
  316. package/src/live-voice/task-outcomes.ts +131 -0
  317. package/src/live-voice/voice-input-diagnostics.ts +117 -0
  318. package/src/mcp/__tests__/manager-tool-caps.test.ts +63 -18
  319. package/src/mcp/__tests__/tool-caps.test.ts +28 -1
  320. package/src/mcp/manager.ts +3 -0
  321. package/src/mcp/tool-caps.ts +12 -4
  322. package/src/media/__tests__/image-models.test.ts +58 -0
  323. package/src/media/image-credentials.ts +5 -0
  324. package/src/media/image-models.ts +52 -0
  325. package/src/media/image-service.ts +8 -0
  326. package/src/media/openrouter-image-service.test.ts +133 -0
  327. package/src/media/openrouter-image-service.ts +153 -0
  328. package/src/media/types.ts +1 -1
  329. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +65 -11
  330. package/src/messaging/providers/discord/send.ts +4 -17
  331. package/src/messaging/providers/discord/transport.ts +2 -2
  332. package/src/messaging/providers/outlook/client.ts +16 -22
  333. package/src/messaging/providers/telegram-bot/send.test.ts +236 -1
  334. package/src/messaging/providers/telegram-bot/send.ts +10 -7
  335. package/src/messaging/providers/telegram-bot/transport.ts +3 -10
  336. package/src/messaging/providers/whatsapp/send.test.ts +169 -4
  337. package/src/monitoring/__tests__/control.test.ts +17 -5
  338. package/src/monitoring/control.ts +15 -5
  339. package/src/notifications/AGENTS.md +1 -1
  340. package/src/notifications/README.md +83 -99
  341. package/src/notifications/__tests__/broadcaster.test.ts +1 -1
  342. package/src/notifications/__tests__/channel-allowlist.test.ts +39 -0
  343. package/src/notifications/__tests__/copy-composer.test.ts +1 -1
  344. package/src/notifications/__tests__/decision-engine.test.ts +79 -2
  345. package/src/notifications/__tests__/deterministic-checks.test.ts +10 -10
  346. package/src/notifications/__tests__/home-feed-side-effect.test.ts +93 -4
  347. package/src/notifications/__tests__/notification-utils.test.ts +18 -0
  348. package/src/notifications/adapters/macos.ts +37 -46
  349. package/src/notifications/broadcaster.ts +5 -6
  350. package/src/notifications/channel-allowlist.ts +44 -0
  351. package/src/notifications/copy-composer.ts +0 -23
  352. package/src/notifications/decision-engine.ts +24 -14
  353. package/src/notifications/deterministic-checks.ts +1 -1
  354. package/src/notifications/emit-signal.ts +123 -37
  355. package/src/notifications/home-feed-side-effect.ts +9 -7
  356. package/src/notifications/notification-utils.ts +19 -0
  357. package/src/notifications/signal.ts +4 -19
  358. package/src/notifications/types.ts +15 -0
  359. package/src/oauth/connection-resolver.ts +1 -2
  360. package/src/oauth/oauth-store.ts +1 -3
  361. package/src/oauth/provider-serializer.ts +7 -0
  362. package/src/oauth/seed-providers.ts +30 -0
  363. package/src/onboarding/onboarding-events-store.ts +99 -12
  364. package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
  365. package/src/persistence/client-connection-events-store.ts +377 -0
  366. package/src/persistence/conversation-crud.ts +55 -6
  367. package/src/persistence/conversation-mode-sessions.test.ts +424 -0
  368. package/src/persistence/conversation-mode-sessions.ts +460 -0
  369. package/src/persistence/conversation-queries.ts +6 -19
  370. package/src/persistence/llm-usage-store.ts +3 -9
  371. package/src/persistence/message-metadata.ts +25 -0
  372. package/src/persistence/migrations/381-create-conversation-mode-sessions.ts +41 -0
  373. package/src/persistence/migrations/382-create-client-connection-events.ts +40 -0
  374. package/src/persistence/migrations/__tests__/382-create-client-connection-events.test.ts +56 -0
  375. package/src/persistence/schema/conversations.ts +67 -0
  376. package/src/persistence/schema/infrastructure.ts +29 -0
  377. package/src/persistence/steps.ts +17 -0
  378. package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
  379. package/src/plugin-api/effective-context-window.ts +33 -0
  380. package/src/plugin-api/index.ts +24 -5
  381. package/src/plugin-api/plugin-name-env.ts +125 -0
  382. package/src/plugin-api/resolve-credential.ts +79 -20
  383. package/src/plugin-api/vision-support.test.ts +22 -0
  384. package/src/plugin-api/vision-support.ts +17 -5
  385. package/src/plugins/__tests__/active-plugin-env.test.ts +136 -0
  386. package/src/plugins/active-plugin-env.ts +51 -0
  387. package/src/plugins/defaults/index.ts +24 -0
  388. package/src/plugins/defaults/memory/__tests__/archive-removal-framing.test.ts +64 -0
  389. package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
  390. package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
  391. package/src/plugins/defaults/memory/context-search/agent-runner.ts +78 -22
  392. package/src/plugins/defaults/memory/context-search/format.ts +51 -7
  393. package/src/plugins/defaults/memory/context-search/limits.ts +10 -12
  394. package/src/plugins/defaults/memory/context-search/recall-input.ts +38 -0
  395. package/src/plugins/defaults/memory/context-search/search.ts +8 -14
  396. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +1 -0
  397. package/src/plugins/defaults/memory/context-search/types.ts +1 -11
  398. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
  399. package/src/plugins/defaults/memory/graph/tool-handlers.ts +12 -21
  400. package/src/plugins/defaults/memory/graph/tools.ts +35 -64
  401. package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
  402. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +5 -0
  403. package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
  404. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +2 -2
  405. package/src/plugins/defaults/memory/tools.test.ts +124 -9
  406. package/src/plugins/defaults/memory/tools.ts +38 -11
  407. package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +165 -27
  408. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
  409. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +46 -27
  410. package/src/plugins/defaults/memory/v3/orchestrate.ts +78 -38
  411. package/src/plugins/defaults/memory/v3/pool-log-store.ts +65 -79
  412. package/src/plugins/defaults/memory/v3/pool-select.test.ts +14 -5
  413. package/src/plugins/defaults/memory/v3/pool-select.ts +683 -44
  414. package/src/plugins/defaults/memory/worker-control.ts +15 -5
  415. package/src/plugins/defaults/task-progress/__tests__/registration.test.ts +70 -0
  416. package/src/plugins/defaults/task-progress/__tests__/task-progress-context.test.ts +380 -0
  417. package/src/plugins/defaults/task-progress/hooks/post-compact.ts +19 -0
  418. package/src/plugins/defaults/task-progress/hooks/user-prompt-submit.ts +24 -0
  419. package/src/plugins/defaults/task-progress/package.json +14 -0
  420. package/src/plugins/defaults/task-progress/src/apply-task-progress-context.ts +93 -0
  421. package/src/plugins/defaults/task-progress/src/format-task-progress-context.ts +67 -0
  422. package/src/providers/__tests__/context-overflow-error.test.ts +10 -0
  423. package/src/providers/__tests__/search-provider-catalog.test.ts +18 -0
  424. package/src/providers/anthropic/client.ts +10 -1
  425. package/src/providers/anthropic-gateway-shared.ts +6 -0
  426. package/src/providers/fetch-provider-catalog.ts +11 -0
  427. package/src/providers/gemini/client.ts +25 -8
  428. package/src/providers/inference/adapter-factory.ts +1 -1
  429. package/src/providers/jev/client.test.ts +10 -0
  430. package/src/providers/jev/client.ts +20 -4
  431. package/src/providers/model-catalog.ts +4 -3
  432. package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +94 -1
  433. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +106 -1
  434. package/src/providers/openai/__tests__/chat-template-flatten.test.ts +24 -0
  435. package/src/providers/openai/__tests__/google-thought-signature.test.ts +1 -2
  436. package/src/providers/openai/chat-completions-provider.ts +100 -29
  437. package/src/providers/openai/google-thought-signature.ts +0 -6
  438. package/src/providers/openai/responses-provider.ts +23 -1
  439. package/src/providers/ratelimit.ts +1 -3
  440. package/src/providers/search-provider-catalog.ts +24 -0
  441. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +24 -0
  442. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +7 -1
  443. package/src/providers/speech-to-text/__tests__/vellum-managed-realtime.test.ts +50 -1
  444. package/src/providers/speech-to-text/deepgram-flux-frames.ts +11 -0
  445. package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +13 -0
  446. package/src/providers/types.ts +5 -0
  447. package/src/routes/control.ts +7 -5
  448. package/src/runtime/AGENTS.md +4 -2
  449. package/src/runtime/actor-trust-resolver.ts +3 -3
  450. package/src/runtime/agent-wake.ts +1 -1
  451. package/src/runtime/approval-message-composer.ts +3 -69
  452. package/src/runtime/assistant-event-hub.ts +73 -75
  453. package/src/runtime/assistant-event-publish-options.ts +7 -0
  454. package/src/runtime/assistant-event-targeting.ts +79 -0
  455. package/src/runtime/assistant-stream-state.ts +13 -100
  456. package/src/runtime/background-job-runner.ts +5 -5
  457. package/src/runtime/channel-retry-sweep.ts +1 -0
  458. package/src/runtime/guardian-action-message-composer.ts +8 -161
  459. package/src/runtime/guardian-reply-router.ts +28 -32
  460. package/src/runtime/http-types.ts +6 -0
  461. package/src/runtime/message-composer-types.ts +3 -61
  462. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +67 -1
  463. package/src/runtime/migrations/vbundle-builder.ts +24 -1
  464. package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
  465. package/src/runtime/migrations/vbundle-import-policy.ts +10 -0
  466. package/src/runtime/migrations/vbundle-importer.ts +4 -1
  467. package/src/runtime/migrations/vbundle-streaming-importer.ts +4 -1
  468. package/src/runtime/migrations/vbundle-validator.ts +1 -0
  469. package/src/runtime/pending-interactions-mode-session.test.ts +62 -0
  470. package/src/runtime/pending-interactions.ts +29 -0
  471. package/src/runtime/routes/__tests__/client-history-routes.test.ts +104 -0
  472. package/src/runtime/routes/__tests__/image-generation-routes.test.ts +97 -0
  473. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +1 -1
  474. package/src/runtime/routes/__tests__/roadmap-routes.test.ts +29 -6
  475. package/src/runtime/routes/browser-context.ts +31 -11
  476. package/src/runtime/routes/browser-routes.ts +43 -4
  477. package/src/runtime/routes/btw-routes.ts +9 -4
  478. package/src/runtime/routes/client-routes.ts +108 -0
  479. package/src/runtime/routes/conversation-management-routes.ts +4 -1
  480. package/src/runtime/routes/conversation-routes.ts +113 -27
  481. package/src/runtime/routes/credential-prompt-routes.ts +4 -3
  482. package/src/runtime/routes/credential-routes.ts +1 -2
  483. package/src/runtime/routes/desktop-setup-routes.test.ts +9 -18
  484. package/src/runtime/routes/desktop-setup-routes.ts +4 -6
  485. package/src/runtime/routes/documents-routes.ts +51 -0
  486. package/src/runtime/routes/email-routes.ts +3 -7
  487. package/src/runtime/routes/events-routes.ts +75 -54
  488. package/src/runtime/routes/identity-routes.ts +1 -5
  489. package/src/runtime/routes/image-generation-routes.ts +8 -14
  490. package/src/runtime/routes/inbound-message-handler.ts +35 -38
  491. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.test.ts +168 -0
  492. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.ts +96 -0
  493. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +38 -9
  494. package/src/runtime/routes/inbound-stages/background-dispatch.ts +2 -5
  495. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -0
  496. package/src/runtime/routes/llm-context-normalization.ts +1 -7
  497. package/src/runtime/routes/migration-routes.ts +73 -2
  498. package/src/runtime/routes/notification-routes.ts +10 -0
  499. package/src/runtime/routes/oauth-providers.ts +7 -0
  500. package/src/runtime/routes/plugins-routes.ts +6 -0
  501. package/src/runtime/routes/roadmap-routes.ts +47 -8
  502. package/src/runtime/routes/secret-routes.ts +1 -1
  503. package/src/runtime/routes/settings-routes.ts +10 -4
  504. package/src/runtime/routes/watcher-routes.ts +1 -3
  505. package/src/runtime/trust-class.test.ts +7 -6
  506. package/src/runtime/trust-class.ts +9 -17
  507. package/src/schedule/__tests__/worker-control.test.ts +36 -5
  508. package/src/schedule/worker-control.ts +15 -5
  509. package/src/security/secure-keys.ts +53 -103
  510. package/src/skills/clawhub.ts +3 -8
  511. package/src/skills/managed-store.ts +1 -5
  512. package/src/stt/types.ts +4 -0
  513. package/src/subagent/notify.ts +54 -34
  514. package/src/subagent/parent-notification.ts +6 -0
  515. package/src/telemetry/__tests__/phone-call-funnel.test.ts +72 -0
  516. package/src/telemetry/phone-call-funnel.ts +144 -0
  517. package/src/tools/__tests__/tool-schema-root-combinator-guard.test.ts +81 -0
  518. package/src/tools/ask-question/ask-question-tool.ts +6 -4
  519. package/src/tools/browser/browser-execution.ts +22 -59
  520. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +561 -39
  521. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +435 -39
  522. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +1 -8
  523. package/src/tools/client-os.ts +15 -0
  524. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
  525. package/src/tools/computer-use/target.ts +42 -0
  526. package/src/tools/document/document-tool.ts +13 -18
  527. package/src/tools/execution-target.ts +14 -16
  528. package/src/tools/host-terminal/host-shell.ts +4 -1
  529. package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +250 -0
  530. package/src/tools/network/__tests__/web-search.test.ts +203 -0
  531. package/src/tools/network/web-fetch.ts +274 -7
  532. package/src/tools/network/web-search.ts +468 -6
  533. package/src/tools/permission-checker.ts +3 -3
  534. package/src/tools/policy-context.ts +3 -1
  535. package/src/tools/skills/load.ts +2 -1
  536. package/src/tools/skills/sandbox-runner.ts +11 -2
  537. package/src/tools/skills/skill-script-runner.ts +7 -0
  538. package/src/tools/skills/skill-tool-factory.ts +15 -1
  539. package/src/tools/terminal/__tests__/safe-env.test.ts +11 -0
  540. package/src/tools/terminal/safe-env.ts +4 -0
  541. package/src/tools/terminal/shell.ts +2 -0
  542. package/src/tools/tool-approval-handler.ts +5 -1
  543. package/src/tools/tool-types.ts +3 -3
  544. package/src/tools/types.ts +8 -2
  545. package/src/util/__tests__/worker-process-command.test.ts +5 -4
  546. package/src/util/abort-reasons.ts +18 -26
  547. package/src/util/canonicalize-identity.test.ts +21 -0
  548. package/src/util/canonicalize-identity.ts +16 -0
  549. package/src/util/errors.ts +13 -3
  550. package/src/util/provider-error-patterns.ts +3 -0
  551. package/src/util/worker-process.ts +77 -35
  552. package/src/workspace/migrations/157-add-desktop-files-launcher.ts +94 -0
  553. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +4 -10
  554. package/src/workspace/migrations/__tests__/157-add-desktop-files-launcher.test.ts +113 -0
  555. package/src/workspace/migrations/registry.ts +2 -0
  556. package/src/__tests__/computer-use-window-schema.test.ts +0 -24
  557. package/src/api/events/heartbeat-alert.ts +0 -20
  558. package/src/daemon/message-types/web-activity.ts +0 -65
  559. package/src/desktop/desktop-dependencies.test.ts +0 -131
  560. package/src/tools/computer-use/definitions.ts +0 -607
@@ -0,0 +1,186 @@
1
+ import type { ModeSession } from "../api/mode-session.js";
2
+ import type { BrowserOperationLifecycle } from "../browser/operations.js";
3
+ import type {
4
+ ConversationModeSessionCoordinator,
5
+ ModeSessionSourceHandle,
6
+ ModeSessionTerminalDisposition,
7
+ } from "./conversation-mode-session.js";
8
+ import { claimModeSessionTurn } from "./mode-session-tracking.js";
9
+
10
+ export type { BrowserOperationLifecycle } from "../browser/operations.js";
11
+
12
+ type ModeSessionCoordinator = Pick<
13
+ ConversationModeSessionCoordinator,
14
+ | "activateSource"
15
+ | "claimTurn"
16
+ | "getTurnOwner"
17
+ | "recordActivity"
18
+ | "retireSource"
19
+ >;
20
+
21
+ export interface BrowserOperationToken {
22
+ readonly turnId: string;
23
+ readonly lifecycle: Exclude<BrowserOperationLifecycle, "status">;
24
+ readonly owner: ModeSession;
25
+ readonly handle?: ModeSessionSourceHandle;
26
+ }
27
+
28
+ const BROWSER_SOURCE_ID = "browser";
29
+
30
+ /** Maps typed browser operations onto the shared session coordinator. */
31
+ export class BrowserModeSessionProducer {
32
+ readonly #coordinator: ModeSessionCoordinator;
33
+ readonly #generation: number;
34
+ readonly #isTrackingEnabled: () => boolean;
35
+ #activeHandle?: ModeSessionSourceHandle;
36
+
37
+ constructor(
38
+ coordinator: ModeSessionCoordinator,
39
+ generation = 1,
40
+ isTrackingEnabled: () => boolean = () => true,
41
+ ) {
42
+ this.#coordinator = coordinator;
43
+ this.#generation = generation;
44
+ this.#isTrackingEnabled = isTrackingEnabled;
45
+ }
46
+
47
+ beginOperation(input: {
48
+ turnId: string;
49
+ lifecycle: BrowserOperationLifecycle;
50
+ at: number;
51
+ }): BrowserOperationToken | undefined {
52
+ if (input.lifecycle === "status") {
53
+ return undefined;
54
+ }
55
+
56
+ const existingOwner = this.#coordinator.getTurnOwner(input.turnId);
57
+ if (existingOwner) {
58
+ const handle = this.#activeHandle;
59
+ return {
60
+ turnId: input.turnId,
61
+ lifecycle: input.lifecycle,
62
+ owner: existingOwner,
63
+ ...(handle &&
64
+ (input.lifecycle === "terminal" || handle.id === existingOwner.id)
65
+ ? { handle }
66
+ : {}),
67
+ };
68
+ }
69
+
70
+ if (input.lifecycle === "terminal") {
71
+ const handle = this.#activeHandle;
72
+ if (!handle) {
73
+ return undefined;
74
+ }
75
+ const owner = claimModeSessionTurn(
76
+ this.#coordinator,
77
+ input.turnId,
78
+ handle,
79
+ input.at,
80
+ );
81
+ if (!owner || owner.id !== handle.id) {
82
+ return undefined;
83
+ }
84
+ return {
85
+ turnId: input.turnId,
86
+ lifecycle: input.lifecycle,
87
+ owner,
88
+ handle,
89
+ };
90
+ }
91
+
92
+ if (!this.#isTrackingEnabled()) {
93
+ return undefined;
94
+ }
95
+
96
+ const handle = this.#coordinator.activateSource({
97
+ sourceId: BROWSER_SOURCE_ID,
98
+ generation: this.#generation,
99
+ mode: "browser",
100
+ sourceStartedAt: input.at,
101
+ });
102
+ if (!handle) {
103
+ return undefined;
104
+ }
105
+ const owner = claimModeSessionTurn(
106
+ this.#coordinator,
107
+ input.turnId,
108
+ handle,
109
+ input.at,
110
+ );
111
+ if (!owner) {
112
+ return undefined;
113
+ }
114
+ if (owner.id === handle.id) {
115
+ this.#activeHandle = handle;
116
+ }
117
+ return {
118
+ turnId: input.turnId,
119
+ lifecycle: input.lifecycle,
120
+ owner,
121
+ ...(owner.id === handle.id ? { handle } : {}),
122
+ };
123
+ }
124
+
125
+ finishOperation(
126
+ token: BrowserOperationToken | undefined,
127
+ outcome: {
128
+ at: number;
129
+ isError: boolean;
130
+ cancelled: boolean;
131
+ terminalReason?: "browser_closed" | "browser_detached";
132
+ },
133
+ ): boolean {
134
+ if (!token) {
135
+ return false;
136
+ }
137
+
138
+ const handle = token.handle;
139
+ if (outcome.cancelled) {
140
+ if (!handle || !this.#isCurrent(handle)) {
141
+ return false;
142
+ }
143
+ return this.#retire(handle, {
144
+ status: "interrupted",
145
+ endReason: "browser_operation_cancelled",
146
+ });
147
+ }
148
+
149
+ if (token.lifecycle === "terminal") {
150
+ if (!handle || !this.#isCurrent(handle)) {
151
+ return false;
152
+ }
153
+ if (!outcome.isError) {
154
+ return this.#retire(handle, {
155
+ status: "completed",
156
+ endReason: outcome.terminalReason ?? "browser_closed",
157
+ });
158
+ }
159
+ }
160
+
161
+ return this.#coordinator.recordActivity(token.turnId, outcome.at);
162
+ }
163
+
164
+ #isCurrent(handle: ModeSessionSourceHandle): boolean {
165
+ const active = this.#activeHandle;
166
+ return (
167
+ active?.sourceId === handle.sourceId &&
168
+ active.generation === handle.generation &&
169
+ active.activation === handle.activation &&
170
+ active.id === handle.id
171
+ );
172
+ }
173
+
174
+ #retire(
175
+ handle: ModeSessionSourceHandle,
176
+ disposition: ModeSessionTerminalDisposition,
177
+ ): boolean {
178
+ try {
179
+ return this.#coordinator.retireSource(handle, disposition);
180
+ } finally {
181
+ if (this.#isCurrent(handle)) {
182
+ this.#activeHandle = undefined;
183
+ }
184
+ }
185
+ }
186
+ }
@@ -0,0 +1,210 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+
3
+ import { setOverridesForTesting } from "../__tests__/feature-flag-test-helpers.js";
4
+ import type { ModeSession } from "../api/mode-session.js";
5
+ import { isSessionGroupsEnabled } from "../config/session-groups-gate.js";
6
+ import { ComputerUseModeSessionProducer } from "./computer-use-mode-session.js";
7
+ import type { ModeSessionSourceHandle } from "./conversation-mode-session.js";
8
+
9
+ function createCoordinator() {
10
+ let owner: ModeSession | undefined;
11
+ let activation = 0;
12
+ const activated: Array<{
13
+ sourceId: string;
14
+ generation: number;
15
+ mode: "computer_use";
16
+ sourceStartedAt: number;
17
+ }> = [];
18
+ const retired: ModeSessionSourceHandle[] = [];
19
+ const activity: Array<{ turnId: string; at: number }> = [];
20
+ const draining: string[] = [];
21
+
22
+ const coordinator = {
23
+ activateSource(input: {
24
+ sourceId: string;
25
+ generation: number;
26
+ mode: "computer_use";
27
+ sourceStartedAt: number;
28
+ }): ModeSessionSourceHandle {
29
+ activated.push(input);
30
+ activation += 1;
31
+ return {
32
+ sourceId: input.sourceId,
33
+ generation: input.generation,
34
+ activation,
35
+ id: `session-${activation}`,
36
+ mode: input.mode,
37
+ };
38
+ },
39
+ claimTurn(
40
+ _turnId: string,
41
+ handle: ModeSessionSourceHandle,
42
+ _at: number,
43
+ ): ModeSession {
44
+ owner = { id: handle.id, mode: handle.mode };
45
+ return owner;
46
+ },
47
+ getTurnOwner: () => owner,
48
+ recordActivity: (turnId: string, at: number) => {
49
+ activity.push({ turnId, at });
50
+ return true;
51
+ },
52
+ retireSource: (handle: ModeSessionSourceHandle) => {
53
+ retired.push(handle);
54
+ return true;
55
+ },
56
+ beginDraining: (turnId: string) => {
57
+ draining.push(turnId);
58
+ return true;
59
+ },
60
+ };
61
+
62
+ return {
63
+ coordinator,
64
+ activated,
65
+ retired,
66
+ activity,
67
+ draining,
68
+ clearOwner: () => {
69
+ owner = undefined;
70
+ },
71
+ setOwner: (next: ModeSession) => {
72
+ owner = next;
73
+ },
74
+ };
75
+ }
76
+
77
+ describe("ComputerUseModeSessionProducer", () => {
78
+ afterEach(() => {
79
+ setOverridesForTesting({});
80
+ });
81
+
82
+ test.each([false, true])(
83
+ "retires failed admission while preserving a partial turn owner (%s)",
84
+ (partial) => {
85
+ const state = createCoordinator();
86
+ const claim = state.coordinator.claimTurn;
87
+ state.coordinator.claimTurn = (...args) => {
88
+ if (partial) {
89
+ claim(...args);
90
+ }
91
+ throw new Error("tracking unavailable");
92
+ };
93
+ const producer = new ComputerUseModeSessionProducer(state.coordinator);
94
+ expect(() =>
95
+ producer.recordAction({
96
+ turnId: "turn-123",
97
+ source: { sourceId: "computer", generation: 1 },
98
+ at: 100,
99
+ }),
100
+ ).toThrow("tracking unavailable");
101
+ expect(state.retired).toHaveLength(1);
102
+ expect(state.retired[0]?.mode).toBe("computer_use");
103
+ if (partial) {
104
+ expect(state.coordinator.getTurnOwner()).toBeDefined();
105
+ } else {
106
+ expect(state.coordinator.getTurnOwner()).toBeUndefined();
107
+ }
108
+ },
109
+ );
110
+
111
+ test("admits new tracking only while the shared feature flag is enabled", () => {
112
+ const state = createCoordinator();
113
+ const producer = new ComputerUseModeSessionProducer(
114
+ state.coordinator,
115
+ isSessionGroupsEnabled,
116
+ );
117
+ const source = { sourceId: "proxy-123", generation: 2 };
118
+
119
+ setOverridesForTesting({ "session-groups": false });
120
+ expect(
121
+ producer.recordAction({ turnId: "turn-disabled", source, at: 100 }),
122
+ ).toBeUndefined();
123
+ expect(state.activated).toEqual([]);
124
+
125
+ setOverridesForTesting({ "session-groups": true });
126
+ expect(
127
+ producer.recordAction({ turnId: "turn-enabled", source, at: 110 }),
128
+ ).toEqual({ id: "session-1", mode: "computer_use" });
129
+
130
+ setOverridesForTesting({ "session-groups": false });
131
+ expect(producer.endTask({ turnId: "turn-enabled", source })).toBe(true);
132
+ expect(state.retired).toHaveLength(1);
133
+ expect(state.draining).toEqual(["turn-enabled"]);
134
+ });
135
+
136
+ test("activates on the first action and records later action activity", () => {
137
+ const state = createCoordinator();
138
+ const producer = new ComputerUseModeSessionProducer(state.coordinator);
139
+ const source = { sourceId: "proxy-123", generation: 2 };
140
+
141
+ expect(
142
+ producer.recordAction({ turnId: "turn-123", source, at: 100 }),
143
+ ).toEqual({ id: "session-1", mode: "computer_use" });
144
+ expect(state.activated).toEqual([
145
+ expect.objectContaining({ ...source, sourceStartedAt: 100 }),
146
+ ]);
147
+
148
+ producer.recordAction({ turnId: "turn-123", source, at: 120 });
149
+ expect(state.activated).toHaveLength(1);
150
+ expect(state.activity).toEqual([{ turnId: "turn-123", at: 120 }]);
151
+ });
152
+
153
+ test("keeps an existing mode owner without activating a secondary run", () => {
154
+ const state = createCoordinator();
155
+ state.setOwner({ id: "live-123", mode: "live_vision" });
156
+ const producer = new ComputerUseModeSessionProducer(state.coordinator);
157
+
158
+ expect(
159
+ producer.recordAction({
160
+ turnId: "turn-123",
161
+ source: { sourceId: "proxy-123", generation: 0 },
162
+ at: 100,
163
+ }),
164
+ ).toEqual({ id: "live-123", mode: "live_vision" });
165
+ expect(state.activated).toEqual([]);
166
+ expect(state.activity).toEqual([{ turnId: "turn-123", at: 100 }]);
167
+ });
168
+
169
+ test("terminal end retires its exact handle and begins final reply drain", () => {
170
+ const state = createCoordinator();
171
+ const producer = new ComputerUseModeSessionProducer(state.coordinator);
172
+ const source = { sourceId: "proxy-123", generation: 3 };
173
+ producer.recordAction({ turnId: "turn-123", source, at: 100 });
174
+
175
+ expect(producer.endTask({ turnId: "turn-123", source })).toBe(true);
176
+ expect(state.retired).toEqual([
177
+ expect.objectContaining({
178
+ sourceId: "proxy-123",
179
+ generation: 3,
180
+ activation: 1,
181
+ id: "session-1",
182
+ }),
183
+ ]);
184
+ expect(state.draining).toEqual(["turn-123"]);
185
+ });
186
+
187
+ test("stale terminal callbacks cannot retire a replacement activation", () => {
188
+ const state = createCoordinator();
189
+ const producer = new ComputerUseModeSessionProducer(state.coordinator);
190
+ const oldSource = { sourceId: "proxy-123", generation: 3 };
191
+ producer.recordAction({ turnId: "turn-old", source: oldSource, at: 100 });
192
+ state.clearOwner();
193
+
194
+ const nextSource = { sourceId: "proxy-123", generation: 4 };
195
+ producer.recordAction({ turnId: "turn-new", source: nextSource, at: 200 });
196
+
197
+ expect(producer.endTask({ turnId: "turn-old", source: oldSource })).toBe(
198
+ false,
199
+ );
200
+ expect(state.retired).toEqual([]);
201
+ expect(producer.endTask({ turnId: "turn-new", source: nextSource })).toBe(
202
+ true,
203
+ );
204
+ expect(state.retired[0]).toMatchObject({
205
+ generation: 4,
206
+ activation: 2,
207
+ id: "session-2",
208
+ });
209
+ });
210
+ });
@@ -0,0 +1,105 @@
1
+ import type { ModeSession } from "../api/mode-session.js";
2
+ import type {
3
+ ConversationModeSessionCoordinator,
4
+ ModeSessionSourceHandle,
5
+ } from "./conversation-mode-session.js";
6
+ import { claimModeSessionTurn } from "./mode-session-tracking.js";
7
+
8
+ type ModeSessionCoordinator = Pick<
9
+ ConversationModeSessionCoordinator,
10
+ | "activateSource"
11
+ | "beginDraining"
12
+ | "claimTurn"
13
+ | "getTurnOwner"
14
+ | "recordActivity"
15
+ | "retireSource"
16
+ >;
17
+
18
+ export interface ComputerUseSourceIdentity {
19
+ sourceId: string;
20
+ generation: number;
21
+ }
22
+
23
+ /** Maps validated host computer actions onto the shared session coordinator. */
24
+ export class ComputerUseModeSessionProducer {
25
+ readonly #coordinator: ModeSessionCoordinator;
26
+ readonly #isTrackingEnabled: () => boolean;
27
+ #activeHandle?: ModeSessionSourceHandle;
28
+
29
+ constructor(
30
+ coordinator: ModeSessionCoordinator,
31
+ isTrackingEnabled: () => boolean = () => true,
32
+ ) {
33
+ this.#coordinator = coordinator;
34
+ this.#isTrackingEnabled = isTrackingEnabled;
35
+ }
36
+
37
+ recordAction(input: {
38
+ turnId: string;
39
+ source: ComputerUseSourceIdentity;
40
+ at: number;
41
+ }): ModeSession | undefined {
42
+ const existingOwner = this.#coordinator.getTurnOwner(input.turnId);
43
+ if (existingOwner) {
44
+ this.#coordinator.recordActivity(input.turnId, input.at);
45
+ return existingOwner;
46
+ }
47
+
48
+ if (!this.#isTrackingEnabled()) {
49
+ return undefined;
50
+ }
51
+
52
+ const handle = this.#coordinator.activateSource({
53
+ ...input.source,
54
+ mode: "computer_use",
55
+ sourceStartedAt: input.at,
56
+ });
57
+ if (!handle) {
58
+ return undefined;
59
+ }
60
+ const owner = claimModeSessionTurn(
61
+ this.#coordinator,
62
+ input.turnId,
63
+ handle,
64
+ input.at,
65
+ );
66
+ if (owner?.id === handle.id) {
67
+ this.#activeHandle = handle;
68
+ }
69
+ return owner;
70
+ }
71
+
72
+ endTask(input: {
73
+ turnId?: string;
74
+ source: ComputerUseSourceIdentity;
75
+ disposition?: {
76
+ status: "completed" | "interrupted";
77
+ endReason: string;
78
+ };
79
+ }): boolean {
80
+ const handle = this.#activeHandle;
81
+ if (
82
+ !handle ||
83
+ handle.sourceId !== input.source.sourceId ||
84
+ handle.generation !== input.source.generation
85
+ ) {
86
+ return false;
87
+ }
88
+
89
+ this.#activeHandle = undefined;
90
+ const retired = this.#coordinator.retireSource(
91
+ handle,
92
+ input.disposition ?? {
93
+ status: "completed",
94
+ endReason: "computer_use_ended",
95
+ },
96
+ );
97
+ if (
98
+ input.turnId &&
99
+ this.#coordinator.getTurnOwner(input.turnId)?.id === handle.id
100
+ ) {
101
+ this.#coordinator.beginDraining(input.turnId);
102
+ }
103
+ return retired;
104
+ }
105
+ }
@@ -190,12 +190,22 @@ export class ConfigWatcher {
190
190
  if (this.suppressReload) {
191
191
  return;
192
192
  }
193
+ const prevMcpGlobalMax = this.lastConfig?.tools.mcpGlobalMaxTools;
193
194
  try {
194
195
  const changed = await this.refreshConfigFromSources();
195
196
  if (changed) {
196
197
  evictConversationsForReload();
197
198
  refreshAuthenticatedApiRateLimit();
198
199
  publishConfigChanged();
200
+ const nextMcpGlobalMax = getConfig().tools.mcpGlobalMaxTools;
201
+ if (prevMcpGlobalMax !== nextMcpGlobalMax) {
202
+ reloadMcpServers().catch((err: unknown) => {
203
+ log.error(
204
+ { err },
205
+ "MCP reload after tools.mcpGlobalMaxTools change failed",
206
+ );
207
+ });
208
+ }
199
209
  }
200
210
  } catch (err) {
201
211
  log.error(
@@ -14,6 +14,11 @@ import { onActivationToolCall } from "../activation/turn-hooks.js";
14
14
  import type { AgentEvent } from "../agent/loop.js";
15
15
  import { resolveComputerUseToolName } from "../api/computer-use-tool.js";
16
16
  import type { AnsweredQuestion } from "../api/events/question-answered.js";
17
+ import type {
18
+ ToolActivityMetadata,
19
+ WebSearchMetadata,
20
+ WebSearchResultItem,
21
+ } from "../api/events/tool-result.js";
17
22
  import type { AssistantEvent } from "../api/index.js";
18
23
  import type {
19
24
  TurnChannelContext,
@@ -147,11 +152,7 @@ import type {
147
152
  SurfaceAction,
148
153
  UiSurfaceShow,
149
154
  } from "./message-protocol.js";
150
- import type {
151
- ToolActivityMetadata,
152
- WebSearchMetadata,
153
- WebSearchResultItem,
154
- } from "./message-types/web-activity.js";
155
+ import { bestEffortModeSessionTracking } from "./mode-session-tracking.js";
155
156
  import { referenceMediaBlocksForPersist } from "./persist-media-references.js";
156
157
  import { buildProviderRejectionLogFields } from "./provider-rejection-log-fields.js";
157
158
  import { turnOrRestingTrust } from "./trust-context-types.js";
@@ -1205,7 +1206,7 @@ async function reserveInflightMessageRow(
1205
1206
  conversationId: string,
1206
1207
  role: "assistant" | "user",
1207
1208
  metadata: Record<string, unknown> | undefined,
1208
- ): Promise<{ id: string }> {
1209
+ ): Promise<{ id: string; createdAt: number }> {
1209
1210
  const writer = createInflightContentWriter(conversationId);
1210
1211
  const reserved = await reserveMessage(
1211
1212
  conversationId,
@@ -1644,7 +1645,11 @@ export async function handleLlmCallStarted(
1644
1645
  }
1645
1646
  }
1646
1647
 
1647
- const metadata = buildAssistantChannelMetadata(state, deps);
1648
+ const modeSession = deps.ctx.modeSessions.getTurnOwner(deps.reqId);
1649
+ const metadata = {
1650
+ ...buildAssistantChannelMetadata(state, deps),
1651
+ ...(modeSession ? { modeSession } : {}),
1652
+ };
1648
1653
  const reservedRow = await reserveInflightMessageRow(
1649
1654
  state,
1650
1655
  deps.ctx.conversationId,
@@ -1652,6 +1657,13 @@ export async function handleLlmCallStarted(
1652
1657
  metadata,
1653
1658
  );
1654
1659
  state.lastAssistantMessageId = reservedRow.id;
1660
+ bestEffortModeSessionTracking("assistant row reservation", () =>
1661
+ deps.ctx.modeSessions.trackPersistedRow(
1662
+ deps.reqId,
1663
+ reservedRow.id,
1664
+ reservedRow.createdAt,
1665
+ ),
1666
+ );
1655
1667
  state.assistantRowAwaitingFinalization = true;
1656
1668
  // Fresh row → fresh accumulator. If an earlier (failed) LLM call
1657
1669
  // within the same run left partial state behind, the
@@ -1663,6 +1675,7 @@ export async function handleLlmCallStarted(
1663
1675
  type: "assistant_turn_start",
1664
1676
  messageId: reservedRow.id,
1665
1677
  conversationId: deps.ctx.conversationId,
1678
+ ...(modeSession ? { modeSession } : {}),
1666
1679
  });
1667
1680
  }
1668
1681
 
@@ -1881,6 +1894,7 @@ export function handleToolUse(
1881
1894
  // Carry the first-byte timestamp through so a client that connected after
1882
1895
  // the preview event still anchors the perceived-latency timer to it.
1883
1896
  previewStartedAt: state.toolPreviewStartedAt.get(event.id),
1897
+ modeSession: deps.ctx.modeSessions.getTurnOwner(deps.reqId),
1884
1898
  });
1885
1899
  // `message_complete` always precedes tool events (see handleMessageComplete),
1886
1900
  // so this tool_use block is already durable in the assistant row. The
@@ -2152,6 +2166,7 @@ function buildToolResultBlocks(
2152
2166
  function buildToolResultMetadata(
2153
2167
  deps: EventHandlerDeps,
2154
2168
  ): Record<string, unknown> {
2169
+ const modeSession = deps.ctx.modeSessions.getTurnOwner(deps.reqId);
2155
2170
  return {
2156
2171
  ...provenanceFromTrustContext(turnOrRestingTrust(deps.ctx)),
2157
2172
  userMessageChannel: deps.turnChannelContext.userMessageChannel,
@@ -2159,6 +2174,7 @@ function buildToolResultMetadata(
2159
2174
  userMessageInterface: deps.turnInterfaceContext.userMessageInterface,
2160
2175
  assistantMessageInterface:
2161
2176
  deps.turnInterfaceContext.assistantMessageInterface,
2177
+ ...(modeSession ? { modeSession } : {}),
2162
2178
  };
2163
2179
  }
2164
2180
 
@@ -2215,6 +2231,19 @@ async function persistPendingToolResultRow(
2215
2231
  deps.ctx.conversationId,
2216
2232
  buildToolResultMetadata(deps),
2217
2233
  );
2234
+ bestEffortModeSessionTracking("tool result persistence", () => {
2235
+ const row = getMessageById(rowId, deps.ctx.conversationId);
2236
+ if (row) {
2237
+ deps.ctx.modeSessions.trackPersistedRow(
2238
+ deps.reqId,
2239
+ rowId,
2240
+ row.createdAt,
2241
+ {
2242
+ startsDisplayBoundary: false,
2243
+ },
2244
+ );
2245
+ }
2246
+ });
2218
2247
  // Snapshot the batch after the reservation resolves so the last of the
2219
2248
  // concurrent writers reflects the fullest batch. On-arrival writes go to
2220
2249
  // the in-flight delta file; the finalize seam folds the row inline.
@@ -2253,7 +2282,7 @@ export async function finalizePendingToolResultRow(
2253
2282
  conversationId: string,
2254
2283
  metadata: Record<string, unknown>,
2255
2284
  rlog: pino.Logger,
2256
- ): Promise<void> {
2285
+ ): Promise<string | undefined> {
2257
2286
  if (state.pendingToolResults.size === 0) {
2258
2287
  return;
2259
2288
  }
@@ -2380,6 +2409,7 @@ export async function finalizePendingToolResultRow(
2380
2409
  }
2381
2410
  state.pendingToolResults.clear();
2382
2411
  state.pendingToolResultRowReservation = undefined;
2412
+ return rowId;
2383
2413
  }
2384
2414
 
2385
2415
  export async function handleToolResult(
@@ -2487,6 +2517,7 @@ export async function handleToolResult(
2487
2517
  conversationId: deps.ctx.conversationId,
2488
2518
  messageId: state.lastAssistantMessageId,
2489
2519
  toolUseId: event.toolUseId,
2520
+ modeSession: deps.ctx.modeSessions.getTurnOwner(deps.reqId),
2490
2521
  });
2491
2522
  // Capture the seq synchronously (before the persist await) so it reflects
2492
2523
  // the just-stamped tool_result event, then persist on arrival. A failure
@@ -2680,6 +2711,7 @@ export async function handleToolResult(
2680
2711
  answeredQuestion: event.answeredQuestion,
2681
2712
  errorCode: event.errorCode,
2682
2713
  completedAt,
2714
+ modeSession: deps.ctx.modeSessions.getTurnOwner(deps.reqId),
2683
2715
  });
2684
2716
 
2685
2717
  // Capture the seq synchronously (before the persist await) so it reflects the
@@ -3167,12 +3199,28 @@ export async function handleMessageComplete(
3167
3199
  // row as it arrived (`persistPendingToolResultRow`); this rewrites it to the
3168
3200
  // full batch (covering the case where a mid-arrival write failed), indexes it
3169
3201
  // for memory recall, and clears the batch state.
3170
- await finalizePendingToolResultRow(
3202
+ const toolResultRowId = await finalizePendingToolResultRow(
3171
3203
  state,
3172
3204
  deps.ctx.conversationId,
3173
3205
  buildToolResultMetadata(deps),
3174
3206
  deps.rlog,
3175
3207
  );
3208
+ if (toolResultRowId) {
3209
+ bestEffortModeSessionTracking("tool result finalization", () => {
3210
+ const toolResultRow = getMessageById(
3211
+ toolResultRowId,
3212
+ deps.ctx.conversationId,
3213
+ );
3214
+ if (toolResultRow) {
3215
+ deps.ctx.modeSessions.trackPersistedRow(
3216
+ deps.reqId,
3217
+ toolResultRowId,
3218
+ toolResultRow.createdAt,
3219
+ { startsDisplayBoundary: false },
3220
+ );
3221
+ }
3222
+ });
3223
+ }
3176
3224
 
3177
3225
  // Accumulate directives + warnings from the assistant content for
3178
3226
  // downstream attachment processing. `cleanAssistantContent` is also
@@ -3526,7 +3574,7 @@ function handleProviderError(
3526
3574
  try {
3527
3575
  recordRequestLog(
3528
3576
  deps.ctx.conversationId,
3529
- JSON.stringify(event.rawRequest),
3577
+ JSON.stringify(event.rawRequest ?? null),
3530
3578
  JSON.stringify(buildProviderErrorResponsePayload(event.error)),
3531
3579
  undefined,
3532
3580
  event.actualProvider,