@vellumai/assistant 0.4.48 → 0.4.50
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 +26 -35
- package/README.md +5 -26
- package/docs/architecture/integrations.md +45 -41
- package/docs/architecture/keychain-broker.md +3 -3
- package/docs/architecture/memory.md +180 -119
- package/docs/runbook-trusted-contacts.md +3 -8
- package/hook-templates/debug-prompt-logger/hook.json +1 -1
- package/hook-templates/debug-prompt-logger/run.sh +1 -3
- package/package.json +2 -2
- package/src/__tests__/actor-token-service.test.ts +0 -1
- package/src/__tests__/agent-loop.test.ts +3 -1
- package/src/__tests__/anthropic-provider.test.ts +249 -2
- package/src/__tests__/approval-cascade.test.ts +796 -0
- package/src/__tests__/approval-primitive.test.ts +0 -1
- package/src/__tests__/approval-routes-http.test.ts +4 -0
- package/src/__tests__/assistant-attachments.test.ts +12 -34
- package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +76 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -1
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/canonical-guardian-store.test.ts +95 -0
- package/src/__tests__/channel-guardian.test.ts +0 -2
- package/src/__tests__/channel-readiness-routes.test.ts +15 -6
- package/src/__tests__/channel-readiness-service.test.ts +10 -9
- package/src/__tests__/checker.test.ts +13 -20
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-tools.test.ts +2 -19
- package/src/__tests__/config-schema.test.ts +1 -68
- package/src/__tests__/config-watcher.test.ts +0 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/context-image-dimensions.test.ts +332 -0
- package/src/__tests__/context-memory-e2e.test.ts +11 -100
- package/src/__tests__/context-token-estimator.test.ts +196 -13
- package/src/__tests__/conversation-attention-store.test.ts +0 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +152 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
- package/src/__tests__/credential-metadata-store.test.ts +64 -73
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +13 -7
- package/src/__tests__/credential-vault-unit.test.ts +284 -49
- package/src/__tests__/credential-vault.test.ts +150 -16
- package/src/__tests__/credentials-cli.test.ts +71 -0
- package/src/__tests__/cu-unified-flow.test.ts +532 -0
- package/src/__tests__/date-context.test.ts +93 -77
- package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -39
- package/src/__tests__/heartbeat-service.test.ts +0 -1
- package/src/__tests__/history-repair.test.ts +245 -0
- package/src/__tests__/host-cu-proxy.test.ts +791 -0
- package/src/__tests__/host-shell-tool.test.ts +27 -15
- package/src/__tests__/http-user-message-parity.test.ts +2 -0
- package/src/__tests__/ingress-url-consistency.test.ts +14 -21
- package/src/__tests__/integration-status.test.ts +32 -51
- package/src/__tests__/intent-routing.test.ts +0 -1
- package/src/__tests__/invite-redemption-service.test.ts +65 -1
- package/src/__tests__/invite-routes-http.test.ts +10 -9
- package/src/__tests__/keychain-broker-client.test.ts +14 -46
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
- package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
- package/src/__tests__/memory-recall-quality.test.ts +244 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
- package/src/__tests__/memory-regressions.test.ts +477 -2841
- package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
- package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
- package/src/__tests__/mime-builder.test.ts +28 -0
- package/src/__tests__/native-web-search.test.ts +1 -0
- package/src/__tests__/notification-routing-intent.test.ts +0 -1
- package/src/__tests__/oauth-cli.test.ts +941 -15
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -9
- package/src/__tests__/oauth-scope-policy.test.ts +4 -6
- package/src/__tests__/oauth-store.test.ts +870 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
- package/src/__tests__/provider-error-scenarios.test.ts +0 -1
- package/src/__tests__/provider-streaming.benchmark.test.ts +0 -1
- package/src/__tests__/public-ingress-urls.test.ts +15 -21
- package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
- package/src/__tests__/recording-handler.test.ts +3 -4
- package/src/__tests__/registry.test.ts +2 -3
- package/src/__tests__/relay-server.test.ts +46 -1
- package/src/__tests__/runtime-events-sse.test.ts +55 -7
- package/src/__tests__/schedule-store.test.ts +0 -1
- package/src/__tests__/schedule-tools.test.ts +32 -0
- package/src/__tests__/scheduler-recurrence.test.ts +0 -1
- package/src/__tests__/scoped-approval-grants.test.ts +0 -1
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/secret-ingress-handler.test.ts +0 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -0
- package/src/__tests__/secure-keys.test.ts +7 -2
- package/src/__tests__/send-endpoint-busy.test.ts +24 -6
- package/src/__tests__/sequence-store.test.ts +0 -1
- package/src/__tests__/session-abort-tool-results.test.ts +1 -14
- package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
- package/src/__tests__/session-agent-loop.test.ts +19 -15
- package/src/__tests__/session-confirmation-signals.test.ts +1 -15
- package/src/__tests__/session-error.test.ts +124 -2
- package/src/__tests__/session-history-web-search.test.ts +918 -0
- package/src/__tests__/session-init.benchmark.test.ts +4 -5
- package/src/__tests__/session-pre-run-repair.test.ts +1 -14
- package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
- package/src/__tests__/session-queue.test.ts +37 -27
- package/src/__tests__/session-runtime-assembly.test.ts +54 -0
- package/src/__tests__/session-slash-known.test.ts +1 -15
- package/src/__tests__/session-slash-queue.test.ts +1 -15
- package/src/__tests__/session-slash-unknown.test.ts +1 -15
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
- package/src/__tests__/session-workspace-injection.test.ts +3 -37
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
- package/src/__tests__/skill-include-graph.test.ts +66 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -1
- package/src/__tests__/skill-load-tool.test.ts +149 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
- package/src/__tests__/skills-install-extract.test.ts +93 -0
- package/src/__tests__/skills-uninstall.test.ts +1 -1
- package/src/__tests__/skills.test.ts +3 -3
- package/src/__tests__/skillssh-registry.test.ts +451 -0
- package/src/__tests__/slack-channel-config.test.ts +67 -3
- package/src/__tests__/slack-share-routes.test.ts +17 -19
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/telegram-invite-adapter.test.ts +18 -22
- package/src/__tests__/terminal-tools.test.ts +4 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/tool-approval-handler.test.ts +0 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
- package/src/__tests__/trust-store-pattern-matches.test.ts +29 -0
- package/src/__tests__/trust-store.test.ts +7 -13
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/twilio-routes.test.ts +0 -16
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
- package/src/__tests__/voice-invite-redemption.test.ts +32 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
- package/src/agent/ax-tree-compaction.test.ts +286 -0
- package/src/agent/loop.ts +104 -131
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-request-resolvers.ts +14 -2
- package/src/bundler/compiler-tools.ts +66 -2
- package/src/calls/call-domain.ts +133 -6
- package/src/calls/call-store.ts +6 -0
- package/src/calls/relay-server.ts +52 -18
- package/src/calls/relay-setup-router.ts +17 -1
- package/src/calls/twilio-config.ts +3 -8
- package/src/calls/twilio-routes.ts +1 -2
- package/src/calls/types.ts +3 -1
- package/src/calls/voice-ingress-preflight.ts +1 -1
- package/src/cli/commands/browser-relay.ts +18 -12
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/credentials.ts +101 -15
- package/src/cli/commands/doctor.ts +4 -3
- package/src/cli/commands/mcp.ts +46 -59
- package/src/cli/commands/memory.ts +16 -165
- package/src/cli/commands/oauth/apps.ts +284 -0
- package/src/cli/commands/oauth/connections.ts +633 -0
- package/src/cli/commands/oauth/index.ts +52 -0
- package/src/cli/commands/oauth/providers.ts +256 -0
- package/src/cli/commands/sessions.ts +5 -2
- package/src/cli/commands/skills.ts +177 -339
- package/src/cli/http-client.ts +0 -20
- package/src/cli/main-screen.tsx +2 -2
- package/src/cli/program.ts +6 -11
- package/src/cli/reference.ts +1 -3
- package/src/cli.ts +4 -10
- package/src/config/assistant-feature-flags.ts +0 -3
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +3 -6
- package/src/config/bundled-skills/computer-use/TOOLS.json +23 -5
- package/src/config/bundled-skills/computer-use/tools/{computer-use-request-control.ts → computer-use-observe.ts} +1 -5
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +21 -16
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -4
- package/src/config/bundled-skills/settings/SKILL.md +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +2 -8
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +5 -33
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/env-registry.ts +14 -83
- package/src/config/env.ts +11 -50
- package/src/config/feature-flag-registry.json +16 -16
- package/src/config/loader.ts +0 -6
- package/src/config/schema.ts +4 -13
- package/src/config/schemas/memory-lifecycle.ts +0 -9
- package/src/config/schemas/memory-processing.ts +0 -180
- package/src/config/schemas/memory-retrieval.ts +32 -104
- package/src/config/schemas/memory.ts +0 -10
- package/src/config/skills.ts +21 -2
- package/src/config/types.ts +0 -4
- package/src/context/image-dimensions.ts +229 -0
- package/src/context/token-estimator.ts +75 -12
- package/src/context/window-manager.ts +53 -11
- package/src/daemon/assistant-attachments.ts +1 -13
- package/src/daemon/config-watcher.ts +61 -3
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +114 -31
- package/src/daemon/handlers/config-ingress.ts +8 -33
- package/src/daemon/handlers/config-slack-channel.ts +49 -46
- package/src/daemon/handlers/config-telegram.ts +32 -16
- package/src/daemon/handlers/sessions.ts +27 -36
- package/src/daemon/handlers/shared.ts +0 -130
- package/src/daemon/handlers/skills.ts +20 -1
- package/src/daemon/history-repair.ts +72 -8
- package/src/daemon/host-cu-proxy.ts +430 -0
- package/src/daemon/lifecycle.ts +67 -71
- package/src/daemon/mcp-reload-service.ts +2 -2
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/computer-use.ts +1 -129
- package/src/daemon/message-types/host-cu.ts +19 -0
- package/src/daemon/message-types/memory.ts +4 -16
- package/src/daemon/message-types/messages.ts +4 -0
- package/src/daemon/message-types/sessions.ts +4 -0
- package/src/daemon/server.ts +25 -21
- package/src/daemon/session-agent-loop-handlers.ts +40 -0
- package/src/daemon/session-agent-loop.ts +334 -48
- package/src/daemon/session-attachments.ts +1 -2
- package/src/daemon/session-error.ts +89 -6
- package/src/daemon/session-history.ts +17 -7
- package/src/daemon/session-media-retry.ts +6 -2
- package/src/daemon/session-memory.ts +69 -149
- package/src/daemon/session-process.ts +10 -1
- package/src/daemon/session-runtime-assembly.ts +49 -19
- package/src/daemon/session-slash.ts +1 -1
- package/src/daemon/session-surfaces.ts +43 -28
- package/src/daemon/session-tool-setup.ts +9 -10
- package/src/daemon/session.ts +150 -17
- package/src/daemon/tool-side-effects.ts +2 -8
- package/src/daemon/watch-handler.ts +2 -2
- package/src/events/tool-metrics-listener.ts +2 -2
- package/src/hooks/manager.ts +1 -4
- package/src/inbound/public-ingress-urls.ts +7 -7
- package/src/instrument.ts +61 -1
- package/src/logfire.ts +16 -5
- package/src/memory/admin.ts +2 -191
- package/src/memory/canonical-guardian-store.ts +38 -2
- package/src/memory/conversation-crud.ts +0 -33
- package/src/memory/conversation-key-store.ts +21 -0
- package/src/memory/conversation-queries.ts +22 -3
- package/src/memory/db-init.ts +32 -0
- package/src/memory/embedding-backend.ts +84 -8
- package/src/memory/embedding-types.ts +9 -1
- package/src/memory/indexer.ts +7 -46
- package/src/memory/items-extractor.ts +274 -76
- package/src/memory/job-handlers/backfill.ts +2 -127
- package/src/memory/job-handlers/cleanup.ts +2 -16
- package/src/memory/job-handlers/extraction.ts +2 -138
- package/src/memory/job-handlers/index-maintenance.ts +1 -6
- package/src/memory/job-handlers/summarization.ts +3 -148
- package/src/memory/job-utils.ts +21 -59
- package/src/memory/jobs-store.ts +1 -159
- package/src/memory/jobs-worker.ts +9 -52
- package/src/memory/migrations/104-core-indexes.ts +3 -3
- package/src/memory/migrations/149-oauth-tables.ts +62 -0
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
- package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
- package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
- package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
- package/src/memory/migrations/154-drop-fts.ts +20 -0
- package/src/memory/migrations/155-drop-conflicts.ts +7 -0
- package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/qdrant-client.ts +148 -51
- package/src/memory/raw-query.ts +1 -1
- package/src/memory/retriever.test.ts +294 -273
- package/src/memory/retriever.ts +421 -645
- package/src/memory/schema/calls.ts +2 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/memory-core.ts +3 -48
- package/src/memory/schema/oauth.ts +67 -0
- package/src/memory/search/formatting.ts +263 -176
- package/src/memory/search/lexical.ts +1 -254
- package/src/memory/search/ranking.ts +0 -455
- package/src/memory/search/semantic.ts +100 -14
- package/src/memory/search/staleness.ts +47 -0
- package/src/memory/search/tier-classifier.ts +21 -0
- package/src/memory/search/types.ts +15 -77
- package/src/memory/task-memory-cleanup.ts +4 -6
- package/src/messaging/provider.ts +4 -4
- package/src/messaging/providers/gmail/client.ts +82 -2
- package/src/messaging/providers/gmail/mime-builder.ts +17 -7
- package/src/messaging/providers/gmail/people-client.ts +10 -10
- package/src/messaging/providers/telegram-bot/adapter.ts +17 -17
- package/src/messaging/providers/whatsapp/adapter.ts +11 -8
- package/src/messaging/registry.ts +2 -32
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/signal.ts +4 -5
- package/src/oauth/byo-connection.test.ts +133 -25
- package/src/oauth/byo-connection.ts +22 -6
- package/src/oauth/connect-orchestrator.ts +113 -57
- package/src/oauth/connect-types.ts +17 -23
- package/src/oauth/connection-resolver.ts +35 -11
- package/src/oauth/connection.ts +1 -1
- package/src/oauth/manual-token-connection.ts +104 -0
- package/src/oauth/oauth-store.ts +582 -0
- package/src/oauth/platform-connection.test.ts +29 -0
- package/src/oauth/platform-connection.ts +6 -5
- package/src/oauth/provider-behaviors.ts +124 -0
- package/src/oauth/scope-policy.ts +9 -2
- package/src/oauth/seed-providers.ts +167 -0
- package/src/oauth/token-persistence.ts +81 -77
- package/src/permissions/checker.ts +3 -3
- package/src/permissions/defaults.ts +1 -1
- package/src/permissions/prompter.ts +10 -1
- package/src/permissions/trust-store.ts +36 -1
- package/src/playbooks/playbook-compiler.ts +1 -1
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +3 -1
- package/src/prompts/system-prompt.ts +46 -42
- package/src/providers/anthropic/client.ts +59 -20
- package/src/providers/retry.ts +1 -27
- package/src/providers/types.ts +7 -1
- package/src/runtime/AGENTS.md +9 -0
- package/src/runtime/auth/route-policy.ts +6 -6
- package/src/runtime/channel-reply-delivery.ts +0 -40
- package/src/runtime/gateway-client.ts +0 -7
- package/src/runtime/guardian-reply-router.ts +24 -22
- package/src/runtime/http-server.ts +10 -8
- package/src/runtime/http-types.ts +2 -2
- package/src/runtime/invite-redemption-service.ts +19 -1
- package/src/runtime/invite-service.ts +25 -0
- package/src/runtime/middleware/twilio-validation.ts +1 -11
- package/src/runtime/pending-interactions.ts +14 -12
- package/src/runtime/routes/brain-graph-routes.ts +10 -90
- package/src/runtime/routes/channel-delivery-routes.ts +0 -1
- package/src/runtime/routes/conversation-routes.ts +81 -19
- package/src/runtime/routes/events-routes.ts +21 -11
- package/src/runtime/routes/host-cu-routes.ts +97 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +12 -111
- package/src/runtime/routes/integrations/slack/share.ts +6 -7
- package/src/runtime/routes/log-export-routes.ts +126 -8
- package/src/runtime/routes/memory-item-routes.test.ts +754 -0
- package/src/runtime/routes/memory-item-routes.ts +503 -0
- package/src/runtime/routes/session-management-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +55 -48
- package/src/runtime/routes/surface-action-routes.ts +1 -1
- package/src/runtime/routes/trust-rules-routes.ts +14 -0
- package/src/runtime/routes/watch-routes.ts +128 -0
- package/src/runtime/routes/workspace-routes.ts +2 -1
- package/src/schedule/integration-status.ts +10 -9
- package/src/security/credential-key.ts +0 -156
- package/src/security/keychain-broker-client.ts +22 -10
- package/src/security/oauth2.ts +1 -1
- package/src/security/secure-keys.ts +25 -3
- package/src/security/token-manager.ts +137 -64
- package/src/skills/catalog-install.ts +414 -0
- package/src/skills/include-graph.ts +32 -0
- package/src/skills/skillssh-registry.ts +503 -0
- package/src/telegram/bot-username.ts +2 -3
- package/src/tools/assets/search.ts +5 -1
- package/src/tools/browser/network-recorder.ts +1 -1
- package/src/tools/browser/network-recording-types.ts +1 -1
- package/src/tools/computer-use/definitions.ts +36 -11
- package/src/tools/computer-use/registry.ts +5 -6
- package/src/tools/credentials/broker.ts +1 -2
- package/src/tools/credentials/metadata-store.ts +17 -121
- package/src/tools/credentials/vault.ts +92 -167
- package/src/tools/memory/definitions.ts +4 -13
- package/src/tools/memory/handlers.test.ts +83 -103
- package/src/tools/memory/handlers.ts +50 -85
- package/src/tools/registry.ts +2 -7
- package/src/tools/schedule/create.ts +8 -1
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/skills/load.ts +85 -3
- package/src/tools/watch/watch-state.ts +0 -12
- package/src/util/logger.ts +7 -41
- package/src/util/platform.ts +9 -28
- package/src/watcher/providers/google-calendar.ts +2 -1
- package/src/__tests__/clarification-resolver.test.ts +0 -193
- package/src/__tests__/computer-use-session-compaction.test.ts +0 -143
- package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -322
- package/src/__tests__/computer-use-session-working-dir.test.ts +0 -166
- package/src/__tests__/computer-use-skill-baseline.test.ts +0 -78
- package/src/__tests__/computer-use-skill-endstate.test.ts +0 -105
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -249
- package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
- package/src/__tests__/conflict-policy.test.ts +0 -269
- package/src/__tests__/conflict-store.test.ts +0 -372
- package/src/__tests__/contradiction-checker.test.ts +0 -361
- package/src/__tests__/entity-extractor.test.ts +0 -211
- package/src/__tests__/entity-search.test.ts +0 -1117
- package/src/__tests__/profile-compiler.test.ts +0 -392
- package/src/__tests__/ride-shotgun-handler.test.ts +0 -452
- package/src/__tests__/session-conflict-gate.test.ts +0 -1228
- package/src/__tests__/session-profile-injection.test.ts +0 -557
- package/src/cli/commands/dev.ts +0 -129
- package/src/cli/commands/map.ts +0 -391
- package/src/cli/commands/oauth.ts +0 -77
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
- package/src/daemon/computer-use-session.ts +0 -1026
- package/src/daemon/ride-shotgun-handler.ts +0 -569
- package/src/daemon/session-conflict-gate.ts +0 -167
- package/src/daemon/session-dynamic-profile.ts +0 -77
- package/src/memory/clarification-resolver.ts +0 -417
- package/src/memory/conflict-intent.ts +0 -205
- package/src/memory/conflict-policy.ts +0 -127
- package/src/memory/conflict-store.ts +0 -410
- package/src/memory/contradiction-checker.ts +0 -508
- package/src/memory/entity-extractor.ts +0 -535
- package/src/memory/format-recall.ts +0 -47
- package/src/memory/fts-reconciler.ts +0 -165
- package/src/memory/job-handlers/conflict.ts +0 -200
- package/src/memory/profile-compiler.ts +0 -195
- package/src/memory/recall-cache.ts +0 -117
- package/src/memory/search/entity.ts +0 -535
- package/src/memory/search/query-expansion.test.ts +0 -70
- package/src/memory/search/query-expansion.ts +0 -118
- package/src/oauth/provider-base-urls.ts +0 -21
- package/src/oauth/provider-profiles.ts +0 -192
- package/src/prompts/computer-use-prompt.ts +0 -98
- package/src/runtime/routes/computer-use-routes.ts +0 -641
- package/src/runtime/routes/mcp-routes.ts +0 -20
- package/src/runtime/telegram-streaming-delivery.test.ts +0 -729
- package/src/runtime/telegram-streaming-delivery.ts +0 -393
- package/src/tools/computer-use/request-computer-control.ts +0 -56
|
@@ -63,14 +63,54 @@ mock.module("../security/oauth2.js", () => {
|
|
|
63
63
|
};
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Mock oauth-store — token-manager reads refresh config from SQLite
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
/** Mutable per-test map of provider connections for getConnectionByProvider */
|
|
71
|
+
const mockConnections = new Map<
|
|
72
|
+
string,
|
|
73
|
+
{
|
|
74
|
+
id: string;
|
|
75
|
+
providerKey: string;
|
|
76
|
+
oauthAppId: string;
|
|
77
|
+
expiresAt: number | null;
|
|
78
|
+
grantedScopes?: string;
|
|
79
|
+
accountInfo?: string | null;
|
|
80
|
+
}
|
|
81
|
+
>();
|
|
82
|
+
const mockApps = new Map<
|
|
83
|
+
string,
|
|
84
|
+
{
|
|
85
|
+
id: string;
|
|
86
|
+
providerKey: string;
|
|
87
|
+
clientId: string;
|
|
88
|
+
clientSecretCredentialPath: string;
|
|
89
|
+
}
|
|
90
|
+
>();
|
|
91
|
+
const mockProviders = new Map<
|
|
92
|
+
string,
|
|
93
|
+
{
|
|
94
|
+
key: string;
|
|
95
|
+
tokenUrl: string;
|
|
96
|
+
tokenEndpointAuthMethod?: string;
|
|
97
|
+
baseUrl?: string;
|
|
98
|
+
}
|
|
99
|
+
>();
|
|
100
|
+
|
|
101
|
+
mock.module("./oauth-store.js", () => ({
|
|
102
|
+
getConnectionByProvider: (service: string) => mockConnections.get(service),
|
|
103
|
+
getApp: (id: string) => mockApps.get(id),
|
|
104
|
+
getProvider: (key: string) => mockProviders.get(key),
|
|
105
|
+
updateConnection: () => {},
|
|
106
|
+
getMostRecentAppByProvider: () => undefined,
|
|
107
|
+
listConnections: () => [],
|
|
108
|
+
}));
|
|
109
|
+
|
|
66
110
|
// ---------------------------------------------------------------------------
|
|
67
111
|
// Imports (after mocks)
|
|
68
112
|
// ---------------------------------------------------------------------------
|
|
69
113
|
|
|
70
|
-
import {
|
|
71
|
-
_resetMigrationFlag,
|
|
72
|
-
credentialKey,
|
|
73
|
-
} from "../security/credential-key.js";
|
|
74
114
|
import { setSecureKey } from "../security/secure-keys.js";
|
|
75
115
|
import {
|
|
76
116
|
_resetInflightRefreshes,
|
|
@@ -88,7 +128,6 @@ import { resolveOAuthConnection } from "./connection-resolver.js";
|
|
|
88
128
|
// ---------------------------------------------------------------------------
|
|
89
129
|
|
|
90
130
|
const originalFetch = globalThis.fetch;
|
|
91
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
131
|
let mockFetch: ReturnType<typeof mock<any>>;
|
|
93
132
|
|
|
94
133
|
// ---------------------------------------------------------------------------
|
|
@@ -110,7 +149,10 @@ beforeEach(() => {
|
|
|
110
149
|
_resetBackend();
|
|
111
150
|
_resetRefreshBreakers();
|
|
112
151
|
_resetInflightRefreshes();
|
|
113
|
-
|
|
152
|
+
// Clear mock oauth-store maps
|
|
153
|
+
mockConnections.clear();
|
|
154
|
+
mockApps.clear();
|
|
155
|
+
mockProviders.clear();
|
|
114
156
|
|
|
115
157
|
// Default mock fetch returning 200 JSON
|
|
116
158
|
mockFetch = mock(() =>
|
|
@@ -139,16 +181,41 @@ function setupCredential(
|
|
|
139
181
|
service: string,
|
|
140
182
|
opts?: { expiresAt?: number; grantedScopes?: string[] },
|
|
141
183
|
) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
184
|
+
// Seed mock oauth-store maps so token-manager can resolve refresh config
|
|
185
|
+
const appId = `app-${service}`;
|
|
186
|
+
const connId = `conn-${service}`;
|
|
187
|
+
mockProviders.set(service, {
|
|
188
|
+
key: service,
|
|
189
|
+
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
190
|
+
// Only well-known providers (gmail) have a baseUrl; custom services don't
|
|
191
|
+
baseUrl:
|
|
192
|
+
service === "integration:gmail"
|
|
193
|
+
? "https://gmail.googleapis.com/gmail/v1/users/me"
|
|
194
|
+
: undefined,
|
|
195
|
+
});
|
|
196
|
+
mockApps.set(appId, {
|
|
197
|
+
id: appId,
|
|
198
|
+
providerKey: service,
|
|
199
|
+
clientId: "test-client-id",
|
|
200
|
+
clientSecretCredentialPath: `oauth_app/${appId}/client_secret`,
|
|
201
|
+
});
|
|
202
|
+
mockConnections.set(service, {
|
|
203
|
+
id: connId,
|
|
204
|
+
providerKey: service,
|
|
205
|
+
oauthAppId: appId,
|
|
146
206
|
expiresAt: opts?.expiresAt ?? Date.now() + 3600 * 1000,
|
|
147
|
-
grantedScopes: opts?.grantedScopes ?? ["read", "write"],
|
|
148
|
-
|
|
149
|
-
oauth2ClientId: "test-client-id",
|
|
150
|
-
hasRefreshToken: true,
|
|
207
|
+
grantedScopes: JSON.stringify(opts?.grantedScopes ?? ["read", "write"]),
|
|
208
|
+
accountInfo: null,
|
|
151
209
|
});
|
|
210
|
+
// Store access token in oauth-store key format
|
|
211
|
+
setSecureKey(`oauth_connection/${connId}/access_token`, "test-access-token");
|
|
212
|
+
// Store refresh token and client_secret in secure keys (token-manager reads them)
|
|
213
|
+
setSecureKey(
|
|
214
|
+
`oauth_connection/${connId}/refresh_token`,
|
|
215
|
+
"test-refresh-token",
|
|
216
|
+
);
|
|
217
|
+
setSecureKey(`oauth_app/${appId}/client_secret`, "test-client-secret");
|
|
218
|
+
upsertCredentialMetadata(service, "access_token", {});
|
|
152
219
|
}
|
|
153
220
|
|
|
154
221
|
function createConnection(service = "integration:gmail"): BYOOAuthConnection {
|
|
@@ -185,10 +252,10 @@ describe("BYOOAuthConnection", () => {
|
|
|
185
252
|
expect(url).toBe(
|
|
186
253
|
"https://gmail.googleapis.com/gmail/v1/users/me/messages",
|
|
187
254
|
);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
255
|
+
const headers = (init as RequestInit).headers as Headers;
|
|
256
|
+
expect(headers.get("Authorization")).toBe("Bearer test-access-token");
|
|
257
|
+
// GET requests have no body, so Content-Type should not be set
|
|
258
|
+
expect(headers.has("Content-Type")).toBe(false);
|
|
192
259
|
expect((init as RequestInit).method).toBe("GET");
|
|
193
260
|
});
|
|
194
261
|
|
|
@@ -237,6 +304,9 @@ describe("BYOOAuthConnection", () => {
|
|
|
237
304
|
JSON.stringify({ raw: "base64-encoded-email" }),
|
|
238
305
|
);
|
|
239
306
|
expect((init as RequestInit).method).toBe("POST");
|
|
307
|
+
// POST requests with a body should include Content-Type
|
|
308
|
+
const headers = (init as RequestInit).headers as Headers;
|
|
309
|
+
expect(headers.get("Content-Type")).toBe("application/json");
|
|
240
310
|
});
|
|
241
311
|
|
|
242
312
|
test("retries once on 401 response", async () => {
|
|
@@ -336,10 +406,9 @@ describe("BYOOAuthConnection", () => {
|
|
|
336
406
|
});
|
|
337
407
|
|
|
338
408
|
const [, init] = mockFetch.mock.calls[0];
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
});
|
|
409
|
+
const headers = (init as RequestInit).headers as Headers;
|
|
410
|
+
expect(headers.get("X-Custom-Header")).toBe("custom-value");
|
|
411
|
+
expect(headers.get("Authorization")).toBe("Bearer test-access-token");
|
|
343
412
|
});
|
|
344
413
|
});
|
|
345
414
|
|
|
@@ -361,9 +430,10 @@ describe("BYOOAuthConnection", () => {
|
|
|
361
430
|
|
|
362
431
|
// The request should use the refreshed token
|
|
363
432
|
const [, init] = mockFetch.mock.calls[0];
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
433
|
+
const headers = (init as RequestInit).headers as Headers;
|
|
434
|
+
expect(headers.get("Authorization")).toBe(
|
|
435
|
+
"Bearer refreshed-access-token",
|
|
436
|
+
);
|
|
367
437
|
});
|
|
368
438
|
});
|
|
369
439
|
|
|
@@ -433,4 +503,42 @@ describe("resolveOAuthConnection", () => {
|
|
|
433
503
|
/No base URL configured for "integration:custom-service"/,
|
|
434
504
|
);
|
|
435
505
|
});
|
|
506
|
+
|
|
507
|
+
test("resolves base URL via app's canonical providerKey for custom credential_service", () => {
|
|
508
|
+
// Set up a well-known provider with a baseUrl
|
|
509
|
+
mockProviders.set("github", {
|
|
510
|
+
key: "github",
|
|
511
|
+
tokenUrl: "https://github.com/login/oauth/access_token",
|
|
512
|
+
baseUrl: "https://api.github.com",
|
|
513
|
+
});
|
|
514
|
+
// The custom credential service has no provider entry of its own
|
|
515
|
+
// (getProvider("integration:github-work") returns undefined)
|
|
516
|
+
|
|
517
|
+
// App points to the canonical "github" provider
|
|
518
|
+
const appId = "app-github-work";
|
|
519
|
+
mockApps.set(appId, {
|
|
520
|
+
id: appId,
|
|
521
|
+
providerKey: "github",
|
|
522
|
+
clientId: "test-client-id",
|
|
523
|
+
clientSecretCredentialPath: `oauth_app/${appId}/client_secret`,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
// Connection uses the custom credential service as its providerKey
|
|
527
|
+
const connId = "conn-github-work";
|
|
528
|
+
mockConnections.set("integration:github-work", {
|
|
529
|
+
id: connId,
|
|
530
|
+
providerKey: "integration:github-work",
|
|
531
|
+
oauthAppId: appId,
|
|
532
|
+
expiresAt: Date.now() + 3600 * 1000,
|
|
533
|
+
grantedScopes: JSON.stringify(["repo"]),
|
|
534
|
+
accountInfo: null,
|
|
535
|
+
});
|
|
536
|
+
setSecureKey(`oauth_connection/${connId}/access_token`, "ghp-test-token");
|
|
537
|
+
|
|
538
|
+
const conn = resolveOAuthConnection("integration:github-work");
|
|
539
|
+
|
|
540
|
+
expect(conn).toBeInstanceOf(BYOOAuthConnection);
|
|
541
|
+
expect(conn.providerKey).toBe("integration:github-work");
|
|
542
|
+
expect(conn.grantedScopes).toEqual(["repo"]);
|
|
543
|
+
});
|
|
436
544
|
});
|
|
@@ -53,7 +53,14 @@ export class BYOOAuthConnection implements OAuthConnection {
|
|
|
53
53
|
let fullUrl = `${effectiveBaseUrl}${req.path}`;
|
|
54
54
|
|
|
55
55
|
if (req.query && Object.keys(req.query).length > 0) {
|
|
56
|
-
const params = new URLSearchParams(
|
|
56
|
+
const params = new URLSearchParams();
|
|
57
|
+
for (const [key, value] of Object.entries(req.query)) {
|
|
58
|
+
if (Array.isArray(value)) {
|
|
59
|
+
for (const v of value) params.append(key, v);
|
|
60
|
+
} else {
|
|
61
|
+
params.append(key, value);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
57
64
|
fullUrl += `?${params.toString()}`;
|
|
58
65
|
}
|
|
59
66
|
|
|
@@ -62,13 +69,22 @@ export class BYOOAuthConnection implements OAuthConnection {
|
|
|
62
69
|
"Making authenticated request",
|
|
63
70
|
);
|
|
64
71
|
|
|
72
|
+
// Use the Headers API for case-insensitive merging. Set defaults
|
|
73
|
+
// first so caller-supplied headers (in any casing) override them.
|
|
74
|
+
const headers = new Headers();
|
|
75
|
+
if (req.body) {
|
|
76
|
+
headers.set("Content-Type", "application/json");
|
|
77
|
+
}
|
|
78
|
+
if (req.headers) {
|
|
79
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
80
|
+
headers.set(key, value);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
84
|
+
|
|
65
85
|
const resp = await fetch(fullUrl, {
|
|
66
86
|
method: req.method,
|
|
67
|
-
headers
|
|
68
|
-
...(req.body ? { "Content-Type": "application/json" } : {}),
|
|
69
|
-
...req.headers,
|
|
70
|
-
Authorization: `Bearer ${token}`,
|
|
71
|
-
},
|
|
87
|
+
headers,
|
|
72
88
|
body: req.body ? JSON.stringify(req.body) : undefined,
|
|
73
89
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
74
90
|
});
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* - Ensuring metadata is writable (assertMetadataWritable)
|
|
13
13
|
*
|
|
14
14
|
* The orchestrator handles:
|
|
15
|
-
* - Provider
|
|
15
|
+
* - Provider config resolution (from DB)
|
|
16
16
|
* - Scope policy enforcement
|
|
17
17
|
* - Building the OAuth2Config
|
|
18
18
|
* - Running the interactive or deferred flow
|
|
@@ -23,13 +23,54 @@
|
|
|
23
23
|
import type { TokenEndpointAuthMethod } from "../security/oauth2.js";
|
|
24
24
|
import { prepareOAuth2Flow, startOAuth2Flow } from "../security/oauth2.js";
|
|
25
25
|
import { getLogger } from "../util/logger.js";
|
|
26
|
-
import type {
|
|
27
|
-
|
|
26
|
+
import type {
|
|
27
|
+
OAuthConnectResult,
|
|
28
|
+
OAuthProviderBehavior,
|
|
29
|
+
OAuthScopePolicy,
|
|
30
|
+
} from "./connect-types.js";
|
|
31
|
+
import { getProvider } from "./oauth-store.js";
|
|
32
|
+
import { getProviderBehavior, resolveService } from "./provider-behaviors.js";
|
|
28
33
|
import { resolveScopes } from "./scope-policy.js";
|
|
29
34
|
import { storeOAuth2Tokens } from "./token-persistence.js";
|
|
30
35
|
|
|
31
36
|
const log = getLogger("oauth-connect-orchestrator");
|
|
32
37
|
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Helpers
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Look up the code-side behavioral fields for a provider.
|
|
44
|
+
* Returns an empty object when no behavior is registered.
|
|
45
|
+
*/
|
|
46
|
+
function resolveBehavior(providerKey: string): {
|
|
47
|
+
identityVerifier?: OAuthProviderBehavior["identityVerifier"];
|
|
48
|
+
setup?: OAuthProviderBehavior["setup"];
|
|
49
|
+
setupSkillId?: string;
|
|
50
|
+
postConnectHookId?: string;
|
|
51
|
+
injectionTemplates?: OAuthProviderBehavior["injectionTemplates"];
|
|
52
|
+
} {
|
|
53
|
+
const behavior = getProviderBehavior(providerKey);
|
|
54
|
+
if (!behavior) return {};
|
|
55
|
+
return {
|
|
56
|
+
identityVerifier: behavior.identityVerifier,
|
|
57
|
+
setup: behavior.setup,
|
|
58
|
+
setupSkillId: behavior.setupSkillId,
|
|
59
|
+
postConnectHookId: behavior.postConnectHookId,
|
|
60
|
+
injectionTemplates: behavior.injectionTemplates,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Safely parse a JSON string, returning a fallback on failure or null/undefined input. */
|
|
65
|
+
function safeJsonParse<T>(value: string | null | undefined, fallback: T): T {
|
|
66
|
+
if (value == null) return fallback;
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(value) as T;
|
|
69
|
+
} catch {
|
|
70
|
+
return fallback;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
33
74
|
// ---------------------------------------------------------------------------
|
|
34
75
|
// Options
|
|
35
76
|
// ---------------------------------------------------------------------------
|
|
@@ -63,15 +104,6 @@ export interface OAuthConnectOptions {
|
|
|
63
104
|
accountInfo?: string;
|
|
64
105
|
error?: string;
|
|
65
106
|
}) => void;
|
|
66
|
-
|
|
67
|
-
// Optional overrides — when provided, these take precedence over the
|
|
68
|
-
// provider profile. This lets callers connect custom / unknown providers.
|
|
69
|
-
authUrl?: string;
|
|
70
|
-
tokenUrl?: string;
|
|
71
|
-
scopes?: string[];
|
|
72
|
-
extraParams?: Record<string, string>;
|
|
73
|
-
userinfoUrl?: string;
|
|
74
|
-
tokenEndpointAuthMethod?: TokenEndpointAuthMethod;
|
|
75
107
|
}
|
|
76
108
|
|
|
77
109
|
// ---------------------------------------------------------------------------
|
|
@@ -90,42 +122,69 @@ export async function orchestrateOAuthConnect(
|
|
|
90
122
|
options: OAuthConnectOptions,
|
|
91
123
|
): Promise<OAuthConnectResult> {
|
|
92
124
|
const resolvedService = resolveService(options.service);
|
|
93
|
-
const profile = getProviderProfile(resolvedService);
|
|
94
125
|
|
|
95
|
-
//
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
126
|
+
// Read provider config from the DB
|
|
127
|
+
const providerRow = getProvider(resolvedService);
|
|
128
|
+
if (!providerRow) {
|
|
129
|
+
return {
|
|
130
|
+
success: false,
|
|
131
|
+
error: `No OAuth provider registered for "${resolvedService}". Ensure the provider is seeded in the database.`,
|
|
132
|
+
safeError: true,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Behavioral/code-side fields come from the behavior registry
|
|
137
|
+
const behavior = resolveBehavior(resolvedService);
|
|
102
138
|
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
139
|
+
// Deserialize JSON fields from the DB row
|
|
140
|
+
const dbDefaultScopes = safeJsonParse<string[]>(
|
|
141
|
+
providerRow.defaultScopes,
|
|
142
|
+
[],
|
|
143
|
+
);
|
|
144
|
+
const dbScopePolicy = safeJsonParse<OAuthScopePolicy>(
|
|
145
|
+
providerRow.scopePolicy,
|
|
146
|
+
{
|
|
147
|
+
allowAdditionalScopes: false,
|
|
148
|
+
allowedOptionalScopes: [],
|
|
149
|
+
forbiddenScopes: [],
|
|
150
|
+
},
|
|
151
|
+
);
|
|
152
|
+
const dbExtraParams = safeJsonParse<Record<string, string> | undefined>(
|
|
153
|
+
providerRow.extraParams,
|
|
154
|
+
undefined,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// Resolve all protocol-level config from the DB
|
|
158
|
+
const authUrl = providerRow.authUrl;
|
|
159
|
+
const tokenUrl = providerRow.tokenUrl;
|
|
160
|
+
const extraParams = dbExtraParams;
|
|
161
|
+
const userinfoUrl = providerRow.userinfoUrl ?? undefined;
|
|
162
|
+
const tokenEndpointAuthMethod = providerRow.tokenEndpointAuthMethod as
|
|
163
|
+
| TokenEndpointAuthMethod
|
|
164
|
+
| undefined;
|
|
165
|
+
const callbackTransport =
|
|
166
|
+
(providerRow.callbackTransport as "loopback" | "gateway" | null) ??
|
|
167
|
+
"gateway";
|
|
168
|
+
const loopbackPort = providerRow.loopbackPort;
|
|
169
|
+
|
|
170
|
+
// Resolve scopes via the scope policy engine
|
|
171
|
+
const scopeProfile = {
|
|
172
|
+
service: resolvedService,
|
|
173
|
+
defaultScopes: dbDefaultScopes,
|
|
174
|
+
scopePolicy: dbScopePolicy,
|
|
175
|
+
};
|
|
176
|
+
const scopeResult = resolveScopes(scopeProfile, options.requestedScopes);
|
|
177
|
+
if (!scopeResult.ok) {
|
|
178
|
+
const guidance = scopeResult.allowedScopes
|
|
179
|
+
? ` Allowed scopes: ${scopeResult.allowedScopes.join(", ")}`
|
|
180
|
+
: "";
|
|
123
181
|
return {
|
|
124
182
|
success: false,
|
|
125
|
-
error:
|
|
183
|
+
error: `${scopeResult.error}${guidance}`,
|
|
126
184
|
safeError: true,
|
|
127
185
|
};
|
|
128
186
|
}
|
|
187
|
+
const finalScopes = scopeResult.scopes;
|
|
129
188
|
|
|
130
189
|
if (!authUrl) {
|
|
131
190
|
return {
|
|
@@ -161,7 +220,7 @@ export async function orchestrateOAuthConnect(
|
|
|
161
220
|
tokenEndpointAuthMethod,
|
|
162
221
|
userinfoUrl,
|
|
163
222
|
allowedTools: options.allowedTools,
|
|
164
|
-
wellKnownInjectionTemplates:
|
|
223
|
+
wellKnownInjectionTemplates: behavior.injectionTemplates,
|
|
165
224
|
};
|
|
166
225
|
|
|
167
226
|
// -----------------------------------------------------------------------
|
|
@@ -169,8 +228,6 @@ export async function orchestrateOAuthConnect(
|
|
|
169
228
|
// -----------------------------------------------------------------------
|
|
170
229
|
if (!options.isInteractive) {
|
|
171
230
|
try {
|
|
172
|
-
const callbackTransport = profile?.callbackTransport ?? "gateway";
|
|
173
|
-
|
|
174
231
|
// Gateway transport needs a public ingress URL
|
|
175
232
|
if (callbackTransport !== "loopback") {
|
|
176
233
|
const { loadConfig } = await import("../config/loader.js");
|
|
@@ -191,7 +248,7 @@ export async function orchestrateOAuthConnect(
|
|
|
191
248
|
const prepared = await prepareOAuth2Flow(
|
|
192
249
|
oauthConfig,
|
|
193
250
|
callbackTransport === "loopback"
|
|
194
|
-
? { callbackTransport, loopbackPort:
|
|
251
|
+
? { callbackTransport, loopbackPort: loopbackPort ?? undefined }
|
|
195
252
|
: undefined,
|
|
196
253
|
);
|
|
197
254
|
|
|
@@ -201,10 +258,10 @@ export async function orchestrateOAuthConnect(
|
|
|
201
258
|
try {
|
|
202
259
|
let accountInfo: string | undefined;
|
|
203
260
|
|
|
204
|
-
// Run identity verifier if available
|
|
205
|
-
if (
|
|
261
|
+
// Run identity verifier if available (code-side behavior)
|
|
262
|
+
if (behavior.identityVerifier) {
|
|
206
263
|
try {
|
|
207
|
-
accountInfo = await
|
|
264
|
+
accountInfo = await behavior.identityVerifier(
|
|
208
265
|
result.tokens.accessToken,
|
|
209
266
|
);
|
|
210
267
|
} catch {
|
|
@@ -293,19 +350,18 @@ export async function orchestrateOAuthConnect(
|
|
|
293
350
|
}
|
|
294
351
|
},
|
|
295
352
|
},
|
|
296
|
-
|
|
297
|
-
? {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
: undefined,
|
|
353
|
+
callbackTransport === "loopback"
|
|
354
|
+
? { callbackTransport, loopbackPort: loopbackPort ?? undefined }
|
|
355
|
+
: callbackTransport === "gateway"
|
|
356
|
+
? { callbackTransport }
|
|
357
|
+
: undefined,
|
|
302
358
|
);
|
|
303
359
|
|
|
304
|
-
// Run identity verifier if available
|
|
360
|
+
// Run identity verifier if available (code-side behavior)
|
|
305
361
|
let verifiedIdentity: string | undefined;
|
|
306
|
-
if (
|
|
362
|
+
if (behavior.identityVerifier) {
|
|
307
363
|
try {
|
|
308
|
-
verifiedIdentity = await
|
|
364
|
+
verifiedIdentity = await behavior.identityVerifier(tokens.accessToken);
|
|
309
365
|
} catch {
|
|
310
366
|
// Non-fatal
|
|
311
367
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared types for the OAuth provider extensibility layer.
|
|
3
3
|
*
|
|
4
|
-
* These types are consumed by the provider
|
|
4
|
+
* These types are consumed by the provider behavior registry, the token
|
|
5
5
|
* persistence module, and the credential vault orchestrator.
|
|
6
|
+
*
|
|
7
|
+
* Protocol-level OAuth config (authUrl, tokenUrl, scopes, etc.) is now
|
|
8
|
+
* stored exclusively in the `oauth_providers` SQLite table. This file
|
|
9
|
+
* only defines code-side behavioral types that cannot be serialised to
|
|
10
|
+
* a DB row (functions, templates, UI metadata).
|
|
6
11
|
*/
|
|
7
12
|
|
|
8
|
-
import type { TokenEndpointAuthMethod } from "../security/oauth2.js";
|
|
9
13
|
import type { CredentialInjectionTemplate } from "../tools/credentials/policy-types.js";
|
|
10
14
|
|
|
11
15
|
// ---------------------------------------------------------------------------
|
|
@@ -23,31 +27,21 @@ export interface OAuthScopePolicy {
|
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
// ---------------------------------------------------------------------------
|
|
26
|
-
// Provider
|
|
30
|
+
// Provider behavior
|
|
27
31
|
// ---------------------------------------------------------------------------
|
|
28
32
|
|
|
29
|
-
/**
|
|
30
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Code-side behavioral configuration for a well-known OAuth provider.
|
|
35
|
+
*
|
|
36
|
+
* Protocol-level fields (authUrl, tokenUrl, defaultScopes, scopePolicy,
|
|
37
|
+
* tokenEndpointAuthMethod, callbackTransport, loopbackPort, userinfoUrl,
|
|
38
|
+
* extraParams) are stored in the `oauth_providers` DB table. This
|
|
39
|
+
* interface contains only fields that require code references (functions,
|
|
40
|
+
* templates, skill IDs) and cannot be serialised to a DB row.
|
|
41
|
+
*/
|
|
42
|
+
export interface OAuthProviderBehavior {
|
|
31
43
|
/** Canonical service key (e.g. "integration:twitter"). */
|
|
32
44
|
service: string;
|
|
33
|
-
/** OAuth2 authorization endpoint. */
|
|
34
|
-
authUrl: string;
|
|
35
|
-
/** OAuth2 token endpoint. */
|
|
36
|
-
tokenUrl: string;
|
|
37
|
-
/** Default scopes requested during authorization. */
|
|
38
|
-
defaultScopes: string[];
|
|
39
|
-
/** Policy governing additional/forbidden scopes. */
|
|
40
|
-
scopePolicy: OAuthScopePolicy;
|
|
41
|
-
/** How to send client credentials at the token endpoint. */
|
|
42
|
-
tokenEndpointAuthMethod?: TokenEndpointAuthMethod;
|
|
43
|
-
/** Force a specific callback transport. */
|
|
44
|
-
callbackTransport?: "loopback" | "gateway";
|
|
45
|
-
/** Fixed port for loopback transport (e.g. Slack). */
|
|
46
|
-
loopbackPort?: number;
|
|
47
|
-
/** Endpoint to fetch user identity info after authorization. */
|
|
48
|
-
userinfoUrl?: string;
|
|
49
|
-
/** Extra query parameters appended to the authorization URL. */
|
|
50
|
-
extraParams?: Record<string, string>;
|
|
51
45
|
/**
|
|
52
46
|
* Async function that verifies the user's identity after a successful
|
|
53
47
|
* token exchange. Returns a human-readable account identifier (e.g.
|
|
@@ -1,34 +1,58 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getSecureKey } from "../security/secure-keys.js";
|
|
2
2
|
import { BYOOAuthConnection } from "./byo-connection.js";
|
|
3
3
|
import type { OAuthConnection } from "./connection.js";
|
|
4
|
-
import {
|
|
4
|
+
import { getApp, getConnectionByProvider, getProvider } from "./oauth-store.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Resolve an OAuthConnection for a given credential service.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
*
|
|
9
|
+
* Reads exclusively from the SQLite oauth-store. Throws if no connection
|
|
10
|
+
* exists (authorization required).
|
|
10
11
|
*/
|
|
11
12
|
export function resolveOAuthConnection(
|
|
12
13
|
credentialService: string,
|
|
13
14
|
): OAuthConnection {
|
|
14
|
-
const
|
|
15
|
-
if (!
|
|
15
|
+
const conn = getConnectionByProvider(credentialService);
|
|
16
|
+
if (!conn) {
|
|
16
17
|
throw new Error(
|
|
17
18
|
`No credential found for "${credentialService}". Authorization required.`,
|
|
18
19
|
);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
const
|
|
22
|
+
const accessToken = getSecureKey(`oauth_connection/${conn.id}/access_token`);
|
|
23
|
+
|
|
24
|
+
if (!accessToken) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`No access token found for "${credentialService}". Authorization required.`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Look up the provider by credentialService first; fall back to the
|
|
31
|
+
// connection's app's canonical providerKey so custom credential_service
|
|
32
|
+
// overrides (e.g. "integration:github-work") still resolve to the well-known
|
|
33
|
+
// provider's base URL. We traverse conn -> oauthApp -> providerKey because
|
|
34
|
+
// conn.providerKey equals credentialService (getConnectionByProvider queries
|
|
35
|
+
// WHERE providerKey = credentialService), whereas the app's providerKey is a
|
|
36
|
+
// foreign key to the oauthProviders table.
|
|
37
|
+
const provider =
|
|
38
|
+
getProvider(credentialService) ??
|
|
39
|
+
getProvider(getApp(conn.oauthAppId)?.providerKey ?? "");
|
|
40
|
+
const baseUrl = provider?.baseUrl;
|
|
41
|
+
|
|
22
42
|
if (!baseUrl) {
|
|
23
43
|
throw new Error(`No base URL configured for "${credentialService}".`);
|
|
24
44
|
}
|
|
25
45
|
|
|
46
|
+
const grantedScopes: string[] = conn.grantedScopes
|
|
47
|
+
? JSON.parse(conn.grantedScopes)
|
|
48
|
+
: [];
|
|
49
|
+
|
|
26
50
|
return new BYOOAuthConnection({
|
|
27
|
-
id:
|
|
28
|
-
providerKey:
|
|
51
|
+
id: conn.id,
|
|
52
|
+
providerKey: conn.providerKey,
|
|
29
53
|
baseUrl,
|
|
30
|
-
accountInfo:
|
|
31
|
-
grantedScopes
|
|
54
|
+
accountInfo: conn.accountInfo,
|
|
55
|
+
grantedScopes,
|
|
32
56
|
credentialService,
|
|
33
57
|
});
|
|
34
58
|
}
|
package/src/oauth/connection.ts
CHANGED