@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
@@ -7,6 +7,8 @@
7
7
  * the same pattern as other gateway-forwarded control-plane endpoints.
8
8
  */
9
9
 
10
+ import { z } from "zod";
11
+
10
12
  import { getDb } from "../../memory/db-connection.js";
11
13
  import { getMaxMigrationVersion } from "../../memory/migrations/registry.js";
12
14
  import { rollbackMemoryMigration } from "../../memory/migrations/validate-migration-state.js";
@@ -25,6 +27,29 @@ export function migrationRollbackRouteDefinitions(): RouteDefinition[] {
25
27
  {
26
28
  endpoint: "admin/rollback-migrations",
27
29
  method: "POST",
30
+ summary: "Rollback migrations",
31
+ description:
32
+ "Roll back DB and/or workspace migrations to a specified target version. Restricted to gateway service principals.",
33
+ tags: ["admin"],
34
+ requestBody: z.object({
35
+ targetDbVersion: z
36
+ .number()
37
+ .int()
38
+ .describe("Target DB migration version"),
39
+ targetWorkspaceMigrationId: z
40
+ .string()
41
+ .describe("Target workspace migration ID"),
42
+ rollbackToRegistryCeiling: z
43
+ .boolean()
44
+ .describe("Auto-determine targets from daemon registry ceilings"),
45
+ }),
46
+ responseBody: z.object({
47
+ ok: z.boolean(),
48
+ rolledBack: z
49
+ .object({})
50
+ .passthrough()
51
+ .describe("Lists of rolled-back DB and workspace migrations"),
52
+ }),
28
53
  handler: async ({ req }) => {
29
54
  let body: unknown;
30
55
  try {
@@ -14,6 +14,8 @@
14
14
  import { join } from "node:path";
15
15
  import { Database } from "bun:sqlite";
16
16
 
17
+ import { z } from "zod";
18
+
17
19
  import { invalidateConfigCache } from "../../config/loader.js";
18
20
  import { getDb, resetDb } from "../../memory/db-connection.js";
19
21
  import { validateMigrationState } from "../../memory/migrations/validate-migration-state.js";
@@ -474,21 +476,59 @@ export function migrationRouteDefinitions(): RouteDefinition[] {
474
476
  {
475
477
  endpoint: "migrations/validate",
476
478
  method: "POST",
479
+ summary: "Validate a .vbundle archive",
480
+ description:
481
+ "Upload a .vbundle archive for validation. Accepts raw binary or multipart form data.",
482
+ tags: ["migrations"],
483
+ responseBody: z.object({
484
+ is_valid: z.boolean(),
485
+ errors: z.array(z.unknown()),
486
+ manifest: z.object({}).passthrough(),
487
+ }),
477
488
  handler: async ({ req }) => handleMigrationValidate(req),
478
489
  },
479
490
  {
480
491
  endpoint: "migrations/export",
481
492
  method: "POST",
493
+ summary: "Export a .vbundle archive",
494
+ description:
495
+ "Generate and download a .vbundle archive of the assistant's data. Optional JSON body for metadata.",
496
+ tags: ["migrations"],
497
+ requestBody: z.object({
498
+ description: z.string().describe("Human-readable export description"),
499
+ }),
482
500
  handler: async ({ req }) => handleMigrationExport(req),
483
501
  },
484
502
  {
485
503
  endpoint: "migrations/import-preflight",
486
504
  method: "POST",
505
+ summary: "Dry-run import analysis",
506
+ description:
507
+ "Validate a .vbundle archive and return a report of what would change on import without modifying data.",
508
+ tags: ["migrations"],
509
+ responseBody: z.object({
510
+ can_import: z.boolean(),
511
+ summary: z.object({}).passthrough(),
512
+ files: z.array(z.unknown()),
513
+ conflicts: z.array(z.unknown()),
514
+ manifest: z.object({}).passthrough(),
515
+ }),
487
516
  handler: async ({ req }) => handleMigrationImportPreflight(req),
488
517
  },
489
518
  {
490
519
  endpoint: "migrations/import",
491
520
  method: "POST",
521
+ summary: "Import a .vbundle archive",
522
+ description:
523
+ "Commit a .vbundle archive import to disk — destructive. Backs up existing files before overwriting.",
524
+ tags: ["migrations"],
525
+ responseBody: z.object({
526
+ success: z.boolean(),
527
+ summary: z.object({}).passthrough(),
528
+ files: z.array(z.unknown()),
529
+ manifest: z.object({}).passthrough(),
530
+ warnings: z.array(z.unknown()),
531
+ }),
492
532
  handler: async ({ req }) => handleMigrationImport(req),
493
533
  },
494
534
  ];
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import { eq } from "drizzle-orm";
9
+ import { z } from "zod";
9
10
 
10
11
  import { getDb } from "../../memory/db.js";
11
12
  import { notificationDeliveries } from "../../memory/schema.js";
@@ -19,6 +20,25 @@ export function notificationRouteDefinitions(): RouteDefinition[] {
19
20
  endpoint: "notification-intent-result",
20
21
  method: "POST",
21
22
  policyKey: "notification-intent-result",
23
+ summary: "Report notification delivery result",
24
+ description:
25
+ "Client acknowledgment for local notification delivery outcome.",
26
+ tags: ["notifications"],
27
+ requestBody: z.object({
28
+ deliveryId: z.string().describe("Notification delivery ID"),
29
+ success: z.boolean().describe("Whether delivery succeeded").optional(),
30
+ errorMessage: z
31
+ .string()
32
+ .describe("Error message if delivery failed")
33
+ .optional(),
34
+ errorCode: z
35
+ .string()
36
+ .describe("Error code if delivery failed")
37
+ .optional(),
38
+ }),
39
+ responseBody: z.object({
40
+ ok: z.boolean(),
41
+ }),
22
42
  handler: async ({ req }) => {
23
43
  const body = (await req.json()) as {
24
44
  deliveryId?: string;
@@ -21,9 +21,13 @@ import {
21
21
  import { httpError } from "../http-errors.js";
22
22
  import type { RouteDefinition } from "../http-router.js";
23
23
 
24
- function parseGrantedScopes(grantedScopes: string | string[] | null | undefined): string[] {
24
+ function parseGrantedScopes(
25
+ grantedScopes: string | string[] | null | undefined,
26
+ ): string[] {
25
27
  if (Array.isArray(grantedScopes)) {
26
- return grantedScopes.filter((scope): scope is string => typeof scope === "string");
28
+ return grantedScopes.filter(
29
+ (scope): scope is string => typeof scope === "string",
30
+ );
27
31
  }
28
32
 
29
33
  if (typeof grantedScopes !== "string" || grantedScopes.trim() === "") {
@@ -77,7 +81,8 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
77
81
  description: providerRow.description ?? null,
78
82
  dashboard_url: providerRow.dashboardUrl ?? null,
79
83
  client_id_placeholder: providerRow.clientIdPlaceholder ?? null,
80
- requires_client_secret: providerRow.requiresClientSecret ?? 1,
84
+ requires_client_secret: !!(providerRow.requiresClientSecret ?? 1),
85
+ supports_managed_mode: !!providerRow.managedServiceConfigKey,
81
86
  }
82
87
  : null;
83
88
 
@@ -159,7 +164,11 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
159
164
  handler: async ({ params }) => {
160
165
  const app = getApp(params.id);
161
166
  if (!app) {
162
- return httpError("NOT_FOUND", `OAuth app not found: ${params.id}`, 404);
167
+ return httpError(
168
+ "NOT_FOUND",
169
+ `OAuth app not found: ${params.id}`,
170
+ 404,
171
+ );
163
172
  }
164
173
 
165
174
  // Disconnect all connections for this app first to clean up tokens.
@@ -2,6 +2,8 @@
2
2
  * Pairing HTTP route handlers for device pairing flow.
3
3
  */
4
4
 
5
+ import { z } from "zod";
6
+
5
7
  import {
6
8
  hashDeviceId,
7
9
  isDeviceApproved,
@@ -417,6 +419,19 @@ export function pairingRouteDefinitions(deps: {
417
419
  {
418
420
  endpoint: "pairing/register",
419
421
  method: "POST",
422
+ summary: "Register pairing request",
423
+ description:
424
+ "Pre-register a pairing request when the QR code is displayed.",
425
+ tags: ["pairing"],
426
+ requestBody: z.object({
427
+ pairingRequestId: z.string(),
428
+ pairingSecret: z.string(),
429
+ gatewayUrl: z.string(),
430
+ localLanUrl: z.string().optional(),
431
+ }),
432
+ responseBody: z.object({
433
+ ok: z.boolean(),
434
+ }),
420
435
  handler: async ({ req }) =>
421
436
  handlePairingRegister(req, deps.getPairingContext()),
422
437
  },
@@ -9,6 +9,8 @@
9
9
  * require `settings.read`.
10
10
  */
11
11
 
12
+ import { z } from "zod";
13
+
12
14
  import {
13
15
  getActiveRestartToken,
14
16
  handleRecordingPause,
@@ -298,36 +300,106 @@ export function recordingRouteDefinitions(deps: {
298
300
  endpoint: "recordings/start",
299
301
  method: "POST",
300
302
  policyKey: "recordings/start",
303
+ summary: "Start recording",
304
+ description: "Start a screen recording for a conversation.",
305
+ tags: ["recordings"],
306
+ requestBody: z.object({
307
+ conversationId: z.string(),
308
+ options: z
309
+ .object({})
310
+ .passthrough()
311
+ .describe("Recording options")
312
+ .optional(),
313
+ }),
314
+ responseBody: z.object({
315
+ recordingId: z.string(),
316
+ }),
301
317
  handler: async ({ req }) => handleStartRecording(req, getDeps()),
302
318
  },
303
319
  {
304
320
  endpoint: "recordings/stop",
305
321
  method: "POST",
306
322
  policyKey: "recordings/stop",
323
+ summary: "Stop recording",
324
+ description: "Stop the active screen recording.",
325
+ tags: ["recordings"],
326
+ requestBody: z.object({
327
+ conversationId: z.string(),
328
+ }),
329
+ responseBody: z.object({
330
+ recordingId: z.string(),
331
+ stopped: z.boolean(),
332
+ }),
307
333
  handler: async ({ req }) => handleStopRecording(req, getDeps()),
308
334
  },
309
335
  {
310
336
  endpoint: "recordings/pause",
311
337
  method: "POST",
312
338
  policyKey: "recordings/pause",
339
+ summary: "Pause recording",
340
+ description: "Pause the active screen recording.",
341
+ tags: ["recordings"],
342
+ requestBody: z.object({
343
+ conversationId: z.string(),
344
+ }),
345
+ responseBody: z.object({
346
+ recordingId: z.string(),
347
+ paused: z.boolean(),
348
+ }),
313
349
  handler: async ({ req }) => handlePauseRecording(req, getDeps()),
314
350
  },
315
351
  {
316
352
  endpoint: "recordings/resume",
317
353
  method: "POST",
318
354
  policyKey: "recordings/resume",
355
+ summary: "Resume recording",
356
+ description: "Resume a paused screen recording.",
357
+ tags: ["recordings"],
358
+ requestBody: z.object({
359
+ conversationId: z.string(),
360
+ }),
361
+ responseBody: z.object({
362
+ recordingId: z.string(),
363
+ resumed: z.boolean(),
364
+ }),
319
365
  handler: async ({ req }) => handleResumeRecording(req, getDeps()),
320
366
  },
321
367
  {
322
368
  endpoint: "recordings/status",
323
369
  method: "GET",
324
370
  policyKey: "recordings/status",
371
+ summary: "Get recording status",
372
+ description: "Return the current recording state.",
373
+ tags: ["recordings"],
374
+ responseBody: z.object({
375
+ idle: z.boolean(),
376
+ restartInProgress: z.boolean(),
377
+ }),
325
378
  handler: () => handleGetRecordingStatus(),
326
379
  },
327
380
  {
328
381
  endpoint: "recordings/status",
329
382
  method: "POST",
330
383
  policyKey: "recordings/status:POST",
384
+ summary: "Post recording status",
385
+ description: "Recording lifecycle callback from the client.",
386
+ tags: ["recordings"],
387
+ requestBody: z.object({
388
+ conversationId: z.string(),
389
+ status: z
390
+ .string()
391
+ .describe(
392
+ "started, stopped, failed, restart_cancelled, paused, resumed",
393
+ ),
394
+ filePath: z.string().optional(),
395
+ durationMs: z.number().optional(),
396
+ error: z.string().optional(),
397
+ attachToConversationId: z.string().optional(),
398
+ operationToken: z.string().optional(),
399
+ }),
400
+ responseBody: z.object({
401
+ ok: z.boolean(),
402
+ }),
331
403
  handler: async ({ req }) => handlePostRecordingStatus(req, getDeps()),
332
404
  },
333
405
  ];
@@ -4,6 +4,8 @@
4
4
  * HTTP route handlers for schedule management.
5
5
  */
6
6
 
7
+ import { z } from "zod";
8
+
7
9
  import { bootstrapConversation } from "../../memory/conversation-bootstrap.js";
8
10
  import {
9
11
  cancelSchedule,
@@ -112,11 +114,27 @@ function handleUpdateSchedule(
112
114
  ): Response {
113
115
  const updates: Record<string, unknown> = {};
114
116
 
115
- if ("mode" in body && !VALID_MODES.includes(body.mode as (typeof VALID_MODES)[number])) {
116
- return httpError("BAD_REQUEST", `Invalid mode: must be one of ${VALID_MODES.join(", ")}`, 400);
117
+ if (
118
+ "mode" in body &&
119
+ !VALID_MODES.includes(body.mode as (typeof VALID_MODES)[number])
120
+ ) {
121
+ return httpError(
122
+ "BAD_REQUEST",
123
+ `Invalid mode: must be one of ${VALID_MODES.join(", ")}`,
124
+ 400,
125
+ );
117
126
  }
118
- if ("routingIntent" in body && !VALID_ROUTING_INTENTS.includes(body.routingIntent as (typeof VALID_ROUTING_INTENTS)[number])) {
119
- return httpError("BAD_REQUEST", `Invalid routingIntent: must be one of ${VALID_ROUTING_INTENTS.join(", ")}`, 400);
127
+ if (
128
+ "routingIntent" in body &&
129
+ !VALID_ROUTING_INTENTS.includes(
130
+ body.routingIntent as (typeof VALID_ROUTING_INTENTS)[number],
131
+ )
132
+ ) {
133
+ return httpError(
134
+ "BAD_REQUEST",
135
+ `Invalid routingIntent: must be one of ${VALID_ROUTING_INTENTS.join(", ")}`,
136
+ 400,
137
+ );
120
138
  }
121
139
 
122
140
  for (const key of [
@@ -276,12 +294,27 @@ export function scheduleRouteDefinitions(deps: {
276
294
  endpoint: "schedules",
277
295
  method: "GET",
278
296
  policyKey: "schedules",
297
+ summary: "List schedules",
298
+ description: "Return all scheduled jobs.",
299
+ tags: ["schedules"],
300
+ responseBody: z.object({
301
+ schedules: z.array(z.unknown()).describe("Schedule objects"),
302
+ }),
279
303
  handler: () => handleListSchedules(),
280
304
  },
281
305
  {
282
306
  endpoint: "schedules/:id/toggle",
283
307
  method: "POST",
284
308
  policyKey: "schedules/toggle",
309
+ summary: "Toggle schedule",
310
+ description: "Enable or disable a schedule.",
311
+ tags: ["schedules"],
312
+ requestBody: z.object({
313
+ enabled: z.boolean().describe("New enabled state"),
314
+ }),
315
+ responseBody: z.object({
316
+ schedules: z.array(z.unknown()).describe("Updated schedule list"),
317
+ }),
285
318
  handler: async ({ req, params }) => {
286
319
  const body = (await req.json()) as { enabled?: boolean };
287
320
  if (body.enabled === undefined) {
@@ -294,16 +327,43 @@ export function scheduleRouteDefinitions(deps: {
294
327
  endpoint: "schedules/:id",
295
328
  method: "DELETE",
296
329
  policyKey: "schedules",
330
+ summary: "Delete schedule",
331
+ description: "Remove a schedule by ID.",
332
+ tags: ["schedules"],
333
+ responseBody: z.object({
334
+ schedules: z.array(z.unknown()).describe("Updated schedule list"),
335
+ }),
297
336
  handler: ({ params }) => handleDeleteSchedule(params.id),
298
337
  },
299
338
  {
300
339
  endpoint: "schedules/:id",
301
340
  method: "PATCH",
302
341
  policyKey: "schedules",
342
+ summary: "Update schedule",
343
+ description: "Partially update fields on a schedule.",
344
+ tags: ["schedules"],
345
+ requestBody: z.object({
346
+ name: z.string(),
347
+ expression: z.string(),
348
+ timezone: z.string(),
349
+ message: z.string(),
350
+ mode: z.string().describe("notify or execute"),
351
+ routingIntent: z
352
+ .string()
353
+ .describe("single_channel, multi_channel, or all_channels"),
354
+ quiet: z.boolean(),
355
+ }),
356
+ responseBody: z.object({
357
+ schedules: z.array(z.unknown()).describe("Updated schedule list"),
358
+ }),
303
359
  handler: async ({ req, params }) => {
304
360
  const body: unknown = await req.json();
305
361
  if (typeof body !== "object" || !body || Array.isArray(body)) {
306
- return httpError("BAD_REQUEST", "Request body must be a JSON object", 400);
362
+ return httpError(
363
+ "BAD_REQUEST",
364
+ "Request body must be a JSON object",
365
+ 400,
366
+ );
307
367
  }
308
368
  return handleUpdateSchedule(params.id, body as Record<string, unknown>);
309
369
  },
@@ -312,6 +372,12 @@ export function scheduleRouteDefinitions(deps: {
312
372
  endpoint: "schedules/:id/run",
313
373
  method: "POST",
314
374
  policyKey: "schedules/run",
375
+ summary: "Run schedule now",
376
+ description: "Trigger an immediate execution of a schedule.",
377
+ tags: ["schedules"],
378
+ responseBody: z.object({
379
+ schedules: z.array(z.unknown()).describe("Updated schedule list"),
380
+ }),
315
381
  handler: async ({ params }) =>
316
382
  handleRunScheduleNow(params.id, deps.sendMessageDeps),
317
383
  },
@@ -319,6 +385,12 @@ export function scheduleRouteDefinitions(deps: {
319
385
  endpoint: "schedules/:id/cancel",
320
386
  method: "POST",
321
387
  policyKey: "schedules/cancel",
388
+ summary: "Cancel schedule",
389
+ description: "Cancel a pending schedule.",
390
+ tags: ["schedules"],
391
+ responseBody: z.object({
392
+ schedules: z.array(z.unknown()).describe("Updated schedule list"),
393
+ }),
322
394
  handler: ({ params }) => handleCancelSchedule(params.id),
323
395
  },
324
396
  ];
@@ -1,3 +1,5 @@
1
+ import { z } from "zod";
2
+
1
3
  import {
2
4
  setPlatformAssistantId,
3
5
  setPlatformBaseUrl,
@@ -104,7 +106,7 @@ async function queueApiKeyPropagation(
104
106
 
105
107
  export async function handleAddSecret(
106
108
  req: Request,
107
- getCesClient?: () => CesClient | undefined,
109
+ deps?: SecretRouteDeps,
108
110
  ): Promise<Response> {
109
111
  let body: { type?: string; name?: string; value?: string };
110
112
  try {
@@ -190,9 +192,7 @@ export async function handleAddSecret(
190
192
  500,
191
193
  );
192
194
  }
193
- clearEmbeddingBackendCache();
194
- invalidateConfigCache();
195
- await initializeProviders(getConfig());
195
+ await refreshProvidersAfterSecretChange(deps);
196
196
  log.info({ provider: name }, "API key updated via HTTP");
197
197
  return Response.json({ success: true, type, name }, { status: 201 });
198
198
  }
@@ -273,12 +273,12 @@ export async function handleAddSecret(
273
273
  }
274
274
  }
275
275
  if (isManagedProxyCredential(service, field)) {
276
- await initializeProviders(getConfig());
276
+ await refreshProvidersAfterSecretChange(deps);
277
277
  if (service === "vellum" && field === "assistant_api_key") {
278
278
  // Push the API key to CES so managed credential materialization
279
279
  // works even though the handshake ran before the key was available.
280
280
  const generation = ++apiKeyGeneration;
281
- const cesClient = getCesClient?.();
281
+ const cesClient = deps?.getCesClient?.();
282
282
  if (cesClient) {
283
283
  if (cesClient.isReady()) {
284
284
  try {
@@ -392,7 +392,10 @@ export async function handleReadSecret(req: Request): Promise<Response> {
392
392
  }
393
393
  }
394
394
 
395
- export async function handleDeleteSecret(req: Request): Promise<Response> {
395
+ export async function handleDeleteSecret(
396
+ req: Request,
397
+ deps?: SecretRouteDeps,
398
+ ): Promise<Response> {
396
399
  let body: { type?: string; name?: string };
397
400
  try {
398
401
  body = (await req.json()) as { type?: string; name?: string };
@@ -436,9 +439,7 @@ export async function handleDeleteSecret(req: Request): Promise<Response> {
436
439
  500,
437
440
  );
438
441
  }
439
- clearEmbeddingBackendCache();
440
- invalidateConfigCache();
441
- await initializeProviders(getConfig());
442
+ await refreshProvidersAfterSecretChange(deps);
442
443
  log.info({ provider: name }, "API key deleted via HTTP");
443
444
  return Response.json({ success: true, type, name });
444
445
  }
@@ -486,7 +487,7 @@ export async function handleDeleteSecret(req: Request): Promise<Response> {
486
487
  setSentryUserId(undefined);
487
488
  }
488
489
  if (isManagedProxyCredential(service, field)) {
489
- await initializeProviders(getConfig());
490
+ await refreshProvidersAfterSecretChange(deps);
490
491
  }
491
492
  log.info({ service, field }, "Credential deleted via HTTP");
492
493
  return Response.json({ success: true, type, name });
@@ -532,7 +533,7 @@ export async function handleListSecrets(): Promise<Response> {
532
533
  return { type: "api_key" as const, name: account };
533
534
  });
534
535
 
535
- return Response.json({ secrets });
536
+ return Response.json({ secrets, accounts: secrets });
536
537
  } catch (err) {
537
538
  const message = err instanceof Error ? err.message : String(err);
538
539
  return httpError("INTERNAL_ERROR", message, 500);
@@ -546,6 +547,30 @@ export async function handleListSecrets(): Promise<Response> {
546
547
  export interface SecretRouteDeps {
547
548
  /** Accessor for the CES client, used to push API key updates after hatch. */
548
549
  getCesClient?: () => CesClient | undefined;
550
+ /**
551
+ * Called after provider-affecting credentials change so live conversations
552
+ * can be reloaded with fresh provider instances.
553
+ */
554
+ onProviderCredentialsChanged?: () => void | Promise<void>;
555
+ }
556
+
557
+ async function refreshProvidersAfterSecretChange(
558
+ deps?: SecretRouteDeps,
559
+ ): Promise<void> {
560
+ clearEmbeddingBackendCache();
561
+ invalidateConfigCache();
562
+ await initializeProviders(getConfig());
563
+
564
+ if (!deps?.onProviderCredentialsChanged) return;
565
+
566
+ try {
567
+ await deps.onProviderCredentialsChanged();
568
+ } catch (err) {
569
+ log.warn(
570
+ { error: err instanceof Error ? err.message : String(err) },
571
+ "Failed to refresh live conversations after provider credential change",
572
+ );
573
+ }
549
574
  }
550
575
 
551
576
  export function secretRouteDefinitions(
@@ -555,22 +580,82 @@ export function secretRouteDefinitions(
555
580
  {
556
581
  endpoint: "secrets",
557
582
  method: "POST",
558
- handler: async ({ req }) => handleAddSecret(req, deps?.getCesClient),
583
+ handler: async ({ req }) => handleAddSecret(req, deps),
584
+ summary: "Add a secret",
585
+ description:
586
+ "Store a new secret (API key, OAuth token, etc.) in the credential vault.",
587
+ tags: ["secrets"],
588
+ requestBody: z.object({
589
+ type: z.string().describe("Secret type: 'api_key' or 'credential'"),
590
+ name: z.string().describe("Unique name for the secret"),
591
+ value: z.string().describe("Secret value to store"),
592
+ }),
593
+ responseBody: z.object({
594
+ success: z.boolean(),
595
+ type: z.string(),
596
+ name: z.string(),
597
+ }),
559
598
  },
560
599
  {
561
600
  endpoint: "secrets",
562
601
  method: "DELETE",
563
- handler: async ({ req }) => handleDeleteSecret(req),
602
+ handler: async ({ req }) => handleDeleteSecret(req, deps),
603
+ summary: "Delete a secret",
604
+ description: "Remove a secret from the credential vault by name.",
605
+ tags: ["secrets"],
606
+ requestBody: z.object({
607
+ type: z.string().describe("Secret type: 'api_key' or 'credential'"),
608
+ name: z.string().describe("Name of the secret to delete"),
609
+ }),
610
+ responseBody: z.object({
611
+ success: z.boolean(),
612
+ type: z.string(),
613
+ name: z.string(),
614
+ }),
564
615
  },
565
616
  {
566
617
  endpoint: "secrets",
567
618
  method: "GET",
568
619
  handler: async () => handleListSecrets(),
620
+ summary: "List secrets",
621
+ description: "Return the names (not values) of all stored secrets.",
622
+ tags: ["secrets"],
623
+ responseBody: z.object({
624
+ secrets: z
625
+ .array(z.unknown())
626
+ .describe("List of secret metadata entries, each with type and name"),
627
+ accounts: z
628
+ .array(z.unknown())
629
+ .describe("Alias for secrets (same data)"),
630
+ }),
569
631
  },
570
632
  {
571
633
  endpoint: "secrets/read",
572
634
  method: "POST",
573
635
  handler: async ({ req }) => handleReadSecret(req),
636
+ summary: "Read a secret value",
637
+ description: "Retrieve the decrypted value of a stored secret by name.",
638
+ tags: ["secrets"],
639
+ requestBody: z.object({
640
+ type: z.string().describe("Secret type: 'api_key' or 'credential'"),
641
+ name: z.string().describe("Name of the secret to read"),
642
+ reveal: z
643
+ .boolean()
644
+ .describe(
645
+ "If true, return the decrypted value; otherwise return a masked version",
646
+ )
647
+ .optional(),
648
+ }),
649
+ responseBody: z.object({
650
+ found: z.boolean(),
651
+ value: z
652
+ .string()
653
+ .describe("Decrypted value (only when reveal=true and found)"),
654
+ masked: z
655
+ .string()
656
+ .describe("Masked value (when reveal=false and found)"),
657
+ unreachable: z.boolean(),
658
+ }),
574
659
  },
575
660
  ];
576
661
  }