@vellumai/assistant 0.5.1 → 0.5.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/ARCHITECTURE.md +163 -54
- package/docs/architecture/integrations.md +62 -67
- package/docs/credential-execution-service.md +3 -3
- package/docs/skills.md +100 -0
- 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__/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 +156 -5
- package/src/__tests__/conversation-agent-loop.test.ts +297 -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-memory-dirty-tail.test.ts +150 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +7 -0
- 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-wipe.test.ts +226 -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-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__/db-memory-archive-migration.test.ts +372 -0
- package/src/__tests__/db-memory-brief-state-migration.test.ts +213 -0
- package/src/__tests__/db-memory-reducer-checkpoints.test.ts +273 -0
- package/src/__tests__/diagnostics-export.test.ts +70 -1
- 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 +32 -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__/inline-command-runner.test.ts +311 -0
- package/src/__tests__/inline-skill-authoring-guard.test.ts +220 -0
- package/src/__tests__/inline-skill-load-permissions.test.ts +435 -0
- package/src/__tests__/list-messages-attachments.test.ts +96 -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-brief-open-loops.test.ts +530 -0
- package/src/__tests__/memory-brief-time.test.ts +285 -0
- package/src/__tests__/memory-brief-wrapper.test.ts +311 -0
- package/src/__tests__/memory-chunk-archive.test.ts +400 -0
- package/src/__tests__/memory-chunk-dual-write.test.ts +453 -0
- package/src/__tests__/memory-episode-archive.test.ts +370 -0
- package/src/__tests__/memory-episode-dual-write.test.ts +626 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
- package/src/__tests__/memory-observation-archive.test.ts +375 -0
- package/src/__tests__/memory-observation-dual-write.test.ts +318 -0
- package/src/__tests__/memory-recall-quality.test.ts +7 -7
- package/src/__tests__/memory-reducer-store.test.ts +728 -0
- package/src/__tests__/memory-reducer-types.test.ts +699 -0
- package/src/__tests__/memory-reducer.test.ts +698 -0
- package/src/__tests__/memory-regressions.test.ts +6 -4
- package/src/__tests__/memory-simplified-config.test.ts +281 -0
- 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__/parse-identity-fields.test.ts +129 -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-load-inline-command.test.ts +598 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +644 -0
- package/src/__tests__/skills-inline-command-expansions.test.ts +301 -0
- package/src/__tests__/skills-transitive-hash.test.ts +333 -0
- 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__/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__/vellum-self-knowledge-inline-command.test.ts +320 -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/SKILL.md +1 -1
- 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 +24 -0
- package/src/config/loader.ts +65 -0
- package/src/config/raw-config-utils.ts +58 -0
- package/src/config/schema-utils.ts +28 -7
- package/src/config/schema.ts +20 -0
- package/src/config/schemas/elevenlabs.ts +18 -0
- package/src/config/schemas/memory-lifecycle.ts +4 -2
- package/src/config/schemas/memory-simplified.ts +101 -0
- package/src/config/schemas/memory-storage.ts +1 -1
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/services.ts +8 -6
- package/src/config/skills.ts +50 -4
- 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 +54 -8
- package/src/daemon/conversation-agent-loop.ts +127 -20
- 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 +50 -16
- package/src/daemon/conversation-messaging.ts +70 -26
- package/src/daemon/conversation-process.ts +58 -34
- package/src/daemon/conversation-runtime-assembly.ts +22 -38
- package/src/daemon/conversation-slash.ts +121 -256
- package/src/daemon/conversation-surfaces.ts +170 -24
- package/src/daemon/conversation-tool-setup.ts +0 -6
- package/src/daemon/conversation-workspace.ts +21 -1
- package/src/daemon/conversation.ts +69 -30
- package/src/daemon/first-greeting.ts +35 -0
- package/src/daemon/handlers/config-embeddings.ts +156 -0
- package/src/daemon/handlers/config-model.ts +62 -26
- package/src/daemon/handlers/conversations.ts +0 -23
- package/src/daemon/handlers/identity.ts +12 -1
- package/src/daemon/handlers/recording.ts +26 -21
- package/src/daemon/host-cu-proxy.ts +2 -2
- package/src/daemon/lifecycle.ts +115 -65
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +18 -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/followups/followup-store.ts +47 -1
- 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/archive-store.ts +400 -0
- package/src/memory/attachments-store.ts +558 -130
- package/src/memory/brief-formatting.ts +33 -0
- package/src/memory/brief-open-loops.ts +266 -0
- package/src/memory/brief-time.ts +161 -0
- package/src/memory/brief.ts +75 -0
- package/src/memory/conversation-attention-store.ts +70 -0
- package/src/memory/conversation-crud.ts +591 -8
- 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 +40 -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 +114 -21
- 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 +2 -4
- package/src/memory/job-handlers/embedding.ts +83 -0
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +6 -0
- package/src/memory/jobs-worker.ts +12 -0
- 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/185-memory-brief-state.ts +52 -0
- package/src/memory/migrations/186-memory-archive.ts +109 -0
- package/src/memory/migrations/187-memory-reducer-checkpoints.ts +19 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/qdrant-client.ts +23 -4
- package/src/memory/reducer-store.ts +271 -0
- package/src/memory/reducer-types.ts +99 -0
- package/src/memory/reducer.ts +453 -0
- package/src/memory/retriever.test.ts +601 -2
- package/src/memory/retriever.ts +85 -9
- package/src/memory/schema/conversations.ts +9 -0
- package/src/memory/schema/index.ts +2 -0
- package/src/memory/schema/infrastructure.ts +13 -7
- package/src/memory/schema/memory-archive.ts +121 -0
- package/src/memory/schema/memory-brief.ts +55 -0
- package/src/memory/schema/oauth.ts +6 -0
- package/src/memory/search/semantic.ts +17 -4
- 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 +99 -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 +160 -14
- package/src/permissions/defaults.ts +14 -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 +74 -0
- package/src/runtime/routes/conversation-query-routes.ts +187 -10
- package/src/runtime/routes/conversation-routes.ts +269 -28
- package/src/runtime/routes/conversation-starter-routes.ts +9 -11
- package/src/runtime/routes/diagnostics-routes.ts +1 -0
- package/src/runtime/routes/identity-routes.ts +2 -35
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
- package/src/runtime/routes/llm-context-normalization.ts +1212 -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 +94 -5
- 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 +30 -1
- package/src/runtime/routes/settings-routes.ts +14 -0
- package/src/runtime/routes/surface-action-routes.ts +68 -1
- package/src/runtime/routes/trace-event-routes.ts +4 -1
- package/src/schedule/schedule-store.ts +30 -21
- package/src/security/secure-keys.ts +21 -0
- package/src/signals/bash.ts +1 -1
- package/src/skills/inline-command-expansions.ts +204 -0
- package/src/skills/inline-command-render.ts +127 -0
- package/src/skills/inline-command-runner.ts +242 -0
- package/src/skills/transitive-version-hash.ts +88 -0
- package/src/swarm/backend-claude-code.ts +3 -6
- package/src/tasks/task-store.ts +43 -1
- 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/permission-checker.ts +8 -1
- package/src/tools/schedule/list.ts +2 -7
- package/src/tools/schema-transforms.ts +5 -0
- package/src/tools/shared/filesystem/format-diff.ts +2 -7
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +140 -6
- 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 +24 -0
- package/src/util/pricing.ts +1 -0
- package/src/util/retry.ts +1 -3
- 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/{002-backfill-installation-id.ts → 011-backfill-installation-id.ts} +24 -13
- 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 +11 -1
- 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
package/src/oauth/oauth-store.ts
CHANGED
|
@@ -45,7 +45,9 @@ export type OAuthConnectionRow = typeof oauthConnections.$inferSelect;
|
|
|
45
45
|
* Seed well-known provider profiles into the database. Uses INSERT … ON
|
|
46
46
|
* CONFLICT DO UPDATE so that implementation fields (authUrl, tokenUrl,
|
|
47
47
|
* tokenEndpointAuthMethod, userinfoUrl, extraParams, callbackTransport,
|
|
48
|
-
* pingUrl)
|
|
48
|
+
* pingUrl, managedServiceConfigKey) and display metadata (displayName,
|
|
49
|
+
* description, dashboardUrl, clientIdPlaceholder, requiresClientSecret)
|
|
50
|
+
* propagate to existing installations on every startup, while
|
|
49
51
|
* user-customizable fields (defaultScopes, scopePolicy, baseUrl) are
|
|
50
52
|
* only written on the initial insert.
|
|
51
53
|
*/
|
|
@@ -62,6 +64,12 @@ export function seedProviders(
|
|
|
62
64
|
scopePolicy: Record<string, unknown>;
|
|
63
65
|
extraParams?: Record<string, string>;
|
|
64
66
|
callbackTransport?: string;
|
|
67
|
+
managedServiceConfigKey?: string;
|
|
68
|
+
displayName?: string;
|
|
69
|
+
description?: string;
|
|
70
|
+
dashboardUrl?: string | null;
|
|
71
|
+
clientIdPlaceholder?: string | null;
|
|
72
|
+
requiresClientSecret?: boolean;
|
|
65
73
|
}>,
|
|
66
74
|
): void {
|
|
67
75
|
const db = getDb();
|
|
@@ -77,6 +85,12 @@ export function seedProviders(
|
|
|
77
85
|
const scopePolicy = JSON.stringify(p.scopePolicy);
|
|
78
86
|
const extraParams = p.extraParams ? JSON.stringify(p.extraParams) : null;
|
|
79
87
|
const callbackTransport = p.callbackTransport ?? null;
|
|
88
|
+
const managedServiceConfigKey = p.managedServiceConfigKey ?? null;
|
|
89
|
+
const displayName = p.displayName ?? null;
|
|
90
|
+
const description = p.description ?? null;
|
|
91
|
+
const dashboardUrl = p.dashboardUrl ?? null;
|
|
92
|
+
const clientIdPlaceholder = p.clientIdPlaceholder ?? null;
|
|
93
|
+
const requiresClientSecret = p.requiresClientSecret !== false ? 1 : 0;
|
|
80
94
|
|
|
81
95
|
db.insert(oauthProviders)
|
|
82
96
|
.values({
|
|
@@ -91,6 +105,12 @@ export function seedProviders(
|
|
|
91
105
|
extraParams,
|
|
92
106
|
callbackTransport,
|
|
93
107
|
pingUrl,
|
|
108
|
+
managedServiceConfigKey,
|
|
109
|
+
displayName,
|
|
110
|
+
description,
|
|
111
|
+
dashboardUrl,
|
|
112
|
+
clientIdPlaceholder,
|
|
113
|
+
requiresClientSecret,
|
|
94
114
|
createdAt: now,
|
|
95
115
|
updatedAt: now,
|
|
96
116
|
})
|
|
@@ -104,6 +124,12 @@ export function seedProviders(
|
|
|
104
124
|
extraParams,
|
|
105
125
|
callbackTransport,
|
|
106
126
|
pingUrl,
|
|
127
|
+
managedServiceConfigKey,
|
|
128
|
+
displayName,
|
|
129
|
+
description,
|
|
130
|
+
dashboardUrl,
|
|
131
|
+
clientIdPlaceholder,
|
|
132
|
+
requiresClientSecret,
|
|
107
133
|
updatedAt: now,
|
|
108
134
|
},
|
|
109
135
|
})
|
|
@@ -143,6 +169,12 @@ export function registerProvider(params: {
|
|
|
143
169
|
scopePolicy: Record<string, unknown>;
|
|
144
170
|
extraParams?: Record<string, string>;
|
|
145
171
|
callbackTransport?: string;
|
|
172
|
+
managedServiceConfigKey?: string;
|
|
173
|
+
displayName?: string;
|
|
174
|
+
description?: string;
|
|
175
|
+
dashboardUrl?: string;
|
|
176
|
+
clientIdPlaceholder?: string;
|
|
177
|
+
requiresClientSecret?: number;
|
|
146
178
|
}): OAuthProviderRow {
|
|
147
179
|
const db = getDb();
|
|
148
180
|
const now = Date.now();
|
|
@@ -164,6 +196,12 @@ export function registerProvider(params: {
|
|
|
164
196
|
extraParams: params.extraParams ? JSON.stringify(params.extraParams) : null,
|
|
165
197
|
callbackTransport: params.callbackTransport ?? null,
|
|
166
198
|
pingUrl: params.pingUrl ?? null,
|
|
199
|
+
managedServiceConfigKey: params.managedServiceConfigKey ?? null,
|
|
200
|
+
displayName: params.displayName ?? null,
|
|
201
|
+
description: params.description ?? null,
|
|
202
|
+
dashboardUrl: params.dashboardUrl ?? null,
|
|
203
|
+
clientIdPlaceholder: params.clientIdPlaceholder ?? null,
|
|
204
|
+
requiresClientSecret: params.requiresClientSecret ?? 1,
|
|
167
205
|
createdAt: now,
|
|
168
206
|
updatedAt: now,
|
|
169
207
|
};
|
|
@@ -232,6 +270,16 @@ export async function upsertApp(
|
|
|
232
270
|
if (!stored) {
|
|
233
271
|
throw new Error("Failed to store client_secret in secure storage");
|
|
234
272
|
}
|
|
273
|
+
// Bump updatedAt so the rollback guard in the new-row insertion path
|
|
274
|
+
// can detect that a concurrent caller has claimed this row. Without
|
|
275
|
+
// this, a concurrent inserter's rollback DELETE would still match on
|
|
276
|
+
// the original updatedAt and delete the row we just validated.
|
|
277
|
+
const newUpdatedAt = Date.now();
|
|
278
|
+
db.update(oauthApps)
|
|
279
|
+
.set({ updatedAt: newUpdatedAt })
|
|
280
|
+
.where(eq(oauthApps.id, existingRow.id))
|
|
281
|
+
.run();
|
|
282
|
+
return { ...existingRow, updatedAt: newUpdatedAt };
|
|
235
283
|
}
|
|
236
284
|
if (clientSecretCredentialPath) {
|
|
237
285
|
db.update(oauthApps)
|
|
@@ -272,7 +320,14 @@ export async function upsertApp(
|
|
|
272
320
|
if (!stored) {
|
|
273
321
|
// Roll back the just-inserted row to avoid an orphaned app pointing
|
|
274
322
|
// at a non-existent client_secret in secure storage.
|
|
275
|
-
|
|
323
|
+
//
|
|
324
|
+
// Guard: only delete if updatedAt still matches our insertion timestamp.
|
|
325
|
+
// A concurrent upsertApp call may have observed this row, successfully
|
|
326
|
+
// stored the secret, and updated the row — deleting it would orphan that
|
|
327
|
+
// caller's valid reference.
|
|
328
|
+
db.delete(oauthApps)
|
|
329
|
+
.where(and(eq(oauthApps.id, id), eq(oauthApps.updatedAt, now)))
|
|
330
|
+
.run();
|
|
276
331
|
throw new Error("Failed to store client_secret in secure storage");
|
|
277
332
|
}
|
|
278
333
|
}
|
|
@@ -286,6 +341,15 @@ export function getApp(id: string): OAuthAppRow | undefined {
|
|
|
286
341
|
return db.select().from(oauthApps).where(eq(oauthApps.id, id)).get();
|
|
287
342
|
}
|
|
288
343
|
|
|
344
|
+
/** Read an app client_secret from secure storage. */
|
|
345
|
+
export async function getAppClientSecret(
|
|
346
|
+
appOrId: OAuthAppRow | string,
|
|
347
|
+
): Promise<string | undefined> {
|
|
348
|
+
const app = typeof appOrId === "string" ? getApp(appOrId) : appOrId;
|
|
349
|
+
if (!app) return undefined;
|
|
350
|
+
return getSecureKeyAsync(app.clientSecretCredentialPath);
|
|
351
|
+
}
|
|
352
|
+
|
|
289
353
|
/** Look up an app by (provider_key, client_id). */
|
|
290
354
|
export function getAppByProviderAndClientId(
|
|
291
355
|
providerKey: string,
|
|
@@ -407,71 +471,59 @@ export function getConnection(id: string): OAuthConnectionRow | undefined {
|
|
|
407
471
|
|
|
408
472
|
/**
|
|
409
473
|
* Get the most recent active connection for a provider.
|
|
410
|
-
*
|
|
411
|
-
*
|
|
474
|
+
*
|
|
475
|
+
* Optional filters narrow the result:
|
|
476
|
+
* - `account` — match a specific account identifier (e.g. email).
|
|
477
|
+
* - `clientId` — restrict to connections linked to a specific OAuth app.
|
|
478
|
+
*
|
|
479
|
+
* Returns `undefined` when no matching active connection exists.
|
|
412
480
|
*/
|
|
413
|
-
export function
|
|
481
|
+
export function getActiveConnection(
|
|
414
482
|
providerKey: string,
|
|
415
|
-
clientId?: string,
|
|
483
|
+
options?: { clientId?: string; account?: string },
|
|
416
484
|
): OAuthConnectionRow | undefined {
|
|
485
|
+
const { clientId, account } = options ?? {};
|
|
417
486
|
const db = getDb();
|
|
418
487
|
|
|
488
|
+
const conditions = [
|
|
489
|
+
eq(oauthConnections.providerKey, providerKey),
|
|
490
|
+
eq(oauthConnections.status, "active"),
|
|
491
|
+
];
|
|
492
|
+
|
|
493
|
+
if (account) {
|
|
494
|
+
conditions.push(eq(oauthConnections.accountInfo, account));
|
|
495
|
+
}
|
|
496
|
+
|
|
419
497
|
if (clientId) {
|
|
420
498
|
const app = getAppByProviderAndClientId(providerKey, clientId);
|
|
421
499
|
if (!app) return undefined;
|
|
422
|
-
|
|
423
|
-
.select()
|
|
424
|
-
.from(oauthConnections)
|
|
425
|
-
.where(
|
|
426
|
-
and(
|
|
427
|
-
eq(oauthConnections.providerKey, providerKey),
|
|
428
|
-
eq(oauthConnections.oauthAppId, app.id),
|
|
429
|
-
eq(oauthConnections.status, "active"),
|
|
430
|
-
),
|
|
431
|
-
)
|
|
432
|
-
.orderBy(desc(oauthConnections.createdAt), sql`rowid DESC`)
|
|
433
|
-
.limit(1)
|
|
434
|
-
.get();
|
|
500
|
+
conditions.push(eq(oauthConnections.oauthAppId, app.id));
|
|
435
501
|
}
|
|
436
502
|
|
|
437
503
|
return db
|
|
438
504
|
.select()
|
|
439
505
|
.from(oauthConnections)
|
|
440
|
-
.where(
|
|
441
|
-
and(
|
|
442
|
-
eq(oauthConnections.providerKey, providerKey),
|
|
443
|
-
eq(oauthConnections.status, "active"),
|
|
444
|
-
),
|
|
445
|
-
)
|
|
506
|
+
.where(and(...conditions))
|
|
446
507
|
.orderBy(desc(oauthConnections.createdAt), sql`rowid DESC`)
|
|
447
508
|
.limit(1)
|
|
448
509
|
.get();
|
|
449
510
|
}
|
|
450
511
|
|
|
451
|
-
/**
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
512
|
+
/** @deprecated Use {@link getActiveConnection} instead. */
|
|
513
|
+
export function getConnectionByProvider(
|
|
514
|
+
providerKey: string,
|
|
515
|
+
clientId?: string,
|
|
516
|
+
): OAuthConnectionRow | undefined {
|
|
517
|
+
return getActiveConnection(providerKey, { clientId });
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/** @deprecated Use {@link getActiveConnection} instead. */
|
|
455
521
|
export function getConnectionByProviderAndAccount(
|
|
456
522
|
providerKey: string,
|
|
457
523
|
accountInfo?: string,
|
|
524
|
+
clientId?: string,
|
|
458
525
|
): OAuthConnectionRow | undefined {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
const db = getDb();
|
|
462
|
-
return db
|
|
463
|
-
.select()
|
|
464
|
-
.from(oauthConnections)
|
|
465
|
-
.where(
|
|
466
|
-
and(
|
|
467
|
-
eq(oauthConnections.providerKey, providerKey),
|
|
468
|
-
eq(oauthConnections.accountInfo, accountInfo),
|
|
469
|
-
eq(oauthConnections.status, "active"),
|
|
470
|
-
),
|
|
471
|
-
)
|
|
472
|
-
.orderBy(desc(oauthConnections.createdAt), sql`rowid DESC`)
|
|
473
|
-
.limit(1)
|
|
474
|
-
.get();
|
|
526
|
+
return getActiveConnection(providerKey, { clientId, account: accountInfo });
|
|
475
527
|
}
|
|
476
528
|
|
|
477
529
|
/**
|
|
@@ -505,7 +557,7 @@ export function listActiveConnectionsByProvider(
|
|
|
505
557
|
export async function isProviderConnected(
|
|
506
558
|
providerKey: string,
|
|
507
559
|
): Promise<boolean> {
|
|
508
|
-
const conn =
|
|
560
|
+
const conn = getActiveConnection(providerKey);
|
|
509
561
|
if (!conn || conn.status !== "active") return false;
|
|
510
562
|
return (
|
|
511
563
|
(await getSecureKeyAsync(oauthConnectionAccessTokenPath(conn.id))) !==
|
|
@@ -623,7 +675,7 @@ export async function disconnectOAuthProvider(
|
|
|
623
675
|
): Promise<"disconnected" | "not-found" | "error"> {
|
|
624
676
|
const conn = connectionId
|
|
625
677
|
? getConnection(connectionId)
|
|
626
|
-
:
|
|
678
|
+
: getActiveConnection(providerKey, { clientId });
|
|
627
679
|
if (!conn) return "not-found";
|
|
628
680
|
|
|
629
681
|
// Wrap the assistant's secure-key functions into the SecureKeyBackend
|
|
@@ -12,7 +12,6 @@ const DEFAULT_OPTIONS = {
|
|
|
12
12
|
providerKey: "integration:google",
|
|
13
13
|
externalId: "ext-123",
|
|
14
14
|
accountInfo: "user@example.com",
|
|
15
|
-
grantedScopes: ["https://www.googleapis.com/auth/gmail.readonly"],
|
|
16
15
|
assistantId: "asst-abc",
|
|
17
16
|
platformBaseUrl: "https://platform.example.com",
|
|
18
17
|
apiKey: "test-api-key",
|
|
@@ -24,7 +24,6 @@ export interface PlatformOAuthConnectionOptions {
|
|
|
24
24
|
providerKey: string;
|
|
25
25
|
externalId: string;
|
|
26
26
|
accountInfo: string | null;
|
|
27
|
-
grantedScopes: string[];
|
|
28
27
|
assistantId: string;
|
|
29
28
|
platformBaseUrl: string;
|
|
30
29
|
apiKey: string;
|
|
@@ -35,18 +34,27 @@ export class PlatformOAuthConnection implements OAuthConnection {
|
|
|
35
34
|
readonly providerKey: string;
|
|
36
35
|
readonly externalId: string;
|
|
37
36
|
readonly accountInfo: string | null;
|
|
38
|
-
readonly grantedScopes: string[];
|
|
39
37
|
|
|
40
38
|
private readonly assistantId: string;
|
|
41
39
|
private readonly platformBaseUrl: string;
|
|
42
40
|
private readonly apiKey: string;
|
|
43
41
|
|
|
44
42
|
constructor(options: PlatformOAuthConnectionOptions) {
|
|
43
|
+
const missing: string[] = [];
|
|
44
|
+
if (!options.platformBaseUrl) missing.push("platform base URL");
|
|
45
|
+
if (!options.apiKey) missing.push("assistant API key");
|
|
46
|
+
if (!options.assistantId) missing.push("assistant ID");
|
|
47
|
+
if (missing.length > 0) {
|
|
48
|
+
throw new BackendError(
|
|
49
|
+
`Platform-managed connection for "${options.providerKey}" cannot be created: missing ${missing.join(", ")}. ` +
|
|
50
|
+
`Log in to the Vellum platform or switch to using your own OAuth app.`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
45
54
|
this.id = options.id;
|
|
46
55
|
this.providerKey = options.providerKey;
|
|
47
56
|
this.externalId = options.externalId;
|
|
48
57
|
this.accountInfo = options.accountInfo;
|
|
49
|
-
this.grantedScopes = options.grantedScopes;
|
|
50
58
|
this.assistantId = options.assistantId;
|
|
51
59
|
this.platformBaseUrl = options.platformBaseUrl.replace(/\/+$/, "");
|
|
52
60
|
this.apiKey = options.apiKey;
|
|
@@ -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).
|