@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
@@ -698,7 +698,10 @@ export async function streamCommitImport(
698
698
  // Retired-feature paths (see `policy.RETIRED_ARCHIVE_PATHS`) are
699
699
  // expected in legacy bundles and skip silently so the import report
700
700
  // matches preflight; anything else unresolvable earns a warning.
701
- if (!policy.isRetiredArchivePath(archivePath)) {
701
+ if (
702
+ !policy.isRetiredArchivePath(archivePath) &&
703
+ !policy.isGatewayArchivePath(archivePath)
704
+ ) {
702
705
  warnings.push(
703
706
  `Skipped "${archivePath}": no known disk target for this archive path`,
704
707
  );
@@ -53,6 +53,7 @@ const ExportOptions = z.object({
53
53
  include_logs: z.boolean(),
54
54
  include_browser_state: z.boolean(),
55
55
  include_memory_vectors: z.boolean(),
56
+ include_gateway: z.boolean().optional(),
56
57
  });
57
58
 
58
59
  export const ManifestSchema = z
@@ -0,0 +1,62 @@
1
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import type { Conversation } from "../daemon/conversation.js";
4
+ import {
5
+ clearConversations,
6
+ setConversation,
7
+ } from "../daemon/conversation-registry.js";
8
+ import * as pendingInteractions from "./pending-interactions.js";
9
+
10
+ describe("pending interaction mode-session waits", () => {
11
+ const invalidateStructuralWait = mock(() => true);
12
+
13
+ beforeEach(() => {
14
+ pendingInteractions.clear();
15
+ clearConversations();
16
+ invalidateStructuralWait.mockClear();
17
+ setConversation("conv-1", {
18
+ modeSessions: { invalidateStructuralWait },
19
+ } as unknown as Conversation);
20
+ });
21
+
22
+ afterEach(() => {
23
+ pendingInteractions.clear();
24
+ clearConversations();
25
+ });
26
+
27
+ test.each([
28
+ ["question", "question"],
29
+ ["confirmation", "confirmation"],
30
+ ["acp_confirmation", "confirmation"],
31
+ ["secret", "secret"],
32
+ ] as const)(
33
+ "resolving %s invalidates its exact structural wait",
34
+ (interactionKind, structuralKind) => {
35
+ pendingInteractions.register("request-1", {
36
+ conversationId: "conv-1",
37
+ kind: interactionKind,
38
+ });
39
+
40
+ expect(pendingInteractions.resolve("request-1", "answered")).toEqual(
41
+ expect.objectContaining({ kind: interactionKind }),
42
+ );
43
+ expect(invalidateStructuralWait).toHaveBeenCalledTimes(1);
44
+ expect(invalidateStructuralWait).toHaveBeenCalledWith({
45
+ kind: structuralKind,
46
+ responseId: "request-1",
47
+ });
48
+ },
49
+ );
50
+
51
+ test("does not invalidate for a missing or non-structural interaction", () => {
52
+ expect(pendingInteractions.resolve("missing", "answered")).toBeUndefined();
53
+ pendingInteractions.register("host-request", {
54
+ conversationId: "conv-1",
55
+ kind: "host_cu",
56
+ });
57
+
58
+ pendingInteractions.resolve("host-request", "answered");
59
+
60
+ expect(invalidateStructuralWait).not.toHaveBeenCalled();
61
+ });
62
+ });
@@ -20,6 +20,7 @@
20
20
 
21
21
  import type { InteractionResolutionState } from "../api/events/interaction-resolved.js";
22
22
  import type { QuestionEntry } from "../api/events/question-request.js";
23
+ import { findConversationOrSubagent } from "../daemon/conversation-registry.js";
23
24
  import type { UserDecision } from "../permissions/types.js";
24
25
  import { getLogger } from "../util/logger.js";
25
26
  import { broadcastMessage } from "./assistant-event-hub.js";
@@ -135,6 +136,33 @@ export interface PendingInteraction {
135
136
 
136
137
  const pending = new Map<string, PendingInteraction>();
137
138
 
139
+ function invalidateModeSessionStructuralWait(
140
+ requestId: string,
141
+ interaction: PendingInteraction,
142
+ ): void {
143
+ const kind =
144
+ interaction.kind === "acp_confirmation"
145
+ ? "confirmation"
146
+ : interaction.kind === "confirmation" ||
147
+ interaction.kind === "question" ||
148
+ interaction.kind === "secret"
149
+ ? interaction.kind
150
+ : undefined;
151
+ if (!kind) {
152
+ return;
153
+ }
154
+ try {
155
+ findConversationOrSubagent(
156
+ interaction.conversationId,
157
+ )?.modeSessions.invalidateStructuralWait({ kind, responseId: requestId });
158
+ } catch (err) {
159
+ log.warn(
160
+ { err, requestId, conversationId: interaction.conversationId, kind },
161
+ "Could not invalidate mode session structural wait",
162
+ );
163
+ }
164
+ }
165
+
138
166
  export function register(
139
167
  requestId: string,
140
168
  interaction: PendingInteraction,
@@ -162,6 +190,7 @@ export function resolve(
162
190
  return undefined;
163
191
  }
164
192
  pending.delete(requestId);
193
+ invalidateModeSessionStructuralWait(requestId, interaction);
165
194
  if (interaction.timer != null) {
166
195
  clearTimeout(interaction.timer);
167
196
  }
@@ -0,0 +1,104 @@
1
+ import { afterAll, describe, expect, mock, test } from "bun:test";
2
+
3
+ const historyResult = {
4
+ events: [
5
+ {
6
+ id: "evt-1",
7
+ clientId: "client-123",
8
+ interfaceId: "chrome-extension",
9
+ connectionId: "conn-1",
10
+ reason: "sse_open" as const,
11
+ occurredAt: Date.parse("2026-09-16T12:00:00.000Z"),
12
+ actorPrincipalId: "user-123",
13
+ clientVersion: "0.12.1",
14
+ sseWatchdog: true,
15
+ machineName: null,
16
+ },
17
+ ],
18
+ sessions: [
19
+ {
20
+ clientId: "client-123",
21
+ interfaceId: "chrome-extension",
22
+ startedAt: Date.parse("2026-09-16T12:00:00.000Z"),
23
+ endedAt: null,
24
+ durationMs: 60_000,
25
+ flapCount: 1,
26
+ openReason: "sse_open" as const,
27
+ closeReason: null,
28
+ clientVersion: "0.12.1",
29
+ sseWatchdog: true,
30
+ machineName: null,
31
+ connectionIds: ["conn-1", "conn-2"],
32
+ },
33
+ ],
34
+ };
35
+
36
+ const listHistory = mock((params: Record<string, unknown>) => {
37
+ if (params.actorPrincipalId === "user-123") {
38
+ return historyResult;
39
+ }
40
+ return { events: [], sessions: [] };
41
+ });
42
+
43
+ mock.module("../../../config/env.js", () => ({
44
+ isHttpAuthDisabled: () => false,
45
+ hasUngatedHttpAuthDisabled: () => false,
46
+ }));
47
+
48
+ mock.module("../../../persistence/client-connection-events-store.js", () => ({
49
+ listClientConnectionHistory: listHistory,
50
+ }));
51
+
52
+ import { ROUTES } from "../client-routes.js";
53
+ import { BadRequestError } from "../errors.js";
54
+ import type { RouteDefinition } from "../types.js";
55
+
56
+ afterAll(() => {
57
+ mock.restore();
58
+ });
59
+
60
+ function findHandler(operationId: string): RouteDefinition["handler"] {
61
+ const route = ROUTES.find((r) => r.operationId === operationId);
62
+ if (!route) {
63
+ throw new Error(`Route ${operationId} not found`);
64
+ }
65
+ return route.handler;
66
+ }
67
+
68
+ describe("list_client_history", () => {
69
+ test("returns ISO timestamps for the calling actor", () => {
70
+ const handler = findHandler("list_client_history");
71
+ const result = handler({
72
+ headers: { "x-vellum-actor-principal-id": "user-123" },
73
+ queryParams: { clientId: "client-123" },
74
+ }) as {
75
+ sessions: Array<{ startedAt: string; endedAt: string | null }>;
76
+ events: Array<{ occurredAt: string }>;
77
+ };
78
+
79
+ expect(listHistory).toHaveBeenCalled();
80
+ expect(result.sessions[0].startedAt).toBe("2026-09-16T12:00:00.000Z");
81
+ expect(result.sessions[0].endedAt).toBeNull();
82
+ expect(result.events[0].occurredAt).toBe("2026-09-16T12:00:00.000Z");
83
+ });
84
+
85
+ test("hides other actors' history", () => {
86
+ const handler = findHandler("list_client_history");
87
+ const result = handler({
88
+ headers: { "x-vellum-actor-principal-id": "user-other" },
89
+ }) as { sessions: unknown[]; events: unknown[] };
90
+
91
+ expect(result.sessions).toEqual([]);
92
+ expect(result.events).toEqual([]);
93
+ });
94
+
95
+ test("rejects an invalid since value", () => {
96
+ const handler = findHandler("list_client_history");
97
+ expect(() =>
98
+ handler({
99
+ headers: { "x-vellum-actor-principal-id": "user-123" },
100
+ queryParams: { since: "not-a-date" },
101
+ }),
102
+ ).toThrow(BadRequestError);
103
+ });
104
+ });
@@ -0,0 +1,97 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import { setConfig } from "../../../__tests__/helpers/set-config.js";
4
+ import { BadRequestError } from "../errors.js";
5
+
6
+ let lastGenerateProvider: unknown = null;
7
+ let lastGenerateRequest: Record<string, unknown> | null = null;
8
+
9
+ mock.module("../../../media/image-service.js", () => ({
10
+ generateImage: async (
11
+ provider: unknown,
12
+ _credentials: unknown,
13
+ request: Record<string, unknown>,
14
+ ) => {
15
+ lastGenerateProvider = provider;
16
+ lastGenerateRequest = request;
17
+ return {
18
+ images: [{ mimeType: "image/png", dataBase64: "generated" }],
19
+ resolvedModel: request.model,
20
+ };
21
+ },
22
+ mapImageGenError: () => "mapped",
23
+ }));
24
+
25
+ mock.module("../../../media/image-credentials.js", () => ({
26
+ resolveImageGenRouting: (
27
+ svc: { provider: string },
28
+ _model?: unknown,
29
+ ) => ({
30
+ backendProvider: svc.provider === "openrouter" ? "openrouter" : "gemini",
31
+ managed: false,
32
+ }),
33
+ resolveImageGenCredentials: async () => ({
34
+ credentials: { type: "direct", apiKey: "test-key" },
35
+ }),
36
+ }));
37
+
38
+ const { ROUTES } = await import("../image-generation-routes.js");
39
+ const route = ROUTES[0];
40
+
41
+ beforeEach(() => {
42
+ lastGenerateProvider = null;
43
+ lastGenerateRequest = null;
44
+ setConfig("services", {
45
+ "image-generation": {
46
+ provider: "openrouter",
47
+ model: "google/gemini-3.1-flash-image-preview",
48
+ },
49
+ });
50
+ });
51
+
52
+ describe("image-generation generate route", () => {
53
+ test("mentions OpenRouter in the route description", () => {
54
+ expect(route.description).toContain("OpenRouter");
55
+ });
56
+
57
+ test("accepts an arbitrary OpenRouter slug", async () => {
58
+ const result = await route.handler({
59
+ body: {
60
+ prompt: "a nebula",
61
+ model: "black-forest-labs/flux",
62
+ },
63
+ });
64
+
65
+ expect(lastGenerateProvider).toBe("openrouter");
66
+ expect(lastGenerateRequest?.model).toBe("black-forest-labs/flux");
67
+ expect(result).toMatchObject({
68
+ resolvedModel: "black-forest-labs/flux",
69
+ });
70
+ });
71
+
72
+ test("qualifies a built-in alias for OpenRouter", async () => {
73
+ await route.handler({
74
+ body: { prompt: "a robot", model: "fast" },
75
+ });
76
+
77
+ expect(lastGenerateRequest?.model).toBe(
78
+ "google/gemini-3.1-flash-image-preview",
79
+ );
80
+ });
81
+
82
+ test("rejects an unknown model when the provider is not OpenRouter", async () => {
83
+ setConfig("services", {
84
+ "image-generation": {
85
+ provider: "gemini",
86
+ model: "gemini-3.1-flash-image-preview",
87
+ },
88
+ });
89
+
90
+ await expect(
91
+ route.handler({
92
+ body: { prompt: "a cat", model: "black-forest-labs/flux" },
93
+ }),
94
+ ).rejects.toThrow(BadRequestError);
95
+ expect(lastGenerateProvider).toBeNull();
96
+ });
97
+ });
@@ -927,7 +927,7 @@ describe("PUT inference/active-profile validation", () => {
927
927
  profiles: {
928
928
  jev: {
929
929
  source: "user",
930
- provider: "jev",
930
+ provider: "typesafe",
931
931
  model: "jev-latest",
932
932
  status: "active",
933
933
  },
@@ -334,12 +334,35 @@ describe("roadmap writes", () => {
334
334
  });
335
335
  });
336
336
 
337
- test("update can clear every tag", async () => {
337
+ test("update refuses the staff-only fields before calling out", async () => {
338
338
  stubFetch(UPSTREAM_ITEM);
339
339
 
340
- await update({ pathParams: { slug: "dark-mode" }, body: { tags: [] } });
340
+ await expect(
341
+ update({
342
+ pathParams: { slug: "dark-mode" },
343
+ body: { title: "Dark mode", status: "planned" },
344
+ }),
345
+ ).rejects.toThrow(
346
+ "An assistant can change only an item's title and description; status is set by Vellum staff.",
347
+ );
348
+ await expect(
349
+ update({ pathParams: { slug: "dark-mode" }, body: { tags: [] } }),
350
+ ).rejects.toThrow("tags is set by Vellum staff");
351
+ expect(calls).toHaveLength(0);
352
+ });
341
353
 
342
- expect(calls[0].body).toEqual({ tags: [] });
354
+ test("update names an unknown field as unknown, not as staff-only", async () => {
355
+ stubFetch(UPSTREAM_ITEM);
356
+
357
+ await expect(
358
+ update({
359
+ pathParams: { slug: "dark-mode" },
360
+ body: { titel: "Dark mode", status: "planned" },
361
+ }),
362
+ ).rejects.toThrow(
363
+ "An assistant can change only an item's title and description; status is set by Vellum staff; titel is not a roadmap update field.",
364
+ );
365
+ expect(calls).toHaveLength(0);
343
366
  });
344
367
 
345
368
  test("create rejects a blank title before calling out", async () => {
@@ -365,11 +388,11 @@ describe("roadmap writes", () => {
365
388
 
366
389
  await update({
367
390
  pathParams: { slug: "dark-mode" },
368
- body: { status: "planned" },
391
+ body: { description: "Follow the OS setting" },
369
392
  });
370
393
 
371
394
  expect(calls[0].method).toBe("PATCH");
372
- expect(calls[0].body).toEqual({ status: "planned" });
395
+ expect(calls[0].body).toEqual({ description: "Follow the OS setting" });
373
396
  });
374
397
 
375
398
  test("delete reports the removed slug", async () => {
@@ -403,7 +426,7 @@ describe("roadmap writes", () => {
403
426
  const writes = [
404
427
  () => create({ body: { title: "Add dark mode" } }),
405
428
  () =>
406
- update({ pathParams: { slug: "dark-mode" }, body: { status: "open" } }),
429
+ update({ pathParams: { slug: "dark-mode" }, body: { title: "Dark" } }),
407
430
  () => remove({ pathParams: { slug: "dark-mode" } }),
408
431
  () => upvote({ pathParams: { slug: "dark-mode" } }),
409
432
  () => unvote({ pathParams: { slug: "dark-mode" } }),
@@ -1,3 +1,4 @@
1
+ import type { Conversation } from "../../daemon/conversation.js";
1
2
  import { resolveTurnClientOs } from "../../daemon/conversation-client-surface.js";
2
3
  import { findConversation } from "../../daemon/conversation-registry.js";
3
4
  import type { ToolContext } from "../../tools/types.js";
@@ -13,6 +14,22 @@ export async function resolveBrowserContext(
13
14
  headers: Record<string, string>,
14
15
  abortSignal?: AbortSignal,
15
16
  ): Promise<ToolContext> {
17
+ return (
18
+ await resolveBrowserExecutionContext(
19
+ conversationId,
20
+ sessionId,
21
+ headers,
22
+ abortSignal,
23
+ )
24
+ ).context;
25
+ }
26
+
27
+ export async function resolveBrowserExecutionContext(
28
+ conversationId: string | undefined,
29
+ sessionId: string,
30
+ headers: Record<string, string>,
31
+ abortSignal?: AbortSignal,
32
+ ): Promise<{ context: ToolContext; conversation?: Conversation }> {
16
33
  const conversation = conversationId
17
34
  ? findConversation(conversationId)
18
35
  : undefined;
@@ -24,16 +41,19 @@ export async function resolveBrowserContext(
24
41
  (signal): signal is AbortSignal => signal !== undefined,
25
42
  );
26
43
  return {
27
- workingDir: process.cwd(),
28
- conversationId: conversation
29
- ? conversationId!
30
- : browserCliConversationKey(sessionId),
31
- trustClass: conversation?.trustContext?.trustClass ?? "unknown",
32
- transportInterface: conversation?.transportInterface,
33
- clientOs: conversation
34
- ? resolveTurnClientOs(conversation).clientOs
35
- : undefined,
36
- sourceActorPrincipalId: actor,
37
- signal: signals.length ? AbortSignal.any(signals) : undefined,
44
+ context: {
45
+ workingDir: process.cwd(),
46
+ conversationId: conversation
47
+ ? conversationId!
48
+ : browserCliConversationKey(sessionId),
49
+ trustClass: conversation?.trustContext?.trustClass ?? "unknown",
50
+ transportInterface: conversation?.transportInterface,
51
+ clientOs: conversation
52
+ ? resolveTurnClientOs(conversation).clientOs
53
+ : undefined,
54
+ sourceActorPrincipalId: actor,
55
+ signal: signals.length ? AbortSignal.any(signals) : undefined,
56
+ },
57
+ ...(conversation ? { conversation } : {}),
38
58
  };
39
59
  }
@@ -11,16 +11,20 @@
11
11
 
12
12
  import { z } from "zod";
13
13
 
14
- import { executeBrowserOperation } from "../../browser/operations.js";
14
+ import {
15
+ browserOperationLifecycle,
16
+ executeBrowserOperation,
17
+ } from "../../browser/operations.js";
15
18
  import {
16
19
  BROWSER_OPERATIONS,
17
20
  type BrowserOperation,
18
21
  } from "../../browser/types.js";
19
22
  import { shouldUseVirtualDesktopBrowser } from "../../browser/virtual-desktop-target.js";
23
+ import { bestEffortModeSessionTracking } from "../../daemon/mode-session-tracking.js";
20
24
  import { executeDesktopBrowserOperation } from "../../desktop/desktop-browser-operations.js";
21
25
  import type { ContentBlock } from "../../providers/types.js";
22
26
  import { LOCAL_PRINCIPALS } from "../auth/route-policy.js";
23
- import { resolveBrowserContext } from "./browser-context.js";
27
+ import { resolveBrowserExecutionContext } from "./browser-context.js";
24
28
  export { browserCliConversationKey } from "./browser-context.js";
25
29
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
26
30
 
@@ -68,16 +72,51 @@ async function handleBrowserExecute({
68
72
  const { operation, input, sessionId, conversationId, desktop } =
69
73
  BrowserExecuteParams.parse(body);
70
74
 
71
- const context = await resolveBrowserContext(
75
+ const resolved = await resolveBrowserExecutionContext(
72
76
  conversationId,
73
77
  sessionId,
74
78
  headers,
75
79
  abortSignal,
76
80
  );
81
+ const { context, conversation } = resolved;
82
+ const typedOperation = operation as BrowserOperation;
83
+ const operationToken = conversation?.currentRequestId
84
+ ? bestEffortModeSessionTracking("browser admission", () =>
85
+ conversation.browserModeSessions.beginOperation({
86
+ turnId: conversation.currentRequestId!,
87
+ lifecycle: browserOperationLifecycle(typedOperation),
88
+ at: Date.now(),
89
+ }),
90
+ )
91
+ : undefined;
77
92
  const execute = shouldUseVirtualDesktopBrowser(desktop, input, context)
78
93
  ? executeDesktopBrowserOperation
79
94
  : executeBrowserOperation;
80
- const result = await execute(operation as BrowserOperation, input, context);
95
+ const finishOperationTracking = (isError: boolean) => {
96
+ if (!operationToken) {
97
+ return;
98
+ }
99
+ bestEffortModeSessionTracking("browser completion", () =>
100
+ conversation?.browserModeSessions.finishOperation(operationToken, {
101
+ at: Date.now(),
102
+ isError,
103
+ cancelled: context.signal?.aborted === true,
104
+ ...(typedOperation === "close"
105
+ ? { terminalReason: "browser_closed" as const }
106
+ : typedOperation === "detach"
107
+ ? { terminalReason: "browser_detached" as const }
108
+ : {}),
109
+ }),
110
+ );
111
+ };
112
+ let result;
113
+ try {
114
+ result = await execute(typedOperation, input, context);
115
+ } catch (error) {
116
+ finishOperationTracking(true);
117
+ throw error;
118
+ }
119
+ finishOperationTracking(result.isError);
81
120
 
82
121
  const screenshots = extractScreenshots(result.contentBlocks);
83
122
 
@@ -2,9 +2,10 @@
2
2
  * Route handler for the POST /v1/btw SSE-streaming side-chain endpoint.
3
3
  *
4
4
  * Runs an ephemeral LLM call that reuses the conversation's provider, tool
5
- * definitions, and message history for prompt-cache efficiency. Uses the
6
- * conversation's system prompt when a conversation-specific override is active;
7
- * otherwise builds a fresh prompt excluding BOOTSTRAP.md so first-run
5
+ * definitions, and message history for prompt-cache efficiency; the
6
+ * empty-state greeting targets no real conversation and sends no tools. Uses
7
+ * the conversation's system prompt when a conversation-specific override is
8
+ * active; otherwise builds a fresh prompt excluding BOOTSTRAP.md so first-run
8
9
  * onboarding instructions don't leak into cosmetic UI calls like identity
9
10
  * intro generation. The response is streamed as SSE events (`btw_text_delta`,
10
11
  * `btw_complete`, `btw_error`).
@@ -145,7 +146,11 @@ async function handleBtw({
145
146
  const result = await runBtwSidechain({
146
147
  content: effectiveContent,
147
148
  conversation,
148
- tools: getAllToolDefinitions(),
149
+ // The side-chain forces `tool_choice: none`, so tool definitions
150
+ // only earn their tokens as a shared cache prefix with a real
151
+ // conversation. The greeting runs against an ephemeral one with its
152
+ // own system prompt, so it shares nothing and sends no tools.
153
+ tools: isGreeting ? [] : getAllToolDefinitions(),
149
154
  signal: abortSignal,
150
155
  ...(isGreeting ? { callSite: "emptyStateGreeting" as const } : {}),
151
156
  onEvent: (event) => {