@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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import { BackendError, VellumError } from "../util/errors.js";
|
|
3
4
|
import {
|
|
4
5
|
CredentialRequiredError,
|
|
5
6
|
PlatformOAuthConnection,
|
|
@@ -116,6 +117,16 @@ describe("PlatformOAuthConnection", () => {
|
|
|
116
117
|
await conn.request({ method: "GET", path: "/some/path" });
|
|
117
118
|
});
|
|
118
119
|
|
|
120
|
+
test("error classes extend VellumError hierarchy", () => {
|
|
121
|
+
const credErr = new CredentialRequiredError();
|
|
122
|
+
expect(credErr).toBeInstanceOf(BackendError);
|
|
123
|
+
expect(credErr).toBeInstanceOf(VellumError);
|
|
124
|
+
|
|
125
|
+
const provErr = new ProviderUnreachableError();
|
|
126
|
+
expect(provErr).toBeInstanceOf(BackendError);
|
|
127
|
+
expect(provErr).toBeInstanceOf(VellumError);
|
|
128
|
+
});
|
|
129
|
+
|
|
119
130
|
test("424 response throws CredentialRequiredError", async () => {
|
|
120
131
|
globalThis.fetch = mock(async () => {
|
|
121
132
|
return new Response("", { status: 424 });
|
|
@@ -145,6 +156,24 @@ describe("PlatformOAuthConnection", () => {
|
|
|
145
156
|
);
|
|
146
157
|
});
|
|
147
158
|
|
|
159
|
+
test("strips trailing slash from platformBaseUrl to avoid double slashes", async () => {
|
|
160
|
+
globalThis.fetch = mock(async (url: string | URL | Request) => {
|
|
161
|
+
expect(String(url)).toBe(
|
|
162
|
+
"https://platform.example.com/v1/assistants/asst-abc/external-provider-proxy/gmail/",
|
|
163
|
+
);
|
|
164
|
+
return new Response(
|
|
165
|
+
JSON.stringify({ status: 200, headers: {}, body: null }),
|
|
166
|
+
{ status: 200 },
|
|
167
|
+
);
|
|
168
|
+
}) as unknown as typeof globalThis.fetch;
|
|
169
|
+
|
|
170
|
+
const conn = new PlatformOAuthConnection({
|
|
171
|
+
...DEFAULT_OPTIONS,
|
|
172
|
+
platformBaseUrl: "https://platform.example.com/",
|
|
173
|
+
});
|
|
174
|
+
await conn.request({ method: "GET", path: "/test" });
|
|
175
|
+
});
|
|
176
|
+
|
|
148
177
|
test("strips integration: prefix from providerKey for slug", async () => {
|
|
149
178
|
globalThis.fetch = mock(async (url: string | URL | Request) => {
|
|
150
179
|
expect(String(url)).toContain("/external-provider-proxy/slack/");
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
import { BackendError } from "../util/errors.js";
|
|
1
2
|
import type {
|
|
2
3
|
OAuthConnection,
|
|
3
4
|
OAuthConnectionRequest,
|
|
4
5
|
OAuthConnectionResponse,
|
|
5
6
|
} from "./connection.js";
|
|
6
7
|
|
|
7
|
-
export class CredentialRequiredError extends
|
|
8
|
+
export class CredentialRequiredError extends BackendError {
|
|
8
9
|
constructor(message = "Connection not set up on platform") {
|
|
9
10
|
super(message);
|
|
10
11
|
this.name = "CredentialRequiredError";
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export class ProviderUnreachableError extends
|
|
15
|
+
export class ProviderUnreachableError extends BackendError {
|
|
15
16
|
constructor(message = "Provider is unreachable") {
|
|
16
17
|
super(message);
|
|
17
18
|
this.name = "ProviderUnreachableError";
|
|
@@ -47,7 +48,7 @@ export class PlatformOAuthConnection implements OAuthConnection {
|
|
|
47
48
|
this.accountInfo = options.accountInfo;
|
|
48
49
|
this.grantedScopes = options.grantedScopes;
|
|
49
50
|
this.assistantId = options.assistantId;
|
|
50
|
-
this.platformBaseUrl = options.platformBaseUrl;
|
|
51
|
+
this.platformBaseUrl = options.platformBaseUrl.replace(/\/+$/, "");
|
|
51
52
|
this.apiKey = options.apiKey;
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -84,7 +85,7 @@ export class PlatformOAuthConnection implements OAuthConnection {
|
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
if (!response.ok) {
|
|
87
|
-
throw new
|
|
88
|
+
throw new BackendError(
|
|
88
89
|
`Platform proxy returned unexpected status ${response.status}`,
|
|
89
90
|
);
|
|
90
91
|
}
|
|
@@ -103,7 +104,7 @@ export class PlatformOAuthConnection implements OAuthConnection {
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
async withToken<T>(_fn: (token: string) => Promise<T>): Promise<T> {
|
|
106
|
-
throw new
|
|
107
|
+
throw new BackendError(
|
|
107
108
|
"Raw token access is not supported for platform-managed connections. Use connection.request() instead.",
|
|
108
109
|
);
|
|
109
110
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth provider behavior registry.
|
|
3
|
+
*
|
|
4
|
+
* Contains code-side behavioral configuration for well-known OAuth
|
|
5
|
+
* providers. Protocol-level fields (authUrl, tokenUrl, scopes, etc.)
|
|
6
|
+
* are stored in the `oauth_providers` SQLite table and seeded by
|
|
7
|
+
* `seed-providers.ts`. This module contains only fields that require
|
|
8
|
+
* code references (functions, templates, skill IDs) and cannot be
|
|
9
|
+
* serialised to a DB row.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { OAuthProviderBehavior } from "./connect-types.js";
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Provider behaviors
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
export const PROVIDER_BEHAVIORS: Record<string, OAuthProviderBehavior> = {
|
|
19
|
+
"integration:gmail": {
|
|
20
|
+
service: "integration:gmail",
|
|
21
|
+
// Google APIs for Gmail/Calendar/Contacts span multiple hosts; register
|
|
22
|
+
// all of them so proxied bash can inject the OAuth bearer token reliably.
|
|
23
|
+
injectionTemplates: [
|
|
24
|
+
{
|
|
25
|
+
hostPattern: "gmail.googleapis.com",
|
|
26
|
+
injectionType: "header",
|
|
27
|
+
headerName: "Authorization",
|
|
28
|
+
valuePrefix: "Bearer ",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
hostPattern: "www.googleapis.com",
|
|
32
|
+
injectionType: "header",
|
|
33
|
+
headerName: "Authorization",
|
|
34
|
+
valuePrefix: "Bearer ",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
hostPattern: "people.googleapis.com",
|
|
38
|
+
injectionType: "header",
|
|
39
|
+
headerName: "Authorization",
|
|
40
|
+
valuePrefix: "Bearer ",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
setupSkillId: "google-oauth-applescript",
|
|
44
|
+
setup: {
|
|
45
|
+
displayName: "Google (Gmail & Calendar)",
|
|
46
|
+
dashboardUrl: "https://console.cloud.google.com/apis/credentials",
|
|
47
|
+
appType: "Desktop app",
|
|
48
|
+
requiresClientSecret: true,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
"integration:slack": {
|
|
53
|
+
service: "integration:slack",
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
"integration:notion": {
|
|
57
|
+
service: "integration:notion",
|
|
58
|
+
injectionTemplates: [
|
|
59
|
+
{
|
|
60
|
+
hostPattern: "api.notion.com",
|
|
61
|
+
injectionType: "header",
|
|
62
|
+
headerName: "Authorization",
|
|
63
|
+
valuePrefix: "Bearer ",
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
"integration:twitter": {
|
|
69
|
+
service: "integration:twitter",
|
|
70
|
+
setup: {
|
|
71
|
+
displayName: "Twitter / X",
|
|
72
|
+
dashboardUrl: "https://developer.x.com/en/portal/dashboard",
|
|
73
|
+
appType: "App",
|
|
74
|
+
requiresClientSecret: false,
|
|
75
|
+
},
|
|
76
|
+
identityVerifier: async (
|
|
77
|
+
accessToken: string,
|
|
78
|
+
): Promise<string | undefined> => {
|
|
79
|
+
try {
|
|
80
|
+
const resp = await fetch("https://api.x.com/2/users/me", {
|
|
81
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
82
|
+
});
|
|
83
|
+
if (resp.ok) {
|
|
84
|
+
const body = (await resp.json()) as { data?: { username?: string } };
|
|
85
|
+
return body.data?.username ? `@${body.data.username}` : undefined;
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
// Non-fatal — identity verification is best-effort
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Aliases & resolution
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
/** Map shorthand aliases to canonical service names. */
|
|
100
|
+
export const SERVICE_ALIASES: Record<string, string> = {
|
|
101
|
+
gmail: "integration:gmail",
|
|
102
|
+
slack: "integration:slack",
|
|
103
|
+
notion: "integration:notion",
|
|
104
|
+
twitter: "integration:twitter",
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Resolve a service name through aliases, then fall back to `integration:`
|
|
109
|
+
* prefix for providers registered in PROVIDER_BEHAVIORS without a
|
|
110
|
+
* SERVICE_ALIASES entry.
|
|
111
|
+
*/
|
|
112
|
+
export function resolveService(service: string): string {
|
|
113
|
+
if (SERVICE_ALIASES[service]) return SERVICE_ALIASES[service];
|
|
114
|
+
if (!service.includes(":") && PROVIDER_BEHAVIORS[`integration:${service}`])
|
|
115
|
+
return `integration:${service}`;
|
|
116
|
+
return service;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Look up a provider behavior by canonical service name. */
|
|
120
|
+
export function getProviderBehavior(
|
|
121
|
+
service: string,
|
|
122
|
+
): OAuthProviderBehavior | undefined {
|
|
123
|
+
return PROVIDER_BEHAVIORS[service];
|
|
124
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* scopes for an OAuth flow based on the provider profile's scope policy.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {
|
|
8
|
+
import type { OAuthScopePolicy } from "./connect-types.js";
|
|
9
9
|
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
11
11
|
// Result types
|
|
@@ -28,8 +28,15 @@ export type ScopeResolutionResult =
|
|
|
28
28
|
* requested scope against the provider's `scopePolicy`.
|
|
29
29
|
* - Returns a deduplicated union of default + approved requested scopes.
|
|
30
30
|
*/
|
|
31
|
+
/** Minimal shape needed by the scope resolver. */
|
|
32
|
+
export interface ScopeResolverInput {
|
|
33
|
+
service: string;
|
|
34
|
+
defaultScopes: string[];
|
|
35
|
+
scopePolicy: OAuthScopePolicy;
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
export function resolveScopes(
|
|
32
|
-
profile:
|
|
39
|
+
profile: ScopeResolverInput,
|
|
33
40
|
requestedScopes?: string[],
|
|
34
41
|
): ScopeResolutionResult {
|
|
35
42
|
const { defaultScopes, scopePolicy, service } = profile;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { seedProviders } from "./oauth-store.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Protocol-level seed data for each well-known OAuth provider.
|
|
5
|
+
*
|
|
6
|
+
* These values are upserted into the `oauth_providers` SQLite table on
|
|
7
|
+
* every startup so that corrections (e.g. a fixed baseUrl) propagate to
|
|
8
|
+
* existing installations. Code-side behavioral fields (identityVerifier,
|
|
9
|
+
* injectionTemplates, setup, etc.) live in `provider-behaviors.ts` and
|
|
10
|
+
* are never persisted to the DB.
|
|
11
|
+
*/
|
|
12
|
+
const PROVIDER_SEED_DATA: Record<
|
|
13
|
+
string,
|
|
14
|
+
{
|
|
15
|
+
providerKey: string;
|
|
16
|
+
authUrl: string;
|
|
17
|
+
tokenUrl: string;
|
|
18
|
+
tokenEndpointAuthMethod?: string;
|
|
19
|
+
userinfoUrl?: string;
|
|
20
|
+
pingUrl?: string;
|
|
21
|
+
baseUrl?: string;
|
|
22
|
+
defaultScopes: string[];
|
|
23
|
+
scopePolicy: {
|
|
24
|
+
allowAdditionalScopes: boolean;
|
|
25
|
+
allowedOptionalScopes: string[];
|
|
26
|
+
forbiddenScopes: string[];
|
|
27
|
+
};
|
|
28
|
+
extraParams?: Record<string, string>;
|
|
29
|
+
callbackTransport?: string;
|
|
30
|
+
loopbackPort?: number;
|
|
31
|
+
}
|
|
32
|
+
> = {
|
|
33
|
+
"integration:gmail": {
|
|
34
|
+
providerKey: "integration:gmail",
|
|
35
|
+
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
36
|
+
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
37
|
+
userinfoUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
|
|
38
|
+
pingUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
|
|
39
|
+
baseUrl: "https://gmail.googleapis.com/gmail/v1/users/me",
|
|
40
|
+
defaultScopes: [
|
|
41
|
+
"https://www.googleapis.com/auth/gmail.readonly",
|
|
42
|
+
"https://www.googleapis.com/auth/gmail.modify",
|
|
43
|
+
"https://www.googleapis.com/auth/gmail.send",
|
|
44
|
+
"https://www.googleapis.com/auth/calendar.readonly",
|
|
45
|
+
"https://www.googleapis.com/auth/calendar.events",
|
|
46
|
+
"https://www.googleapis.com/auth/userinfo.email",
|
|
47
|
+
"https://www.googleapis.com/auth/contacts.readonly",
|
|
48
|
+
],
|
|
49
|
+
scopePolicy: {
|
|
50
|
+
allowAdditionalScopes: false,
|
|
51
|
+
allowedOptionalScopes: [],
|
|
52
|
+
forbiddenScopes: [],
|
|
53
|
+
},
|
|
54
|
+
extraParams: { access_type: "offline", prompt: "consent" },
|
|
55
|
+
callbackTransport: "loopback",
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
"integration:slack": {
|
|
59
|
+
providerKey: "integration:slack",
|
|
60
|
+
authUrl: "https://slack.com/oauth/v2/authorize",
|
|
61
|
+
tokenUrl: "https://slack.com/api/oauth.v2.access",
|
|
62
|
+
pingUrl: "https://slack.com/api/auth.test",
|
|
63
|
+
baseUrl: "https://slack.com/api",
|
|
64
|
+
defaultScopes: [
|
|
65
|
+
"channels:read",
|
|
66
|
+
"channels:history",
|
|
67
|
+
"groups:read",
|
|
68
|
+
"groups:history",
|
|
69
|
+
"im:read",
|
|
70
|
+
"im:history",
|
|
71
|
+
"im:write",
|
|
72
|
+
"mpim:read",
|
|
73
|
+
"mpim:history",
|
|
74
|
+
"users:read",
|
|
75
|
+
"chat:write",
|
|
76
|
+
"search:read",
|
|
77
|
+
"reactions:write",
|
|
78
|
+
],
|
|
79
|
+
scopePolicy: {
|
|
80
|
+
allowAdditionalScopes: false,
|
|
81
|
+
allowedOptionalScopes: [],
|
|
82
|
+
forbiddenScopes: [],
|
|
83
|
+
},
|
|
84
|
+
extraParams: {
|
|
85
|
+
user_scope:
|
|
86
|
+
"channels:read,channels:history,groups:read,groups:history,im:read,im:history,im:write,mpim:read,mpim:history,users:read,chat:write,search:read,reactions:write",
|
|
87
|
+
},
|
|
88
|
+
callbackTransport: "loopback",
|
|
89
|
+
loopbackPort: 17322,
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
"integration:notion": {
|
|
93
|
+
providerKey: "integration:notion",
|
|
94
|
+
authUrl: "https://api.notion.com/v1/oauth/authorize",
|
|
95
|
+
tokenUrl: "https://api.notion.com/v1/oauth/token",
|
|
96
|
+
pingUrl: "https://api.notion.com/v1/users/me",
|
|
97
|
+
baseUrl: "https://api.notion.com",
|
|
98
|
+
defaultScopes: [],
|
|
99
|
+
scopePolicy: {
|
|
100
|
+
allowAdditionalScopes: false,
|
|
101
|
+
allowedOptionalScopes: [],
|
|
102
|
+
forbiddenScopes: [],
|
|
103
|
+
},
|
|
104
|
+
extraParams: { owner: "user" },
|
|
105
|
+
tokenEndpointAuthMethod: "client_secret_basic",
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
"integration:twitter": {
|
|
109
|
+
providerKey: "integration:twitter",
|
|
110
|
+
authUrl: "https://twitter.com/i/oauth2/authorize",
|
|
111
|
+
tokenUrl: "https://api.x.com/2/oauth2/token",
|
|
112
|
+
pingUrl: "https://api.x.com/2/users/me",
|
|
113
|
+
baseUrl: "https://api.x.com",
|
|
114
|
+
defaultScopes: [
|
|
115
|
+
"tweet.read",
|
|
116
|
+
"tweet.write",
|
|
117
|
+
"users.read",
|
|
118
|
+
"offline.access",
|
|
119
|
+
],
|
|
120
|
+
scopePolicy: {
|
|
121
|
+
allowAdditionalScopes: false,
|
|
122
|
+
allowedOptionalScopes: [],
|
|
123
|
+
forbiddenScopes: [],
|
|
124
|
+
},
|
|
125
|
+
tokenEndpointAuthMethod: "client_secret_basic",
|
|
126
|
+
callbackTransport: "gateway",
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
// Manual-token providers: these don't use OAuth2 flows but need provider
|
|
130
|
+
// rows so that oauth_app and oauth_connection FK chains can reference them.
|
|
131
|
+
// The authUrl/tokenUrl values are placeholders — never used at runtime.
|
|
132
|
+
slack_channel: {
|
|
133
|
+
providerKey: "slack_channel",
|
|
134
|
+
authUrl: "urn:manual-token",
|
|
135
|
+
tokenUrl: "urn:manual-token",
|
|
136
|
+
pingUrl: "https://slack.com/api/auth.test",
|
|
137
|
+
baseUrl: "https://slack.com/api",
|
|
138
|
+
defaultScopes: [],
|
|
139
|
+
scopePolicy: {
|
|
140
|
+
allowAdditionalScopes: false,
|
|
141
|
+
allowedOptionalScopes: [],
|
|
142
|
+
forbiddenScopes: [],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
telegram: {
|
|
147
|
+
providerKey: "telegram",
|
|
148
|
+
authUrl: "urn:manual-token",
|
|
149
|
+
tokenUrl: "urn:manual-token",
|
|
150
|
+
baseUrl: "https://api.telegram.org",
|
|
151
|
+
defaultScopes: [],
|
|
152
|
+
scopePolicy: {
|
|
153
|
+
allowAdditionalScopes: false,
|
|
154
|
+
allowedOptionalScopes: [],
|
|
155
|
+
forbiddenScopes: [],
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Seed the oauth_providers table with well-known provider configurations.
|
|
162
|
+
* Uses INSERT … ON CONFLICT DO UPDATE so seed-data corrections propagate
|
|
163
|
+
* to existing installations. Safe to call on every startup.
|
|
164
|
+
*/
|
|
165
|
+
export function seedOAuthProviders(): void {
|
|
166
|
+
seedProviders(Object.values(PROVIDER_SEED_DATA));
|
|
167
|
+
}
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
* OAuth2 token persistence helper.
|
|
3
3
|
*
|
|
4
4
|
* Extracted from vault.ts so it can be reused by both the credential
|
|
5
|
-
* vault tool (interactive and deferred paths) and the
|
|
5
|
+
* vault tool (interactive and deferred paths) and the OAuth
|
|
6
6
|
* orchestrator without duplicating storage logic.
|
|
7
|
+
*
|
|
8
|
+
* Writes exclusively to the SQLite tables (oauth_app, oauth_connection)
|
|
9
|
+
* and new-format secure keys (`oauth_app/{id}/...`,
|
|
10
|
+
* `oauth_connection/{id}/...`).
|
|
7
11
|
*/
|
|
8
12
|
|
|
9
|
-
import { credentialKey, migrateKeys } from "../security/credential-key.js";
|
|
10
13
|
import type {
|
|
11
14
|
OAuth2FlowResult,
|
|
12
15
|
TokenEndpointAuthMethod,
|
|
@@ -15,9 +18,15 @@ import {
|
|
|
15
18
|
deleteSecureKeyAsync,
|
|
16
19
|
setSecureKeyAsync,
|
|
17
20
|
} from "../security/secure-keys.js";
|
|
18
|
-
import { upsertCredentialMetadata } from "../tools/credentials/metadata-store.js";
|
|
19
21
|
import type { CredentialInjectionTemplate } from "../tools/credentials/policy-types.js";
|
|
20
22
|
import { runPostConnectHook } from "../tools/credentials/post-connect-hooks.js";
|
|
23
|
+
import {
|
|
24
|
+
createConnection,
|
|
25
|
+
getApp,
|
|
26
|
+
getConnectionByProvider,
|
|
27
|
+
updateConnection,
|
|
28
|
+
upsertApp,
|
|
29
|
+
} from "./oauth-store.js";
|
|
21
30
|
|
|
22
31
|
// ---------------------------------------------------------------------------
|
|
23
32
|
// Types
|
|
@@ -37,6 +46,8 @@ export interface StoreOAuth2TokensParams {
|
|
|
37
46
|
wellKnownInjectionTemplates?: CredentialInjectionTemplate[];
|
|
38
47
|
/** Fallback account info from an identity verifier (e.g. @username, email). */
|
|
39
48
|
identityAccountInfo?: string;
|
|
49
|
+
/** Pre-resolved oauth_app ID — skips the upsertApp() call if provided. */
|
|
50
|
+
oauthAppId?: string;
|
|
40
51
|
}
|
|
41
52
|
|
|
42
53
|
// ---------------------------------------------------------------------------
|
|
@@ -47,15 +58,13 @@ export interface StoreOAuth2TokensParams {
|
|
|
47
58
|
* Store OAuth2 tokens and associated metadata after a successful flow.
|
|
48
59
|
*
|
|
49
60
|
* Persists the access token, optional refresh token, client credentials,
|
|
50
|
-
* and metadata (scopes, expiry, account info) into the
|
|
51
|
-
*
|
|
52
|
-
* for the service.
|
|
61
|
+
* and metadata (scopes, expiry, account info) into the SQLite oauth_app /
|
|
62
|
+
* oauth_connection tables with new-format secure keys. Runs any registered
|
|
63
|
+
* post-connect hook for the service.
|
|
53
64
|
*/
|
|
54
65
|
export async function storeOAuth2Tokens(
|
|
55
66
|
params: StoreOAuth2TokensParams,
|
|
56
67
|
): Promise<{ accountInfo?: string }> {
|
|
57
|
-
migrateKeys();
|
|
58
|
-
|
|
59
68
|
const {
|
|
60
69
|
service,
|
|
61
70
|
tokens,
|
|
@@ -63,21 +72,9 @@ export async function storeOAuth2Tokens(
|
|
|
63
72
|
rawTokenResponse,
|
|
64
73
|
clientId,
|
|
65
74
|
clientSecret,
|
|
66
|
-
tokenUrl,
|
|
67
|
-
tokenEndpointAuthMethod,
|
|
68
75
|
userinfoUrl,
|
|
69
|
-
allowedTools,
|
|
70
|
-
wellKnownInjectionTemplates,
|
|
71
76
|
} = params;
|
|
72
77
|
|
|
73
|
-
const tokenStored = await setSecureKeyAsync(
|
|
74
|
-
credentialKey(service, "access_token"),
|
|
75
|
-
tokens.accessToken,
|
|
76
|
-
);
|
|
77
|
-
if (!tokenStored) {
|
|
78
|
-
throw new Error("Failed to store access token in secure storage");
|
|
79
|
-
}
|
|
80
|
-
|
|
81
78
|
const expiresAt = tokens.expiresIn
|
|
82
79
|
? Date.now() + tokens.expiresIn * 1000
|
|
83
80
|
: null;
|
|
@@ -97,82 +94,89 @@ export async function storeOAuth2Tokens(
|
|
|
97
94
|
}
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
97
|
+
const resolvedAccountInfo = accountInfo ?? params.identityAccountInfo;
|
|
98
|
+
|
|
99
|
+
// -------------------------------------------------------------------
|
|
100
|
+
// SQLite oauth_app + oauth_connection + new-format secure keys
|
|
101
|
+
// -------------------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
// 1. Upsert the oauth_app row (or use the pre-resolved ID).
|
|
104
|
+
const app = params.oauthAppId
|
|
105
|
+
? (getApp(params.oauthAppId) ?? {
|
|
106
|
+
id: params.oauthAppId,
|
|
107
|
+
clientSecretCredentialPath: `oauth_app/${params.oauthAppId}/client_secret`,
|
|
108
|
+
})
|
|
109
|
+
: await upsertApp(
|
|
110
|
+
service,
|
|
111
|
+
clientId,
|
|
112
|
+
clientSecret ? { clientSecretValue: clientSecret } : undefined,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// When oauthAppId is pre-resolved, still persist clientSecret if provided.
|
|
116
|
+
if (params.oauthAppId && clientSecret) {
|
|
117
|
+
const stored = await setSecureKeyAsync(
|
|
118
|
+
app.clientSecretCredentialPath,
|
|
105
119
|
clientSecret,
|
|
106
120
|
);
|
|
107
|
-
if (!
|
|
121
|
+
if (!stored) {
|
|
108
122
|
throw new Error("Failed to store client_secret in secure storage");
|
|
109
123
|
}
|
|
110
124
|
}
|
|
111
125
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
setNestedValue(
|
|
142
|
-
raw,
|
|
143
|
-
`integrations.${service}.accountInfo`,
|
|
144
|
-
resolvedAccountInfo,
|
|
145
|
-
);
|
|
126
|
+
// 2. Upsert oauth_connection — reuse existing active connection for this
|
|
127
|
+
// provider, or create a new one.
|
|
128
|
+
const existingConn = getConnectionByProvider(service);
|
|
129
|
+
let connId: string;
|
|
130
|
+
|
|
131
|
+
const hasRefreshToken = !!tokens.refreshToken;
|
|
132
|
+
|
|
133
|
+
if (existingConn) {
|
|
134
|
+
connId = existingConn.id;
|
|
135
|
+
updateConnection(connId, {
|
|
136
|
+
oauthAppId: app.id,
|
|
137
|
+
accountInfo: resolvedAccountInfo,
|
|
138
|
+
grantedScopes,
|
|
139
|
+
expiresAt,
|
|
140
|
+
hasRefreshToken,
|
|
141
|
+
metadata: rawTokenResponse,
|
|
142
|
+
});
|
|
143
|
+
} else {
|
|
144
|
+
const conn = createConnection({
|
|
145
|
+
oauthAppId: app.id,
|
|
146
|
+
providerKey: service,
|
|
147
|
+
accountInfo: resolvedAccountInfo,
|
|
148
|
+
grantedScopes,
|
|
149
|
+
expiresAt: expiresAt ?? undefined,
|
|
150
|
+
hasRefreshToken,
|
|
151
|
+
metadata: rawTokenResponse,
|
|
152
|
+
});
|
|
153
|
+
connId = conn.id;
|
|
154
|
+
}
|
|
146
155
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
156
|
+
// 3. Write access_token: oauth_connection/{conn.id}/access_token
|
|
157
|
+
const tokenStored = await setSecureKeyAsync(
|
|
158
|
+
`oauth_connection/${connId}/access_token`,
|
|
159
|
+
tokens.accessToken,
|
|
160
|
+
);
|
|
161
|
+
if (!tokenStored) {
|
|
162
|
+
throw new Error("Failed to store access token in secure storage");
|
|
152
163
|
}
|
|
153
164
|
|
|
165
|
+
// 4. Write or clear refresh_token: oauth_connection/{conn.id}/refresh_token
|
|
154
166
|
if (tokens.refreshToken) {
|
|
155
|
-
|
|
156
|
-
|
|
167
|
+
await setSecureKeyAsync(
|
|
168
|
+
`oauth_connection/${connId}/refresh_token`,
|
|
157
169
|
tokens.refreshToken,
|
|
158
170
|
);
|
|
159
|
-
if (refreshStored) {
|
|
160
|
-
upsertCredentialMetadata(service, "access_token", {
|
|
161
|
-
hasRefreshToken: true,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
171
|
} else {
|
|
165
172
|
// Re-auth grants that omit refresh_token must clear any stale stored
|
|
166
173
|
// token — otherwise withValidToken() will attempt refresh with invalid
|
|
167
174
|
// credentials.
|
|
168
|
-
await deleteSecureKeyAsync(
|
|
169
|
-
upsertCredentialMetadata(service, "access_token", {
|
|
170
|
-
hasRefreshToken: false,
|
|
171
|
-
});
|
|
175
|
+
await deleteSecureKeyAsync(`oauth_connection/${connId}/refresh_token`);
|
|
172
176
|
}
|
|
173
177
|
|
|
174
178
|
// Run any provider-specific post-connect actions (e.g. Slack welcome DM)
|
|
175
179
|
await runPostConnectHook({ service, rawTokenResponse });
|
|
176
180
|
|
|
177
|
-
return { accountInfo:
|
|
181
|
+
return { accountInfo: resolvedAccountInfo };
|
|
178
182
|
}
|
|
@@ -805,12 +805,12 @@ export async function check(
|
|
|
805
805
|
}
|
|
806
806
|
|
|
807
807
|
// Workspace mode: auto-allow workspace-scoped operations that don't have
|
|
808
|
-
// an explicit rule
|
|
809
|
-
//
|
|
808
|
+
// an explicit rule, but only when risk is Low. Medium and High risk operations
|
|
809
|
+
// fall through to risk-based policy and always require approval.
|
|
810
810
|
if (
|
|
811
811
|
permissionsMode === "workspace" &&
|
|
812
812
|
!matchedRule &&
|
|
813
|
-
risk
|
|
813
|
+
risk === RiskLevel.Low
|
|
814
814
|
) {
|
|
815
815
|
// When sandbox is disabled, bash runs on the host — don't auto-allow
|
|
816
816
|
const sandboxEnabled = getConfig().sandbox.enabled;
|
|
@@ -20,6 +20,7 @@ const HOST_FILE_TOOLS = [
|
|
|
20
20
|
"host_file_edit",
|
|
21
21
|
] as const;
|
|
22
22
|
const COMPUTER_USE_TOOLS = [
|
|
23
|
+
"computer_use_observe",
|
|
23
24
|
"computer_use_click",
|
|
24
25
|
"computer_use_type_text",
|
|
25
26
|
"computer_use_key",
|
|
@@ -28,7 +29,6 @@ const COMPUTER_USE_TOOLS = [
|
|
|
28
29
|
"computer_use_wait",
|
|
29
30
|
"computer_use_open_app",
|
|
30
31
|
"computer_use_run_applescript",
|
|
31
|
-
"computer_use_request_control",
|
|
32
32
|
// computer_use_done and computer_use_respond are terminal signal tools
|
|
33
33
|
// (RiskLevel.Low) — they don't perform any computer action, so they
|
|
34
34
|
// should NOT get an 'ask' rule.
|