@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.
- package/bun.lock +40 -40
- package/bunfig.toml +3 -0
- package/docker-entrypoint.sh +12 -2
- package/docs/architecture/memory.md +1 -1
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
- package/openapi.yaml +184 -69
- package/package.json +41 -41
- package/scripts/generate-openapi.ts +1 -2
- package/src/__tests__/acp-session.test.ts +43 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +1 -0
- package/src/__tests__/app-source-watcher.test.ts +37 -11
- package/src/__tests__/approval-routes-http.test.ts +178 -1
- package/src/__tests__/assistant-event-hub.test.ts +30 -0
- package/src/__tests__/browser-fill-credential.test.ts +229 -94
- package/src/__tests__/browser-manager.test.ts +40 -27
- package/src/__tests__/catalog-files.test.ts +862 -0
- package/src/__tests__/channel-approvals.test.ts +53 -0
- package/src/__tests__/checker.test.ts +104 -170
- package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
- package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +125 -48
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
- package/src/__tests__/context-overflow-approval.test.ts +21 -6
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +1 -1
- package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
- package/src/__tests__/conversation-attachments.test.ts +80 -4
- package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
- package/src/__tests__/conversation-directories-parse.test.ts +105 -0
- package/src/__tests__/conversation-fork-crud.test.ts +17 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
- package/src/__tests__/conversation-inject-context.test.ts +103 -0
- package/src/__tests__/conversation-queue.test.ts +45 -2
- package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
- package/src/__tests__/conversation-starter-routes.test.ts +126 -0
- package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
- package/src/__tests__/conversation-store.test.ts +195 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -3
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +4 -4
- package/src/__tests__/credential-vault.test.ts +152 -13
- package/src/__tests__/credentials-cli.test.ts +2 -2
- package/src/__tests__/date-context.test.ts +4 -4
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
- package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
- package/src/__tests__/gateway-only-guard.test.ts +3 -0
- package/src/__tests__/gemini-provider.test.ts +2 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
- package/src/__tests__/headless-browser-interactions.test.ts +707 -371
- package/src/__tests__/headless-browser-navigate.test.ts +389 -47
- package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
- package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
- package/src/__tests__/host-bash-proxy.test.ts +150 -1
- package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
- package/src/__tests__/host-browser-event-routes.test.ts +350 -0
- package/src/__tests__/host-browser-proxy.test.ts +444 -0
- package/src/__tests__/host-browser-routes.test.ts +198 -0
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
- package/src/__tests__/host-cu-proxy.test.ts +171 -1
- package/src/__tests__/host-file-proxy.test.ts +185 -1
- package/src/__tests__/host-file-read-tool.test.ts +52 -0
- package/src/__tests__/host-proxy-interface.test.ts +165 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -11
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
- package/src/__tests__/inline-command-runner.test.ts +7 -5
- package/src/__tests__/integration-status.test.ts +6 -7
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- package/src/__tests__/log-export-workspace.test.ts +190 -0
- package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
- package/src/__tests__/mcp-client-auth.test.ts +40 -4
- package/src/__tests__/mcp-health-check.test.ts +10 -3
- package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
- package/src/__tests__/migration-export-http.test.ts +61 -2
- package/src/__tests__/migration-export-streaming.test.ts +66 -0
- package/src/__tests__/migration-import-commit-http.test.ts +101 -1
- package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
- package/src/__tests__/navigate-settings-tab.test.ts +14 -1
- package/src/__tests__/notification-broadcaster.test.ts +65 -0
- package/src/__tests__/oauth-apps-routes.test.ts +17 -12
- package/src/__tests__/oauth-cli.test.ts +707 -60
- package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
- package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
- package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
- package/src/__tests__/oauth-providers-routes.test.ts +50 -14
- package/src/__tests__/oauth-store.test.ts +1386 -182
- package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
- package/src/__tests__/onboarding-template-contract.test.ts +74 -55
- package/src/__tests__/openai-provider.test.ts +2 -2
- package/src/__tests__/outlook-categories.test.ts +1 -1
- package/src/__tests__/outlook-client-automation.test.ts +1 -1
- package/src/__tests__/outlook-compose-tools.test.ts +1 -1
- package/src/__tests__/outlook-email-watcher.test.ts +1 -1
- package/src/__tests__/outlook-follow-up.test.ts +1 -1
- package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
- package/src/__tests__/outlook-trash.test.ts +1 -1
- package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
- package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
- package/src/__tests__/permission-mode.test.ts +28 -56
- package/src/__tests__/pkb-autoinject.test.ts +96 -0
- package/src/__tests__/platform-callback-registration.test.ts +19 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
- package/src/__tests__/proxy-approval-callback.test.ts +18 -0
- package/src/__tests__/require-fresh-approval.test.ts +40 -3
- package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
- package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
- package/src/__tests__/schedule-routes.test.ts +162 -0
- package/src/__tests__/secret-detection-handler.test.ts +84 -0
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/set-permission-mode.test.ts +13 -250
- package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
- package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
- package/src/__tests__/slack-channel-config.test.ts +12 -15
- package/src/__tests__/subagent-detail.test.ts +44 -2
- package/src/__tests__/subagent-disposal.test.ts +1 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
- package/src/__tests__/subagent-manager-notify.test.ts +1 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
- package/src/__tests__/subagent-tools.test.ts +1 -0
- package/src/__tests__/subagent-types.test.ts +1 -0
- package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
- package/src/__tests__/system-prompt.test.ts +72 -1
- package/src/__tests__/task-scheduler.test.ts +32 -6
- package/src/__tests__/telegram-config.test.ts +10 -13
- package/src/__tests__/terminal-sandbox.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +11 -5
- package/src/__tests__/test-preload.ts +14 -0
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
- package/src/__tests__/top-level-renderer.test.ts +73 -1
- package/src/__tests__/transport-hints-queue.test.ts +62 -0
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- package/src/__tests__/v2-consent-policy.test.ts +103 -0
- package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
- package/src/__tests__/workspace-policy.test.ts +2 -7
- package/src/acp/client-handler.ts +30 -4
- package/src/agent/loop.ts +12 -35
- package/src/approvals/guardian-request-resolvers.ts +21 -15
- package/src/browser-session/__tests__/manager.test.ts +297 -0
- package/src/browser-session/backends/cdp-inspect.ts +30 -0
- package/src/browser-session/backends/extension.ts +26 -0
- package/src/browser-session/backends/local.ts +24 -0
- package/src/browser-session/events.ts +164 -0
- package/src/browser-session/index.ts +27 -0
- package/src/browser-session/manager.ts +159 -0
- package/src/browser-session/types.ts +28 -0
- package/src/channels/__tests__/types.test.ts +134 -0
- package/src/channels/types.ts +55 -0
- package/src/cli/__tests__/run-assistant-command.ts +34 -7
- package/src/cli/__tests__/unknown-command.test.ts +33 -0
- package/src/cli/commands/browser-relay.ts +339 -409
- package/src/cli/commands/credentials.ts +3 -3
- package/src/cli/commands/default-action.ts +68 -1
- package/src/cli/commands/email.ts +18 -13
- package/src/cli/commands/mcp.ts +16 -4
- package/src/cli/commands/oauth/__tests__/connect.test.ts +68 -41
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
- package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
- package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
- package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
- package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
- package/src/cli/commands/oauth/apps.ts +7 -4
- package/src/cli/commands/oauth/connect.ts +16 -2
- package/src/cli/commands/oauth/disconnect.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +200 -36
- package/src/cli/commands/oauth/shared.ts +5 -5
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
- package/src/cli/commands/platform/index.ts +107 -10
- package/src/cli/commands/usage.ts +10 -9
- package/src/cli/lib/daemon-credential-client.ts +4 -0
- package/src/cli/program.ts +10 -3
- package/src/config/assistant-feature-flags.ts +59 -55
- package/src/config/bundled-skills/app-builder/SKILL.md +33 -173
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
- package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
- package/src/config/bundled-skills/contacts/SKILL.md +3 -0
- package/src/config/bundled-skills/document/SKILL.md +4 -0
- package/src/config/bundled-skills/gmail/SKILL.md +12 -7
- package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
- package/src/config/bundled-skills/outlook/SKILL.md +7 -0
- package/src/config/bundled-skills/settings/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
- package/src/config/bundled-skills/subagent/SKILL.md +21 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
- package/src/config/bundled-skills/tasks/SKILL.md +5 -0
- package/src/config/env-registry.ts +14 -0
- package/src/config/env.ts +21 -0
- package/src/config/feature-flag-registry.json +46 -7
- package/src/config/loader.ts +56 -1
- package/src/config/sanitize-for-transfer.ts +47 -0
- package/src/config/schema.ts +46 -5
- package/src/config/schemas/host-browser.ts +66 -0
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/schemas/memory-retrieval.ts +103 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +16 -0
- package/src/config/types.ts +0 -1
- package/src/context/post-turn-tool-result-truncation.ts +176 -0
- package/src/context/window-manager.ts +19 -1
- package/src/credential-execution/approval-bridge.ts +49 -16
- package/src/credential-execution/managed-catalog.ts +3 -7
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/config-watcher.ts +6 -2
- package/src/daemon/context-overflow-approval.ts +5 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
- package/src/daemon/conversation-agent-loop.ts +74 -19
- package/src/daemon/conversation-attachments.ts +40 -1
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +66 -3
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +159 -20
- package/src/daemon/conversation-surfaces.ts +78 -12
- package/src/daemon/conversation-tool-setup.ts +74 -11
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +227 -11
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/conversations.ts +9 -139
- package/src/daemon/handlers/shared.ts +65 -0
- package/src/daemon/handlers/skills.ts +232 -37
- package/src/daemon/host-bash-proxy.ts +48 -13
- package/src/daemon/host-browser-proxy.ts +191 -0
- package/src/daemon/host-cu-proxy.ts +36 -11
- package/src/daemon/host-file-proxy.ts +57 -9
- package/src/daemon/lifecycle.ts +86 -12
- package/src/daemon/message-protocol.ts +7 -0
- package/src/daemon/message-types/conversations.ts +59 -13
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/messages.ts +5 -6
- package/src/daemon/message-types/notifications.ts +12 -0
- package/src/daemon/message-types/settings.ts +12 -0
- package/src/daemon/message-types/skills.ts +10 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/server.ts +112 -35
- package/src/daemon/tool-side-effects.ts +6 -0
- package/src/daemon/transport-hints.ts +14 -0
- package/src/inbound/platform-callback-registration.ts +18 -17
- package/src/index.ts +1 -1
- package/src/mcp/client.ts +59 -24
- package/src/memory/app-store.ts +31 -1
- package/src/memory/conversation-crud.ts +38 -10
- package/src/memory/conversation-directories.ts +39 -0
- package/src/memory/conversation-group-migration.ts +65 -5
- package/src/memory/conversation-starters-cadence.ts +76 -0
- package/src/memory/conversation-title-service.ts +5 -2
- package/src/memory/db-init.ts +12 -0
- package/src/memory/embedding-backend.test.ts +75 -0
- package/src/memory/embedding-backend.ts +131 -5
- package/src/memory/embedding-gemini.test.ts +54 -0
- package/src/memory/embedding-gemini.ts +20 -9
- package/src/memory/embedding-local.ts +177 -18
- package/src/memory/graph/capability-seed.ts +3 -5
- package/src/memory/graph/consolidation.ts +10 -23
- package/src/memory/graph/extraction-job.ts +15 -0
- package/src/memory/graph/retriever.ts +40 -22
- package/src/memory/graph/store.test.ts +7 -3
- package/src/memory/graph/store.ts +47 -12
- package/src/memory/group-crud.ts +25 -9
- package/src/memory/llm-usage-store.ts +45 -4
- package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
- package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
- package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
- package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
- package/src/memory/migrations/217-conversation-host-access.ts +40 -0
- package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +1 -0
- package/src/memory/schema/oauth.ts +18 -13
- package/src/messaging/provider.ts +1 -1
- package/src/notifications/broadcaster.ts +6 -0
- package/src/notifications/conversation-pairing.ts +12 -4
- package/src/notifications/emit-signal.ts +14 -0
- package/src/notifications/signal.ts +11 -0
- package/src/oauth/AGENTS.md +76 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
- package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
- package/src/oauth/byo-connection.test.ts +8 -8
- package/src/oauth/byo-connection.ts +7 -7
- package/src/oauth/connect-orchestrator.ts +23 -21
- package/src/oauth/connect-types.ts +3 -3
- package/src/oauth/connection-resolver.test.ts +17 -4
- package/src/oauth/connection-resolver.ts +16 -16
- package/src/oauth/connection.ts +1 -1
- package/src/oauth/manual-token-connection.ts +13 -13
- package/src/oauth/oauth-store.ts +214 -100
- package/src/oauth/platform-connection.test.ts +5 -5
- package/src/oauth/platform-connection.ts +4 -4
- package/src/oauth/provider-serializer.ts +31 -5
- package/src/oauth/revoke.ts +76 -0
- package/src/oauth/seed-providers.ts +127 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/checker.ts +3 -3
- package/src/permissions/defaults.ts +7 -8
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -3
- package/src/permissions/v2-consent-policy.ts +87 -0
- package/src/platform/client.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -21
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -96
- package/src/prompts/templates/SOUL.md +11 -11
- package/src/providers/anthropic/client.ts +1 -0
- package/src/providers/types.ts +1 -1
- package/src/runtime/AGENTS.md +23 -0
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
- package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
- package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
- package/src/runtime/assistant-event-hub.ts +24 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
- package/src/runtime/auth/middleware.ts +98 -0
- package/src/runtime/auth/route-policy.ts +6 -7
- package/src/runtime/auth/token-service.ts +8 -0
- package/src/runtime/capability-tokens.ts +414 -0
- package/src/runtime/channel-approvals.ts +18 -5
- package/src/runtime/chrome-extension-registry.ts +332 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
- package/src/runtime/guardian-decision-types.ts +7 -0
- package/src/runtime/http-server.ts +425 -70
- package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
- package/src/runtime/migrations/migration-transport.ts +6 -0
- package/src/runtime/migrations/migration-wizard.ts +22 -2
- package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
- package/src/runtime/migrations/vbundle-builder.ts +145 -38
- package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
- package/src/runtime/migrations/vbundle-importer.ts +55 -5
- package/src/runtime/pending-interactions.ts +29 -13
- package/src/runtime/routes/approval-routes.ts +90 -16
- package/src/runtime/routes/browser-cdp-routes.ts +229 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
- package/src/runtime/routes/conversation-analysis-routes.ts +18 -5
- package/src/runtime/routes/conversation-management-routes.ts +108 -0
- package/src/runtime/routes/conversation-routes.ts +308 -28
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- package/src/runtime/routes/group-routes.ts +22 -8
- package/src/runtime/routes/guardian-action-routes.ts +24 -13
- package/src/runtime/routes/host-browser-routes.ts +279 -0
- package/src/runtime/routes/host-file-routes.ts +9 -1
- package/src/runtime/routes/identity-routes.ts +259 -16
- package/src/runtime/routes/log-export/AGENTS.md +104 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
- package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
- package/src/runtime/routes/log-export-routes.ts +60 -25
- package/src/runtime/routes/memory-item-routes.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +87 -2
- package/src/runtime/routes/oauth-apps.ts +15 -17
- package/src/runtime/routes/oauth-providers.ts +4 -0
- package/src/runtime/routes/schedule-routes.ts +24 -11
- package/src/runtime/routes/settings-routes.ts +9 -97
- package/src/runtime/routes/skills-routes.ts +52 -2
- package/src/runtime/routes/subagents-routes.ts +14 -10
- package/src/runtime/routes/usage-routes.ts +8 -7
- package/src/runtime/routes/workspace-routes.test.ts +22 -0
- package/src/runtime/routes/workspace-routes.ts +8 -1
- package/src/runtime/routes/workspace-utils.ts +2 -0
- package/src/schedule/scheduler.ts +7 -5
- package/src/security/ces-credential-client.ts +20 -0
- package/src/security/ces-rpc-credential-backend.ts +17 -0
- package/src/security/credential-backend.ts +5 -0
- package/src/security/oauth2.ts +42 -25
- package/src/security/secure-keys.ts +118 -25
- package/src/security/token-manager.ts +23 -10
- package/src/skills/catalog-files.ts +492 -0
- package/src/skills/inline-command-runner.ts +12 -14
- package/src/subagent/manager.ts +131 -26
- package/src/subagent/types.ts +19 -0
- package/src/tools/apps/executors.ts +11 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
- package/src/tools/browser/auth-detector.ts +43 -12
- package/src/tools/browser/browser-execution.ts +645 -340
- package/src/tools/browser/browser-manager.ts +36 -12
- package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
- package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
- package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
- package/src/tools/browser/cdp-client/errors.ts +34 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
- package/src/tools/browser/cdp-client/factory.ts +204 -0
- package/src/tools/browser/cdp-client/index.ts +14 -0
- package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
- package/src/tools/browser/cdp-client/types.ts +52 -0
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +1 -1
- package/src/tools/host-filesystem/read.ts +12 -15
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +21 -16
- package/src/tools/permission-checker.ts +77 -100
- package/src/tools/registry.ts +0 -2
- package/src/tools/secret-detection-handler.ts +34 -1
- package/src/tools/shared/filesystem/image-read.ts +61 -40
- package/src/tools/skills/sandbox-runner.ts +3 -6
- package/src/tools/subagent/spawn.ts +47 -3
- package/src/tools/subagent/status.ts +2 -0
- package/src/tools/system/register.ts +2 -16
- package/src/tools/terminal/safe-env.ts +7 -0
- package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
- package/src/tools/terminal/sandbox.ts +4 -1
- package/src/tools/terminal/shell.ts +24 -21
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/types.ts +2 -3
- package/src/util/platform.ts +14 -19
- package/src/watcher/provider-types.ts +1 -1
- package/src/workspace/migrations/029-seed-pkb.ts +1 -0
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/workspace/top-level-renderer.ts +19 -1
- package/src/__tests__/chrome-cdp.test.ts +0 -419
- package/src/__tests__/permission-mode-sse.test.ts +0 -418
- package/src/__tests__/permission-mode-store.test.ts +0 -277
- package/src/browser-extension-relay/protocol.ts +0 -63
- package/src/browser-extension-relay/server.ts +0 -203
- package/src/config/schemas/sandbox.ts +0 -14
- package/src/permissions/permission-mode-store.ts +0 -180
- package/src/tools/browser/chrome-cdp.ts +0 -239
- 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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
+
provider: text("provider_key")
|
|
50
57
|
.notNull()
|
|
51
|
-
.references(() => oauthProviders.
|
|
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.
|
|
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
|
-
|
|
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. '
|
|
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
|
-
|
|
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
|
-
|
|
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> & {
|
|
27
|
+
overrides: Partial<OAuthProviderRow> & { provider: string },
|
|
28
28
|
): OAuthProviderRow {
|
|
29
29
|
const now = Date.now();
|
|
30
|
-
const {
|
|
30
|
+
const { provider, ...rest } = overrides;
|
|
31
31
|
return {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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({
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
103
|
+
return withValidToken(this.provider, fn, {
|
|
104
104
|
connectionId: this.id,
|
|
105
105
|
});
|
|
106
106
|
}
|