@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
package/ARCHITECTURE.md CHANGED
@@ -347,8 +347,8 @@ Both tokens are stored in the secure key store (macOS Keychain with encrypted fi
347
347
 
348
348
  | Secure key | Content |
349
349
  | ------------------------------------ | -------------------------------------------------------------------------- |
350
- | `credential:slack_channel:bot_token` | Slack bot token (used for `chat.postMessage` and `auth.test`) |
351
- | `credential:slack_channel:app_token` | Slack app token (`xapp-...`, used for Socket Mode `apps.connections.open`) |
350
+ | `credential/slack_channel/bot_token` | Slack bot token (used for `chat.postMessage` and `auth.test`) |
351
+ | `credential/slack_channel/app_token` | Slack app token (`xapp-...`, used for Socket Mode `apps.connections.open`) |
352
352
 
353
353
  Workspace metadata (team ID, team name, bot user ID, bot username) is stored as JSON in the credential metadata store under `('slack_channel', 'bot_token')`.
354
354
 
@@ -468,7 +468,7 @@ A complementary access-granting flow where the guardian proactively creates a sh
468
468
 
469
469
  | Channel | Status | Prerequisites |
470
470
  | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
471
- | Telegram | Shipped | Bot username resolved from credential metadata or `TELEGRAM_BOT_USERNAME` env |
471
+ | Telegram | Shipped | Bot username resolved from credential metadata or config |
472
472
  | Voice | Shipped | Identity-bound voice code redemption via DTMF/speech in the relay state machine. Always-on canonical behavior with personalized friend/guardian name prompts. |
473
473
  | Slack | Deferred | Needs DM-safe ingress — Socket Mode handles channel messages but DM-initiated invite flows need routing |
474
474
 
@@ -641,7 +641,7 @@ The assistant feature-flag resolver (`src/config/assistant-feature-flags.ts`) is
641
641
  | **3. `skill_load` tool** | `executeSkillLoad()` in `tools/skills/load.ts` | If the model attempts to load a flagged-off skill by name, the tool returns an error: `"skill is currently unavailable (disabled by feature flag)"`. |
642
642
  | **4. Runtime tool projection** | `projectSkillTools()` in `daemon/session-skill-tools.ts` | Even if a skill was previously active in a session (has `<loaded_skill>` markers in history), the per-turn projection drops it when the flag is OFF. Already-registered tools are unregistered. |
643
643
  | **5. Included child skills** | `executeSkillLoad()` in `tools/skills/load.ts` | When a parent skill includes children via the `includes` directive, each child is independently checked against its feature flag. Flagged-off children are silently excluded from the loaded skill content. |
644
- | **6. Skill install gate** | `handleInstallSkill()` in `daemon/handlers/skills.ts` | When a client requests skill installation, the handler checks the skill's feature flag before proceeding. If the flag is OFF, the install is rejected with an error. |
644
+ | **6. Skill install gate** | `handleSkillsInstall()` in `daemon/handlers/skills.ts` | When a client requests skill installation, the handler checks the skill's feature flag before proceeding. If the flag is OFF, the install is rejected with an error. |
645
645
 
646
646
  All six enforcement points derive the flag key via `skillFlagKey(skill)` — which returns `undefined` for ungated skills, short-circuiting the check — and then call `isAssistantFeatureFlagEnabled(flagKey, config)` for consistency.
647
647
 
@@ -657,7 +657,7 @@ All six enforcement points derive the flag key via `skillFlagKey(skill)` — whi
657
657
  | `src/tools/skills/load.ts` | `executeSkillLoad()` — enforcement points 3 and 5 |
658
658
  | `src/daemon/session-skill-tools.ts` | `projectSkillTools()` — enforcement point 4 |
659
659
  | `src/config/schema.ts` | `assistantFeatureFlagValues` field definition in `AssistantConfig` (Zod schema) |
660
- | `src/daemon/handlers/skills.ts` | `handleSkillsList()` — uses `resolveSkillStates()` for client responses; `handleInstallSkill()` — enforcement point 6 |
660
+ | `src/daemon/handlers/skills.ts` | `handleSkillsList()` — uses `resolveSkillStates()` for client responses; `handleSkillsInstall()` — enforcement point 6 |
661
661
  | `meta/feature-flags/feature-flag-registry.json` | Unified feature flag registry (repo root) — all declared flags with scope, label, default values, and descriptions |
662
662
  | `src/config/feature-flag-registry.json` | Bundled copy of the unified registry for compiled binary resolution |
663
663
 
@@ -669,7 +669,7 @@ All six enforcement points derive the flag key via `skillFlagKey(skill)` — whi
669
669
  graph LR
670
670
  subgraph "macOS Keychain"
671
671
  K1["API Key<br/>service: vellum-assistant<br/>account: anthropic<br/>stored via /usr/bin/security CLI"]
672
- K2["Credential Secrets<br/>key: credential:{service}:{field}<br/>stored via secure-keys.ts<br/>(encrypted file fallback if Keychain unavailable)"]
672
+ K2["Credential Secrets<br/>key: credential/{service}/{field}<br/>stored via secure-keys.ts<br/>(encrypted file fallback if Keychain unavailable)"]
673
673
  end
674
674
 
675
675
  subgraph "UserDefaults (plist)"
@@ -1709,7 +1709,7 @@ graph TB
1709
1709
  end
1710
1710
 
1711
1711
  subgraph "SSE Transport"
1712
- SSE_ROUTE["SSE Route<br/>GET /v1/events?conversationKey=...<br/>(events-routes.ts)<br/>──────────────────────<br/>ReadableStream + CountQueuingStrategy(16)<br/>Heartbeat every 30 s<br/>Slow-consumer shed"]
1712
+ SSE_ROUTE["SSE Route<br/>GET /v1/events[?conversationKey=...]<br/>(events-routes.ts)<br/>──────────────────────<br/>ReadableStream + CountQueuingStrategy(16)<br/>Heartbeat every 30 s<br/>Slow-consumer shed"]
1713
1713
  end
1714
1714
 
1715
1715
  subgraph "Clients"
package/README.md CHANGED
@@ -73,7 +73,6 @@ For low-level development (e.g., working on the assistant runtime itself):
73
73
  ```bash
74
74
  bun run src/index.ts daemon start # start daemon only
75
75
  bun run src/index.ts # interactive CLI session
76
- bun run src/index.ts dev # dev mode (auto-restart on file changes)
77
76
  ```
78
77
 
79
78
  ### CLI commands
@@ -84,7 +83,6 @@ bun run src/index.ts dev # dev mode (auto-restart on file changes)
84
83
  | `vellum sleep` | Stop assistant + gateway processes |
85
84
  | `vellum ps` | List assistants and per-assistant process status |
86
85
  | `assistant` | Launch interactive CLI session |
87
- | `assistant dev` | Run assistant with auto-restart on file changes |
88
86
  | `assistant sessions list\|new\|export\|clear` | Manage conversation sessions |
89
87
  | `assistant config set\|get\|list` | Manage configuration |
90
88
  | `assistant keys set\|list\|delete` | Manage API keys in secure storage |
@@ -241,12 +239,9 @@ The per-assistant mapping is propagated to the gateway via the config file watch
241
239
 
242
240
  ### Phone Number Resolution Order
243
241
 
244
- At runtime, `getTwilioConfig()` resolves the phone number using this priority chain:
242
+ At runtime, `getTwilioConfig()` resolves the phone number from **`twilio.phoneNumber` in config** — the primary source of truth, written by `provision_number` and `assign_number`.
245
243
 
246
- 1. **`TWILIO_PHONE_NUMBER` env var** — highest priority, explicit override for dev/CI.
247
- 2. **`twilio.phoneNumber` in config** — the primary source of truth, written by `provision_number` and `assign_number`.
248
-
249
- If no number is found after both sources, an error is thrown.
244
+ If no number is found, an error is thrown.
250
245
 
251
246
  ### Assistant-Scoped Guardian State
252
247
 
@@ -476,22 +471,6 @@ docker run --rm -p 3001:3001 \
476
471
 
477
472
  The image exposes port `3001` and bundles the `assistant` CLI binary.
478
473
 
479
- ## Ride Shotgun
480
-
481
- Ride Shotgun is a background screen-watching feature that observes user workflows. It has two modes:
482
-
483
- - **Observe mode** — captures periodic screenshots and generates a workflow summary via the LLM.
484
- - **Learn mode** — records browser network traffic alongside screenshots to capture API patterns. The assistant owns CDP browser lifecycle: `ride-shotgun-handler.ts` calls `ensureChromeWithCdp()` to launch or connect to Chrome with remote debugging, so clients do not need to pre-launch Chrome with `--remote-debugging-port`.
485
-
486
- Key modules:
487
-
488
- | File | Purpose |
489
- | --------------------------------------- | ------------------------------------------------------- |
490
- | `src/daemon/ride-shotgun-handler.ts` | Session orchestration, CDP bootstrap, network recording |
491
- | `src/tools/browser/chrome-cdp.ts` | Reusable Chrome CDP launcher (`ensureChromeWithCdp`) |
492
- | `src/tools/browser/network-recorder.ts` | CDP-based network traffic capture |
493
- | `src/tools/browser/recording-store.ts` | Session recording persistence |
494
-
495
474
  ## Troubleshooting
496
475
 
497
476
  ### Guardian and gateway-origin issues
@@ -28,7 +28,7 @@ graph TB
28
28
  DRAFT["messaging_draft"]
29
29
  SENDER_DIGEST["messaging_sender_digest"]
30
30
  ARCHIVE_BY_SENDER["messaging_archive_by_sender"]
31
- SHARED["shared.ts<br/>resolveProvider + withProviderToken"]
31
+ SHARED["shared.ts<br/>resolveProvider + getProviderConnection"]
32
32
  end
33
33
 
34
34
  subgraph "Gmail Skill (bundled-skills/gmail/)"
@@ -121,7 +121,8 @@ sequenceDiagram
121
121
  participant OAuth as OAuth2 PKCE Flow
122
122
  participant Browser as System Browser
123
123
  participant Google as Google OAuth Server
124
- participant Vault as Credential Vault
124
+ participant Store as SQLite OAuth Store
125
+ participant Vault as Secure Keychain
125
126
  participant TokenMgr as TokenManager
126
127
  participant Tool as Gmail Tool Executor
127
128
  participant API as Gmail REST API
@@ -140,20 +141,25 @@ sequenceDiagram
140
141
  Google->>OAuth: callback with auth code
141
142
  OAuth->>Google: exchange code + code_verifier for tokens
142
143
  Google-->>OAuth: access + refresh tokens
143
- OAuth->>Vault: setSecureKey (access + refresh)
144
- OAuth->>Vault: upsertCredentialMetadata (allowedTools, expiresAt)
144
+ OAuth->>Store: storeOAuth2Tokens() → upsert oauth_app + oauth_connection rows
145
+ Store->>Vault: setSecureKeyAsync("oauth_connection/{id}/access_token")
146
+ Store->>Vault: setSecureKeyAsync("oauth_connection/{id}/refresh_token")
147
+ Store->>Store: write expiresAt, grantedScopes to oauth_connections
145
148
  OAuth-->>Handler: success + account email
146
149
  Handler->>HTTP: integration_connect_result {success, accountInfo}
147
150
  HTTP->>UI: show connected state
148
151
 
149
152
  Note over UI,API: Tool Execution Flow
150
153
  Tool->>TokenMgr: withValidToken("gmail", callback)
151
- TokenMgr->>Vault: getSecureKey("integration:gmail:access_token")
152
- TokenMgr->>Vault: getMetadata (check expiresAt)
154
+ TokenMgr->>Store: getConnectionByProvider("integration:gmail")
155
+ TokenMgr->>Vault: getSecureKey("oauth_connection/{conn.id}/access_token")
156
+ TokenMgr->>Store: check oauth_connections.expires_at
153
157
  alt Token expired
158
+ TokenMgr->>Store: resolveRefreshConfig() → tokenUrl, clientId from provider/app rows
154
159
  TokenMgr->>Google: refresh with refresh_token
155
160
  Google-->>TokenMgr: new access token
156
- TokenMgr->>Vault: update access token + expiresAt
161
+ TokenMgr->>Vault: setSecureKeyAsync("oauth_connection/{id}/access_token")
162
+ TokenMgr->>Store: updateConnection(expiresAt)
157
163
  end
158
164
  TokenMgr->>Tool: callback(validToken)
159
165
  Tool->>API: Gmail REST API call with Bearer token
@@ -167,13 +173,13 @@ sequenceDiagram
167
173
 
168
174
  | Decision | Rationale |
169
175
  | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
170
- | PKCE by default, optional client_secret | Desktop apps prefer PKCE; some providers (Slack) require a secret, which is stored in credential metadata for autonomous refresh |
176
+ | PKCE by default, optional client_secret | Desktop apps prefer PKCE; some providers (Slack) require a secret, which is stored in the secure keychain (`oauth_app/{id}/client_secret`) for autonomous refresh |
171
177
  | Shared connect orchestrator | All OAuth providers route through `orchestrateOAuthConnect()`, which resolves profiles, enforces scope policy, runs the flow, stores tokens, and verifies identity. Adding a provider is a declarative profile entry, not new orchestration code |
172
178
  | Canonical credential naming | All reads and writes use `client_id`/`client_secret` as canonical field names |
173
179
  | Gateway callback transport | OAuth callbacks are now routed through the gateway at `${ingress.publicBaseUrl}/webhooks/oauth/callback` instead of a loopback redirect URI. This enables OAuth flows to work in remote and tunneled deployments. |
174
180
  | Unified `MessagingProvider` interface | All platforms implement the same contract; generic tools work immediately for new providers |
175
181
  | Provider auto-selection | If only one provider is connected, tools skip the `platform` parameter — seamless single-platform UX |
176
- | Token expiry in credential metadata | Reuses existing `CredentialMetadata` store; `expiresAt` field enables proactive refresh with 5min buffer |
182
+ | Token expiry in SQLite oauth-store | `oauth_connections.expires_at` column tracks token expiry; `TokenManager` reads it for proactive refresh with 5min buffer. No separate metadata store needed |
177
183
  | Confidence scores on medium-risk tools | LLM self-reports confidence (0-1); enables future trust calibration without blocking execution |
178
184
  | Platform-specific extension tools | Operations unique to one platform (e.g. Gmail labels, Slack reactions) are separate tools, not forced into the generic interface |
179
185
  | Identity verification before token storage | OAuth2 tokens are only persisted after a successful identity verification call, preventing storage of invalid or mismatched credentials |
@@ -197,34 +203,32 @@ sequenceDiagram
197
203
  | `assistant/src/watcher/providers/gmail.ts` | Gmail watcher using History API |
198
204
  | `assistant/src/watcher/providers/github.ts` | GitHub watcher for PRs, issues, review requests, and mentions |
199
205
  | `assistant/src/watcher/providers/linear.ts` | Linear watcher for assigned issues, status changes, and @mentions |
200
- | `assistant/src/oauth/provider-profiles.ts` | Provider profile registry: auth URLs, token URLs, scopes, policies, identity verifiers |
206
+ | `assistant/src/oauth/provider-behaviors.ts` | Provider behavior registry: identity verifiers, setup metadata, injection templates |
201
207
  | `assistant/src/oauth/connect-orchestrator.ts` | Shared OAuth connect orchestrator: profile resolution, scope policy, flow execution, token storage |
202
208
  | `assistant/src/oauth/scope-policy.ts` | Deterministic scope resolution and policy enforcement |
203
- | `assistant/src/oauth/connect-types.ts` | Shared types: `OAuthProviderProfile`, `OAuthScopePolicy`, `OAuthConnectResult` |
209
+ | `assistant/src/oauth/connect-types.ts` | Shared types: `OAuthProviderBehavior`, `OAuthScopePolicy`, `OAuthConnectResult` |
204
210
  | `assistant/src/oauth/token-persistence.ts` | Token storage helper: persists tokens, metadata, and runs post-connect hooks |
205
211
  | `assistant/src/daemon/handlers/oauth-connect.ts` | Generic OAuth connect handler (`oauth_connect_start` / `oauth_connect_result`) |
206
212
 
207
213
  ---
208
214
 
209
- ## OAuth Extensibility — Provider Profiles, Scope Policy, and Connect Orchestrator
215
+ ## OAuth Extensibility — Provider Behaviors, Scope Policy, and Connect Orchestrator
210
216
 
211
- The OAuth extensibility layer makes adding a new OAuth provider a declarative operation. Instead of writing custom auth handlers, new providers are added as entries in the **provider profile registry**. The shared **connect orchestrator** handles the full flow from profile resolution through token storage.
217
+ The OAuth extensibility layer makes adding a new OAuth provider a declarative operation. Protocol fields (auth URLs, token URLs, scopes, scope policy) are stored in the `oauth_providers` database table, while behavioral fields (identity verifiers, setup metadata, injection templates) live in the **provider behavior registry**. The shared **connect orchestrator** handles the full flow from provider resolution through token storage.
212
218
 
213
- ### Provider Profile Registry
219
+ ### Provider Behavior Registry
214
220
 
215
- `assistant/src/oauth/provider-profiles.ts` contains the `PROVIDER_PROFILES` map — a canonical registry of well-known OAuth providers. Each profile (`OAuthProviderProfile`) declares:
221
+ `assistant/src/oauth/provider-behaviors.ts` contains the `PROVIDER_BEHAVIORS` map — a registry of behavioral aspects for well-known OAuth providers. Each behavior (`OAuthProviderBehavior`) declares:
216
222
 
217
- | Field | Purpose |
218
- | ---------------------- | ------------------------------------------------------------------------------------------------------ |
219
- | `authUrl` / `tokenUrl` | OAuth2 authorization and token endpoints |
220
- | `defaultScopes` | Scopes requested on every connect attempt |
221
- | `scopePolicy` | Controls whether additional scopes are allowed (see Scope Policy below) |
222
- | `callbackTransport` | `'loopback'` (local redirect) or `'gateway'` (public ingress) |
223
- | `identityVerifier` | Async function that fetches human-readable account info (e.g. `@username`, email) after token exchange |
224
- | `setup` | Optional metadata for the generic OAuth setup skill (display name, dashboard URL, app type) |
225
- | `injectionTemplates` | Auto-applied credential injection rules for the script proxy |
223
+ | Field | Purpose |
224
+ | -------------------- | ------------------------------------------------------------------------------------------------------ |
225
+ | `identityVerifier` | Async function that fetches human-readable account info (e.g. `@username`, email) after token exchange |
226
+ | `setup` | Optional metadata for the generic OAuth setup skill (display name, dashboard URL, app type) |
227
+ | `injectionTemplates` | Auto-applied credential injection rules for the script proxy |
226
228
 
227
- Registered providers: `integration:gmail`, `integration:slack`, `integration:notion`. Short aliases (e.g. `gmail`, `slack`) are resolved via `SERVICE_ALIASES`.
229
+ Protocol fields (`authUrl`, `tokenUrl`, `defaultScopes`, `scopePolicy`, `callbackTransport`) are stored in the `oauth_providers` database table rather than in code.
230
+
231
+ Registered providers: `integration:gmail`, `integration:slack`, `integration:notion`. Short aliases (e.g. `gmail`, `slack`) are resolved via `resolveService()`.
228
232
 
229
233
  ### Scope Policy Engine
230
234
 
@@ -244,9 +248,9 @@ Returns `{ ok: true, scopes }` or `{ ok: false, error, allowedScopes }`.
244
248
  `assistant/src/oauth/connect-orchestrator.ts` exports `orchestrateOAuthConnect(options)`, which runs the full OAuth2 flow:
245
249
 
246
250
  1. **Resolve service** — alias expansion via `resolveService()`.
247
- 2. **Load profile** — `getProviderProfile()` from the registry.
251
+ 2. **Load behavior** — `getProviderBehavior()` from the registry; load protocol fields from the `oauth_providers` DB table.
248
252
  3. **Compute scopes** — `resolveScopes()` with scope policy enforcement.
249
- 4. **Build OAuth config** — merge profile defaults with caller overrides.
253
+ 4. **Build OAuth config** — assemble protocol-level config from the DB provider row.
250
254
  5. **Run flow** — interactive (opens browser, blocks until completion) or deferred (returns auth URL for the caller to deliver).
251
255
  6. **Verify identity** — runs the profile's `identityVerifier` if defined.
252
256
  7. **Store tokens** — `storeOAuth2Tokens()` persists access/refresh tokens, client credentials, and metadata.
@@ -266,24 +270,24 @@ This replaces provider-specific handlers — any provider in the registry can be
266
270
 
267
271
  ### Adding a New OAuth Provider
268
272
 
269
- 1. **Declare a profile** in `PROVIDER_PROFILES` (`oauth/provider-profiles.ts`):
273
+ 1. **Register protocol fields** in the `oauth_providers` database table (via CLI or migration):
270
274
  - Set `authUrl`, `tokenUrl`, `defaultScopes`, `scopePolicy`, and `callbackTransport`.
271
- - Add a `SERVICE_ALIASES` entry if a shorthand name is desired.
272
- 2. **Optional: add an identity verifier** — an async function on the profile that fetches the user's account info from the provider's API.
273
- 3. **Optional: add setup metadata** — `setup.displayName`, `setup.dashboardUrl`, `setup.appType` enable the generic OAuth setup skill to guide users through app creation.
274
- 4. **Optional: add injection templates** — for providers whose tokens should be auto-injected by the script proxy.
275
- 5. **No handler code needed** — the generic `oauth_connect_start` handler and the connect orchestrator handle the flow automatically.
275
+ 2. **Optional: declare behavioral fields** in `PROVIDER_BEHAVIORS` (`oauth/provider-behaviors.ts`):
276
+ - Add an `identityVerifier` — an async function that fetches the user's account info from the provider's API.
277
+ - Add `setup` metadata — `displayName`, `dashboardUrl`, `appType` enable the generic OAuth setup skill to guide users through app creation.
278
+ - Add `injectionTemplates` — for providers whose tokens should be auto-injected by the script proxy.
279
+ 3. **No handler code needed** — the generic `oauth_connect_start` handler and the connect orchestrator handle the flow automatically.
276
280
 
277
281
  ### Key Source Files
278
282
 
279
- | File | Role |
280
- | ------------------------------------------------ | ------------------------------------------------------------------------------- |
281
- | `assistant/src/oauth/provider-profiles.ts` | Provider profile registry and alias resolution |
282
- | `assistant/src/oauth/scope-policy.ts` | Scope resolution and policy enforcement (pure, no I/O) |
283
- | `assistant/src/oauth/connect-orchestrator.ts` | Shared connect orchestrator (profile → scopes → flow → tokens) |
284
- | `assistant/src/oauth/connect-types.ts` | Shared types (`OAuthProviderProfile`, `OAuthScopePolicy`, `OAuthConnectResult`) |
285
- | `assistant/src/oauth/token-persistence.ts` | Token storage: keychain writes, metadata upsert, post-connect hooks |
286
- | `assistant/src/daemon/handlers/oauth-connect.ts` | Generic `oauth_connect_start` / `oauth_connect_result` handler |
283
+ | File | Role |
284
+ | ------------------------------------------------ | -------------------------------------------------------------------------------- |
285
+ | `assistant/src/oauth/provider-behaviors.ts` | Provider behavior registry and alias resolution |
286
+ | `assistant/src/oauth/scope-policy.ts` | Scope resolution and policy enforcement (pure, no I/O) |
287
+ | `assistant/src/oauth/connect-orchestrator.ts` | Shared connect orchestrator (profile → scopes → flow → tokens) |
288
+ | `assistant/src/oauth/connect-types.ts` | Shared types (`OAuthProviderBehavior`, `OAuthScopePolicy`, `OAuthConnectResult`) |
289
+ | `assistant/src/oauth/token-persistence.ts` | Token storage: keychain writes, metadata upsert, post-connect hooks |
290
+ | `assistant/src/daemon/handlers/oauth-connect.ts` | Generic `oauth_connect_start` / `oauth_connect_result` handler |
287
291
 
288
292
  ---
289
293
 
@@ -66,7 +66,7 @@ graph LR
66
66
  ### Transport
67
67
 
68
68
  - Unix domain socket: `~/.vellum/keychain-broker.sock`
69
- - Socket path is passed to daemon/gateway via `VELLUM_KEYCHAIN_BROKER_SOCKET` environment variable
69
+ - Socket path is derived from the data directory (e.g., `join(getRootDir(), "keychain-broker.sock")`)
70
70
  - Newline-delimited JSON (`\n` as message boundary)
71
71
 
72
72
  ### Request envelope
@@ -157,8 +157,8 @@ XPC provides stronger caller identity guarantees via audit tokens and code requi
157
157
 
158
158
  ## Developer Experience
159
159
 
160
- - **Debug builds:** The `#if !DEBUG` guard compiles out the entire `KeychainBrokerServer`. The `VELLUM_KEYCHAIN_BROKER_SOCKET` env var is not set, so clients see the broker as unavailable and use the encrypted store. Developers never encounter keychain prompts during the edit-build-run cycle.
161
- - **Release builds:** The broker starts automatically with the app. The daemon discovers it via the socket env var and token file. No configuration needed.
160
+ - **Debug builds:** The `#if !DEBUG` guard compiles out the entire `KeychainBrokerServer`. The broker socket is not created, so clients see the broker as unavailable and use the encrypted store. Developers never encounter keychain prompts during the edit-build-run cycle.
161
+ - **Release builds:** The broker starts automatically with the app. The daemon discovers the broker via the derived socket path (`join(getRootDir(), "keychain-broker.sock")`) and token file. No configuration needed.
162
162
  - **CLI-only / headless:** No macOS app means no broker socket. All storage uses the encrypted file store. This is the expected path for CI, servers, and non-macOS platforms.
163
163
 
164
164
  ## Callsite Policy
@@ -179,7 +179,7 @@ File tool candidates include canonical (symlink-resolved) absolute paths via `no
179
179
  | `assistant/src/permissions/checker.ts` | `classifyRisk()`, `check()`, `buildCommandCandidates()`, allowlist/scope generation |
180
180
  | `assistant/src/permissions/shell-identity.ts` | `analyzeShellCommand()`, `deriveShellActionKeys()`, `buildShellCommandCandidates()`, `buildShellAllowlistOptions()` — parser-based shell command identity and action key derivation |
181
181
  | `assistant/src/permissions/trust-store.ts` | Rule persistence, `findHighestPriorityRule()`, execution-target matching, starter bundle |
182
- | `assistant/src/permissions/prompter.ts` | HTTP prompt flow: `confirmation_request` → `confirmation_response` |
182
+ | `assistant/src/permissions/prompter.ts` | HTTP prompt flow: `confirmation_request` → `confirmation_response` |
183
183
  | `assistant/src/permissions/defaults.ts` | Default rule templates (system ask rules for host tools, CU, etc.) |
184
184
  | `assistant/src/skills/version-hash.ts` | `computeSkillVersionHash()` — deterministic SHA-256 of skill source files |
185
185
  | `assistant/src/skills/path-classifier.ts` | `isSkillSourcePath()`, `normalizeFilePath()`, skill root detection |
@@ -233,7 +233,7 @@ sequenceDiagram
233
233
  UI->>HTTP: secret_response {requestId, value, delivery: "store"}
234
234
  HTTP->>Prompter: resolve(value, "store")
235
235
  Prompter->>Vault: {value, delivery: "store"}
236
- Vault->>Keychain: setSecureKey("credential:svc:field", value)
236
+ Vault->>Keychain: setSecureKey("credential/svc/field", value)
237
237
  Vault->>Model: "Credential stored securely" (no value in output)
238
238
  else One-Time Send (if enabled)
239
239
  UI->>HTTP: secret_response {requestId, value, delivery: "transient_send"}
@@ -272,7 +272,7 @@ graph TB
272
272
  TOOL["Tool (e.g. browser_fill_credential)"] --> BROKER["CredentialBroker.use(service, field, tool, domain)"]
273
273
  BROKER --> POLICY{"Check policy:<br/>allowedTools + allowedDomains"}
274
274
  POLICY -->|denied| REJECT["PolicyDenied error"]
275
- POLICY -->|allowed| FETCH["getSecureKey(credential:svc:field)"]
275
+ POLICY -->|allowed| FETCH["getSecureKey(credential/svc/field)"]
276
276
  FETCH --> INJECT["Inject value into tool execution<br/>(never returned to model)"]
277
277
  ```
278
278
 
@@ -290,7 +290,7 @@ The `allowOneTimeSend` config gate (default: `false`) enables a secondary "Send
290
290
 
291
291
  | Component | Location | What it stores |
292
292
  | ------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
293
- | Secret values | macOS Keychain (primary) or encrypted file fallback | Encrypted credential values keyed as `credential:{service}:{field}`. Falls back to encrypted file backend on Linux/headless or when Keychain is unavailable. |
293
+ | Secret values | macOS Keychain (primary) or encrypted file fallback | Encrypted credential values keyed as `credential/{service}/{field}`. Falls back to encrypted file backend on Linux/headless or when Keychain is unavailable. |
294
294
  | Credential metadata | `~/.vellum/workspace/data/credentials/metadata.json` | Service, field, label, policy (allowedTools, allowedDomains), timestamps |
295
295
  | Config | `~/.vellum/workspace/config.*` | `secretDetection` settings: enabled, action, entropyThreshold, allowOneTimeSend |
296
296
 
@@ -303,7 +303,7 @@ The `allowOneTimeSend` config gate (default: `false`) enables a secondary "Send
303
303
  | `assistant/src/tools/credentials/metadata-store.ts` | JSON file metadata CRUD for credential records |
304
304
  | `assistant/src/tools/credentials/broker.ts` | Brokered credential access with policy enforcement and transient send |
305
305
  | `assistant/src/tools/credentials/policy-validate.ts` | Policy input validation (allowedTools, allowedDomains) |
306
- | `assistant/src/permissions/secret-prompter.ts` | HTTP secret_request/secret_response flow |
306
+ | `assistant/src/permissions/secret-prompter.ts` | HTTP secret_request/secret_response flow |
307
307
  | `assistant/src/security/secret-scanner.ts` | Regex + entropy-based secret detection |
308
308
  | `assistant/src/security/secret-ingress.ts` | Inbound message secret blocking |
309
309
  | `clients/macos/.../SecretPromptManager.swift` | Floating panel UI for secure credential entry |
@@ -1,17 +1,12 @@
1
1
  # Trusted Contacts — Operator Runbook
2
2
 
3
- Operational procedures for inspecting, managing, and debugging the trusted contact access flow. HTTP commands use the assistant runtime API (default `http://localhost:7821`) with bearer authentication.
4
-
5
- > **Note:** The `/v1/contacts` endpoints are served by the assistant runtime, not
6
- > the gateway. If you prefer to route through the gateway (`localhost:7830`), set
7
- > `GATEWAY_RUNTIME_PROXY_ENABLED=true` in the gateway environment — the proxy is
8
- > disabled by default and these routes will 404 without it.
3
+ Operational procedures for inspecting, managing, and debugging the trusted contact access flow. HTTP commands use the gateway API (default `http://localhost:7830`) with bearer authentication.
9
4
 
10
5
  ## Prerequisites
11
6
 
12
7
  ```bash
13
- # Base URL — assistant runtime (adjust if using a non-default port)
14
- BASE=http://localhost:7821
8
+ # Base URL — gateway (adjust if using a non-default port)
9
+ BASE=http://localhost:7830
15
10
 
16
11
  # Bearer token: for operator use, retrieve from the daemon process environment
17
12
  # or use `assistant` CLI commands which handle auth automatically.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "debug-prompt-logger",
3
- "description": "Logs system prompt and conversation history to stderr when VELLUM_DEBUG=1",
3
+ "description": "Logs system prompt and conversation history to stderr before each LLM call",
4
4
  "version": "1.0.0",
5
5
  "events": ["pre-llm-call"],
6
6
  "script": "run.sh"
@@ -1,8 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # Debug Prompt Logger — prints the system prompt and conversation
3
- # history before each LLM call. Only active when VELLUM_DEBUG=1.
4
-
5
- [ "$VELLUM_DEBUG" != "1" ] && exit 0
3
+ # history before each LLM call. Runs whenever the hook is installed.
6
4
 
7
5
  data=$(cat)
8
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.4.46",
3
+ "version": "0.4.49",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
@@ -41,7 +41,6 @@ mock.module("../config/env.js", () => ({
41
41
  getGatewayBaseUrl: () => "http://localhost:7822",
42
42
  getRuntimeGatewayOriginSecret: () => undefined,
43
43
  isHttpAuthDisabledWithoutSafetyGate: () => false,
44
- getEnableMonitoring: () => false,
45
44
  checkUnrecognizedEnvVars: () => {},
46
45
  getBaseDataDir: () => testDir,
47
46
  }));
@@ -666,6 +666,162 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
666
666
  ).toBe(true);
667
667
  });
668
668
 
669
+ // -----------------------------------------------------------------------
670
+ // ensureToolPairing — server_tool_use / web_search_tool_result pairing
671
+ // -----------------------------------------------------------------------
672
+
673
+ test("server_tool_use with missing web_search_tool_result gets synthetic result injected", async () => {
674
+ const messages: Message[] = [
675
+ userMsg("Search for something"),
676
+ {
677
+ role: "assistant",
678
+ content: [
679
+ {
680
+ type: "server_tool_use",
681
+ id: "srvtoolu_abc123",
682
+ name: "web_search",
683
+ input: { query: "test" },
684
+ },
685
+ ],
686
+ },
687
+ userMsg("Thanks"), // user text but no web_search_tool_result
688
+ ];
689
+ await provider.sendMessage(messages);
690
+
691
+ const sent = lastStreamParams!.messages as Array<{
692
+ role: string;
693
+ content: Array<{
694
+ type: string;
695
+ tool_use_id?: string;
696
+ content?: unknown;
697
+ }>;
698
+ }>;
699
+
700
+ // The user message after assistant should contain a synthetic web_search_tool_result
701
+ const userAfterAssistant = sent[2];
702
+ expect(userAfterAssistant.role).toBe("user");
703
+ expect(userAfterAssistant.content[0].type).toBe("web_search_tool_result");
704
+ expect(userAfterAssistant.content[0].tool_use_id).toBe("srvtoolu_abc123");
705
+ });
706
+
707
+ test("server_tool_use at end of messages gets synthetic web_search_tool_result appended", async () => {
708
+ const messages: Message[] = [
709
+ userMsg("Search something"),
710
+ {
711
+ role: "assistant",
712
+ content: [
713
+ {
714
+ type: "server_tool_use",
715
+ id: "srvtoolu_end",
716
+ name: "web_search",
717
+ input: { query: "test" },
718
+ },
719
+ ],
720
+ },
721
+ ];
722
+ await provider.sendMessage(messages);
723
+
724
+ const sent = lastStreamParams!.messages as Array<{
725
+ role: string;
726
+ content: Array<{ type: string; tool_use_id?: string }>;
727
+ }>;
728
+
729
+ // A synthetic user message should have been appended
730
+ expect(sent).toHaveLength(3);
731
+ expect(sent[2].role).toBe("user");
732
+ expect(sent[2].content[0].type).toBe("web_search_tool_result");
733
+ expect(sent[2].content[0].tool_use_id).toBe("srvtoolu_end");
734
+ });
735
+
736
+ test("server_tool_use with matching web_search_tool_result passes through unchanged", async () => {
737
+ const messages: Message[] = [
738
+ userMsg("Search something"),
739
+ {
740
+ role: "assistant",
741
+ content: [
742
+ {
743
+ type: "server_tool_use",
744
+ id: "srvtoolu_ok",
745
+ name: "web_search",
746
+ input: { query: "test" },
747
+ },
748
+ ],
749
+ },
750
+ {
751
+ role: "user",
752
+ content: [
753
+ {
754
+ type: "web_search_tool_result",
755
+ tool_use_id: "srvtoolu_ok",
756
+ content: [
757
+ {
758
+ type: "web_search_result",
759
+ url: "https://example.com",
760
+ title: "Example",
761
+ encrypted_content: "enc_abc",
762
+ },
763
+ ],
764
+ },
765
+ ],
766
+ },
767
+ ];
768
+ await provider.sendMessage(messages);
769
+
770
+ const sent = lastStreamParams!.messages as Array<{
771
+ role: string;
772
+ content: Array<{ type: string; tool_use_id?: string }>;
773
+ }>;
774
+
775
+ // No synthetic messages or blocks added
776
+ expect(sent).toHaveLength(3);
777
+ const resultBlocks = sent[2].content.filter(
778
+ (b) => b.type === "web_search_tool_result",
779
+ );
780
+ expect(resultBlocks).toHaveLength(1);
781
+ expect(resultBlocks[0].tool_use_id).toBe("srvtoolu_ok");
782
+ });
783
+
784
+ test("mixed tool_use and server_tool_use with partial results gets missing ones filled", async () => {
785
+ const messages: Message[] = [
786
+ userMsg("Do things"),
787
+ {
788
+ role: "assistant",
789
+ content: [
790
+ { type: "tool_use", id: "tu_a", name: "file_read", input: {} },
791
+ {
792
+ type: "server_tool_use",
793
+ id: "srvtoolu_b",
794
+ name: "web_search",
795
+ input: { query: "test" },
796
+ },
797
+ ],
798
+ },
799
+ // Only tu_a has a result
800
+ toolResultMsg("tu_a", "result A"),
801
+ ];
802
+ await provider.sendMessage(messages);
803
+
804
+ const sent = lastStreamParams!.messages as Array<{
805
+ role: string;
806
+ content: Array<{
807
+ type: string;
808
+ tool_use_id?: string;
809
+ }>;
810
+ }>;
811
+
812
+ const userAfterAssistant = sent[2];
813
+ // Should have tool_result for tu_a and synthetic web_search_tool_result for srvtoolu_b
814
+ expect(userAfterAssistant.content).toHaveLength(2);
815
+ expect(userAfterAssistant.content[0]).toMatchObject({
816
+ type: "tool_result",
817
+ tool_use_id: "tu_a",
818
+ });
819
+ expect(userAfterAssistant.content[1]).toMatchObject({
820
+ type: "web_search_tool_result",
821
+ tool_use_id: "srvtoolu_b",
822
+ });
823
+ });
824
+
669
825
  test("assistant message with only unknown blocks gets placeholder text", async () => {
670
826
  const messages: Message[] = [
671
827
  userMsg("Start"),