@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.12.3-staging.1",
3
+ "version": "0.12.3-staging.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -14,26 +14,18 @@ import { registerBrowserCommand } from "../src/cli/commands/browser.js";
14
14
  import { DesktopAutomationLease } from "../src/desktop/desktop-automation-lease.js";
15
15
  import {
16
16
  desktopChromePath,
17
- desktopDependencyInstaller,
17
+ desktopDependencies,
18
18
  } from "../src/desktop/desktop-dependencies.js";
19
19
  import { DesktopSessionManager } from "../src/desktop/desktop-session-manager.js";
20
20
  import { getAssistantSocketPath } from "../src/ipc/socket-path.js";
21
21
 
22
- if (
23
- process.platform !== "linux" ||
24
- !process.argv[2] ||
25
- !process.env.ASSISTANT_IPC_SOCKET_DIR
26
- ) {
22
+ if (process.platform !== "linux" || !process.env.ASSISTANT_IPC_SOCKET_DIR) {
27
23
  throw new Error(
28
- "Run in disposable Linux with desktop packages, a temporary ASSISTANT_IPC_SOCKET_DIR and a Chrome executable argument or --install for a cold install.",
24
+ "Run in a disposable Linux assistant image with a temporary ASSISTANT_IPC_SOCKET_DIR.",
29
25
  );
30
26
  }
31
- const coldInstall = process.argv[2] === "--install";
32
- const executable = coldInstall ? desktopChromePath() : process.argv[2];
33
- if (coldInstall) {
34
- assert.equal(desktopDependencyInstaller.getStatus().state, "required");
35
- assert.equal(Bun.which("Xtigervnc"), null);
36
- }
27
+ const executable = desktopChromePath();
28
+ assert.equal(desktopDependencies.getStatus().state, "ready");
37
29
  const directory = await mkdtemp(join(tmpdir(), "desktop-browser-cli-"));
38
30
  const manager = new DesktopSessionManager({
39
31
  resolveChromePath: async () => executable,
@@ -44,9 +36,7 @@ const manager = new DesktopSessionManager({
44
36
  const control = new DesktopAutomationLease({
45
37
  notify: async () => {},
46
38
  enabled: () => true,
47
- ready: () =>
48
- !coldInstall || desktopDependencyInstaller.getStatus().state === "ready",
49
- ensureReady: (signal) => desktopDependencyInstaller.ensureReady(signal),
39
+ ready: () => desktopDependencies.getStatus().state === "ready",
50
40
  manager: () => manager,
51
41
  });
52
42
  const context = {
@@ -182,16 +172,10 @@ try {
182
172
  "--allow-private-network",
183
173
  );
184
174
  assert.equal(navigations, 1);
185
- if (coldInstall) {
186
- assert.equal(desktopDependencyInstaller.getStatus().state, "ready");
187
- assert(
188
- setupNotifications >= 3,
189
- "Installation progress must reach the viewer",
190
- );
191
- console.error(
192
- "PASS: one CLI navigate installed desktop and Chrome from scratch, then loaded the requested page once",
193
- );
194
- }
175
+ assert.equal(setupNotifications, 0);
176
+ console.error(
177
+ "PASS: baked desktop opened the requested page without installation",
178
+ );
195
179
  const snapshot = await cli("snapshot");
196
180
  await cli(
197
181
  "type",
@@ -91,15 +91,14 @@ try {
91
91
  `<bindings xmlns="http://openbox.org/3.4/rc">${mouse}${keyboard}</bindings>`,
92
92
  );
93
93
  const configDir = join(directory, "managed & theme");
94
- const config = writeDesktopWindowTheme(configDir, home, "#53b7aa");
94
+ const config = writeDesktopWindowTheme(configDir, home);
95
95
  const expanded = run("xmllint", "--xinclude", config);
96
96
  assert(
97
97
  expanded.includes("NextWindow") && expanded.includes("Move"),
98
98
  "Relative includes must retain the actual keyboard and mouse bindings",
99
99
  );
100
100
  assert.equal(await readFile(sourcePath, "utf8"), source);
101
- writeDesktopWindowTheme(configDir, home, "#bc7799");
102
- writeDesktopWindowTheme(configDir, home, "#53b7aa");
101
+ writeDesktopWindowTheme(configDir, home);
103
102
  assert.equal(await readFile(sourcePath, "utf8"), source);
104
103
  const blocked = join(directory, "blocked");
105
104
  await writeFile(blocked, "not a directory");
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { beforeEach, describe, expect, test } from "bun:test";
8
8
 
9
- import type { AgentEvent } from "../agent/loop.js";
9
+ import type { AgentEvent, PreparedModelCall } from "../agent/loop.js";
10
10
  import { AgentLoop } from "../agent/loop.js";
11
11
  import type {
12
12
  PostModelCallContext,
@@ -18,12 +18,14 @@ import type {
18
18
  ContentBlock,
19
19
  Message,
20
20
  ProviderResponse,
21
+ ToolDefinition,
21
22
  } from "../providers/types.js";
22
23
  import {
23
24
  createMockProvider,
24
25
  textResponse,
25
26
  toolUseResponse,
26
27
  } from "./helpers/mock-provider.js";
28
+ import { setConfig } from "./helpers/set-config.js";
27
29
 
28
30
  const userMessage: Message = {
29
31
  role: "user",
@@ -91,6 +93,7 @@ function registerOutputHookPlugin(hooks: {
91
93
  describe("agent loop output hooks", () => {
92
94
  beforeEach(() => {
93
95
  resetPluginRegistryAndRegisterDefaults();
96
+ setConfig("llm", {});
94
97
  });
95
98
 
96
99
  test("post-model-call transforms the persisted message content", async () => {
@@ -403,6 +406,152 @@ describe("agent loop output hooks", () => {
403
406
  expect(calls[0].options?.config?.overrideProfile).toBe("fast-profile");
404
407
  });
405
408
 
409
+ test("prepared model call carries the post-hook route and wire tool surface", async () => {
410
+ registerOutputHookPlugin({
411
+ preModelCall: (ctx) => {
412
+ ctx.systemPrompt = `${ctx.systemPrompt ?? ""} [EDITED]`;
413
+ ctx.modelProfile = "fast-profile";
414
+ },
415
+ });
416
+ const tool: ToolDefinition = {
417
+ name: "dynamic_tool",
418
+ description: "Dynamic",
419
+ input_schema: { type: "object" },
420
+ };
421
+ const { provider, calls } = createMockProvider([textResponse("hi")]);
422
+ Object.assign(provider, { supportsNativeWebSearch: true });
423
+ const loop = new AgentLoop({
424
+ provider,
425
+ systemPrompt: "base prompt",
426
+ conversationId: "test-conversation",
427
+ config: { enableNativeWebSearch: true },
428
+ resolveTools: () => [tool],
429
+ });
430
+ let prepared: PreparedModelCall | undefined;
431
+
432
+ await loop.run({
433
+ requestId: "test-request",
434
+ messages: [userMessage],
435
+ onEvent: collect([]),
436
+ callSite: "mainAgent",
437
+ overrideProfile: "conversation-profile",
438
+ forceOverrideProfile: true,
439
+ onModelCallPrepared: (value) => {
440
+ prepared = value;
441
+ },
442
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
443
+ });
444
+
445
+ expect(prepared).toMatchObject({
446
+ callSite: "mainAgent",
447
+ overrideProfile: "fast-profile",
448
+ forceOverrideProfile: true,
449
+ systemPrompt: "base prompt [EDITED]",
450
+ });
451
+ expect(prepared?.tools.map((item) => item.name)).toEqual([
452
+ "dynamic_tool",
453
+ "web_search",
454
+ ]);
455
+ expect(calls[0].tools).toEqual(prepared?.tools);
456
+ });
457
+
458
+ test("prepared model call carries the finalized cache policy", async () => {
459
+ setConfig("llm", {
460
+ callSites: { mainAgent: { disableCache: true } },
461
+ });
462
+ const { provider } = createMockProvider([textResponse("hi")]);
463
+ const loop = new AgentLoop({
464
+ provider,
465
+ systemPrompt: "base prompt",
466
+ conversationId: "test-conversation",
467
+ });
468
+ let prepared: PreparedModelCall | undefined;
469
+
470
+ await loop.run({
471
+ requestId: "test-request",
472
+ messages: [userMessage],
473
+ onEvent: collect([]),
474
+ callSite: "mainAgent",
475
+ onModelCallPrepared: (value) => {
476
+ prepared = value;
477
+ },
478
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
479
+ });
480
+
481
+ expect(prepared?.disableCache).toBe(true);
482
+ });
483
+
484
+ test("prepared model call preserves explicit system-prompt removal", async () => {
485
+ registerOutputHookPlugin({
486
+ preModelCall: (ctx) => {
487
+ ctx.systemPrompt = null;
488
+ },
489
+ });
490
+ const { provider, calls } = createMockProvider([textResponse("hi")]);
491
+ const loop = new AgentLoop({
492
+ provider,
493
+ systemPrompt: "base prompt",
494
+ conversationId: "test-conversation",
495
+ });
496
+ let prepared: PreparedModelCall | undefined;
497
+
498
+ await loop.run({
499
+ requestId: "test-request",
500
+ messages: [userMessage],
501
+ onEvent: collect([]),
502
+ callSite: "mainAgent",
503
+ onModelCallPrepared: (value) => {
504
+ prepared = value;
505
+ },
506
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
507
+ });
508
+
509
+ expect(prepared?.systemPrompt).toBeNull();
510
+ expect(calls[0].options?.systemPrompt).toBeUndefined();
511
+ });
512
+
513
+ test("inference routing preserves the semantic call site for hooks and events", async () => {
514
+ const preModelCallSites: Array<string | null> = [];
515
+ const postModelCallSites: Array<string | null> = [];
516
+ registerOutputHookPlugin({
517
+ preModelCall: (ctx) => {
518
+ preModelCallSites.push(ctx.callSite);
519
+ },
520
+ postModelCall: (ctx) => {
521
+ postModelCallSites.push(ctx.callSite);
522
+ },
523
+ });
524
+ const { provider, calls } = createMockProvider([textResponse("hi")]);
525
+ const loop = new AgentLoop({
526
+ provider,
527
+ systemPrompt: "system",
528
+ conversationId: "test-conversation",
529
+ });
530
+ const events: AgentEvent[] = [];
531
+ let prepared: PreparedModelCall | undefined;
532
+
533
+ await loop.run({
534
+ requestId: "test-request",
535
+ messages: [userMessage],
536
+ onEvent: collect(events),
537
+ callSite: "callAgent",
538
+ inferenceCallSite: "mainAgent",
539
+ onModelCallPrepared: (value) => {
540
+ prepared = value;
541
+ },
542
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
543
+ });
544
+
545
+ expect(calls[0].options?.config?.callSite).toBe("mainAgent");
546
+ expect(prepared?.callSite).toBe("mainAgent");
547
+ expect(preModelCallSites).toEqual(["callAgent"]);
548
+ expect(postModelCallSites).toEqual(["callAgent"]);
549
+ expect(events).toContainEqual({
550
+ type: "llm_call_started",
551
+ callSite: "callAgent",
552
+ });
553
+ });
554
+
406
555
  test("pre-model-call seeds modelProfile from the resolved override and clearing it drops the override", async () => {
407
556
  // GIVEN a hook that observes the seeded override and then clears it
408
557
  let seeded: string | null | undefined;
@@ -3,17 +3,19 @@
3
3
  *
4
4
  * When the provider call throws (provider rejected the request before
5
5
  * returning a usable response), the loop must emit a `provider_error` event
6
- * carrying the loop-level raw request and the thrown error so downstream
7
- * consumers can persist an `llm_request_logs` row. Without this, rejected
8
- * calls leave nothing in the LLM inspector — only a pino log line.
6
+ * carrying the inspectable wire request (when the throw attached one) and
7
+ * the thrown error so downstream consumers can persist an `llm_request_logs`
8
+ * row. Without this, rejected calls leave nothing in the LLM inspector —
9
+ * only a pino log line.
9
10
  *
10
11
  * Coverage:
11
- * - Emits `provider_error` with `rawRequest`, `error`, and `actualProvider`
12
- * when the provider throws a `ProviderError`.
13
- * - `rawRequest` carries the message history, tools, and system prompt the
14
- * loop attempted to send — so the row replays/debugs cleanly.
15
- * - `actualProvider` echoes `ProviderError.provider` when available, falling
16
- * back to `provider.name` for non-ProviderError throws.
12
+ * - Emits `provider_error` with `error` and `actualProvider` when the
13
+ * provider throws a `ProviderError`.
14
+ * - `rawRequest` is `ProviderError.rawRequest` when present, and stays
15
+ * unset when the throw did not attach a wire payload. The loop does
16
+ * not invent a messages/tools/systemPrompt snapshot.
17
+ * - `actualProvider` echoes `ProviderError.provider` when available, even
18
+ * if the wrapping `provider.name` is a different default transport.
17
19
  * - The error is still re-thrown internally (the existing `error` event
18
20
  * still fires after the new `provider_error` event), preserving the
19
21
  * outer-catch behavior (abort/Sentry/break).
@@ -73,7 +75,7 @@ function makeThrowingProvider(
73
75
  }
74
76
 
75
77
  describe("AgentLoop provider_error event emission", () => {
76
- test("emits provider_error with loop-level rawRequest when provider throws ProviderError", async () => {
78
+ test("emits provider_error without inventing a rawRequest snapshot", async () => {
77
79
  const thrown = new ProviderError(
78
80
  "Anthropic API error (429): rate limited",
79
81
  "anthropic",
@@ -107,16 +109,81 @@ describe("AgentLoop provider_error event emission", () => {
107
109
  }
108
110
  expect(providerErrorEvent.error).toBe(thrown);
109
111
  expect(providerErrorEvent.actualProvider).toBe("anthropic");
112
+ expect(providerErrorEvent.rawRequest).toBeUndefined();
113
+ });
114
+
115
+ test("forwards ProviderError.rawRequest on the provider_error event", async () => {
116
+ const wirePayload = {
117
+ model: "qwen/qwen3-8b",
118
+ directions: { serious: 0.8 },
119
+ };
120
+ const thrown = new ProviderError(
121
+ "Vellum API error (400): directions are not loaded",
122
+ "vellum",
123
+ 400,
124
+ {
125
+ rawRequest: wirePayload,
126
+ rawBody: '{"detail":"directions are not loaded"}',
127
+ },
128
+ );
129
+ const { provider } = makeThrowingProvider("fireworks", () => thrown);
130
+
131
+ const events: AgentEvent[] = [];
132
+ const loop = new AgentLoop({
133
+ provider: provider,
134
+ systemPrompt: "system",
135
+ conversationId: "test-conversation",
136
+ });
137
+
138
+ await loop.run({
139
+ requestId: "test-request",
140
+ messages: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
141
+ onEvent: (e) => {
142
+ events.push(e);
143
+ },
144
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
145
+ });
146
+
147
+ const providerErrorEvent = events.find((e) => e.type === "provider_error");
148
+ expect(providerErrorEvent).toBeDefined();
149
+ if (providerErrorEvent?.type !== "provider_error") {
150
+ throw new Error("type narrowing");
151
+ }
152
+ expect(providerErrorEvent.actualProvider).toBe("vellum");
153
+ expect(providerErrorEvent.rawRequest).toEqual(wirePayload);
154
+ });
155
+
156
+ test("actualProvider uses ProviderError.provider, not the wrapper name", async () => {
157
+ const thrown = new ProviderError(
158
+ "Vellum API error (400): directions are not loaded",
159
+ "vellum",
160
+ 400,
161
+ );
162
+ const { provider } = makeThrowingProvider("fireworks", () => thrown);
163
+
164
+ const events: AgentEvent[] = [];
165
+ const loop = new AgentLoop({
166
+ provider: provider,
167
+ systemPrompt: "system",
168
+ conversationId: "test-conversation",
169
+ });
170
+
171
+ await loop.run({
172
+ requestId: "test-request",
173
+ messages: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
174
+ onEvent: (e) => {
175
+ events.push(e);
176
+ },
177
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
178
+ });
110
179
 
111
- // rawRequest should carry the loop-level abstract shape: messages,
112
- // tools, systemPrompt, and the provider name we tried to dispatch
113
- // through. The provider-specific shape (e.g. Gemini's `contents`) is
114
- // never built because the provider threw before returning it.
115
- const raw = providerErrorEvent.rawRequest as Record<string, unknown>;
116
- expect(raw.provider).toBe("anthropic");
117
- expect(raw.systemPrompt).toBe("you are a helpful assistant");
118
- expect(Array.isArray(raw.messages)).toBe(true);
119
- expect((raw.messages as Message[])[0].role).toBe("user");
180
+ const providerErrorEvent = events.find((e) => e.type === "provider_error");
181
+ expect(providerErrorEvent).toBeDefined();
182
+ if (providerErrorEvent?.type !== "provider_error") {
183
+ throw new Error("type narrowing");
184
+ }
185
+ expect(providerErrorEvent.actualProvider).toBe("vellum");
186
+ expect(providerErrorEvent.rawRequest).toBeUndefined();
120
187
  });
121
188
 
122
189
  test("error event still fires after provider_error (outer catch behavior unchanged)", async () => {
@@ -185,6 +252,7 @@ describe("AgentLoop provider_error event emission", () => {
185
252
  // `provider` column populated even for surprise errors.
186
253
  expect(providerErrorEvent.actualProvider).toBe("openai");
187
254
  expect(providerErrorEvent.error).toBe(thrown);
255
+ expect(providerErrorEvent.rawRequest).toBeUndefined();
188
256
  });
189
257
 
190
258
  test("does NOT emit provider_error on user-aborted runs", async () => {
@@ -44,6 +44,7 @@ mock.module("../persistence/llm-request-log-store.js", () => ({
44
44
  }));
45
45
 
46
46
  // ── Imports (after mocks) ─────────────────────────────────────────────────────
47
+ import type { ToolActivityMetadata } from "../api/events/tool-result.js";
47
48
  import type {
48
49
  EventHandlerDeps,
49
50
  EventHandlerState,
@@ -52,7 +53,7 @@ import {
52
53
  createEventHandlerState,
53
54
  handleToolResult,
54
55
  } from "../daemon/conversation-agent-loop-handlers.js";
55
- import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
56
+ import { mockUnownedModeSessions } from "./helpers/mock-conversation.js";
56
57
 
57
58
  // ── Helpers ───────────────────────────────────────────────────────────────────
58
59
 
@@ -65,6 +66,7 @@ function makeDeps(): EventHandlerDeps {
65
66
  emitActivityState: () => {},
66
67
  markWorkspaceTopLevelDirty: () => {},
67
68
  currentTurnSurfaces: [],
69
+ modeSessions: mockUnownedModeSessions(),
68
70
  } as unknown as EventHandlerDeps["ctx"],
69
71
  onEvent: () => {},
70
72
  reqId: "test-req",
@@ -47,6 +47,7 @@ import {
47
47
  createEventHandlerState,
48
48
  handleToolResult,
49
49
  } from "../daemon/conversation-agent-loop-handlers.js";
50
+ import { mockUnownedModeSessions } from "./helpers/mock-conversation.js";
50
51
 
51
52
  // ── Helpers ───────────────────────────────────────────────────────────────────
52
53
 
@@ -59,6 +60,7 @@ function makeDeps(): EventHandlerDeps {
59
60
  emitActivityState: () => {},
60
61
  markWorkspaceTopLevelDirty: () => {},
61
62
  currentTurnSurfaces: [],
63
+ modeSessions: mockUnownedModeSessions(),
62
64
  } as unknown as EventHandlerDeps["ctx"],
63
65
  onEvent: () => {},
64
66
  reqId: "test-req",
@@ -46,6 +46,7 @@ import {
46
46
  createEventHandlerState,
47
47
  handleToolResult,
48
48
  } from "../daemon/conversation-agent-loop-handlers.js";
49
+ import { mockUnownedModeSessions } from "./helpers/mock-conversation.js";
49
50
 
50
51
  // ── Helpers ───────────────────────────────────────────────────────────────────
51
52
 
@@ -60,6 +61,7 @@ function makeDeps(): EventHandlerDeps {
60
61
  emitActivityState: () => {},
61
62
  markWorkspaceTopLevelDirty: () => {},
62
63
  currentTurnSurfaces: [],
64
+ modeSessions: mockUnownedModeSessions(),
63
65
  } as unknown as EventHandlerDeps["ctx"],
64
66
  onEvent: (event: unknown) => {
65
67
  emitted.push(event as Record<string, unknown>);
@@ -103,6 +103,26 @@ describe("retagDelegateError", () => {
103
103
  expect(err.apiErrorCode).toBe("restricted");
104
104
  expect(err.apiErrorParam).toBe("model");
105
105
  expect(err.rawBody).toBe('{"type":"error"}');
106
+ expect(err.rawRequest).toBeUndefined();
107
+ });
108
+
109
+ test("carries rawRequest across the re-tag", () => {
110
+ const rawRequest = { model: "anthropic/claude-opus-4.6", messages: [] };
111
+ const inner = new ProviderError("rate limited", "anthropic", 429, {
112
+ rawRequest,
113
+ });
114
+
115
+ let caught: unknown;
116
+ try {
117
+ retagDelegateError(inner, providerName);
118
+ } catch (error) {
119
+ caught = error;
120
+ }
121
+
122
+ expect(caught).toBeInstanceOf(ProviderError);
123
+ const err = caught as ProviderError;
124
+ expect(err.provider).toBe(providerName);
125
+ expect(err.rawRequest).toEqual(rawRequest);
106
126
  });
107
127
 
108
128
  test("re-tags a delegate ContextOverflowError, preserving token counts", () => {
@@ -110,6 +130,7 @@ describe("retagDelegateError", () => {
110
130
  actualTokens: 250_000,
111
131
  maxTokens: 200_000,
112
132
  statusCode: 400,
133
+ rawRequest: { model: "anthropic/claude-opus-4.6" },
113
134
  });
114
135
 
115
136
  let caught: unknown;
@@ -126,6 +147,7 @@ describe("retagDelegateError", () => {
126
147
  expect(err.maxTokens).toBe(200_000);
127
148
  expect(err.statusCode).toBe(400);
128
149
  expect(err.cause).toBe(inner);
150
+ expect(err.rawRequest).toEqual({ model: "anthropic/claude-opus-4.6" });
129
151
  });
130
152
 
131
153
  test("rethrows an already-tagged error unchanged", () => {
@@ -17,21 +17,10 @@ const ALL_SCENARIOS: ApprovalMessageScenario[] = [
17
17
  "standard_prompt",
18
18
  "guardian_prompt",
19
19
  "reminder_prompt",
20
- "guardian_delivery_failed",
21
- "guardian_request_forwarded",
22
- "guardian_disambiguation",
23
20
  "guardian_identity_mismatch",
24
21
  "request_pending_guardian",
25
- "guardian_decision_outcome",
26
- "guardian_expired_requester",
27
- "guardian_expired_guardian",
28
- "guardian_verify_success",
29
22
  "guardian_verify_failed",
30
23
  "guardian_verify_challenge_setup",
31
- "guardian_verify_status_bound",
32
- "guardian_verify_status_unbound",
33
- "guardian_deny_no_identity",
34
- "guardian_deny_no_binding",
35
24
  ];
36
25
 
37
26
  describe("approval-message-composer", () => {
@@ -66,59 +55,6 @@ describe("approval-message-composer", () => {
66
55
  expect(msg).toContain("write_file");
67
56
  });
68
57
 
69
- test("guardian_delivery_failed includes toolName when provided", () => {
70
- const msg = getFallbackMessage({
71
- scenario: "guardian_delivery_failed",
72
- toolName: "execute_shell",
73
- });
74
- expect(msg).toContain("execute_shell");
75
- });
76
-
77
- test("guardian_request_forwarded includes toolName", () => {
78
- const msg = getFallbackMessage({
79
- scenario: "guardian_request_forwarded",
80
- toolName: "execute_shell",
81
- });
82
- expect(msg).toContain("execute_shell");
83
- });
84
-
85
- test("guardian_disambiguation includes pendingCount", () => {
86
- const msg = getFallbackMessage({
87
- scenario: "guardian_disambiguation",
88
- pendingCount: 3,
89
- });
90
- expect(msg).toContain("3");
91
- });
92
-
93
- test("guardian_decision_outcome includes decision and toolName", () => {
94
- const msg = getFallbackMessage({
95
- scenario: "guardian_decision_outcome",
96
- decision: "approved",
97
- toolName: "read_file",
98
- });
99
- expect(msg).toContain("approved");
100
- expect(msg).toContain("read_file");
101
- });
102
-
103
- test("guardian_expired_requester includes toolName", () => {
104
- const msg = getFallbackMessage({
105
- scenario: "guardian_expired_requester",
106
- toolName: "deploy",
107
- });
108
- expect(msg).toContain("deploy");
109
- expect(msg).toContain("expired");
110
- });
111
-
112
- test("guardian_expired_guardian includes requester and toolName", () => {
113
- const msg = getFallbackMessage({
114
- scenario: "guardian_expired_guardian",
115
- requesterIdentifier: "bob",
116
- toolName: "delete_file",
117
- });
118
- expect(msg).toContain("bob");
119
- expect(msg).toContain("delete_file");
120
- });
121
-
122
58
  test("guardian_verify_failed includes failureReason", () => {
123
59
  const msg = getFallbackMessage({
124
60
  scenario: "guardian_verify_failed",
@@ -141,36 +77,7 @@ describe("approval-message-composer", () => {
141
77
  // -----------------------------------------------------------------------
142
78
 
143
79
  describe("composeApprovalMessage", () => {
144
- test("returns assistantPreface when provided (primary source)", () => {
145
- const preface = "The assistant already said something helpful.";
146
- const msg = composeApprovalMessage({
147
- scenario: "standard_prompt",
148
- toolName: "execute_shell",
149
- assistantPreface: preface,
150
- });
151
- expect(msg).toBe(preface);
152
- });
153
-
154
- test("ignores empty assistantPreface and falls back to template", () => {
155
- const msg = composeApprovalMessage({
156
- scenario: "standard_prompt",
157
- toolName: "execute_shell",
158
- assistantPreface: "",
159
- });
160
- expect(msg).toContain("execute_shell");
161
- expect(msg).not.toBe("");
162
- });
163
-
164
- test("ignores whitespace-only assistantPreface", () => {
165
- const msg = composeApprovalMessage({
166
- scenario: "standard_prompt",
167
- toolName: "execute_shell",
168
- assistantPreface: " ",
169
- });
170
- expect(msg).toContain("execute_shell");
171
- });
172
-
173
- test("falls back to deterministic template when no assistantPreface", () => {
80
+ test("returns the deterministic template", () => {
174
81
  const msg = composeApprovalMessage({
175
82
  scenario: "guardian_prompt",
176
83
  toolName: "write_file",
@@ -222,29 +129,5 @@ describe("approval-message-composer", () => {
222
129
  expect(msg.length).toBeGreaterThan(0);
223
130
  expect(msg).toContain("invalid or has expired");
224
131
  });
225
-
226
- test("guardian_verify_success produces a non-empty success message", () => {
227
- const msg = composeApprovalMessage({
228
- scenario: "guardian_verify_success",
229
- });
230
- expect(typeof msg).toBe("string");
231
- expect(msg.length).toBeGreaterThan(0);
232
- });
233
-
234
- test("guardian_verify_status_bound produces a non-empty message", () => {
235
- const msg = composeApprovalMessage({
236
- scenario: "guardian_verify_status_bound",
237
- });
238
- expect(typeof msg).toBe("string");
239
- expect(msg.length).toBeGreaterThan(0);
240
- });
241
-
242
- test("guardian_verify_status_unbound produces a non-empty message", () => {
243
- const msg = composeApprovalMessage({
244
- scenario: "guardian_verify_status_unbound",
245
- });
246
- expect(typeof msg).toBe("string");
247
- expect(msg.length).toBeGreaterThan(0);
248
- });
249
132
  });
250
133
  });