@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
@@ -37,10 +37,6 @@
37
37
  "type": "string",
38
38
  "enum": ["general", "researcher", "coder", "reviewer"],
39
39
  "description": "Worker profile that scopes tool access. Defaults to general (backward compatible)."
40
- },
41
- "reason": {
42
- "type": "string",
43
- "description": "Brief non-technical explanation of what you are delegating and why, shown to the user as a status update. Use simple language a non-technical person would understand."
44
40
  }
45
41
  }
46
42
  },
@@ -10,13 +10,10 @@ metadata:
10
10
  disable-model-invocation: true
11
11
  ---
12
12
 
13
- This skill provides the 12 computer*use*\* action tools for controlling
14
- the macOS desktop (used by CU sessions).
13
+ This skill provides the computer_use_* action tools for controlling
14
+ the macOS desktop. CU tools run through the main agent loop via HostCuProxy.
15
15
 
16
- The `computer_use_request_control` escalation tool is registered in the core
17
- registry (not this skill) so text_qa sessions can execute it directly.
18
-
19
- The skill is internally preactivated for computer-use sessions.
16
+ The skill is internally preactivated for sessions with a connected desktop client.
20
17
 
21
18
  Tools in this skill are proxy tools — execution is forwarded to the connected
22
19
  macOS client, never handled locally by the assistant.
@@ -1,9 +1,27 @@
1
1
  {
2
2
  "version": 1,
3
3
  "tools": [
4
+ {
5
+ "name": "computer_use_observe",
6
+ "description": "Capture the current screen state. Returns the accessibility tree with [ID] element references and optionally a screenshot.\n\nThe accessibility tree shows interactive elements like [3] AXButton 'Save' or [17] AXTextField 'Search'. Use element_id to target these elements in subsequent actions — this is much more reliable than pixel coordinates.\n\nCall this before your first computer use action, or to check screen state without acting.",
7
+ "category": "computer-use",
8
+ "risk": "low",
9
+ "input_schema": {
10
+ "type": "object",
11
+ "properties": {
12
+ "reason": {
13
+ "type": "string",
14
+ "description": "Brief non-technical explanation of why this tool is being called"
15
+ }
16
+ },
17
+ "required": ["reason"]
18
+ },
19
+ "executor": "tools/computer-use-observe.ts",
20
+ "execution_target": "host"
21
+ },
4
22
  {
5
23
  "name": "computer_use_click",
6
- "description": "Click on a UI element by its [ID] from the accessibility tree, or at raw screen coordinates as fallback. Supports single click, double-click, and right-click via the click_type parameter.",
24
+ "description": "Click an element on screen. Prefer element_id (from the accessibility tree) over x/y coordinates.",
7
25
  "category": "computer-use",
8
26
  "risk": "low",
9
27
  "input_schema": {
@@ -42,7 +60,7 @@
42
60
  },
43
61
  {
44
62
  "name": "computer_use_type_text",
45
- "description": "Type text at the current cursor position. The target field must already be focused (click it first).",
63
+ "description": "Type text at the current cursor position. First click a text field (by element_id) to focus it, then call this tool. If a field shows 'FOCUSED', skip the click.",
46
64
  "category": "computer-use",
47
65
  "risk": "low",
48
66
  "input_schema": {
@@ -235,7 +253,7 @@
235
253
  },
236
254
  {
237
255
  "name": "computer_use_run_applescript",
238
- "description": "Execute an AppleScript to control applications via Apple's scripting bridge. Use this for operations that are more reliable through scripting than UI interaction: setting a browser URL directly, navigating Finder to a path, querying app state (tab count, window titles, document status), or clicking deeply nested menu items. The script's return value (if any) will be reported back. NEVER use \"do shell script\" \u2014 it is blocked for security. Keep scripts short and targeted to a single operation.",
256
+ "description": "Run an AppleScript command. Prefer this over click/type when possible \u2014 it doesn't move the cursor or interrupt the user. Never use 'do shell script' inside AppleScript (blocked for security).",
239
257
  "category": "computer-use",
240
258
  "risk": "low",
241
259
  "input_schema": {
@@ -261,7 +279,7 @@
261
279
  },
262
280
  {
263
281
  "name": "computer_use_done",
264
- "description": "Task is complete",
282
+ "description": "Signal that the computer use task is complete. Provide a summary of what was accomplished. This ends the computer use session.",
265
283
  "category": "computer-use",
266
284
  "risk": "low",
267
285
  "input_schema": {
@@ -3,8 +3,7 @@ import {
3
3
  searchContacts,
4
4
  } from "../../../../messaging/providers/gmail/people-client.js";
5
5
  import type { Person } from "../../../../messaging/providers/gmail/people-types.js";
6
- import { getMessagingProvider } from "../../../../messaging/registry.js";
7
- import { withValidToken } from "../../../../security/token-manager.js";
6
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
8
7
  import type {
9
8
  ToolContext,
10
9
  ToolExecutionResult,
@@ -44,43 +43,41 @@ export async function run(
44
43
  }
45
44
 
46
45
  try {
47
- const provider = getMessagingProvider("gmail");
48
- return await withValidToken(provider.credentialService, async (token) => {
49
- switch (action) {
50
- case "list": {
51
- const pageSize = (input.page_size as number) ?? 50;
52
- const pageToken = input.page_token as string | undefined;
46
+ const connection = resolveOAuthConnection("integration:gmail");
47
+ switch (action) {
48
+ case "list": {
49
+ const pageSize = (input.page_size as number) ?? 50;
50
+ const pageToken = input.page_token as string | undefined;
53
51
 
54
- const resp = await listContacts(token, pageSize, pageToken);
55
- const contacts = (resp.connections ?? []).map(formatContact);
52
+ const resp = await listContacts(connection, pageSize, pageToken);
53
+ const contacts = (resp.connections ?? []).map(formatContact);
56
54
 
57
- const result: Record<string, unknown> = {
58
- contacts,
59
- total: resp.totalPeople ?? contacts.length,
60
- };
61
- if (resp.nextPageToken) result.nextPageToken = resp.nextPageToken;
55
+ const result: Record<string, unknown> = {
56
+ contacts,
57
+ total: resp.totalPeople ?? contacts.length,
58
+ };
59
+ if (resp.nextPageToken) result.nextPageToken = resp.nextPageToken;
62
60
 
63
- return ok(JSON.stringify(result, null, 2));
64
- }
65
-
66
- case "search": {
67
- const query = input.query as string;
68
- if (!query) return err("query is required for search action.");
61
+ return ok(JSON.stringify(result, null, 2));
62
+ }
69
63
 
70
- const resp = await searchContacts(token, query);
71
- const contacts = (resp.results ?? []).map((r) =>
72
- formatContact(r.person),
73
- );
64
+ case "search": {
65
+ const query = input.query as string;
66
+ if (!query) return err("query is required for search action.");
74
67
 
75
- return ok(
76
- JSON.stringify({ contacts, total: contacts.length }, null, 2),
77
- );
78
- }
68
+ const resp = await searchContacts(connection, query);
69
+ const contacts = (resp.results ?? []).map((r) =>
70
+ formatContact(r.person),
71
+ );
79
72
 
80
- default:
81
- return err(`Unknown action "${action}". Use list or search.`);
73
+ return ok(
74
+ JSON.stringify({ contacts, total: contacts.length }, null, 2),
75
+ );
82
76
  }
83
- });
77
+
78
+ default:
79
+ return err(`Unknown action "${action}". Use list or search.`);
80
+ }
84
81
  } catch (e) {
85
82
  return err(e instanceof Error ? e.message : String(e));
86
83
  }
@@ -30,11 +30,11 @@ Do not offer AgentMail as an option or mention it unless the user specifically a
30
30
  ### Gmail
31
31
 
32
32
  1. **Try connecting directly first.** Call `credential_store` with `action: "oauth2_connect"` and `service: "gmail"`. The tool auto-fills Google's OAuth endpoints and looks up any previously stored client credentials — so this single call may be all that's needed.
33
- 2. **If it fails because no client_id is found:** The user needs to create Google Cloud OAuth credentials first. Load the **google-oauth-setup** skill (which depends on **public-ingress** for the redirect URI):
34
- - Call `skill_load` with `skill: "google-oauth-setup"` to load the dependency skill.
33
+ 2. **If it fails because no client_id is found:** The user needs to create Google Cloud OAuth credentials first. Load the **google-oauth-applescript** skill:
34
+ - Call `skill_load` with `skill: "google-oauth-applescript"` to load the dependency skill.
35
35
  - Tell the user Gmail isn't connected yet and briefly explain what the setup involves, then use `ui_show` with `surface_type: "confirmation"` to ask for permission to start:
36
36
  - **message:** "Ready to set up Gmail?"
37
- - **detail:** "I'll open a browser where you sign in to Google, then automate everything else — creating a project, enabling APIs, and connecting your account. Takes 2-3 minutes and you can watch in the browser preview panel."
37
+ - **detail:** "I'll open a few pages in your browser and walk you through setting up Google Cloud credentials — creating a project, enabling APIs, and connecting your account. Takes about 5 minutes."
38
38
  - **confirmLabel:** "Get Started"
39
39
  - **cancelLabel:** "Not Now"
40
40
  - If the user confirms, briefly acknowledge (e.g., "Setting up Gmail now...") and proceed with the setup guide. If they decline, acknowledge and let them know they can set it up later.
@@ -45,7 +45,7 @@ Do not offer AgentMail as an option or mention it unless the user specifically a
45
45
  When a Gmail tool fails with a token or authorization error:
46
46
 
47
47
  1. **Try to reconnect silently.** Call `credential_store` with `action: "oauth2_connect"` and `service: "gmail"`. This often resolves expired tokens automatically.
48
- 2. **If reconnection fails, go straight to setup.** Don't present options, ask which route the user prefers, or explain what went wrong technically. Just tell the user briefly (e.g., "Gmail needs to be reconnected — let me set that up") and immediately follow the connection setup flow for Gmail (e.g., install and load **google-oauth-setup**). The user came to you to get something done, not to troubleshoot OAuth — make it seamless.
48
+ 2. **If reconnection fails, go straight to setup.** Don't present options, ask which route the user prefers, or explain what went wrong technically. Just tell the user briefly (e.g., "Gmail needs to be reconnected — let me set that up") and immediately follow the connection setup flow for Gmail (e.g., install and load **google-oauth-applescript**). The user came to you to get something done, not to troubleshoot OAuth — make it seamless.
49
49
  3. **Never try alternative approaches.** Don't use bash, curl, browser automation, or any workaround. If the Gmail tools can't do it, the reconnection flow is the answer.
50
50
  4. **Never expose error details.** The user doesn't need to see error messages about tokens, OAuth, or API failures. Translate errors into plain language.
51
51
 
@@ -3,8 +3,7 @@ import {
3
3
  listMessages,
4
4
  modifyMessage,
5
5
  } from "../../../../messaging/providers/gmail/client.js";
6
- import { getMessagingProvider } from "../../../../messaging/registry.js";
7
- import { withValidToken } from "../../../../security/token-manager.js";
6
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
8
7
  import type {
9
8
  ToolContext,
10
9
  ToolExecutionResult,
@@ -35,49 +34,47 @@ export async function run(
35
34
  }
36
35
 
37
36
  try {
38
- const provider = getMessagingProvider("gmail");
39
- return await withValidToken(provider.credentialService, async (token) => {
40
- const allMessageIds: string[] = [];
41
- let pageToken: string | undefined;
42
- let truncated = false;
37
+ const connection = resolveOAuthConnection("integration:gmail");
38
+ const allMessageIds: string[] = [];
39
+ let pageToken: string | undefined;
40
+ let truncated = false;
43
41
 
44
- while (allMessageIds.length < MAX_MESSAGES) {
45
- const listResp = await listMessages(
46
- token,
47
- query,
48
- Math.min(500, MAX_MESSAGES - allMessageIds.length),
49
- pageToken,
50
- );
51
- const ids = (listResp.messages ?? []).map((m) => m.id);
52
- if (ids.length === 0) break;
53
- allMessageIds.push(...ids);
54
- pageToken = listResp.nextPageToken ?? undefined;
55
- if (!pageToken) break;
56
- }
42
+ while (allMessageIds.length < MAX_MESSAGES) {
43
+ const listResp = await listMessages(
44
+ connection,
45
+ query,
46
+ Math.min(500, MAX_MESSAGES - allMessageIds.length),
47
+ pageToken,
48
+ );
49
+ const ids = (listResp.messages ?? []).map((m) => m.id);
50
+ if (ids.length === 0) break;
51
+ allMessageIds.push(...ids);
52
+ pageToken = listResp.nextPageToken ?? undefined;
53
+ if (!pageToken) break;
54
+ }
57
55
 
58
- if (allMessageIds.length >= MAX_MESSAGES && pageToken) {
59
- truncated = true;
60
- }
56
+ if (allMessageIds.length >= MAX_MESSAGES && pageToken) {
57
+ truncated = true;
58
+ }
61
59
 
62
- if (allMessageIds.length === 0) {
63
- return ok("No messages matched the query. Nothing archived.");
64
- }
60
+ if (allMessageIds.length === 0) {
61
+ return ok("No messages matched the query. Nothing archived.");
62
+ }
65
63
 
66
- for (let i = 0; i < allMessageIds.length; i += BATCH_MODIFY_LIMIT) {
67
- const chunk = allMessageIds.slice(i, i + BATCH_MODIFY_LIMIT);
68
- await batchModifyMessages(token, chunk, {
69
- removeLabelIds: ["INBOX"],
70
- });
71
- }
64
+ for (let i = 0; i < allMessageIds.length; i += BATCH_MODIFY_LIMIT) {
65
+ const chunk = allMessageIds.slice(i, i + BATCH_MODIFY_LIMIT);
66
+ await batchModifyMessages(connection, chunk, {
67
+ removeLabelIds: ["INBOX"],
68
+ });
69
+ }
72
70
 
73
- const summary = `Archived ${allMessageIds.length} message(s) matching query: ${query}`;
74
- if (truncated) {
75
- return ok(
76
- `${summary}\n\nNote: this operation was capped at ${MAX_MESSAGES} messages. Additional messages matching the query may remain in the inbox. Run the command again to archive more.`,
77
- );
78
- }
79
- return ok(summary);
80
- });
71
+ const summary = `Archived ${allMessageIds.length} message(s) matching query: ${query}`;
72
+ if (truncated) {
73
+ return ok(
74
+ `${summary}\n\nNote: this operation was capped at ${MAX_MESSAGES} messages. Additional messages matching the query may remain in the inbox. Run the command again to archive more.`,
75
+ );
76
+ }
77
+ return ok(summary);
81
78
  } catch (e) {
82
79
  return err(e instanceof Error ? e.message : String(e));
83
80
  }
@@ -106,11 +103,9 @@ export async function run(
106
103
  } else if (messageId) {
107
104
  // Single message path
108
105
  try {
109
- const provider = getMessagingProvider("gmail");
110
- return await withValidToken(provider.credentialService, async (token) => {
111
- await modifyMessage(token, messageId, { removeLabelIds: ["INBOX"] });
112
- return ok("Message archived.");
113
- });
106
+ const connection = resolveOAuthConnection("integration:gmail");
107
+ await modifyMessage(connection, messageId, { removeLabelIds: ["INBOX"] });
108
+ return ok("Message archived.");
114
109
  } catch (e) {
115
110
  return err(e instanceof Error ? e.message : String(e));
116
111
  }
@@ -126,23 +121,21 @@ export async function run(
126
121
  }
127
122
 
128
123
  try {
129
- const provider = getMessagingProvider("gmail");
130
- return await withValidToken(provider.credentialService, async (token) => {
131
- if (messageIds.length === 1) {
132
- await modifyMessage(token, messageIds[0], {
133
- removeLabelIds: ["INBOX"],
134
- });
135
- return ok("Message archived.");
136
- }
124
+ const connection = resolveOAuthConnection("integration:gmail");
125
+ if (messageIds.length === 1) {
126
+ await modifyMessage(connection, messageIds[0], {
127
+ removeLabelIds: ["INBOX"],
128
+ });
129
+ return ok("Message archived.");
130
+ }
137
131
 
138
- for (let i = 0; i < messageIds.length; i += BATCH_MODIFY_LIMIT) {
139
- const chunk = messageIds.slice(i, i + BATCH_MODIFY_LIMIT);
140
- await batchModifyMessages(token, chunk, {
141
- removeLabelIds: ["INBOX"],
142
- });
143
- }
144
- return ok(`Archived ${messageIds.length} message(s).`);
145
- });
132
+ for (let i = 0; i < messageIds.length; i += BATCH_MODIFY_LIMIT) {
133
+ const chunk = messageIds.slice(i, i + BATCH_MODIFY_LIMIT);
134
+ await batchModifyMessages(connection, chunk, {
135
+ removeLabelIds: ["INBOX"],
136
+ });
137
+ }
138
+ return ok(`Archived ${messageIds.length} message(s).`);
146
139
  } catch (e) {
147
140
  return err(e instanceof Error ? e.message : String(e));
148
141
  }
@@ -6,8 +6,7 @@ import {
6
6
  getMessage,
7
7
  } from "../../../../messaging/providers/gmail/client.js";
8
8
  import type { GmailMessagePart } from "../../../../messaging/providers/gmail/types.js";
9
- import { getMessagingProvider } from "../../../../messaging/registry.js";
10
- import { withValidToken } from "../../../../security/token-manager.js";
9
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
11
10
  import type {
12
11
  ToolContext,
13
12
  ToolExecutionResult,
@@ -57,17 +56,15 @@ export async function run(
57
56
 
58
57
  if (action === "list") {
59
58
  try {
60
- const provider = getMessagingProvider("gmail");
61
- return await withValidToken(provider.credentialService, async (token) => {
62
- const message = await getMessage(token, messageId, "full");
63
- const attachments = collectAttachments(message.payload?.parts);
59
+ const connection = resolveOAuthConnection("integration:gmail");
60
+ const message = await getMessage(connection, messageId, "full");
61
+ const attachments = collectAttachments(message.payload?.parts);
64
62
 
65
- if (attachments.length === 0) {
66
- return ok("No attachments found on this message.");
67
- }
63
+ if (attachments.length === 0) {
64
+ return ok("No attachments found on this message.");
65
+ }
68
66
 
69
- return ok(JSON.stringify(attachments, null, 2));
70
- });
67
+ return ok(JSON.stringify(attachments, null, 2));
71
68
  } catch (e) {
72
69
  return err(e instanceof Error ? e.message : String(e));
73
70
  }
@@ -81,26 +78,26 @@ export async function run(
81
78
  if (!filename) return err("filename is required for download.");
82
79
 
83
80
  try {
84
- const provider = getMessagingProvider("gmail");
85
- return await withValidToken(provider.credentialService, async (token) => {
86
- const attachment = await getAttachment(token, messageId, attachmentId);
81
+ const connection = resolveOAuthConnection("integration:gmail");
82
+ const attachment = await getAttachment(
83
+ connection,
84
+ messageId,
85
+ attachmentId,
86
+ );
87
87
 
88
- // Gmail returns base64url; convert to standard base64 then to Buffer
89
- const base64 = attachment.data.replace(/-/g, "+").replace(/_/g, "/");
90
- const buffer = Buffer.from(base64, "base64");
88
+ // Gmail returns base64url; convert to standard base64 then to Buffer
89
+ const base64 = attachment.data.replace(/-/g, "+").replace(/_/g, "/");
90
+ const buffer = Buffer.from(base64, "base64");
91
91
 
92
- const outputDir = context.workingDir ?? process.cwd();
93
- // Sanitize filename: strip path separators to prevent traversal attacks from crafted MIME filenames
94
- const safeName = basename(filename).replace(/\.\./g, "_");
95
- const outputPath = resolve(outputDir, safeName);
96
- if (!outputPath.startsWith(outputDir))
97
- return err("Invalid filename: path traversal detected.");
98
- await writeFile(outputPath, buffer);
92
+ const outputDir = context.workingDir ?? process.cwd();
93
+ // Sanitize filename: strip path separators to prevent traversal attacks from crafted MIME filenames
94
+ const safeName = basename(filename).replace(/\.\./g, "_");
95
+ const outputPath = resolve(outputDir, safeName);
96
+ if (!outputPath.startsWith(outputDir))
97
+ return err("Invalid filename: path traversal detected.");
98
+ await writeFile(outputPath, buffer);
99
99
 
100
- return ok(
101
- `Attachment saved to ${outputPath} (${buffer.length} bytes).`,
102
- );
103
- });
100
+ return ok(`Attachment saved to ${outputPath} (${buffer.length} bytes).`);
104
101
  } catch (e) {
105
102
  return err(e instanceof Error ? e.message : String(e));
106
103
  }
@@ -1,6 +1,5 @@
1
1
  import { createDraft } from "../../../../messaging/providers/gmail/client.js";
2
- import { getMessagingProvider } from "../../../../messaging/registry.js";
3
- import { withValidToken } from "../../../../security/token-manager.js";
2
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
4
3
  import type {
5
4
  ToolContext,
6
5
  ToolExecutionResult,
@@ -23,21 +22,19 @@ export async function run(
23
22
  if (!body) return err("body is required.");
24
23
 
25
24
  try {
26
- const provider = getMessagingProvider("gmail");
27
- return await withValidToken(provider.credentialService, async (token) => {
28
- const draft = await createDraft(
29
- token,
30
- to,
31
- subject,
32
- body,
33
- inReplyTo,
34
- cc,
35
- bcc,
36
- );
37
- return ok(
38
- `Draft created (ID: ${draft.id}). It will appear in your Gmail Drafts.`,
39
- );
40
- });
25
+ const connection = resolveOAuthConnection("integration:gmail");
26
+ const draft = await createDraft(
27
+ connection,
28
+ to,
29
+ subject,
30
+ body,
31
+ inReplyTo,
32
+ cc,
33
+ bcc,
34
+ );
35
+ return ok(
36
+ `Draft created (ID: ${draft.id}). It will appear in your Gmail Drafts.`,
37
+ );
41
38
  } catch (e) {
42
39
  return err(e instanceof Error ? e.message : String(e));
43
40
  }
@@ -7,8 +7,7 @@ import type {
7
7
  GmailFilterAction,
8
8
  GmailFilterCriteria,
9
9
  } from "../../../../messaging/providers/gmail/types.js";
10
- import { getMessagingProvider } from "../../../../messaging/registry.js";
11
- import { withValidToken } from "../../../../security/token-manager.js";
10
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
12
11
  import type {
13
12
  ToolContext,
14
13
  ToolExecutionResult,
@@ -26,57 +25,53 @@ export async function run(
26
25
  }
27
26
 
28
27
  try {
29
- const provider = getMessagingProvider("gmail");
30
- return await withValidToken(provider.credentialService, async (token) => {
31
- switch (action) {
32
- case "list": {
33
- const filters = await listFilters(token);
34
- if (filters.length === 0) {
35
- return ok("No filters configured.");
36
- }
37
- return ok(JSON.stringify(filters, null, 2));
28
+ const connection = resolveOAuthConnection("integration:gmail");
29
+ switch (action) {
30
+ case "list": {
31
+ const filters = await listFilters(connection);
32
+ if (filters.length === 0) {
33
+ return ok("No filters configured.");
38
34
  }
35
+ return ok(JSON.stringify(filters, null, 2));
36
+ }
39
37
 
40
- case "create": {
41
- const criteria: GmailFilterCriteria = {};
42
- if (input.from) criteria.from = input.from as string;
43
- if (input.to) criteria.to = input.to as string;
44
- if (input.subject) criteria.subject = input.subject as string;
45
- if (input.query) criteria.query = input.query as string;
46
- if (input.has_attachment !== undefined)
47
- criteria.hasAttachment = input.has_attachment as boolean;
48
-
49
- const filterAction: GmailFilterAction = {};
50
- if (input.add_label_ids)
51
- filterAction.addLabelIds = input.add_label_ids as string[];
52
- if (input.remove_label_ids)
53
- filterAction.removeLabelIds = input.remove_label_ids as string[];
54
- if (input.forward) filterAction.forward = input.forward as string;
38
+ case "create": {
39
+ const criteria: GmailFilterCriteria = {};
40
+ if (input.from) criteria.from = input.from as string;
41
+ if (input.to) criteria.to = input.to as string;
42
+ if (input.subject) criteria.subject = input.subject as string;
43
+ if (input.query) criteria.query = input.query as string;
44
+ if (input.has_attachment !== undefined)
45
+ criteria.hasAttachment = input.has_attachment as boolean;
55
46
 
56
- if (Object.keys(criteria).length === 0) {
57
- return err(
58
- "At least one filter criteria is required (from, to, subject, query, or has_attachment).",
59
- );
60
- }
47
+ const filterAction: GmailFilterAction = {};
48
+ if (input.add_label_ids)
49
+ filterAction.addLabelIds = input.add_label_ids as string[];
50
+ if (input.remove_label_ids)
51
+ filterAction.removeLabelIds = input.remove_label_ids as string[];
52
+ if (input.forward) filterAction.forward = input.forward as string;
61
53
 
62
- const filter = await createFilter(token, criteria, filterAction);
63
- return ok(`Filter created (ID: ${filter.id}).`);
54
+ if (Object.keys(criteria).length === 0) {
55
+ return err(
56
+ "At least one filter criteria is required (from, to, subject, query, or has_attachment).",
57
+ );
64
58
  }
65
59
 
66
- case "delete": {
67
- const filterId = input.filter_id as string;
68
- if (!filterId) return err("filter_id is required for delete action.");
60
+ const filter = await createFilter(connection, criteria, filterAction);
61
+ return ok(`Filter created (ID: ${filter.id}).`);
62
+ }
69
63
 
70
- await deleteFilter(token, filterId);
71
- return ok("Filter deleted.");
72
- }
64
+ case "delete": {
65
+ const filterId = input.filter_id as string;
66
+ if (!filterId) return err("filter_id is required for delete action.");
73
67
 
74
- default:
75
- return err(
76
- `Unknown action "${action}". Use list, create, or delete.`,
77
- );
68
+ await deleteFilter(connection, filterId);
69
+ return ok("Filter deleted.");
78
70
  }
79
- });
71
+
72
+ default:
73
+ return err(`Unknown action "${action}". Use list, create, or delete.`);
74
+ }
80
75
  } catch (e) {
81
76
  return err(e instanceof Error ? e.message : String(e));
82
77
  }