@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
@@ -3,11 +3,18 @@ import { type Command } from "commander";
3
3
  import { loadConfig } from "../../../config/loader.js";
4
4
  import { getOAuthCallbackUrl } from "../../../inbound/public-ingress-urls.js";
5
5
  import {
6
+ deleteApp,
7
+ deleteConnection,
8
+ deleteProvider,
9
+ disconnectOAuthProvider,
6
10
  getProvider,
11
+ listApps,
12
+ listConnections,
7
13
  listProviders,
8
14
  registerProvider,
15
+ updateProvider,
9
16
  } from "../../../oauth/oauth-store.js";
10
- import { getProviderBehavior } from "../../../oauth/provider-behaviors.js";
17
+ import { SEEDED_PROVIDER_KEYS } from "../../../oauth/seed-providers.js";
11
18
  import { getCliLogger } from "../../logger.js";
12
19
  import { shouldOutputJson, writeOutput } from "../../output.js";
13
20
 
@@ -17,21 +24,19 @@ const LOOPBACK_CALLBACK_PATH = "/oauth/callback";
17
24
 
18
25
  /** Resolve the redirect URI for a provider based on its callback transport. */
19
26
  function resolveRedirectUri(
20
- providerKey: string,
21
27
  callbackTransport: string | null,
28
+ loopbackPort: number | null,
22
29
  ): string | null {
23
30
  const transport = callbackTransport ?? "loopback";
24
31
  if (transport === "loopback") {
25
- const behavior = getProviderBehavior(providerKey);
26
- const port = behavior?.loopbackPort;
27
- if (!port) {
32
+ if (!loopbackPort) {
28
33
  // No fixed port — loopback still works at runtime with an OS-assigned
29
34
  // port, but we can't predict the redirect URI ahead of time. Return
30
35
  // a sentinel so callers know the transport is loopback-dynamic rather
31
36
  // than unsupported.
32
37
  return "http://localhost:<dynamic>/oauth/callback";
33
38
  }
34
- return `http://localhost:${port}${LOOPBACK_CALLBACK_PATH}`;
39
+ return `http://localhost:${loopbackPort}${LOOPBACK_CALLBACK_PATH}`;
35
40
  }
36
41
  // Gateway transport — resolve from public ingress config.
37
42
  // Try the explicit publicBaseUrl first, then fall back to platform
@@ -55,11 +60,31 @@ function parseProviderRow(row: ReturnType<typeof getProvider>) {
55
60
  description: row.description ?? null,
56
61
  dashboardUrl: row.dashboardUrl ?? null,
57
62
  clientIdPlaceholder: row.clientIdPlaceholder ?? null,
58
- requiresClientSecret: row.requiresClientSecret ?? 1,
63
+ requiresClientSecret: !!(row.requiresClientSecret ?? 1),
64
+ supportsManagedMode: !!row.managedServiceConfigKey,
59
65
  defaultScopes: row.defaultScopes ? JSON.parse(row.defaultScopes) : [],
60
66
  scopePolicy: row.scopePolicy ? JSON.parse(row.scopePolicy) : {},
61
67
  extraParams: row.extraParams ? JSON.parse(row.extraParams) : null,
62
- redirectUri: resolveRedirectUri(row.providerKey, row.callbackTransport),
68
+ pingHeaders: row.pingHeaders ? JSON.parse(row.pingHeaders) : null,
69
+ pingBody: row.pingBody ? JSON.parse(row.pingBody) : null,
70
+ loopbackPort: row.loopbackPort ?? null,
71
+ injectionTemplates: row.injectionTemplates
72
+ ? JSON.parse(row.injectionTemplates)
73
+ : null,
74
+ appType: row.appType ?? null,
75
+ setupNotes: row.setupNotes ? JSON.parse(row.setupNotes) : null,
76
+ identityUrl: row.identityUrl ?? null,
77
+ identityMethod: row.identityMethod ?? null,
78
+ identityHeaders: row.identityHeaders
79
+ ? JSON.parse(row.identityHeaders)
80
+ : null,
81
+ identityBody: row.identityBody ? JSON.parse(row.identityBody) : null,
82
+ identityResponsePaths: row.identityResponsePaths
83
+ ? JSON.parse(row.identityResponsePaths)
84
+ : null,
85
+ identityFormat: row.identityFormat ?? null,
86
+ identityOkField: row.identityOkField ?? null,
87
+ redirectUri: resolveRedirectUri(row.callbackTransport, row.loopbackPort),
63
88
  createdAt: new Date(row.createdAt).toISOString(),
64
89
  updatedAt: new Date(row.updatedAt).toISOString(),
65
90
  };
@@ -69,7 +94,7 @@ export function registerProviderCommands(oauth: Command): void {
69
94
  const providers = oauth
70
95
  .command("providers")
71
96
  .description(
72
- "Manage OAuth provider configurations (auth URLs, scopes, endpoints)",
97
+ "Fetch configured OAuth providers and register custom providers of your own",
73
98
  );
74
99
 
75
100
  providers.addHelpText(
@@ -81,7 +106,7 @@ authorization URL, token URL, default scopes, and other endpoint details.
81
106
  They are seeded on startup for built-in integrations (e.g. Google, Slack,
82
107
  GitHub) but can also be registered dynamically via the "register" subcommand.
83
108
 
84
- Each provider is identified by a provider key (e.g. "integration:google").`,
109
+ Each provider is identified by a provider key (e.g. "google").`,
85
110
  );
86
111
 
87
112
  // ---------------------------------------------------------------------------
@@ -157,7 +182,7 @@ Examples:
157
182
  "after",
158
183
  `
159
184
  Arguments:
160
- provider-key The full provider key (e.g. "integration:google").
185
+ provider-key Provider key (e.g. "google").
161
186
  Must match the key used during registration or seeding.
162
187
 
163
188
  Returns the full provider configuration including auth URL, token URL,
@@ -165,8 +190,8 @@ default scopes, scope policy, and extra parameters. Exits with code 1
165
190
  if the provider key is not found.
166
191
 
167
192
  Examples:
168
- $ assistant oauth providers get integration:google
169
- $ assistant oauth providers get integration:twitter --json`,
193
+ $ assistant oauth providers get google
194
+ $ assistant oauth providers get twitter --json`,
170
195
  )
171
196
  .action((providerKey: string, _opts: unknown, cmd: Command) => {
172
197
  try {
@@ -175,7 +200,7 @@ Examples:
175
200
  if (!row) {
176
201
  writeOutput(cmd, {
177
202
  ok: false,
178
- error: `Provider not found: ${providerKey}`,
203
+ error: `Provider not found: "${providerKey}". Run 'assistant oauth providers list' to see all registered providers. To register a custom provider, run 'assistant oauth providers register --help'.`,
179
204
  });
180
205
  process.exitCode = 1;
181
206
  return;
@@ -196,69 +221,150 @@ Examples:
196
221
  providers
197
222
  .command("register")
198
223
  .description("Register a new OAuth provider configuration")
199
- .requiredOption("--provider-key <key>", "Provider key")
200
- .requiredOption("--auth-url <url>", "Authorization endpoint URL")
201
- .requiredOption("--token-url <url>", "Token endpoint URL")
202
- .option("--base-url <url>", "API base URL")
203
- .option("--userinfo-url <url>", "Userinfo endpoint URL")
204
- .option("--scopes <scopes>", "Comma-separated default scopes")
205
- .option("--token-auth-method <method>", "Token endpoint auth method")
206
- .option("--callback-transport <transport>", "Callback transport")
224
+ .requiredOption(
225
+ "--provider-key <key>",
226
+ "Unique provider key (e.g. \"custom-service\"). Must not collide with an existing key from 'assistant oauth providers list'.",
227
+ )
228
+ .requiredOption(
229
+ "--auth-url <url>",
230
+ "OAuth authorization endpoint URL (e.g. https://accounts.example.com/o/oauth2/auth)",
231
+ )
232
+ .requiredOption(
233
+ "--token-url <url>",
234
+ "OAuth token endpoint URL (e.g. https://oauth2.example.com/token)",
235
+ )
236
+ .option("--base-url <url>", "API base URL for the service")
237
+ .option("--userinfo-url <url>", "OpenID Connect userinfo endpoint URL")
238
+ .option(
239
+ "--scopes <scopes>",
240
+ 'Comma-separated default scopes (e.g. "read,write,profile")',
241
+ )
242
+ .option(
243
+ "--token-auth-method <method>",
244
+ 'How the client authenticates at the token endpoint: "client_secret_post" or "client_secret_basic"',
245
+ )
246
+ .option(
247
+ "--callback-transport <transport>",
248
+ 'OAuth callback transport: "loopback" (local HTTP server, default) or "gateway" (public ingress)',
249
+ )
207
250
  .option(
208
251
  "--ping-url <url>",
209
- "Health-check endpoint URL for token validation",
252
+ 'Health-check endpoint URL for token validation (e.g. "https://api.example.com/user"). Used by "assistant oauth ping" to verify a stored token.',
253
+ )
254
+ .option(
255
+ "--ping-method <method>",
256
+ "HTTP method for the ping endpoint: GET (default) or POST",
257
+ )
258
+ .option(
259
+ "--ping-headers <json>",
260
+ 'JSON object of extra headers for the ping request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
261
+ )
262
+ .option(
263
+ "--ping-body <json>",
264
+ 'JSON body to send with the ping request (e.g. \'{"query":"{ viewer { id } }"}\')',
265
+ )
266
+ .option(
267
+ "--display-name <name>",
268
+ "Human-readable display name for the provider",
269
+ )
270
+ .option("--description <text>", "Short description of the provider")
271
+ .option(
272
+ "--dashboard-url <url>",
273
+ "URL to the provider's developer console / dashboard",
274
+ )
275
+ .option(
276
+ "--client-id-placeholder <text>",
277
+ "Placeholder text shown in the client ID input field",
210
278
  )
211
- .option("--display-name <name>", "Display name for the provider")
212
- .option("--description <text>", "Short description")
213
- .option("--dashboard-url <url>", "Developer console URL")
214
- .option("--client-id-placeholder <text>", "Placeholder for client ID field")
215
279
  .option(
216
280
  "--no-client-secret",
217
- "Set requires_client_secret to false (default is true)",
281
+ "Mark this provider as not requiring a client secret (default: required)",
282
+ )
283
+ .option(
284
+ "--loopback-port <port>",
285
+ "Fixed port for the local OAuth callback server (e.g. 17322). When set, the redirect URI is http://localhost:<port>/oauth/callback",
286
+ )
287
+ .option(
288
+ "--injection-templates <json>",
289
+ 'JSON array of token injection templates — each with hostPattern, injectionType, headerName, valuePrefix (e.g. \'[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]\')',
290
+ )
291
+ .option(
292
+ "--app-type <type>",
293
+ 'What the provider calls its OAuth apps (e.g. "OAuth App", "Desktop app", "Public integration")',
294
+ )
295
+ .option(
296
+ "--identity-url <url>",
297
+ "Identity verification endpoint URL — called after OAuth to identify the connected account",
298
+ )
299
+ .option(
300
+ "--identity-method <method>",
301
+ "HTTP method for the identity endpoint: GET (default) or POST",
302
+ )
303
+ .option(
304
+ "--identity-headers <json>",
305
+ 'JSON object of extra headers for the identity request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
306
+ )
307
+ .option(
308
+ "--identity-body <body>",
309
+ 'JSON body to send with the identity request (e.g. \'{"query":"{ viewer { email } }"}\')',
310
+ )
311
+ .option(
312
+ "--identity-response-paths <paths>",
313
+ 'Comma-separated dot-notation paths to extract identity from the response (e.g. "email,name,person.email")',
314
+ )
315
+ .option(
316
+ "--identity-format <template>",
317
+ 'Format template for the extracted identity using ${pathName} tokens from --identity-response-paths (e.g. "@${login}" or "@${user} (${team})")',
318
+ )
319
+ .option(
320
+ "--identity-ok-field <field>",
321
+ 'Dot-notation path to a boolean field that must be truthy for the response to be considered valid (e.g. "ok")',
322
+ )
323
+ .option(
324
+ "--setup-notes <json>",
325
+ 'JSON array of setup instruction notes shown during guided setup (e.g. \'["Enable the Gmail API","Add test users"]\')',
218
326
  )
219
327
  .addHelpText(
220
328
  "after",
221
329
  `
222
- Arguments (via options):
223
- --provider-key Unique identifier for this provider (e.g. "integration:custom-service").
224
- Must not collide with an existing provider key.
225
- --auth-url The OAuth authorization endpoint URL.
226
- --token-url The OAuth token endpoint URL.
227
- --base-url Optional API base URL for the service.
228
- --userinfo-url Optional OpenID Connect userinfo endpoint.
229
- --scopes Comma-separated list of default scopes (e.g. "read,write,profile").
230
- --token-auth-method How the client authenticates at the token endpoint
231
- (e.g. "client_secret_post", "client_secret_basic").
232
- --callback-transport Transport method for the OAuth callback.
233
- --ping-url Optional URL for a lightweight health-check endpoint.
234
- Used by "connections ping" to validate that a stored token
235
- is still functional (e.g. "https://api.example.com/user").
236
- --display-name Optional human-readable display name for the provider.
237
- --description Optional short description of the provider.
238
- --dashboard-url Optional URL to the provider's developer console / dashboard.
239
- --client-id-placeholder Optional placeholder text for the client ID input field.
240
- --no-client-secret If set, marks the provider as not requiring a client secret
241
- (sets requires_client_secret to 0). Default is true (1).
242
-
243
- Registers a new OAuth provider configuration in the local store. This is
244
- used for custom integrations not covered by the built-in provider seeds.
330
+ Registers a new OAuth provider configuration in the local store for custom
331
+ integrations not covered by the built-in provider seeds. The provider key
332
+ must be unique — if it collides with an existing key, the command fails.
333
+ Run 'assistant oauth providers list' to see existing keys.
334
+
245
335
  On success, returns the full provider row including generated timestamps.
336
+ After registering, create an OAuth app with 'assistant oauth apps create'
337
+ and then connect with 'assistant oauth connect <provider-key>'.
338
+
339
+ Token injection templates control how the OAuth access token is injected
340
+ into outgoing HTTP requests matched by host pattern. Identity config
341
+ defines how the assistant verifies the connected account after OAuth.
246
342
 
247
343
  Examples:
248
344
  $ assistant oauth providers register \\
249
- --provider-key integration:custom-api \\
345
+ --provider-key custom-api \\
250
346
  --auth-url https://custom-api.example.com/oauth/authorize \\
251
347
  --token-url https://custom-api.example.com/oauth/token
252
348
  $ assistant oauth providers register \\
253
- --provider-key integration:my-service \\
349
+ --provider-key my-service \\
254
350
  --auth-url https://my-service.com/auth \\
255
351
  --token-url https://my-service.com/token \\
256
352
  --scopes read,write --json
257
353
  $ assistant oauth providers register \\
258
- --provider-key integration:custom-api \\
354
+ --provider-key my-graphql-api \\
259
355
  --auth-url https://example.com/auth \\
260
356
  --token-url https://example.com/token \\
261
- --ping-url https://example.com/user`,
357
+ --ping-url https://example.com/graphql \\
358
+ --ping-method POST \\
359
+ --ping-body '{"query":"{ viewer { id } }"}'
360
+ $ assistant oauth providers register \\
361
+ --provider-key my-api \\
362
+ --auth-url https://example.com/auth \\
363
+ --token-url https://example.com/token \\
364
+ --loopback-port 17400 \\
365
+ --injection-templates '[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]' \\
366
+ --identity-url https://api.example.com/me \\
367
+ --identity-response-paths email,name`,
262
368
  )
263
369
  .action(
264
370
  (
@@ -272,11 +378,25 @@ Examples:
272
378
  tokenAuthMethod?: string;
273
379
  callbackTransport?: string;
274
380
  pingUrl?: string;
381
+ pingMethod?: string;
382
+ pingHeaders?: string;
383
+ pingBody?: string;
275
384
  displayName?: string;
276
385
  description?: string;
277
386
  dashboardUrl?: string;
278
387
  clientIdPlaceholder?: string;
279
388
  clientSecret: boolean;
389
+ loopbackPort?: string;
390
+ injectionTemplates?: string;
391
+ appType?: string;
392
+ identityUrl?: string;
393
+ identityMethod?: string;
394
+ identityHeaders?: string;
395
+ identityBody?: string;
396
+ identityResponsePaths?: string;
397
+ identityFormat?: string;
398
+ identityOkField?: string;
399
+ setupNotes?: string;
280
400
  },
281
401
  cmd: Command,
282
402
  ) => {
@@ -292,14 +412,428 @@ Examples:
292
412
  tokenEndpointAuthMethod: opts.tokenAuthMethod,
293
413
  callbackTransport: opts.callbackTransport,
294
414
  pingUrl: opts.pingUrl,
415
+ pingMethod: opts.pingMethod,
416
+ pingHeaders: opts.pingHeaders
417
+ ? JSON.parse(opts.pingHeaders)
418
+ : undefined,
419
+ pingBody: opts.pingBody ? JSON.parse(opts.pingBody) : undefined,
295
420
  displayName: opts.displayName,
296
421
  description: opts.description,
297
422
  dashboardUrl: opts.dashboardUrl,
298
423
  clientIdPlaceholder: opts.clientIdPlaceholder,
299
424
  requiresClientSecret: opts.clientSecret ? 1 : 0,
425
+ loopbackPort: opts.loopbackPort
426
+ ? parseInt(opts.loopbackPort, 10)
427
+ : undefined,
428
+ injectionTemplates: opts.injectionTemplates
429
+ ? JSON.parse(opts.injectionTemplates)
430
+ : undefined,
431
+ appType: opts.appType,
432
+ identityUrl: opts.identityUrl,
433
+ identityMethod: opts.identityMethod,
434
+ identityHeaders: opts.identityHeaders
435
+ ? JSON.parse(opts.identityHeaders)
436
+ : undefined,
437
+ identityBody: opts.identityBody
438
+ ? JSON.parse(opts.identityBody)
439
+ : undefined,
440
+ identityResponsePaths: opts.identityResponsePaths
441
+ ? opts.identityResponsePaths.split(",")
442
+ : undefined,
443
+ identityFormat: opts.identityFormat,
444
+ identityOkField: opts.identityOkField,
445
+ setupNotes: opts.setupNotes
446
+ ? JSON.parse(opts.setupNotes)
447
+ : undefined,
300
448
  });
301
449
 
302
450
  writeOutput(cmd, parseProviderRow(row));
451
+ } catch (err) {
452
+ let message = err instanceof Error ? err.message : String(err);
453
+ if (message.includes("already exists")) {
454
+ message += ` Run 'assistant oauth providers list' to see existing providers, or choose a different --provider-key.`;
455
+ }
456
+ writeOutput(cmd, { ok: false, error: message });
457
+ process.exitCode = 1;
458
+ }
459
+ },
460
+ );
461
+
462
+ // ---------------------------------------------------------------------------
463
+ // providers update <provider-key>
464
+ // ---------------------------------------------------------------------------
465
+
466
+ providers
467
+ .command("update <provider-key>")
468
+ .description("Update an existing custom OAuth provider configuration")
469
+ .option(
470
+ "--auth-url <url>",
471
+ "OAuth authorization endpoint URL (e.g. https://accounts.example.com/o/oauth2/auth)",
472
+ )
473
+ .option(
474
+ "--token-url <url>",
475
+ "OAuth token endpoint URL (e.g. https://oauth2.example.com/token)",
476
+ )
477
+ .option("--base-url <url>", "API base URL for the service")
478
+ .option("--userinfo-url <url>", "OpenID Connect userinfo endpoint URL")
479
+ .option(
480
+ "--scopes <scopes>",
481
+ 'Comma-separated default scopes (e.g. "read,write,profile")',
482
+ )
483
+ .option(
484
+ "--token-auth-method <method>",
485
+ 'How the client authenticates at the token endpoint: "client_secret_post" or "client_secret_basic"',
486
+ )
487
+ .option(
488
+ "--callback-transport <transport>",
489
+ 'OAuth callback transport: "loopback" (local HTTP server, default) or "gateway" (public ingress)',
490
+ )
491
+ .option(
492
+ "--ping-url <url>",
493
+ 'Health-check endpoint URL for token validation (e.g. "https://api.example.com/user"). Used by "assistant oauth ping" to verify a stored token.',
494
+ )
495
+ .option(
496
+ "--ping-method <method>",
497
+ "HTTP method for the ping endpoint: GET (default) or POST",
498
+ )
499
+ .option(
500
+ "--ping-headers <json>",
501
+ 'JSON object of extra headers for the ping request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
502
+ )
503
+ .option(
504
+ "--ping-body <json>",
505
+ 'JSON body to send with the ping request (e.g. \'{"query":"{ viewer { id } }"}\')',
506
+ )
507
+ .option(
508
+ "--display-name <name>",
509
+ "Human-readable display name for the provider",
510
+ )
511
+ .option("--description <text>", "Short description of the provider")
512
+ .option(
513
+ "--dashboard-url <url>",
514
+ "URL to the provider's developer console / dashboard",
515
+ )
516
+ .option(
517
+ "--client-id-placeholder <text>",
518
+ "Placeholder text shown in the client ID input field",
519
+ )
520
+ .option(
521
+ "--no-client-secret",
522
+ "Mark this provider as not requiring a client secret (default: required)",
523
+ )
524
+ .option(
525
+ "--loopback-port <port>",
526
+ "Fixed port for the local OAuth callback server (e.g. 17322). When set, the redirect URI is http://localhost:<port>/oauth/callback",
527
+ )
528
+ .option(
529
+ "--injection-templates <json>",
530
+ 'JSON array of token injection templates — each with hostPattern, injectionType, headerName, valuePrefix (e.g. \'[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]\')',
531
+ )
532
+ .option(
533
+ "--app-type <type>",
534
+ 'What the provider calls its OAuth apps (e.g. "OAuth App", "Desktop app", "Public integration")',
535
+ )
536
+ .option(
537
+ "--identity-url <url>",
538
+ "Identity verification endpoint URL — called after OAuth to identify the connected account",
539
+ )
540
+ .option(
541
+ "--identity-method <method>",
542
+ "HTTP method for the identity endpoint: GET (default) or POST",
543
+ )
544
+ .option(
545
+ "--identity-headers <json>",
546
+ 'JSON object of extra headers for the identity request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
547
+ )
548
+ .option(
549
+ "--identity-body <body>",
550
+ 'JSON body to send with the identity request (e.g. \'{"query":"{ viewer { email } }"}\')',
551
+ )
552
+ .option(
553
+ "--identity-response-paths <paths>",
554
+ 'Comma-separated dot-notation paths to extract identity from the response (e.g. "email,name,person.email")',
555
+ )
556
+ .option(
557
+ "--identity-format <template>",
558
+ 'Format template for the extracted identity using ${pathName} tokens from --identity-response-paths (e.g. "@${login}" or "@${user} (${team})")',
559
+ )
560
+ .option(
561
+ "--identity-ok-field <field>",
562
+ 'Dot-notation path to a boolean field that must be truthy for the response to be considered valid (e.g. "ok")',
563
+ )
564
+ .option(
565
+ "--setup-notes <json>",
566
+ 'JSON array of setup instruction notes shown during guided setup (e.g. \'["Enable the Gmail API","Add test users"]\')',
567
+ )
568
+ .addHelpText(
569
+ "after",
570
+ `
571
+ Arguments:
572
+ provider-key Provider key to update (e.g. "custom-api").
573
+ Run 'assistant oauth providers list' to see all registered providers.
574
+
575
+ Only the fields you specify are updated — all other fields remain unchanged.
576
+ Built-in providers (e.g. "google", "slack") cannot be updated; they are
577
+ managed by the system and reset on startup. To create a custom provider with
578
+ different settings, use 'assistant oauth providers register'.
579
+
580
+ Token injection templates control how the OAuth access token is injected
581
+ into outgoing HTTP requests matched by host pattern. Identity config
582
+ defines how the assistant verifies the connected account after OAuth.
583
+
584
+ Examples:
585
+ $ assistant oauth providers update custom-api --display-name "My Custom API"
586
+ $ assistant oauth providers update custom-api --scopes read,write --auth-url https://new.example.com/auth
587
+ $ assistant oauth providers update custom-api --ping-url https://api.example.com/me --json
588
+ $ assistant oauth providers update custom-api \\
589
+ --identity-url https://api.example.com/me \\
590
+ --identity-response-paths email,name
591
+ $ assistant oauth providers update custom-api \\
592
+ --injection-templates '[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]'`,
593
+ )
594
+ .action(
595
+ (
596
+ providerKey: string,
597
+ opts: {
598
+ authUrl?: string;
599
+ tokenUrl?: string;
600
+ baseUrl?: string;
601
+ userinfoUrl?: string;
602
+ scopes?: string;
603
+ tokenAuthMethod?: string;
604
+ callbackTransport?: string;
605
+ pingUrl?: string;
606
+ pingMethod?: string;
607
+ pingHeaders?: string;
608
+ pingBody?: string;
609
+ displayName?: string;
610
+ description?: string;
611
+ dashboardUrl?: string;
612
+ clientIdPlaceholder?: string;
613
+ clientSecret: boolean;
614
+ loopbackPort?: string;
615
+ injectionTemplates?: string;
616
+ appType?: string;
617
+ identityUrl?: string;
618
+ identityMethod?: string;
619
+ identityHeaders?: string;
620
+ identityBody?: string;
621
+ identityResponsePaths?: string;
622
+ identityFormat?: string;
623
+ identityOkField?: string;
624
+ setupNotes?: string;
625
+ },
626
+ cmd: Command,
627
+ ) => {
628
+ try {
629
+ // Verify provider exists
630
+ const existing = getProvider(providerKey);
631
+ if (!existing) {
632
+ writeOutput(cmd, {
633
+ ok: false,
634
+ error: `Provider "${providerKey}" not found. Run 'assistant oauth providers list' to see all registered providers.`,
635
+ });
636
+ process.exitCode = 1;
637
+ return;
638
+ }
639
+
640
+ // Block updates to built-in providers
641
+ if (SEEDED_PROVIDER_KEYS.has(providerKey)) {
642
+ writeOutput(cmd, {
643
+ ok: false,
644
+ error: `Cannot update built-in provider "${providerKey}". Built-in providers are managed by the system and reset on startup. To create a custom provider with different settings, use 'assistant oauth providers register --provider-key <your-custom-key> ...'`,
645
+ });
646
+ process.exitCode = 1;
647
+ return;
648
+ }
649
+
650
+ // Build params object from provided options, omitting undefined values
651
+ const params: Record<string, unknown> = {};
652
+
653
+ if (opts.authUrl !== undefined) params.authUrl = opts.authUrl;
654
+ if (opts.tokenUrl !== undefined) params.tokenUrl = opts.tokenUrl;
655
+ if (opts.baseUrl !== undefined) params.baseUrl = opts.baseUrl;
656
+ if (opts.userinfoUrl !== undefined)
657
+ params.userinfoUrl = opts.userinfoUrl;
658
+ if (opts.scopes !== undefined)
659
+ params.defaultScopes = opts.scopes.split(",");
660
+ if (opts.tokenAuthMethod !== undefined)
661
+ params.tokenEndpointAuthMethod = opts.tokenAuthMethod;
662
+ if (opts.callbackTransport !== undefined)
663
+ params.callbackTransport = opts.callbackTransport;
664
+ if (opts.pingUrl !== undefined) params.pingUrl = opts.pingUrl;
665
+ if (opts.pingMethod !== undefined)
666
+ params.pingMethod = opts.pingMethod;
667
+ if (opts.pingHeaders !== undefined)
668
+ params.pingHeaders = JSON.parse(opts.pingHeaders);
669
+ if (opts.pingBody !== undefined)
670
+ params.pingBody = JSON.parse(opts.pingBody);
671
+ if (opts.displayName !== undefined)
672
+ params.displayName = opts.displayName;
673
+ if (opts.description !== undefined)
674
+ params.description = opts.description;
675
+ if (opts.dashboardUrl !== undefined)
676
+ params.dashboardUrl = opts.dashboardUrl;
677
+ if (opts.clientIdPlaceholder !== undefined)
678
+ params.clientIdPlaceholder = opts.clientIdPlaceholder;
679
+
680
+ // Handle the negated --no-client-* flag: Commander defaults
681
+ // opts.clientSecret to true; the negated form sets it to false.
682
+ // Use getOptionValueSource to detect explicit user intent.
683
+ if (cmd.getOptionValueSource("clientSecret") === "cli") {
684
+ params.requiresClientSecret = opts.clientSecret ? 1 : 0;
685
+ }
686
+
687
+ if (opts.loopbackPort !== undefined)
688
+ params.loopbackPort = parseInt(opts.loopbackPort, 10);
689
+ if (opts.injectionTemplates !== undefined)
690
+ params.injectionTemplates = JSON.parse(opts.injectionTemplates);
691
+ if (opts.appType !== undefined) params.appType = opts.appType;
692
+ if (opts.identityUrl !== undefined)
693
+ params.identityUrl = opts.identityUrl;
694
+ if (opts.identityMethod !== undefined)
695
+ params.identityMethod = opts.identityMethod;
696
+ if (opts.identityHeaders !== undefined)
697
+ params.identityHeaders = JSON.parse(opts.identityHeaders);
698
+ if (opts.identityBody !== undefined)
699
+ params.identityBody = JSON.parse(opts.identityBody);
700
+ if (opts.identityResponsePaths !== undefined)
701
+ params.identityResponsePaths =
702
+ opts.identityResponsePaths.split(",");
703
+ if (opts.identityFormat !== undefined)
704
+ params.identityFormat = opts.identityFormat;
705
+ if (opts.identityOkField !== undefined)
706
+ params.identityOkField = opts.identityOkField;
707
+ if (opts.setupNotes !== undefined)
708
+ params.setupNotes = JSON.parse(opts.setupNotes);
709
+
710
+ // Check if any fields were actually provided
711
+ if (Object.keys(params).length === 0) {
712
+ writeOutput(cmd, {
713
+ ok: false,
714
+ error:
715
+ "Nothing to update. Provide at least one option to change (e.g. --auth-url, --scopes, --display-name). Run 'assistant oauth providers update --help' for all options.",
716
+ });
717
+ process.exitCode = 1;
718
+ return;
719
+ }
720
+
721
+ const row = updateProvider(providerKey, params);
722
+
723
+ writeOutput(cmd, parseProviderRow(row));
724
+ } catch (err) {
725
+ const message = err instanceof Error ? err.message : String(err);
726
+ writeOutput(cmd, { ok: false, error: message });
727
+ process.exitCode = 1;
728
+ }
729
+ },
730
+ );
731
+
732
+ // ---------------------------------------------------------------------------
733
+ // providers delete <provider-key>
734
+ // ---------------------------------------------------------------------------
735
+
736
+ providers
737
+ .command("delete <provider-key>")
738
+ .description(
739
+ "Delete a custom OAuth provider and optionally its associated apps and connections",
740
+ )
741
+ .option(
742
+ "--force",
743
+ "Cascade-delete all associated apps and connections before removing the provider",
744
+ )
745
+ .addHelpText(
746
+ "after",
747
+ `
748
+ Arguments:
749
+ provider-key Provider key to delete (e.g. "custom-api").
750
+ Run 'assistant oauth providers list' to see registered providers.
751
+
752
+ When --force is specified, all OAuth connections and apps that depend on
753
+ this provider are deleted before the provider itself is removed. Without
754
+ --force, the command refuses to delete a provider that has dependents and
755
+ exits with an error listing the counts.
756
+
757
+ Built-in providers (e.g. "google", "slack") can be deleted, but a warning
758
+ is emitted because they will be re-created automatically on the next
759
+ assistant startup.
760
+
761
+ Examples:
762
+ $ assistant oauth providers delete custom-api
763
+ $ assistant oauth providers delete custom-api --force
764
+ $ assistant oauth providers delete custom-api --force --json`,
765
+ )
766
+ .action(
767
+ async (providerKey: string, opts: { force?: boolean }, cmd: Command) => {
768
+ try {
769
+ const provider = getProvider(providerKey);
770
+ if (!provider) {
771
+ writeOutput(cmd, {
772
+ ok: false,
773
+ error: `Provider not found: "${providerKey}". Run 'assistant oauth providers list' to see all registered providers.`,
774
+ });
775
+ process.exitCode = 1;
776
+ return;
777
+ }
778
+
779
+ if (SEEDED_PROVIDER_KEYS.has(providerKey) && !opts.force) {
780
+ log.info(
781
+ `Note: "${providerKey}" is a built-in provider and will be re-created on next startup.`,
782
+ );
783
+ }
784
+
785
+ const dependentApps = listApps().filter(
786
+ (a) => a.providerKey === providerKey,
787
+ );
788
+ const dependentConnections = listConnections(providerKey);
789
+ const appCount = dependentApps.length;
790
+ const connCount = dependentConnections.length;
791
+
792
+ if ((appCount > 0 || connCount > 0) && !opts.force) {
793
+ writeOutput(cmd, {
794
+ ok: false,
795
+ error: `Cannot delete provider "${providerKey}": ${appCount} app(s) and ${connCount} connection(s) depend on it. Use --force to cascade-delete all dependent apps and connections, or remove them manually first with 'assistant oauth apps delete' and 'assistant oauth disconnect'.`,
796
+ });
797
+ process.exitCode = 1;
798
+ return;
799
+ }
800
+
801
+ // Warn about built-in providers when --force is used
802
+ if (SEEDED_PROVIDER_KEYS.has(providerKey) && opts.force) {
803
+ log.info(
804
+ `Note: "${providerKey}" is a built-in provider and will be re-created on next startup.`,
805
+ );
806
+ }
807
+
808
+ // Cascade-delete connections first, then apps, then the provider.
809
+ // Use disconnectOAuthProvider to clean up OAuth tokens from secure
810
+ // storage in addition to deleting the connection DB row.
811
+ for (const conn of dependentConnections) {
812
+ const result = await disconnectOAuthProvider(
813
+ providerKey,
814
+ undefined,
815
+ conn.id as string,
816
+ );
817
+ if (result === "error") {
818
+ log.info(
819
+ `Warning: failed to clean up tokens for connection ${conn.id} — deleting connection row to continue cascade.`,
820
+ );
821
+ deleteConnection(conn.id);
822
+ }
823
+ }
824
+ for (const app of dependentApps) {
825
+ await deleteApp(app.id);
826
+ }
827
+ deleteProvider(providerKey);
828
+
829
+ if (!shouldOutputJson(cmd)) {
830
+ log.info(`Deleted provider: ${providerKey}`);
831
+ }
832
+
833
+ writeOutput(cmd, {
834
+ ok: true,
835
+ deleted: { provider: 1, apps: appCount, connections: connCount },
836
+ });
303
837
  } catch (err) {
304
838
  const message = err instanceof Error ? err.message : String(err);
305
839
  writeOutput(cmd, { ok: false, error: message });