@vellumai/assistant 0.5.10 → 0.5.12

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 (395) hide show
  1. package/AGENTS.md +8 -0
  2. package/ARCHITECTURE.md +43 -43
  3. package/Dockerfile +3 -0
  4. package/docs/architecture/integrations.md +37 -42
  5. package/docs/architecture/memory.md +7 -12
  6. package/docs/credential-execution-service.md +9 -9
  7. package/docs/skills.md +1 -1
  8. package/node_modules/@vellumai/ces-contracts/src/__tests__/grants.test.ts +7 -7
  9. package/node_modules/@vellumai/ces-contracts/src/handles.ts +5 -4
  10. package/node_modules/@vellumai/credential-storage/src/index.ts +3 -3
  11. package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +1 -1
  12. package/openapi.yaml +7208 -0
  13. package/package.json +2 -1
  14. package/scripts/generate-openapi.ts +562 -0
  15. package/src/__tests__/acp-session.test.ts +239 -44
  16. package/src/__tests__/assistant-feature-flag-guard.test.ts +8 -8
  17. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +5 -86
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -14
  19. package/src/__tests__/browser-skill-endstate.test.ts +1 -1
  20. package/src/__tests__/btw-routes.test.ts +8 -0
  21. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +10 -10
  22. package/src/__tests__/catalog-cache.test.ts +164 -0
  23. package/src/__tests__/catalog-search.test.ts +61 -0
  24. package/src/__tests__/channel-approvals.test.ts +7 -7
  25. package/src/__tests__/channel-readiness-service.test.ts +41 -0
  26. package/src/__tests__/cli-command-risk-guard.test.ts +181 -6
  27. package/src/__tests__/config-schema.test.ts +10 -2
  28. package/src/__tests__/context-memory-e2e.test.ts +2 -6
  29. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +396 -0
  30. package/src/__tests__/conversation-error.test.ts +3 -2
  31. package/src/__tests__/conversation-skill-tools.test.ts +1 -3
  32. package/src/__tests__/conversation-title-service.test.ts +2 -15
  33. package/src/__tests__/credential-execution-feature-gates.test.ts +4 -8
  34. package/src/__tests__/credential-execution-managed-contract.test.ts +8 -8
  35. package/src/__tests__/credential-security-e2e.test.ts +4 -4
  36. package/src/__tests__/credential-security-invariants.test.ts +12 -18
  37. package/src/__tests__/credential-vault-unit.test.ts +32 -34
  38. package/src/__tests__/credential-vault.test.ts +25 -33
  39. package/src/__tests__/credentials-cli.test.ts +3 -3
  40. package/src/__tests__/daemon-credential-client.test.ts +2 -2
  41. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -1
  42. package/src/__tests__/gateway-only-guard.test.ts +3 -0
  43. package/src/__tests__/heartbeat-service.test.ts +35 -0
  44. package/src/__tests__/host-bash-proxy.test.ts +79 -0
  45. package/src/__tests__/host-cu-proxy.test.ts +90 -0
  46. package/src/__tests__/host-file-proxy.test.ts +89 -0
  47. package/src/__tests__/host-shell-tool.test.ts +1 -1
  48. package/src/__tests__/inline-skill-load-permissions.test.ts +3 -3
  49. package/src/__tests__/integration-status.test.ts +5 -5
  50. package/src/__tests__/list-messages-attachments.test.ts +171 -0
  51. package/src/__tests__/llm-request-log-turn-query.test.ts +64 -0
  52. package/src/__tests__/log-export-workspace.test.ts +1 -1
  53. package/src/__tests__/mcp-abort-signal.test.ts +205 -0
  54. package/src/__tests__/mcp-client-auth.test.ts +1 -1
  55. package/src/__tests__/memory-lifecycle-e2e.test.ts +2 -2
  56. package/src/__tests__/memory-recall-log-store.test.ts +182 -0
  57. package/src/__tests__/memory-recall-quality.test.ts +6 -8
  58. package/src/__tests__/memory-regressions.test.ts +53 -42
  59. package/src/__tests__/memory-retrieval.benchmark.test.ts +5 -9
  60. package/src/__tests__/messaging-send-tool.test.ts +5 -5
  61. package/src/__tests__/messaging-skill-split.test.ts +2 -17
  62. package/src/__tests__/notification-telegram-adapter.test.ts +125 -0
  63. package/src/__tests__/oauth-cli.test.ts +203 -649
  64. package/src/__tests__/oauth-provider-profiles.test.ts +55 -20
  65. package/src/__tests__/oauth-scope-policy.test.ts +4 -6
  66. package/src/__tests__/onboarding-template-contract.test.ts +2 -2
  67. package/src/__tests__/platform-callback-registration.test.ts +119 -0
  68. package/src/__tests__/secret-ingress-channel.test.ts +261 -0
  69. package/src/__tests__/secret-ingress-cli.test.ts +201 -0
  70. package/src/__tests__/secret-ingress-http.test.ts +312 -0
  71. package/src/__tests__/secret-ingress.test.ts +283 -0
  72. package/src/__tests__/secret-onetime-send.test.ts +4 -4
  73. package/src/__tests__/secret-routes-managed-proxy.test.ts +78 -0
  74. package/src/__tests__/secure-keys-managed-failover.test.ts +73 -0
  75. package/src/__tests__/skill-feature-flags-integration.test.ts +4 -4
  76. package/src/__tests__/skill-feature-flags.test.ts +11 -19
  77. package/src/__tests__/skill-load-feature-flag.test.ts +1 -1
  78. package/src/__tests__/skill-load-inline-command.test.ts +3 -3
  79. package/src/__tests__/skill-load-inline-includes.test.ts +2 -2
  80. package/src/__tests__/skill-memory.test.ts +2 -4
  81. package/src/__tests__/skill-projection-feature-flag.test.ts +2 -4
  82. package/src/__tests__/skill-projection.benchmark.test.ts +1 -3
  83. package/src/__tests__/skills-uninstall.test.ts +2 -2
  84. package/src/__tests__/skills.test.ts +16 -2
  85. package/src/__tests__/slack-channel-config.test.ts +1 -1
  86. package/src/__tests__/slack-messaging-token-resolution.test.ts +22 -24
  87. package/src/__tests__/slack-share-routes.test.ts +5 -5
  88. package/src/__tests__/slack-skill.test.ts +5 -69
  89. package/src/__tests__/system-prompt.test.ts +39 -0
  90. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -1
  91. package/src/__tests__/workspace-migration-018-rekey-compound-credential-keys.test.ts +181 -0
  92. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +5 -4
  93. package/src/acp/client-handler.ts +113 -31
  94. package/src/acp/session-manager.ts +29 -27
  95. package/src/approvals/guardian-request-resolvers.ts +1 -1
  96. package/src/cli/AGENTS.md +113 -0
  97. package/src/cli/commands/autonomy.ts +3 -5
  98. package/src/cli/commands/browser-relay.ts +2 -17
  99. package/src/cli/commands/contacts.ts +6 -4
  100. package/src/cli/commands/conversations.ts +13 -1
  101. package/src/cli/commands/credential-execution.ts +17 -3
  102. package/src/cli/commands/credentials.ts +2 -8
  103. package/src/cli/commands/memory.ts +2 -3
  104. package/src/cli/commands/oauth/__tests__/connect.test.ts +706 -0
  105. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +686 -0
  106. package/src/cli/commands/oauth/__tests__/mode.test.ts +625 -0
  107. package/src/cli/commands/oauth/__tests__/ping.test.ts +631 -0
  108. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +574 -0
  109. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +416 -0
  110. package/src/cli/commands/oauth/__tests__/status.test.ts +551 -0
  111. package/src/cli/commands/oauth/__tests__/token.test.ts +420 -0
  112. package/src/cli/commands/oauth/apps.ts +87 -50
  113. package/src/cli/commands/oauth/connect.ts +405 -0
  114. package/src/cli/commands/oauth/disconnect.ts +285 -0
  115. package/src/cli/commands/oauth/index.ts +62 -20
  116. package/src/cli/commands/oauth/mode.ts +251 -0
  117. package/src/cli/commands/oauth/ping.ts +196 -0
  118. package/src/cli/commands/oauth/providers.ts +589 -55
  119. package/src/cli/commands/oauth/request.ts +564 -0
  120. package/src/cli/commands/oauth/shared.ts +114 -0
  121. package/src/cli/commands/oauth/status.ts +191 -0
  122. package/src/cli/commands/oauth/token.ts +150 -0
  123. package/src/cli/commands/platform/connect.ts +104 -0
  124. package/src/cli/commands/platform/disconnect.ts +118 -0
  125. package/src/cli/commands/platform/index.ts +252 -0
  126. package/src/cli/commands/sequence.ts +5 -4
  127. package/src/cli/commands/shotgun.ts +16 -0
  128. package/src/cli/commands/skills.ts +173 -41
  129. package/src/cli/commands/usage.ts +5 -11
  130. package/src/cli/lib/daemon-credential-client.ts +22 -38
  131. package/src/cli/program.ts +1 -1
  132. package/src/cli.ts +82 -17
  133. package/src/config/assistant-feature-flags.ts +77 -18
  134. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
  135. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -1
  136. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
  137. package/src/config/bundled-skills/conversations/SKILL.md +20 -0
  138. package/src/config/bundled-skills/conversations/TOOLS.json +23 -0
  139. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +66 -0
  140. package/src/config/bundled-skills/gmail/SKILL.md +13 -13
  141. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +3 -3
  142. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +2 -2
  143. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +1 -1
  144. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +1 -1
  145. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +1 -1
  146. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +1 -1
  147. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +2 -2
  148. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +1 -1
  149. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +1 -1
  150. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
  151. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +1 -1
  152. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +1 -1
  153. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +1 -1
  154. package/src/config/bundled-skills/google-calendar/SKILL.md +10 -4
  155. package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
  156. package/src/config/bundled-skills/messaging/SKILL.md +19 -42
  157. package/src/config/bundled-skills/messaging/TOOLS.json +9 -9
  158. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
  159. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -2
  160. package/src/config/bundled-skills/messaging/tools/shared.ts +5 -6
  161. package/src/config/bundled-skills/notifications/SKILL.md +1 -1
  162. package/src/config/bundled-skills/schedule/SKILL.md +2 -2
  163. package/src/config/bundled-skills/settings/SKILL.md +5 -3
  164. package/src/config/bundled-skills/settings/TOOLS.json +17 -0
  165. package/src/config/bundled-skills/settings/tools/avatar-get.ts +50 -0
  166. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +7 -0
  167. package/src/config/bundled-skills/settings/tools/avatar-update.ts +6 -1
  168. package/src/config/bundled-skills/settings/tools/identity-avatar.ts +55 -0
  169. package/src/config/bundled-skills/skills-catalog/SKILL.md +3 -3
  170. package/src/config/bundled-skills/slack/SKILL.md +58 -44
  171. package/src/config/bundled-tool-registry.ts +7 -19
  172. package/src/config/env.ts +5 -1
  173. package/src/config/feature-flag-registry.json +58 -42
  174. package/src/config/loader.ts +4 -0
  175. package/src/config/schemas/platform.ts +0 -8
  176. package/src/config/schemas/security.ts +9 -1
  177. package/src/config/schemas/services.ts +1 -1
  178. package/src/config/skill-state.ts +1 -3
  179. package/src/config/skills.ts +2 -4
  180. package/src/credential-execution/client.ts +1 -1
  181. package/src/credential-execution/feature-gates.ts +9 -16
  182. package/src/credential-execution/process-manager.ts +12 -0
  183. package/src/daemon/config-watcher.ts +4 -0
  184. package/src/daemon/conversation-agent-loop-handlers.ts +10 -0
  185. package/src/daemon/conversation-agent-loop.ts +51 -2
  186. package/src/daemon/conversation-error.ts +36 -6
  187. package/src/daemon/conversation-memory.ts +0 -1
  188. package/src/daemon/conversation-messaging.ts +9 -0
  189. package/src/daemon/conversation-runtime-assembly.ts +33 -0
  190. package/src/daemon/conversation-surfaces.ts +120 -14
  191. package/src/daemon/conversation.ts +5 -0
  192. package/src/daemon/handlers/config-slack-channel.ts +43 -1
  193. package/src/daemon/handlers/conversations.ts +41 -33
  194. package/src/daemon/handlers/skills.ts +148 -3
  195. package/src/daemon/host-bash-proxy.ts +16 -0
  196. package/src/daemon/host-cu-proxy.ts +16 -0
  197. package/src/daemon/host-file-proxy.ts +16 -0
  198. package/src/daemon/lifecycle.ts +73 -3
  199. package/src/daemon/message-types/acp.ts +0 -15
  200. package/src/daemon/message-types/conversations.ts +1 -0
  201. package/src/daemon/message-types/guardian-actions.ts +2 -0
  202. package/src/daemon/message-types/host-bash.ts +6 -1
  203. package/src/daemon/message-types/host-cu.ts +6 -1
  204. package/src/daemon/message-types/host-file.ts +6 -1
  205. package/src/daemon/message-types/integrations.ts +0 -1
  206. package/src/daemon/message-types/memory.ts +0 -1
  207. package/src/daemon/message-types/messages.ts +9 -1
  208. package/src/daemon/message-types/schedules.ts +9 -0
  209. package/src/daemon/server.ts +48 -9
  210. package/src/email/feature-gate.ts +3 -3
  211. package/src/heartbeat/heartbeat-service.ts +48 -0
  212. package/src/hooks/cli.ts +74 -0
  213. package/src/inbound/platform-callback-registration.ts +68 -19
  214. package/src/mcp/client.ts +6 -1
  215. package/src/mcp/manager.ts +2 -1
  216. package/src/mcp/mcp-oauth-provider.ts +3 -3
  217. package/src/memory/app-store.ts +3 -3
  218. package/src/memory/conversation-crud.ts +213 -0
  219. package/src/memory/conversation-key-store.ts +26 -0
  220. package/src/memory/conversation-title-service.ts +7 -17
  221. package/src/memory/db-init.ts +24 -0
  222. package/src/memory/embedding-local.ts +47 -2
  223. package/src/memory/indexer.ts +13 -10
  224. package/src/memory/items-extractor.ts +12 -4
  225. package/src/memory/job-utils.ts +5 -0
  226. package/src/memory/jobs-store.ts +10 -2
  227. package/src/memory/journal-memory.ts +6 -2
  228. package/src/memory/llm-request-log-store.ts +88 -21
  229. package/src/memory/memory-recall-log-store.ts +128 -0
  230. package/src/memory/migrations/194-memory-recall-logs.ts +50 -0
  231. package/src/memory/migrations/195-oauth-providers-ping-config.ts +23 -0
  232. package/src/memory/migrations/196-messages-conversation-created-at-index.ts +9 -0
  233. package/src/memory/migrations/196-strip-integration-prefix-from-provider-keys.ts +186 -0
  234. package/src/memory/migrations/197-oauth-providers-behavior-columns.ts +29 -0
  235. package/src/memory/migrations/198-drop-setup-skill-id-column.ts +11 -0
  236. package/src/memory/migrations/index.ts +6 -0
  237. package/src/memory/migrations/registry.ts +8 -0
  238. package/src/memory/retriever.test.ts +4 -5
  239. package/src/memory/schema/infrastructure.ts +31 -0
  240. package/src/memory/schema/oauth.ts +14 -0
  241. package/src/messaging/provider.ts +13 -12
  242. package/src/messaging/providers/gmail/adapter.ts +44 -35
  243. package/src/messaging/providers/slack/adapter.ts +63 -33
  244. package/src/messaging/providers/telegram-bot/adapter.ts +7 -9
  245. package/src/messaging/providers/whatsapp/adapter.ts +6 -8
  246. package/src/notifications/adapters/telegram.ts +78 -2
  247. package/src/oauth/__tests__/identity-verifier.test.ts +464 -0
  248. package/src/oauth/byo-connection.test.ts +22 -24
  249. package/src/oauth/connect-orchestrator.ts +79 -64
  250. package/src/oauth/connect-types.ts +7 -65
  251. package/src/oauth/connection-resolver.test.ts +13 -13
  252. package/src/oauth/connection-resolver.ts +3 -4
  253. package/src/oauth/identity-verifier.ts +177 -0
  254. package/src/oauth/manual-token-connection.ts +5 -5
  255. package/src/oauth/oauth-store.ts +251 -5
  256. package/src/oauth/platform-connection.test.ts +56 -6
  257. package/src/oauth/platform-connection.ts +8 -1
  258. package/src/oauth/seed-providers.ts +256 -34
  259. package/src/permissions/checker.ts +129 -3
  260. package/src/permissions/trust-client.ts +2 -2
  261. package/src/platform/client.ts +2 -2
  262. package/src/prompts/journal-context.ts +6 -1
  263. package/src/prompts/system-prompt.ts +43 -9
  264. package/src/prompts/templates/BOOTSTRAP.md +16 -5
  265. package/src/providers/anthropic/client.ts +139 -28
  266. package/src/runtime/auth/__tests__/middleware.test.ts +19 -0
  267. package/src/runtime/auth/route-policy.ts +0 -1
  268. package/src/runtime/btw-sidechain.ts +7 -1
  269. package/src/runtime/channel-approvals.ts +2 -2
  270. package/src/runtime/channel-readiness-service.ts +30 -7
  271. package/src/runtime/guardian-action-service.ts +7 -2
  272. package/src/runtime/http-router.ts +31 -0
  273. package/src/runtime/http-server.ts +26 -7
  274. package/src/runtime/http-types.ts +9 -0
  275. package/src/runtime/pending-interactions.ts +21 -3
  276. package/src/runtime/routes/acp-routes.ts +46 -28
  277. package/src/runtime/routes/app-management-routes.ts +123 -0
  278. package/src/runtime/routes/app-routes.ts +31 -0
  279. package/src/runtime/routes/approval-routes.ts +108 -3
  280. package/src/runtime/routes/attachment-routes.ts +45 -0
  281. package/src/runtime/routes/avatar-routes.ts +16 -0
  282. package/src/runtime/routes/brain-graph-routes.ts +18 -0
  283. package/src/runtime/routes/btw-routes.ts +20 -0
  284. package/src/runtime/routes/call-routes.ts +81 -0
  285. package/src/runtime/routes/channel-readiness-routes.ts +48 -7
  286. package/src/runtime/routes/channel-routes.ts +18 -0
  287. package/src/runtime/routes/channel-verification-routes.ts +49 -1
  288. package/src/runtime/routes/contact-routes.ts +77 -0
  289. package/src/runtime/routes/conversation-attention-routes.ts +37 -0
  290. package/src/runtime/routes/conversation-management-routes.ts +125 -0
  291. package/src/runtime/routes/conversation-query-routes.ts +78 -0
  292. package/src/runtime/routes/conversation-routes.ts +191 -39
  293. package/src/runtime/routes/conversation-starter-routes.ts +29 -0
  294. package/src/runtime/routes/debug-routes.ts +23 -0
  295. package/src/runtime/routes/diagnostics-routes.ts +30 -0
  296. package/src/runtime/routes/documents-routes.ts +42 -0
  297. package/src/runtime/routes/events-routes.ts +10 -0
  298. package/src/runtime/routes/global-search-routes.ts +35 -0
  299. package/src/runtime/routes/guardian-action-routes.ts +61 -3
  300. package/src/runtime/routes/guardian-approval-prompt.ts +77 -2
  301. package/src/runtime/routes/heartbeat-routes.ts +278 -0
  302. package/src/runtime/routes/host-bash-routes.ts +16 -1
  303. package/src/runtime/routes/host-cu-routes.ts +23 -1
  304. package/src/runtime/routes/host-file-routes.ts +18 -1
  305. package/src/runtime/routes/identity-routes.ts +35 -0
  306. package/src/runtime/routes/inbound-message-handler.ts +46 -25
  307. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -8
  308. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +30 -2
  309. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -2
  310. package/src/runtime/routes/integrations/slack/share.ts +1 -1
  311. package/src/runtime/routes/integrations/twilio.ts +32 -22
  312. package/src/runtime/routes/invite-routes.ts +83 -0
  313. package/src/runtime/routes/log-export-routes.ts +14 -0
  314. package/src/runtime/routes/memory-item-routes.ts +99 -1
  315. package/src/runtime/routes/migration-rollback-routes.ts +25 -0
  316. package/src/runtime/routes/migration-routes.ts +40 -0
  317. package/src/runtime/routes/notification-routes.ts +20 -0
  318. package/src/runtime/routes/oauth-apps.ts +13 -4
  319. package/src/runtime/routes/pairing-routes.ts +15 -0
  320. package/src/runtime/routes/recording-routes.ts +72 -0
  321. package/src/runtime/routes/schedule-routes.ts +77 -5
  322. package/src/runtime/routes/secret-routes.ts +99 -14
  323. package/src/runtime/routes/settings-routes.ts +102 -19
  324. package/src/runtime/routes/skills-routes.ts +141 -18
  325. package/src/runtime/routes/subagents-routes.ts +38 -3
  326. package/src/runtime/routes/surface-action-routes.ts +66 -24
  327. package/src/runtime/routes/surface-content-routes.ts +20 -0
  328. package/src/runtime/routes/telemetry-routes.ts +12 -0
  329. package/src/runtime/routes/trace-event-routes.ts +25 -0
  330. package/src/runtime/routes/trust-rules-routes.ts +46 -0
  331. package/src/runtime/routes/tts-routes.ts +15 -4
  332. package/src/runtime/routes/upgrade-broadcast-routes.ts +38 -0
  333. package/src/runtime/routes/usage-routes.ts +59 -0
  334. package/src/runtime/routes/watch-routes.ts +28 -0
  335. package/src/runtime/routes/work-items-routes.ts +59 -0
  336. package/src/runtime/routes/workspace-commit-routes.ts +12 -0
  337. package/src/runtime/routes/workspace-routes.ts +102 -0
  338. package/src/schedule/integration-status.ts +2 -2
  339. package/src/schedule/scheduler.ts +7 -1
  340. package/src/security/AGENTS.md +7 -0
  341. package/src/security/ces-rpc-credential-backend.ts +19 -16
  342. package/src/security/credential-backend.ts +1 -1
  343. package/src/security/encrypted-store.ts +3 -3
  344. package/src/security/oauth-completion-page.ts +153 -0
  345. package/src/security/oauth2.ts +58 -17
  346. package/src/security/secret-ingress.ts +174 -0
  347. package/src/security/secret-patterns.ts +133 -0
  348. package/src/security/secret-scanner.ts +28 -117
  349. package/src/security/secure-keys.ts +207 -7
  350. package/src/security/token-manager.ts +3 -6
  351. package/src/signals/bash.ts +6 -1
  352. package/src/signals/confirm.ts +12 -8
  353. package/src/signals/user-message.ts +18 -3
  354. package/src/skills/catalog-cache.ts +44 -0
  355. package/src/skills/catalog-search.ts +18 -0
  356. package/src/skills/skill-memory.ts +1 -2
  357. package/src/tasks/task-runner.ts +7 -1
  358. package/src/tools/credentials/broker.ts +1 -1
  359. package/src/tools/credentials/metadata-store.ts +1 -1
  360. package/src/tools/credentials/post-connect-hooks.ts +1 -1
  361. package/src/tools/credentials/vault.ts +36 -48
  362. package/src/tools/host-terminal/host-shell.ts +16 -3
  363. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  364. package/src/tools/memory/definitions.ts +1 -1
  365. package/src/tools/memory/handlers.test.ts +2 -4
  366. package/src/tools/skills/load.ts +1 -1
  367. package/src/tools/skills/sandbox-runner.ts +16 -3
  368. package/src/tools/terminal/safe-env.ts +7 -0
  369. package/src/tools/terminal/shell.ts +16 -3
  370. package/src/tools/tool-manifest.ts +1 -1
  371. package/src/util/log-redact.ts +9 -34
  372. package/src/util/logger.ts +11 -1
  373. package/src/util/sentry-log-stream.ts +51 -0
  374. package/src/watcher/providers/github.ts +2 -2
  375. package/src/watcher/providers/gmail.ts +1 -1
  376. package/src/watcher/providers/google-calendar.ts +1 -1
  377. package/src/watcher/providers/linear.ts +2 -2
  378. package/src/workspace/migrations/011-backfill-installation-id.ts +5 -3
  379. package/src/workspace/migrations/020-rename-oauth-skill-dirs.ts +119 -0
  380. package/src/workspace/migrations/registry.ts +2 -0
  381. package/docs/architecture/keychain-broker.md +0 -68
  382. package/src/cli/commands/oauth/connections.ts +0 -734
  383. package/src/cli/commands/oauth/platform.ts +0 -525
  384. package/src/cli/commands/platform.ts +0 -176
  385. package/src/config/bundled-skills/slack/TOOLS.json +0 -272
  386. package/src/config/bundled-skills/slack/tools/shared.ts +0 -34
  387. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +0 -27
  388. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +0 -38
  389. package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +0 -146
  390. package/src/config/bundled-skills/slack/tools/slack-configure-channels.ts +0 -105
  391. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +0 -26
  392. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +0 -27
  393. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +0 -25
  394. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +0 -372
  395. package/src/oauth/provider-behaviors.ts +0 -634
@@ -26,7 +26,7 @@ export async function run(
26
26
  }
27
27
 
28
28
  try {
29
- const connection = await resolveOAuthConnection("integration:google", {
29
+ const connection = await resolveOAuthConnection("google", {
30
30
  account,
31
31
  });
32
32
  switch (action) {
@@ -38,7 +38,7 @@ export async function run(
38
38
  }
39
39
 
40
40
  try {
41
- const connection = await resolveOAuthConnection("integration:google", {
41
+ const connection = await resolveOAuthConnection("google", {
42
42
  account,
43
43
  });
44
44
  switch (action) {
@@ -76,7 +76,7 @@ export async function run(
76
76
  if (!forwardTo) return err("to is required.");
77
77
 
78
78
  try {
79
- const connection = await resolveOAuthConnection("integration:google", {
79
+ const connection = await resolveOAuthConnection("google", {
80
80
  account,
81
81
  });
82
82
  const message = await getMessage(connection, messageId, "full");
@@ -21,7 +21,7 @@ export async function run(
21
21
 
22
22
  if (messageIds && messageIds.length > 0) {
23
23
  try {
24
- const connection = await resolveOAuthConnection("integration:google", {
24
+ const connection = await resolveOAuthConnection("google", {
25
25
  account,
26
26
  });
27
27
  await batchModifyMessages(connection, messageIds, {
@@ -36,7 +36,7 @@ export async function run(
36
36
 
37
37
  if (messageId) {
38
38
  try {
39
- const connection = await resolveOAuthConnection("integration:google", {
39
+ const connection = await resolveOAuthConnection("google", {
40
40
  account,
41
41
  });
42
42
  await modifyMessage(connection, messageId, {
@@ -56,7 +56,7 @@ export async function run(
56
56
  const query = `in:inbox -has:unsubscribe newer_than:${timeRange}`;
57
57
 
58
58
  try {
59
- const connection = await resolveOAuthConnection("integration:google", {
59
+ const connection = await resolveOAuthConnection("google", {
60
60
  account,
61
61
  });
62
62
  // Pipeline: fire metadata fetches for each page of IDs as they arrive
@@ -15,7 +15,7 @@ export async function run(
15
15
  if (!draftId) return err("draft_id is required.");
16
16
 
17
17
  try {
18
- const connection = await resolveOAuthConnection("integration:google", {
18
+ const connection = await resolveOAuthConnection("google", {
19
19
  account,
20
20
  });
21
21
  const msg = await sendDraft(connection, draftId);
@@ -58,7 +58,7 @@ export async function run(
58
58
  const inputPageToken = input.page_token as string | undefined;
59
59
 
60
60
  try {
61
- const connection = await resolveOAuthConnection("integration:google", {
61
+ const connection = await resolveOAuthConnection("google", {
62
62
  account,
63
63
  });
64
64
  // Pipeline: fire metadata fetches for each page of IDs as they arrive,
@@ -18,7 +18,7 @@ export async function run(
18
18
  }
19
19
 
20
20
  try {
21
- const connection = await resolveOAuthConnection("integration:google", {
21
+ const connection = await resolveOAuthConnection("google", {
22
22
  account,
23
23
  });
24
24
  await trashMessage(connection, messageId);
@@ -32,7 +32,7 @@ export async function run(
32
32
  }
33
33
 
34
34
  try {
35
- const connection = await resolveOAuthConnection("integration:google", {
35
+ const connection = await resolveOAuthConnection("google", {
36
36
  account,
37
37
  });
38
38
  const message = await getMessage(connection, messageId, "metadata", [
@@ -22,7 +22,7 @@ export async function run(
22
22
  }
23
23
 
24
24
  try {
25
- const connection = await resolveOAuthConnection("integration:google", {
25
+ const connection = await resolveOAuthConnection("google", {
26
26
  account,
27
27
  });
28
28
  switch (action) {
@@ -14,10 +14,16 @@ You are a Google Calendar assistant with full access to the user's calendar. Use
14
14
 
15
15
  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:
16
16
 
17
- 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.
18
- 2. Instead, load the **google-oauth-applescript** skill, which walks the user through creating real credentials in Google Cloud Console:
19
- - Call `skill_load` with `skill: "google-oauth-applescript"` to load the dependency skill.
20
- 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."_
17
+ 1. **Try connecting directly first.** Run `assistant oauth status google`. This will show whether or not the user had previously connected their google account. If so, they are ready to go.
18
+ 2. **If no connections are found:** The user needs to either use Vellum's managed google integration or set up their own google oauth app.
19
+ - Call `skill_load` with `skill: "vellum-oauth-integrations"` with `provider-key: google` throughout.
20
+ - To use `your-own` mode, you will need to call `skill_load` with `skill: google-oauth-app-setup`. In this case:
21
+ - Tell the user Google account 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:
22
+ - **message:** "Ready to set up Google Calendar?"
23
+ - **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.\n\n**Your emails stay under your control** — I only ever create drafts. Nothing gets sent without your explicit say-so."
24
+ - **confirmLabel:** "Get Started"
25
+ - **cancelLabel:** "Not Now"
26
+ - If the user confirms, briefly acknowledge (e.g., "Setting up Google Calendar now...") and proceed with the setup guide. If they decline, acknowledge and let them know they can set it up later.
21
27
 
22
28
  ## Capabilities
23
29
 
@@ -13,5 +13,5 @@ export function ok(content: string): ToolExecutionResult {
13
13
  export async function getCalendarConnection(
14
14
  account?: string,
15
15
  ): Promise<OAuthConnection> {
16
- return resolveOAuthConnection("integration:google", { account });
16
+ return resolveOAuthConnection("google", { account });
17
17
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: messaging
3
- description: Read, search, send, and manage messages across Slack, Gmail, Telegram, and other platforms
3
+ description: Read, search, send, and manage messages across Gmail, Telegram, and other platforms
4
4
  compatibility: "Designed for Vellum personal assistants"
5
5
  metadata:
6
6
  emoji: "\U0001F4AC"
@@ -11,7 +11,9 @@ metadata:
11
11
  - "Handles credential flows -- do not improvise setup instructions"
12
12
  ---
13
13
 
14
- You are a unified messaging assistant with access to multiple platforms (Slack, Gmail, Telegram, and more). Use the messaging tools to help users read, search, organize, draft, and send messages across all connected platforms.
14
+ You are a unified messaging assistant with access to multiple platforms (Gmail, Telegram, and more). Use the messaging tools to help users read, search, organize, draft, and send messages across all connected platforms.
15
+
16
+ **Slack is not handled by this skill.** Slack messaging (send, read, search) is handled by the **slack** skill, which uses the Slack Web API directly via CLI. Do not use messaging tools with `platform: "slack"`.
15
17
 
16
18
  ## External Identity
17
19
 
@@ -28,19 +30,21 @@ Do not offer AgentMail as an option or mention it unless the user specifically a
28
30
  ## Communication Style
29
31
 
30
32
  - **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.
31
- - **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."
33
+ - **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 google has expired."
32
34
  - **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.
33
35
  - **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.
34
36
 
35
37
  When a platform is connected (auth test succeeds), always use the messaging API tools for that platform. Never fall back to browser automation, shell commands (bash, curl), or any other approach for operations that messaging tools can handle. The messaging tools handle authentication internally - never try to access tokens or call APIs directly. Browser automation is only appropriate for initial credential setup (OAuth consent screens), not for day-to-day messaging operations.
36
38
 
39
+ **Exception: Slack.** Slack messaging should use the Slack Web API directly via CLI, not messaging tools. See the **slack** skill for details.
40
+
37
41
  ## Connection Setup
38
42
 
39
43
  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.
40
44
 
41
45
  ### Public Ingress (required for Telegram)
42
46
 
43
- Telegram setup requires a publicly reachable URL for webhook delivery. The **public-ingress** skill handles ngrok tunnel setup and persists the URL as `ingress.publicBaseUrl`. Slack uses Socket Mode and does not require public ingress. 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.
47
+ Telegram setup requires webhook routing, but it does **not** always require ngrok. Before suggesting public ingress for Telegram, check managed callback availability with `assistant platform status --json`. If that reports `containerized: true` with a non-empty `assistantId` and `available: true`, use the platform callback route flow and do not prompt for ngrok. Only use the **public-ingress** skill for local assistants that genuinely need a public gateway URL. Slack uses Socket Mode and does not require public ingress. Gmail on the desktop app uses a loopback callback and does not require public ingress; the channel path (Path B in the google-oauth-app-setup skill) handles public ingress internally when needed.
44
48
 
45
49
  ### Email Connection Flow
46
50
 
@@ -52,30 +56,26 @@ When the user asks to "connect my email", "set up email", "manage my email", or
52
56
 
53
57
  ### Gmail
54
58
 
55
- 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.
56
- 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:
57
- - Call `skill_load` with `skill: "google-oauth-applescript"` to load the dependency skill.
59
+ 1. **Try connecting directly first.** Call `credential_store` with `action: "oauth2_connect"` and `service: "google"`. 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.
60
+ 2. **If it fails because no client_id is found:** The user needs to create Google Cloud OAuth credentials first. Load the **google-oauth-app-setup** skill:
61
+ - Call `skill_load` with `skill: "google-oauth-app-setup"` to load the dependency skill.
58
62
  - 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:
59
63
  - **message:** "Ready to set up Gmail?"
60
64
  - **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."
61
65
  - **confirmLabel:** "Get Started"
62
66
  - **cancelLabel:** "Not Now"
63
67
  - 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.
64
- 3. **If the user provides a client_id directly in chat:** Call `credential_store` with `action: "oauth2_connect"`, `service: "gmail"`, and `client_id: "<their value>"`. Include `client_secret` too if they provide one. Everything else is auto-filled.
68
+ 3. **If the user provides a client_id directly in chat:** Call `credential_store` with `action: "oauth2_connect"`, `service: "google"`, and `client_id: "<their value>"`. Include `client_secret` too if they provide one. Everything else is auto-filled.
65
69
 
66
70
  ### Slack
67
71
 
68
- Slack connects via Socket Mode using a bot token and app-level token (not OAuth). Load the **slack-app-setup** skill, which guides the user through creating a Slack app from a pre-configured manifest, collecting tokens securely, and connecting the bot to their workspace:
69
-
70
- - Call `skill_load` with `skill: "slack-app-setup"` to load the dependency skill.
71
- - Tell the user Slack isn't connected yet and briefly explain what the setup involves, then follow the skill's guided flow.
72
-
73
- The slack-app-setup skill handles: manifest-driven app creation, app token and bot token collection via secure prompt (never accept tokens pasted in plaintext chat), and Slack connection setup through the same settings handler used by the Settings UI. That handler validates the bot token, stores workspace metadata, and activates Socket Mode. The skill also covers optional identity verification.
72
+ Slack is **not** handled by this skill. For Slack setup, load the **slack-app-setup** skill directly. For Slack messaging, use the **slack** skill which accesses the Slack Web API via CLI.
74
73
 
75
74
  ### Telegram
76
75
 
77
- Telegram uses a bot token (not OAuth). Load the **telegram-setup** skill (which depends on **public-ingress** for the webhook URL) which automates the full setup:
76
+ Telegram uses a bot token (not OAuth). Load the **telegram-setup** skill, which uses a managed platform callback route in containerized deployments and falls back to **public-ingress** locally when needed:
78
77
 
78
+ - First run `assistant platform status --json`. If it shows managed callback routing is available, tell the user you will use the platform callback route and skip ngrok/public-ingress.
79
79
  - Call `skill_load` with `skill: "telegram-setup"` to load the dependency skill.
80
80
  - Tell the user: _"I've loaded a setup guide for Telegram. It will walk you through connecting a Telegram bot to your assistant."_
81
81
 
@@ -96,7 +96,7 @@ The guardian-verify-setup skill handles the full outbound verification flow for
96
96
  When a messaging tool fails with a token or authorization error:
97
97
 
98
98
  1. **Try to reconnect silently.** Call `credential_store` with `action: "oauth2_connect"` and the appropriate `service`. This often resolves expired tokens automatically.
99
- 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
+ 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-app-setup** for Gmail). The user came to you to get something done, not to troubleshoot OAuth - make it seamless.
100
100
  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.
101
101
  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.
102
102
 
@@ -109,10 +109,10 @@ When a messaging tool fails with a token or authorization error:
109
109
 
110
110
  ## Capabilities
111
111
 
112
- ### Universal (Slack, Gmail)
112
+ ### Universal (Gmail)
113
113
 
114
114
  - **Auth Test**: Verify connection and show account info
115
- - **List Conversations**: Show channels, inboxes, DMs with unread counts
115
+ - **List Conversations**: Show inboxes, DMs with unread counts
116
116
  - **Read Messages**: Read message history from a conversation
117
117
  - **Search**: Search messages with platform-appropriate query syntax
118
118
  - **Send / Reply**: Send a message or reply in a thread (via `thread_id`). High risk - requires user approval.
@@ -120,7 +120,7 @@ When a messaging tool fails with a token or authorization error:
120
120
 
121
121
  ### Telegram
122
122
 
123
- Telegram is supported as a messaging provider with limited capabilities compared to Slack and Gmail due to Bot API constraints:
123
+ Telegram is supported as a messaging provider with limited capabilities compared to Gmail due to Bot API constraints:
124
124
 
125
125
  - **Send**: Send a message to a known chat ID (high risk - requires user approval)
126
126
  - **Auth Test**: Verify bot token and show bot info
@@ -136,29 +136,6 @@ Telegram is supported as a messaging provider with limited capabilities compared
136
136
  - The bot can only message users or groups that have previously interacted with it (sent `/start` or been added to a group). Bots cannot initiate conversations with arbitrary phone numbers.
137
137
  - Future support for MTProto user-account sessions may lift some of these restrictions.
138
138
 
139
- ### Slack-specific
140
-
141
- - **Add Reaction**: Add an emoji reaction to a message
142
- - **Leave Channel**: Leave a Slack channel
143
- - **Edit Message**: `slack_edit_message` - edit a message the assistant previously sent. Requires `channel_id` and the message timestamp (`ts`) from the original send response. High risk - requires confidence score.
144
- - **Delete Message**: `slack_delete_message` - delete a message the assistant previously sent. Requires `channel_id` and the message timestamp (`ts`). High risk - requires confidence score. This is irreversible.
145
-
146
- When sending a Slack message, retain the `ts` (message timestamp) from the send response - it is needed to edit or delete that message later. Only messages sent by the assistant's bot can be edited or deleted.
147
-
148
- ## Slack Search Syntax
149
-
150
- When searching Slack, the query is passed directly to Slack's search API:
151
-
152
- | Operator | Example | What it finds |
153
- | -------------- | ------------------- | ------------------------------ |
154
- | `from:` | `from:@alice` | Messages from a specific user |
155
- | `in:` | `in:#general` | Messages in a specific channel |
156
- | `has:` | `has:link` | Messages containing links |
157
- | `before:` | `before:2024-01-01` | Messages before a date |
158
- | `after:` | `after:2024-01-01` | Messages after a date |
159
- | `has:reaction` | `has:reaction` | Messages with reactions |
160
- | `has:star` | `has:star` | Starred messages |
161
-
162
139
  ## Notifications vs Messages
163
140
 
164
141
  - `send_notification` is provided by the **notifications** skill (always active) -- use it when the user asks for an alert/notification (for example "send this as a desktop notification").
@@ -11,7 +11,7 @@
11
11
  "properties": {
12
12
  "platform": {
13
13
  "type": "string",
14
- "description": "Platform to test (e.g. \"slack\", \"gmail\"). Auto-detected if only one is connected."
14
+ "description": "Platform to test (e.g. \"gmail\"). Auto-detected if only one is connected. Slack is not supported — use the Slack Web API directly."
15
15
  },
16
16
  "account": {
17
17
  "type": "string",
@@ -36,7 +36,7 @@
36
36
  "properties": {
37
37
  "platform": {
38
38
  "type": "string",
39
- "description": "Platform (e.g. \"slack\", \"gmail\"). Auto-detected if only one is connected."
39
+ "description": "Platform (e.g. \"gmail\"). Auto-detected if only one is connected. Slack is not supported — use the Slack Web API directly."
40
40
  },
41
41
  "account": {
42
42
  "type": "string",
@@ -73,7 +73,7 @@
73
73
  "properties": {
74
74
  "platform": {
75
75
  "type": "string",
76
- "description": "Platform (e.g. \"slack\", \"gmail\"). Auto-detected if only one is connected."
76
+ "description": "Platform (e.g. \"gmail\"). Auto-detected if only one is connected. Slack is not supported — use the Slack Web API directly."
77
77
  },
78
78
  "account": {
79
79
  "type": "string",
@@ -111,7 +111,7 @@
111
111
  "properties": {
112
112
  "platform": {
113
113
  "type": "string",
114
- "description": "Platform (e.g. \"slack\", \"gmail\"). Auto-detected if only one is connected."
114
+ "description": "Platform (e.g. \"gmail\"). Auto-detected if only one is connected. Slack is not supported — use the Slack Web API directly."
115
115
  },
116
116
  "account": {
117
117
  "type": "string",
@@ -145,7 +145,7 @@
145
145
  "properties": {
146
146
  "platform": {
147
147
  "type": "string",
148
- "description": "Platform (e.g. \"slack\", \"gmail\"). Auto-detected if only one is connected."
148
+ "description": "Platform (e.g. \"gmail\"). Auto-detected if only one is connected. Slack is not supported — use the Slack Web API directly."
149
149
  },
150
150
  "account": {
151
151
  "type": "string",
@@ -202,7 +202,7 @@
202
202
  "properties": {
203
203
  "platform": {
204
204
  "type": "string",
205
- "description": "Platform (e.g. \"slack\", \"gmail\"). Auto-detected if only one is connected."
205
+ "description": "Platform (e.g. \"gmail\"). Auto-detected if only one is connected. Slack is not supported — use the Slack Web API directly."
206
206
  },
207
207
  "account": {
208
208
  "type": "string",
@@ -236,7 +236,7 @@
236
236
  "properties": {
237
237
  "platform": {
238
238
  "type": "string",
239
- "description": "Platform (e.g. \"slack\", \"gmail\"). Auto-detected if only one is connected."
239
+ "description": "Platform (e.g. \"gmail\"). Auto-detected if only one is connected. Slack is not supported — use the Slack Web API directly."
240
240
  },
241
241
  "account": {
242
242
  "type": "string",
@@ -248,7 +248,7 @@
248
248
  },
249
249
  "query_filter": {
250
250
  "type": "string",
251
- "description": "Optional search filter (e.g. 'to:alice@example.com' for Gmail, 'from:@me' for Slack)"
251
+ "description": "Optional search filter (e.g. 'to:alice@example.com' for Gmail)"
252
252
  },
253
253
  "activity": {
254
254
  "type": "string",
@@ -274,7 +274,7 @@
274
274
  },
275
275
  "platform": {
276
276
  "type": "string",
277
- "description": "Platform (e.g. \"slack\", \"gmail\"). Required for create/list."
277
+ "description": "Platform (e.g. \"gmail\"). Required for create/list. Slack is not supported — use the Slack Web API directly."
278
278
  },
279
279
  "conversation_id": {
280
280
  "type": "string",
@@ -50,7 +50,7 @@ function upsertMemoryItem(opts: {
50
50
  Math.max(existing.importance ?? 0, opts.importance),
51
51
  ),
52
52
  lastSeenAt: now,
53
- sourceType: "extraction",
53
+ sourceType: existing.sourceType === "tool" ? "tool" : "extraction",
54
54
  })
55
55
  .where(eq(memoryItems.id, existing.id))
56
56
  .run();
@@ -9,7 +9,6 @@ import {
9
9
  listMessages,
10
10
  } from "../../../../messaging/providers/gmail/client.js";
11
11
  import { buildMultipartMime } from "../../../../messaging/providers/gmail/mime-builder.js";
12
- import type { OAuthConnection } from "../../../../oauth/connection.js";
13
12
  import type {
14
13
  ToolContext,
15
14
  ToolExecutionResult,
@@ -57,7 +56,11 @@ export async function run(
57
56
 
58
57
  // Gmail: create a draft instead of sending directly
59
58
  if (provider.id === "gmail") {
60
- const gmailConn = conn as OAuthConnection;
59
+ if (!conn)
60
+ return err(
61
+ "Gmail requires an OAuth connection — is the account connected?",
62
+ );
63
+ const gmailConn = conn;
61
64
  // Reply mode: thread_id provided - create a threaded draft with reply-all recipients
62
65
  if (threadId) {
63
66
  // Fetch thread messages to extract recipients and threading headers
@@ -126,17 +126,16 @@ export async function resolveProvider(
126
126
  }
127
127
 
128
128
  /**
129
- * Resolve an OAuthConnection (or empty string for non-OAuth providers)
130
- * for the given messaging provider.
129
+ * Resolve an OAuthConnection for the given messaging provider.
131
130
  *
132
- * Non-OAuth providers (e.g. Telegram) use isConnected() and don't need
133
- * tokens - they receive an empty string which the string overload handles.
131
+ * Returns undefined for providers that manage credentials internally
132
+ * (e.g. Telegram bot tokens, Slack Socket Mode bot tokens).
134
133
  */
135
134
  export async function getProviderConnection(
136
135
  provider: MessagingProvider,
137
136
  account?: string,
138
- ): Promise<OAuthConnection | string> {
137
+ ): Promise<OAuthConnection | undefined> {
139
138
  if (provider.resolveConnection) return provider.resolveConnection(account);
140
- if (await provider.isConnected?.()) return "";
139
+ if (await provider.isConnected?.()) return undefined;
141
140
  return resolveOAuthConnection(provider.credentialService, { account });
142
141
  }
@@ -37,4 +37,4 @@ Conversation grouping is handled by the LLM-powered decision engine, not by any
37
37
  ## Important
38
38
 
39
39
  - Do **NOT** use AppleScript `display notification` or other OS-level notification commands for assistant-managed alerts. Always use `send_notification`.
40
- - For sending rich content (digests, summaries, reports) to a specific chat or email destination, use the messaging skill's `messaging_send` instead. The decision engine rewrites `send_notification` content into short alerts, which strips rich formatting.
40
+ - For sending rich content (digests, summaries, reports) to a specific chat or email destination, use the appropriate platform's API directly. For Gmail, use `messaging_send`. For Slack, use the Slack Web API directly (see the **slack** skill). The decision engine rewrites `send_notification` content into short alerts, which strips rich formatting.
@@ -163,9 +163,9 @@ Scheduled messages run without user interaction. If the task produces output tha
163
163
 
164
164
  Choose the right delivery tool based on the content:
165
165
 
166
- - **Rich content** (digests, summaries, reports): Use `messaging_send` with the target platform and conversation ID. This preserves the full content and posts directly.
166
+ - **Rich content** (digests, summaries, reports): For Gmail, use `messaging_send` with the target platform and conversation ID. For Slack, use the Slack Web API directly via CLI (`chat.postMessage`). This preserves the full content and posts directly.
167
167
  - **Short alerts** (status updates, completion notices): Use `send_notification` to let the notification router pick the best channel. Note: the router's decision engine rewrites content into short alerts, so it is not suitable for rich content.
168
168
 
169
169
  Example schedule message for a Slack digest:
170
170
 
171
- > "Scan my Slack channels for the last 24 hours using slack_scan_digest, then use messaging_send with platform 'slack' and conversation_id 'C0A7STRJ4G5' to post the summary to #alex-agent-messages."
171
+ > "Scan my Slack channels for the last 24 hours using the Slack Web API via bash (network_mode: proxied, credential_ids: ['slack_channel/bot_token']), then post the summary to #alex-agent-messages (C0A7STRJ4G5)."
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: settings
3
- description: Manage assistant voice, avatar, and system settings
3
+ description: Manage assistant voice, avatar (set, remove, view, show), and system settings
4
4
  compatibility: "Designed for Vellum personal assistants"
5
5
  metadata:
6
6
  emoji: "\u2699\uFE0F"
@@ -8,7 +8,7 @@ metadata:
8
8
  display-name: "Settings"
9
9
  ---
10
10
 
11
- Tools for managing assistant settings: voice configuration (TTS voice, PTT activation key, conversation timeout), avatar management, system settings navigation, and in-app settings tab navigation.
11
+ Tools for managing assistant settings: voice configuration (TTS voice, PTT activation key, conversation timeout), avatar management (set, remove, view/show current avatar), system settings navigation, and in-app settings tab navigation.
12
12
 
13
13
  ## Avatar
14
14
 
@@ -21,4 +21,6 @@ The tool copies the image to the canonical location (`data/avatar/avatar-image.p
21
21
 
22
22
  When the user asks to remove, clear, or reset their avatar, use the `remove_avatar` tool. It deletes only the custom image and preserves the native character traits so the character avatar is restored automatically.
23
23
 
24
- **Do NOT use `bash`, `cp`, or `rm` for avatar operations** always use `set_avatar` / `remove_avatar` so the app is properly notified and character traits are preserved.
24
+ When the user asks to see, view, or show their current avatar, use the `get_avatar` tool. It returns the avatar image inline. If no custom image exists but character traits are set, it regenerates the static PNG from traits and returns that.
25
+
26
+ **Do NOT use `bash`, `cp`, `rm`, or `file_read` for avatar operations** — always use `set_avatar` / `remove_avatar` / `get_avatar` so the app is properly notified and character traits are preserved.
@@ -124,6 +124,23 @@
124
124
  },
125
125
  "executor": "tools/avatar-remove.ts",
126
126
  "execution_target": "sandbox"
127
+ },
128
+ {
129
+ "name": "get_avatar",
130
+ "description": "Read and return the current avatar image so the user can see it. Use this when the user asks to see, view, or show their avatar. Returns the image inline — do NOT copy the file or use bash commands.",
131
+ "category": "system",
132
+ "risk": "low",
133
+ "input_schema": {
134
+ "type": "object",
135
+ "properties": {
136
+ "activity": {
137
+ "type": "string",
138
+ "description": "Brief non-technical explanation of what you are doing, shown to the user as a status update."
139
+ }
140
+ }
141
+ },
142
+ "executor": "tools/avatar-get.ts",
143
+ "execution_target": "sandbox"
127
144
  }
128
145
  ]
129
146
  }
@@ -0,0 +1,50 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import { writeTraitsAndRenderAvatar } from "../../../../avatar/traits-png-sync.js";
5
+ import { readImageFile } from "../../../../tools/shared/filesystem/image-read.js";
6
+ import type {
7
+ ToolContext,
8
+ ToolExecutionResult,
9
+ } from "../../../../tools/types.js";
10
+ import { getWorkspaceDir } from "../../../../util/platform.js";
11
+
12
+ export async function run(
13
+ _input: Record<string, unknown>,
14
+ _context: ToolContext,
15
+ ): Promise<ToolExecutionResult> {
16
+ const avatarPath = join(
17
+ getWorkspaceDir(),
18
+ "data",
19
+ "avatar",
20
+ "avatar-image.png",
21
+ );
22
+
23
+ if (!existsSync(avatarPath)) {
24
+ // Check for native character traits and regenerate the static PNG
25
+ const traitsPath = join(
26
+ getWorkspaceDir(),
27
+ "data",
28
+ "avatar",
29
+ "character-traits.json",
30
+ );
31
+ if (existsSync(traitsPath)) {
32
+ try {
33
+ const traits = JSON.parse(readFileSync(traitsPath, "utf-8"));
34
+ const result = writeTraitsAndRenderAvatar(traits);
35
+ if (result.ok && existsSync(avatarPath)) {
36
+ return readImageFile(avatarPath);
37
+ }
38
+ } catch {
39
+ // Fall through to default message
40
+ }
41
+ }
42
+ return {
43
+ content:
44
+ "No avatar is currently set — no custom image and no character traits found.",
45
+ isError: false,
46
+ };
47
+ }
48
+
49
+ return readImageFile(avatarPath);
50
+ }
@@ -10,6 +10,7 @@ import type {
10
10
  } from "../../../../tools/types.js";
11
11
  import { getLogger } from "../../../../util/logger.js";
12
12
  import { getWorkspaceDir } from "../../../../util/platform.js";
13
+ import { updateIdentityAvatarSection } from "./identity-avatar.js";
13
14
 
14
15
  const log = getLogger("avatar-remove");
15
16
 
@@ -50,6 +51,12 @@ export async function run(
50
51
  log.warn({ err }, "Failed to publish avatar_updated event");
51
52
  });
52
53
 
54
+ // Update IDENTITY.md to reflect the avatar was removed.
55
+ updateIdentityAvatarSection(
56
+ "Default character avatar (no custom image set)",
57
+ log,
58
+ );
59
+
53
60
  log.info("Custom avatar removed, reverting to character avatar");
54
61
 
55
62
  return {
@@ -10,6 +10,7 @@ import type {
10
10
  } from "../../../../tools/types.js";
11
11
  import { getLogger } from "../../../../util/logger.js";
12
12
  import { getWorkspaceDir } from "../../../../util/platform.js";
13
+ import { updateIdentityAvatarSection } from "./identity-avatar.js";
13
14
 
14
15
  const log = getLogger("avatar-update");
15
16
 
@@ -71,10 +72,14 @@ export async function run(
71
72
  log.warn({ err }, "Failed to publish avatar_updated event");
72
73
  });
73
74
 
75
+ // Clear any stale avatar description from IDENTITY.md so the assistant
76
+ // is prompted to describe the new image on next read.
77
+ updateIdentityAvatarSection(null, log);
78
+
74
79
  log.info({ avatarPath, source: resolvedSource }, "Avatar updated");
75
80
 
76
81
  return {
77
- content: `Avatar updated from ${sourcePath}. The app will refresh automatically.`,
82
+ content: `Avatar updated from ${sourcePath}. The app will refresh automatically. The avatar description in IDENTITY.md has been cleared — describe what the new avatar looks like by updating the ## Avatar section in IDENTITY.md with a plain-text description (not an image link).`,
78
83
  isError: false,
79
84
  };
80
85
  }