@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
@@ -16,8 +16,8 @@ You are a Google Calendar assistant with full access to the user's calendar. Use
16
16
  Before using any Calendar tool, verify that Google Calendar is connected by attempting a lightweight call (e.g., `calendar_list_events` with a narrow date range). If the call fails with a token/authorization error:
17
17
 
18
18
  1. **Do NOT call `credential_store oauth2_connect` yourself.** You do not have valid OAuth client credentials, and fabricating a client_id will cause a "401: invalid_client" error from Google.
19
- 2. Instead, load the **google-oauth-setup** skill, which walks the user through creating real credentials in Google Cloud Console:
20
- - Call `skill_load` with `skill: "google-oauth-setup"` to load the dependency skill.
19
+ 2. Instead, load the **google-oauth-applescript** skill, which walks the user through creating real credentials in Google Cloud Console:
20
+ - Call `skill_load` with `skill: "google-oauth-applescript"` to load the dependency skill.
21
21
  3. Tell the user: _"Google Calendar isn't connected yet. I've loaded a setup guide that will walk you through connecting your Google account — it only takes a couple of minutes."_
22
22
 
23
23
  ## Capabilities
@@ -1,3 +1,6 @@
1
+ import type { OAuthConnection } from "../../../oauth/connection.js";
2
+
3
+ const GOOGLE_CALENDAR_BASE_URL = "https://www.googleapis.com/calendar/v3";
1
4
  import type {
2
5
  CalendarEvent,
3
6
  CalendarEventsListResponse,
@@ -6,8 +9,6 @@ import type {
6
9
  FreeBusyResponse,
7
10
  } from "./types.js";
8
11
 
9
- const CALENDAR_API_BASE = "https://www.googleapis.com/calendar/v3";
10
-
11
12
  export class CalendarApiError extends Error {
12
13
  constructor(
13
14
  public readonly status: number,
@@ -20,39 +21,82 @@ export class CalendarApiError extends Error {
20
21
  }
21
22
 
22
23
  async function request<T>(
23
- token: string,
24
+ connection: OAuthConnection,
24
25
  path: string,
25
26
  options?: RequestInit,
27
+ query?: Record<string, string | string[]>,
26
28
  ): Promise<T> {
27
- const url = `${CALENDAR_API_BASE}${path}`;
28
- const resp = await fetch(url, {
29
- ...options,
29
+ const method = (options?.method ?? "GET").toUpperCase();
30
+
31
+ // Extract non-auth headers
32
+ let extraHeaders: Record<string, string> | undefined;
33
+ if (options?.headers) {
34
+ const raw = options.headers;
35
+ const result: Record<string, string> = {};
36
+ if (raw instanceof Headers) {
37
+ raw.forEach((v, k) => {
38
+ if (k.toLowerCase() !== "authorization") result[k] = v;
39
+ });
40
+ } else if (Array.isArray(raw)) {
41
+ for (const [k, v] of raw) {
42
+ if (k.toLowerCase() !== "authorization") result[k] = v;
43
+ }
44
+ } else {
45
+ for (const [k, v] of Object.entries(raw)) {
46
+ if (k.toLowerCase() !== "authorization" && v !== undefined)
47
+ result[k] = v;
48
+ }
49
+ }
50
+ if (Object.keys(result).length > 0) extraHeaders = result;
51
+ }
52
+
53
+ // Extract body
54
+ let reqBody: unknown | undefined;
55
+ if (options?.body) {
56
+ if (typeof options.body === "string") {
57
+ try {
58
+ reqBody = JSON.parse(options.body);
59
+ } catch {
60
+ reqBody = options.body;
61
+ }
62
+ } else {
63
+ reqBody = options.body;
64
+ }
65
+ }
66
+
67
+ const resp = await connection.request({
68
+ method,
69
+ path,
70
+ query,
71
+ baseUrl: GOOGLE_CALENDAR_BASE_URL,
30
72
  headers: {
31
- Authorization: `Bearer ${token}`,
32
73
  "Content-Type": "application/json",
33
- ...options?.headers,
74
+ ...extraHeaders,
34
75
  },
76
+ body: reqBody,
35
77
  });
36
- if (!resp.ok) {
37
- const body = await resp.text().catch(() => "");
78
+
79
+ if (resp.status < 200 || resp.status >= 300) {
80
+ const bodyStr =
81
+ typeof resp.body === "string"
82
+ ? resp.body
83
+ : JSON.stringify(resp.body ?? "");
38
84
  throw new CalendarApiError(
39
85
  resp.status,
40
- resp.statusText,
41
- `Calendar API ${resp.status}: ${body}`,
86
+ "",
87
+ `Calendar API ${resp.status}: ${bodyStr}`,
42
88
  );
43
89
  }
44
- const contentLength = resp.headers.get("content-length");
45
- if (resp.status === 204 || contentLength === "0") {
90
+
91
+ if (resp.status === 204 || resp.body === undefined) {
46
92
  return undefined as T;
47
93
  }
48
- const text = await resp.text();
49
- if (!text) return undefined as T;
50
- return JSON.parse(text) as T;
94
+ return resp.body as T;
51
95
  }
52
96
 
53
97
  /** List events from a calendar. */
54
98
  export async function listEvents(
55
- token: string,
99
+ connection: OAuthConnection,
56
100
  calendarId = "primary",
57
101
  options?: {
58
102
  timeMin?: string;
@@ -65,40 +109,42 @@ export async function listEvents(
65
109
  syncToken?: string;
66
110
  },
67
111
  ): Promise<CalendarEventsListResponse> {
68
- const params = new URLSearchParams();
112
+ const query: Record<string, string> = {};
69
113
 
70
- if (options?.timeMin) params.set("timeMin", options.timeMin);
71
- if (options?.timeMax) params.set("timeMax", options.timeMax);
72
- params.set("maxResults", String(options?.maxResults ?? 25));
73
- if (options?.query) params.set("q", options.query);
114
+ if (options?.timeMin) query.timeMin = options.timeMin;
115
+ if (options?.timeMax) query.timeMax = options.timeMax;
116
+ query.maxResults = String(options?.maxResults ?? 25);
117
+ if (options?.query) query.q = options.query;
74
118
 
75
119
  // Default to expanding recurring events into instances
76
120
  const singleEvents = options?.singleEvents ?? true;
77
- params.set("singleEvents", String(singleEvents));
121
+ query.singleEvents = String(singleEvents);
78
122
 
79
123
  if (singleEvents && options?.orderBy) {
80
- params.set("orderBy", options.orderBy);
124
+ query.orderBy = options.orderBy;
81
125
  } else if (singleEvents) {
82
- params.set("orderBy", "startTime");
126
+ query.orderBy = "startTime";
83
127
  }
84
128
 
85
- if (options?.pageToken) params.set("pageToken", options.pageToken);
86
- if (options?.syncToken) params.set("syncToken", options.syncToken);
129
+ if (options?.pageToken) query.pageToken = options.pageToken;
130
+ if (options?.syncToken) query.syncToken = options.syncToken;
87
131
 
88
132
  return request<CalendarEventsListResponse>(
89
- token,
90
- `/calendars/${encodeURIComponent(calendarId)}/events?${params}`,
133
+ connection,
134
+ `/calendars/${encodeURIComponent(calendarId)}/events`,
135
+ undefined,
136
+ query,
91
137
  );
92
138
  }
93
139
 
94
140
  /** Get a single event by ID. */
95
141
  export async function getEvent(
96
- token: string,
142
+ connection: OAuthConnection,
97
143
  eventId: string,
98
144
  calendarId = "primary",
99
145
  ): Promise<CalendarEvent> {
100
146
  return request<CalendarEvent>(
101
- token,
147
+ connection,
102
148
  `/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(
103
149
  eventId,
104
150
  )}`,
@@ -107,7 +153,7 @@ export async function getEvent(
107
153
 
108
154
  /** Create a new event. */
109
155
  export async function createEvent(
110
- token: string,
156
+ connection: OAuthConnection,
111
157
  event: {
112
158
  summary: string;
113
159
  start: { dateTime?: string; date?: string; timeZone?: string };
@@ -119,20 +165,20 @@ export async function createEvent(
119
165
  calendarId = "primary",
120
166
  sendUpdates: "all" | "externalOnly" | "none" = "all",
121
167
  ): Promise<CalendarEvent> {
122
- const params = new URLSearchParams({ sendUpdates });
123
168
  return request<CalendarEvent>(
124
- token,
125
- `/calendars/${encodeURIComponent(calendarId)}/events?${params}`,
169
+ connection,
170
+ `/calendars/${encodeURIComponent(calendarId)}/events`,
126
171
  {
127
172
  method: "POST",
128
173
  body: JSON.stringify(event),
129
174
  },
175
+ { sendUpdates },
130
176
  );
131
177
  }
132
178
 
133
179
  /** Update an event (patch). */
134
180
  export async function patchEvent(
135
- token: string,
181
+ connection: OAuthConnection,
136
182
  eventId: string,
137
183
  updates: Partial<{
138
184
  summary: string;
@@ -145,25 +191,25 @@ export async function patchEvent(
145
191
  calendarId = "primary",
146
192
  sendUpdates: "all" | "externalOnly" | "none" = "all",
147
193
  ): Promise<CalendarEvent> {
148
- const params = new URLSearchParams({ sendUpdates });
149
194
  return request<CalendarEvent>(
150
- token,
195
+ connection,
151
196
  `/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(
152
197
  eventId,
153
- )}?${params}`,
198
+ )}`,
154
199
  {
155
200
  method: "PATCH",
156
201
  body: JSON.stringify(updates),
157
202
  },
203
+ { sendUpdates },
158
204
  );
159
205
  }
160
206
 
161
207
  /** Query free/busy information. */
162
208
  export async function freeBusy(
163
- token: string,
209
+ connection: OAuthConnection,
164
210
  query: FreeBusyRequest,
165
211
  ): Promise<FreeBusyResponse> {
166
- return request<FreeBusyResponse>(token, "/freeBusy", {
212
+ return request<FreeBusyResponse>(connection, "/freeBusy", {
167
213
  method: "POST",
168
214
  body: JSON.stringify(query),
169
215
  });
@@ -171,7 +217,7 @@ export async function freeBusy(
171
217
 
172
218
  /** List calendars the user has access to. */
173
219
  export async function listCalendars(
174
- token: string,
220
+ connection: OAuthConnection,
175
221
  ): Promise<CalendarListResponse> {
176
- return request<CalendarListResponse>(token, "/users/me/calendarList");
222
+ return request<CalendarListResponse>(connection, "/users/me/calendarList");
177
223
  }
@@ -3,7 +3,7 @@ import type {
3
3
  ToolExecutionResult,
4
4
  } from "../../../../tools/types.js";
5
5
  import * as calendar from "../calendar-client.js";
6
- import { ok, withCalendarToken } from "./shared.js";
6
+ import { getCalendarConnection, ok } from "./shared.js";
7
7
 
8
8
  export async function run(
9
9
  input: Record<string, unknown>,
@@ -14,14 +14,13 @@ export async function run(
14
14
  const calendarIds = (input.calendar_ids as string[]) ?? ["primary"];
15
15
  const timezone = input.timezone as string | undefined;
16
16
 
17
- return withCalendarToken(async (token) => {
18
- const result = await calendar.freeBusy(token, {
19
- timeMin,
20
- timeMax,
21
- timeZone: timezone,
22
- items: calendarIds.map((id) => ({ id })),
23
- });
24
-
25
- return ok(JSON.stringify(result, null, 2));
17
+ const connection = getCalendarConnection();
18
+ const result = await calendar.freeBusy(connection, {
19
+ timeMin,
20
+ timeMax,
21
+ timeZone: timezone,
22
+ items: calendarIds.map((id) => ({ id })),
26
23
  });
24
+
25
+ return ok(JSON.stringify(result, null, 2));
27
26
  }
@@ -3,7 +3,7 @@ import type {
3
3
  ToolExecutionResult,
4
4
  } from "../../../../tools/types.js";
5
5
  import * as calendar from "../calendar-client.js";
6
- import { ok, withCalendarToken } from "./shared.js";
6
+ import { getCalendarConnection, ok } from "./shared.js";
7
7
 
8
8
  export async function run(
9
9
  input: Record<string, unknown>,
@@ -40,9 +40,8 @@ export async function run(
40
40
  eventBody.attendees = attendees.map((email) => ({ email }));
41
41
  }
42
42
 
43
- return withCalendarToken(async (token) => {
44
- const event = await calendar.createEvent(token, eventBody, calendarId);
45
- const link = event.htmlLink ? ` View it here: ${event.htmlLink}` : "";
46
- return ok(`Event created (ID: ${event.id}).${link}`);
47
- });
43
+ const connection = getCalendarConnection();
44
+ const event = await calendar.createEvent(connection, eventBody, calendarId);
45
+ const link = event.htmlLink ? ` View it here: ${event.htmlLink}` : "";
46
+ return ok(`Event created (ID: ${event.id}).${link}`);
48
47
  }
@@ -3,7 +3,7 @@ import type {
3
3
  ToolExecutionResult,
4
4
  } from "../../../../tools/types.js";
5
5
  import * as calendar from "../calendar-client.js";
6
- import { ok, withCalendarToken } from "./shared.js";
6
+ import { getCalendarConnection, ok } from "./shared.js";
7
7
 
8
8
  export async function run(
9
9
  input: Record<string, unknown>,
@@ -12,8 +12,7 @@ export async function run(
12
12
  const eventId = input.event_id as string;
13
13
  const calendarId = (input.calendar_id as string) ?? "primary";
14
14
 
15
- return withCalendarToken(async (token) => {
16
- const event = await calendar.getEvent(token, eventId, calendarId);
17
- return ok(JSON.stringify(event, null, 2));
18
- });
15
+ const connection = getCalendarConnection();
16
+ const event = await calendar.getEvent(connection, eventId, calendarId);
17
+ return ok(JSON.stringify(event, null, 2));
19
18
  }
@@ -3,7 +3,7 @@ import type {
3
3
  ToolExecutionResult,
4
4
  } from "../../../../tools/types.js";
5
5
  import * as calendar from "../calendar-client.js";
6
- import { ok, withCalendarToken } from "./shared.js";
6
+ import { getCalendarConnection, ok } from "./shared.js";
7
7
 
8
8
  export async function run(
9
9
  input: Record<string, unknown>,
@@ -17,20 +17,19 @@ export async function run(
17
17
  const singleEvents = (input.single_events as boolean) ?? true;
18
18
  const orderBy = input.order_by as "startTime" | "updated" | undefined;
19
19
 
20
- return withCalendarToken(async (token) => {
21
- const result = await calendar.listEvents(token, calendarId, {
22
- timeMin,
23
- timeMax,
24
- maxResults,
25
- query,
26
- singleEvents,
27
- orderBy,
28
- });
20
+ const connection = getCalendarConnection();
21
+ const result = await calendar.listEvents(connection, calendarId, {
22
+ timeMin,
23
+ timeMax,
24
+ maxResults,
25
+ query,
26
+ singleEvents,
27
+ orderBy,
28
+ });
29
29
 
30
- if (!result.items?.length) {
31
- return ok("No events found in the specified time range.");
32
- }
30
+ if (!result.items?.length) {
31
+ return ok("No events found in the specified time range.");
32
+ }
33
33
 
34
- return ok(JSON.stringify(result, null, 2));
35
- });
34
+ return ok(JSON.stringify(result, null, 2));
36
35
  }
@@ -3,7 +3,7 @@ import type {
3
3
  ToolExecutionResult,
4
4
  } from "../../../../tools/types.js";
5
5
  import * as calendar from "../calendar-client.js";
6
- import { ok, withCalendarToken } from "./shared.js";
6
+ import { getCalendarConnection, ok } from "./shared.js";
7
7
 
8
8
  export async function run(
9
9
  input: Record<string, unknown>,
@@ -13,45 +13,45 @@ export async function run(
13
13
  const response = input.response as "accepted" | "declined" | "tentative";
14
14
  const calendarId = (input.calendar_id as string) ?? "primary";
15
15
 
16
- return withCalendarToken(async (token) => {
17
- // First get the event to find the user's attendee entry
18
- const event = await calendar.getEvent(token, eventId, calendarId);
19
- const selfAttendee = event.attendees?.find((a) => a.self);
16
+ const connection = getCalendarConnection();
20
17
 
21
- if (!selfAttendee) {
22
- // If the user is the organizer and not in the attendees list,
23
- // they don't need to RSVP
24
- if (event.organizer?.self) {
25
- return ok("You are the organizer of this event. No RSVP needed.");
26
- }
27
- return ok(
28
- "Could not find your attendee entry for this event. You may not be invited.",
29
- );
30
- }
18
+ // First get the event to find the user's attendee entry
19
+ const event = await calendar.getEvent(connection, eventId, calendarId);
20
+ const selfAttendee = event.attendees?.find((a) => a.self);
31
21
 
32
- // Update the attendee's response status
33
- const updatedAttendees = event.attendees!.map((a) =>
34
- a.self ? { ...a, responseStatus: response } : a,
22
+ if (!selfAttendee) {
23
+ // If the user is the organizer and not in the attendees list,
24
+ // they don't need to RSVP
25
+ if (event.organizer?.self) {
26
+ return ok("You are the organizer of this event. No RSVP needed.");
27
+ }
28
+ return ok(
29
+ "Could not find your attendee entry for this event. You may not be invited.",
35
30
  );
31
+ }
36
32
 
37
- await calendar.patchEvent(
38
- token,
39
- eventId,
40
- {
41
- attendees: updatedAttendees as Array<{
42
- email: string;
43
- responseStatus?: string;
44
- }>,
45
- },
46
- calendarId,
47
- );
33
+ // Update the attendee's response status
34
+ const updatedAttendees = event.attendees!.map((a) =>
35
+ a.self ? { ...a, responseStatus: response } : a,
36
+ );
37
+
38
+ await calendar.patchEvent(
39
+ connection,
40
+ eventId,
41
+ {
42
+ attendees: updatedAttendees as Array<{
43
+ email: string;
44
+ responseStatus?: string;
45
+ }>,
46
+ },
47
+ calendarId,
48
+ );
48
49
 
49
- const responseLabel =
50
- response === "accepted"
51
- ? "Accepted"
52
- : response === "declined"
53
- ? "Declined"
54
- : "Tentatively accepted";
55
- return ok(`${responseLabel} the event "${event.summary ?? eventId}".`);
56
- });
50
+ const responseLabel =
51
+ response === "accepted"
52
+ ? "Accepted"
53
+ : response === "declined"
54
+ ? "Declined"
55
+ : "Tentatively accepted";
56
+ return ok(`${responseLabel} the event "${event.summary ?? eventId}".`);
57
57
  }
@@ -1,20 +1,15 @@
1
- import { withValidToken } from "../../../../security/token-manager.js";
1
+ import type { OAuthConnection } from "../../../../oauth/connection.js";
2
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
2
3
  import type { ToolExecutionResult } from "../../../../tools/types.js";
3
4
 
4
5
  export function ok(content: string): ToolExecutionResult {
5
6
  return { content, isError: false };
6
7
  }
7
8
 
8
- export function err(message: string): ToolExecutionResult {
9
- return { content: message, isError: true };
10
- }
11
-
12
9
  /**
13
10
  * Calendar uses the same OAuth credential service as Gmail since both
14
11
  * scopes are granted in a single OAuth consent flow.
15
12
  */
16
- export async function withCalendarToken<T>(
17
- fn: (token: string) => Promise<T>,
18
- ): Promise<T> {
19
- return withValidToken("integration:gmail", fn);
13
+ export function getCalendarConnection(): OAuthConnection {
14
+ return resolveOAuthConnection("integration:gmail");
20
15
  }
@@ -5,10 +5,15 @@ import {
5
5
  } from "../../../../daemon/media-visibility-policy.js";
6
6
  import {
7
7
  generateImage,
8
+ type ImageGenCredentials,
8
9
  mapGeminiError,
9
10
  } from "../../../../media/gemini-image-service.js";
10
11
  import { getAttachmentsByIds } from "../../../../memory/attachments-store.js";
11
12
  import { getConversationThreadType } from "../../../../memory/conversation-crud.js";
13
+ import {
14
+ buildManagedBaseUrl,
15
+ resolveManagedProxyContext,
16
+ } from "../../../../providers/managed-proxy/context.js";
12
17
  import type { ImageContent } from "../../../../providers/types.js";
13
18
  import { getAttachmentSourceConversations } from "../../../../tools/assets/search.js";
14
19
  import type {
@@ -46,9 +51,25 @@ export async function run(
46
51
  context: ToolContext,
47
52
  ): Promise<ToolExecutionResult> {
48
53
  const config = getConfig();
49
- const apiKey = config.apiKeys.gemini;
54
+ const apiKey = config.apiKeys.gemini ?? process.env.GEMINI_API_KEY;
55
+
56
+ // Resolve credentials: prefer direct API key, fall back to managed proxy
57
+ let credentials: ImageGenCredentials | undefined;
58
+ if (apiKey) {
59
+ credentials = { type: "direct", apiKey };
60
+ } else {
61
+ const managedBaseUrl = buildManagedBaseUrl("vertex");
62
+ if (managedBaseUrl) {
63
+ const ctx = resolveManagedProxyContext();
64
+ credentials = {
65
+ type: "managed-proxy",
66
+ assistantApiKey: ctx.assistantApiKey,
67
+ baseUrl: managedBaseUrl,
68
+ };
69
+ }
70
+ }
50
71
 
51
- if (!apiKey) {
72
+ if (!credentials) {
52
73
  return {
53
74
  content:
54
75
  "No Gemini API key configured. Please set your Gemini API key to use image generation.",
@@ -95,7 +116,7 @@ export async function run(
95
116
  }
96
117
 
97
118
  try {
98
- const result = await generateImage(apiKey, {
119
+ const result = await generateImage(credentials, {
99
120
  prompt,
100
121
  mode,
101
122
  sourceImages,
@@ -33,9 +33,9 @@ When a platform is connected (auth test succeeds), always use the messaging API
33
33
 
34
34
  Before using any messaging tool, verify that the platform is connected by calling `messaging_auth_test` with the appropriate `platform` parameter. If the call fails with a token/authorization error, follow the steps below.
35
35
 
36
- ### Public Ingress (required for all platforms)
36
+ ### Public Ingress (required for Slack and Telegram)
37
37
 
38
- Gmail, Slack, and Telegram setup all require a publicly reachable URL for OAuth callbacks or webhook delivery. The **public-ingress** skill handles ngrok tunnel setup and persists the URL as `ingress.publicBaseUrl`. Each setup skill below declares `public-ingress` as a dependency and will prompt you to run it if `ingress.publicBaseUrl` is not configured.
38
+ Slack and Telegram setup require a publicly reachable URL for OAuth callbacks or webhook delivery. The **public-ingress** skill handles ngrok tunnel setup and persists the URL as `ingress.publicBaseUrl`. Gmail on the desktop app uses a loopback callback and does not require public ingress; the channel path (Path B in the google-oauth-applescript skill) handles public ingress internally when needed.
39
39
 
40
40
  ### Email Connection Flow
41
41
 
@@ -48,11 +48,11 @@ When the user asks to "connect my email", "set up email", "manage my email", or
48
48
  ### Gmail
49
49
 
50
50
  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.
51
- 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):
52
- - Call `skill_load` with `skill: "google-oauth-setup"` to load the dependency skill.
51
+ 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:
52
+ - Call `skill_load` with `skill: "google-oauth-applescript"` to load the dependency skill.
53
53
  - 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:
54
54
  - **message:** "Ready to set up Gmail?"
55
- - **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."
55
+ - **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."
56
56
  - **confirmLabel:** "Get Started"
57
57
  - **cancelLabel:** "Not Now"
58
58
  - 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.
@@ -95,7 +95,7 @@ The guardian-verify-setup skill handles the full outbound verification flow for
95
95
  When a messaging tool fails with a token or authorization error:
96
96
 
97
97
  1. **Try to reconnect silently.** Call `credential_store` with `action: "oauth2_connect"` and the appropriate `service`. This often resolves expired tokens automatically.
98
- 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 that platform (e.g., install and load **google-oauth-setup** for Gmail). The user came to you to get something done, not to troubleshoot OAuth — make it seamless.
98
+ 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 that platform (e.g., install and load **google-oauth-applescript** for Gmail). The user came to you to get something done, not to troubleshoot OAuth — make it seamless.
99
99
  3. **Never try alternative approaches.** Don't use bash, curl, browser automation, or any workaround. If the messaging tools can't do it, the reconnection flow is the answer.
100
100
  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.
101
101