@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
@@ -10,11 +10,15 @@
10
10
  */
11
11
 
12
12
  import { getPlatformBaseUrl } from "../../config/env.js";
13
+ import { credentialKey } from "../../security/credential-key.js";
13
14
  import { getSecureKey } from "../../security/secure-keys.js";
14
15
  import { MANAGED_PROVIDER_META } from "./constants.js";
15
16
 
16
17
  /** Storage key for the assistant API key credential. */
17
- const ASSISTANT_API_KEY_STORAGE_KEY = "credential:vellum:assistant_api_key";
18
+ const ASSISTANT_API_KEY_STORAGE_KEY = credentialKey(
19
+ "vellum",
20
+ "assistant_api_key",
21
+ );
18
22
 
19
23
  export interface ManagedProxyContext {
20
24
  /** Whether managed proxy prerequisites are satisfied. */
@@ -74,6 +74,15 @@ export class RateLimitProvider implements Provider {
74
74
 
75
75
  if (this.requestTimestamps.length >= limit) {
76
76
  const waitSec = Math.ceil((oldestInWindow + 60_000 - now) / 1000);
77
+ log.warn(
78
+ {
79
+ provider: this.name,
80
+ limit,
81
+ currentCount: this.requestTimestamps.length,
82
+ retryAfterSec: waitSec,
83
+ },
84
+ `Provider rate limit exceeded: ${limit} requests/minute for ${this.name}`,
85
+ );
77
86
  throw new RateLimitError(
78
87
  `Rate limit exceeded: ${limit} requests/minute. Try again in ${waitSec}s.`,
79
88
  );
@@ -85,6 +94,14 @@ export class RateLimitProvider implements Provider {
85
94
  if (limit <= 0) return;
86
95
 
87
96
  if (this.sessionTokens >= limit) {
97
+ log.warn(
98
+ {
99
+ provider: this.name,
100
+ sessionTokens: this.sessionTokens,
101
+ limit,
102
+ },
103
+ `Session token budget exhausted for ${this.name}: ${this.sessionTokens.toLocaleString()}/${limit.toLocaleString()}`,
104
+ );
88
105
  throw new RateLimitError(
89
106
  `Session token budget exhausted: ${this.sessionTokens.toLocaleString()}/${limit.toLocaleString()} tokens used. Start a new session to continue.`,
90
107
  );
@@ -289,8 +289,8 @@ export function initializeProviders(config: ProvidersConfig): void {
289
289
  );
290
290
  routingSources.set("gemini", "user-key");
291
291
  } else {
292
- // No user Gemini key — try managed proxy fallback
293
- const managedBaseUrl = buildManagedBaseUrl("gemini");
292
+ // No user Gemini key — route through Vertex managed proxy
293
+ const managedBaseUrl = buildManagedBaseUrl("vertex");
294
294
  if (managedBaseUrl) {
295
295
  const ctx = resolveManagedProxyContext();
296
296
  const model = resolveModel(config, "gemini");
@@ -1,5 +1,5 @@
1
1
  import { ProviderError } from "../util/errors.js";
2
- import { getLogger, isDebug } from "../util/logger.js";
2
+ import { getLogger } from "../util/logger.js";
3
3
  import {
4
4
  computeRetryDelay,
5
5
  DEFAULT_BASE_DELAY_MS,
@@ -96,43 +96,17 @@ export class RetryProvider implements Provider {
96
96
  options?: SendMessageOptions,
97
97
  ): Promise<ProviderResponse> {
98
98
  let lastError: unknown;
99
- const debug = isDebug();
100
-
101
- if (debug) {
102
- log.debug(
103
- {
104
- provider: this.name,
105
- messageCount: messages.length,
106
- toolCount: tools?.length ?? 0,
107
- },
108
- "Provider sendMessage start",
109
- );
110
- }
111
99
 
112
100
  const normalizedOptions = normalizeSendMessageOptions(this.name, options);
113
101
 
114
102
  for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
115
103
  try {
116
- const start = Date.now();
117
104
  const result = await this.inner.sendMessage(
118
105
  messages,
119
106
  tools,
120
107
  systemPrompt,
121
108
  normalizedOptions,
122
109
  );
123
- if (debug) {
124
- log.debug(
125
- {
126
- provider: this.name,
127
- durationMs: Date.now() - start,
128
- attempt: attempt + 1,
129
- model: result.model,
130
- inputTokens: result.usage.inputTokens,
131
- outputTokens: result.usage.outputTokens,
132
- },
133
- "Provider sendMessage success",
134
- );
135
- }
136
110
  return result;
137
111
  } catch (error) {
138
112
  lastError = error;
@@ -51,6 +51,23 @@ Channel approval flows use `requestId` (not `runId`) as the primary identifier:
51
51
  - Guardian approval records in `channelGuardianApprovalRequests` link via `requestId`.
52
52
  - The conversational approval engine classifies user intent and resolves via `session.handleConfirmationResponse(requestId, decision)`.
53
53
 
54
+ ## Rate Limiting & Diagnostics
55
+
56
+ All `/v1/*` endpoints share a per-client-IP sliding-window rate limiter (`middleware/rate-limiter.ts`):
57
+
58
+ - **Authenticated**: 300 requests/minute
59
+ - **Unauthenticated**: 20 requests/minute
60
+
61
+ When the limit is exceeded, the limiter returns 429 and logs a structured warning (module: `rate-limiter`) with the denied endpoint and a breakdown of which endpoints consumed the budget in the current window. This makes it easy to identify whether the cause is rapid thread switching, polling, or unexpected request volume.
62
+
63
+ Logs are written to `~/.vellum/workspace/data/logs/vellum.log` by default. If `logFile.dir` is configured, logs rotate daily as `assistant-YYYY-MM-DD.log` in that directory. To watch rate limit events in real time:
64
+
65
+ ```bash
66
+ tail -f ~/.vellum/workspace/data/logs/vellum.log | grep rate-limit
67
+ ```
68
+
69
+ The provider-level rate limiter (`providers/ratelimit.ts`) also logs warnings (module: `rate-limit`) when request rate or token budget limits are enforced.
70
+
54
71
  ## HTTP-Only Transport
55
72
 
56
73
  HTTP is the sole transport for client-daemon communication. The runtime HTTP server (`assistant/src/runtime/http-server.ts`) is the canonical API surface. Clients connect via HTTP for request/response operations and SSE (`GET /v1/events`) for streaming server-to-client events.
@@ -358,9 +358,6 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
358
358
  { endpoint: "computer-use/sessions/abort", scopes: ["chat.write"] },
359
359
  { endpoint: "computer-use/observations", scopes: ["chat.write"] },
360
360
  { endpoint: "computer-use/tasks", scopes: ["chat.write"] },
361
- { endpoint: "computer-use/ride-shotgun/start", scopes: ["chat.write"] },
362
- { endpoint: "computer-use/ride-shotgun/stop", scopes: ["chat.write"] },
363
- { endpoint: "computer-use/ride-shotgun/status", scopes: ["chat.write"] },
364
361
  { endpoint: "computer-use/watch", scopes: ["chat.write"] },
365
362
 
366
363
  // Recordings
@@ -16,6 +16,7 @@ import {
16
16
  saveRawConfig,
17
17
  setNestedValue,
18
18
  } from "../../config/loader.js";
19
+ import { credentialKey } from "../../security/credential-key.js";
19
20
  import { getSecureKey } from "../../security/secure-keys.js";
20
21
  import { getTelegramBotUsername } from "../../telegram/bot-username.js";
21
22
  import { getLogger } from "../../util/logger.js";
@@ -40,7 +41,7 @@ export async function ensureTelegramBotUsernameResolved(): Promise<void> {
40
41
  return; // Username already cached in config
41
42
  }
42
43
 
43
- const token = getSecureKey("credential:telegram:bot_token");
44
+ const token = getSecureKey(credentialKey("telegram", "bot_token"));
44
45
  if (!token) return;
45
46
 
46
47
  try {
@@ -3,6 +3,7 @@ import { hasTwilioCredentials } from "../calls/twilio-rest.js";
3
3
  import { getChannelInvitePolicy } from "../channels/config.js";
4
4
  import { loadRawConfig } from "../config/loader.js";
5
5
  import { getEmailService } from "../email/service.js";
6
+ import { credentialKey } from "../security/credential-key.js";
6
7
  import { getSecureKey } from "../security/secure-keys.js";
7
8
  import { resolveWhatsAppDisplayNumber } from "./channel-invite-transports/whatsapp.js";
8
9
  import type {
@@ -11,6 +12,7 @@ import type {
11
12
  ChannelProbeContext,
12
13
  ChannelReadinessSnapshot,
13
14
  ReadinessCheckResult,
15
+ SetupStatus,
14
16
  } from "./channel-readiness-types.js";
15
17
  import { probeLocalGatewayHealth } from "./local-gateway-health.js";
16
18
 
@@ -31,55 +33,83 @@ function hasIngressConfigured(): boolean {
31
33
  }
32
34
  }
33
35
 
36
+ // ── Shared check helpers ────────────────────────────────────────────────────
37
+
38
+ /** Build a check result from a boolean condition. */
39
+ function check(
40
+ name: string,
41
+ passed: boolean,
42
+ passMessage: string,
43
+ failMessage: string,
44
+ ): ReadinessCheckResult {
45
+ return { name, passed, message: passed ? passMessage : failMessage };
46
+ }
47
+
48
+ /** Check that a secure credential key exists. */
49
+ function checkCredential(
50
+ name: string,
51
+ service: string,
52
+ field: string,
53
+ label: string,
54
+ ): ReadinessCheckResult {
55
+ const exists = !!getSecureKey(credentialKey(service, field));
56
+ return check(
57
+ name,
58
+ exists,
59
+ `${label} is configured`,
60
+ `${label} is not configured`,
61
+ );
62
+ }
63
+
64
+ /** Check that public ingress is configured and enabled. */
65
+ function checkIngress(): ReadinessCheckResult {
66
+ const has = hasIngressConfigured();
67
+ return check(
68
+ "ingress",
69
+ has,
70
+ "Public ingress URL is configured",
71
+ "Public ingress URL is not configured or disabled",
72
+ );
73
+ }
74
+
34
75
  // ── Voice Probe ─────────────────────────────────────────────────────────────
35
76
 
36
77
  const voiceProbe: ChannelProbe = {
37
78
  channel: "phone",
38
79
  async runLocalChecks(): Promise<ReadinessCheckResult[]> {
39
- const results: ReadinessCheckResult[] = [];
40
-
41
80
  const hasCreds = hasTwilioCredentials();
42
- results.push({
43
- name: "twilio_credentials",
44
- passed: hasCreds,
45
- message: hasCreds
46
- ? "Twilio credentials are configured"
47
- : "Twilio Account SID and Auth Token are not configured",
48
- });
49
-
50
- const resolvedNumber = resolveTwilioPhoneNumber();
51
- const hasPhone = !!resolvedNumber;
52
- results.push({
53
- name: "phone_number",
54
- passed: hasPhone,
55
- message: hasPhone
56
- ? "Phone number is assigned for voice calls"
57
- : "No phone number assigned for voice calls",
58
- });
59
-
60
- const hasIngress = hasIngressConfigured();
61
- results.push({
62
- name: "ingress",
63
- passed: hasIngress,
64
- message: hasIngress
65
- ? "Public ingress URL is configured"
66
- : "Public ingress URL is not configured or disabled",
67
- });
68
-
69
- if (hasIngress) {
70
- const gatewayHealth = await probeLocalGatewayHealth();
71
- results.push({
72
- name: "gateway_health",
73
- passed: gatewayHealth.healthy,
74
- message: gatewayHealth.healthy
75
- ? `Local gateway is serving requests at ${gatewayHealth.target}`
76
- : `Local gateway is not serving requests at ${gatewayHealth.target}${
77
- gatewayHealth.error ? `: ${gatewayHealth.error}` : ""
78
- }`,
79
- });
81
+ const hasPhone = !!resolveTwilioPhoneNumber();
82
+ const ingress = checkIngress();
83
+
84
+ const checks: ReadinessCheckResult[] = [
85
+ check(
86
+ "twilio_credentials",
87
+ hasCreds,
88
+ "Twilio credentials are configured",
89
+ "Twilio Account SID and Auth Token are not configured",
90
+ ),
91
+ check(
92
+ "phone_number",
93
+ hasPhone,
94
+ "Phone number is assigned for voice calls",
95
+ "No phone number assigned for voice calls",
96
+ ),
97
+ ingress,
98
+ ];
99
+
100
+ if (ingress.passed) {
101
+ const gw = await probeLocalGatewayHealth();
102
+ checks.push(
103
+ check(
104
+ "gateway_health",
105
+ gw.healthy,
106
+ `Local gateway is serving requests at ${gw.target}`,
107
+ `Local gateway is not serving requests at ${gw.target}${gw.error ? `: ${gw.error}` : ""}`,
108
+ ),
109
+ );
80
110
  }
81
111
 
82
- return results;
112
+ return checks;
83
113
  },
84
114
  };
85
115
 
@@ -87,41 +117,16 @@ const voiceProbe: ChannelProbe = {
87
117
 
88
118
  const telegramProbe: ChannelProbe = {
89
119
  channel: "telegram",
90
- runLocalChecks(): ReadinessCheckResult[] {
91
- const results: ReadinessCheckResult[] = [];
92
-
93
- const hasBotToken = !!getSecureKey("credential:telegram:bot_token");
94
- results.push({
95
- name: "bot_token",
96
- passed: hasBotToken,
97
- message: hasBotToken
98
- ? "Telegram bot token is configured"
99
- : "Telegram bot token is not configured",
100
- });
101
-
102
- const hasWebhookSecret = !!getSecureKey(
103
- "credential:telegram:webhook_secret",
104
- );
105
- results.push({
106
- name: "webhook_secret",
107
- passed: hasWebhookSecret,
108
- message: hasWebhookSecret
109
- ? "Telegram webhook secret is configured"
110
- : "Telegram webhook secret is not configured",
111
- });
112
-
113
- const hasIngress = hasIngressConfigured();
114
- results.push({
115
- name: "ingress",
116
- passed: hasIngress,
117
- message: hasIngress
118
- ? "Public ingress URL is configured"
119
- : "Public ingress URL is not configured or disabled",
120
- });
121
-
122
- return results;
123
- },
124
- // Telegram has no remote checks currently
120
+ runLocalChecks: () => [
121
+ checkCredential("bot_token", "telegram", "bot_token", "Telegram bot token"),
122
+ checkCredential(
123
+ "webhook_secret",
124
+ "telegram",
125
+ "webhook_secret",
126
+ "Telegram webhook secret",
127
+ ),
128
+ checkIngress(),
129
+ ],
125
130
  };
126
131
 
127
132
  // ── Email Probe ─────────────────────────────────────────────────────────────
@@ -129,43 +134,33 @@ const telegramProbe: ChannelProbe = {
129
134
  const emailProbe: ChannelProbe = {
130
135
  channel: "email",
131
136
  runLocalChecks(): ReadinessCheckResult[] {
132
- const results: ReadinessCheckResult[] = [];
133
-
134
137
  const hasApiKey = !!(
135
- getSecureKey("agentmail") || getSecureKey("credential:agentmail:api_key")
138
+ getSecureKey("agentmail") ||
139
+ getSecureKey(credentialKey("agentmail", "api_key"))
136
140
  );
137
- results.push({
138
- name: "agentmail_api_key",
139
- passed: hasApiKey,
140
- message: hasApiKey
141
- ? "AgentMail API key is configured"
142
- : "AgentMail API key is not configured",
143
- });
144
-
145
141
  const invitePolicy = getChannelInvitePolicy("email");
146
- results.push({
147
- name: "invite_policy",
148
- passed: invitePolicy.codeRedemptionEnabled,
149
- message: invitePolicy.codeRedemptionEnabled
150
- ? "Email invite code redemption is enabled"
151
- : "Email invite code redemption is disabled",
152
- });
153
-
154
- const hasIngress = hasIngressConfigured();
155
- results.push({
156
- name: "ingress",
157
- passed: hasIngress,
158
- message: hasIngress
159
- ? "Public ingress URL is configured"
160
- : "Public ingress URL is not configured or disabled",
161
- });
162
-
163
- return results;
142
+ return [
143
+ check(
144
+ "agentmail_api_key",
145
+ hasApiKey,
146
+ "AgentMail API key is configured",
147
+ "AgentMail API key is not configured",
148
+ ),
149
+ check(
150
+ "invite_policy",
151
+ invitePolicy.codeRedemptionEnabled,
152
+ "Email invite code redemption is enabled",
153
+ "Email invite code redemption is disabled",
154
+ ),
155
+ checkIngress(),
156
+ ];
164
157
  },
158
+ // runRemoteChecks UNCHANGED — keep the existing inbox_configured check as-is
165
159
  async runRemoteChecks(): Promise<ReadinessCheckResult[]> {
166
160
  // Only worth checking if the API key is present
167
161
  const hasApiKey = !!(
168
- getSecureKey("agentmail") || getSecureKey("credential:agentmail:api_key")
162
+ getSecureKey("agentmail") ||
163
+ getSecureKey(credentialKey("agentmail", "api_key"))
169
164
  );
170
165
  if (!hasApiKey) return [];
171
166
 
@@ -199,77 +194,47 @@ const emailProbe: ChannelProbe = {
199
194
  const whatsappProbe: ChannelProbe = {
200
195
  channel: "whatsapp",
201
196
  runLocalChecks(): ReadinessCheckResult[] {
202
- const results: ReadinessCheckResult[] = [];
203
-
204
- const hasPhoneNumberId = !!getSecureKey(
205
- "credential:whatsapp:phone_number_id",
206
- );
207
- results.push({
208
- name: "whatsapp_phone_number_id",
209
- passed: hasPhoneNumberId,
210
- message: hasPhoneNumberId
211
- ? "WhatsApp phone number ID is configured"
212
- : "WhatsApp phone number ID is not configured",
213
- });
214
-
215
- const hasAccessToken = !!getSecureKey("credential:whatsapp:access_token");
216
- results.push({
217
- name: "whatsapp_access_token",
218
- passed: hasAccessToken,
219
- message: hasAccessToken
220
- ? "WhatsApp access token is configured"
221
- : "WhatsApp access token is not configured",
222
- });
223
-
224
- const hasAppSecret = !!getSecureKey("credential:whatsapp:app_secret");
225
- results.push({
226
- name: "whatsapp_app_secret",
227
- passed: hasAppSecret,
228
- message: hasAppSecret
229
- ? "WhatsApp app secret is configured"
230
- : "WhatsApp app secret is not configured",
231
- });
232
-
233
- const hasWebhookVerifyToken = !!getSecureKey(
234
- "credential:whatsapp:webhook_verify_token",
235
- );
236
- results.push({
237
- name: "whatsapp_webhook_verify_token",
238
- passed: hasWebhookVerifyToken,
239
- message: hasWebhookVerifyToken
240
- ? "WhatsApp webhook verify token is configured"
241
- : "WhatsApp webhook verify token is not configured",
242
- });
243
-
244
197
  const displayNumber = resolveWhatsAppDisplayNumber();
245
- const hasDisplayNumber = !!displayNumber;
246
- results.push({
247
- name: "whatsapp_display_phone_number",
248
- passed: hasDisplayNumber,
249
- message: hasDisplayNumber
250
- ? `WhatsApp display phone number is configured (${displayNumber})`
251
- : "WhatsApp display phone number is not configured — set whatsapp.phoneNumber in workspace config",
252
- });
253
-
254
198
  const invitePolicy = getChannelInvitePolicy("whatsapp");
255
- results.push({
256
- name: "invite_policy",
257
- passed: invitePolicy.codeRedemptionEnabled,
258
- message: invitePolicy.codeRedemptionEnabled
259
- ? "WhatsApp invite code redemption is enabled"
260
- : "WhatsApp invite code redemption is disabled",
261
- });
262
-
263
- const hasIngress = hasIngressConfigured();
264
- results.push({
265
- name: "ingress",
266
- passed: hasIngress,
267
- message: hasIngress
268
- ? "Public ingress URL is configured"
269
- : "Public ingress URL is not configured or disabled",
270
- });
271
-
272
- return results;
199
+ return [
200
+ checkCredential(
201
+ "whatsapp_phone_number_id",
202
+ "whatsapp",
203
+ "phone_number_id",
204
+ "WhatsApp phone number ID",
205
+ ),
206
+ checkCredential(
207
+ "whatsapp_access_token",
208
+ "whatsapp",
209
+ "access_token",
210
+ "WhatsApp access token",
211
+ ),
212
+ checkCredential(
213
+ "whatsapp_app_secret",
214
+ "whatsapp",
215
+ "app_secret",
216
+ "WhatsApp app secret",
217
+ ),
218
+ checkCredential(
219
+ "whatsapp_webhook_verify_token",
220
+ "whatsapp",
221
+ "webhook_verify_token",
222
+ "WhatsApp webhook verify token",
223
+ ),
224
+ check(
225
+ "whatsapp_display_phone_number",
226
+ !!displayNumber,
227
+ `WhatsApp display phone number is configured (${displayNumber})`,
228
+ "WhatsApp display phone number is not configured — set whatsapp.phoneNumber in workspace config",
229
+ ),
230
+ check(
231
+ "invite_policy",
232
+ invitePolicy.codeRedemptionEnabled,
233
+ "WhatsApp invite code redemption is enabled",
234
+ "WhatsApp invite code redemption is disabled",
235
+ ),
236
+ checkIngress(),
237
+ ];
273
238
  },
274
239
  };
275
240
 
@@ -277,26 +242,20 @@ const whatsappProbe: ChannelProbe = {
277
242
 
278
243
  const slackProbe: ChannelProbe = {
279
244
  channel: "slack",
280
- runLocalChecks(): ReadinessCheckResult[] {
281
- const hasBotToken = !!getSecureKey("credential:slack_channel:bot_token");
282
- const hasAppToken = !!getSecureKey("credential:slack_channel:app_token");
283
- return [
284
- {
285
- name: "bot_token",
286
- passed: hasBotToken,
287
- message: hasBotToken
288
- ? "Slack bot token is configured"
289
- : "Slack bot token is not configured",
290
- },
291
- {
292
- name: "app_token",
293
- passed: hasAppToken,
294
- message: hasAppToken
295
- ? "Slack app token is configured"
296
- : "Slack app token is not configured",
297
- },
298
- ];
299
- },
245
+ runLocalChecks: () => [
246
+ checkCredential(
247
+ "bot_token",
248
+ "slack_channel",
249
+ "bot_token",
250
+ "Slack bot token",
251
+ ),
252
+ checkCredential(
253
+ "app_token",
254
+ "slack_channel",
255
+ "app_token",
256
+ "Slack app token",
257
+ ),
258
+ ],
300
259
  };
301
260
 
302
261
  // ── Service ─────────────────────────────────────────────────────────────────
@@ -369,6 +328,18 @@ export class ChannelReadinessService {
369
328
  : true;
370
329
  const ready = allLocalPassed && allRemotePassed;
371
330
 
331
+ // setupStatus: considers all checks (credentials + infrastructure)
332
+ const consideredChecks = [
333
+ ...localChecks,
334
+ ...(remoteChecks && remoteChecksAffectReadiness ? remoteChecks : []),
335
+ ];
336
+ const anyCheckPassed = consideredChecks.some((c) => c.passed);
337
+ const setupStatus: SetupStatus = !anyCheckPassed
338
+ ? "not_configured"
339
+ : ready
340
+ ? "ready"
341
+ : "incomplete";
342
+
372
343
  const reasons: Array<{ code: string; text: string }> = [];
373
344
  for (const check of localChecks) {
374
345
  if (!check.passed) {
@@ -386,6 +357,7 @@ export class ChannelReadinessService {
386
357
  const snapshot: ChannelReadinessSnapshot = {
387
358
  channel: ch,
388
359
  ready,
360
+ setupStatus,
389
361
  checkedAt:
390
362
  remoteChecks && cached && !remoteChecksFreshlyFetched
391
363
  ? cached.checkedAt
@@ -422,6 +394,7 @@ export class ChannelReadinessService {
422
394
  return {
423
395
  channel,
424
396
  ready: false,
397
+ setupStatus: "not_configured",
425
398
  checkedAt: Date.now(),
426
399
  stale: false,
427
400
  reasons: [
@@ -4,6 +4,9 @@ import type { ChannelId } from "../channels/types.js";
4
4
 
5
5
  export type { ChannelId };
6
6
 
7
+ /** Setup progress for a channel: not_configured → incomplete → ready. */
8
+ export type SetupStatus = "not_configured" | "incomplete" | "ready";
9
+
7
10
  /** Result of a single readiness check (local or remote). */
8
11
  export interface ReadinessCheckResult {
9
12
  name: string;
@@ -15,6 +18,7 @@ export interface ReadinessCheckResult {
15
18
  export interface ChannelReadinessSnapshot {
16
19
  channel: ChannelId;
17
20
  ready: boolean;
21
+ setupStatus: SetupStatus;
18
22
  checkedAt: number;
19
23
  stale: boolean;
20
24
  reasons: Array<{ code: string; text: string }>;
@@ -208,43 +208,3 @@ export async function deliverReplyViaCallback(
208
208
  break;
209
209
  }
210
210
  }
211
-
212
- /**
213
- * Deliver only the attachments from the last assistant message, skipping text.
214
- * Used when streaming already delivered the text content and only file
215
- * attachments remain to be sent via the normal delivery path.
216
- */
217
- export async function deliverAttachmentsOnly(
218
- conversationId: string,
219
- externalChatId: string,
220
- callbackUrl: string,
221
- bearerToken?: string,
222
- assistantId?: string,
223
- ): Promise<void> {
224
- const msgs = getMessages(conversationId);
225
- for (let i = msgs.length - 1; i >= 0; i--) {
226
- if (msgs[i].role !== "assistant") continue;
227
-
228
- const linked = attachmentsStore.getAttachmentMetadataForMessage(msgs[i].id);
229
- if (linked.length === 0) return;
230
-
231
- const replyAttachments: RuntimeAttachmentMetadata[] = linked.map((a) => ({
232
- id: a.id,
233
- filename: a.originalFilename,
234
- mimeType: a.mimeType,
235
- sizeBytes: a.sizeBytes,
236
- kind: a.kind,
237
- }));
238
-
239
- await deliverChannelReply(
240
- callbackUrl,
241
- {
242
- chatId: externalChatId,
243
- attachments: replyAttachments,
244
- assistantId,
245
- },
246
- bearerToken,
247
- );
248
- break;
249
- }
250
- }