@vellumai/assistant 0.6.2 → 0.6.3

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 (396) hide show
  1. package/bun.lock +40 -40
  2. package/bunfig.toml +3 -0
  3. package/docs/architecture/memory.md +1 -1
  4. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
  5. package/openapi.yaml +184 -69
  6. package/package.json +41 -41
  7. package/scripts/generate-openapi.ts +1 -2
  8. package/src/__tests__/acp-session.test.ts +43 -0
  9. package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
  10. package/src/__tests__/app-executors.test.ts +1 -0
  11. package/src/__tests__/app-source-watcher.test.ts +37 -11
  12. package/src/__tests__/approval-routes-http.test.ts +178 -1
  13. package/src/__tests__/browser-fill-credential.test.ts +229 -94
  14. package/src/__tests__/browser-manager.test.ts +40 -27
  15. package/src/__tests__/catalog-files.test.ts +862 -0
  16. package/src/__tests__/channel-approvals.test.ts +53 -0
  17. package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
  18. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  19. package/src/__tests__/config-schema.test.ts +125 -48
  20. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
  21. package/src/__tests__/context-overflow-approval.test.ts +16 -1
  22. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
  23. package/src/__tests__/conversation-agent-loop.test.ts +1 -1
  24. package/src/__tests__/conversation-analysis-routes.test.ts +2 -2
  25. package/src/__tests__/conversation-attachments.test.ts +80 -4
  26. package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
  27. package/src/__tests__/conversation-fork-crud.test.ts +17 -0
  28. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  29. package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
  30. package/src/__tests__/conversation-inject-context.test.ts +103 -0
  31. package/src/__tests__/conversation-queue.test.ts +45 -2
  32. package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
  33. package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
  34. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  35. package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
  36. package/src/__tests__/conversation-starter-routes.test.ts +126 -0
  37. package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
  38. package/src/__tests__/conversation-store.test.ts +195 -0
  39. package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
  40. package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -1
  41. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  42. package/src/__tests__/credential-vault-unit.test.ts +4 -4
  43. package/src/__tests__/credential-vault.test.ts +152 -13
  44. package/src/__tests__/credentials-cli.test.ts +2 -2
  45. package/src/__tests__/date-context.test.ts +4 -4
  46. package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
  47. package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
  48. package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
  49. package/src/__tests__/gateway-only-guard.test.ts +3 -0
  50. package/src/__tests__/gemini-provider.test.ts +2 -2
  51. package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
  52. package/src/__tests__/headless-browser-interactions.test.ts +707 -371
  53. package/src/__tests__/headless-browser-navigate.test.ts +389 -47
  54. package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
  55. package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
  56. package/src/__tests__/host-bash-proxy.test.ts +150 -1
  57. package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
  58. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
  59. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
  60. package/src/__tests__/host-browser-event-routes.test.ts +350 -0
  61. package/src/__tests__/host-browser-proxy.test.ts +444 -0
  62. package/src/__tests__/host-browser-routes.test.ts +198 -0
  63. package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
  64. package/src/__tests__/host-cu-proxy.test.ts +171 -1
  65. package/src/__tests__/host-file-proxy.test.ts +185 -1
  66. package/src/__tests__/host-file-read-tool.test.ts +52 -0
  67. package/src/__tests__/host-proxy-interface.test.ts +165 -0
  68. package/src/__tests__/host-shell-tool.test.ts +1 -11
  69. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  70. package/src/__tests__/integration-status.test.ts +6 -7
  71. package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
  72. package/src/__tests__/mcp-client-auth.test.ts +40 -4
  73. package/src/__tests__/mcp-health-check.test.ts +10 -3
  74. package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
  75. package/src/__tests__/migration-export-http.test.ts +61 -2
  76. package/src/__tests__/migration-export-streaming.test.ts +66 -0
  77. package/src/__tests__/migration-import-commit-http.test.ts +101 -1
  78. package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
  79. package/src/__tests__/oauth-apps-routes.test.ts +17 -12
  80. package/src/__tests__/oauth-cli.test.ts +707 -60
  81. package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
  82. package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
  83. package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
  84. package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
  85. package/src/__tests__/oauth-providers-routes.test.ts +50 -14
  86. package/src/__tests__/oauth-store.test.ts +1386 -182
  87. package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
  88. package/src/__tests__/onboarding-template-contract.test.ts +75 -57
  89. package/src/__tests__/openai-provider.test.ts +2 -2
  90. package/src/__tests__/outlook-categories.test.ts +1 -1
  91. package/src/__tests__/outlook-client-automation.test.ts +1 -1
  92. package/src/__tests__/outlook-compose-tools.test.ts +1 -1
  93. package/src/__tests__/outlook-email-watcher.test.ts +1 -1
  94. package/src/__tests__/outlook-follow-up.test.ts +1 -1
  95. package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
  96. package/src/__tests__/outlook-trash.test.ts +1 -1
  97. package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
  98. package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
  99. package/src/__tests__/permission-mode.test.ts +28 -56
  100. package/src/__tests__/platform-callback-registration.test.ts +19 -0
  101. package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
  102. package/src/__tests__/proxy-approval-callback.test.ts +18 -0
  103. package/src/__tests__/require-fresh-approval.test.ts +40 -1
  104. package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
  105. package/src/__tests__/schedule-routes.test.ts +162 -0
  106. package/src/__tests__/secret-detection-handler.test.ts +84 -0
  107. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  108. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  109. package/src/__tests__/set-permission-mode.test.ts +13 -250
  110. package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
  111. package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
  112. package/src/__tests__/slack-channel-config.test.ts +12 -15
  113. package/src/__tests__/subagent-detail.test.ts +44 -2
  114. package/src/__tests__/subagent-disposal.test.ts +1 -0
  115. package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
  116. package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
  117. package/src/__tests__/subagent-manager-notify.test.ts +1 -0
  118. package/src/__tests__/subagent-notify-parent.test.ts +1 -0
  119. package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
  120. package/src/__tests__/subagent-tools.test.ts +1 -0
  121. package/src/__tests__/subagent-types.test.ts +1 -0
  122. package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
  123. package/src/__tests__/system-prompt.test.ts +72 -1
  124. package/src/__tests__/task-scheduler.test.ts +32 -6
  125. package/src/__tests__/telegram-config.test.ts +10 -13
  126. package/src/__tests__/terminal-tools.test.ts +9 -0
  127. package/src/__tests__/tool-approval-handler.test.ts +73 -0
  128. package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
  129. package/src/__tests__/top-level-renderer.test.ts +73 -1
  130. package/src/__tests__/transport-hints-queue.test.ts +14 -29
  131. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
  132. package/src/__tests__/v2-consent-policy.test.ts +103 -0
  133. package/src/acp/client-handler.ts +30 -4
  134. package/src/agent/loop.ts +12 -6
  135. package/src/approvals/guardian-request-resolvers.ts +21 -15
  136. package/src/browser-session/__tests__/manager.test.ts +297 -0
  137. package/src/browser-session/backends/cdp-inspect.ts +30 -0
  138. package/src/browser-session/backends/extension.ts +26 -0
  139. package/src/browser-session/backends/local.ts +24 -0
  140. package/src/browser-session/events.ts +164 -0
  141. package/src/browser-session/index.ts +27 -0
  142. package/src/browser-session/manager.ts +159 -0
  143. package/src/browser-session/types.ts +28 -0
  144. package/src/channels/__tests__/types.test.ts +134 -0
  145. package/src/channels/types.ts +53 -3
  146. package/src/cli/commands/browser-relay.ts +339 -409
  147. package/src/cli/commands/credentials.ts +3 -3
  148. package/src/cli/commands/email.ts +18 -13
  149. package/src/cli/commands/mcp.ts +16 -4
  150. package/src/cli/commands/oauth/__tests__/connect.test.ts +44 -44
  151. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
  152. package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
  153. package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
  154. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
  155. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
  156. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
  157. package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
  158. package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
  159. package/src/cli/commands/oauth/apps.ts +7 -4
  160. package/src/cli/commands/oauth/connect.ts +6 -3
  161. package/src/cli/commands/oauth/disconnect.ts +1 -1
  162. package/src/cli/commands/oauth/providers.ts +200 -36
  163. package/src/cli/commands/oauth/shared.ts +5 -5
  164. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
  165. package/src/cli/commands/platform/index.ts +107 -10
  166. package/src/cli/commands/usage.ts +10 -9
  167. package/src/cli/lib/daemon-credential-client.ts +4 -0
  168. package/src/cli/program.ts +1 -1
  169. package/src/config/bundled-skills/app-builder/SKILL.md +26 -249
  170. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
  171. package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
  172. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
  173. package/src/config/bundled-skills/contacts/SKILL.md +3 -0
  174. package/src/config/bundled-skills/document/SKILL.md +4 -0
  175. package/src/config/bundled-skills/gmail/SKILL.md +1 -1
  176. package/src/config/bundled-skills/outlook/SKILL.md +7 -0
  177. package/src/config/bundled-skills/subagent/SKILL.md +21 -0
  178. package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
  179. package/src/config/bundled-skills/tasks/SKILL.md +5 -0
  180. package/src/config/env-registry.ts +14 -0
  181. package/src/config/env.ts +21 -0
  182. package/src/config/feature-flag-registry.json +44 -5
  183. package/src/config/loader.ts +56 -1
  184. package/src/config/sanitize-for-transfer.ts +47 -0
  185. package/src/config/schema.ts +46 -5
  186. package/src/config/schemas/host-browser.ts +66 -0
  187. package/src/config/schemas/memory-lifecycle.ts +1 -1
  188. package/src/config/schemas/memory-retrieval.ts +103 -0
  189. package/src/config/schemas/security.ts +0 -6
  190. package/src/config/schemas/services.ts +8 -0
  191. package/src/config/types.ts +0 -1
  192. package/src/context/post-turn-tool-result-truncation.ts +176 -0
  193. package/src/context/window-manager.ts +19 -1
  194. package/src/credential-execution/approval-bridge.ts +49 -15
  195. package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
  196. package/src/daemon/app-source-watcher.ts +35 -0
  197. package/src/daemon/context-overflow-approval.ts +5 -0
  198. package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
  199. package/src/daemon/conversation-agent-loop.ts +58 -24
  200. package/src/daemon/conversation-attachments.ts +40 -0
  201. package/src/daemon/conversation-process.ts +48 -1
  202. package/src/daemon/conversation-runtime-assembly.ts +118 -36
  203. package/src/daemon/conversation-surfaces.ts +37 -36
  204. package/src/daemon/conversation-tool-setup.ts +74 -8
  205. package/src/daemon/conversation-workspace.ts +12 -0
  206. package/src/daemon/conversation.ts +226 -8
  207. package/src/daemon/date-context.ts +10 -10
  208. package/src/daemon/first-greeting.ts +3 -2
  209. package/src/daemon/handlers/conversations.ts +9 -140
  210. package/src/daemon/handlers/shared.ts +58 -0
  211. package/src/daemon/handlers/skills.ts +232 -37
  212. package/src/daemon/host-bash-proxy.ts +48 -13
  213. package/src/daemon/host-browser-proxy.ts +191 -0
  214. package/src/daemon/host-cu-proxy.ts +36 -11
  215. package/src/daemon/host-file-proxy.ts +57 -9
  216. package/src/daemon/lifecycle.ts +65 -11
  217. package/src/daemon/message-protocol.ts +7 -0
  218. package/src/daemon/message-types/conversations.ts +55 -13
  219. package/src/daemon/message-types/host-browser.ts +100 -0
  220. package/src/daemon/message-types/messages.ts +5 -5
  221. package/src/daemon/message-types/skills.ts +10 -0
  222. package/src/daemon/message-types/subagents.ts +2 -0
  223. package/src/daemon/server.ts +92 -12
  224. package/src/daemon/tool-side-effects.ts +6 -0
  225. package/src/daemon/transport-hints.ts +5 -24
  226. package/src/inbound/platform-callback-registration.ts +18 -17
  227. package/src/mcp/client.ts +59 -24
  228. package/src/memory/app-store.ts +31 -1
  229. package/src/memory/conversation-crud.ts +23 -0
  230. package/src/memory/conversation-starters-cadence.ts +76 -0
  231. package/src/memory/conversation-title-service.ts +5 -2
  232. package/src/memory/db-init.ts +12 -0
  233. package/src/memory/embedding-backend.test.ts +75 -0
  234. package/src/memory/embedding-backend.ts +131 -5
  235. package/src/memory/embedding-gemini.test.ts +54 -0
  236. package/src/memory/embedding-gemini.ts +20 -9
  237. package/src/memory/embedding-local.ts +176 -17
  238. package/src/memory/graph/consolidation.ts +10 -23
  239. package/src/memory/graph/extraction-job.ts +15 -0
  240. package/src/memory/graph/retriever.ts +40 -22
  241. package/src/memory/graph/store.test.ts +7 -3
  242. package/src/memory/graph/store.ts +47 -12
  243. package/src/memory/llm-usage-store.ts +45 -4
  244. package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
  245. package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
  246. package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
  247. package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
  248. package/src/memory/migrations/217-conversation-host-access.ts +40 -0
  249. package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
  250. package/src/memory/migrations/index.ts +6 -0
  251. package/src/memory/migrations/registry.ts +8 -0
  252. package/src/memory/schema/conversations.ts +1 -0
  253. package/src/memory/schema/oauth.ts +18 -13
  254. package/src/oauth/AGENTS.md +76 -0
  255. package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
  256. package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
  257. package/src/oauth/byo-connection.test.ts +8 -8
  258. package/src/oauth/byo-connection.ts +7 -7
  259. package/src/oauth/connect-orchestrator.ts +23 -21
  260. package/src/oauth/connect-types.ts +3 -3
  261. package/src/oauth/connection-resolver.test.ts +17 -4
  262. package/src/oauth/connection-resolver.ts +16 -16
  263. package/src/oauth/connection.ts +1 -1
  264. package/src/oauth/manual-token-connection.ts +13 -13
  265. package/src/oauth/oauth-store.ts +214 -100
  266. package/src/oauth/platform-connection.test.ts +3 -3
  267. package/src/oauth/platform-connection.ts +4 -4
  268. package/src/oauth/provider-serializer.ts +31 -5
  269. package/src/oauth/revoke.ts +76 -0
  270. package/src/oauth/seed-providers.ts +126 -87
  271. package/src/oauth/token-persistence.ts +1 -1
  272. package/src/permissions/permission-mode.ts +4 -11
  273. package/src/permissions/prompter.ts +13 -1
  274. package/src/permissions/v2-consent-policy.ts +87 -0
  275. package/src/prompts/system-prompt.ts +18 -21
  276. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
  277. package/src/prompts/templates/BOOTSTRAP.md +59 -105
  278. package/src/providers/anthropic/client.ts +1 -0
  279. package/src/providers/types.ts +1 -1
  280. package/src/runtime/AGENTS.md +23 -0
  281. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
  282. package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
  283. package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
  284. package/src/runtime/assistant-event-hub.ts +2 -2
  285. package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
  286. package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
  287. package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
  288. package/src/runtime/auth/middleware.ts +98 -0
  289. package/src/runtime/auth/route-policy.ts +6 -7
  290. package/src/runtime/capability-tokens.ts +414 -0
  291. package/src/runtime/channel-approvals.ts +18 -5
  292. package/src/runtime/chrome-extension-registry.ts +332 -0
  293. package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
  294. package/src/runtime/guardian-decision-types.ts +7 -0
  295. package/src/runtime/http-server.ts +425 -70
  296. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
  297. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
  298. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
  299. package/src/runtime/migrations/migration-transport.ts +6 -0
  300. package/src/runtime/migrations/migration-wizard.ts +22 -2
  301. package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
  302. package/src/runtime/migrations/vbundle-builder.ts +145 -38
  303. package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
  304. package/src/runtime/migrations/vbundle-importer.ts +55 -5
  305. package/src/runtime/pending-interactions.ts +29 -13
  306. package/src/runtime/routes/approval-routes.ts +90 -16
  307. package/src/runtime/routes/browser-cdp-routes.ts +229 -0
  308. package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
  309. package/src/runtime/routes/conversation-analysis-routes.ts +2 -1
  310. package/src/runtime/routes/conversation-management-routes.ts +108 -0
  311. package/src/runtime/routes/conversation-routes.ts +301 -27
  312. package/src/runtime/routes/conversation-starter-routes.ts +78 -16
  313. package/src/runtime/routes/guardian-action-routes.ts +24 -13
  314. package/src/runtime/routes/host-browser-routes.ts +279 -0
  315. package/src/runtime/routes/host-file-routes.ts +9 -1
  316. package/src/runtime/routes/identity-routes.ts +259 -16
  317. package/src/runtime/routes/log-export-routes.ts +42 -22
  318. package/src/runtime/routes/memory-item-routes.ts +1 -7
  319. package/src/runtime/routes/migration-routes.ts +87 -2
  320. package/src/runtime/routes/oauth-apps.ts +15 -17
  321. package/src/runtime/routes/oauth-providers.ts +4 -0
  322. package/src/runtime/routes/schedule-routes.ts +24 -11
  323. package/src/runtime/routes/settings-routes.ts +9 -97
  324. package/src/runtime/routes/skills-routes.ts +52 -2
  325. package/src/runtime/routes/subagents-routes.ts +14 -10
  326. package/src/runtime/routes/usage-routes.ts +8 -7
  327. package/src/runtime/routes/workspace-routes.test.ts +22 -0
  328. package/src/runtime/routes/workspace-routes.ts +8 -1
  329. package/src/runtime/routes/workspace-utils.ts +2 -0
  330. package/src/schedule/scheduler.ts +7 -5
  331. package/src/security/ces-credential-client.ts +20 -0
  332. package/src/security/ces-rpc-credential-backend.ts +17 -0
  333. package/src/security/credential-backend.ts +5 -0
  334. package/src/security/oauth2.ts +42 -25
  335. package/src/security/secure-keys.ts +118 -25
  336. package/src/security/token-manager.ts +23 -10
  337. package/src/skills/catalog-files.ts +492 -0
  338. package/src/subagent/manager.ts +131 -26
  339. package/src/subagent/types.ts +19 -0
  340. package/src/tools/apps/executors.ts +11 -2
  341. package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
  342. package/src/tools/browser/auth-detector.ts +43 -12
  343. package/src/tools/browser/browser-execution.ts +645 -340
  344. package/src/tools/browser/browser-manager.ts +36 -12
  345. package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
  346. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
  347. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
  348. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
  349. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
  350. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
  351. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
  352. package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
  353. package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
  354. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
  355. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
  356. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
  357. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
  358. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
  359. package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
  360. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
  361. package/src/tools/browser/cdp-client/errors.ts +34 -0
  362. package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
  363. package/src/tools/browser/cdp-client/factory.ts +204 -0
  364. package/src/tools/browser/cdp-client/index.ts +14 -0
  365. package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
  366. package/src/tools/browser/cdp-client/types.ts +52 -0
  367. package/src/tools/filesystem/edit.ts +1 -1
  368. package/src/tools/filesystem/list.ts +1 -1
  369. package/src/tools/filesystem/read.ts +1 -1
  370. package/src/tools/filesystem/write.ts +2 -1
  371. package/src/tools/host-filesystem/edit.ts +1 -1
  372. package/src/tools/host-filesystem/read.ts +12 -15
  373. package/src/tools/host-filesystem/write.ts +1 -1
  374. package/src/tools/host-terminal/host-shell.ts +21 -16
  375. package/src/tools/permission-checker.ts +77 -82
  376. package/src/tools/registry.ts +0 -2
  377. package/src/tools/secret-detection-handler.ts +34 -0
  378. package/src/tools/shared/filesystem/image-read.ts +61 -40
  379. package/src/tools/subagent/spawn.ts +47 -3
  380. package/src/tools/subagent/status.ts +2 -0
  381. package/src/tools/system/register.ts +2 -16
  382. package/src/tools/terminal/safe-env.ts +7 -0
  383. package/src/tools/terminal/shell.ts +21 -16
  384. package/src/tools/tool-approval-handler.ts +48 -2
  385. package/src/tools/types.ts +2 -0
  386. package/src/util/platform.ts +14 -19
  387. package/src/workspace/top-level-renderer.ts +19 -1
  388. package/src/__tests__/chrome-cdp.test.ts +0 -419
  389. package/src/__tests__/permission-mode-sse.test.ts +0 -418
  390. package/src/__tests__/permission-mode-store.test.ts +0 -277
  391. package/src/browser-extension-relay/protocol.ts +0 -63
  392. package/src/browser-extension-relay/server.ts +0 -203
  393. package/src/config/schemas/sandbox.ts +0 -14
  394. package/src/permissions/permission-mode-store.ts +0 -180
  395. package/src/tools/browser/chrome-cdp.ts +0 -239
  396. package/src/tools/system/set-permission-mode.ts +0 -103
@@ -13,7 +13,7 @@ let lastStartArgs: {
13
13
  } | null = null;
14
14
 
15
15
  let mockPrepareResult: {
16
- authUrl: string;
16
+ authorizeUrl: string;
17
17
  state: string;
18
18
  completion: Promise<{
19
19
  tokens: { accessToken: string; refreshToken?: string };
@@ -21,7 +21,7 @@ let mockPrepareResult: {
21
21
  rawTokenResponse: Record<string, unknown>;
22
22
  }>;
23
23
  } = {
24
- authUrl: "https://provider.example.com/authorize?prepared",
24
+ authorizeUrl: "https://provider.example.com/authorize?prepared",
25
25
  state: "mock-state-123",
26
26
  completion: new Promise(() => {}), // never resolves by default
27
27
  };
@@ -86,21 +86,25 @@ mock.module("../oauth/scope-policy.js", () => ({
86
86
 
87
87
  // Provider store mock — configurable per test
88
88
  type ProviderRow = {
89
- providerKey: string;
90
- authUrl: string;
91
- tokenUrl: string;
92
- tokenEndpointAuthMethod: string | null;
89
+ provider: string;
90
+ authorizeUrl: string;
91
+ tokenExchangeUrl: string;
92
+ refreshUrl: string | null;
93
+ tokenEndpointAuthMethod: string;
93
94
  userinfoUrl: string | null;
94
95
  baseUrl: string | null;
95
96
  defaultScopes: string;
96
97
  scopePolicy: string;
97
- extraParams: string | null;
98
+ scopeSeparator: string;
99
+ authorizeParams: string | null;
98
100
  pingUrl: string | null;
99
101
  pingMethod: string | null;
100
102
  pingHeaders: string | null;
101
103
  pingBody: string | null;
104
+ revokeUrl: string | null;
105
+ revokeBodyTemplate: string | null;
102
106
  managedServiceConfigKey: string | null;
103
- displayName: string | null;
107
+ displayLabel: string | null;
104
108
  description: string | null;
105
109
  dashboardUrl: string | null;
106
110
  clientIdPlaceholder: string | null;
@@ -157,25 +161,29 @@ import { orchestrateOAuthConnect } from "../oauth/connect-orchestrator.js";
157
161
  // ---------------------------------------------------------------------------
158
162
 
159
163
  function makeProviderRow(
160
- overrides: Partial<ProviderRow> & { providerKey: string },
164
+ overrides: Partial<ProviderRow> & { provider: string },
161
165
  ): ProviderRow {
162
166
  const now = Date.now();
163
167
  return {
164
- authUrl: "https://provider.example.com/authorize",
165
- tokenUrl: "https://provider.example.com/token",
166
- tokenEndpointAuthMethod: null,
168
+ authorizeUrl: "https://provider.example.com/authorize",
169
+ tokenExchangeUrl: "https://provider.example.com/token",
170
+ refreshUrl: null,
171
+ tokenEndpointAuthMethod: "client_secret_post",
167
172
  userinfoUrl: null,
168
173
  baseUrl: null,
169
174
  defaultScopes: '["openid","email"]',
170
175
  scopePolicy:
171
176
  '{"allowAdditionalScopes":false,"allowedOptionalScopes":[],"forbiddenScopes":[]}',
172
- extraParams: null,
177
+ scopeSeparator: " ",
178
+ authorizeParams: null,
173
179
  pingUrl: null,
174
180
  pingMethod: null,
175
181
  pingHeaders: null,
176
182
  pingBody: null,
183
+ revokeUrl: null,
184
+ revokeBodyTemplate: null,
177
185
  managedServiceConfigKey: null,
178
- displayName: null,
186
+ displayLabel: null,
179
187
  description: null,
180
188
  dashboardUrl: null,
181
189
  clientIdPlaceholder: null,
@@ -200,19 +208,21 @@ function makeProviderRow(
200
208
 
201
209
  // Shared provider definitions used across tests
202
210
  const GOOGLE_PROVIDER = makeProviderRow({
203
- providerKey: "google",
204
- authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
205
- tokenUrl: "https://oauth2.googleapis.com/token",
211
+ provider: "google",
212
+ authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
213
+ tokenExchangeUrl: "https://oauth2.googleapis.com/token",
206
214
  loopbackPort: 17332,
207
- displayName: "Google",
215
+ displayLabel: "Google",
208
216
  });
209
217
 
210
218
  const OUTLOOK_PROVIDER = makeProviderRow({
211
- providerKey: "outlook",
212
- authUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
213
- tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
219
+ provider: "outlook",
220
+ authorizeUrl:
221
+ "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
222
+ tokenExchangeUrl:
223
+ "https://login.microsoftonline.com/common/oauth2/v2.0/token",
214
224
  loopbackPort: 17334,
215
- displayName: "Outlook",
225
+ displayLabel: "Outlook",
216
226
  });
217
227
 
218
228
  // ---------------------------------------------------------------------------
@@ -227,7 +237,7 @@ beforeEach(() => {
227
237
  mockProviderStore = {};
228
238
 
229
239
  mockPrepareResult = {
230
- authUrl: "https://provider.example.com/authorize?prepared",
240
+ authorizeUrl: "https://provider.example.com/authorize?prepared",
231
241
  state: "mock-state-123",
232
242
  completion: new Promise(() => {}),
233
243
  };
@@ -689,7 +699,7 @@ describe("orchestrateOAuthConnect — transport selection", () => {
689
699
  describe("provider without loopbackPort", () => {
690
700
  test("loopback transport passes undefined loopbackPort when provider has none", async () => {
691
701
  mockProviderStore["custom"] = makeProviderRow({
692
- providerKey: "custom",
702
+ provider: "custom",
693
703
  loopbackPort: null, // no fixed port
694
704
  });
695
705
 
@@ -706,4 +716,86 @@ describe("orchestrateOAuthConnect — transport selection", () => {
706
716
  });
707
717
  });
708
718
  });
719
+
720
+ // -------------------------------------------------------------------------
721
+ // Scope separator propagation
722
+ // -------------------------------------------------------------------------
723
+
724
+ describe("scope separator propagation", () => {
725
+ test("comma separator from providerRow propagates to oauthConfig (deferred)", async () => {
726
+ mockProviderStore["linear"] = makeProviderRow({
727
+ provider: "linear",
728
+ scopeSeparator: ",",
729
+ });
730
+
731
+ await orchestrateOAuthConnect({
732
+ service: "linear",
733
+ clientId: "client-id",
734
+ isInteractive: false,
735
+ callbackTransport: "loopback",
736
+ });
737
+
738
+ expect(lastPrepareArgs).not.toBeNull();
739
+ const capturedConfig = lastPrepareArgs!.config as {
740
+ scopeSeparator: string;
741
+ };
742
+ expect(capturedConfig.scopeSeparator).toBe(",");
743
+ });
744
+
745
+ test("space separator (default) from providerRow propagates to oauthConfig (deferred)", async () => {
746
+ mockProviderStore["google"] = GOOGLE_PROVIDER;
747
+
748
+ await orchestrateOAuthConnect({
749
+ service: "google",
750
+ clientId: "client-id",
751
+ isInteractive: false,
752
+ callbackTransport: "loopback",
753
+ });
754
+
755
+ expect(lastPrepareArgs).not.toBeNull();
756
+ const capturedConfig = lastPrepareArgs!.config as {
757
+ scopeSeparator: string;
758
+ };
759
+ expect(capturedConfig.scopeSeparator).toBe(" ");
760
+ });
761
+
762
+ test("comma separator from providerRow propagates to oauthConfig (interactive)", async () => {
763
+ mockProviderStore["linear"] = makeProviderRow({
764
+ provider: "linear",
765
+ scopeSeparator: ",",
766
+ });
767
+
768
+ await orchestrateOAuthConnect({
769
+ service: "linear",
770
+ clientId: "client-id",
771
+ isInteractive: true,
772
+ callbackTransport: "loopback",
773
+ openUrl: () => {},
774
+ });
775
+
776
+ expect(lastStartArgs).not.toBeNull();
777
+ const capturedConfig = lastStartArgs!.config as {
778
+ scopeSeparator: string;
779
+ };
780
+ expect(capturedConfig.scopeSeparator).toBe(",");
781
+ });
782
+
783
+ test("space separator from providerRow propagates to oauthConfig (interactive)", async () => {
784
+ mockProviderStore["google"] = GOOGLE_PROVIDER;
785
+
786
+ await orchestrateOAuthConnect({
787
+ service: "google",
788
+ clientId: "client-id",
789
+ isInteractive: true,
790
+ callbackTransport: "loopback",
791
+ openUrl: () => {},
792
+ });
793
+
794
+ expect(lastStartArgs).not.toBeNull();
795
+ const capturedConfig = lastStartArgs!.config as {
796
+ scopeSeparator: string;
797
+ };
798
+ expect(capturedConfig.scopeSeparator).toBe(" ");
799
+ });
800
+ });
709
801
  });
@@ -0,0 +1,23 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { PROVIDER_SEED_DATA } from "../oauth/seed-providers.js";
4
+
5
+ describe("PROVIDER_SEED_DATA logo URLs", () => {
6
+ test("every well-known provider has a Simple Icons CDN logoUrl", () => {
7
+ const missing: string[] = [];
8
+ const invalid: Array<{ provider: string; logoUrl: string }> = [];
9
+
10
+ for (const [key, seed] of Object.entries(PROVIDER_SEED_DATA)) {
11
+ if (!seed.logoUrl) {
12
+ missing.push(key);
13
+ continue;
14
+ }
15
+ if (!seed.logoUrl.startsWith("https://cdn.simpleicons.org/")) {
16
+ invalid.push({ provider: key, logoUrl: seed.logoUrl });
17
+ }
18
+ }
19
+
20
+ expect(missing).toEqual([]);
21
+ expect(invalid).toEqual([]);
22
+ });
23
+ });
@@ -10,24 +10,29 @@ import {
10
10
  function makeRow(overrides: Partial<OAuthProviderRow> = {}): OAuthProviderRow {
11
11
  const now = Date.now();
12
12
  return {
13
- providerKey: "test-provider",
14
- authUrl: "https://auth.example.com/authorize",
15
- tokenUrl: "https://auth.example.com/token",
16
- tokenEndpointAuthMethod: null,
13
+ provider: "test-provider",
14
+ authorizeUrl: "https://auth.example.com/authorize",
15
+ tokenExchangeUrl: "https://auth.example.com/token",
16
+ refreshUrl: null,
17
+ tokenEndpointAuthMethod: "client_secret_post",
17
18
  userinfoUrl: null,
18
19
  baseUrl: null,
19
20
  defaultScopes: "[]",
20
21
  scopePolicy: "{}",
21
- extraParams: null,
22
+ scopeSeparator: " ",
23
+ authorizeParams: null,
22
24
  pingUrl: null,
23
25
  pingMethod: null,
24
26
  pingHeaders: null,
25
27
  pingBody: null,
28
+ revokeUrl: null,
29
+ revokeBodyTemplate: null,
26
30
  managedServiceConfigKey: null,
27
- displayName: null,
31
+ displayLabel: null,
28
32
  description: null,
29
33
  dashboardUrl: null,
30
34
  clientIdPlaceholder: null,
35
+ logoUrl: null,
31
36
  requiresClientSecret: 1,
32
37
  loopbackPort: null,
33
38
  injectionTemplates: null,
@@ -52,7 +57,7 @@ describe("serializeProvider", () => {
52
57
  const row = makeRow({
53
58
  defaultScopes: JSON.stringify(["openid", "email"]),
54
59
  scopePolicy: JSON.stringify({ required: ["openid"] }),
55
- extraParams: JSON.stringify({ access_type: "offline" }),
60
+ authorizeParams: JSON.stringify({ access_type: "offline" }),
56
61
  pingHeaders: JSON.stringify({ "X-Api-Version": "2" }),
57
62
  pingBody: JSON.stringify({ query: "{ me { id } }" }),
58
63
  injectionTemplates: JSON.stringify([
@@ -167,13 +172,103 @@ describe("serializeProvider", () => {
167
172
  test("returns null for null input", () => {
168
173
  expect(serializeProvider(null)).toBeNull();
169
174
  });
175
+
176
+ test("emits scopeSeparator from the row when set to ','", () => {
177
+ const row = makeRow({ scopeSeparator: "," });
178
+ const result = serializeProvider(row)!;
179
+ expect(result.scopeSeparator).toBe(",");
180
+ });
181
+
182
+ test("emits scopeSeparator default ' ' when row uses the default", () => {
183
+ const row = makeRow({ scopeSeparator: " " });
184
+ const result = serializeProvider(row)!;
185
+ expect(result.scopeSeparator).toBe(" ");
186
+ });
187
+
188
+ test("emits refreshUrl from the row when set to a URL", () => {
189
+ const row = makeRow({
190
+ refreshUrl: "https://refresh.example.com/token",
191
+ });
192
+ const result = serializeProvider(row)!;
193
+ expect(result.refreshUrl).toBe("https://refresh.example.com/token");
194
+ });
195
+
196
+ test("emits refreshUrl as null when the row value is null", () => {
197
+ const row = makeRow({ refreshUrl: null });
198
+ const result = serializeProvider(row)!;
199
+ expect(result.refreshUrl).toBeNull();
200
+ });
201
+
202
+ test("emits revokeUrl from the row and parses revokeBodyTemplate JSON", () => {
203
+ const row = makeRow({
204
+ revokeUrl: "https://revoke.example.com",
205
+ revokeBodyTemplate: JSON.stringify({ token: "{access_token}" }),
206
+ });
207
+ const result = serializeProvider(row)!;
208
+ expect(result.revokeUrl).toBe("https://revoke.example.com");
209
+ expect(result.revokeBodyTemplate).toEqual({ token: "{access_token}" });
210
+ // Make sure it's a parsed object, not a string.
211
+ expect(typeof result.revokeBodyTemplate).toBe("object");
212
+ expect(result.revokeBodyTemplate).not.toBe(
213
+ JSON.stringify({ token: "{access_token}" }),
214
+ );
215
+ });
216
+
217
+ test("emits revokeUrl and revokeBodyTemplate as null when the row values are null", () => {
218
+ const row = makeRow({
219
+ revokeUrl: null,
220
+ revokeBodyTemplate: null,
221
+ });
222
+ const result = serializeProvider(row)!;
223
+ expect(result.revokeUrl).toBeNull();
224
+ expect(result.revokeBodyTemplate).toBeNull();
225
+ });
226
+
227
+ test("normalizes empty string revokeUrl to null on the serialized output", () => {
228
+ // Legacy rows or rows that reached the store via an empty-string update
229
+ // may persist `revokeUrl: ""`. The serializer should normalize this to
230
+ // null so wire consumers (CLI --json, HTTP API) see a consistent
231
+ // "disabled" signal.
232
+ const row = makeRow({ revokeUrl: "" });
233
+ const result = serializeProvider(row)!;
234
+ expect(result.revokeUrl).toBeNull();
235
+ });
236
+
237
+ test("preserves all keys in a complex revokeBodyTemplate", () => {
238
+ const template = {
239
+ token: "{access_token}",
240
+ client_id: "{client_id}",
241
+ token_type_hint: "access_token",
242
+ extra_field: "static-value",
243
+ };
244
+ const row = makeRow({
245
+ revokeUrl: "https://revoke.example.com/oauth/revoke",
246
+ revokeBodyTemplate: JSON.stringify(template),
247
+ });
248
+ const result = serializeProvider(row)!;
249
+ expect(result.revokeBodyTemplate).toEqual(template);
250
+ });
251
+
252
+ test("serializeProvider exposes logoUrl", () => {
253
+ const row = makeRow({
254
+ logoUrl: "https://cdn.simpleicons.org/notion",
255
+ });
256
+ const result = serializeProvider(row)!;
257
+ expect(result.logoUrl).toBe("https://cdn.simpleicons.org/notion");
258
+ });
259
+
260
+ test("serializeProvider emits logoUrl as null when row value is null", () => {
261
+ const row = makeRow({ logoUrl: null });
262
+ const result = serializeProvider(row)!;
263
+ expect(result.logoUrl).toBeNull();
264
+ });
170
265
  });
171
266
 
172
267
  describe("serializeProviderSummary", () => {
173
268
  test("returns the expected subset of fields in snake_case", () => {
174
269
  const row = makeRow({
175
- providerKey: "google",
176
- displayName: "Google",
270
+ provider: "google",
271
+ displayLabel: "Google",
177
272
  description: "Google OAuth 2.0",
178
273
  dashboardUrl: "https://console.cloud.google.com",
179
274
  clientIdPlaceholder: "your-client-id.apps.googleusercontent.com",
@@ -190,6 +285,7 @@ describe("serializeProviderSummary", () => {
190
285
  dashboard_url: "https://console.cloud.google.com",
191
286
  client_id_placeholder: "your-client-id.apps.googleusercontent.com",
192
287
  requires_client_secret: true,
288
+ logo_url: null,
193
289
  supports_managed_mode: true,
194
290
  feature_flag: null,
195
291
  });
@@ -197,7 +293,7 @@ describe("serializeProviderSummary", () => {
197
293
 
198
294
  test("nullifies missing optional fields", () => {
199
295
  const row = makeRow({
200
- displayName: null,
296
+ displayLabel: null,
201
297
  description: null,
202
298
  dashboardUrl: null,
203
299
  clientIdPlaceholder: null,
@@ -230,4 +326,44 @@ describe("serializeProviderSummary", () => {
230
326
  test("returns null for undefined input", () => {
231
327
  expect(serializeProviderSummary(undefined)).toBeNull();
232
328
  });
329
+
330
+ test("does NOT include scope_separator (intentionally omitted from the HTTP summary)", () => {
331
+ const row = makeRow({ scopeSeparator: "," });
332
+ const result = serializeProviderSummary(row)!;
333
+ expect(result).not.toHaveProperty("scope_separator");
334
+ expect(result).not.toHaveProperty("scopeSeparator");
335
+ });
336
+
337
+ test("does NOT include refresh_url (intentionally omitted from the HTTP summary)", () => {
338
+ const row = makeRow({ refreshUrl: "https://refresh.example.com/token" });
339
+ const result = serializeProviderSummary(row)!;
340
+ expect(result).not.toHaveProperty("refresh_url");
341
+ expect(result).not.toHaveProperty("refreshUrl");
342
+ });
343
+
344
+ test("does NOT include revoke_url or revoke_body_template (internal protocol details)", () => {
345
+ const row = makeRow({
346
+ revokeUrl: "https://revoke.example.com",
347
+ revokeBodyTemplate: JSON.stringify({ token: "{access_token}" }),
348
+ });
349
+ const result = serializeProviderSummary(row)!;
350
+ expect(result).not.toHaveProperty("revoke_url");
351
+ expect(result).not.toHaveProperty("revokeUrl");
352
+ expect(result).not.toHaveProperty("revoke_body_template");
353
+ expect(result).not.toHaveProperty("revokeBodyTemplate");
354
+ });
355
+
356
+ test("serializeProviderSummary exposes logo_url", () => {
357
+ const row = makeRow({
358
+ logoUrl: "https://cdn.simpleicons.org/notion",
359
+ });
360
+ const result = serializeProviderSummary(row)!;
361
+ expect(result).toHaveProperty("logo_url");
362
+ expect(result.logo_url).toBe("https://cdn.simpleicons.org/notion");
363
+ });
364
+
365
+ test("logo_url is null when row logoUrl is null", () => {
366
+ const result = serializeProviderSummary({ ...makeRow(), logoUrl: null })!;
367
+ expect(result.logo_url).toBeNull();
368
+ });
233
369
  });
@@ -51,18 +51,16 @@ describe("isProviderVisible", () => {
51
51
  test("returns true when featureFlag is null", () => {
52
52
  seedProviders([
53
53
  {
54
- providerKey: "no-flag-provider",
55
- authUrl: "https://example.com/auth",
56
- tokenUrl: "https://example.com/token",
54
+ provider: "no-flag-provider",
55
+ authorizeUrl: "https://example.com/auth",
56
+ tokenExchangeUrl: "https://example.com/token",
57
57
  defaultScopes: ["read"],
58
58
  scopePolicy: {},
59
59
  },
60
60
  ]);
61
61
 
62
62
  const providers = listProviders();
63
- const provider = providers.find(
64
- (p) => p.providerKey === "no-flag-provider",
65
- );
63
+ const provider = providers.find((p) => p.provider === "no-flag-provider");
66
64
  expect(provider).toBeDefined();
67
65
  expect(provider!.featureFlag).toBeNull();
68
66
 
@@ -75,9 +73,9 @@ describe("isProviderVisible", () => {
75
73
 
76
74
  seedProviders([
77
75
  {
78
- providerKey: "gated-provider",
79
- authUrl: "https://example.com/auth",
80
- tokenUrl: "https://example.com/token",
76
+ provider: "gated-provider",
77
+ authorizeUrl: "https://example.com/auth",
78
+ tokenExchangeUrl: "https://example.com/token",
81
79
  defaultScopes: ["read"],
82
80
  scopePolicy: {},
83
81
  featureFlag: "test-gate",
@@ -85,7 +83,7 @@ describe("isProviderVisible", () => {
85
83
  ]);
86
84
 
87
85
  const providers = listProviders();
88
- const provider = providers.find((p) => p.providerKey === "gated-provider");
86
+ const provider = providers.find((p) => p.provider === "gated-provider");
89
87
  expect(provider).toBeDefined();
90
88
  expect(provider!.featureFlag).toBe("test-gate");
91
89
 
@@ -98,9 +96,9 @@ describe("isProviderVisible", () => {
98
96
 
99
97
  seedProviders([
100
98
  {
101
- providerKey: "gated-provider",
102
- authUrl: "https://example.com/auth",
103
- tokenUrl: "https://example.com/token",
99
+ provider: "gated-provider",
100
+ authorizeUrl: "https://example.com/auth",
101
+ tokenExchangeUrl: "https://example.com/token",
104
102
  defaultScopes: ["read"],
105
103
  scopePolicy: {},
106
104
  featureFlag: "test-gate",
@@ -108,7 +106,7 @@ describe("isProviderVisible", () => {
108
106
  ]);
109
107
 
110
108
  const providers = listProviders();
111
- const provider = providers.find((p) => p.providerKey === "gated-provider");
109
+ const provider = providers.find((p) => p.provider === "gated-provider");
112
110
  expect(provider).toBeDefined();
113
111
 
114
112
  const config = makeConfig();
@@ -120,16 +118,16 @@ describe("isProviderVisible", () => {
120
118
 
121
119
  seedProviders([
122
120
  {
123
- providerKey: "visible-provider",
124
- authUrl: "https://example.com/auth",
125
- tokenUrl: "https://example.com/token",
121
+ provider: "visible-provider",
122
+ authorizeUrl: "https://example.com/auth",
123
+ tokenExchangeUrl: "https://example.com/token",
126
124
  defaultScopes: ["read"],
127
125
  scopePolicy: {},
128
126
  },
129
127
  {
130
- providerKey: "gated-provider",
131
- authUrl: "https://gated.example.com/auth",
132
- tokenUrl: "https://gated.example.com/token",
128
+ provider: "gated-provider",
129
+ authorizeUrl: "https://gated.example.com/auth",
130
+ tokenExchangeUrl: "https://gated.example.com/token",
133
131
  defaultScopes: ["read"],
134
132
  scopePolicy: {},
135
133
  featureFlag: "test-gate",
@@ -144,6 +142,6 @@ describe("isProviderVisible", () => {
144
142
  isProviderVisible(p, config),
145
143
  );
146
144
  expect(visibleProviders).toHaveLength(1);
147
- expect(visibleProviders[0]!.providerKey).toBe("visible-provider");
145
+ expect(visibleProviders[0]!.provider).toBe("visible-provider");
148
146
  });
149
147
  });
@@ -2,26 +2,31 @@ import { describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  const mockListProviders = mock(() => [
4
4
  {
5
- providerKey: "google",
6
- displayName: "Google",
5
+ provider: "google",
6
+ displayLabel: "Google",
7
7
  description: "Google OAuth provider",
8
8
  dashboardUrl: "https://console.cloud.google.com/apis/credentials",
9
9
  clientIdPlaceholder: null,
10
+ logoUrl: "https://cdn.simpleicons.org/google",
10
11
  requiresClientSecret: 1,
11
12
  managedServiceConfigKey: "google-oauth",
12
- authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
13
- tokenUrl: "https://oauth2.googleapis.com/token",
14
- tokenEndpointAuthMethod: null,
13
+ authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
14
+ tokenExchangeUrl: "https://oauth2.googleapis.com/token",
15
+ refreshUrl: null,
16
+ tokenEndpointAuthMethod: "client_secret_post",
15
17
  userinfoUrl: null,
16
18
  baseUrl: null,
17
19
  defaultScopes: "[]",
18
20
  scopePolicy: "[]",
19
- extraParams: null,
21
+ scopeSeparator: null,
22
+ authorizeParams: null,
20
23
 
21
24
  pingUrl: null,
22
25
  pingMethod: null,
23
26
  pingHeaders: null,
24
27
  pingBody: null,
28
+ revokeUrl: null,
29
+ revokeBodyTemplate: null,
25
30
  loopbackPort: null,
26
31
  injectionTemplates: null,
27
32
  appType: null,
@@ -38,26 +43,31 @@ const mockListProviders = mock(() => [
38
43
  updatedAt: 1735689550000,
39
44
  },
40
45
  {
41
- providerKey: "github",
42
- displayName: "GitHub",
46
+ provider: "github",
47
+ displayLabel: "GitHub",
43
48
  description: "GitHub OAuth provider",
44
49
  dashboardUrl: "https://github.com/settings/developers",
45
50
  clientIdPlaceholder: null,
51
+ logoUrl: null,
46
52
  requiresClientSecret: 1,
47
53
  managedServiceConfigKey: null,
48
- authUrl: "https://github.com/login/oauth/authorize",
49
- tokenUrl: "https://github.com/login/oauth/access_token",
50
- tokenEndpointAuthMethod: null,
54
+ authorizeUrl: "https://github.com/login/oauth/authorize",
55
+ tokenExchangeUrl: "https://github.com/login/oauth/access_token",
56
+ refreshUrl: null,
57
+ tokenEndpointAuthMethod: "client_secret_post",
51
58
  userinfoUrl: null,
52
59
  baseUrl: null,
53
60
  defaultScopes: "[]",
54
61
  scopePolicy: "[]",
55
- extraParams: null,
62
+ scopeSeparator: null,
63
+ authorizeParams: null,
56
64
 
57
65
  pingUrl: null,
58
66
  pingMethod: null,
59
67
  pingHeaders: null,
60
68
  pingBody: null,
69
+ revokeUrl: null,
70
+ revokeBodyTemplate: null,
61
71
  loopbackPort: null,
62
72
  injectionTemplates: null,
63
73
  appType: null,
@@ -75,9 +85,9 @@ const mockListProviders = mock(() => [
75
85
  },
76
86
  ]);
77
87
 
78
- const mockGetProvider = mock((providerKey: string) => {
88
+ const mockGetProvider = mock((provider: string) => {
79
89
  const all = mockListProviders();
80
- return all.find((p) => p.providerKey === providerKey) ?? undefined;
90
+ return all.find((p) => p.provider === provider) ?? undefined;
81
91
  });
82
92
 
83
93
  mock.module("../oauth/oauth-store.js", () => ({
@@ -149,6 +159,7 @@ describe("GET /v1/oauth/providers", () => {
149
159
  "dashboard_url",
150
160
  "client_id_placeholder",
151
161
  "requires_client_secret",
162
+ "logo_url",
152
163
  "supports_managed_mode",
153
164
  "feature_flag",
154
165
  ];
@@ -158,6 +169,31 @@ describe("GET /v1/oauth/providers", () => {
158
169
  }
159
170
  });
160
171
 
172
+ test("response includes logo_url for each provider", async () => {
173
+ const req = new Request("http://localhost/v1/oauth/providers");
174
+ const url = new URL(req.url);
175
+ const res = await getRoute("GET", "oauth/providers").handler({
176
+ req,
177
+ url,
178
+ server: null as never,
179
+ authContext: null as never,
180
+ params: {},
181
+ });
182
+
183
+ expect(res.status).toBe(200);
184
+ const body = (await res.json()) as {
185
+ providers: Array<{
186
+ provider_key: string;
187
+ logo_url: string | null;
188
+ }>;
189
+ };
190
+
191
+ expect(body.providers[0]!.logo_url).toBe(
192
+ "https://cdn.simpleicons.org/google",
193
+ );
194
+ expect(body.providers[1]!.logo_url).toBeNull();
195
+ });
196
+
161
197
  test("supports_managed_mode=true returns only managed providers", async () => {
162
198
  const req = new Request(
163
199
  "http://localhost/v1/oauth/providers?supports_managed_mode=true",