@vellumai/assistant 0.4.46 → 0.4.49

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 (382) hide show
  1. package/ARCHITECTURE.md +7 -7
  2. package/README.md +2 -23
  3. package/docs/architecture/integrations.md +45 -41
  4. package/docs/architecture/keychain-broker.md +3 -3
  5. package/docs/architecture/security.md +5 -5
  6. package/docs/runbook-trusted-contacts.md +3 -8
  7. package/hook-templates/debug-prompt-logger/hook.json +1 -1
  8. package/hook-templates/debug-prompt-logger/run.sh +1 -3
  9. package/package.json +1 -1
  10. package/src/__tests__/actor-token-service.test.ts +0 -1
  11. package/src/__tests__/anthropic-provider.test.ts +156 -0
  12. package/src/__tests__/approval-cascade.test.ts +810 -0
  13. package/src/__tests__/approval-primitive.test.ts +0 -1
  14. package/src/__tests__/approval-routes-http.test.ts +2 -0
  15. package/src/__tests__/assistant-attachments.test.ts +12 -34
  16. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +76 -0
  17. package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -1
  18. package/src/__tests__/browser-fill-credential.test.ts +5 -2
  19. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  20. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +2 -1
  21. package/src/__tests__/channel-guardian.test.ts +0 -2
  22. package/src/__tests__/channel-readiness-routes.test.ts +35 -25
  23. package/src/__tests__/channel-readiness-service.test.ts +10 -9
  24. package/src/__tests__/checker.test.ts +9 -29
  25. package/src/__tests__/cli.test.ts +23 -0
  26. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
  27. package/src/__tests__/computer-use-tools.test.ts +2 -19
  28. package/src/__tests__/config-watcher.test.ts +0 -1
  29. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  30. package/src/__tests__/context-image-dimensions.test.ts +332 -0
  31. package/src/__tests__/context-token-estimator.test.ts +196 -13
  32. package/src/__tests__/conversation-attention-store.test.ts +0 -1
  33. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  34. package/src/__tests__/conversation-routes-guardian-reply.test.ts +144 -0
  35. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  36. package/src/__tests__/credential-broker-browser-fill.test.ts +23 -22
  37. package/src/__tests__/credential-broker-server-use.test.ts +22 -21
  38. package/src/__tests__/credential-broker.test.ts +2 -1
  39. package/src/__tests__/credential-metadata-store.test.ts +239 -26
  40. package/src/__tests__/credential-resolve.test.ts +5 -4
  41. package/src/__tests__/credential-security-e2e.test.ts +8 -8
  42. package/src/__tests__/credential-security-invariants.test.ts +111 -7
  43. package/src/__tests__/credential-vault-unit.test.ts +287 -54
  44. package/src/__tests__/credential-vault.test.ts +406 -12
  45. package/src/__tests__/credentials-cli.test.ts +82 -6
  46. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  47. package/src/__tests__/ephemeral-permissions.test.ts +3 -3
  48. package/src/__tests__/gateway-only-enforcement.test.ts +4 -2
  49. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  50. package/src/__tests__/gemini-image-service.test.ts +75 -45
  51. package/src/__tests__/gemini-provider.test.ts +9 -6
  52. package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -33
  53. package/src/__tests__/guardian-action-copy-generator.test.ts +0 -20
  54. package/src/__tests__/guardian-action-followup-executor.test.ts +1 -28
  55. package/src/__tests__/guardian-action-followup-store.test.ts +1 -1
  56. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
  57. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
  58. package/src/__tests__/guardian-grant-minting.test.ts +35 -0
  59. package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
  60. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
  61. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -39
  62. package/src/__tests__/heartbeat-service.test.ts +0 -1
  63. package/src/__tests__/host-cu-proxy.test.ts +629 -0
  64. package/src/__tests__/host-shell-tool.test.ts +27 -15
  65. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  66. package/src/__tests__/ingress-url-consistency.test.ts +14 -21
  67. package/src/__tests__/integration-status.test.ts +38 -25
  68. package/src/__tests__/intent-routing.test.ts +0 -1
  69. package/src/__tests__/invite-routes-http.test.ts +10 -9
  70. package/src/__tests__/keychain-broker-client.test.ts +11 -43
  71. package/src/__tests__/managed-proxy-context.test.ts +5 -3
  72. package/src/__tests__/media-generate-image.test.ts +63 -2
  73. package/src/__tests__/media-reuse-story.e2e.test.ts +7 -3
  74. package/src/__tests__/messaging-send-tool.test.ts +4 -6
  75. package/src/__tests__/notification-routing-intent.test.ts +0 -1
  76. package/src/__tests__/oauth-cli.test.ts +373 -14
  77. package/src/__tests__/oauth-provider-profiles.test.ts +9 -9
  78. package/src/__tests__/oauth-scope-policy.test.ts +4 -6
  79. package/src/__tests__/oauth-store.test.ts +756 -0
  80. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
  81. package/src/__tests__/provider-error-scenarios.test.ts +0 -1
  82. package/src/__tests__/provider-fail-open-selection.test.ts +3 -1
  83. package/src/__tests__/provider-managed-proxy-integration.test.ts +70 -6
  84. package/src/__tests__/provider-streaming.benchmark.test.ts +0 -1
  85. package/src/__tests__/public-ingress-urls.test.ts +15 -21
  86. package/src/__tests__/recording-handler.test.ts +3 -4
  87. package/src/__tests__/registry.test.ts +2 -2
  88. package/src/__tests__/runtime-events-sse.test.ts +55 -7
  89. package/src/__tests__/schedule-store.test.ts +0 -1
  90. package/src/__tests__/scheduler-recurrence.test.ts +0 -1
  91. package/src/__tests__/schema-transforms.test.ts +226 -0
  92. package/src/__tests__/scoped-approval-grants.test.ts +0 -1
  93. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
  94. package/src/__tests__/script-proxy-injection-runtime.test.ts +23 -13
  95. package/src/__tests__/script-proxy-policy-runtime.test.ts +1 -1
  96. package/src/__tests__/script-proxy-session-manager.test.ts +1 -1
  97. package/src/__tests__/secret-ingress-handler.test.ts +0 -1
  98. package/src/__tests__/secret-onetime-send.test.ts +5 -3
  99. package/src/__tests__/send-endpoint-busy.test.ts +21 -6
  100. package/src/__tests__/sequence-store.test.ts +0 -1
  101. package/src/__tests__/session-init.benchmark.test.ts +4 -5
  102. package/src/__tests__/session-messaging-secret-redirect.test.ts +5 -4
  103. package/src/__tests__/skill-include-graph.test.ts +66 -0
  104. package/src/__tests__/skill-load-feature-flag.test.ts +0 -1
  105. package/src/__tests__/skill-load-tool.test.ts +149 -1
  106. package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
  107. package/src/__tests__/skills-uninstall.test.ts +3 -3
  108. package/src/__tests__/skills.test.ts +3 -12
  109. package/src/__tests__/slack-channel-config.test.ts +76 -11
  110. package/src/__tests__/slack-share-routes.test.ts +17 -14
  111. package/src/__tests__/system-prompt.test.ts +0 -1
  112. package/src/__tests__/telegram-bot-username-resolution.test.ts +3 -0
  113. package/src/__tests__/telegram-invite-adapter.test.ts +18 -22
  114. package/src/__tests__/terminal-tools.test.ts +4 -3
  115. package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
  116. package/src/__tests__/tool-approval-handler.test.ts +0 -1
  117. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
  118. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  119. package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
  120. package/src/__tests__/tool-executor.test.ts +0 -1
  121. package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
  122. package/src/__tests__/trust-store-pattern-matches.test.ts +29 -0
  123. package/src/__tests__/trust-store.test.ts +1 -22
  124. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  125. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
  126. package/src/__tests__/twilio-config.test.ts +2 -1
  127. package/src/__tests__/twilio-provider.test.ts +4 -2
  128. package/src/__tests__/twilio-routes.test.ts +5 -20
  129. package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
  130. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
  131. package/src/agent/ax-tree-compaction.test.ts +235 -0
  132. package/src/agent/loop.ts +76 -130
  133. package/src/calls/call-domain.ts +8 -10
  134. package/src/calls/relay-server.ts +9 -13
  135. package/src/calls/twilio-config.ts +4 -8
  136. package/src/calls/twilio-provider.ts +2 -1
  137. package/src/calls/twilio-rest.ts +2 -1
  138. package/src/calls/twilio-routes.ts +1 -2
  139. package/src/calls/voice-ingress-preflight.ts +1 -1
  140. package/src/cli/commands/browser-relay.ts +46 -15
  141. package/src/cli/commands/completions.ts +0 -3
  142. package/src/cli/commands/credentials.ts +110 -23
  143. package/src/cli/commands/oauth/apps.ts +255 -0
  144. package/src/cli/commands/oauth/connections.ts +299 -0
  145. package/src/cli/commands/oauth/index.ts +52 -0
  146. package/src/cli/commands/oauth/providers.ts +242 -0
  147. package/src/cli/commands/skills.ts +4 -338
  148. package/src/cli/program.ts +1 -5
  149. package/src/cli/reference.ts +1 -3
  150. package/src/cli.ts +3 -2
  151. package/src/config/assistant-feature-flags.ts +0 -3
  152. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
  153. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -4
  154. package/src/config/bundled-skills/computer-use/SKILL.md +3 -6
  155. package/src/config/bundled-skills/computer-use/TOOLS.json +22 -4
  156. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +29 -32
  157. package/src/config/bundled-skills/gmail/SKILL.md +4 -4
  158. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +54 -61
  159. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +25 -28
  160. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +14 -17
  161. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +39 -44
  162. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +61 -58
  163. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +50 -49
  164. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +11 -13
  165. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +148 -146
  166. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +4 -7
  167. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +175 -173
  168. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +4 -7
  169. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +71 -76
  170. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +32 -38
  171. package/src/config/bundled-skills/google-calendar/SKILL.md +2 -2
  172. package/src/config/bundled-skills/google-calendar/calendar-client.ts +90 -44
  173. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +9 -10
  174. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +5 -6
  175. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +4 -5
  176. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +14 -15
  177. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +37 -37
  178. package/src/config/bundled-skills/google-calendar/tools/shared.ts +4 -9
  179. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +24 -3
  180. package/src/config/bundled-skills/messaging/SKILL.md +6 -6
  181. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +62 -63
  182. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +15 -16
  183. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +4 -5
  184. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +6 -7
  185. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +4 -5
  186. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +14 -15
  187. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +4 -5
  188. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +128 -128
  189. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +33 -34
  190. package/src/config/bundled-skills/messaging/tools/shared.ts +12 -15
  191. package/src/config/bundled-skills/settings/SKILL.md +1 -1
  192. package/src/config/bundled-skills/settings/TOOLS.json +2 -8
  193. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +5 -33
  194. package/src/config/bundled-skills/slack/tools/shared.ts +4 -10
  195. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +4 -5
  196. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +15 -16
  197. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +4 -5
  198. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +4 -5
  199. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +4 -5
  200. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +95 -92
  201. package/src/config/env-registry.ts +14 -83
  202. package/src/config/env.ts +11 -50
  203. package/src/config/feature-flag-registry.json +16 -16
  204. package/src/config/schema.ts +3 -1
  205. package/src/config/skills.ts +21 -2
  206. package/src/context/image-dimensions.ts +229 -0
  207. package/src/context/token-estimator.ts +75 -12
  208. package/src/context/window-manager.ts +49 -10
  209. package/src/daemon/assistant-attachments.ts +1 -13
  210. package/src/daemon/guardian-action-generators.ts +4 -5
  211. package/src/daemon/handlers/config-ingress.ts +8 -33
  212. package/src/daemon/handlers/config-slack-channel.ts +76 -56
  213. package/src/daemon/handlers/config-telegram.ts +53 -24
  214. package/src/daemon/handlers/sessions.ts +10 -24
  215. package/src/daemon/handlers/shared.ts +0 -130
  216. package/src/daemon/host-cu-proxy.ts +401 -0
  217. package/src/daemon/lifecycle.ts +39 -63
  218. package/src/daemon/message-protocol.ts +3 -0
  219. package/src/daemon/message-types/computer-use.ts +2 -119
  220. package/src/daemon/message-types/host-cu.ts +19 -0
  221. package/src/daemon/message-types/integrations.ts +1 -0
  222. package/src/daemon/message-types/messages.ts +3 -0
  223. package/src/daemon/server.ts +14 -21
  224. package/src/daemon/session-agent-loop-handlers.ts +2 -0
  225. package/src/daemon/session-attachments.ts +1 -2
  226. package/src/daemon/session-messaging.ts +3 -1
  227. package/src/daemon/session-slash.ts +1 -1
  228. package/src/daemon/session-surfaces.ts +40 -28
  229. package/src/daemon/session-tool-setup.ts +20 -11
  230. package/src/daemon/session.ts +139 -16
  231. package/src/daemon/tool-side-effects.ts +2 -8
  232. package/src/daemon/watch-handler.ts +2 -2
  233. package/src/email/providers/index.ts +2 -1
  234. package/src/events/tool-metrics-listener.ts +2 -2
  235. package/src/hooks/manager.ts +1 -4
  236. package/src/inbound/public-ingress-urls.ts +7 -7
  237. package/src/instrument.ts +15 -1
  238. package/src/logfire.ts +16 -5
  239. package/src/media/app-icon-generator.ts +30 -4
  240. package/src/media/avatar-router.ts +26 -3
  241. package/src/media/gemini-image-service.ts +28 -2
  242. package/src/memory/conversation-key-store.ts +21 -0
  243. package/src/memory/db-init.ts +4 -0
  244. package/src/memory/guardian-action-store.ts +1 -1
  245. package/src/memory/migrations/149-oauth-tables.ts +60 -0
  246. package/src/memory/migrations/index.ts +1 -0
  247. package/src/memory/schema/guardian.ts +1 -1
  248. package/src/memory/schema/index.ts +1 -0
  249. package/src/memory/schema/oauth.ts +65 -0
  250. package/src/messaging/provider.ts +19 -13
  251. package/src/messaging/providers/gmail/adapter.ts +40 -23
  252. package/src/messaging/providers/gmail/client.ts +283 -122
  253. package/src/messaging/providers/gmail/people-client.ts +32 -24
  254. package/src/messaging/providers/slack/adapter.ts +29 -19
  255. package/src/messaging/providers/slack/client.ts +265 -78
  256. package/src/messaging/providers/telegram-bot/adapter.ts +19 -18
  257. package/src/messaging/providers/whatsapp/adapter.ts +17 -11
  258. package/src/messaging/registry.ts +2 -31
  259. package/src/notifications/copy-composer.ts +0 -5
  260. package/src/notifications/signal.ts +4 -5
  261. package/src/oauth/byo-connection.test.ts +537 -0
  262. package/src/oauth/byo-connection.ts +128 -0
  263. package/src/oauth/connect-orchestrator.ts +139 -56
  264. package/src/oauth/connect-types.ts +17 -23
  265. package/src/oauth/connection-resolver.ts +58 -0
  266. package/src/oauth/connection.ts +38 -0
  267. package/src/oauth/manual-token-connection.ts +104 -0
  268. package/src/oauth/oauth-store.ts +496 -0
  269. package/src/oauth/platform-connection.test.ts +192 -0
  270. package/src/oauth/platform-connection.ts +111 -0
  271. package/src/oauth/provider-behaviors.ts +124 -0
  272. package/src/oauth/scope-policy.ts +9 -2
  273. package/src/oauth/seed-providers.ts +161 -0
  274. package/src/oauth/token-persistence.ts +74 -78
  275. package/src/permissions/checker.ts +8 -4
  276. package/src/permissions/defaults.ts +0 -1
  277. package/src/permissions/prompter.ts +10 -1
  278. package/src/permissions/trust-store.ts +13 -0
  279. package/src/prompts/__tests__/build-cli-reference-section.test.ts +3 -1
  280. package/src/prompts/system-prompt.ts +70 -45
  281. package/src/providers/anthropic/client.ts +133 -24
  282. package/src/providers/gemini/client.ts +15 -6
  283. package/src/providers/managed-proxy/constants.ts +2 -2
  284. package/src/providers/managed-proxy/context.ts +5 -1
  285. package/src/providers/ratelimit.ts +17 -0
  286. package/src/providers/registry.ts +2 -2
  287. package/src/providers/retry.ts +1 -27
  288. package/src/runtime/AGENTS.md +17 -0
  289. package/src/runtime/auth/route-policy.ts +0 -3
  290. package/src/runtime/channel-invite-transports/telegram.ts +2 -1
  291. package/src/runtime/channel-readiness-service.ts +168 -195
  292. package/src/runtime/channel-readiness-types.ts +4 -0
  293. package/src/runtime/channel-reply-delivery.ts +0 -40
  294. package/src/runtime/gateway-client.ts +0 -7
  295. package/src/runtime/guardian-action-conversation-turn.ts +1 -3
  296. package/src/runtime/guardian-action-followup-executor.ts +1 -1
  297. package/src/runtime/guardian-action-message-composer.ts +3 -23
  298. package/src/runtime/http-server.ts +17 -10
  299. package/src/runtime/http-types.ts +2 -3
  300. package/src/runtime/middleware/rate-limiter.ts +74 -20
  301. package/src/runtime/middleware/twilio-validation.ts +1 -11
  302. package/src/runtime/pending-interactions.ts +14 -12
  303. package/src/runtime/routes/channel-delivery-routes.ts +0 -1
  304. package/src/runtime/routes/channel-readiness-routes.ts +2 -0
  305. package/src/runtime/routes/conversation-routes.ts +73 -19
  306. package/src/runtime/routes/diagnostics-routes.ts +11 -9
  307. package/src/runtime/routes/events-routes.ts +21 -11
  308. package/src/runtime/routes/guardian-approval-interception.ts +20 -5
  309. package/src/runtime/routes/host-cu-routes.ts +97 -0
  310. package/src/runtime/routes/inbound-stages/background-dispatch.ts +12 -111
  311. package/src/runtime/routes/integrations/slack/share.ts +6 -6
  312. package/src/runtime/routes/integrations/twilio.ts +6 -5
  313. package/src/runtime/routes/log-export-routes.ts +126 -8
  314. package/src/runtime/routes/secret-routes.ts +3 -2
  315. package/src/runtime/routes/settings-routes.ts +113 -48
  316. package/src/runtime/routes/surface-action-routes.ts +1 -1
  317. package/src/runtime/routes/watch-routes.ts +128 -0
  318. package/src/schedule/integration-status.ts +10 -8
  319. package/src/security/credential-key.ts +14 -0
  320. package/src/security/keychain-broker-client.ts +5 -6
  321. package/src/security/oauth2.ts +1 -1
  322. package/src/security/token-manager.ts +145 -43
  323. package/src/skills/catalog-install.ts +358 -0
  324. package/src/skills/include-graph.ts +32 -0
  325. package/src/telegram/bot-username.ts +2 -3
  326. package/src/tools/apps/definitions.ts +0 -5
  327. package/src/tools/assets/materialize.ts +0 -5
  328. package/src/tools/assets/search.ts +0 -5
  329. package/src/tools/browser/headless-browser.ts +1 -67
  330. package/src/tools/browser/network-recorder.ts +1 -1
  331. package/src/tools/browser/network-recording-types.ts +1 -1
  332. package/src/tools/claude-code/claude-code.ts +0 -5
  333. package/src/tools/computer-use/definitions.ts +46 -11
  334. package/src/tools/computer-use/registry.ts +4 -5
  335. package/src/tools/credentials/broker.ts +5 -4
  336. package/src/tools/credentials/metadata-store.ts +22 -74
  337. package/src/tools/credentials/resolve.ts +2 -1
  338. package/src/tools/credentials/vault.ts +139 -151
  339. package/src/tools/filesystem/edit.ts +1 -6
  340. package/src/tools/filesystem/read.ts +0 -5
  341. package/src/tools/filesystem/write.ts +1 -6
  342. package/src/tools/host-filesystem/edit.ts +1 -6
  343. package/src/tools/host-filesystem/read.ts +1 -6
  344. package/src/tools/host-filesystem/write.ts +1 -6
  345. package/src/tools/mcp/mcp-tool-factory.ts +18 -1
  346. package/src/tools/memory/definitions.ts +0 -5
  347. package/src/tools/network/web-fetch.ts +0 -5
  348. package/src/tools/network/web-search.ts +0 -5
  349. package/src/tools/registry.ts +2 -7
  350. package/src/tools/schema-transforms.ts +99 -0
  351. package/src/tools/skills/load.ts +62 -8
  352. package/src/tools/swarm/delegate.ts +0 -5
  353. package/src/tools/system/avatar-generator.ts +0 -5
  354. package/src/tools/ui-surface/definitions.ts +0 -15
  355. package/src/tools/watch/screen-watch.ts +0 -5
  356. package/src/tools/watch/watch-state.ts +0 -12
  357. package/src/util/logger.ts +7 -41
  358. package/src/util/platform.ts +9 -28
  359. package/src/version.ts +10 -0
  360. package/src/watcher/providers/github.ts +51 -52
  361. package/src/watcher/providers/gmail.ts +88 -80
  362. package/src/watcher/providers/google-calendar.ts +94 -86
  363. package/src/watcher/providers/linear.ts +87 -93
  364. package/src/__tests__/computer-use-session-compaction.test.ts +0 -143
  365. package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -322
  366. package/src/__tests__/computer-use-session-working-dir.test.ts +0 -166
  367. package/src/__tests__/computer-use-skill-baseline.test.ts +0 -78
  368. package/src/__tests__/computer-use-skill-endstate.test.ts +0 -105
  369. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -249
  370. package/src/__tests__/ride-shotgun-handler.test.ts +0 -452
  371. package/src/cli/commands/dev.ts +0 -129
  372. package/src/cli/commands/map.ts +0 -391
  373. package/src/cli/commands/oauth.ts +0 -77
  374. package/src/config/bundled-skills/computer-use/tools/computer-use-request-control.ts +0 -16
  375. package/src/daemon/computer-use-session.ts +0 -1020
  376. package/src/daemon/ride-shotgun-handler.ts +0 -567
  377. package/src/oauth/provider-profiles.ts +0 -192
  378. package/src/prompts/computer-use-prompt.ts +0 -98
  379. package/src/runtime/routes/computer-use-routes.ts +0 -641
  380. package/src/runtime/telegram-streaming-delivery.test.ts +0 -597
  381. package/src/runtime/telegram-streaming-delivery.ts +0 -383
  382. package/src/tools/computer-use/request-computer-control.ts +0 -61
@@ -84,7 +84,10 @@ export class PermissionPrompter {
84
84
  "Permission prompt timed out, defaulting to deny",
85
85
  );
86
86
  this.onStateChanged?.(requestId, "timed_out", "timeout", toolUseId);
87
- resolve({ decision: "deny" });
87
+ resolve({
88
+ decision: "deny",
89
+ decisionContext: `The permission prompt for the "${toolName}" tool timed out. The user did not explicitly deny this request — they may have been away or busy. You may retry this tool call if it is still needed for the current task.`,
90
+ });
88
91
  }, timeoutMs);
89
92
 
90
93
  this.pending.set(requestId, { resolve, reject, timer, toolUseId });
@@ -121,6 +124,7 @@ export class PermissionPrompter {
121
124
  executionTarget,
122
125
  persistentDecisionsAllowed: persistentDecisionsAllowed ?? true,
123
126
  temporaryOptionsAvailable,
127
+ toolUseId,
124
128
  });
125
129
 
126
130
  this.onStateChanged?.(requestId, "pending", "system", toolUseId);
@@ -131,6 +135,11 @@ export class PermissionPrompter {
131
135
  return this.pending.has(requestId);
132
136
  }
133
137
 
138
+ /** Returns all currently pending request IDs. */
139
+ getPendingRequestIds(): string[] {
140
+ return [...this.pending.keys()];
141
+ }
142
+
134
143
  /** Returns the toolUseId associated with a pending request, if any. */
135
144
  getToolUseId(requestId: string): string | undefined {
136
145
  return this.pending.get(requestId)?.toolUseId;
@@ -76,6 +76,19 @@ function getCompiledPattern(pattern: string): Minimatch | null {
76
76
  return compiled;
77
77
  }
78
78
 
79
+ /**
80
+ * Check whether a minimatch pattern matches a candidate string.
81
+ * Reuses the compiled pattern cache from trust rule evaluation.
82
+ */
83
+ export function patternMatchesCandidate(
84
+ pattern: string,
85
+ candidate: string,
86
+ ): boolean {
87
+ const compiled = getCompiledPattern(pattern);
88
+ if (!compiled) return false;
89
+ return compiled.match(candidate);
90
+ }
91
+
79
92
  /** Rebuild the compiled pattern cache from the current rule set. */
80
93
  function rebuildPatternCache(rules: TrustRule[]): void {
81
94
  compiledPatterns.clear();
@@ -38,7 +38,9 @@ describe("buildCliReferenceSection", () => {
38
38
  "prefer real `assistant` CLI workflows over any legacy account-record abstraction",
39
39
  );
40
40
  expect(result).toContain("assistant credentials");
41
- expect(result).toContain("assistant oauth token <service>");
41
+ expect(result).toContain(
42
+ "assistant oauth connections token <provider-key>",
43
+ );
42
44
  expect(result).toContain("assistant mcp auth <name>");
43
45
  expect(result).toContain("assistant platform status");
44
46
  });
@@ -4,10 +4,10 @@ import { join } from "node:path";
4
4
  import { CLI_HELP_REFERENCE } from "../cli/reference.js";
5
5
  import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
6
6
  import { getBaseDataDir, getIsContainerized } from "../config/env-registry.js";
7
- import { getConfig, getNestedValue, loadRawConfig } from "../config/loader.js";
7
+ import { getConfig } from "../config/loader.js";
8
8
  import { skillFlagKey } from "../config/skill-state.js";
9
9
  import { loadSkillCatalog, type SkillSummary } from "../config/skills.js";
10
- import { listCredentialMetadata } from "../tools/credentials/metadata-store.js";
10
+ import { listConnections } from "../oauth/oauth-store.js";
11
11
  import { resolveBundledDir } from "../util/bundled-asset.js";
12
12
  import { getLogger } from "../util/logger.js";
13
13
  import {
@@ -109,7 +109,11 @@ export function isOnboardingComplete(): boolean {
109
109
  * 3. If BOOTSTRAP.md exists, append first-run ritual instructions
110
110
  * 4. Append skills catalog from ~/.vellum/workspace/skills
111
111
  */
112
- export function buildSystemPrompt(): string {
112
+ export interface BuildSystemPromptOptions {
113
+ hasNoClient?: boolean;
114
+ }
115
+
116
+ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
113
117
  const soulPath = getWorkspacePromptPath("SOUL.md");
114
118
  const identityPath = getWorkspacePromptPath("IDENTITY.md");
115
119
  const userPath = getWorkspacePromptPath("USER.md");
@@ -156,13 +160,14 @@ export function buildSystemPrompt(): string {
156
160
  );
157
161
  }
158
162
  if (getIsContainerized()) parts.push(buildContainerizedSection());
159
- parts.push(buildConfigSection());
163
+ const hasNoClient = options?.hasNoClient ?? false;
164
+ parts.push(buildConfigSection(hasNoClient));
160
165
  parts.push(buildCliReferenceSection());
161
166
  parts.push(buildPostToolResponseSection());
162
167
  parts.push(buildExternalCommsIdentitySection());
163
168
  parts.push(buildChannelAwarenessSection());
164
169
  const config = getConfig();
165
- parts.push(buildToolPermissionSection());
170
+ if (!hasNoClient) parts.push(buildToolPermissionSection());
166
171
  parts.push(buildTaskScheduleReminderRoutingSection());
167
172
  if (
168
173
  isAssistantFeatureFlagEnabled(
@@ -181,9 +186,9 @@ export function buildSystemPrompt(): string {
181
186
  if (!isOnboardingComplete()) {
182
187
  parts.push(buildStarterTaskPlaybookSection());
183
188
  }
184
- parts.push(buildSystemPermissionSection());
189
+ if (!hasNoClient) parts.push(buildSystemPermissionSection());
185
190
  parts.push(buildSwarmGuidanceSection());
186
- parts.push(buildAccessPreferenceSection());
191
+ parts.push(buildAccessPreferenceSection(hasNoClient));
187
192
  parts.push(buildIntegrationSection());
188
193
  parts.push(buildMemoryPersistenceSection());
189
194
  parts.push(buildMemoryRecallSection());
@@ -278,7 +283,7 @@ function buildAttachmentSection(): string {
278
283
  "",
279
284
  'Example: `<vellum-attachment source="sandbox" path="scratch/chart.png" />`',
280
285
  "",
281
- "Limits: up to 5 attachments per turn, 20 MB each. Tool outputs that produce image or file content blocks are also automatically converted into attachments.",
286
+ "Limits: 20 MB per attachment. Tool outputs that produce image or file content blocks are also automatically converted into attachments.",
282
287
  "",
283
288
  "### Inline Images and GIFs",
284
289
  "Embed images/GIFs inline using markdown: `![description](URL)`. Do NOT wrap in code fences.",
@@ -363,13 +368,11 @@ export function buildVoiceSetupRoutingSection(): string {
363
368
  return [
364
369
  "## Routing: Voice Setup & Troubleshooting",
365
370
  "",
366
- "Voice features include push-to-talk (PTT), wake word detection, and text-to-speech.",
371
+ "Voice features include push-to-talk (PTT) and text-to-speech.",
367
372
  "",
368
373
  "### Quick changes — use `voice_config_update` directly",
369
374
  '- "Change my PTT key to ctrl" — call `voice_config_update` with `setting: "activation_key"`',
370
- '- "Enable wake word" — call `voice_config_update` with `setting: "wake_word_enabled"`, `value: true`',
371
- '- "Set my wake word to jarvis" — call `voice_config_update` with `setting: "wake_word_keyword"`',
372
- '- "Set wake word timeout to 30 seconds" — call `voice_config_update` with `setting: "wake_word_timeout"`',
375
+ '- "Set conversation timeout to 30 seconds" — call `voice_config_update` with `setting: "conversation_timeout"`',
373
376
  "",
374
377
  "For simple setting changes, use the tool directly without loading the voice-setup skill.",
375
378
  "",
@@ -379,15 +382,14 @@ export function buildVoiceSetupRoutingSection(): string {
379
382
  "**Trigger phrases:**",
380
383
  '- "Help me set up voice"',
381
384
  '- "Set up push-to-talk"',
382
- '- "Configure voice / PTT / wake word"',
385
+ '- "Configure voice / PTT"',
383
386
  '- "PTT isn\'t working" / "push-to-talk not working"',
384
387
  '- "Recording but no text"',
385
- '- "Wake word not detecting"',
386
388
  '- "Microphone not working"',
387
389
  '- "Set up ElevenLabs" / "configure TTS"',
388
390
  "",
389
391
  "### Disambiguation",
390
- "- Voice setup (this skill) = **local PTT, wake word, microphone permissions** on the Mac desktop app.",
392
+ "- Voice setup (this skill) = **local PTT, microphone permissions** on the Mac desktop app.",
391
393
  "- Phone calls skill = **Twilio-powered voice calls** over the phone network. Completely separate.",
392
394
  '- If the user says "voice" in the context of phone calls or Twilio, load `phone-calls` instead.',
393
395
  ].join("\n");
@@ -418,7 +420,7 @@ export function buildPhoneCallsRoutingSection(): string {
418
420
  "",
419
421
  "### Exclusivity rules",
420
422
  "- Do NOT improvise Twilio setup instructions from general knowledge — always load the skill first.",
421
- "- Do NOT confuse with voice-setup (local PTT/wake word/microphone) or guardian-verify-setup (channel verification).",
423
+ "- Do NOT confuse with voice-setup (local PTT/microphone) or guardian-verify-setup (channel verification).",
422
424
  '- If the user says "voice" in the context of phone calls or Twilio, load phone-calls, not voice-setup.',
423
425
  "- For guardian voice verification specifically, load guardian-verify-setup instead.",
424
426
  ].join("\n");
@@ -572,7 +574,23 @@ export function buildSwarmGuidanceSection(): string {
572
574
  ].join("\n");
573
575
  }
574
576
 
575
- function buildAccessPreferenceSection(): string {
577
+ function buildAccessPreferenceSection(hasNoClient: boolean): string {
578
+ if (hasNoClient) {
579
+ return [
580
+ "## External Service Access Preference",
581
+ "",
582
+ "When interacting with external services (GitHub, Slack, Linear, Jira, cloud providers, etc.),",
583
+ "follow this priority order:",
584
+ "",
585
+ "1. **Sandbox first (`bash`)** — Always try to do things in your own sandbox environment first.",
586
+ " If a tool (git, curl, jq, etc.) is not installed, install it yourself using `bash`",
587
+ " (e.g. `apt-get install -y git`). The sandbox is your own machine — you have full control.",
588
+ "2. **web_fetch** — For public endpoints or simple API calls that don't need auth.",
589
+ "3. **Browser automation as last resort** — Only when the task genuinely requires a browser",
590
+ " (e.g., no API exists, visual interaction needed, or OAuth consent screen).",
591
+ ].join("\n");
592
+ }
593
+
576
594
  return [
577
595
  "## External Service Access Preference",
578
596
  "",
@@ -609,38 +627,38 @@ function buildAccessPreferenceSection(): string {
609
627
  "",
610
628
  "### Foreground Computer Use — Last Resort",
611
629
  "",
612
- "Foreground computer use (`computer_use_request_control`) takes over the user's cursor and",
613
- "keyboard. It is disruptive and should be your LAST resort. Prefer this hierarchy:",
630
+ "Computer use tools (clicking, typing, scrolling) take over the user's cursor and keyboard.",
631
+ "They are disruptive and should be your LAST resort. Prefer this hierarchy:",
614
632
  "",
615
633
  "1. **CLI tools / osascript** — Use `host_bash` with shell commands or `osascript` with",
616
634
  " AppleScript to accomplish tasks in the background without interrupting the user.",
617
635
  "2. **Background computer use** — If you must interact with a GUI app, prefer AppleScript",
618
636
  ' automation (e.g. `tell application "Safari" to set URL of current tab to ...`).',
619
- "3. **Foreground computer use** — Only escalate via `computer_use_request_control` when",
620
- " the task genuinely cannot be done any other way (e.g. complex multi-step GUI interactions",
621
- " with no scripting support) or the user explicitly asks you to take control.",
637
+ "3. **Foreground computer use** — Only use computer use tools when the task genuinely",
638
+ " cannot be done any other way (e.g. complex multi-step GUI interactions with no scripting",
639
+ " support) or the user explicitly asks you to take control.",
622
640
  ]
623
641
  : []),
624
642
  ].join("\n");
625
643
  }
626
644
 
627
645
  function buildIntegrationSection(): string {
628
- const allCreds = listCredentialMetadata();
629
- // Show OAuth2-connected services (those with oauth2TokenUrl in metadata)
630
- const oauthCreds = allCreds.filter(
631
- (c) => c.oauth2TokenUrl && c.field === "access_token",
632
- );
633
- if (oauthCreds.length === 0) return "";
646
+ let connections: { providerKey: string; accountInfo?: string | null }[];
647
+ try {
648
+ connections = listConnections().filter((c) => c.status === "active");
649
+ } catch {
650
+ // DB not available — no connected services to show
651
+ return "";
652
+ }
653
+
654
+ if (connections.length === 0) return "";
634
655
 
635
- const raw = loadRawConfig();
636
656
  const lines = ["## Connected Services", ""];
637
- for (const cred of oauthCreds) {
638
- const acctInfo = getNestedValue(
639
- raw,
640
- `integrations.accountInfo.${cred.service}`,
641
- ) as string | undefined;
642
- const state = acctInfo ? `Connected (${acctInfo})` : "Connected";
643
- lines.push(`- **${cred.service}**: ${state}`);
657
+ for (const conn of connections) {
658
+ const state = conn.accountInfo
659
+ ? `Connected (${conn.accountInfo})`
660
+ : "Connected";
661
+ lines.push(`- **${conn.providerKey}**: ${state}`);
644
662
  }
645
663
 
646
664
  return lines.join("\n");
@@ -751,10 +769,12 @@ function buildPostToolResponseSection(): string {
751
769
  " → Call document_create",
752
770
  "",
753
771
  "For permission-gated tools, send one short context sentence immediately before the tool call so the user can make an informed allow/deny decision.",
772
+ "",
773
+ '**Reason field:** For every tool call, include a `reason` parameter — a brief, non-technical explanation of what you are doing and why. This is shown to the user as a live status update. Use simple language a non-technical person would understand (e.g. "Checking your project settings" not "file_read config.ts").',
754
774
  ].join("\n");
755
775
  }
756
776
 
757
- function buildConfigSection(): string {
777
+ function buildConfigSection(hasNoClient: boolean): string {
758
778
  // Always use `file_edit` (not `host_file_edit`) for workspace files — file_edit
759
779
  // handles sandbox path mapping internally, and host_file_edit is permission-gated
760
780
  // which would trigger approval prompts for routine workspace updates.
@@ -763,10 +783,14 @@ function buildConfigSection(): string {
763
783
  const config = getConfig();
764
784
  const configPreamble = `Your configuration directory is \`${hostWorkspaceDir}/\`.`;
765
785
 
786
+ const fileToolGuidance = hasNoClient
787
+ ? `${configPreamble} **Always use \`file_read\` and \`file_edit\` for these files** — they are inside your sandbox working directory:`
788
+ : `${configPreamble} **Always use \`file_read\` and \`file_edit\` (not \`host_file_read\` / \`host_file_edit\`) for these files** — they are inside your sandbox working directory and do not require host access or user approval:`;
789
+
766
790
  return [
767
791
  "## Configuration",
768
792
  `- **Active model**: \`${config.model}\` (provider: ${config.provider})`,
769
- `${configPreamble} **Always use \`file_read\` and \`file_edit\` (not \`host_file_read\` / \`host_file_edit\`) for these files** — they are inside your sandbox working directory and do not require host access or user approval:`,
793
+ fileToolGuidance,
770
794
  "",
771
795
  "- `IDENTITY.md` — Your name, nature, personality, and emoji. Updated during the first-run ritual.",
772
796
  "- `SOUL.md` — Core principles, personality, and evolution guidance. Your behavioral foundation.",
@@ -801,7 +825,13 @@ function buildConfigSection(): string {
801
825
  "- They rename you or change your role",
802
826
  "- Your avatar appearance changes (update the `## Avatar` section with a description of the new look)",
803
827
  "",
804
- "When reading or updating workspace files, always use the sandbox tools (`file_read`, `file_edit`). Never use `host_file_read` or `host_file_edit` for workspace files — those are for host-only resources outside your workspace.",
828
+ ...(hasNoClient
829
+ ? [
830
+ "When reading or updating workspace files, always use the sandbox tools (`file_read`, `file_edit`).",
831
+ ]
832
+ : [
833
+ "When reading or updating workspace files, always use the sandbox tools (`file_read`, `file_edit`). Never use `host_file_read` or `host_file_edit` for workspace files — those are for host-only resources outside your workspace.",
834
+ ]),
805
835
  "",
806
836
  "When updating, read the file first, then make a targeted edit. Include all useful information, but don't bloat the files over time",
807
837
  ].join("\n");
@@ -818,7 +848,7 @@ export function buildCliReferenceSection(): string {
818
848
  "The `assistant` CLI is installed on the user's machine and available via `bash`.",
819
849
  "For account and authentication work, prefer real `assistant` CLI workflows over any legacy account-record abstraction.",
820
850
  "- Use `assistant credentials ...` for stored secrets and credential metadata.",
821
- "- Use `assistant oauth token <service>` for connected integration tokens.",
851
+ "- Use `assistant oauth connections token <provider-key>` for connected integration tokens.",
822
852
  "- Use `assistant mcp auth <name>` when an MCP server needs OAuth login.",
823
853
  "- Use `assistant platform status` for platform-linked deployment and auth context.",
824
854
  "- If a bundled skill documents a service-specific `assistant <service>` auth or session flow, follow that CLI exactly.",
@@ -992,10 +1022,5 @@ function formatSkillsCatalog(skills: SkillSummary[]): string {
992
1022
  "",
993
1023
  lines.join("\n"),
994
1024
  "",
995
- "### Installing additional skills",
996
- "If `skill_load` fails because a skill is not found, additional first-party skills may be available in the Vellum catalog.",
997
- "Use `bash` to discover and install them:",
998
- "- `assistant skills list` — list all available catalog skills",
999
- "- `assistant skills install <skill-id>` — install a skill, then retry `skill_load`",
1000
1025
  ].join("\n");
1001
1026
  }
@@ -63,6 +63,20 @@ function isToolUseBlock(block: unknown): block is Anthropic.ToolUseBlockParam {
63
63
  );
64
64
  }
65
65
 
66
+ /** Type-guard for server_tool_use blocks (e.g. native web search). */
67
+ function isServerToolUseBlock(block: unknown): block is {
68
+ type: "server_tool_use";
69
+ id: string;
70
+ name: string;
71
+ input: unknown;
72
+ } {
73
+ return (
74
+ typeof block === "object" &&
75
+ block != null &&
76
+ (block as { type: string }).type === "server_tool_use"
77
+ );
78
+ }
79
+
66
80
  /** Type-guard for tool_result blocks in Anthropic-formatted content. */
67
81
  function isToolResultBlock(
68
82
  block: unknown,
@@ -74,6 +88,19 @@ function isToolResultBlock(
74
88
  );
75
89
  }
76
90
 
91
+ /** Type-guard for web_search_tool_result blocks. */
92
+ function isWebSearchToolResultBlock(block: unknown): block is {
93
+ type: "web_search_tool_result";
94
+ tool_use_id: string;
95
+ content: unknown;
96
+ } {
97
+ return (
98
+ typeof block === "object" &&
99
+ block != null &&
100
+ (block as { type: string }).type === "web_search_tool_result"
101
+ );
102
+ }
103
+
77
104
  /**
78
105
  * Build a short diagnostic summary of a message array for error logging.
79
106
  * Shows role + block types (with tool_use/tool_result IDs) for each message.
@@ -84,8 +111,12 @@ function summarizeMessages(messages: Anthropic.MessageParam[]): string[] {
84
111
  const blockDescs = content.map((b) => {
85
112
  const bt = (b as { type: string }).type;
86
113
  if (bt === "tool_use") return `tool_use(${(b as { id: string }).id})`;
114
+ if (bt === "server_tool_use")
115
+ return `server_tool_use(${(b as { id: string }).id})`;
87
116
  if (bt === "tool_result")
88
117
  return `tool_result(${(b as { tool_use_id: string }).tool_use_id})`;
118
+ if (bt === "web_search_tool_result")
119
+ return `web_search_tool_result(${(b as { tool_use_id: string }).tool_use_id})`;
89
120
  return bt;
90
121
  });
91
122
  return `[${idx}] ${m.role}: ${blockDescs.join(", ") || "(empty)"}`;
@@ -103,29 +134,70 @@ function buildSyntheticToolResult(
103
134
  };
104
135
  }
105
136
 
106
- function getOrderedToolUseIds(
107
- content: Anthropic.ContentBlockParam[],
108
- ): string[] {
137
+ function buildSyntheticWebSearchToolResult(
138
+ toolUseId: string,
139
+ ): Anthropic.ContentBlockParam {
140
+ return {
141
+ type: "web_search_tool_result",
142
+ tool_use_id: toolUseId,
143
+ content: {
144
+ type: "web_search_tool_result_error",
145
+ error_code: "unavailable",
146
+ },
147
+ } as unknown as Anthropic.ContentBlockParam;
148
+ }
149
+
150
+ /** Build the appropriate synthetic result block based on whether the ID is for a server tool or regular tool. */
151
+ function buildSyntheticResult(
152
+ toolUseId: string,
153
+ serverToolIds: ReadonlySet<string>,
154
+ ): Anthropic.ContentBlockParam {
155
+ if (serverToolIds.has(toolUseId)) {
156
+ return buildSyntheticWebSearchToolResult(toolUseId);
157
+ }
158
+ return buildSyntheticToolResult(toolUseId);
159
+ }
160
+
161
+ function getOrderedToolUseIds(content: Anthropic.ContentBlockParam[]): {
162
+ ids: string[];
163
+ serverToolIds: Set<string>;
164
+ } {
109
165
  const ids: string[] = [];
110
166
  const seen = new Set<string>();
167
+ const serverToolIds = new Set<string>();
111
168
  for (const block of content) {
112
- if (!isToolUseBlock(block)) continue;
113
- if (seen.has(block.id)) continue;
114
- seen.add(block.id);
115
- ids.push(block.id);
169
+ if (isToolUseBlock(block)) {
170
+ if (!seen.has(block.id)) {
171
+ seen.add(block.id);
172
+ ids.push(block.id);
173
+ }
174
+ } else if (isServerToolUseBlock(block)) {
175
+ if (!seen.has(block.id)) {
176
+ seen.add(block.id);
177
+ ids.push(block.id);
178
+ serverToolIds.add(block.id);
179
+ }
180
+ }
116
181
  }
117
- return ids;
182
+ return { ids, serverToolIds };
118
183
  }
119
184
 
120
185
  function hasOrderedToolResultPrefix(
121
186
  content: Anthropic.ContentBlockParam[],
122
187
  orderedToolUseIds: string[],
188
+ serverToolIds: ReadonlySet<string>,
123
189
  ): boolean {
124
190
  if (content.length < orderedToolUseIds.length) return false;
125
191
  for (let idx = 0; idx < orderedToolUseIds.length; idx++) {
126
192
  const block = content[idx];
127
- if (!isToolResultBlock(block)) return false;
128
- if (block.tool_use_id !== orderedToolUseIds[idx]) return false;
193
+ const expectedId = orderedToolUseIds[idx];
194
+ if (serverToolIds.has(expectedId)) {
195
+ if (!isWebSearchToolResultBlock(block)) return false;
196
+ if (block.tool_use_id !== expectedId) return false;
197
+ } else {
198
+ if (!isToolResultBlock(block)) return false;
199
+ if (block.tool_use_id !== expectedId) return false;
200
+ }
129
201
  }
130
202
  return true;
131
203
  }
@@ -134,14 +206,15 @@ function splitAssistantForToolPairing(content: Anthropic.ContentBlockParam[]): {
134
206
  pairedContent: Anthropic.ContentBlockParam[];
135
207
  carryoverContent: Anthropic.ContentBlockParam[];
136
208
  toolUseIds: string[];
209
+ serverToolIds: Set<string>;
137
210
  } {
138
211
  const leading: Anthropic.ContentBlockParam[] = [];
139
- const toolUseBlocks: Anthropic.ToolUseBlockParam[] = [];
212
+ const toolUseBlocks: Anthropic.ContentBlockParam[] = [];
140
213
  const carryover: Anthropic.ContentBlockParam[] = [];
141
214
  let seenToolUse = false;
142
215
 
143
216
  for (const block of content) {
144
- if (isToolUseBlock(block)) {
217
+ if (isToolUseBlock(block) || isServerToolUseBlock(block)) {
145
218
  seenToolUse = true;
146
219
  toolUseBlocks.push(block);
147
220
  continue;
@@ -158,6 +231,7 @@ function splitAssistantForToolPairing(content: Anthropic.ContentBlockParam[]): {
158
231
  pairedContent: content,
159
232
  carryoverContent: [],
160
233
  toolUseIds: [],
234
+ serverToolIds: new Set(),
161
235
  };
162
236
  }
163
237
 
@@ -165,16 +239,19 @@ function splitAssistantForToolPairing(content: Anthropic.ContentBlockParam[]): {
165
239
  ...leading,
166
240
  ...toolUseBlocks,
167
241
  ];
242
+ const { ids, serverToolIds } = getOrderedToolUseIds(pairedContent);
168
243
  return {
169
244
  pairedContent,
170
245
  carryoverContent: carryover,
171
- toolUseIds: getOrderedToolUseIds(pairedContent),
246
+ toolUseIds: ids,
247
+ serverToolIds,
172
248
  };
173
249
  }
174
250
 
175
251
  function normalizeFollowingUserContent(
176
252
  nextContent: Anthropic.ContentBlockParam[],
177
253
  orderedToolUseIds: string[],
254
+ serverToolIds: ReadonlySet<string>,
178
255
  ): {
179
256
  toolResultPrefix: Anthropic.ContentBlockParam[];
180
257
  remainingContent: Anthropic.ContentBlockParam[];
@@ -182,24 +259,37 @@ function normalizeFollowingUserContent(
182
259
  hadOrderedPrefix: boolean;
183
260
  } {
184
261
  const pendingIds = new Set(orderedToolUseIds);
185
- const matchedById = new Map<string, Anthropic.ToolResultBlockParam>();
262
+ const matchedById = new Map<string, Anthropic.ContentBlockParam>();
186
263
  const remaining: Anthropic.ContentBlockParam[] = [];
187
264
 
188
265
  for (const block of nextContent) {
189
266
  if (
190
267
  isToolResultBlock(block) &&
191
268
  pendingIds.has(block.tool_use_id) &&
192
- !matchedById.has(block.tool_use_id)
269
+ !matchedById.has(block.tool_use_id) &&
270
+ !serverToolIds.has(block.tool_use_id)
193
271
  ) {
194
272
  matchedById.set(block.tool_use_id, block);
195
273
  continue;
196
274
  }
275
+ if (
276
+ isWebSearchToolResultBlock(block) &&
277
+ pendingIds.has(block.tool_use_id) &&
278
+ !matchedById.has(block.tool_use_id) &&
279
+ serverToolIds.has(block.tool_use_id)
280
+ ) {
281
+ matchedById.set(
282
+ block.tool_use_id,
283
+ block as unknown as Anthropic.ContentBlockParam,
284
+ );
285
+ continue;
286
+ }
197
287
  remaining.push(block);
198
288
  }
199
289
 
200
290
  const missingIds = orderedToolUseIds.filter((id) => !matchedById.has(id));
201
291
  const orderedResults = orderedToolUseIds.map(
202
- (id) => matchedById.get(id) ?? buildSyntheticToolResult(id),
292
+ (id) => matchedById.get(id) ?? buildSyntheticResult(id, serverToolIds),
203
293
  );
204
294
 
205
295
  return {
@@ -209,6 +299,7 @@ function normalizeFollowingUserContent(
209
299
  hadOrderedPrefix: hasOrderedToolResultPrefix(
210
300
  nextContent,
211
301
  orderedToolUseIds,
302
+ serverToolIds,
212
303
  ),
213
304
  };
214
305
  }
@@ -237,7 +328,7 @@ function ensureToolPairing(
237
328
  }
238
329
 
239
330
  const content = Array.isArray(msg.content) ? msg.content : [];
240
- const { pairedContent, carryoverContent, toolUseIds } =
331
+ const { pairedContent, carryoverContent, toolUseIds, serverToolIds } =
241
332
  splitAssistantForToolPairing(content);
242
333
 
243
334
  if (toolUseIds.length === 0) {
@@ -246,7 +337,7 @@ function ensureToolPairing(
246
337
  continue;
247
338
  }
248
339
 
249
- // Assistant message — push the paired portion (pre-tool text + tool_use blocks)
340
+ // Assistant message — push the paired portion (pre-tool text + tool_use/server_tool_use blocks)
250
341
  result.push({
251
342
  role: "assistant" as const,
252
343
  content: pairedContent,
@@ -267,7 +358,11 @@ function ensureToolPairing(
267
358
  const next = messages[i + 1];
268
359
  if (next && next.role === "user") {
269
360
  const nextContent = Array.isArray(next.content) ? next.content : [];
270
- const normalized = normalizeFollowingUserContent(nextContent, toolUseIds);
361
+ const normalized = normalizeFollowingUserContent(
362
+ nextContent,
363
+ toolUseIds,
364
+ serverToolIds,
365
+ );
271
366
  if (normalized.missingIds.length > 0) {
272
367
  log.warn(
273
368
  {
@@ -332,7 +427,9 @@ function ensureToolPairing(
332
427
  );
333
428
  result.push({
334
429
  role: "user" as const,
335
- content: toolUseIds.map((id) => buildSyntheticToolResult(id)),
430
+ content: toolUseIds.map((id) =>
431
+ buildSyntheticResult(id, serverToolIds),
432
+ ),
336
433
  });
337
434
 
338
435
  // If the assistant contained collapsed post-tool text, preserve it as a
@@ -353,17 +450,29 @@ function ensureToolPairing(
353
450
  const m = result[j];
354
451
  if (m.role !== "assistant") continue;
355
452
  const c = Array.isArray(m.content) ? m.content : [];
356
- const ids = getOrderedToolUseIds(c);
357
- if (ids.length === 0) continue;
453
+ const { ids: validationIds, serverToolIds: validationServerToolIds } =
454
+ getOrderedToolUseIds(c);
455
+ if (validationIds.length === 0) continue;
358
456
 
359
457
  const nxt = result[j + 1];
360
458
  const nxtContent =
361
459
  nxt && nxt.role === "user" && Array.isArray(nxt.content)
362
460
  ? nxt.content
363
461
  : [];
364
- if (!hasOrderedToolResultPrefix(nxtContent, ids)) {
365
- const unmatchedIds = ids.filter((id, idx) => {
462
+ if (
463
+ !hasOrderedToolResultPrefix(
464
+ nxtContent,
465
+ validationIds,
466
+ validationServerToolIds,
467
+ )
468
+ ) {
469
+ const unmatchedIds = validationIds.filter((id, idx) => {
366
470
  const block = nxtContent[idx];
471
+ if (validationServerToolIds.has(id)) {
472
+ return !(
473
+ isWebSearchToolResultBlock(block) && block.tool_use_id === id
474
+ );
475
+ }
367
476
  return !(isToolResultBlock(block) && block.tool_use_id === id);
368
477
  });
369
478
  log.error(
@@ -16,6 +16,10 @@ export interface GeminiProviderOptions {
16
16
  streamTimeoutMs?: number;
17
17
  /** When set, routes requests through the managed proxy at this base URL. */
18
18
  managedBaseUrl?: string;
19
+ /** Vertex AI project placeholder (used with managed proxy). */
20
+ vertexProject?: string;
21
+ /** Vertex AI location placeholder (used with managed proxy). */
22
+ vertexLocation?: string;
19
23
  }
20
24
 
21
25
  export class GeminiProvider implements Provider {
@@ -29,12 +33,17 @@ export class GeminiProvider implements Provider {
29
33
  model: string,
30
34
  options: GeminiProviderOptions = {},
31
35
  ) {
32
- this.client = new GoogleGenAI({
33
- apiKey,
34
- ...(options.managedBaseUrl
35
- ? { httpOptions: { baseUrl: options.managedBaseUrl } }
36
- : {}),
37
- });
36
+ this.client = options.managedBaseUrl
37
+ ? new GoogleGenAI({
38
+ vertexai: true,
39
+ project: options.vertexProject ?? "proxy",
40
+ location: options.vertexLocation ?? "us-central1",
41
+ httpOptions: {
42
+ baseUrl: options.managedBaseUrl,
43
+ headers: { Authorization: `Bearer ${apiKey}` },
44
+ },
45
+ })
46
+ : new GoogleGenAI({ apiKey });
38
47
  this.model = model;
39
48
  this.streamTimeoutMs = options.streamTimeoutMs ?? 300_000;
40
49
  }
@@ -29,12 +29,12 @@ export const MANAGED_PROVIDER_META: Record<string, ManagedProviderMeta> = {
29
29
  anthropic: {
30
30
  name: "anthropic",
31
31
  managed: true,
32
- proxyPath: "/v1/runtime-proxy/anthropic",
32
+ proxyPath: "/v1/runtime-proxy/vertex",
33
33
  },
34
34
  gemini: {
35
35
  name: "gemini",
36
36
  managed: true,
37
- proxyPath: "/v1/runtime-proxy/gemini",
37
+ proxyPath: "/v1/runtime-proxy/vertex",
38
38
  },
39
39
  fireworks: {
40
40
  name: "fireworks",