@vellumai/assistant 0.5.0 → 0.5.2
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/ARCHITECTURE.md +54 -54
- package/docs/architecture/integrations.md +62 -67
- package/docs/credential-execution-service.md +3 -3
- package/package.json +1 -1
- package/src/__tests__/agent-loop.test.ts +111 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
- package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
- package/src/__tests__/app-dir-path-guard.test.ts +78 -0
- package/src/__tests__/app-executors.test.ts +1 -291
- package/src/__tests__/app-git-history.test.ts +4 -4
- package/src/__tests__/app-routes-csp.test.ts +1 -0
- package/src/__tests__/app-store-dir-names.test.ts +426 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -9
- package/src/__tests__/attachments-store.test.ts +169 -21
- package/src/__tests__/attachments.test.ts +115 -1
- package/src/__tests__/btw-routes.test.ts +1 -0
- package/src/__tests__/canonical-guardian-store.test.ts +38 -0
- package/src/__tests__/channel-reply-delivery.test.ts +55 -0
- package/src/__tests__/checker.test.ts +54 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
- package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/config-schema-cmd.test.ts +68 -21
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +149 -5
- package/src/__tests__/conversation-agent-loop.test.ts +290 -2
- package/src/__tests__/conversation-attachments.test.ts +17 -19
- package/src/__tests__/conversation-disk-view-integration.test.ts +277 -0
- package/src/__tests__/conversation-disk-view.test.ts +810 -0
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +551 -0
- package/src/__tests__/conversation-fork-route.test.ts +386 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-key-store-disk-view.test.ts +130 -0
- package/src/__tests__/conversation-media-retry.test.ts +8 -2
- package/src/__tests__/conversation-queue.test.ts +36 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
- package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
- package/src/__tests__/conversation-skill-tools.test.ts +4 -9
- package/src/__tests__/conversation-slash-commands.test.ts +149 -0
- package/src/__tests__/conversation-store.test.ts +24 -21
- package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
- package/src/__tests__/conversation-title-service.test.ts +137 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-vault-unit.test.ts +5 -10
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
- package/src/__tests__/diagnostics-export.test.ts +70 -1
- package/src/__tests__/filesystem-tools.test.ts +4 -2
- package/src/__tests__/first-greeting.test.ts +80 -0
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
- package/src/__tests__/history-repair.test.ts +103 -10
- package/src/__tests__/http-conversation-lineage.test.ts +251 -0
- package/src/__tests__/image-source-path-reinject.test.ts +136 -0
- package/src/__tests__/llm-context-normalization.test.ts +1116 -0
- package/src/__tests__/llm-context-route-provider.test.ts +217 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
- package/src/__tests__/media-generate-image.test.ts +47 -94
- package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
- package/src/__tests__/memory-recall-quality.test.ts +5 -5
- package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
- package/src/__tests__/migration-export-http.test.ts +3 -1
- package/src/__tests__/migration-import-commit-http.test.ts +18 -4
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
- package/src/__tests__/mime-builder.test.ts +3 -2
- package/src/__tests__/non-member-access-request.test.ts +12 -1
- package/src/__tests__/notification-decision-identity.test.ts +52 -0
- package/src/__tests__/oauth-apps-routes.test.ts +103 -0
- package/src/__tests__/oauth-store.test.ts +115 -0
- package/src/__tests__/provider-error-scenarios.test.ts +1 -3
- package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
- package/src/__tests__/recording-handler.test.ts +17 -0
- package/src/__tests__/registry.test.ts +3 -8
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
- package/src/__tests__/schema-transforms.test.ts +165 -5
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/skill-feature-flags-integration.test.ts +18 -17
- package/src/__tests__/skill-feature-flags.test.ts +13 -13
- package/src/__tests__/skill-load-feature-flag.test.ts +4 -4
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -2
- package/src/__tests__/starter-task-flow.test.ts +1 -0
- package/src/__tests__/suggestion-routes.test.ts +443 -0
- package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
- package/src/__tests__/swarm-recursion.test.ts +1 -0
- package/src/__tests__/swarm-tool.test.ts +1 -0
- package/src/__tests__/system-prompt.test.ts +8 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
- package/src/__tests__/top-level-renderer.test.ts +22 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
- package/src/__tests__/web-fetch.test.ts +6 -2
- package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
- package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
- package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
- package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
- package/src/agent/attachments.ts +27 -1
- package/src/agent/loop.ts +29 -1
- package/src/avatar/traits-png-sync.ts +80 -25
- package/src/bundler/app-bundler.ts +4 -4
- package/src/calls/call-domain.ts +1 -0
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/commands/auth.ts +92 -0
- package/src/cli/commands/avatar.ts +7 -6
- package/src/cli/commands/config.ts +2 -0
- package/src/cli/commands/oauth/providers.ts +29 -0
- package/src/cli/program.ts +12 -0
- package/src/cli.ts +15 -48
- package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
- package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
- package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
- package/src/config/bundled-tool-registry.ts +2 -14
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +64 -0
- package/src/config/raw-config-utils.ts +30 -0
- package/src/config/schema-utils.ts +28 -7
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/elevenlabs.ts +18 -0
- package/src/config/schemas/memory-lifecycle.ts +4 -2
- package/src/config/schemas/memory-storage.ts +1 -1
- package/src/config/schemas/services.ts +8 -6
- package/src/contacts/contact-store.ts +13 -6
- package/src/contacts/contacts-write.ts +0 -1
- package/src/context/window-manager.ts +13 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +46 -42
- package/src/daemon/conversation-agent-loop.ts +56 -19
- package/src/daemon/conversation-attachments.ts +18 -36
- package/src/daemon/conversation-error.ts +2 -1
- package/src/daemon/conversation-history.ts +18 -4
- package/src/daemon/conversation-lifecycle.ts +39 -15
- package/src/daemon/conversation-messaging.ts +70 -26
- package/src/daemon/conversation-process.ts +58 -34
- package/src/daemon/conversation-runtime-assembly.ts +21 -38
- package/src/daemon/conversation-slash.ts +121 -256
- package/src/daemon/conversation-surfaces.ts +143 -20
- package/src/daemon/conversation-tool-setup.ts +0 -6
- package/src/daemon/conversation-workspace.ts +21 -1
- package/src/daemon/conversation.ts +51 -29
- package/src/daemon/first-greeting.ts +35 -0
- package/src/daemon/handlers/config-embeddings.ts +148 -0
- package/src/daemon/handlers/config-model.ts +71 -26
- package/src/daemon/handlers/conversations.ts +0 -23
- package/src/daemon/handlers/recording.ts +26 -21
- package/src/daemon/history-repair.ts +28 -8
- package/src/daemon/host-cu-proxy.ts +2 -2
- package/src/daemon/lifecycle.ts +106 -64
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +19 -0
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/shared.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/message-types/upgrades.ts +23 -0
- package/src/daemon/server.ts +83 -12
- package/src/daemon/shutdown-handlers.ts +8 -5
- package/src/daemon/startup-error.ts +9 -0
- package/src/daemon/tool-side-effects.ts +11 -28
- package/src/events/tool-permission-telemetry-listener.ts +1 -3
- package/src/instrument.ts +0 -4
- package/src/media/app-icon-generator.ts +2 -2
- package/src/memory/app-git-service.ts +28 -16
- package/src/memory/app-store.ts +230 -41
- package/src/memory/attachments-store.ts +558 -130
- package/src/memory/conversation-attention-store.ts +70 -0
- package/src/memory/conversation-crud.ts +442 -3
- package/src/memory/conversation-directories.ts +125 -0
- package/src/memory/conversation-disk-view.ts +390 -0
- package/src/memory/conversation-key-store.ts +17 -5
- package/src/memory/conversation-queries.ts +5 -1
- package/src/memory/conversation-title-service.ts +21 -49
- package/src/memory/db-init.ts +28 -0
- package/src/memory/embedding-backend.ts +42 -53
- package/src/memory/embedding-gemini.test.ts +4 -4
- package/src/memory/embedding-local.ts +1 -3
- package/src/memory/embedding-ollama.ts +1 -3
- package/src/memory/embedding-openai.ts +1 -3
- package/src/memory/indexer.ts +9 -7
- package/src/memory/items-extractor.ts +42 -13
- package/src/memory/job-handlers/conversation-starters.ts +6 -1
- package/src/memory/job-handlers/embedding.test.ts +1 -4
- package/src/memory/llm-request-log-store.ts +100 -1
- package/src/memory/migrations/102-alter-table-columns.ts +5 -0
- package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
- package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
- package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
- package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
- package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
- package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
- package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
- package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
- package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
- package/src/memory/migrations/index.ts +7 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/retriever.test.ts +601 -2
- package/src/memory/retriever.ts +85 -9
- package/src/memory/schema/conversations.ts +6 -0
- package/src/memory/schema/infrastructure.ts +13 -7
- package/src/memory/schema/oauth.ts +6 -0
- package/src/messaging/providers/gmail/mime-builder.ts +3 -1
- package/src/notifications/copy-composer.ts +26 -0
- package/src/notifications/decision-engine.ts +14 -1
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +36 -0
- package/src/oauth/byo-connection.test.ts +1 -45
- package/src/oauth/byo-connection.ts +2 -8
- package/src/oauth/connect-orchestrator.ts +15 -11
- package/src/oauth/connection-resolver.test.ts +191 -0
- package/src/oauth/connection-resolver.ts +66 -38
- package/src/oauth/connection.ts +0 -1
- package/src/oauth/oauth-store.ts +97 -47
- package/src/oauth/platform-connection.test.ts +0 -1
- package/src/oauth/platform-connection.ts +11 -3
- package/src/oauth/seed-providers.ts +78 -3
- package/src/oauth/token-persistence.ts +16 -10
- package/src/permissions/checker.ts +62 -19
- package/src/prompts/system-prompt.ts +2 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -0
- package/src/providers/anthropic/client.ts +8 -1
- package/src/providers/failover.ts +4 -1
- package/src/providers/gemini/client.ts +50 -0
- package/src/providers/model-catalog.ts +92 -0
- package/src/providers/model-intents.ts +29 -20
- package/src/providers/openai/client.ts +49 -0
- package/src/providers/types.ts +2 -0
- package/src/runtime/access-request-helper.ts +16 -7
- package/src/runtime/auth/credential-service.ts +3 -1
- package/src/runtime/auth/route-policy.ts +14 -1
- package/src/runtime/btw-sidechain.ts +101 -0
- package/src/runtime/channel-reply-delivery.ts +17 -1
- package/src/runtime/http-router.ts +3 -1
- package/src/runtime/http-server.ts +196 -141
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +5 -1
- package/src/runtime/routes/access-request-decision.ts +41 -0
- package/src/runtime/routes/app-management-routes.ts +6 -3
- package/src/runtime/routes/app-routes.ts +7 -3
- package/src/runtime/routes/approval-routes.ts +1 -0
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
- package/src/runtime/routes/attachment-routes.ts +45 -15
- package/src/runtime/routes/btw-routes.ts +21 -61
- package/src/runtime/routes/conversation-management-routes.ts +68 -0
- package/src/runtime/routes/conversation-query-routes.ts +180 -10
- package/src/runtime/routes/conversation-routes.ts +222 -28
- package/src/runtime/routes/conversation-starter-routes.ts +9 -11
- package/src/runtime/routes/diagnostics-routes.ts +1 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
- package/src/runtime/routes/llm-context-normalization.ts +1199 -0
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/memory-item-routes.test.ts +34 -0
- package/src/runtime/routes/memory-item-routes.ts +4 -0
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/oauth-apps.ts +291 -0
- package/src/runtime/routes/secret-routes.ts +28 -1
- package/src/runtime/routes/settings-routes.ts +14 -0
- package/src/runtime/routes/trace-event-routes.ts +4 -1
- package/src/schedule/schedule-store.ts +9 -21
- package/src/security/secure-keys.ts +21 -0
- package/src/signals/bash.ts +1 -1
- package/src/swarm/backend-claude-code.ts +3 -6
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +3 -1
- package/src/tools/AGENTS.md +6 -10
- package/src/tools/apps/executors.ts +17 -232
- package/src/tools/claude-code/claude-code.ts +2 -3
- package/src/tools/credentials/vault.ts +7 -12
- package/src/tools/host-filesystem/read.ts +13 -10
- package/src/tools/network/__tests__/web-search.test.ts +4 -2
- package/src/tools/schedule/list.ts +2 -7
- package/src/tools/schema-transforms.ts +5 -0
- package/src/tools/shared/filesystem/format-diff.ts +4 -21
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/tool-manifest.ts +0 -6
- package/src/tools/ui-surface/definitions.ts +2 -2
- package/src/util/device-id.ts +28 -5
- package/src/util/platform.ts +6 -0
- package/src/util/pricing.ts +1 -0
- package/src/util/retry.ts +1 -3
- package/src/workspace/migrations/002-backfill-installation-id.ts +23 -12
- package/src/workspace/migrations/003-seed-device-id.ts +3 -4
- package/src/workspace/migrations/006-services-config.ts +5 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
- package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/top-level-renderer.ts +12 -0
- package/src/__tests__/asset-materialize-tool.test.ts +0 -523
- package/src/__tests__/asset-search-tool.test.ts +0 -536
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
- package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
- package/src/__tests__/media-visibility-policy.test.ts +0 -190
- package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
- package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
- package/src/daemon/media-visibility-policy.ts +0 -59
- package/src/tools/assets/materialize.ts +0 -248
- package/src/tools/assets/search.ts +0 -400
|
@@ -5,9 +5,11 @@ import { seedProviders } from "./oauth-store.js";
|
|
|
5
5
|
*
|
|
6
6
|
* These values are upserted into the `oauth_providers` SQLite table on
|
|
7
7
|
* every startup. Only Vellum implementation fields (authUrl, tokenUrl,
|
|
8
|
-
* tokenEndpointAuthMethod, extraParams, callbackTransport,
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* tokenEndpointAuthMethod, userinfoUrl, extraParams, callbackTransport,
|
|
9
|
+
* pingUrl, managedServiceConfigKey) and display metadata (displayName,
|
|
10
|
+
* description, dashboardUrl, clientIdPlaceholder, requiresClientSecret)
|
|
11
|
+
* are overwritten on subsequent startups — user-customizable
|
|
12
|
+
* fields (defaultScopes, scopePolicy, baseUrl) are only
|
|
11
13
|
* written on initial insert and preserved across restarts.
|
|
12
14
|
*
|
|
13
15
|
* Code-side behavioral fields (identityVerifier, injectionTemplates,
|
|
@@ -32,6 +34,12 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
32
34
|
};
|
|
33
35
|
extraParams?: Record<string, string>;
|
|
34
36
|
callbackTransport?: string;
|
|
37
|
+
managedServiceConfigKey?: string;
|
|
38
|
+
displayName: string;
|
|
39
|
+
description: string;
|
|
40
|
+
dashboardUrl: string | null;
|
|
41
|
+
clientIdPlaceholder: string | null;
|
|
42
|
+
requiresClientSecret?: boolean;
|
|
35
43
|
}
|
|
36
44
|
> = {
|
|
37
45
|
"integration:google": {
|
|
@@ -41,6 +49,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
41
49
|
userinfoUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
|
|
42
50
|
pingUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
|
|
43
51
|
baseUrl: "https://gmail.googleapis.com/gmail/v1/users/me",
|
|
52
|
+
displayName: "Google",
|
|
53
|
+
description: "Gmail, Calendar, and Contacts",
|
|
54
|
+
dashboardUrl: "https://console.cloud.google.com/apis/credentials",
|
|
55
|
+
clientIdPlaceholder: "123456789.apps.googleusercontent.com",
|
|
44
56
|
defaultScopes: [
|
|
45
57
|
"https://www.googleapis.com/auth/gmail.readonly",
|
|
46
58
|
"https://www.googleapis.com/auth/gmail.modify",
|
|
@@ -60,6 +72,7 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
60
72
|
},
|
|
61
73
|
extraParams: { access_type: "offline", prompt: "consent" },
|
|
62
74
|
callbackTransport: "loopback",
|
|
75
|
+
managedServiceConfigKey: "google-oauth",
|
|
63
76
|
},
|
|
64
77
|
|
|
65
78
|
"integration:slack": {
|
|
@@ -68,6 +81,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
68
81
|
tokenUrl: "https://slack.com/api/oauth.v2.access",
|
|
69
82
|
pingUrl: "https://slack.com/api/auth.test",
|
|
70
83
|
baseUrl: "https://slack.com/api",
|
|
84
|
+
displayName: "Slack",
|
|
85
|
+
description: "Workspace messaging",
|
|
86
|
+
dashboardUrl: "https://api.slack.com/apps",
|
|
87
|
+
clientIdPlaceholder: null,
|
|
71
88
|
defaultScopes: [
|
|
72
89
|
"channels:read",
|
|
73
90
|
"channels:history",
|
|
@@ -101,6 +118,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
101
118
|
tokenUrl: "https://api.notion.com/v1/oauth/token",
|
|
102
119
|
pingUrl: "https://api.notion.com/v1/users/me",
|
|
103
120
|
baseUrl: "https://api.notion.com",
|
|
121
|
+
displayName: "Notion",
|
|
122
|
+
description: "Pages and databases",
|
|
123
|
+
dashboardUrl: "https://www.notion.so/my-integrations",
|
|
124
|
+
clientIdPlaceholder: null,
|
|
104
125
|
defaultScopes: [],
|
|
105
126
|
scopePolicy: {
|
|
106
127
|
allowAdditionalScopes: false,
|
|
@@ -118,6 +139,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
118
139
|
tokenUrl: "https://api.x.com/2/oauth2/token",
|
|
119
140
|
pingUrl: "https://api.x.com/2/users/me",
|
|
120
141
|
baseUrl: "https://api.x.com",
|
|
142
|
+
displayName: "Twitter",
|
|
143
|
+
description: "Posts and direct messages",
|
|
144
|
+
dashboardUrl: "https://developer.twitter.com/en/portal/dashboard",
|
|
145
|
+
clientIdPlaceholder: null,
|
|
121
146
|
defaultScopes: [
|
|
122
147
|
"tweet.read",
|
|
123
148
|
"tweet.write",
|
|
@@ -139,6 +164,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
139
164
|
tokenUrl: "https://github.com/login/oauth/access_token",
|
|
140
165
|
pingUrl: "https://api.github.com/user",
|
|
141
166
|
baseUrl: "https://api.github.com",
|
|
167
|
+
displayName: "GitHub",
|
|
168
|
+
description: "Repositories and issues",
|
|
169
|
+
dashboardUrl: "https://github.com/settings/developers",
|
|
170
|
+
clientIdPlaceholder: null,
|
|
142
171
|
defaultScopes: ["repo", "read:user", "notifications"],
|
|
143
172
|
scopePolicy: {
|
|
144
173
|
allowAdditionalScopes: true,
|
|
@@ -159,6 +188,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
159
188
|
tokenUrl: "https://api.linear.app/oauth/token",
|
|
160
189
|
pingUrl: "https://api.linear.app/graphql",
|
|
161
190
|
baseUrl: "https://api.linear.app",
|
|
191
|
+
displayName: "Linear",
|
|
192
|
+
description: "Issues and projects",
|
|
193
|
+
dashboardUrl: "https://linear.app/settings/api",
|
|
194
|
+
clientIdPlaceholder: null,
|
|
162
195
|
defaultScopes: ["read", "write", "issues:create"],
|
|
163
196
|
scopePolicy: {
|
|
164
197
|
allowAdditionalScopes: false,
|
|
@@ -175,6 +208,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
175
208
|
tokenUrl: "https://accounts.spotify.com/api/token",
|
|
176
209
|
pingUrl: "https://api.spotify.com/v1/me",
|
|
177
210
|
baseUrl: "https://api.spotify.com/v1",
|
|
211
|
+
displayName: "Spotify",
|
|
212
|
+
description: "Music and playlists",
|
|
213
|
+
dashboardUrl: "https://developer.spotify.com/dashboard",
|
|
214
|
+
clientIdPlaceholder: null,
|
|
178
215
|
defaultScopes: [
|
|
179
216
|
"user-read-playback-state",
|
|
180
217
|
"user-modify-playback-state",
|
|
@@ -201,6 +238,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
201
238
|
tokenUrl: "https://todoist.com/oauth/access_token",
|
|
202
239
|
pingUrl: "https://api.todoist.com/rest/v2/projects",
|
|
203
240
|
baseUrl: "https://api.todoist.com/rest/v2",
|
|
241
|
+
displayName: "Todoist",
|
|
242
|
+
description: "Tasks and projects",
|
|
243
|
+
dashboardUrl: "https://developer.todoist.com/appconsole.html",
|
|
244
|
+
clientIdPlaceholder: null,
|
|
204
245
|
defaultScopes: ["data:read_write"],
|
|
205
246
|
scopePolicy: {
|
|
206
247
|
allowAdditionalScopes: false,
|
|
@@ -216,6 +257,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
216
257
|
tokenUrl: "https://discord.com/api/v10/oauth2/token",
|
|
217
258
|
pingUrl: "https://discord.com/api/v10/users/@me",
|
|
218
259
|
baseUrl: "https://discord.com/api/v10",
|
|
260
|
+
displayName: "Discord",
|
|
261
|
+
description: "Servers and messages",
|
|
262
|
+
dashboardUrl: "https://discord.com/developers/applications",
|
|
263
|
+
clientIdPlaceholder: null,
|
|
219
264
|
defaultScopes: [
|
|
220
265
|
"identify",
|
|
221
266
|
"guilds",
|
|
@@ -236,6 +281,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
236
281
|
tokenUrl: "https://api.dropboxapi.com/oauth2/token",
|
|
237
282
|
pingUrl: "https://api.dropboxapi.com/2/users/get_current_account",
|
|
238
283
|
baseUrl: "https://api.dropboxapi.com/2",
|
|
284
|
+
displayName: "Dropbox",
|
|
285
|
+
description: "Files and folders",
|
|
286
|
+
dashboardUrl: "https://www.dropbox.com/developers/apps",
|
|
287
|
+
clientIdPlaceholder: null,
|
|
239
288
|
defaultScopes: [
|
|
240
289
|
"files.metadata.read",
|
|
241
290
|
"files.content.read",
|
|
@@ -257,6 +306,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
257
306
|
tokenUrl: "https://app.asana.com/-/oauth_token",
|
|
258
307
|
pingUrl: "https://app.asana.com/api/1.0/users/me",
|
|
259
308
|
baseUrl: "https://app.asana.com/api/1.0",
|
|
309
|
+
displayName: "Asana",
|
|
310
|
+
description: "Tasks and projects",
|
|
311
|
+
dashboardUrl: "https://app.asana.com/0/my-apps",
|
|
312
|
+
clientIdPlaceholder: null,
|
|
260
313
|
defaultScopes: ["default"],
|
|
261
314
|
scopePolicy: {
|
|
262
315
|
allowAdditionalScopes: false,
|
|
@@ -272,6 +325,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
272
325
|
tokenUrl: "https://airtable.com/oauth2/v1/token",
|
|
273
326
|
pingUrl: "https://api.airtable.com/v0/meta/whoami",
|
|
274
327
|
baseUrl: "https://api.airtable.com/v0",
|
|
328
|
+
displayName: "Airtable",
|
|
329
|
+
description: "Bases and records",
|
|
330
|
+
dashboardUrl: "https://airtable.com/create/tokens",
|
|
331
|
+
clientIdPlaceholder: null,
|
|
275
332
|
defaultScopes: [
|
|
276
333
|
"data.records:read",
|
|
277
334
|
"data.records:write",
|
|
@@ -292,6 +349,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
292
349
|
tokenUrl: "https://api.hubapi.com/oauth/v1/token",
|
|
293
350
|
pingUrl: "https://api.hubapi.com/crm/v3/objects/contacts?limit=1",
|
|
294
351
|
baseUrl: "https://api.hubapi.com",
|
|
352
|
+
displayName: "HubSpot",
|
|
353
|
+
description: "CRM contacts and deals",
|
|
354
|
+
dashboardUrl: "https://developers.hubspot.com/",
|
|
355
|
+
clientIdPlaceholder: null,
|
|
295
356
|
defaultScopes: [
|
|
296
357
|
"crm.objects.contacts.read",
|
|
297
358
|
"crm.objects.contacts.write",
|
|
@@ -316,6 +377,10 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
316
377
|
tokenUrl: "https://api.figma.com/v1/oauth/token",
|
|
317
378
|
pingUrl: "https://api.figma.com/v1/me",
|
|
318
379
|
baseUrl: "https://api.figma.com/v1",
|
|
380
|
+
displayName: "Figma",
|
|
381
|
+
description: "Design files and comments",
|
|
382
|
+
dashboardUrl: "https://www.figma.com/developers/apps",
|
|
383
|
+
clientIdPlaceholder: null,
|
|
319
384
|
defaultScopes: ["files:read", "file_comments:write"],
|
|
320
385
|
scopePolicy: {
|
|
321
386
|
allowAdditionalScopes: false,
|
|
@@ -335,6 +400,11 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
335
400
|
tokenUrl: "urn:manual-token",
|
|
336
401
|
pingUrl: "https://slack.com/api/auth.test",
|
|
337
402
|
baseUrl: "https://slack.com/api",
|
|
403
|
+
displayName: "Slack Channel",
|
|
404
|
+
description: "Channel bot token",
|
|
405
|
+
dashboardUrl: null,
|
|
406
|
+
clientIdPlaceholder: null,
|
|
407
|
+
requiresClientSecret: false,
|
|
338
408
|
defaultScopes: [],
|
|
339
409
|
scopePolicy: {
|
|
340
410
|
allowAdditionalScopes: false,
|
|
@@ -348,6 +418,11 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
348
418
|
authUrl: "urn:manual-token",
|
|
349
419
|
tokenUrl: "urn:manual-token",
|
|
350
420
|
baseUrl: "https://api.telegram.org",
|
|
421
|
+
displayName: "Telegram",
|
|
422
|
+
description: "Bot messaging",
|
|
423
|
+
dashboardUrl: null,
|
|
424
|
+
clientIdPlaceholder: null,
|
|
425
|
+
requiresClientSecret: false,
|
|
351
426
|
defaultScopes: [],
|
|
352
427
|
scopePolicy: {
|
|
353
428
|
allowAdditionalScopes: false,
|
|
@@ -28,9 +28,8 @@ import {
|
|
|
28
28
|
import { runPostConnectHook } from "../tools/credentials/post-connect-hooks.js";
|
|
29
29
|
import {
|
|
30
30
|
createConnection,
|
|
31
|
+
getActiveConnection,
|
|
31
32
|
getApp,
|
|
32
|
-
getConnectionByProvider,
|
|
33
|
-
getConnectionByProviderAndAccount,
|
|
34
33
|
listActiveConnectionsByProvider,
|
|
35
34
|
updateConnection,
|
|
36
35
|
upsertApp,
|
|
@@ -59,8 +58,12 @@ export interface StoreOAuth2TokensParams {
|
|
|
59
58
|
clientId: string;
|
|
60
59
|
clientSecret?: string;
|
|
61
60
|
userinfoUrl?: string;
|
|
62
|
-
/**
|
|
63
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Best-effort account identifier parsed from the provider's identity
|
|
63
|
+
* endpoint (e.g. email, @username, display name). The format varies by
|
|
64
|
+
* provider and may be undefined if the API call fails.
|
|
65
|
+
*/
|
|
66
|
+
parsedAccountIdentifier?: string;
|
|
64
67
|
/** Pre-resolved oauth_app ID — skips the upsertApp() call if provided. */
|
|
65
68
|
oauthAppId?: string;
|
|
66
69
|
}
|
|
@@ -94,6 +97,10 @@ export async function storeOAuth2Tokens(
|
|
|
94
97
|
? Date.now() + tokens.expiresIn * 1000
|
|
95
98
|
: null;
|
|
96
99
|
|
|
100
|
+
// Account identifier parsing is best-effort. The format varies by provider
|
|
101
|
+
// (email for Google, username for GitHub, display name for Spotify, etc.)
|
|
102
|
+
// and may be undefined if the userinfo/identity API call fails or the
|
|
103
|
+
// required scope wasn't granted.
|
|
97
104
|
let accountInfo: string | undefined;
|
|
98
105
|
if (userinfoUrl && grantedScopes.some((s) => s.includes("userinfo"))) {
|
|
99
106
|
try {
|
|
@@ -109,7 +116,7 @@ export async function storeOAuth2Tokens(
|
|
|
109
116
|
}
|
|
110
117
|
}
|
|
111
118
|
|
|
112
|
-
const resolvedAccountInfo = accountInfo ?? params.
|
|
119
|
+
const resolvedAccountInfo = accountInfo ?? params.parsedAccountIdentifier;
|
|
113
120
|
|
|
114
121
|
// -------------------------------------------------------------------
|
|
115
122
|
// SQLite oauth_app + oauth_connection + new-format secure keys
|
|
@@ -144,12 +151,11 @@ export async function storeOAuth2Tokens(
|
|
|
144
151
|
// lookup so that re-auth without userinfo still updates the right row.
|
|
145
152
|
// However, treat provider-only matches as ambiguous when multiple active
|
|
146
153
|
// connections exist to avoid overwriting the wrong account's tokens.
|
|
147
|
-
let existingConn: ReturnType<typeof
|
|
154
|
+
let existingConn: ReturnType<typeof getActiveConnection>;
|
|
148
155
|
if (resolvedAccountInfo) {
|
|
149
|
-
existingConn =
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
);
|
|
156
|
+
existingConn = getActiveConnection(service, {
|
|
157
|
+
account: resolvedAccountInfo,
|
|
158
|
+
});
|
|
153
159
|
} else {
|
|
154
160
|
const activeConns = listActiveConnectionsByProvider(service);
|
|
155
161
|
// Only reuse the provider-only match when it's unambiguous (single connection).
|
|
@@ -144,6 +144,7 @@ const LOW_RISK_PROGRAMS = new Set([
|
|
|
144
144
|
"du",
|
|
145
145
|
"df",
|
|
146
146
|
"assistant",
|
|
147
|
+
"vellum",
|
|
147
148
|
]);
|
|
148
149
|
|
|
149
150
|
// High-risk shell programs / patterns
|
|
@@ -197,13 +198,30 @@ const LOW_RISK_GIT_SUBCOMMANDS = new Set([
|
|
|
197
198
|
"reflog",
|
|
198
199
|
]);
|
|
199
200
|
|
|
200
|
-
//
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
"
|
|
206
|
-
"
|
|
201
|
+
// Mutating assistant/vellum CLI subcommands that should be escalated to Medium
|
|
202
|
+
// risk. Most assistant/vellum subcommands are read-only and stay Low risk.
|
|
203
|
+
// This mirrors the git subcommand pattern — only known mutating operations
|
|
204
|
+
// get escalated.
|
|
205
|
+
const MEDIUM_RISK_CLI_SUBCOMMANDS = new Set([
|
|
206
|
+
"credentials",
|
|
207
|
+
"config",
|
|
208
|
+
"bash",
|
|
209
|
+
"trust",
|
|
210
|
+
"autonomy",
|
|
211
|
+
"contacts",
|
|
212
|
+
"mcp",
|
|
213
|
+
"keys",
|
|
214
|
+
"wake",
|
|
215
|
+
"sleep",
|
|
216
|
+
"hatch",
|
|
217
|
+
"retire",
|
|
218
|
+
"clean",
|
|
219
|
+
"setup",
|
|
220
|
+
"upgrade",
|
|
221
|
+
"recover",
|
|
222
|
+
"login",
|
|
223
|
+
"use",
|
|
224
|
+
"pair",
|
|
207
225
|
]);
|
|
208
226
|
|
|
209
227
|
// Commands that wrap another program — the real program appears as the first
|
|
@@ -228,15 +246,40 @@ const WRAPPER_PROGRAMS = new Set([
|
|
|
228
246
|
// value of -u) as the wrapped program instead of `echo`.
|
|
229
247
|
const ENV_VALUE_FLAGS = new Set(["-u", "--unset", "-C", "--chdir"]);
|
|
230
248
|
|
|
249
|
+
// `git` global flags that consume the next positional argument as their value.
|
|
250
|
+
// Without this, `git -C status commit` would incorrectly identify `status`
|
|
251
|
+
// (the directory path) as the subcommand instead of `commit`.
|
|
252
|
+
const GIT_VALUE_FLAGS = new Set([
|
|
253
|
+
"-C",
|
|
254
|
+
"-c",
|
|
255
|
+
"--git-dir",
|
|
256
|
+
"--work-tree",
|
|
257
|
+
"--namespace",
|
|
258
|
+
"--super-prefix",
|
|
259
|
+
"--config-env",
|
|
260
|
+
]);
|
|
261
|
+
|
|
231
262
|
/**
|
|
232
|
-
* Return the first non-flag argument from an argument list
|
|
233
|
-
* Flags are arguments that start with `-`.
|
|
234
|
-
* options (e.g. `--verbose`, `-h
|
|
235
|
-
* CLIs like `git`, `vellum`, and
|
|
263
|
+
* Return the first non-flag argument from an argument list, optionally
|
|
264
|
+
* skipping value-taking flags. Flags are arguments that start with `-`.
|
|
265
|
+
* This is used to skip global options (e.g. `--verbose`, `-h`, `-C <path>`)
|
|
266
|
+
* when extracting the subcommand from CLIs like `git`, `vellum`, and
|
|
267
|
+
* `assistant`.
|
|
268
|
+
*
|
|
269
|
+
* When `valueFlags` is provided, any flag in that set causes the next
|
|
270
|
+
* argument to be skipped as well (it is the flag's value, not a positional).
|
|
236
271
|
*/
|
|
237
|
-
function firstPositionalArg(
|
|
238
|
-
|
|
239
|
-
|
|
272
|
+
function firstPositionalArg(
|
|
273
|
+
args: string[],
|
|
274
|
+
valueFlags?: Set<string>,
|
|
275
|
+
): string | undefined {
|
|
276
|
+
for (let i = 0; i < args.length; i++) {
|
|
277
|
+
const arg = args[i];
|
|
278
|
+
if (arg.startsWith("-")) {
|
|
279
|
+
if (valueFlags?.has(arg)) i++; // skip the next arg (the flag's value)
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
return arg;
|
|
240
283
|
}
|
|
241
284
|
return undefined;
|
|
242
285
|
}
|
|
@@ -661,7 +704,7 @@ async function classifyRiskUncached(
|
|
|
661
704
|
}
|
|
662
705
|
|
|
663
706
|
if (prog === "git") {
|
|
664
|
-
const subcommand = firstPositionalArg(seg.args);
|
|
707
|
+
const subcommand = firstPositionalArg(seg.args, GIT_VALUE_FLAGS);
|
|
665
708
|
if (subcommand && LOW_RISK_GIT_SUBCOMMANDS.has(subcommand)) {
|
|
666
709
|
// Stay at current risk
|
|
667
710
|
continue;
|
|
@@ -673,12 +716,12 @@ async function classifyRiskUncached(
|
|
|
673
716
|
|
|
674
717
|
if (prog === "vellum" || prog === "assistant") {
|
|
675
718
|
const subcommand = firstPositionalArg(seg.args);
|
|
676
|
-
if (subcommand &&
|
|
677
|
-
//
|
|
719
|
+
if (subcommand && MEDIUM_RISK_CLI_SUBCOMMANDS.has(subcommand)) {
|
|
720
|
+
// Known mutating subcommands are medium
|
|
721
|
+
maxRisk = RiskLevel.Medium;
|
|
678
722
|
continue;
|
|
679
723
|
}
|
|
680
|
-
//
|
|
681
|
-
maxRisk = RiskLevel.Medium;
|
|
724
|
+
// Read-only / unknown subcommands stay at current risk
|
|
682
725
|
continue;
|
|
683
726
|
}
|
|
684
727
|
|
|
@@ -206,6 +206,8 @@ function buildAttachmentSection(): string {
|
|
|
206
206
|
"",
|
|
207
207
|
'Use `source="host"` with an absolute path for host filesystem files. Optional attributes: `filename` (display name override), `mime_type` (override auto-detection).',
|
|
208
208
|
"",
|
|
209
|
+
"Image and video attachments can render inline in chat. If the user asks to preview a media file here, attach it instead of only printing its path.",
|
|
210
|
+
"",
|
|
209
211
|
"Embed images/GIFs inline using markdown: ``.",
|
|
210
212
|
].join("\n");
|
|
211
213
|
}
|
|
@@ -208,6 +208,8 @@ Once you've completed Phase 1 and made reasonable progress through Phase 2, you'
|
|
|
208
208
|
|
|
209
209
|
If you still haven't shown the two suggestions (Phase 2 step 4), do that before wrapping.
|
|
210
210
|
|
|
211
|
+
When you're confident onboarding is complete, delete `BOOTSTRAP.md` so it doesn't re-trigger on the next conversation.
|
|
212
|
+
|
|
211
213
|
---
|
|
212
214
|
|
|
213
215
|
_Good luck out there. Make it count._
|
|
@@ -16,6 +16,9 @@ import type {
|
|
|
16
16
|
|
|
17
17
|
const log = getLogger("anthropic-client");
|
|
18
18
|
|
|
19
|
+
/** Validation-specific timeout (10s) so a stalled network doesn't block key submission. */
|
|
20
|
+
const VALIDATION_TIMEOUT_MS = 10_000;
|
|
21
|
+
|
|
19
22
|
/**
|
|
20
23
|
* Validate an Anthropic API key by making a lightweight GET /v1/models call.
|
|
21
24
|
* Returns `{ valid: true }` on success or `{ valid: false, reason: string }` on failure.
|
|
@@ -24,7 +27,11 @@ export async function validateAnthropicApiKey(
|
|
|
24
27
|
apiKey: string,
|
|
25
28
|
): Promise<{ valid: true } | { valid: false; reason: string }> {
|
|
26
29
|
try {
|
|
27
|
-
const client = new Anthropic({
|
|
30
|
+
const client = new Anthropic({
|
|
31
|
+
apiKey,
|
|
32
|
+
timeout: VALIDATION_TIMEOUT_MS,
|
|
33
|
+
maxRetries: 0,
|
|
34
|
+
});
|
|
28
35
|
await client.models.list({ limit: 1 });
|
|
29
36
|
return { valid: true };
|
|
30
37
|
} catch (error) {
|
|
@@ -133,7 +133,10 @@ export class FailoverProvider implements Provider {
|
|
|
133
133
|
);
|
|
134
134
|
health.unhealthySince = null;
|
|
135
135
|
}
|
|
136
|
-
return
|
|
136
|
+
return {
|
|
137
|
+
...response,
|
|
138
|
+
actualProvider: response.actualProvider ?? provider.name,
|
|
139
|
+
};
|
|
137
140
|
} catch (error) {
|
|
138
141
|
lastError = error;
|
|
139
142
|
|
|
@@ -3,6 +3,7 @@ import { ApiError, GoogleGenAI } from "@google/genai";
|
|
|
3
3
|
|
|
4
4
|
import { SYSTEM_PROMPT_CACHE_BOUNDARY } from "../../prompts/cache-boundary.js";
|
|
5
5
|
import { ProviderError } from "../../util/errors.js";
|
|
6
|
+
import { getLogger } from "../../util/logger.js";
|
|
6
7
|
import { createStreamTimeout } from "../stream-timeout.js";
|
|
7
8
|
import type {
|
|
8
9
|
ContentBlock,
|
|
@@ -13,6 +14,55 @@ import type {
|
|
|
13
14
|
ToolDefinition,
|
|
14
15
|
} from "../types.js";
|
|
15
16
|
|
|
17
|
+
const log = getLogger("gemini-client");
|
|
18
|
+
|
|
19
|
+
/** Validation-specific timeout (10s) so a stalled network doesn't block key submission. */
|
|
20
|
+
const VALIDATION_TIMEOUT_MS = 10_000;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Validate a Gemini API key by making a lightweight models.list() call.
|
|
24
|
+
* Returns `{ valid: true }` on success or `{ valid: false, reason: string }` on failure.
|
|
25
|
+
*/
|
|
26
|
+
export async function validateGeminiApiKey(
|
|
27
|
+
apiKey: string,
|
|
28
|
+
): Promise<{ valid: true } | { valid: false; reason: string }> {
|
|
29
|
+
try {
|
|
30
|
+
const client = new GoogleGenAI({ apiKey });
|
|
31
|
+
await client.models.list({
|
|
32
|
+
config: {
|
|
33
|
+
pageSize: 1,
|
|
34
|
+
httpOptions: { timeout: VALIDATION_TIMEOUT_MS },
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
return { valid: true };
|
|
38
|
+
} catch (error) {
|
|
39
|
+
if (error instanceof ApiError) {
|
|
40
|
+
if (error.status === 401) {
|
|
41
|
+
return { valid: false, reason: "API key is invalid or expired." };
|
|
42
|
+
}
|
|
43
|
+
if (error.status === 403) {
|
|
44
|
+
return {
|
|
45
|
+
valid: false,
|
|
46
|
+
reason: `Gemini API error (${error.status}): ${error.message}`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Transient errors (429, 5xx, etc.) — validation is inconclusive,
|
|
50
|
+
// allow the key to be stored rather than blocking the user.
|
|
51
|
+
log.warn(
|
|
52
|
+
{ status: error.status },
|
|
53
|
+
"Gemini API returned a transient error during key validation — allowing key storage",
|
|
54
|
+
);
|
|
55
|
+
return { valid: true };
|
|
56
|
+
}
|
|
57
|
+
// Network errors — validation is inconclusive, allow key storage.
|
|
58
|
+
log.warn(
|
|
59
|
+
{ error: error instanceof Error ? error.message : String(error) },
|
|
60
|
+
"Network error during Gemini key validation — allowing key storage",
|
|
61
|
+
);
|
|
62
|
+
return { valid: true };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
16
66
|
export interface GeminiProviderOptions {
|
|
17
67
|
streamTimeoutMs?: number;
|
|
18
68
|
/** When set, routes requests through the managed proxy at this base URL. */
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export interface CatalogModel {
|
|
2
|
+
id: string;
|
|
3
|
+
displayName: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface ProviderCatalogEntry {
|
|
7
|
+
id: string;
|
|
8
|
+
displayName: string;
|
|
9
|
+
models: CatalogModel[];
|
|
10
|
+
defaultModel: string;
|
|
11
|
+
apiKeyUrl?: string;
|
|
12
|
+
apiKeyPlaceholder?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Single source of truth for all inference provider metadata and models. */
|
|
16
|
+
export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
17
|
+
{
|
|
18
|
+
id: "anthropic",
|
|
19
|
+
displayName: "Anthropic",
|
|
20
|
+
models: [
|
|
21
|
+
{ id: "claude-opus-4-6", displayName: "Claude Opus 4.6" },
|
|
22
|
+
{ id: "claude-sonnet-4-6", displayName: "Claude Sonnet 4.6" },
|
|
23
|
+
{ id: "claude-haiku-4-5-20251001", displayName: "Claude Haiku 4.5" },
|
|
24
|
+
],
|
|
25
|
+
defaultModel: "claude-opus-4-6",
|
|
26
|
+
apiKeyUrl: "https://console.anthropic.com/settings/keys",
|
|
27
|
+
apiKeyPlaceholder: "sk-ant-api03-...",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "openai",
|
|
31
|
+
displayName: "OpenAI",
|
|
32
|
+
models: [
|
|
33
|
+
{ id: "gpt-5.4", displayName: "GPT-5.4" },
|
|
34
|
+
{ id: "gpt-5.2", displayName: "GPT-5.2" },
|
|
35
|
+
{ id: "gpt-5.4-mini", displayName: "GPT-5.4 Mini" },
|
|
36
|
+
{ id: "gpt-5.4-nano", displayName: "GPT-5.4 Nano" },
|
|
37
|
+
],
|
|
38
|
+
defaultModel: "gpt-5.4",
|
|
39
|
+
apiKeyUrl: "https://platform.openai.com/api-keys",
|
|
40
|
+
apiKeyPlaceholder: "sk-proj-...",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "gemini",
|
|
44
|
+
displayName: "Google Gemini",
|
|
45
|
+
models: [
|
|
46
|
+
{ id: "gemini-3-flash", displayName: "Gemini 3 Flash" },
|
|
47
|
+
{ id: "gemini-3-pro", displayName: "Gemini 3 Pro" },
|
|
48
|
+
],
|
|
49
|
+
defaultModel: "gemini-3-flash",
|
|
50
|
+
apiKeyUrl: "https://aistudio.google.com/apikey",
|
|
51
|
+
apiKeyPlaceholder: "AIza...",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "ollama",
|
|
55
|
+
displayName: "Ollama",
|
|
56
|
+
models: [
|
|
57
|
+
{ id: "llama3.2", displayName: "Llama 3.2" },
|
|
58
|
+
{ id: "mistral", displayName: "Mistral" },
|
|
59
|
+
],
|
|
60
|
+
defaultModel: "llama3.2",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: "fireworks",
|
|
64
|
+
displayName: "Fireworks",
|
|
65
|
+
models: [
|
|
66
|
+
{
|
|
67
|
+
id: "accounts/fireworks/models/kimi-k2p5",
|
|
68
|
+
displayName: "Kimi K2.5",
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
defaultModel: "accounts/fireworks/models/kimi-k2p5",
|
|
72
|
+
apiKeyUrl: "https://fireworks.ai/account/api-keys",
|
|
73
|
+
apiKeyPlaceholder: "fw_...",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: "openrouter",
|
|
77
|
+
displayName: "OpenRouter",
|
|
78
|
+
models: [
|
|
79
|
+
{ id: "x-ai/grok-4", displayName: "Grok 4" },
|
|
80
|
+
{ id: "x-ai/grok-4.20-beta", displayName: "Grok 4.20 Beta" },
|
|
81
|
+
],
|
|
82
|
+
defaultModel: "x-ai/grok-4",
|
|
83
|
+
apiKeyUrl: "https://openrouter.ai/keys",
|
|
84
|
+
apiKeyPlaceholder: "sk-or-v1-...",
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
/** Check if a model ID is in the catalog for a given provider. */
|
|
89
|
+
export function isModelInCatalog(provider: string, modelId: string): boolean {
|
|
90
|
+
const entry = PROVIDER_CATALOG.find((p) => p.id === provider);
|
|
91
|
+
return entry?.models.some((m) => m.id === modelId) ?? false;
|
|
92
|
+
}
|