@vellumai/assistant 0.6.1 → 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 (463) hide show
  1. package/bun.lock +40 -40
  2. package/bunfig.toml +3 -0
  3. package/docker-entrypoint.sh +12 -2
  4. package/docs/architecture/memory.md +1 -1
  5. package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
  6. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
  7. package/openapi.yaml +184 -69
  8. package/package.json +41 -41
  9. package/scripts/generate-openapi.ts +1 -2
  10. package/src/__tests__/acp-session.test.ts +43 -0
  11. package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
  12. package/src/__tests__/app-executors.test.ts +1 -0
  13. package/src/__tests__/app-source-watcher.test.ts +37 -11
  14. package/src/__tests__/approval-routes-http.test.ts +178 -1
  15. package/src/__tests__/assistant-event-hub.test.ts +30 -0
  16. package/src/__tests__/browser-fill-credential.test.ts +229 -94
  17. package/src/__tests__/browser-manager.test.ts +40 -27
  18. package/src/__tests__/catalog-files.test.ts +862 -0
  19. package/src/__tests__/channel-approvals.test.ts +53 -0
  20. package/src/__tests__/checker.test.ts +104 -170
  21. package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
  22. package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
  23. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  24. package/src/__tests__/config-schema.test.ts +125 -48
  25. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
  26. package/src/__tests__/context-overflow-approval.test.ts +21 -6
  27. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
  28. package/src/__tests__/conversation-agent-loop.test.ts +1 -1
  29. package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
  30. package/src/__tests__/conversation-attachments.test.ts +80 -4
  31. package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
  32. package/src/__tests__/conversation-directories-parse.test.ts +105 -0
  33. package/src/__tests__/conversation-fork-crud.test.ts +17 -0
  34. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  35. package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
  36. package/src/__tests__/conversation-inject-context.test.ts +103 -0
  37. package/src/__tests__/conversation-queue.test.ts +45 -2
  38. package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
  39. package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
  40. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  41. package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
  42. package/src/__tests__/conversation-starter-routes.test.ts +126 -0
  43. package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
  44. package/src/__tests__/conversation-store.test.ts +195 -0
  45. package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
  46. package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -3
  47. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  48. package/src/__tests__/credential-vault-unit.test.ts +4 -4
  49. package/src/__tests__/credential-vault.test.ts +152 -13
  50. package/src/__tests__/credentials-cli.test.ts +2 -2
  51. package/src/__tests__/date-context.test.ts +4 -4
  52. package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
  53. package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
  54. package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
  55. package/src/__tests__/gateway-only-guard.test.ts +3 -0
  56. package/src/__tests__/gemini-provider.test.ts +2 -2
  57. package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
  58. package/src/__tests__/headless-browser-interactions.test.ts +707 -371
  59. package/src/__tests__/headless-browser-navigate.test.ts +389 -47
  60. package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
  61. package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
  62. package/src/__tests__/host-bash-proxy.test.ts +150 -1
  63. package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
  64. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
  65. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
  66. package/src/__tests__/host-browser-event-routes.test.ts +350 -0
  67. package/src/__tests__/host-browser-proxy.test.ts +444 -0
  68. package/src/__tests__/host-browser-routes.test.ts +198 -0
  69. package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
  70. package/src/__tests__/host-cu-proxy.test.ts +171 -1
  71. package/src/__tests__/host-file-proxy.test.ts +185 -1
  72. package/src/__tests__/host-file-read-tool.test.ts +52 -0
  73. package/src/__tests__/host-proxy-interface.test.ts +165 -0
  74. package/src/__tests__/host-shell-tool.test.ts +1 -11
  75. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  76. package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
  77. package/src/__tests__/inline-command-runner.test.ts +7 -5
  78. package/src/__tests__/integration-status.test.ts +6 -7
  79. package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
  80. package/src/__tests__/log-export-workspace.test.ts +190 -0
  81. package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
  82. package/src/__tests__/mcp-client-auth.test.ts +40 -4
  83. package/src/__tests__/mcp-health-check.test.ts +10 -3
  84. package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
  85. package/src/__tests__/migration-export-http.test.ts +61 -2
  86. package/src/__tests__/migration-export-streaming.test.ts +66 -0
  87. package/src/__tests__/migration-import-commit-http.test.ts +101 -1
  88. package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
  89. package/src/__tests__/navigate-settings-tab.test.ts +14 -1
  90. package/src/__tests__/notification-broadcaster.test.ts +65 -0
  91. package/src/__tests__/oauth-apps-routes.test.ts +17 -12
  92. package/src/__tests__/oauth-cli.test.ts +707 -60
  93. package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
  94. package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
  95. package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
  96. package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
  97. package/src/__tests__/oauth-providers-routes.test.ts +50 -14
  98. package/src/__tests__/oauth-store.test.ts +1386 -182
  99. package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
  100. package/src/__tests__/onboarding-template-contract.test.ts +74 -55
  101. package/src/__tests__/openai-provider.test.ts +2 -2
  102. package/src/__tests__/outlook-categories.test.ts +1 -1
  103. package/src/__tests__/outlook-client-automation.test.ts +1 -1
  104. package/src/__tests__/outlook-compose-tools.test.ts +1 -1
  105. package/src/__tests__/outlook-email-watcher.test.ts +1 -1
  106. package/src/__tests__/outlook-follow-up.test.ts +1 -1
  107. package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
  108. package/src/__tests__/outlook-trash.test.ts +1 -1
  109. package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
  110. package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
  111. package/src/__tests__/permission-mode.test.ts +28 -56
  112. package/src/__tests__/pkb-autoinject.test.ts +96 -0
  113. package/src/__tests__/platform-callback-registration.test.ts +19 -0
  114. package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
  115. package/src/__tests__/proxy-approval-callback.test.ts +18 -0
  116. package/src/__tests__/require-fresh-approval.test.ts +40 -3
  117. package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
  118. package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
  119. package/src/__tests__/schedule-routes.test.ts +162 -0
  120. package/src/__tests__/secret-detection-handler.test.ts +84 -0
  121. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  122. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  123. package/src/__tests__/set-permission-mode.test.ts +13 -250
  124. package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
  125. package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
  126. package/src/__tests__/slack-channel-config.test.ts +12 -15
  127. package/src/__tests__/subagent-detail.test.ts +44 -2
  128. package/src/__tests__/subagent-disposal.test.ts +1 -0
  129. package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
  130. package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
  131. package/src/__tests__/subagent-manager-notify.test.ts +1 -0
  132. package/src/__tests__/subagent-notify-parent.test.ts +1 -0
  133. package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
  134. package/src/__tests__/subagent-tools.test.ts +1 -0
  135. package/src/__tests__/subagent-types.test.ts +1 -0
  136. package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
  137. package/src/__tests__/system-prompt.test.ts +72 -1
  138. package/src/__tests__/task-scheduler.test.ts +32 -6
  139. package/src/__tests__/telegram-config.test.ts +10 -13
  140. package/src/__tests__/terminal-sandbox.test.ts +1 -1
  141. package/src/__tests__/terminal-tools.test.ts +11 -5
  142. package/src/__tests__/test-preload.ts +14 -0
  143. package/src/__tests__/tool-approval-handler.test.ts +73 -0
  144. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
  145. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
  146. package/src/__tests__/tool-executor.test.ts +0 -1
  147. package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
  148. package/src/__tests__/top-level-renderer.test.ts +73 -1
  149. package/src/__tests__/transport-hints-queue.test.ts +62 -0
  150. package/src/__tests__/trust-store.test.ts +4 -4
  151. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
  152. package/src/__tests__/v2-consent-policy.test.ts +103 -0
  153. package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
  154. package/src/__tests__/workspace-policy.test.ts +2 -7
  155. package/src/acp/client-handler.ts +30 -4
  156. package/src/agent/loop.ts +12 -35
  157. package/src/approvals/guardian-request-resolvers.ts +21 -15
  158. package/src/browser-session/__tests__/manager.test.ts +297 -0
  159. package/src/browser-session/backends/cdp-inspect.ts +30 -0
  160. package/src/browser-session/backends/extension.ts +26 -0
  161. package/src/browser-session/backends/local.ts +24 -0
  162. package/src/browser-session/events.ts +164 -0
  163. package/src/browser-session/index.ts +27 -0
  164. package/src/browser-session/manager.ts +159 -0
  165. package/src/browser-session/types.ts +28 -0
  166. package/src/channels/__tests__/types.test.ts +134 -0
  167. package/src/channels/types.ts +55 -0
  168. package/src/cli/__tests__/run-assistant-command.ts +34 -7
  169. package/src/cli/__tests__/unknown-command.test.ts +33 -0
  170. package/src/cli/commands/browser-relay.ts +339 -409
  171. package/src/cli/commands/credentials.ts +3 -3
  172. package/src/cli/commands/default-action.ts +68 -1
  173. package/src/cli/commands/email.ts +18 -13
  174. package/src/cli/commands/mcp.ts +16 -4
  175. package/src/cli/commands/oauth/__tests__/connect.test.ts +68 -41
  176. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
  177. package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
  178. package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
  179. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
  180. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
  181. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
  182. package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
  183. package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
  184. package/src/cli/commands/oauth/apps.ts +7 -4
  185. package/src/cli/commands/oauth/connect.ts +16 -2
  186. package/src/cli/commands/oauth/disconnect.ts +1 -1
  187. package/src/cli/commands/oauth/providers.ts +200 -36
  188. package/src/cli/commands/oauth/shared.ts +5 -5
  189. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
  190. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  191. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  192. package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
  193. package/src/cli/commands/platform/index.ts +107 -10
  194. package/src/cli/commands/usage.ts +10 -9
  195. package/src/cli/lib/daemon-credential-client.ts +4 -0
  196. package/src/cli/program.ts +10 -3
  197. package/src/config/assistant-feature-flags.ts +59 -55
  198. package/src/config/bundled-skills/app-builder/SKILL.md +33 -173
  199. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
  200. package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
  201. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
  202. package/src/config/bundled-skills/contacts/SKILL.md +3 -0
  203. package/src/config/bundled-skills/document/SKILL.md +4 -0
  204. package/src/config/bundled-skills/gmail/SKILL.md +12 -7
  205. package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
  206. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
  207. package/src/config/bundled-skills/outlook/SKILL.md +7 -0
  208. package/src/config/bundled-skills/settings/TOOLS.json +1 -1
  209. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
  210. package/src/config/bundled-skills/subagent/SKILL.md +21 -0
  211. package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
  212. package/src/config/bundled-skills/tasks/SKILL.md +5 -0
  213. package/src/config/env-registry.ts +14 -0
  214. package/src/config/env.ts +21 -0
  215. package/src/config/feature-flag-registry.json +46 -7
  216. package/src/config/loader.ts +56 -1
  217. package/src/config/sanitize-for-transfer.ts +47 -0
  218. package/src/config/schema.ts +46 -5
  219. package/src/config/schemas/host-browser.ts +66 -0
  220. package/src/config/schemas/memory-lifecycle.ts +1 -1
  221. package/src/config/schemas/memory-retrieval.ts +103 -0
  222. package/src/config/schemas/security.ts +0 -6
  223. package/src/config/schemas/services.ts +16 -0
  224. package/src/config/types.ts +0 -1
  225. package/src/context/post-turn-tool-result-truncation.ts +176 -0
  226. package/src/context/window-manager.ts +19 -1
  227. package/src/credential-execution/approval-bridge.ts +49 -16
  228. package/src/credential-execution/managed-catalog.ts +3 -7
  229. package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
  230. package/src/daemon/app-source-watcher.ts +35 -0
  231. package/src/daemon/config-watcher.ts +6 -2
  232. package/src/daemon/context-overflow-approval.ts +5 -1
  233. package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
  234. package/src/daemon/conversation-agent-loop.ts +74 -19
  235. package/src/daemon/conversation-attachments.ts +40 -1
  236. package/src/daemon/conversation-messaging.ts +3 -0
  237. package/src/daemon/conversation-process.ts +66 -3
  238. package/src/daemon/conversation-queue-manager.ts +8 -0
  239. package/src/daemon/conversation-runtime-assembly.ts +159 -20
  240. package/src/daemon/conversation-surfaces.ts +78 -12
  241. package/src/daemon/conversation-tool-setup.ts +74 -11
  242. package/src/daemon/conversation-workspace.ts +12 -0
  243. package/src/daemon/conversation.ts +227 -11
  244. package/src/daemon/date-context.ts +10 -10
  245. package/src/daemon/first-greeting.ts +3 -2
  246. package/src/daemon/handlers/conversations.ts +9 -139
  247. package/src/daemon/handlers/shared.ts +65 -0
  248. package/src/daemon/handlers/skills.ts +232 -37
  249. package/src/daemon/host-bash-proxy.ts +48 -13
  250. package/src/daemon/host-browser-proxy.ts +191 -0
  251. package/src/daemon/host-cu-proxy.ts +36 -11
  252. package/src/daemon/host-file-proxy.ts +57 -9
  253. package/src/daemon/lifecycle.ts +86 -12
  254. package/src/daemon/message-protocol.ts +7 -0
  255. package/src/daemon/message-types/conversations.ts +59 -13
  256. package/src/daemon/message-types/host-browser.ts +100 -0
  257. package/src/daemon/message-types/messages.ts +5 -6
  258. package/src/daemon/message-types/notifications.ts +12 -0
  259. package/src/daemon/message-types/settings.ts +12 -0
  260. package/src/daemon/message-types/skills.ts +10 -0
  261. package/src/daemon/message-types/subagents.ts +2 -0
  262. package/src/daemon/server.ts +112 -35
  263. package/src/daemon/tool-side-effects.ts +6 -0
  264. package/src/daemon/transport-hints.ts +14 -0
  265. package/src/inbound/platform-callback-registration.ts +18 -17
  266. package/src/index.ts +1 -1
  267. package/src/mcp/client.ts +59 -24
  268. package/src/memory/app-store.ts +31 -1
  269. package/src/memory/conversation-crud.ts +38 -10
  270. package/src/memory/conversation-directories.ts +39 -0
  271. package/src/memory/conversation-group-migration.ts +65 -5
  272. package/src/memory/conversation-starters-cadence.ts +76 -0
  273. package/src/memory/conversation-title-service.ts +5 -2
  274. package/src/memory/db-init.ts +12 -0
  275. package/src/memory/embedding-backend.test.ts +75 -0
  276. package/src/memory/embedding-backend.ts +131 -5
  277. package/src/memory/embedding-gemini.test.ts +54 -0
  278. package/src/memory/embedding-gemini.ts +20 -9
  279. package/src/memory/embedding-local.ts +177 -18
  280. package/src/memory/graph/capability-seed.ts +3 -5
  281. package/src/memory/graph/consolidation.ts +10 -23
  282. package/src/memory/graph/extraction-job.ts +15 -0
  283. package/src/memory/graph/retriever.ts +40 -22
  284. package/src/memory/graph/store.test.ts +7 -3
  285. package/src/memory/graph/store.ts +47 -12
  286. package/src/memory/group-crud.ts +25 -9
  287. package/src/memory/llm-usage-store.ts +45 -4
  288. package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
  289. package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
  290. package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
  291. package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
  292. package/src/memory/migrations/217-conversation-host-access.ts +40 -0
  293. package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
  294. package/src/memory/migrations/index.ts +6 -0
  295. package/src/memory/migrations/registry.ts +8 -0
  296. package/src/memory/schema/conversations.ts +1 -0
  297. package/src/memory/schema/oauth.ts +18 -13
  298. package/src/messaging/provider.ts +1 -1
  299. package/src/notifications/broadcaster.ts +6 -0
  300. package/src/notifications/conversation-pairing.ts +12 -4
  301. package/src/notifications/emit-signal.ts +14 -0
  302. package/src/notifications/signal.ts +11 -0
  303. package/src/oauth/AGENTS.md +76 -0
  304. package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
  305. package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
  306. package/src/oauth/byo-connection.test.ts +8 -8
  307. package/src/oauth/byo-connection.ts +7 -7
  308. package/src/oauth/connect-orchestrator.ts +23 -21
  309. package/src/oauth/connect-types.ts +3 -3
  310. package/src/oauth/connection-resolver.test.ts +17 -4
  311. package/src/oauth/connection-resolver.ts +16 -16
  312. package/src/oauth/connection.ts +1 -1
  313. package/src/oauth/manual-token-connection.ts +13 -13
  314. package/src/oauth/oauth-store.ts +214 -100
  315. package/src/oauth/platform-connection.test.ts +5 -5
  316. package/src/oauth/platform-connection.ts +4 -4
  317. package/src/oauth/provider-serializer.ts +31 -5
  318. package/src/oauth/revoke.ts +76 -0
  319. package/src/oauth/seed-providers.ts +127 -87
  320. package/src/oauth/token-persistence.ts +1 -1
  321. package/src/permissions/checker.ts +3 -3
  322. package/src/permissions/defaults.ts +7 -8
  323. package/src/permissions/permission-mode.ts +4 -11
  324. package/src/permissions/prompter.ts +13 -3
  325. package/src/permissions/v2-consent-policy.ts +87 -0
  326. package/src/platform/client.ts +1 -1
  327. package/src/prompts/system-prompt.ts +18 -21
  328. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
  329. package/src/prompts/templates/BOOTSTRAP.md +59 -96
  330. package/src/prompts/templates/SOUL.md +11 -11
  331. package/src/providers/anthropic/client.ts +1 -0
  332. package/src/providers/types.ts +1 -1
  333. package/src/runtime/AGENTS.md +23 -0
  334. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
  335. package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
  336. package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
  337. package/src/runtime/assistant-event-hub.ts +24 -2
  338. package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
  339. package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
  340. package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
  341. package/src/runtime/auth/middleware.ts +98 -0
  342. package/src/runtime/auth/route-policy.ts +6 -7
  343. package/src/runtime/auth/token-service.ts +8 -0
  344. package/src/runtime/capability-tokens.ts +414 -0
  345. package/src/runtime/channel-approvals.ts +18 -5
  346. package/src/runtime/chrome-extension-registry.ts +332 -0
  347. package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
  348. package/src/runtime/guardian-decision-types.ts +7 -0
  349. package/src/runtime/http-server.ts +425 -70
  350. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
  351. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
  352. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
  353. package/src/runtime/migrations/migration-transport.ts +6 -0
  354. package/src/runtime/migrations/migration-wizard.ts +22 -2
  355. package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
  356. package/src/runtime/migrations/vbundle-builder.ts +145 -38
  357. package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
  358. package/src/runtime/migrations/vbundle-importer.ts +55 -5
  359. package/src/runtime/pending-interactions.ts +29 -13
  360. package/src/runtime/routes/approval-routes.ts +90 -16
  361. package/src/runtime/routes/browser-cdp-routes.ts +229 -0
  362. package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
  363. package/src/runtime/routes/conversation-analysis-routes.ts +18 -5
  364. package/src/runtime/routes/conversation-management-routes.ts +108 -0
  365. package/src/runtime/routes/conversation-routes.ts +308 -28
  366. package/src/runtime/routes/conversation-starter-routes.ts +78 -16
  367. package/src/runtime/routes/group-routes.ts +22 -8
  368. package/src/runtime/routes/guardian-action-routes.ts +24 -13
  369. package/src/runtime/routes/host-browser-routes.ts +279 -0
  370. package/src/runtime/routes/host-file-routes.ts +9 -1
  371. package/src/runtime/routes/identity-routes.ts +259 -16
  372. package/src/runtime/routes/log-export/AGENTS.md +104 -0
  373. package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
  374. package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
  375. package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
  376. package/src/runtime/routes/log-export-routes.ts +60 -25
  377. package/src/runtime/routes/memory-item-routes.ts +1 -7
  378. package/src/runtime/routes/migration-routes.ts +87 -2
  379. package/src/runtime/routes/oauth-apps.ts +15 -17
  380. package/src/runtime/routes/oauth-providers.ts +4 -0
  381. package/src/runtime/routes/schedule-routes.ts +24 -11
  382. package/src/runtime/routes/settings-routes.ts +9 -97
  383. package/src/runtime/routes/skills-routes.ts +52 -2
  384. package/src/runtime/routes/subagents-routes.ts +14 -10
  385. package/src/runtime/routes/usage-routes.ts +8 -7
  386. package/src/runtime/routes/workspace-routes.test.ts +22 -0
  387. package/src/runtime/routes/workspace-routes.ts +8 -1
  388. package/src/runtime/routes/workspace-utils.ts +2 -0
  389. package/src/schedule/scheduler.ts +7 -5
  390. package/src/security/ces-credential-client.ts +20 -0
  391. package/src/security/ces-rpc-credential-backend.ts +17 -0
  392. package/src/security/credential-backend.ts +5 -0
  393. package/src/security/oauth2.ts +42 -25
  394. package/src/security/secure-keys.ts +118 -25
  395. package/src/security/token-manager.ts +23 -10
  396. package/src/skills/catalog-files.ts +492 -0
  397. package/src/skills/inline-command-runner.ts +12 -14
  398. package/src/subagent/manager.ts +131 -26
  399. package/src/subagent/types.ts +19 -0
  400. package/src/tools/apps/executors.ts +11 -2
  401. package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
  402. package/src/tools/browser/auth-detector.ts +43 -12
  403. package/src/tools/browser/browser-execution.ts +645 -340
  404. package/src/tools/browser/browser-manager.ts +36 -12
  405. package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
  406. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
  407. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
  408. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
  409. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
  410. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
  411. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
  412. package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
  413. package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
  414. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
  415. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
  416. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
  417. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
  418. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
  419. package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
  420. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
  421. package/src/tools/browser/cdp-client/errors.ts +34 -0
  422. package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
  423. package/src/tools/browser/cdp-client/factory.ts +204 -0
  424. package/src/tools/browser/cdp-client/index.ts +14 -0
  425. package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
  426. package/src/tools/browser/cdp-client/types.ts +52 -0
  427. package/src/tools/filesystem/edit.ts +1 -1
  428. package/src/tools/filesystem/list.ts +1 -1
  429. package/src/tools/filesystem/read.ts +1 -1
  430. package/src/tools/filesystem/write.ts +2 -1
  431. package/src/tools/host-filesystem/edit.ts +1 -1
  432. package/src/tools/host-filesystem/read.ts +12 -15
  433. package/src/tools/host-filesystem/write.ts +1 -1
  434. package/src/tools/host-terminal/host-shell.ts +21 -16
  435. package/src/tools/permission-checker.ts +77 -100
  436. package/src/tools/registry.ts +0 -2
  437. package/src/tools/secret-detection-handler.ts +34 -1
  438. package/src/tools/shared/filesystem/image-read.ts +61 -40
  439. package/src/tools/skills/sandbox-runner.ts +3 -6
  440. package/src/tools/subagent/spawn.ts +47 -3
  441. package/src/tools/subagent/status.ts +2 -0
  442. package/src/tools/system/register.ts +2 -16
  443. package/src/tools/terminal/safe-env.ts +7 -0
  444. package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
  445. package/src/tools/terminal/sandbox.ts +4 -1
  446. package/src/tools/terminal/shell.ts +24 -21
  447. package/src/tools/tool-approval-handler.ts +48 -2
  448. package/src/tools/types.ts +2 -3
  449. package/src/util/platform.ts +14 -19
  450. package/src/watcher/provider-types.ts +1 -1
  451. package/src/workspace/migrations/029-seed-pkb.ts +1 -0
  452. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
  453. package/src/workspace/migrations/registry.ts +2 -0
  454. package/src/workspace/top-level-renderer.ts +19 -1
  455. package/src/__tests__/chrome-cdp.test.ts +0 -419
  456. package/src/__tests__/permission-mode-sse.test.ts +0 -418
  457. package/src/__tests__/permission-mode-store.test.ts +0 -277
  458. package/src/browser-extension-relay/protocol.ts +0 -63
  459. package/src/browser-extension-relay/server.ts +0 -203
  460. package/src/config/schemas/sandbox.ts +0 -14
  461. package/src/permissions/permission-mode-store.ts +0 -180
  462. package/src/tools/browser/chrome-cdp.ts +0 -239
  463. package/src/tools/system/set-permission-mode.ts +0 -103
@@ -154,6 +154,12 @@ export { migrateStripThinkingFromConsolidated } from "./209-strip-thinking-from-
154
154
  export { migrateScheduleReuseConversation } from "./210-schedule-reuse-conversation.js";
155
155
  export { migrateMemoryRecallLogsQueryContext } from "./211-memory-recall-logs-query-context.js";
156
156
  export { migrateLlmRequestLogsCreatedAtIndex } from "./212-llm-request-logs-created-at-index.js";
157
+ export { migrateOAuthProvidersScopeSeparator } from "./213-oauth-providers-scope-separator.js";
158
+ export { migrateOAuthProvidersRefreshUrl } from "./214-oauth-providers-refresh-url.js";
159
+ export { migrateOAuthProvidersRevoke } from "./215-oauth-providers-revoke.js";
160
+ export { migrateOAuthProvidersTokenAuthMethodDefault } from "./216-oauth-providers-token-auth-method.js";
161
+ export { migrateConversationHostAccess } from "./217-conversation-host-access.js";
162
+ export { migrateOAuthProvidersLogoUrl } from "./218-oauth-providers-logo-url.js";
157
163
  export {
158
164
  MIGRATION_REGISTRY,
159
165
  type MigrationRegistryEntry,
@@ -41,6 +41,7 @@ import { migrateAddSourceTypeColumnsDown } from "./193-add-source-type-columns.j
41
41
  import { migrateStripIntegrationPrefixFromProviderKeysDown } from "./196-strip-integration-prefix-from-provider-keys.js";
42
42
  import { migrateRenameMemoryGraphTypeValuesDown } from "./204-rename-memory-graph-type-values.js";
43
43
  import { migrateScrubCorruptedImageAttachmentsDown } from "./206-scrub-corrupted-image-attachments.js";
44
+ import { downConversationHostAccess } from "./217-conversation-host-access.js";
44
45
 
45
46
  export interface MigrationRegistryEntry {
46
47
  /** The checkpoint key written to memory_checkpoints on completion. */
@@ -357,6 +358,13 @@ export const MIGRATION_REGISTRY: MigrationRegistryEntry[] = [
357
358
  "Remove image attachments containing HTML error pages instead of image data",
358
359
  down: migrateScrubCorruptedImageAttachmentsDown,
359
360
  },
361
+ {
362
+ key: "migration_conversation_host_access_v1",
363
+ version: 41,
364
+ description:
365
+ "Add a host_access column to conversations so computer access is persisted per conversation with a safe default of disabled",
366
+ down: downConversationHostAccess,
367
+ },
360
368
  ];
361
369
 
362
370
  export function getMaxMigrationVersion(): number {
@@ -28,6 +28,7 @@ export const conversations = sqliteTable(
28
28
  originInterface: text("origin_interface"),
29
29
  forkParentConversationId: text("fork_parent_conversation_id"),
30
30
  forkParentMessageId: text("fork_parent_message_id"),
31
+ hostAccess: integer("host_access").notNull().default(0),
31
32
  isAutoTitle: integer("is_auto_title").notNull().default(1),
32
33
  scheduleJobId: text("schedule_job_id"),
33
34
  lastMessageAt: integer("last_message_at"),
@@ -7,24 +7,31 @@ import {
7
7
  } from "drizzle-orm/sqlite-core";
8
8
 
9
9
  export const oauthProviders = sqliteTable("oauth_providers", {
10
- providerKey: text("provider_key").primaryKey(),
11
- authUrl: text("auth_url").notNull(),
12
- tokenUrl: text("token_url").notNull(),
13
- tokenEndpointAuthMethod: text("token_endpoint_auth_method"),
10
+ provider: text("provider_key").primaryKey(),
11
+ authorizeUrl: text("auth_url").notNull(),
12
+ tokenExchangeUrl: text("token_url").notNull(),
13
+ refreshUrl: text("refresh_url"),
14
+ tokenEndpointAuthMethod: text("token_endpoint_auth_method")
15
+ .notNull()
16
+ .default("client_secret_post"),
14
17
  userinfoUrl: text("userinfo_url"),
15
18
  baseUrl: text("base_url"),
16
19
  defaultScopes: text("default_scopes").notNull().default("[]"),
17
20
  scopePolicy: text("scope_policy").notNull().default("{}"),
18
- extraParams: text("extra_params"),
21
+ scopeSeparator: text("scope_separator").notNull().default(" "),
22
+ authorizeParams: text("extra_params"),
19
23
  pingUrl: text("ping_url"),
20
24
  pingMethod: text("ping_method"),
21
25
  pingHeaders: text("ping_headers"),
22
26
  pingBody: text("ping_body"),
27
+ revokeUrl: text("revoke_url"),
28
+ revokeBodyTemplate: text("revoke_body_template"),
23
29
  managedServiceConfigKey: text("managed_service_config_key"),
24
- displayName: text("display_name"),
30
+ displayLabel: text("display_name"),
25
31
  description: text("description"),
26
32
  dashboardUrl: text("dashboard_url"),
27
33
  clientIdPlaceholder: text("client_id_placeholder"),
34
+ logoUrl: text("logo_url"),
28
35
  requiresClientSecret: integer("requires_client_secret").notNull().default(1),
29
36
  loopbackPort: integer("loopback_port"),
30
37
  injectionTemplates: text("injection_templates"),
@@ -46,9 +53,9 @@ export const oauthApps = sqliteTable(
46
53
  "oauth_apps",
47
54
  {
48
55
  id: text("id").primaryKey(),
49
- providerKey: text("provider_key")
56
+ provider: text("provider_key")
50
57
  .notNull()
51
- .references(() => oauthProviders.providerKey),
58
+ .references(() => oauthProviders.provider),
52
59
  clientId: text("client_id").notNull(),
53
60
  clientSecretCredentialPath: text("client_secret_credential_path").notNull(),
54
61
  createdAt: integer("created_at").notNull(),
@@ -56,7 +63,7 @@ export const oauthApps = sqliteTable(
56
63
  },
57
64
  (table) => [
58
65
  uniqueIndex("idx_oauth_apps_provider_client").on(
59
- table.providerKey,
66
+ table.provider,
60
67
  table.clientId,
61
68
  ),
62
69
  ],
@@ -69,7 +76,7 @@ export const oauthConnections = sqliteTable(
69
76
  oauthAppId: text("oauth_app_id")
70
77
  .notNull()
71
78
  .references(() => oauthApps.id),
72
- providerKey: text("provider_key").notNull(),
79
+ provider: text("provider_key").notNull(),
73
80
  accountInfo: text("account_info"),
74
81
  grantedScopes: text("granted_scopes").notNull().default("[]"),
75
82
  expiresAt: integer("expires_at"),
@@ -80,7 +87,5 @@ export const oauthConnections = sqliteTable(
80
87
  createdAt: integer("created_at").notNull(),
81
88
  updatedAt: integer("updated_at").notNull(),
82
89
  },
83
- (table) => [
84
- index("idx_oauth_connections_provider_key").on(table.providerKey),
85
- ],
90
+ (table) => [index("idx_oauth_connections_provider_key").on(table.provider)],
86
91
  );
@@ -25,7 +25,7 @@ export interface MessagingProvider {
25
25
  id: string;
26
26
  /** Human-readable name (e.g. 'Slack', 'Gmail'). */
27
27
  displayName: string;
28
- /** Credential service name for token-manager (e.g. 'integration:slack'). */
28
+ /** Credential service name for token-manager (e.g. 'slack'). */
29
29
  credentialService: string;
30
30
 
31
31
  // ── Universal operations (every platform must implement) ──────────
@@ -41,6 +41,10 @@ export interface ConversationCreatedInfo {
41
41
  sourceEventName: string;
42
42
  /** Present when the conversation is for a guardian-sensitive notification. */
43
43
  targetGuardianPrincipalId?: string;
44
+ /** Conversation group identifier from the signal producer (e.g. "system:scheduled"). */
45
+ groupId?: string;
46
+ /** Semantic source from the signal producer (e.g. "schedule", "reminder"). */
47
+ source?: string;
44
48
  }
45
49
  export type OnConversationCreatedFn = (info: ConversationCreatedInfo) => void;
46
50
  export interface BroadcastDecisionOptions {
@@ -238,6 +242,8 @@ export class NotificationBroadcaster {
238
242
  title: conversationTitle,
239
243
  sourceEventName: signal.sourceEventName,
240
244
  targetGuardianPrincipalId,
245
+ groupId: signal.conversationMetadata?.groupId,
246
+ source: signal.conversationMetadata?.source,
241
247
  };
242
248
 
243
249
  // The per-dispatch onConversationCreated callback fires whenever a vellum
@@ -130,7 +130,9 @@ export async function pairDeliveryWithConversation(
130
130
  const targetId = conversationAction.conversationId;
131
131
  const existing = getConversation(targetId);
132
132
 
133
- if (existing && existing.source === "notification") {
133
+ const effectiveSource =
134
+ signal.conversationMetadata?.source ?? "notification";
135
+ if (existing && existing.source === effectiveSource) {
134
136
  // Append the seed message to the existing conversation
135
137
  const message = await addMessage(
136
138
  existing.id,
@@ -186,7 +188,9 @@ export async function pairDeliveryWithConversation(
186
188
  const conversation = createConversation({
187
189
  title,
188
190
  conversationType,
189
- source: "notification",
191
+ source: signal.conversationMetadata?.source ?? "notification",
192
+ groupId: signal.conversationMetadata?.groupId,
193
+ scheduleJobId: signal.conversationMetadata?.scheduleJobId,
190
194
  });
191
195
 
192
196
  const message = await addMessage(
@@ -241,7 +245,9 @@ export async function pairDeliveryWithConversation(
241
245
  existingBinding.conversationId,
242
246
  );
243
247
 
244
- if (boundConversation && boundConversation.source === "notification") {
248
+ const effectiveSource =
249
+ signal.conversationMetadata?.source ?? "notification";
250
+ if (boundConversation && boundConversation.source === effectiveSource) {
245
251
  const message = await addMessage(
246
252
  boundConversation.id,
247
253
  "assistant",
@@ -299,7 +305,9 @@ export async function pairDeliveryWithConversation(
299
305
  const conversation = createConversation({
300
306
  title,
301
307
  conversationType,
302
- source: "notification",
308
+ source: signal.conversationMetadata?.source ?? "notification",
309
+ groupId: signal.conversationMetadata?.groupId,
310
+ scheduleJobId: signal.conversationMetadata?.scheduleJobId,
303
311
  });
304
312
 
305
313
  // Skip memory indexing — notification audit messages are not conversational
@@ -79,6 +79,8 @@ function getBroadcaster(): NotificationBroadcaster {
79
79
  title: info.title,
80
80
  sourceEventName: info.sourceEventName,
81
81
  targetGuardianPrincipalId: info.targetGuardianPrincipalId,
82
+ groupId: info.groupId,
83
+ source: info.source,
82
84
  });
83
85
  log.info(
84
86
  {
@@ -176,6 +178,17 @@ export interface EmitSignalParams<TEventName extends string = string> {
176
178
  * Useful for direct user-invoked actions that must fail closed.
177
179
  */
178
180
  throwOnError?: boolean;
181
+ /**
182
+ * Optional metadata propagated to the conversation created by the notification
183
+ * pipeline. Allows signal producers (e.g. the scheduler) to set groupId,
184
+ * scheduleJobId, or override the default "notification" source on the
185
+ * resulting conversation so it appears in the correct folder on clients.
186
+ */
187
+ conversationMetadata?: {
188
+ groupId?: string;
189
+ scheduleJobId?: string;
190
+ source?: string;
191
+ };
179
192
  }
180
193
 
181
194
  export interface EmitSignalResult {
@@ -210,6 +223,7 @@ export async function emitNotificationSignal<TEventName extends string>(
210
223
  routingIntent: params.routingIntent,
211
224
  routingHints: params.routingHints,
212
225
  conversationAffinityHint: params.conversationAffinityHint,
226
+ conversationMetadata: params.conversationMetadata,
213
227
  };
214
228
 
215
229
  try {
@@ -200,4 +200,15 @@ export interface NotificationSignal<TEventName extends string = string> {
200
200
  * affinity within a call session.
201
201
  */
202
202
  conversationAffinityHint?: Partial<Record<string, string>>;
203
+ /**
204
+ * Optional metadata propagated to the conversation created by the notification
205
+ * pipeline. Allows signal producers (e.g. the scheduler) to set groupId,
206
+ * scheduleJobId, or override the default "notification" source on the
207
+ * resulting conversation so it appears in the correct folder on clients.
208
+ */
209
+ conversationMetadata?: {
210
+ groupId?: string;
211
+ scheduleJobId?: string;
212
+ source?: string;
213
+ };
203
214
  }
@@ -0,0 +1,76 @@
1
+ # OAuth — Agent Instructions
2
+
3
+ ## Adding a New First-Class Provider
4
+
5
+ When introducing a new built-in OAuth integration (one that appears in `seed-providers.ts`), touch each of the following areas. Items marked _(managed only)_ apply only when the provider supports platform-provided credentials.
6
+
7
+ ### 1. Seed the provider — `seed-providers.ts`
8
+
9
+ Add an entry to `PROVIDER_SEED_DATA`. Required fields: `provider`, `authorizeUrl`, `tokenExchangeUrl`, `defaultScopes`, `scopePolicy`, `displayLabel`, `description`, `dashboardUrl`, `clientIdPlaceholder`, `logoUrl`, and `injectionTemplates`. See existing entries for the full shape. The `provider` key must be snake_case and is used as the canonical identifier everywhere else.
10
+
11
+ If the provider will support managed mode, set `managedServiceConfigKey` to a slug matching the key you will add to `ServicesSchema` (e.g. `"acme-oauth"`).
12
+
13
+ ### 2. _(managed only)_ Add a service schema — `../config/schemas/services.ts`
14
+
15
+ Create a schema and export its type:
16
+
17
+ ```ts
18
+ export const AcmeOAuthServiceSchema = BaseServiceSchema.extend({
19
+ mode: ServiceModeSchema.default("your-own"),
20
+ });
21
+ export type AcmeOAuthService = z.infer<typeof AcmeOAuthServiceSchema>;
22
+ ```
23
+
24
+ Then add the key to `ServicesSchema`:
25
+
26
+ ```ts
27
+ "acme-oauth": AcmeOAuthServiceSchema.default(AcmeOAuthServiceSchema.parse({})),
28
+ ```
29
+
30
+ The key here **must** match the `managedServiceConfigKey` in `seed-providers.ts`. The cross-repo invariant test in `__tests__/seed-providers-managed.test.ts` will fail if they drift.
31
+
32
+ ### 3. _(managed only)_ Enable by default during onboarding — `clients/macos/.../HatchingStepView.swift`
33
+
34
+ In `buildOnboardingConfigValues()`, add a line so managed-sign-in users get the integration pre-enabled:
35
+
36
+ ```swift
37
+ configValues["services.acme-oauth.mode"] = "managed"
38
+ ```
39
+
40
+ ### 4. Add a cached logo — `clients/shared/Resources/IntegrationLogos/`
41
+
42
+ Drop a **vector PDF** named `{provider_key}.pdf` (e.g. `acme.pdf`) into the `IntegrationLogos/` directory. The file is automatically bundled via `.copy()` in `clients/Package.swift` and looked up at runtime by `IntegrationLogoBundle` using the provider key — no code changes needed.
43
+
44
+ Most existing logos come from [Simple Icons](https://simpleicons.org) (CC0-licensed). To get a PDF from Simple Icons:
45
+
46
+ 1. Find the icon slug on https://simpleicons.org (e.g. `slack`, `linear`).
47
+ 2. Download the SVG: `curl -o acme.svg https://raw.githubusercontent.com/simple-icons/simple-icons/develop/icons/acme.svg`
48
+ 3. Convert to PDF using `rsvg-convert` (same tool used by `clients/scripts/sync-lucide-icons.sh`):
49
+ ```bash
50
+ # Install if needed: brew install librsvg
51
+ rsvg-convert -f pdf -o clients/shared/Resources/IntegrationLogos/acme.pdf acme.svg
52
+ ```
53
+ 4. Add the provider key to `clients/shared/Resources/integration-logos-manifest.json`.
54
+
55
+ If the service is not on Simple Icons, source or create an SVG and convert it the same way. The result must be a true vector PDF (not a rasterized image wrapped in PDF) so it scales cleanly.
56
+
57
+ The `logoUrl` field in `seed-providers.ts` still serves as the remote fallback (typically a Simple Icons CDN URL like `https://cdn.simpleicons.org/acme`). The client renders the local PDF first, then falls back to `logoUrl`, then to an initials avatar.
58
+
59
+ ### 5. Secret patterns (if applicable) — `../security/secret-patterns.ts`
60
+
61
+ If the provider issues API keys with a recognizable prefix (e.g. `acme_sk_`), add a `PREFIX_PATTERNS` entry. OAuth-only services with opaque access tokens do not need one. See `../security/AGENTS.md` for details.
62
+
63
+ ### 6. Feature-flag gating (optional) — `seed-providers.ts`
64
+
65
+ Set `featureFlag: "acme-oauth"` in the seed entry and register the flag in `meta/feature-flags/feature-flag-registry.json` to hide the provider until the flag is enabled. Omit `featureFlag` to make the provider visible immediately.
66
+
67
+ ### What you do NOT need to change
68
+
69
+ The following are wired automatically once `PROVIDER_SEED_DATA` has an entry:
70
+
71
+ - **Connection resolver** (`connection-resolver.ts`) — routes managed vs. BYO based on config.
72
+ - **CLI commands** (`../cli/commands/oauth/`) — `providers list`, `providers get`, `connect`, `disconnect`, etc.
73
+ - **Runtime API** (`../runtime/routes/oauth-providers.ts`) — `GET /v1/oauth/providers` and related endpoints.
74
+ - **Gateway proxy** (`gateway/src/http/routes/oauth-providers-proxy.ts`) — forwards to the runtime.
75
+ - **OAuth store** (`oauth-store.ts`) — seeding uses upsert; schema already supports arbitrary providers.
76
+ - **Provider serialization** (`provider-serializer.ts`) — generic over all providers.
@@ -24,29 +24,34 @@ afterEach(() => {
24
24
  // ---------------------------------------------------------------------------
25
25
 
26
26
  function makeProviderRow(
27
- overrides: Partial<OAuthProviderRow> & { providerKey: string },
27
+ overrides: Partial<OAuthProviderRow> & { provider: string },
28
28
  ): OAuthProviderRow {
29
29
  const now = Date.now();
30
- const { providerKey, ...rest } = overrides;
30
+ const { provider, ...rest } = overrides;
31
31
  return {
32
- providerKey,
33
- authUrl: "https://example.com/auth",
34
- tokenUrl: "https://example.com/token",
35
- tokenEndpointAuthMethod: null,
32
+ provider,
33
+ authorizeUrl: "https://example.com/auth",
34
+ tokenExchangeUrl: "https://example.com/token",
35
+ refreshUrl: null,
36
+ tokenEndpointAuthMethod: "client_secret_post",
36
37
  userinfoUrl: null,
37
38
  baseUrl: null,
38
39
  defaultScopes: "[]",
39
40
  scopePolicy: "{}",
40
- extraParams: null,
41
+ scopeSeparator: " ",
42
+ authorizeParams: null,
41
43
  pingUrl: null,
42
44
  pingMethod: null,
43
45
  pingHeaders: null,
44
46
  pingBody: null,
47
+ revokeUrl: null,
48
+ revokeBodyTemplate: null,
45
49
  managedServiceConfigKey: null,
46
- displayName: null,
50
+ displayLabel: null,
47
51
  description: null,
48
52
  dashboardUrl: null,
49
53
  clientIdPlaceholder: null,
54
+ logoUrl: null,
50
55
  requiresClientSecret: 1,
51
56
  loopbackPort: null,
52
57
  injectionTemplates: null,
@@ -82,7 +87,7 @@ describe("verifyIdentity", () => {
82
87
  // Missing identity URL
83
88
  // -----------------------------------------------------------------------
84
89
  test("returns undefined when identityUrl is null", async () => {
85
- const row = makeProviderRow({ providerKey: "custom" });
90
+ const row = makeProviderRow({ provider: "custom" });
86
91
  const result = await verifyIdentity(row, "token-abc");
87
92
  expect(result).toBeUndefined();
88
93
  expect(mockFetch).not.toHaveBeenCalled();
@@ -93,7 +98,7 @@ describe("verifyIdentity", () => {
93
98
  // -----------------------------------------------------------------------
94
99
  describe("Google pattern", () => {
95
100
  const googleRow = makeProviderRow({
96
- providerKey: "google",
101
+ provider: "google",
97
102
  identityUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
98
103
  identityResponsePaths: JSON.stringify(["email"]),
99
104
  });
@@ -127,7 +132,7 @@ describe("verifyIdentity", () => {
127
132
  // -----------------------------------------------------------------------
128
133
  describe("Slack pattern", () => {
129
134
  const slackRow = makeProviderRow({
130
- providerKey: "slack",
135
+ provider: "slack",
131
136
  identityUrl: "https://slack.com/api/auth.test",
132
137
  identityOkField: "ok",
133
138
  identityResponsePaths: JSON.stringify(["user", "team"]),
@@ -176,7 +181,7 @@ describe("verifyIdentity", () => {
176
181
  // -----------------------------------------------------------------------
177
182
  describe("HubSpot pattern", () => {
178
183
  const hubspotRow = makeProviderRow({
179
- providerKey: "hubspot",
184
+ provider: "hubspot",
180
185
  identityUrl:
181
186
  "https://api.hubapi.com/oauth/v1/access-tokens/${accessToken}",
182
187
  identityResponsePaths: JSON.stringify(["user", "hub_domain"]),
@@ -219,7 +224,7 @@ describe("verifyIdentity", () => {
219
224
  // -----------------------------------------------------------------------
220
225
  describe("Linear pattern", () => {
221
226
  const linearRow = makeProviderRow({
222
- providerKey: "linear",
227
+ provider: "linear",
223
228
  identityUrl: "https://api.linear.app/graphql",
224
229
  identityMethod: "POST",
225
230
  identityHeaders: JSON.stringify({ "Content-Type": "application/json" }),
@@ -272,7 +277,7 @@ describe("verifyIdentity", () => {
272
277
  // -----------------------------------------------------------------------
273
278
  describe("Todoist pattern", () => {
274
279
  const todoistRow = makeProviderRow({
275
- providerKey: "todoist",
280
+ provider: "todoist",
276
281
  identityUrl: "https://api.todoist.com/sync/v9/sync",
277
282
  identityMethod: "POST",
278
283
  identityHeaders: JSON.stringify({
@@ -317,7 +322,7 @@ describe("verifyIdentity", () => {
317
322
  // -----------------------------------------------------------------------
318
323
  describe("Twitter pattern", () => {
319
324
  const twitterRow = makeProviderRow({
320
- providerKey: "twitter",
325
+ provider: "twitter",
321
326
  identityUrl: "https://api.x.com/2/users/me",
322
327
  identityResponsePaths: JSON.stringify(["data.username"]),
323
328
  identityFormat: "@${data.username}",
@@ -338,7 +343,7 @@ describe("verifyIdentity", () => {
338
343
  // -----------------------------------------------------------------------
339
344
  describe("GitHub pattern", () => {
340
345
  const githubRow = makeProviderRow({
341
- providerKey: "github",
346
+ provider: "github",
342
347
  identityUrl: "https://api.github.com/user",
343
348
  identityResponsePaths: JSON.stringify(["login"]),
344
349
  identityFormat: "@${login}",
@@ -357,7 +362,7 @@ describe("verifyIdentity", () => {
357
362
  // -----------------------------------------------------------------------
358
363
  describe("Notion pattern", () => {
359
364
  const notionRow = makeProviderRow({
360
- providerKey: "notion",
365
+ provider: "notion",
361
366
  identityUrl: "https://api.notion.com/v1/users/me",
362
367
  identityHeaders: JSON.stringify({ "Notion-Version": "2022-06-28" }),
363
368
  identityResponsePaths: JSON.stringify(["name", "person.email"]),
@@ -392,7 +397,7 @@ describe("verifyIdentity", () => {
392
397
  // -----------------------------------------------------------------------
393
398
  describe("error handling", () => {
394
399
  const googleRow = makeProviderRow({
395
- providerKey: "google",
400
+ provider: "google",
396
401
  identityUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
397
402
  identityResponsePaths: JSON.stringify(["email"]),
398
403
  });
@@ -431,7 +436,7 @@ describe("verifyIdentity", () => {
431
436
  // -----------------------------------------------------------------------
432
437
  describe("Dropbox pattern", () => {
433
438
  const dropboxRow = makeProviderRow({
434
- providerKey: "dropbox",
439
+ provider: "dropbox",
435
440
  identityUrl: "https://api.dropboxapi.com/2/users/get_current_account",
436
441
  identityMethod: "POST",
437
442
  identityResponsePaths: JSON.stringify(["name.display_name", "email"]),
@@ -0,0 +1,32 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { ServicesSchema } from "../../config/schemas/services.js";
4
+ import { PROVIDER_SEED_DATA } from "../seed-providers.js";
5
+
6
+ describe("PROVIDER_SEED_DATA managed mode wiring", () => {
7
+ test("github provider is wired up for managed mode", () => {
8
+ const github = PROVIDER_SEED_DATA.github;
9
+ expect(github).toBeDefined();
10
+ expect(github.managedServiceConfigKey).toBe("github-oauth");
11
+ expect("github-oauth" in ServicesSchema.shape).toBe(true);
12
+ });
13
+
14
+ test("every managedServiceConfigKey resolves to a ServicesSchema key", () => {
15
+ // Cross-repo invariant: a provider with managedServiceConfigKey but no
16
+ // matching ServicesSchema entry silently falls back to BYO mode in
17
+ // connection-resolver.ts. This test guards against that drift.
18
+ const offenders: Array<{ provider: string; key: string }> = [];
19
+ for (const [provider, seed] of Object.entries(PROVIDER_SEED_DATA)) {
20
+ const key = seed.managedServiceConfigKey;
21
+ if (key && !(key in ServicesSchema.shape)) {
22
+ offenders.push({ provider, key });
23
+ }
24
+ }
25
+ expect(offenders).toEqual([]);
26
+ });
27
+
28
+ test("github managed service schema defaults to your-own", () => {
29
+ const parsed = ServicesSchema.shape["github-oauth"].parse({});
30
+ expect(parsed.mode).toBe("your-own");
31
+ });
32
+ });
@@ -72,7 +72,7 @@ const mockConnections = new Map<
72
72
  string,
73
73
  {
74
74
  id: string;
75
- providerKey: string;
75
+ provider: string;
76
76
  oauthAppId: string;
77
77
  expiresAt: number | null;
78
78
  grantedScopes?: string;
@@ -83,7 +83,7 @@ const mockApps = new Map<
83
83
  string,
84
84
  {
85
85
  id: string;
86
- providerKey: string;
86
+ provider: string;
87
87
  clientId: string;
88
88
  clientSecretCredentialPath: string;
89
89
  }
@@ -92,7 +92,7 @@ const mockProviders = new Map<
92
92
  string,
93
93
  {
94
94
  key: string;
95
- tokenUrl: string;
95
+ tokenExchangeUrl: string;
96
96
  tokenEndpointAuthMethod?: string;
97
97
  baseUrl?: string;
98
98
  }
@@ -192,7 +192,7 @@ async function setupCredential(
192
192
  const connId = `conn-${service}`;
193
193
  mockProviders.set(service, {
194
194
  key: service,
195
- tokenUrl: "https://oauth2.googleapis.com/token",
195
+ tokenExchangeUrl: "https://oauth2.googleapis.com/token",
196
196
  // Only well-known providers (gmail) have a baseUrl; custom services don't
197
197
  baseUrl:
198
198
  service === "google"
@@ -201,13 +201,13 @@ async function setupCredential(
201
201
  });
202
202
  mockApps.set(appId, {
203
203
  id: appId,
204
- providerKey: service,
204
+ provider: service,
205
205
  clientId: "test-client-id",
206
206
  clientSecretCredentialPath: `oauth_app/${appId}/client_secret`,
207
207
  });
208
208
  mockConnections.set(service, {
209
209
  id: connId,
210
- providerKey: service,
210
+ provider: service,
211
211
  oauthAppId: appId,
212
212
  expiresAt: opts?.expiresAt ?? Date.now() + 3600 * 1000,
213
213
  grantedScopes: JSON.stringify(opts?.grantedScopes ?? ["read", "write"]),
@@ -233,7 +233,7 @@ async function setupCredential(
233
233
  function createConnection(service = "google"): BYOOAuthConnection {
234
234
  return new BYOOAuthConnection({
235
235
  id: `conn-${service}`,
236
- providerKey: service,
236
+ provider: service,
237
237
  baseUrl: "https://gmail.googleapis.com/gmail/v1/users/me",
238
238
  accountInfo: null,
239
239
  });
@@ -497,7 +497,7 @@ describe("resolveOAuthConnection", () => {
497
497
  const conn = await resolveOAuthConnection("google");
498
498
 
499
499
  expect(conn).toBeInstanceOf(BYOOAuthConnection);
500
- expect(conn.providerKey).toBe("google");
500
+ expect(conn.provider).toBe("google");
501
501
  });
502
502
 
503
503
  test("throws when no credential metadata exists", async () => {
@@ -22,28 +22,28 @@ const REQUEST_TIMEOUT_MS = 30_000;
22
22
 
23
23
  export interface BYOOAuthConnectionOptions {
24
24
  id: string;
25
- providerKey: string;
25
+ provider: string;
26
26
  baseUrl: string;
27
27
  accountInfo: string | null;
28
28
  }
29
29
 
30
30
  export class BYOOAuthConnection implements OAuthConnection {
31
31
  readonly id: string;
32
- readonly providerKey: string;
32
+ readonly provider: string;
33
33
  readonly accountInfo: string | null;
34
34
 
35
35
  private readonly baseUrl: string;
36
36
 
37
37
  constructor(opts: BYOOAuthConnectionOptions) {
38
38
  this.id = opts.id;
39
- this.providerKey = opts.providerKey;
39
+ this.provider = opts.provider;
40
40
  this.baseUrl = opts.baseUrl;
41
41
  this.accountInfo = opts.accountInfo;
42
42
  }
43
43
 
44
44
  async request(req: OAuthConnectionRequest): Promise<OAuthConnectionResponse> {
45
45
  return withValidToken(
46
- this.providerKey,
46
+ this.provider,
47
47
  async (token) => {
48
48
  const effectiveBaseUrl = req.baseUrl ?? this.baseUrl;
49
49
  let fullUrl = `${effectiveBaseUrl}${req.path}`;
@@ -61,7 +61,7 @@ export class BYOOAuthConnection implements OAuthConnection {
61
61
  }
62
62
 
63
63
  log.debug(
64
- { method: req.method, url: fullUrl, provider: this.providerKey },
64
+ { method: req.method, url: fullUrl, provider: this.provider },
65
65
  "Making authenticated request",
66
66
  );
67
67
 
@@ -88,7 +88,7 @@ export class BYOOAuthConnection implements OAuthConnection {
88
88
  if (resp.status === 401) {
89
89
  // Throw with a status property so withValidToken detects the 401
90
90
  // and triggers its refresh-and-retry logic.
91
- const err = new Error(`HTTP 401 from ${this.providerKey}`);
91
+ const err = new Error(`HTTP 401 from ${this.provider}`);
92
92
  (err as Error & { status: number }).status = 401;
93
93
  throw err;
94
94
  }
@@ -100,7 +100,7 @@ export class BYOOAuthConnection implements OAuthConnection {
100
100
  }
101
101
 
102
102
  async withToken<T>(fn: (token: string) => Promise<T>): Promise<T> {
103
- return withValidToken(this.providerKey, fn, {
103
+ return withValidToken(this.provider, fn, {
104
104
  connectionId: this.id,
105
105
  });
106
106
  }