@vellumai/assistant 0.4.49 → 0.4.51

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 (353) hide show
  1. package/ARCHITECTURE.md +24 -33
  2. package/README.md +3 -3
  3. package/docs/architecture/integrations.md +2 -2
  4. package/docs/architecture/keychain-broker.md +6 -6
  5. package/docs/architecture/memory.md +180 -119
  6. package/knip.json +32 -0
  7. package/package.json +3 -2
  8. package/src/__tests__/agent-loop.test.ts +3 -1
  9. package/src/__tests__/anthropic-provider.test.ts +114 -23
  10. package/src/__tests__/approval-cascade.test.ts +1 -15
  11. package/src/__tests__/approval-routes-http.test.ts +2 -0
  12. package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
  13. package/src/__tests__/btw-routes.test.ts +61 -5
  14. package/src/__tests__/canonical-guardian-store.test.ts +95 -0
  15. package/src/__tests__/checker.test.ts +13 -0
  16. package/src/__tests__/config-schema.test.ts +1 -68
  17. package/src/__tests__/config-watcher.test.ts +8 -0
  18. package/src/__tests__/context-memory-e2e.test.ts +11 -100
  19. package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
  20. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  21. package/src/__tests__/credential-security-e2e.test.ts +1 -0
  22. package/src/__tests__/credential-security-invariants.test.ts +8 -7
  23. package/src/__tests__/credential-vault-unit.test.ts +23 -18
  24. package/src/__tests__/credential-vault.test.ts +30 -18
  25. package/src/__tests__/credentials-cli.test.ts +257 -82
  26. package/src/__tests__/cu-unified-flow.test.ts +532 -0
  27. package/src/__tests__/date-context.test.ts +93 -77
  28. package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
  29. package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
  30. package/src/__tests__/history-repair.test.ts +245 -0
  31. package/src/__tests__/host-cu-proxy.test.ts +165 -3
  32. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  33. package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
  34. package/src/__tests__/integration-status.test.ts +31 -30
  35. package/src/__tests__/invite-redemption-service.test.ts +166 -13
  36. package/src/__tests__/invite-routes-http.test.ts +166 -5
  37. package/src/__tests__/keychain-broker-client.test.ts +4 -4
  38. package/src/__tests__/list-messages-attachments.test.ts +193 -0
  39. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
  40. package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
  41. package/src/__tests__/memory-recall-quality.test.ts +244 -407
  42. package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
  43. package/src/__tests__/memory-regressions.test.ts +477 -2841
  44. package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
  45. package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
  46. package/src/__tests__/mime-builder.test.ts +28 -0
  47. package/src/__tests__/native-web-search.test.ts +1 -0
  48. package/src/__tests__/oauth-cli.test.ts +824 -31
  49. package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
  50. package/src/__tests__/oauth-store.test.ts +363 -17
  51. package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
  52. package/src/__tests__/registry.test.ts +0 -1
  53. package/src/__tests__/relay-server.test.ts +55 -1
  54. package/src/__tests__/schedule-tools.test.ts +32 -0
  55. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  56. package/src/__tests__/secret-onetime-send.test.ts +1 -0
  57. package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
  58. package/src/__tests__/secure-keys.test.ts +78 -18
  59. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  60. package/src/__tests__/server-history-render.test.ts +2 -2
  61. package/src/__tests__/session-abort-tool-results.test.ts +1 -14
  62. package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
  63. package/src/__tests__/session-agent-loop.test.ts +19 -15
  64. package/src/__tests__/session-confirmation-signals.test.ts +1 -15
  65. package/src/__tests__/session-error.test.ts +124 -2
  66. package/src/__tests__/session-history-web-search.test.ts +918 -0
  67. package/src/__tests__/session-pre-run-repair.test.ts +1 -14
  68. package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
  69. package/src/__tests__/session-queue.test.ts +37 -27
  70. package/src/__tests__/session-runtime-assembly.test.ts +54 -0
  71. package/src/__tests__/session-slash-known.test.ts +1 -15
  72. package/src/__tests__/session-slash-queue.test.ts +1 -15
  73. package/src/__tests__/session-slash-unknown.test.ts +1 -15
  74. package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
  75. package/src/__tests__/session-workspace-injection.test.ts +3 -37
  76. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
  77. package/src/__tests__/skills-install-extract.test.ts +93 -0
  78. package/src/__tests__/skills.test.ts +2 -2
  79. package/src/__tests__/skillssh-registry.test.ts +451 -0
  80. package/src/__tests__/slack-channel-config.test.ts +10 -8
  81. package/src/__tests__/trust-store.test.ts +15 -0
  82. package/src/__tests__/twilio-config.test.ts +11 -10
  83. package/src/__tests__/twilio-provider.test.ts +9 -4
  84. package/src/__tests__/voice-invite-redemption.test.ts +85 -5
  85. package/src/agent/ax-tree-compaction.test.ts +51 -0
  86. package/src/agent/loop.ts +39 -12
  87. package/src/approvals/AGENTS.md +1 -1
  88. package/src/approvals/guardian-request-resolvers.ts +14 -2
  89. package/src/bundler/compiler-tools.ts +66 -2
  90. package/src/calls/call-domain.ts +134 -3
  91. package/src/calls/call-store.ts +6 -0
  92. package/src/calls/relay-server.ts +44 -6
  93. package/src/calls/relay-setup-router.ts +17 -1
  94. package/src/calls/twilio-config.ts +5 -4
  95. package/src/calls/twilio-provider.ts +14 -9
  96. package/src/calls/twilio-rest.ts +10 -7
  97. package/src/calls/types.ts +3 -1
  98. package/src/cli/commands/config.ts +14 -9
  99. package/src/cli/commands/contacts.ts +3 -0
  100. package/src/cli/commands/credentials.ts +170 -174
  101. package/src/cli/commands/doctor.ts +11 -8
  102. package/src/cli/commands/keys.ts +9 -9
  103. package/src/cli/commands/mcp.ts +46 -59
  104. package/src/cli/commands/memory.ts +16 -165
  105. package/src/cli/commands/oauth/apps.ts +68 -10
  106. package/src/cli/commands/oauth/connections.ts +475 -105
  107. package/src/cli/commands/oauth/index.ts +3 -3
  108. package/src/cli/commands/oauth/providers.ts +18 -4
  109. package/src/cli/commands/sessions.ts +5 -2
  110. package/src/cli/commands/skills.ts +173 -1
  111. package/src/cli/http-client.ts +0 -20
  112. package/src/cli/main-screen.tsx +2 -2
  113. package/src/cli/program.ts +5 -6
  114. package/src/cli.ts +20 -22
  115. package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
  116. package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
  117. package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
  118. package/src/config/bundled-skills/contacts/SKILL.md +35 -11
  119. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
  120. package/src/config/bundled-skills/gmail/SKILL.md +1 -1
  121. package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
  122. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
  123. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
  124. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
  125. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
  126. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
  127. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
  128. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
  129. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
  130. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
  131. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
  132. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
  133. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
  134. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
  135. package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
  136. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
  137. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
  138. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
  139. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
  140. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
  141. package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
  142. package/src/config/bundled-skills/messaging/SKILL.md +1 -1
  143. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
  144. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  145. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
  146. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
  147. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
  148. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
  149. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
  150. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
  151. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
  152. package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
  153. package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
  154. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
  155. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
  156. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
  157. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
  158. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
  159. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
  160. package/src/config/bundled-tool-registry.ts +2 -5
  161. package/src/config/loader.ts +6 -42
  162. package/src/config/schema.ts +1 -12
  163. package/src/config/schemas/memory-lifecycle.ts +0 -9
  164. package/src/config/schemas/memory-processing.ts +0 -180
  165. package/src/config/schemas/memory-retrieval.ts +32 -104
  166. package/src/config/schemas/memory.ts +0 -10
  167. package/src/config/types.ts +0 -4
  168. package/src/contacts/contact-store.ts +39 -2
  169. package/src/contacts/contacts-write.ts +9 -0
  170. package/src/context/window-manager.ts +4 -1
  171. package/src/daemon/config-watcher.ts +55 -2
  172. package/src/daemon/daemon-control.ts +1 -1
  173. package/src/daemon/date-context.ts +114 -31
  174. package/src/daemon/handlers/config-ingress.ts +2 -2
  175. package/src/daemon/handlers/config-slack-channel.ts +59 -39
  176. package/src/daemon/handlers/config-telegram.ts +23 -14
  177. package/src/daemon/handlers/session-history.ts +1 -358
  178. package/src/daemon/handlers/sessions.ts +18 -13
  179. package/src/daemon/handlers/shared.ts +3 -17
  180. package/src/daemon/handlers/skills.ts +20 -1
  181. package/src/daemon/history-repair.ts +72 -8
  182. package/src/daemon/host-cu-proxy.ts +55 -26
  183. package/src/daemon/lifecycle.ts +39 -4
  184. package/src/daemon/mcp-reload-service.ts +2 -2
  185. package/src/daemon/message-types/computer-use.ts +1 -12
  186. package/src/daemon/message-types/memory.ts +4 -16
  187. package/src/daemon/message-types/messages.ts +1 -0
  188. package/src/daemon/message-types/sessions.ts +4 -42
  189. package/src/daemon/server.ts +6 -1
  190. package/src/daemon/session-agent-loop-handlers.ts +38 -0
  191. package/src/daemon/session-agent-loop.ts +334 -48
  192. package/src/daemon/session-error.ts +89 -6
  193. package/src/daemon/session-history.ts +17 -7
  194. package/src/daemon/session-media-retry.ts +6 -2
  195. package/src/daemon/session-memory.ts +69 -149
  196. package/src/daemon/session-process.ts +10 -1
  197. package/src/daemon/session-runtime-assembly.ts +49 -19
  198. package/src/daemon/session-slash.ts +3 -5
  199. package/src/daemon/session-surfaces.ts +4 -1
  200. package/src/daemon/session-tool-setup.ts +7 -1
  201. package/src/daemon/session.ts +12 -2
  202. package/src/email/providers/index.ts +2 -2
  203. package/src/instrument.ts +61 -1
  204. package/src/media/avatar-router.ts +1 -1
  205. package/src/memory/admin.ts +2 -191
  206. package/src/memory/canonical-guardian-store.ts +38 -2
  207. package/src/memory/conversation-crud.ts +0 -33
  208. package/src/memory/conversation-queries.ts +25 -83
  209. package/src/memory/db-init.ts +32 -0
  210. package/src/memory/embedding-backend.ts +84 -8
  211. package/src/memory/embedding-types.ts +9 -1
  212. package/src/memory/indexer.ts +7 -46
  213. package/src/memory/invite-store.ts +19 -0
  214. package/src/memory/items-extractor.ts +274 -76
  215. package/src/memory/job-handlers/backfill.ts +2 -127
  216. package/src/memory/job-handlers/cleanup.ts +2 -16
  217. package/src/memory/job-handlers/extraction.ts +2 -138
  218. package/src/memory/job-handlers/index-maintenance.ts +1 -6
  219. package/src/memory/job-handlers/summarization.ts +3 -148
  220. package/src/memory/job-utils.ts +21 -59
  221. package/src/memory/jobs-store.ts +1 -159
  222. package/src/memory/jobs-worker.ts +9 -52
  223. package/src/memory/migrations/104-core-indexes.ts +3 -3
  224. package/src/memory/migrations/149-oauth-tables.ts +2 -0
  225. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
  226. package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
  227. package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
  228. package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
  229. package/src/memory/migrations/154-drop-fts.ts +20 -0
  230. package/src/memory/migrations/155-drop-conflicts.ts +7 -0
  231. package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
  232. package/src/memory/migrations/157-invite-contact-id.ts +104 -0
  233. package/src/memory/migrations/index.ts +8 -0
  234. package/src/memory/migrations/registry.ts +6 -0
  235. package/src/memory/qdrant-client.ts +148 -51
  236. package/src/memory/raw-query.ts +1 -1
  237. package/src/memory/retriever.test.ts +294 -273
  238. package/src/memory/retriever.ts +421 -645
  239. package/src/memory/schema/calls.ts +2 -0
  240. package/src/memory/schema/contacts.ts +1 -0
  241. package/src/memory/schema/memory-core.ts +3 -48
  242. package/src/memory/schema/oauth.ts +2 -0
  243. package/src/memory/search/formatting.ts +263 -176
  244. package/src/memory/search/lexical.ts +1 -254
  245. package/src/memory/search/ranking.ts +0 -455
  246. package/src/memory/search/semantic.ts +100 -14
  247. package/src/memory/search/staleness.ts +47 -0
  248. package/src/memory/search/tier-classifier.ts +21 -0
  249. package/src/memory/search/types.ts +15 -77
  250. package/src/memory/task-memory-cleanup.ts +4 -6
  251. package/src/messaging/provider.ts +1 -1
  252. package/src/messaging/providers/gmail/adapter.ts +1 -1
  253. package/src/messaging/providers/gmail/mime-builder.ts +17 -7
  254. package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
  255. package/src/messaging/providers/whatsapp/adapter.ts +13 -9
  256. package/src/messaging/registry.ts +9 -5
  257. package/src/oauth/byo-connection.test.ts +40 -25
  258. package/src/oauth/connect-orchestrator.ts +4 -10
  259. package/src/oauth/connection-resolver.ts +20 -6
  260. package/src/oauth/manual-token-connection.ts +5 -5
  261. package/src/oauth/oauth-store.ts +183 -31
  262. package/src/oauth/platform-connection.test.ts +1 -1
  263. package/src/oauth/provider-behaviors.ts +503 -4
  264. package/src/oauth/seed-providers.ts +214 -8
  265. package/src/oauth/token-persistence.ts +31 -16
  266. package/src/permissions/defaults.ts +1 -0
  267. package/src/permissions/trust-store.ts +23 -1
  268. package/src/playbooks/playbook-compiler.ts +1 -1
  269. package/src/prompts/system-prompt.ts +18 -2
  270. package/src/providers/anthropic/client.ts +56 -126
  271. package/src/providers/types.ts +7 -1
  272. package/src/runtime/AGENTS.md +9 -0
  273. package/src/runtime/auth/route-policy.ts +6 -3
  274. package/src/runtime/channel-readiness-service.ts +48 -40
  275. package/src/runtime/guardian-reply-router.ts +24 -22
  276. package/src/runtime/http-server.ts +2 -2
  277. package/src/runtime/http-types.ts +2 -0
  278. package/src/runtime/invite-redemption-service.ts +72 -12
  279. package/src/runtime/invite-service.ts +43 -0
  280. package/src/runtime/middleware/twilio-validation.ts +1 -1
  281. package/src/runtime/pending-interactions.ts +2 -2
  282. package/src/runtime/routes/brain-graph-routes.ts +10 -90
  283. package/src/runtime/routes/btw-routes.ts +10 -5
  284. package/src/runtime/routes/conversation-routes.ts +56 -11
  285. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
  286. package/src/runtime/routes/integrations/slack/channel.ts +2 -2
  287. package/src/runtime/routes/integrations/telegram.ts +2 -2
  288. package/src/runtime/routes/integrations/twilio.ts +17 -17
  289. package/src/runtime/routes/invite-routes.ts +29 -4
  290. package/src/runtime/routes/memory-item-routes.test.ts +754 -0
  291. package/src/runtime/routes/memory-item-routes.ts +503 -0
  292. package/src/runtime/routes/secret-routes.ts +17 -0
  293. package/src/runtime/routes/session-management-routes.ts +3 -3
  294. package/src/runtime/routes/settings-routes.ts +3 -3
  295. package/src/runtime/routes/trust-rules-routes.ts +14 -0
  296. package/src/runtime/routes/workspace-routes.ts +9 -4
  297. package/src/runtime/routes/workspace-utils.ts +8 -2
  298. package/src/schedule/integration-status.ts +26 -19
  299. package/src/security/keychain-broker-client.ts +17 -4
  300. package/src/security/oauth2.ts +6 -7
  301. package/src/security/secure-keys.ts +44 -19
  302. package/src/security/token-manager.ts +46 -39
  303. package/src/services/vercel-deploy.ts +0 -24
  304. package/src/signals/confirm.ts +78 -0
  305. package/src/signals/mcp-reload.ts +18 -0
  306. package/src/skills/catalog-install.ts +74 -18
  307. package/src/skills/skillssh-registry.ts +503 -0
  308. package/src/tools/assets/search.ts +5 -1
  309. package/src/tools/computer-use/definitions.ts +0 -10
  310. package/src/tools/computer-use/registry.ts +1 -1
  311. package/src/tools/credentials/vault.ts +22 -7
  312. package/src/tools/memory/definitions.ts +4 -13
  313. package/src/tools/memory/handlers.test.ts +83 -103
  314. package/src/tools/memory/handlers.ts +50 -85
  315. package/src/tools/network/script-proxy/session-manager.ts +8 -8
  316. package/src/tools/schedule/create.ts +10 -3
  317. package/src/tools/schedule/update.ts +8 -1
  318. package/src/tools/skills/load.ts +25 -2
  319. package/src/watcher/provider-types.ts +1 -1
  320. package/src/watcher/providers/github.ts +1 -1
  321. package/src/watcher/providers/gmail.ts +3 -3
  322. package/src/watcher/providers/google-calendar.ts +3 -3
  323. package/src/watcher/providers/linear.ts +1 -1
  324. package/src/__tests__/clarification-resolver.test.ts +0 -193
  325. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
  326. package/src/__tests__/conflict-policy.test.ts +0 -269
  327. package/src/__tests__/conflict-store.test.ts +0 -372
  328. package/src/__tests__/contradiction-checker.test.ts +0 -361
  329. package/src/__tests__/entity-extractor.test.ts +0 -211
  330. package/src/__tests__/entity-search.test.ts +0 -1117
  331. package/src/__tests__/profile-compiler.test.ts +0 -392
  332. package/src/__tests__/session-conflict-gate.test.ts +0 -1228
  333. package/src/__tests__/session-profile-injection.test.ts +0 -557
  334. package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
  335. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
  336. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
  337. package/src/daemon/session-conflict-gate.ts +0 -167
  338. package/src/daemon/session-dynamic-profile.ts +0 -77
  339. package/src/memory/clarification-resolver.ts +0 -417
  340. package/src/memory/conflict-intent.ts +0 -205
  341. package/src/memory/conflict-policy.ts +0 -127
  342. package/src/memory/conflict-store.ts +0 -410
  343. package/src/memory/contradiction-checker.ts +0 -508
  344. package/src/memory/entity-extractor.ts +0 -535
  345. package/src/memory/format-recall.ts +0 -47
  346. package/src/memory/fts-reconciler.ts +0 -165
  347. package/src/memory/job-handlers/conflict.ts +0 -200
  348. package/src/memory/profile-compiler.ts +0 -195
  349. package/src/memory/recall-cache.ts +0 -117
  350. package/src/memory/search/entity.ts +0 -535
  351. package/src/memory/search/query-expansion.test.ts +0 -70
  352. package/src/memory/search/query-expansion.ts +0 -118
  353. package/src/runtime/routes/mcp-routes.ts +0 -20
@@ -168,6 +168,8 @@ Replace `<channel_id>` with the channel's `id` from the contact's `channels` arr
168
168
 
169
169
  Invite links let the guardian share a link or code that automatically grants access when used. Telegram invites use a deep link; voice invites use a phone number + numeric code; email, WhatsApp, and Slack invites use a 6-digit code that the invitee sends to the assistant on the respective channel.
170
170
 
171
+ **Every invite must be bound to a contact.** Before creating an invite, look up the contact with `assistant contacts list` or create one with `assistant contacts upsert`, then pass the contact's `id` via the required `--contact-id` flag.
172
+
171
173
  ### Create a Telegram invite link
172
174
 
173
175
  Use this when the guardian wants to invite someone to message the assistant on Telegram without needing their user ID upfront. The invite link is a shareable Telegram deep link -- when someone opens it, they automatically get trusted-contact access.
@@ -175,7 +177,7 @@ Use this when the guardian wants to invite someone to message the assistant on T
175
177
  **Important**: The shell snippet below emits a `<vellum-sensitive-output>` directive containing the raw invite token. The tool executor automatically strips this directive and replaces the raw token with a placeholder so the LLM never sees it. The placeholder is resolved back to the real token in the final assistant reply.
176
178
 
177
179
  ```bash
178
- INVITE_JSON=$(assistant contacts invites create --source-channel telegram --max-uses 1 --note "<optional note, e.g. the person it is for>" --json)
180
+ INVITE_JSON=$(assistant contacts invites create --source-channel telegram --contact-id "<contact_id>" --max-uses 1 --note "<optional note, e.g. the person it is for>" --json)
179
181
 
180
182
  INVITE_TOKEN=$(printf '%s' "$INVITE_JSON" | python3 -c "
181
183
  import json, sys
@@ -210,6 +212,11 @@ echo "<vellum-sensitive-output kind=\"invite_code\" value=\"$INVITE_TOKEN\" />"
210
212
  echo "$INVITE_URL"
211
213
  ```
212
214
 
215
+ Required flags:
216
+
217
+ - `--source-channel` -- must be `telegram`
218
+ - `--contact-id` -- the ID of the contact this invite is for. Look up or create the contact first with `assistant contacts list` or `assistant contacts upsert`.
219
+
213
220
  Optional flags:
214
221
 
215
222
  - `--max-uses` -- how many times the link can be used (default: 1). Use a higher number for group invites.
@@ -242,12 +249,13 @@ Use this when the guardian wants to authorize a specific phone number to call th
242
249
  **Important**: The response includes a `voiceCode` field that is only returned at creation time and cannot be retrieved later. Extract and present it clearly.
243
250
 
244
251
  ```bash
245
- assistant contacts invites create --source-channel phone --expected-external-user-id "<phone_E164>" --friend-name "<invitee_name>" --guardian-name "<guardian_name>" --max-uses 1 --note "<optional note, e.g. the person it is for>" --json
252
+ assistant contacts invites create --source-channel phone --contact-id "<contact_id>" --expected-external-user-id "<phone_E164>" --friend-name "<invitee_name>" --guardian-name "<guardian_name>" --max-uses 1 --note "<optional note, e.g. the person it is for>" --json
246
253
  ```
247
254
 
248
255
  Required flags:
249
256
 
250
257
  - `--source-channel` -- must be `phone`
258
+ - `--contact-id` -- the ID of the contact this invite is for. Look up or create the contact first with `assistant contacts list` or `assistant contacts upsert`.
251
259
  - `--expected-external-user-id` -- the invitee's phone number in E.164 format (e.g., `+15551234567`)
252
260
  - `--friend-name` -- the invitee's display name (e.g., "Mom", "Dr. Smith"). Used during the voice verification call to personalize the experience.
253
261
  - `--guardian-name` -- the guardian's display name (e.g., "Alex"). Used during the voice verification call so the invitee knows who invited them.
@@ -287,9 +295,14 @@ If the user provides a phone number without the `+` country code prefix, ask the
287
295
  Use this when the guardian wants to invite someone to message the assistant via email. Email invites use a 6-digit code — the invitee sends the code to the assistant's email address to redeem access.
288
296
 
289
297
  ```bash
290
- assistant contacts invites create --source-channel email --contact-name "<invitee_name>" --max-uses 1 --note "<optional note, e.g. the person it is for>" --json
298
+ assistant contacts invites create --source-channel email --contact-id "<contact_id>" --contact-name "<invitee_name>" --max-uses 1 --note "<optional note, e.g. the person it is for>" --json
291
299
  ```
292
300
 
301
+ Required flags:
302
+
303
+ - `--source-channel` -- must be `email`
304
+ - `--contact-id` -- the ID of the contact this invite is for. Look up or create the contact first with `assistant contacts list` or `assistant contacts upsert`.
305
+
293
306
  The response contains `{ ok: true, invite: { id, token, inviteCode, guardianInstruction, channelHandle, ... } }`.
294
307
 
295
308
  - `inviteCode` is the 6-digit code the invitee must send to redeem the invite. It is only returned at creation time.
@@ -313,9 +326,14 @@ If the assistant's email address is not available (AgentMail not configured), te
313
326
  Use this when the guardian wants to invite someone to message the assistant on WhatsApp. WhatsApp invites use a 6-digit code — the invitee sends the code to the assistant's WhatsApp number to redeem access.
314
327
 
315
328
  ```bash
316
- assistant contacts invites create --source-channel whatsapp --contact-name "<invitee_name>" --max-uses 1 --note "<optional note, e.g. the person it is for>" --json
329
+ assistant contacts invites create --source-channel whatsapp --contact-id "<contact_id>" --contact-name "<invitee_name>" --max-uses 1 --note "<optional note, e.g. the person it is for>" --json
317
330
  ```
318
331
 
332
+ Required flags:
333
+
334
+ - `--source-channel` -- must be `whatsapp`
335
+ - `--contact-id` -- the ID of the contact this invite is for. Look up or create the contact first with `assistant contacts list` or `assistant contacts upsert`.
336
+
319
337
  The response contains `{ ok: true, invite: { id, token, inviteCode, guardianInstruction, channelHandle?, ... } }`.
320
338
 
321
339
  - `inviteCode` is the 6-digit code the invitee must send to redeem the invite.
@@ -341,9 +359,14 @@ If the assistant's WhatsApp integration is not configured at all (Meta WhatsApp
341
359
  Use this when the guardian wants to invite someone to message the assistant on Slack. Slack invites use a 6-digit code -- the invitee sends the code as a direct message to the assistant's Slack bot to redeem access.
342
360
 
343
361
  ```bash
344
- assistant contacts invites create --source-channel slack --contact-name "<invitee_name>" --max-uses 1 --note "<optional note, e.g. the person it is for>" --json
362
+ assistant contacts invites create --source-channel slack --contact-id "<contact_id>" --contact-name "<invitee_name>" --max-uses 1 --note "<optional note, e.g. the person it is for>" --json
345
363
  ```
346
364
 
365
+ Required flags:
366
+
367
+ - `--source-channel` -- must be `slack`
368
+ - `--contact-id` -- the ID of the contact this invite is for. Look up or create the contact first with `assistant contacts list` or `assistant contacts upsert`.
369
+
347
370
  The response follows the same shape as email and WhatsApp invites (`inviteCode`, `guardianInstruction`, `channelHandle`).
348
371
 
349
372
  **Presenting to the guardian**: Give the guardian the invite code and instructions for the invitee to send the code as a DM to the assistant's Slack bot.
@@ -456,6 +479,7 @@ Each channel has:
456
479
  - `Channel already blocked` -- the channel has already been blocked.
457
480
  - `Cannot revoke a blocked channel` -- the channel is blocked; blocking is stronger than revoking. Tell the user the contact is already blocked.
458
481
  - `sourceChannel is required for create` -- when creating an invite, always pass `--source-channel`.
482
+ - `contactId is required for create` -- when creating an invite, always pass `--contact-id`. Look up or create the contact first.
459
483
  - `expectedExternalUserId is required for voice invites` -- voice invites must include the invitee's phone number via `--expected-external-user-id`.
460
484
  - `expectedExternalUserId must be in E.164 format` -- the phone number must start with `+` followed by country code and number (e.g., `+15551234567`).
461
485
  - `friendName is required for voice invites` -- voice invites must include the invitee's display name via `--friend-name`.
@@ -480,21 +504,21 @@ Each channel has:
480
504
 
481
505
  **"Show me blocked contacts"** -- List contacts with `assistant contacts list --json` and filter for channels with `status: "blocked"`.
482
506
 
483
- **"Create a Telegram invite link"** / **"Invite someone on Telegram"** -- Create an invite with `assistant contacts invites create --source-channel telegram`, look up the bot username, build the deep link, and present it with sharing instructions.
507
+ **"Create a Telegram invite link"** / **"Invite someone on Telegram"** -- Look up or create the contact first, then create an invite with `assistant contacts invites create --source-channel telegram --contact-id <contact_id>`, look up the bot username, build the deep link, and present it with sharing instructions.
484
508
 
485
- **"Invite someone by email"** / **"Send an email invite"** -- Create an invite with `assistant contacts invites create --source-channel email`. Present the 6-digit invite code and the assistant's email address. Tell the guardian to share both with the invitee.
509
+ **"Invite someone by email"** / **"Send an email invite"** -- Look up or create the contact first, then create an invite with `assistant contacts invites create --source-channel email --contact-id <contact_id>`. Present the 6-digit invite code and the assistant's email address. Tell the guardian to share both with the invitee.
486
510
 
487
- **"Invite someone on WhatsApp"** -- Create an invite with `assistant contacts invites create --source-channel whatsapp`. Present the 6-digit invite code. If `channelHandle` is returned, also present the assistant's WhatsApp number; otherwise, tell the guardian to share the code and instruct the invitee to send it to the assistant on WhatsApp.
511
+ **"Invite someone on WhatsApp"** -- Look up or create the contact first, then create an invite with `assistant contacts invites create --source-channel whatsapp --contact-id <contact_id>`. Present the 6-digit invite code. If `channelHandle` is returned, also present the assistant's WhatsApp number; otherwise, tell the guardian to share the code and instruct the invitee to send it to the assistant on WhatsApp.
488
512
 
489
- **"Invite someone on Slack"** -- Create an invite with `assistant contacts invites create --source-channel slack`. Present the 6-digit invite code and tell the guardian to have the invitee DM the code to the assistant's Slack bot.
513
+ **"Invite someone on Slack"** -- Look up or create the contact first, then create an invite with `assistant contacts invites create --source-channel slack --contact-id <contact_id>`. Present the 6-digit invite code and tell the guardian to have the invitee DM the code to the assistant's Slack bot.
490
514
 
491
515
  **"Show my invites"** / **"List active invite links"** -- List invites with `assistant contacts invites list --source-channel telegram --json`, present active invites with uses remaining and expiration info. Use the appropriate `--source-channel` value for other channels.
492
516
 
493
517
  **"Revoke invite"** / **"Cancel invite link"** -- List invites to identify the target, confirm, then revoke with `assistant contacts invites revoke <invite_id> --json`.
494
518
 
495
- **"Create a voice invite for +15551234567"** -- Create a voice invite with `assistant contacts invites create --source-channel phone --expected-external-user-id "+15551234567" --friend-name "<name>" --guardian-name "<name>"`. Present the invite code and instructions: the person must call from that number and enter the code.
519
+ **"Create a voice invite for +15551234567"** -- Look up or create the contact first, then create a voice invite with `assistant contacts invites create --source-channel phone --contact-id <contact_id> --expected-external-user-id "+15551234567" --friend-name "<name>" --guardian-name "<name>"`. Present the invite code and instructions: the person must call from that number and enter the code.
496
520
 
497
- **"Let my mom call in"** / **"Invite someone by phone"** -- Ask for the phone number in E.164 format, create a voice invite with `assistant contacts invites create --source-channel phone`, and present the code + calling instructions.
521
+ **"Let my mom call in"** / **"Invite someone by phone"** -- Ask for the phone number in E.164 format, look up or create the contact first, then create a voice invite with `assistant contacts invites create --source-channel phone --contact-id <contact_id>`, and present the code + calling instructions.
498
522
 
499
523
  **"Show my voice invites"** / **"List phone invites"** -- List invites with `assistant contacts invites list --source-channel phone --json`, present active invites with bound phone number and expiration info.
500
524
 
@@ -43,7 +43,7 @@ export async function run(
43
43
  }
44
44
 
45
45
  try {
46
- const connection = resolveOAuthConnection("integration:gmail");
46
+ const connection = await resolveOAuthConnection("integration:google");
47
47
  switch (action) {
48
48
  case "list": {
49
49
  const pageSize = (input.page_size as number) ?? 50;
@@ -21,7 +21,7 @@ Do not offer AgentMail as an option or mention it unless the user specifically a
21
21
  ## Communication Style
22
22
 
23
23
  - **Be action-oriented.** When the user asks to do something ("declutter", "check my email"), start doing it immediately. Don't ask for permission to read their inbox — that's obviously what they want.
24
- - **Keep it human.** Never mention OAuth, tokens, APIs, sandboxes, credential proxies, or other technical internals. If something isn't working, say "Gmail needs to be reconnected" — not "the OAuth2 access token for integration:gmail has expired."
24
+ - **Keep it human.** Never mention OAuth, tokens, APIs, sandboxes, credential proxies, or other technical internals. If something isn't working, say "Gmail needs to be reconnected" — not "the OAuth2 access token for integration:google has expired."
25
25
  - **Show progress.** When running a tool that scans many emails, tell the user what you're doing: "Scanning your inbox for clutter..." Don't go silent.
26
26
  - **Be brief and warm.** One or two sentences per update is plenty. Don't over-explain what you're about to do — just do it and narrate lightly.
27
27
 
@@ -42,6 +42,10 @@
42
42
  "reason": {
43
43
  "type": "string",
44
44
  "description": "Brief non-technical explanation of why this tool is being called"
45
+ },
46
+ "account": {
47
+ "type": "string",
48
+ "description": "Email address of the Gmail account to use. Required when multiple Gmail accounts are connected. If omitted, uses the most recently connected account."
45
49
  }
46
50
  },
47
51
  "required": ["confidence"]
@@ -89,6 +93,10 @@
89
93
  "reason": {
90
94
  "type": "string",
91
95
  "description": "Brief non-technical explanation of why this tool is being called"
96
+ },
97
+ "account": {
98
+ "type": "string",
99
+ "description": "Email address of the Gmail account to use. Required when multiple Gmail accounts are connected. If omitted, uses the most recently connected account."
92
100
  }
93
101
  },
94
102
  "required": ["confidence"]
@@ -115,6 +123,10 @@
115
123
  "reason": {
116
124
  "type": "string",
117
125
  "description": "Brief non-technical explanation of why this tool is being called"
126
+ },
127
+ "account": {
128
+ "type": "string",
129
+ "description": "Email address of the Gmail account to use. Required when multiple Gmail accounts are connected. If omitted, uses the most recently connected account."
118
130
  }
119
131
  },
120
132
  "required": ["message_id", "confidence"]
@@ -141,6 +153,10 @@
141
153
  "reason": {
142
154
  "type": "string",
143
155
  "description": "Brief non-technical explanation of why this tool is being called"
156
+ },
157
+ "account": {
158
+ "type": "string",
159
+ "description": "Email address of the Gmail account to use. Required when multiple Gmail accounts are connected. If omitted, uses the most recently connected account."
144
160
  }
145
161
  },
146
162
  "required": ["message_id", "confidence"]
@@ -183,6 +199,10 @@
183
199
  "reason": {
184
200
  "type": "string",
185
201
  "description": "Brief non-technical explanation of why this tool is being called"
202
+ },
203
+ "account": {
204
+ "type": "string",
205
+ "description": "Email address of the Gmail account to use. Required when multiple Gmail accounts are connected. If omitted, uses the most recently connected account."
186
206
  }
187
207
  },
188
208
  "required": ["to", "subject", "body"]
@@ -209,6 +229,10 @@
209
229
  "reason": {
210
230
  "type": "string",
211
231
  "description": "Brief non-technical explanation of why this tool is being called"
232
+ },
233
+ "account": {
234
+ "type": "string",
235
+ "description": "Email address of the Gmail account to use. Required when multiple Gmail accounts are connected. If omitted, uses the most recently connected account."
212
236
  }
213
237
  },
214
238
  "required": ["draft_id", "confidence"]
@@ -244,6 +268,10 @@
244
268
  "reason": {
245
269
  "type": "string",
246
270
  "description": "Brief non-technical explanation of why this tool is being called"
271
+ },
272
+ "account": {
273
+ "type": "string",
274
+ "description": "Email address of the Gmail account to use. Required when multiple Gmail accounts are connected. If omitted, uses the most recently connected account."
247
275
  }
248
276
  },
249
277
  "required": ["action", "message_id"]
@@ -278,6 +306,10 @@
278
306
  "reason": {
279
307
  "type": "string",
280
308
  "description": "Brief non-technical explanation of why this tool is being called"
309
+ },
310
+ "account": {
311
+ "type": "string",
312
+ "description": "Email address of the Gmail account to use. Required when multiple Gmail accounts are connected. If omitted, uses the most recently connected account."
281
313
  }
282
314
  },
283
315
  "required": ["message_id", "to", "confidence"]
@@ -309,6 +341,10 @@
309
341
  "reason": {
310
342
  "type": "string",
311
343
  "description": "Brief non-technical explanation of why this tool is being called"
344
+ },
345
+ "account": {
346
+ "type": "string",
347
+ "description": "Email address of the Gmail account to use. Required when multiple Gmail accounts are connected. If omitted, uses the most recently connected account."
312
348
  }
313
349
  },
314
350
  "required": ["action", "confidence"]
@@ -378,6 +414,10 @@
378
414
  "reason": {
379
415
  "type": "string",
380
416
  "description": "Brief non-technical explanation of why this tool is being called"
417
+ },
418
+ "account": {
419
+ "type": "string",
420
+ "description": "Email address of the Gmail account to use. Required when multiple Gmail accounts are connected. If omitted, uses the most recently connected account."
381
421
  }
382
422
  },
383
423
  "required": ["action", "confidence"]
@@ -429,6 +469,10 @@
429
469
  "reason": {
430
470
  "type": "string",
431
471
  "description": "Brief non-technical explanation of why this tool is being called"
472
+ },
473
+ "account": {
474
+ "type": "string",
475
+ "description": "Email address of the Gmail account to use. Required when multiple Gmail accounts are connected. If omitted, uses the most recently connected account."
432
476
  }
433
477
  },
434
478
  "required": ["action", "confidence"]
@@ -463,6 +507,10 @@
463
507
  "reason": {
464
508
  "type": "string",
465
509
  "description": "Brief non-technical explanation of why this tool is being called"
510
+ },
511
+ "account": {
512
+ "type": "string",
513
+ "description": "Email address of the Gmail account to use. Required when multiple Gmail accounts are connected. If omitted, uses the most recently connected account."
466
514
  }
467
515
  }
468
516
  },
@@ -496,6 +544,10 @@
496
544
  "reason": {
497
545
  "type": "string",
498
546
  "description": "Brief non-technical explanation of why this tool is being called"
547
+ },
548
+ "account": {
549
+ "type": "string",
550
+ "description": "Email address of the Gmail account to use. Required when multiple Gmail accounts are connected. If omitted, uses the most recently connected account."
499
551
  }
500
552
  }
501
553
  },
@@ -18,6 +18,7 @@ export async function run(
18
18
  input: Record<string, unknown>,
19
19
  context: ToolContext,
20
20
  ): Promise<ToolExecutionResult> {
21
+ const account = input.account as string | undefined;
21
22
  const query = input.query as string | undefined;
22
23
  const scanId = input.scan_id as string | undefined;
23
24
  const senderIds = input.sender_ids as string[] | undefined;
@@ -34,7 +35,10 @@ export async function run(
34
35
  }
35
36
 
36
37
  try {
37
- const connection = resolveOAuthConnection("integration:gmail");
38
+ const connection = await resolveOAuthConnection(
39
+ "integration:google",
40
+ account,
41
+ );
38
42
  const allMessageIds: string[] = [];
39
43
  let pageToken: string | undefined;
40
44
  let truncated = false;
@@ -103,7 +107,10 @@ export async function run(
103
107
  } else if (messageId) {
104
108
  // Single message path
105
109
  try {
106
- const connection = resolveOAuthConnection("integration:gmail");
110
+ const connection = await resolveOAuthConnection(
111
+ "integration:google",
112
+ account,
113
+ );
107
114
  await modifyMessage(connection, messageId, { removeLabelIds: ["INBOX"] });
108
115
  return ok("Message archived.");
109
116
  } catch (e) {
@@ -121,7 +128,10 @@ export async function run(
121
128
  }
122
129
 
123
130
  try {
124
- const connection = resolveOAuthConnection("integration:gmail");
131
+ const connection = await resolveOAuthConnection(
132
+ "integration:google",
133
+ account,
134
+ );
125
135
  if (messageIds.length === 1) {
126
136
  await modifyMessage(connection, messageIds[0], {
127
137
  removeLabelIds: ["INBOX"],
@@ -48,6 +48,7 @@ export async function run(
48
48
  input: Record<string, unknown>,
49
49
  context: ToolContext,
50
50
  ): Promise<ToolExecutionResult> {
51
+ const account = input.account as string | undefined;
51
52
  const action = input.action as string;
52
53
  const messageId = input.message_id as string;
53
54
 
@@ -56,7 +57,10 @@ export async function run(
56
57
 
57
58
  if (action === "list") {
58
59
  try {
59
- const connection = resolveOAuthConnection("integration:gmail");
60
+ const connection = await resolveOAuthConnection(
61
+ "integration:google",
62
+ account,
63
+ );
60
64
  const message = await getMessage(connection, messageId, "full");
61
65
  const attachments = collectAttachments(message.payload?.parts);
62
66
 
@@ -78,7 +82,10 @@ export async function run(
78
82
  if (!filename) return err("filename is required for download.");
79
83
 
80
84
  try {
81
- const connection = resolveOAuthConnection("integration:gmail");
85
+ const connection = await resolveOAuthConnection(
86
+ "integration:google",
87
+ account,
88
+ );
82
89
  const attachment = await getAttachment(
83
90
  connection,
84
91
  messageId,
@@ -10,6 +10,7 @@ export async function run(
10
10
  input: Record<string, unknown>,
11
11
  _context: ToolContext,
12
12
  ): Promise<ToolExecutionResult> {
13
+ const account = input.account as string | undefined;
13
14
  const to = input.to as string;
14
15
  const subject = input.subject as string;
15
16
  const body = input.body as string;
@@ -22,7 +23,10 @@ export async function run(
22
23
  if (!body) return err("body is required.");
23
24
 
24
25
  try {
25
- const connection = resolveOAuthConnection("integration:gmail");
26
+ const connection = await resolveOAuthConnection(
27
+ "integration:google",
28
+ account,
29
+ );
26
30
  const draft = await createDraft(
27
31
  connection,
28
32
  to,
@@ -18,6 +18,7 @@ export async function run(
18
18
  input: Record<string, unknown>,
19
19
  _context: ToolContext,
20
20
  ): Promise<ToolExecutionResult> {
21
+ const account = input.account as string | undefined;
21
22
  const action = input.action as string;
22
23
 
23
24
  if (!action) {
@@ -25,7 +26,10 @@ export async function run(
25
26
  }
26
27
 
27
28
  try {
28
- const connection = resolveOAuthConnection("integration:gmail");
29
+ const connection = await resolveOAuthConnection(
30
+ "integration:google",
31
+ account,
32
+ );
29
33
  switch (action) {
30
34
  case "list": {
31
35
  const filters = await listFilters(connection);
@@ -30,6 +30,7 @@ export async function run(
30
30
  input: Record<string, unknown>,
31
31
  _context: ToolContext,
32
32
  ): Promise<ToolExecutionResult> {
33
+ const account = input.account as string | undefined;
33
34
  const action = input.action as string;
34
35
 
35
36
  if (!action) {
@@ -37,7 +38,10 @@ export async function run(
37
38
  }
38
39
 
39
40
  try {
40
- const connection = resolveOAuthConnection("integration:gmail");
41
+ const connection = await resolveOAuthConnection(
42
+ "integration:google",
43
+ account,
44
+ );
41
45
  switch (action) {
42
46
  case "track": {
43
47
  const messageId = input.message_id as string;
@@ -67,6 +67,7 @@ export async function run(
67
67
  input: Record<string, unknown>,
68
68
  _context: ToolContext,
69
69
  ): Promise<ToolExecutionResult> {
70
+ const account = input.account as string | undefined;
70
71
  const messageId = input.message_id as string;
71
72
  const forwardTo = input.to as string;
72
73
  const additionalText = input.text as string | undefined;
@@ -75,7 +76,10 @@ export async function run(
75
76
  if (!forwardTo) return err("to is required.");
76
77
 
77
78
  try {
78
- const connection = resolveOAuthConnection("integration:gmail");
79
+ const connection = await resolveOAuthConnection(
80
+ "integration:google",
81
+ account,
82
+ );
79
83
  const message = await getMessage(connection, messageId, "full");
80
84
  const headers = message.payload?.headers ?? [];
81
85
  const originalFrom = extractHeader(headers, "From");
@@ -13,6 +13,7 @@ export async function run(
13
13
  input: Record<string, unknown>,
14
14
  _context: ToolContext,
15
15
  ): Promise<ToolExecutionResult> {
16
+ const account = input.account as string | undefined;
16
17
  const messageId = input.message_id as string | undefined;
17
18
  const messageIds = input.message_ids as string[] | undefined;
18
19
  const addLabelIds = input.add_label_ids as string[] | undefined;
@@ -20,7 +21,10 @@ export async function run(
20
21
 
21
22
  if (messageIds && messageIds.length > 0) {
22
23
  try {
23
- const connection = resolveOAuthConnection("integration:gmail");
24
+ const connection = await resolveOAuthConnection(
25
+ "integration:google",
26
+ account,
27
+ );
24
28
  await batchModifyMessages(connection, messageIds, {
25
29
  addLabelIds,
26
30
  removeLabelIds,
@@ -33,7 +37,10 @@ export async function run(
33
37
 
34
38
  if (messageId) {
35
39
  try {
36
- const connection = resolveOAuthConnection("integration:gmail");
40
+ const connection = await resolveOAuthConnection(
41
+ "integration:google",
42
+ account,
43
+ );
37
44
  await modifyMessage(connection, messageId, {
38
45
  addLabelIds,
39
46
  removeLabelIds,
@@ -44,6 +44,7 @@ export async function run(
44
44
  input: Record<string, unknown>,
45
45
  _context: ToolContext,
46
46
  ): Promise<ToolExecutionResult> {
47
+ const account = input.account as string | undefined;
47
48
  const maxMessages = Math.min(
48
49
  (input.max_messages as number) ?? 2000,
49
50
  MAX_MESSAGES_CAP,
@@ -55,7 +56,10 @@ export async function run(
55
56
  const query = `in:inbox -has:unsubscribe newer_than:${timeRange}`;
56
57
 
57
58
  try {
58
- const connection = resolveOAuthConnection("integration:gmail");
59
+ const connection = await resolveOAuthConnection(
60
+ "integration:google",
61
+ account,
62
+ );
59
63
  // Pipeline: fire metadata fetches for each page of IDs as they arrive
60
64
  const allMessageIds: string[] = [];
61
65
  const fetchPromises: Promise<GmailMessage[]>[] = [];
@@ -10,11 +10,15 @@ export async function run(
10
10
  input: Record<string, unknown>,
11
11
  _context: ToolContext,
12
12
  ): Promise<ToolExecutionResult> {
13
+ const account = input.account as string | undefined;
13
14
  const draftId = input.draft_id as string;
14
15
  if (!draftId) return err("draft_id is required.");
15
16
 
16
17
  try {
17
- const connection = resolveOAuthConnection("integration:gmail");
18
+ const connection = await resolveOAuthConnection(
19
+ "integration:google",
20
+ account,
21
+ );
18
22
  const msg = await sendDraft(connection, draftId);
19
23
  return ok(`Draft sent (Message ID: ${msg.id}).`);
20
24
  } catch (e) {
@@ -48,6 +48,7 @@ export async function run(
48
48
  input: Record<string, unknown>,
49
49
  _context: ToolContext,
50
50
  ): Promise<ToolExecutionResult> {
51
+ const account = input.account as string | undefined;
51
52
  const query = (input.query as string) ?? "category:promotions newer_than:90d";
52
53
  const maxMessages = Math.min(
53
54
  (input.max_messages as number) ?? 5000,
@@ -57,7 +58,10 @@ export async function run(
57
58
  const inputPageToken = input.page_token as string | undefined;
58
59
 
59
60
  try {
60
- const connection = resolveOAuthConnection("integration:gmail");
61
+ const connection = await resolveOAuthConnection(
62
+ "integration:google",
63
+ account,
64
+ );
61
65
  // Pipeline: fire metadata fetches for each page of IDs as they arrive,
62
66
  // overlapping fetch latency with pagination latency
63
67
  const allMessageIds: string[] = [];
@@ -10,6 +10,7 @@ export async function run(
10
10
  input: Record<string, unknown>,
11
11
  _context: ToolContext,
12
12
  ): Promise<ToolExecutionResult> {
13
+ const account = input.account as string | undefined;
13
14
  const messageId = input.message_id as string;
14
15
 
15
16
  if (!messageId) {
@@ -17,7 +18,10 @@ export async function run(
17
18
  }
18
19
 
19
20
  try {
20
- const connection = resolveOAuthConnection("integration:gmail");
21
+ const connection = await resolveOAuthConnection(
22
+ "integration:google",
23
+ account,
24
+ );
21
25
  await trashMessage(connection, messageId);
22
26
  return ok("Message moved to trash.");
23
27
  } catch (e) {
@@ -18,6 +18,7 @@ export async function run(
18
18
  input: Record<string, unknown>,
19
19
  context: ToolContext,
20
20
  ): Promise<ToolExecutionResult> {
21
+ const account = input.account as string | undefined;
21
22
  if (!context.triggeredBySurfaceAction) {
22
23
  return err(
23
24
  "This tool requires user confirmation via a surface action. Present results in a selection table with action buttons and wait for the user to click before proceeding.",
@@ -31,7 +32,10 @@ export async function run(
31
32
  }
32
33
 
33
34
  try {
34
- const connection = resolveOAuthConnection("integration:gmail");
35
+ const connection = await resolveOAuthConnection(
36
+ "integration:google",
37
+ account,
38
+ );
35
39
  const message = await getMessage(connection, messageId, "metadata", [
36
40
  "List-Unsubscribe",
37
41
  "List-Unsubscribe-Post",
@@ -14,6 +14,7 @@ export async function run(
14
14
  input: Record<string, unknown>,
15
15
  _context: ToolContext,
16
16
  ): Promise<ToolExecutionResult> {
17
+ const account = input.account as string | undefined;
17
18
  const action = input.action as string;
18
19
 
19
20
  if (!action) {
@@ -21,7 +22,10 @@ export async function run(
21
22
  }
22
23
 
23
24
  try {
24
- const connection = resolveOAuthConnection("integration:gmail");
25
+ const connection = await resolveOAuthConnection(
26
+ "integration:google",
27
+ account,
28
+ );
25
29
  switch (action) {
26
30
  case "get": {
27
31
  const settings = await getVacation(connection);