@vellumai/assistant 0.12.0 → 0.12.1-dev.202609142113.d1590bc
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/AGENTS.md +5 -1
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/openapi.yaml +327 -52
- package/package.json +2 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +212 -3
- package/src/__tests__/credential-security-invariants.test.ts +4 -2
- package/src/__tests__/cu-unified-flow.test.ts +8 -2
- package/src/__tests__/document-sync-tags.test.ts +25 -0
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/heartbeat-service.test.ts +56 -40
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-cu-proxy.test.ts +539 -0
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
- package/src/__tests__/oauth-commands-routes.test.ts +113 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +8 -16
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/service-account-token.test.ts +247 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +19 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/unicode.test.ts +13 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +96 -10
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/agent/loop.ts +1 -0
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/avatar/identity-avatar.ts +7 -2
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +58 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +37 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +53 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +59 -46
- package/src/calls/voice-triage-escalate.ts +175 -56
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/bash.test.ts +53 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/bash.help.ts +8 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/roadmap.help.ts +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +255 -0
- package/src/config/__tests__/platform-rehydration.test.ts +99 -9
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +20 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
- package/src/config/bundled-skills/schedule/SKILL.md +3 -4
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +24 -12
- package/src/config/loader.ts +1 -0
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +201 -0
- package/src/config/platform-rehydration.ts +11 -27
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/credential-health/credential-health-service.ts +11 -17
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-error.ts +3 -2
- package/src/daemon/conversation-media-retry.ts +2 -1
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-runtime-assembly.ts +2 -1
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +15 -9
- package/src/daemon/conversation-tool-setup.ts +37 -43
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/dictation-profile-store.ts +2 -1
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +6 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-cu-proxy.ts +186 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/reaction-history-render.ts +2 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +119 -0
- package/src/desktop/desktop-chrome-policy.test.ts +71 -0
- package/src/desktop/desktop-chrome-policy.ts +42 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +306 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +627 -0
- package/src/desktop/desktop-session-manager.ts +814 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +213 -693
- package/src/live-voice/protocol.ts +0 -2
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/guardian-feed-projection.ts +125 -5
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/notifications/schedule-result-producer.ts +66 -22
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +54 -0
- package/src/oauth/byo-connection.test.ts +105 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +73 -0
- package/src/oauth/connection-resolver.ts +95 -2
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +204 -25
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/host-utils.ts +10 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +24 -3
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/host-observe.test.ts +40 -1
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +73 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/conversation-routes.ts +16 -3
- package/src/runtime/routes/credential-routes.ts +19 -10
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/oauth-commands-routes.ts +36 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/safe-env.test.ts +6 -6
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/safe-env.ts +23 -3
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/types/host-capabilities.ts +5 -3
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/util/unicode.ts +15 -1
- package/src/watch/watch-timeline.ts +2 -2
- package/src/workflows/run-manager.ts +2 -1
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +6 -55
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/runtime/routes/debug-bash-routes.ts +0 -173
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -308,6 +308,66 @@ describe("BYOOAuthConnection", () => {
|
|
|
308
308
|
expect((init as RequestInit).method).toBe("GET");
|
|
309
309
|
});
|
|
310
310
|
|
|
311
|
+
test("follows a cross-origin redirect without the credential and returns the target's bytes", async () => {
|
|
312
|
+
// Real fetch against two loopback origins: the credential goes to the
|
|
313
|
+
// host the caller named, the redirect target sees no Authorization,
|
|
314
|
+
// and the target's bytes come back as the response body. This is the
|
|
315
|
+
// path a Slack file download takes when files.slack.com answers with a
|
|
316
|
+
// 302 to its CDN.
|
|
317
|
+
globalThis.fetch = originalFetch;
|
|
318
|
+
await setupCredential("google");
|
|
319
|
+
const bytes = Buffer.from([
|
|
320
|
+
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
|
|
321
|
+
]);
|
|
322
|
+
const seen: { origin?: string | null; target?: string | null } = {};
|
|
323
|
+
|
|
324
|
+
const target = Bun.serve({
|
|
325
|
+
port: 0,
|
|
326
|
+
hostname: "127.0.0.1",
|
|
327
|
+
fetch: (req) => {
|
|
328
|
+
seen.target = req.headers.get("authorization");
|
|
329
|
+
return new Response(bytes, {
|
|
330
|
+
status: 200,
|
|
331
|
+
headers: { "content-type": "image/png" },
|
|
332
|
+
});
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
const origin = Bun.serve({
|
|
336
|
+
port: 0,
|
|
337
|
+
hostname: "127.0.0.1",
|
|
338
|
+
fetch: (req) => {
|
|
339
|
+
seen.origin = req.headers.get("authorization");
|
|
340
|
+
return new Response(null, {
|
|
341
|
+
status: 302,
|
|
342
|
+
headers: { Location: `http://127.0.0.1:${target.port}/signed` },
|
|
343
|
+
});
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
const conn = new BYOOAuthConnection({
|
|
349
|
+
id: "conn-google",
|
|
350
|
+
provider: "google",
|
|
351
|
+
baseUrl: `http://127.0.0.1:${origin.port}`,
|
|
352
|
+
accountInfo: null,
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const result = await conn.request({
|
|
356
|
+
method: "GET",
|
|
357
|
+
path: "/files-pri/T0123-F0456/download/shot.png",
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
expect(seen.origin).toBe("Bearer test-access-token");
|
|
361
|
+
expect(seen.target).toBeNull();
|
|
362
|
+
expect(result.status).toBe(200);
|
|
363
|
+
expect(Buffer.isBuffer(result.body)).toBe(true);
|
|
364
|
+
expect(Buffer.from(result.body as Buffer).equals(bytes)).toBe(true);
|
|
365
|
+
} finally {
|
|
366
|
+
origin.stop(true);
|
|
367
|
+
target.stop(true);
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
311
371
|
test("appends query parameters", async () => {
|
|
312
372
|
await setupCredential("google");
|
|
313
373
|
const conn = createConnection();
|
|
@@ -708,6 +768,51 @@ describe("BYOOAuthConnection", () => {
|
|
|
708
768
|
expect(headers.get("Authorization")).toBe("Bearer test-access-token");
|
|
709
769
|
});
|
|
710
770
|
|
|
771
|
+
test("sends the token in the provider's own header when one is configured", async () => {
|
|
772
|
+
// Shopify's Admin API reads X-Shopify-Access-Token and ignores
|
|
773
|
+
// Authorization, so a Bearer header reaches the shop unauthenticated.
|
|
774
|
+
await setupCredential("google");
|
|
775
|
+
const conn = new BYOOAuthConnection({
|
|
776
|
+
id: "conn-google",
|
|
777
|
+
provider: "google",
|
|
778
|
+
baseUrl: "https://example-store.myshopify.com",
|
|
779
|
+
accountInfo: null,
|
|
780
|
+
tokenHeader: { name: "X-Shopify-Access-Token", valuePrefix: "" },
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
await conn.request({
|
|
784
|
+
method: "GET",
|
|
785
|
+
path: "/admin/api/2026-07/shop.json",
|
|
786
|
+
// A caller-supplied Authorization must not ride along.
|
|
787
|
+
headers: { Authorization: "Bearer caller-supplied" },
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
791
|
+
expect(url).toBe(
|
|
792
|
+
"https://example-store.myshopify.com/admin/api/2026-07/shop.json",
|
|
793
|
+
);
|
|
794
|
+
const headers = (init as RequestInit).headers as Headers;
|
|
795
|
+
expect(headers.get("X-Shopify-Access-Token")).toBe("test-access-token");
|
|
796
|
+
expect(headers.has("Authorization")).toBe(false);
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
test("keeps the value prefix from the token header template", async () => {
|
|
800
|
+
await setupCredential("google");
|
|
801
|
+
const conn = new BYOOAuthConnection({
|
|
802
|
+
id: "conn-google",
|
|
803
|
+
provider: "google",
|
|
804
|
+
baseUrl: "https://discord.com/api",
|
|
805
|
+
accountInfo: null,
|
|
806
|
+
tokenHeader: { name: "Authorization", valuePrefix: "Bot " },
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
await conn.request({ method: "GET", path: "/users/@me" });
|
|
810
|
+
|
|
811
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
812
|
+
const headers = (init as RequestInit).headers as Headers;
|
|
813
|
+
expect(headers.get("Authorization")).toBe("Bot test-access-token");
|
|
814
|
+
});
|
|
815
|
+
|
|
711
816
|
test("uses Telegram Bot API token URL format without Bearer auth", async () => {
|
|
712
817
|
await setupTelegramCredential();
|
|
713
818
|
const conn = createConnection("telegram");
|
|
@@ -21,11 +21,29 @@ const log = getLogger("byo-oauth-connection");
|
|
|
21
21
|
/** Default per-request timeout to prevent hung requests from blocking indefinitely. */
|
|
22
22
|
const REQUEST_TIMEOUT_MS = 30_000;
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* How the access token is attached to an outbound request. Almost every
|
|
26
|
+
* provider reads `Authorization: Bearer <token>`; a few use their own header
|
|
27
|
+
* (Shopify's Admin API reads `X-Shopify-Access-Token` and ignores
|
|
28
|
+
* Authorization entirely). Derived from the provider's header-type injection
|
|
29
|
+
* template so the seed is the single source of truth.
|
|
30
|
+
*/
|
|
31
|
+
export interface BYOTokenHeader {
|
|
32
|
+
name: string;
|
|
33
|
+
/** Text placed before the token, e.g. `"Bearer "`, `"Bot "`, or `""`. */
|
|
34
|
+
valuePrefix: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
24
37
|
export interface BYOOAuthConnectionOptions {
|
|
25
38
|
id: string;
|
|
26
39
|
provider: string;
|
|
27
40
|
baseUrl: string;
|
|
28
41
|
accountInfo: string | null;
|
|
42
|
+
/**
|
|
43
|
+
* Token header override. When omitted the connection falls back to
|
|
44
|
+
* `Authorization: Bearer` (or `Bot` for discord_channel).
|
|
45
|
+
*/
|
|
46
|
+
tokenHeader?: BYOTokenHeader | null;
|
|
29
47
|
}
|
|
30
48
|
|
|
31
49
|
export class BYOOAuthConnection implements OAuthConnection {
|
|
@@ -34,12 +52,14 @@ export class BYOOAuthConnection implements OAuthConnection {
|
|
|
34
52
|
readonly accountInfo: string | null;
|
|
35
53
|
|
|
36
54
|
private readonly baseUrl: string;
|
|
55
|
+
private readonly tokenHeader: BYOTokenHeader | null;
|
|
37
56
|
|
|
38
57
|
constructor(opts: BYOOAuthConnectionOptions) {
|
|
39
58
|
this.id = opts.id;
|
|
40
59
|
this.provider = opts.provider;
|
|
41
60
|
this.baseUrl = opts.baseUrl;
|
|
42
61
|
this.accountInfo = opts.accountInfo;
|
|
62
|
+
this.tokenHeader = opts.tokenHeader ?? null;
|
|
43
63
|
}
|
|
44
64
|
|
|
45
65
|
async request(req: OAuthConnectionRequest): Promise<OAuthConnectionResponse> {
|
|
@@ -51,8 +71,12 @@ export class BYOOAuthConnection implements OAuthConnection {
|
|
|
51
71
|
// Discord bot tokens authenticate with the `Bot ` scheme, not
|
|
52
72
|
// `Bearer`. Sending Bearer here reaches Discord as an unusable
|
|
53
73
|
// credential and comes back 401, which reads as a revoked token.
|
|
54
|
-
|
|
55
|
-
|
|
74
|
+
// Providers with their own token header (Shopify) override the
|
|
75
|
+
// whole thing via `tokenHeader`.
|
|
76
|
+
const tokenHeader: BYOTokenHeader = this.tokenHeader ?? {
|
|
77
|
+
name: "Authorization",
|
|
78
|
+
valuePrefix: this.provider === "discord_channel" ? "Bot " : "Bearer ",
|
|
79
|
+
};
|
|
56
80
|
const requestPath = isTelegram
|
|
57
81
|
? buildTelegramBotApiPath(req.path, token)
|
|
58
82
|
: req.path;
|
|
@@ -93,16 +117,22 @@ export class BYOOAuthConnection implements OAuthConnection {
|
|
|
93
117
|
}
|
|
94
118
|
}
|
|
95
119
|
if (!isTelegram) {
|
|
96
|
-
|
|
120
|
+
// The credential always wins over a caller-supplied value, and a
|
|
121
|
+
// provider that reads its own header must not also receive a
|
|
122
|
+
// stray Authorization the caller happened to send.
|
|
123
|
+
if (tokenHeader.name.toLowerCase() !== "authorization") {
|
|
124
|
+
headers.delete("Authorization");
|
|
125
|
+
}
|
|
126
|
+
headers.set(tokenHeader.name, `${tokenHeader.valuePrefix}${token}`);
|
|
97
127
|
}
|
|
98
128
|
|
|
99
129
|
const resp = await fetch(fullUrl, {
|
|
100
130
|
method: req.method,
|
|
101
131
|
headers,
|
|
102
132
|
body: hasBody
|
|
103
|
-
?
|
|
104
|
-
|
|
105
|
-
|
|
133
|
+
? binaryBody !== undefined
|
|
134
|
+
? Buffer.from(binaryBody)
|
|
135
|
+
: (rawBody ?? JSON.stringify(req.body))
|
|
106
136
|
: undefined,
|
|
107
137
|
// Following a redirect would replay a POST as a GET against a URL
|
|
108
138
|
// the caller never asked for, and hide the 3xx from them.
|
|
@@ -75,6 +75,7 @@ import {
|
|
|
75
75
|
resolveEffectiveBaseUrl,
|
|
76
76
|
resolveOAuthConnection,
|
|
77
77
|
resolveOAuthConnectionWithMeta,
|
|
78
|
+
resolveTokenHeader,
|
|
78
79
|
} from "./connection-resolver.js";
|
|
79
80
|
import { PlatformOAuthConnection } from "./platform-connection.js";
|
|
80
81
|
|
|
@@ -753,3 +754,75 @@ describe("resolveEffectiveBaseUrl", () => {
|
|
|
753
754
|
).toBe("https://gmail.googleapis.com/gmail/v1/users/me");
|
|
754
755
|
});
|
|
755
756
|
});
|
|
757
|
+
|
|
758
|
+
describe("resolveTokenHeader", () => {
|
|
759
|
+
const shopify = JSON.stringify([
|
|
760
|
+
{
|
|
761
|
+
hostPattern: "*.myshopify.com",
|
|
762
|
+
injectionType: "header",
|
|
763
|
+
headerName: "X-Shopify-Access-Token",
|
|
764
|
+
valuePrefix: "",
|
|
765
|
+
},
|
|
766
|
+
]);
|
|
767
|
+
|
|
768
|
+
test("returns null when the provider declares no templates", () => {
|
|
769
|
+
expect(resolveTokenHeader(undefined, "https://api.example.com")).toBeNull();
|
|
770
|
+
expect(resolveTokenHeader(null, "https://api.example.com")).toBeNull();
|
|
771
|
+
expect(resolveTokenHeader("[]", "https://api.example.com")).toBeNull();
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
test("returns null for malformed JSON", () => {
|
|
775
|
+
expect(
|
|
776
|
+
resolveTokenHeader("{not json", "https://api.example.com"),
|
|
777
|
+
).toBeNull();
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
test("ignores non-header injection templates", () => {
|
|
781
|
+
const query = JSON.stringify([
|
|
782
|
+
{
|
|
783
|
+
hostPattern: "api.example.com",
|
|
784
|
+
injectionType: "query",
|
|
785
|
+
headerName: "",
|
|
786
|
+
},
|
|
787
|
+
]);
|
|
788
|
+
expect(resolveTokenHeader(query, "https://api.example.com")).toBeNull();
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
test("picks the template whose wildcard host pattern matches the base URL", () => {
|
|
792
|
+
const templates = JSON.stringify([
|
|
793
|
+
{
|
|
794
|
+
hostPattern: "api.other.com",
|
|
795
|
+
injectionType: "header",
|
|
796
|
+
headerName: "Authorization",
|
|
797
|
+
valuePrefix: "Bearer ",
|
|
798
|
+
},
|
|
799
|
+
...JSON.parse(shopify),
|
|
800
|
+
]);
|
|
801
|
+
expect(
|
|
802
|
+
resolveTokenHeader(templates, "https://example-store.myshopify.com"),
|
|
803
|
+
).toEqual({ name: "X-Shopify-Access-Token", valuePrefix: "" });
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
test("falls back to the first header template when the host cannot be matched", () => {
|
|
807
|
+
// A still-templated base URL is not a parseable URL.
|
|
808
|
+
expect(resolveTokenHeader(shopify, "https://{tenant_host}")).toEqual({
|
|
809
|
+
name: "X-Shopify-Access-Token",
|
|
810
|
+
valuePrefix: "",
|
|
811
|
+
});
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
test("preserves the Bot prefix for discord_channel", () => {
|
|
815
|
+
const discord = JSON.stringify([
|
|
816
|
+
{
|
|
817
|
+
hostPattern: "discord.com",
|
|
818
|
+
injectionType: "header",
|
|
819
|
+
headerName: "Authorization",
|
|
820
|
+
valuePrefix: "Bot ",
|
|
821
|
+
},
|
|
822
|
+
]);
|
|
823
|
+
expect(resolveTokenHeader(discord, "https://discord.com/api")).toEqual({
|
|
824
|
+
name: "Authorization",
|
|
825
|
+
valuePrefix: "Bot ",
|
|
826
|
+
});
|
|
827
|
+
});
|
|
828
|
+
});
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "../config/schemas/services.js";
|
|
7
7
|
import { VellumPlatformClient } from "../platform/client.js";
|
|
8
8
|
import { getLogger } from "../util/logger.js";
|
|
9
|
-
import { BYOOAuthConnection } from "./byo-connection.js";
|
|
9
|
+
import { BYOOAuthConnection, type BYOTokenHeader } from "./byo-connection.js";
|
|
10
10
|
import type { OAuthConnection } from "./connection.js";
|
|
11
11
|
import { getConnectionAccessTokenResult } from "./credential-token-resolver.js";
|
|
12
12
|
import { syncManualTokenConnection } from "./manual-token-connection.js";
|
|
@@ -215,15 +215,108 @@ export async function resolveOAuthConnectionWithMeta(
|
|
|
215
215
|
);
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
const effectiveBaseUrl = resolveEffectiveBaseUrl(
|
|
219
|
+
conn.provider,
|
|
220
|
+
baseUrl,
|
|
221
|
+
conn.metadata,
|
|
222
|
+
);
|
|
218
223
|
const connection = new BYOOAuthConnection({
|
|
219
224
|
id: conn.id,
|
|
220
225
|
provider: conn.provider,
|
|
221
|
-
baseUrl:
|
|
226
|
+
baseUrl: effectiveBaseUrl,
|
|
222
227
|
accountInfo: conn.accountInfo,
|
|
228
|
+
tokenHeader: resolveTokenHeader(
|
|
229
|
+
providerRow?.injectionTemplates,
|
|
230
|
+
effectiveBaseUrl,
|
|
231
|
+
),
|
|
223
232
|
});
|
|
224
233
|
return { connection, ambiguous, allAccounts };
|
|
225
234
|
}
|
|
226
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Pick the header a provider expects its access token in from the seed's
|
|
238
|
+
* header-type injection templates.
|
|
239
|
+
*
|
|
240
|
+
* Prefers the template whose `hostPattern` matches the base URL's host
|
|
241
|
+
* (`*.myshopify.com` style globs included), then falls back to the first
|
|
242
|
+
* header template. Returns `null` when the provider declares none, which
|
|
243
|
+
* leaves `BYOOAuthConnection` on its `Authorization: Bearer` default.
|
|
244
|
+
*
|
|
245
|
+
* Shopify is why this exists: its Admin API reads `X-Shopify-Access-Token`
|
|
246
|
+
* and ignores `Authorization`, so a Bearer header reaches the shop as an
|
|
247
|
+
* unauthenticated request and comes back 401.
|
|
248
|
+
*/
|
|
249
|
+
export function resolveTokenHeader(
|
|
250
|
+
rawTemplates: string | null | undefined,
|
|
251
|
+
baseUrl: string,
|
|
252
|
+
): BYOTokenHeader | null {
|
|
253
|
+
if (!rawTemplates) {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
let parsed: unknown;
|
|
257
|
+
try {
|
|
258
|
+
parsed = JSON.parse(rawTemplates);
|
|
259
|
+
} catch {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
if (!Array.isArray(parsed)) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const headerTemplates = parsed.filter(
|
|
267
|
+
(
|
|
268
|
+
t,
|
|
269
|
+
): t is {
|
|
270
|
+
hostPattern?: unknown;
|
|
271
|
+
headerName: string;
|
|
272
|
+
valuePrefix?: unknown;
|
|
273
|
+
} =>
|
|
274
|
+
typeof t === "object" &&
|
|
275
|
+
t !== null &&
|
|
276
|
+
(t as { injectionType?: unknown }).injectionType === "header" &&
|
|
277
|
+
typeof (t as { headerName?: unknown }).headerName === "string" &&
|
|
278
|
+
(t as { headerName: string }).headerName.trim() !== "",
|
|
279
|
+
);
|
|
280
|
+
if (headerTemplates.length === 0) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
let host = "";
|
|
285
|
+
try {
|
|
286
|
+
host = new URL(baseUrl).hostname.toLowerCase();
|
|
287
|
+
} catch {
|
|
288
|
+
// Templated or malformed base URLs (e.g. a still-unfilled
|
|
289
|
+
// `https://{tenant_host}`) cannot be matched by host; fall through to
|
|
290
|
+
// the first header template.
|
|
291
|
+
}
|
|
292
|
+
const matching =
|
|
293
|
+
host === ""
|
|
294
|
+
? undefined
|
|
295
|
+
: headerTemplates.find(
|
|
296
|
+
(t) =>
|
|
297
|
+
typeof t.hostPattern === "string" &&
|
|
298
|
+
hostMatchesPattern(host, t.hostPattern),
|
|
299
|
+
);
|
|
300
|
+
const chosen = matching ?? headerTemplates[0];
|
|
301
|
+
return {
|
|
302
|
+
name: chosen.headerName.trim(),
|
|
303
|
+
valuePrefix:
|
|
304
|
+
typeof chosen.valuePrefix === "string" ? chosen.valuePrefix : "",
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function hostMatchesPattern(host: string, pattern: string): boolean {
|
|
309
|
+
const p = pattern.trim().toLowerCase();
|
|
310
|
+
if (p === "") {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
if (p.startsWith("*.")) {
|
|
314
|
+
const suffix = p.slice(1); // ".myshopify.com"
|
|
315
|
+
return host.endsWith(suffix) && host.length > suffix.length;
|
|
316
|
+
}
|
|
317
|
+
return host === p;
|
|
318
|
+
}
|
|
319
|
+
|
|
227
320
|
/**
|
|
228
321
|
* Resolve the effective API base URL for a connection, preferring per-tenant
|
|
229
322
|
* values stored on the connection's `metadata` over the provider's static
|
package/src/oauth/scope-utils.ts
CHANGED
|
@@ -72,6 +72,30 @@ export function expectedScopesForStoredToken(
|
|
|
72
72
|
return parsed.length > 0 ? parsed : defaultScopes;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* The required scopes a stored grant lacks. Requirements come from the request
|
|
77
|
+
* that produced the stored token ({@link expectedScopesForStoredToken}), so a
|
|
78
|
+
* provider's bot-only scopes are never demanded of a user token. An empty
|
|
79
|
+
* requirement or an unrecorded grant reports nothing, because unknown scope
|
|
80
|
+
* data must never read as a failure.
|
|
81
|
+
*/
|
|
82
|
+
export function missingScopesForStoredToken(
|
|
83
|
+
defaultScopes: string[],
|
|
84
|
+
authorizeParams: Record<string, string> | undefined,
|
|
85
|
+
scopeSeparator: string | undefined,
|
|
86
|
+
grantedScopes: string[],
|
|
87
|
+
): string[] {
|
|
88
|
+
const expected = expectedScopesForStoredToken(
|
|
89
|
+
defaultScopes,
|
|
90
|
+
authorizeParams,
|
|
91
|
+
scopeSeparator,
|
|
92
|
+
);
|
|
93
|
+
if (expected.length === 0 || grantedScopes.length === 0) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
return scopeDifference(expected, grantedScopes);
|
|
97
|
+
}
|
|
98
|
+
|
|
75
99
|
const GMAIL_FULL_ACCESS_SCOPE = "https://mail.google.com/";
|
|
76
100
|
const GMAIL_READONLY_SCOPE = "https://www.googleapis.com/auth/gmail.readonly";
|
|
77
101
|
|