@vellumai/assistant 0.12.1 → 0.12.2-dev.202609162214.308bda0
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/.env.example +2 -0
- package/AGENTS.md +5 -1
- package/ARCHITECTURE.md +20 -21
- package/Dockerfile +7 -7
- package/docs/architecture/integrations.md +7 -0
- package/docs/architecture/memory.md +11 -2
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/credential-execution-service.md +1 -1
- package/docs/desktop-browser-cli.md +35 -0
- package/docs/guardian-request-flow.md +16 -0
- package/node_modules/@vellumai/environments/src/shell.test.ts +21 -0
- package/node_modules/@vellumai/environments/src/shell.ts +24 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +8 -2
- package/node_modules/@vellumai/slack-text/src/index.ts +13 -8
- package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
- package/openapi.yaml +564 -111
- package/package.json +7 -4
- package/scripts/bundled-plugin-packages.ts +154 -0
- package/scripts/generate-bundled-plugin-packages.ts +29 -0
- package/scripts/postinstall.ts +33 -0
- package/scripts/smoke-desktop-browser-cli.ts +280 -0
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/scripts/sync-llm-catalog.ts +3 -0
- package/scripts/test.ts +15 -13
- package/scripts/voice-ttft-spike.ts +2 -2
- package/src/__tests__/agent-loop.test.ts +124 -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__/approval-interception-trust-gates.test.ts +40 -0
- 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__/attachments-store.test.ts +22 -3
- package/src/__tests__/channel-approval.test.ts +9 -14
- package/src/__tests__/channel-reply-delivery.test.ts +56 -0
- package/src/__tests__/computer-use-screenshot-attachments.test.ts +411 -0
- package/src/__tests__/computer-use-screenshot-selection.test.ts +50 -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 +29 -30
- package/src/__tests__/config-sounds-sync.test.ts +23 -0
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop.test.ts +257 -6
- package/src/__tests__/conversation-attachments.test.ts +106 -0
- package/src/__tests__/conversation-error.test.ts +4 -1
- package/src/__tests__/conversation-fork-crud.test.ts +191 -0
- 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-runtime-assembly.test.ts +33 -3
- 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-point-at-budget.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
- 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-execution-client.test.ts +70 -53
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +250 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/cu-unified-flow.test.ts +14 -4
- package/src/__tests__/db-conversation-tool-surface.test.ts +144 -0
- 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__/events-tail-route.test.ts +33 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/external-plugin-loader.test.ts +52 -0
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/headless-browser-mode.test.ts +48 -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 +712 -18
- 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__/inference-profile-session-handler.test.ts +26 -0
- package/src/__tests__/list-all-apps.test.ts +40 -8
- package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +85 -1
- package/src/__tests__/llm-catalog-parity.test.ts +51 -3
- package/src/__tests__/managed-store.test.ts +121 -0
- package/src/__tests__/mcp-auth-routes.test.ts +104 -8
- package/src/__tests__/mcp-client-auth.test.ts +54 -0
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +40 -43
- package/src/__tests__/mcp-list-plugin-servers.test.ts +85 -42
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +4 -2
- 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-apps-routes.test.ts +1 -0
- package/src/__tests__/oauth-commands-routes.test.ts +282 -101
- package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +23 -0
- package/src/__tests__/oauth-providers-routes.test.ts +3 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/persist-media-references.test.ts +50 -0
- 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 +13 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +138 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +317 -6
- package/src/__tests__/schedule-routes.test.ts +20 -0
- package/src/__tests__/scheduler-result-notification.test.ts +23 -4
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/secret-routes-acp-guard.test.ts +59 -1
- package/src/__tests__/secret-routes-platform-proxy.test.ts +86 -31
- package/src/__tests__/secret-routes-scrub.test.ts +22 -27
- package/src/__tests__/secure-keys-managed-failover.test.ts +8 -0
- package/src/__tests__/secure-keys.test.ts +7 -3
- 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 +20 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +239 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +63 -0
- package/src/__tests__/ui-channel-variants.test.ts +1 -56
- package/src/__tests__/unicode.test.ts +49 -0
- package/src/__tests__/user-plugin-loader.test.ts +21 -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__/acp-claude-oauth.test.ts +257 -6
- package/src/acp/__tests__/acp-credentials.test.ts +13 -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__/claude-token-refresh.test.ts +257 -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__/prepare-agent-env.test.ts +60 -1
- 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/acp-claude-oauth.ts +328 -14
- package/src/acp/acp-credentials.ts +19 -0
- 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/claude-token-refresh.ts +150 -0
- 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/prepare-agent-env.ts +21 -6
- 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/session-snapshot.ts +260 -0
- package/src/acp/types.ts +50 -5
- package/src/agent/loop.ts +20 -0
- package/src/api/attachment-provenance.test.ts +68 -0
- package/src/api/computer-use-tool.test.ts +55 -0
- package/src/api/computer-use-tool.ts +27 -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/assistant-outbound-attachment.ts +3 -0
- package/src/api/events/desktop-activity-changed.ts +10 -0
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +17 -0
- package/src/api/responses/conversation-message.ts +8 -0
- package/src/approvals/approval-primitive.ts +5 -2
- package/src/approvals/scoped-approval-grants.ts +6 -2
- package/src/apps/app-store.ts +7 -6
- 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/browser/operations.ts +2 -1
- package/src/browser/types.ts +7 -0
- package/src/browser/virtual-desktop-target.ts +34 -0
- 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 +27 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +151 -27
- 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 +140 -3
- 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 +17 -4
- 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 +194 -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 +140 -76
- package/src/calls/voice-triage-escalate.ts +178 -59
- 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__/browser.test.ts +84 -3
- package/src/cli/commands/__tests__/cli-test-harness.ts +21 -3
- package/src/cli/commands/__tests__/plugins.test.ts +85 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/__tests__/schedules.test.ts +14 -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 +9 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/browser.help.ts +43 -0
- package/src/cli/commands/browser.ts +52 -8
- 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 +9 -5
- 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/oauth/index.help.ts +10 -0
- package/src/cli/commands/oauth/providers.ts +12 -0
- package/src/cli/commands/oauth/request.test.ts +179 -2
- package/src/cli/commands/oauth/request.ts +41 -10
- package/src/cli/commands/oauth/status.ts +5 -0
- package/src/cli/commands/plugins.ts +73 -23
- package/src/cli/commands/roadmap.help.ts +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/commands/schedules.ts +2 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +219 -0
- package/src/cli/lib/__tests__/install-from-platform.test.ts +77 -2
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +45 -3
- package/src/cli/lib/__tests__/local-plugin-upgrade.test.ts +251 -0
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +65 -2
- package/src/cli/lib/__tests__/plugin-catalog-platform.test.ts +14 -0
- package/src/cli/lib/__tests__/plugin-catalog-resolve.test.ts +27 -1
- package/src/cli/lib/__tests__/plugin-details.test.ts +35 -2
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +20 -0
- package/src/cli/lib/__tests__/plugins-install-offline.test.ts +43 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +31 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +122 -5
- package/src/cli/lib/bundled-marketplace.json +962 -81
- package/src/cli/lib/bundled-plugin-packages.json +786 -0
- package/src/cli/lib/bundled-plugin-packages.ts +87 -0
- package/src/cli/lib/diff-plugin.ts +1 -1
- package/src/cli/lib/inspect-plugin.ts +87 -12
- package/src/cli/lib/install-from-github.ts +150 -94
- package/src/cli/lib/install-from-platform.ts +14 -9
- package/src/cli/lib/list-installed-plugins.ts +21 -35
- package/src/cli/lib/plugin-catalog-cache.ts +22 -3
- package/src/cli/lib/plugin-catalog-local.ts +13 -3
- package/src/cli/lib/plugin-catalog-platform.ts +6 -1
- package/src/cli/lib/plugin-catalog-resolve.ts +20 -0
- package/src/cli/lib/plugin-details.ts +33 -8
- package/src/cli/lib/plugin-marketplace.ts +121 -21
- package/src/cli/lib/plugin-pin-history.ts +5 -2
- package/src/cli/lib/search-plugins.ts +58 -16
- package/src/cli/lib/uninstall-plugin.ts +59 -1
- package/src/cli/lib/upgrade-plugin.ts +61 -8
- package/src/cli/output.ts +18 -4
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/default-provider.test.ts +4 -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/__tests__/plugin-resident-skill-discovery.test.ts +21 -0
- 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 +23 -15
- package/src/config/bundled-skills/acp/TOOLS.json +7 -3
- 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 +59 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +101 -2
- 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 +7 -8
- package/src/config/bundled-skills/screen-annotation/SKILL.md +10 -3
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +13 -9
- 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 +33 -13
- package/src/config/loader.ts +11 -17
- 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/profile-text-generation.test.ts +51 -0
- package/src/config/profile-text-generation.ts +51 -0
- 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/llm.ts +7 -2
- package/src/config/schemas/mcp.ts +15 -9
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/monitoring.ts +9 -0
- package/src/config/schemas/services.ts +18 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/config/skills.ts +13 -21
- package/src/credential-execution/ces-runtime.ts +2 -2
- package/src/credential-execution/executable-discovery.ts +36 -134
- package/src/credential-execution/process-manager.test.ts +37 -42
- package/src/credential-execution/process-manager.ts +24 -19
- package/src/credential-health/credential-health-service.ts +11 -17
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +47 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +5 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +36 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/assistant-attachments.ts +20 -12
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-agent-loop-handlers.ts +74 -3
- package/src/daemon/conversation-agent-loop.ts +12 -0
- package/src/daemon/conversation-attachments.ts +93 -12
- package/src/daemon/conversation-client-surface.ts +26 -0
- 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 +21 -8
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +21 -10
- package/src/daemon/conversation-tool-setup.ts +135 -109
- package/src/daemon/conversation-turn-finalize.ts +26 -13
- package/src/daemon/conversation.ts +17 -3
- package/src/daemon/daemon-control.ts +3 -7
- 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 +275 -45
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +4 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +4 -9
- package/src/daemon/mcp-reload-service.ts +13 -6
- package/src/daemon/message-types/shared.ts +1 -0
- package/src/daemon/message-types/sync.ts +2 -0
- package/src/daemon/orphan-reaper.ts +4 -3
- package/src/daemon/persist-media-references.ts +20 -3
- package/src/daemon/providers-setup.ts +5 -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-setup-types.ts +6 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/daemon/wake-conversation-ops.ts +38 -15
- package/src/desktop/__tests__/fake-desktop.ts +123 -0
- package/src/desktop/desktop-automation-lease.test.ts +351 -0
- package/src/desktop/desktop-automation-lease.ts +306 -0
- package/src/desktop/desktop-browser-client.test.ts +413 -0
- package/src/desktop/desktop-browser-client.ts +430 -0
- package/src/desktop/desktop-browser-cursor.ts +40 -0
- package/src/desktop/desktop-browser-endpoint.test.ts +78 -0
- package/src/desktop/desktop-browser-endpoint.ts +145 -0
- package/src/desktop/desktop-browser-operations.ts +130 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.test.ts +48 -0
- package/src/desktop/desktop-chrome-session.ts +87 -0
- package/src/desktop/desktop-dependencies.test.ts +131 -0
- package/src/desktop/desktop-dependencies.ts +329 -0
- package/src/desktop/desktop-display.ts +9 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +162 -0
- package/src/desktop/desktop-session-manager.test.ts +813 -0
- package/src/desktop/desktop-session-manager.ts +970 -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-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/desktop/virtual-desktop-feature.ts +18 -0
- package/src/desktop/virtual-desktop-platform.test.ts +61 -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 +112 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +70 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/ipc/__tests__/browser-ipc.test.ts +230 -2
- package/src/ipc/__tests__/cancel-on-disconnect.test.ts +54 -0
- package/src/ipc/assistant-server.ts +15 -1
- package/src/ipc/cli-client.ts +13 -5
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +143 -0
- package/src/live-voice/__tests__/live-voice-events.test.ts +9 -7
- package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +374 -0
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +94 -15
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +19 -0
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +660 -5
- package/src/live-voice/__tests__/protocol.test.ts +95 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/__tests__/session-controls.test.ts +156 -0
- package/src/live-voice/live-voice-metrics.ts +9 -0
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +1081 -760
- package/src/live-voice/protocol.ts +89 -2
- package/src/live-voice/session-controls.ts +197 -0
- package/src/mcp/__tests__/credential-target.test.ts +59 -0
- package/src/mcp/__tests__/effective-config.test.ts +13 -2
- package/src/mcp/__tests__/manager-state.test.ts +128 -0
- package/src/mcp/__tests__/manager-tool-caps.test.ts +111 -0
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +20 -6
- package/src/mcp/__tests__/mcp-auth-state-target.test.ts +49 -0
- package/src/mcp/__tests__/plugin-mcp-oauth-cleanup.test.ts +107 -0
- package/src/mcp/__tests__/plugin-mcp-oauth-provider-isolation.test.ts +89 -0
- package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +63 -11
- package/src/mcp/__tests__/reload-signal-emission.test.ts +39 -2
- package/src/mcp/__tests__/startup.test.ts +30 -7
- package/src/mcp/__tests__/tool-caps.test.ts +107 -0
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/client.ts +64 -16
- package/src/mcp/credential-target.ts +105 -0
- package/src/mcp/effective-config.ts +45 -18
- package/src/mcp/manager.ts +201 -81
- package/src/mcp/mcp-auth-orchestrator.ts +17 -4
- package/src/mcp/mcp-auth-state.ts +38 -6
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/mcp-oauth-provider.ts +101 -33
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +41 -17
- package/src/mcp/tool-caps.ts +176 -0
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider-message-metadata.ts +3 -3
- 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__/mount-watch.test.ts +236 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/monitoring/mount-watch.ts +348 -0
- package/src/monitoring/plugin-auto-update.ts +6 -0
- package/src/monitoring/worker.ts +9 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/copy-composer.test.ts +70 -0
- package/src/notifications/__tests__/decision-engine.test.ts +175 -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/copy-composer.ts +11 -3
- package/src/notifications/decision-engine.ts +43 -8
- 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 +64 -22
- package/src/oauth/AGENTS.md +4 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +36 -1
- package/src/oauth/__tests__/seed-providers-managed.test.ts +149 -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 +100 -0
- package/src/oauth/connection-resolver.ts +120 -3
- package/src/oauth/identity-verifier.ts +24 -0
- package/src/oauth/oauth-store.ts +11 -1
- package/src/oauth/provider-serializer.ts +10 -0
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +367 -27
- package/src/onboarding/checkin-event.ts +2 -1
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/permissions/confirmation-guardian-request.test.ts +16 -0
- package/src/permissions/confirmation-guardian-request.ts +2 -3
- package/src/persistence/attachments-store.ts +84 -44
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +182 -83
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-plugin-facade.ts +13 -0
- 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/conversation-tool-surface.ts +86 -0
- package/src/persistence/conversation-types.test.ts +32 -0
- package/src/persistence/conversation-types.ts +26 -4
- 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/378-create-conversation-tool-surfaces.test.ts +78 -0
- package/src/persistence/migrations/378-create-conversation-tool-surfaces.ts +29 -0
- package/src/persistence/migrations/379-oauth-providers-response-ok-field.test.ts +92 -0
- package/src/persistence/migrations/379-oauth-providers-response-ok-field.ts +32 -0
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/conversation-tool-surfaces.ts +22 -0
- package/src/persistence/schema/index.ts +1 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/persistence/schema/oauth.ts +1 -0
- package/src/persistence/steps.ts +15 -3
- 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/conversation-turn.ts +31 -7
- package/src/plugin-api/index.ts +10 -1
- package/src/plugin-api/plugin-channel-turn-trust.test.ts +133 -0
- package/src/plugin-api/plugin-channel-turn-trust.ts +71 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +14 -1
- package/src/plugins/__tests__/mcp-servers.test.ts +38 -8
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/AGENTS.md +14 -2
- package/src/plugins/defaults/memory/__tests__/buffer-file.test.ts +365 -0
- package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +43 -0
- package/src/plugins/defaults/memory/__tests__/fixtures/buffer-appender.ts +17 -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 +66 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +20 -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__/memory-run-evidence.test.ts +161 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/buffer-file.ts +354 -0
- package/src/plugins/defaults/memory/buffer-format.ts +40 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -3
- package/src/plugins/defaults/memory/context-search/format.ts +4 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +7 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +4 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -8
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -42
- 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/injectors.ts +4 -3
- 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 +67 -184
- 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/memory-run-evidence.ts +213 -0
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +1 -1
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +407 -99
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +10 -0
- package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +107 -7
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +307 -86
- package/src/plugins/defaults/memory/substrate/consolidation-tool-surface.ts +34 -0
- package/src/plugins/defaults/memory/substrate/page-index.ts +7 -1
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +89 -49
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +1 -1
- package/src/plugins/defaults/memory/tools.ts +1 -1
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -1
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +5 -0
- package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +5 -2
- package/src/plugins/defaults/memory/v2/reranker.ts +7 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +81 -1
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +87 -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 +51 -0
- package/src/plugins/defaults/memory/v3/card.ts +4 -2
- package/src/plugins/defaults/memory/v3/injector.ts +212 -178
- package/src/plugins/defaults/memory/v3/orchestrate.ts +86 -22
- package/src/plugins/defaults/memory/v3/pool-select.ts +13 -8
- package/src/plugins/defaults/memory/v3/sections.ts +10 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +13 -1
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +1 -46
- package/src/plugins/external-plugin-loader.ts +49 -86
- package/src/plugins/installed-plugin-dirs.ts +6 -4
- package/src/plugins/mcp-servers.ts +24 -90
- package/src/plugins/mtime-cache.ts +2 -1
- package/src/plugins/user-loader.ts +2 -1
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-callsite.test.ts +41 -0
- 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/__tests__/endpoint-probe.test.ts +87 -3
- package/src/providers/inference/adapter-factory.ts +6 -0
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +36 -12
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/jev/client.test.ts +260 -0
- package/src/providers/jev/client.ts +518 -0
- package/src/providers/model-catalog.ts +92 -3
- 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/openai/responses-provider.ts +9 -2
- package/src/providers/opencode/client.test.ts +240 -4
- package/src/providers/opencode/client.ts +129 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +9 -3
- package/src/runtime/__tests__/agent-wake.test.ts +86 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +132 -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 +22 -5
- 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/guardian-action-service.ts +2 -17
- package/src/runtime/guardian-reply-router.ts +1 -8
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +89 -0
- package/src/runtime/migrations/vbundle-import-policy.ts +33 -11
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +16 -5
- package/src/runtime/routes/__tests__/acp-routes.test.ts +178 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +275 -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__/inference-profiles-routes.test.ts +24 -2
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +127 -3
- package/src/runtime/routes/acp-claude-auth-routes.ts +11 -3
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +84 -165
- package/src/runtime/routes/app-management-routes.ts +90 -2
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/browser-context.ts +39 -0
- package/src/runtime/routes/browser-routes.ts +16 -47
- package/src/runtime/routes/browser-tabs-routes.ts +72 -29
- package/src/runtime/routes/channel-route-shared.ts +1 -9
- 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 +45 -7
- package/src/runtime/routes/credential-routes.ts +20 -14
- package/src/runtime/routes/desktop-setup-routes.test.ts +89 -0
- package/src/runtime/routes/desktop-setup-routes.ts +41 -0
- package/src/runtime/routes/diagnostics-routes.ts +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/guardian-approval-interception.ts +24 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- package/src/runtime/routes/inbound-message-handler.ts +2 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +1 -1
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -4
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/inference-profile-session-handler.ts +11 -1
- package/src/runtime/routes/inference-profiles-routes.ts +9 -0
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +141 -137
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/oauth-commands-routes.ts +98 -45
- package/src/runtime/routes/oauth-providers.ts +11 -0
- package/src/runtime/routes/oauth-request-hints.test.ts +246 -0
- package/src/runtime/routes/oauth-request-hints.ts +149 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/plugins-routes.ts +83 -50
- package/src/runtime/routes/schedule-routes.ts +2 -0
- package/src/runtime/routes/secret-routes.ts +99 -119
- 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 +6 -1
- package/src/schedule/__tests__/worker-mcp-bootstrap.test.ts +107 -0
- package/src/schedule/__tests__/worker-mcp-readiness.test.ts +173 -0
- package/src/schedule/__tests__/worker-mcp-tools.test.ts +13 -47
- package/src/schedule/run-script.ts +4 -3
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/scheduler.ts +47 -11
- package/src/schedule/tool-surface-readiness.ts +65 -0
- package/src/schedule/worker-mcp.ts +85 -0
- package/src/schedule/worker.ts +9 -31
- 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/secure-keys.ts +60 -48
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/managed-store.ts +153 -31
- 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 +349 -0
- package/src/tools/acp/status.ts +112 -9
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +45 -14
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +69 -0
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +5 -0
- 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 +123 -5
- 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 +16 -26
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/shared/filesystem/file-ops-service.ts +1 -31
- package/src/tools/shared/shell-output.test.ts +10 -0
- package/src/tools/shared/shell-output.ts +14 -2
- package/src/tools/skills/find-similar-skills.test.ts +214 -2
- package/src/tools/skills/find-similar-skills.ts +62 -9
- package/src/tools/skills/resolve-execute-invocation.ts +24 -0
- package/src/tools/skills/sandbox-runner.ts +13 -2
- package/src/tools/skills/scaffold-managed.ts +78 -16
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/safe-env.test.ts +33 -4
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +43 -0
- package/src/tools/terminal/safe-env.ts +35 -24
- package/src/tools/terminal/sanitized-bash.ts +118 -0
- package/src/tools/terminal/shell-launch.test.ts +162 -0
- package/src/tools/terminal/shell.test.ts +29 -0
- package/src/tools/terminal/shell.ts +13 -7
- package/src/tools/ui-surface/channel-variants.ts +10 -59
- 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/abort-reasons.ts +12 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/host-process.test.ts +17 -1
- package/src/util/host-process.ts +24 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/util/plugin-manifest.ts +203 -0
- package/src/util/unicode.ts +44 -1
- package/src/watch/watch-retro.ts +6 -8
- 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
|
@@ -23,7 +23,10 @@ import {
|
|
|
23
23
|
getMessageById,
|
|
24
24
|
updateMessageContent,
|
|
25
25
|
} from "../persistence/conversation-crud.js";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
ASSISTANT_INITIATED_SOURCE,
|
|
28
|
+
isBackgroundConversationType,
|
|
29
|
+
} from "../persistence/conversation-types.js";
|
|
27
30
|
import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
|
|
28
31
|
import { getLogger } from "../util/logger.js";
|
|
29
32
|
import { normalizeTitle, stripMarkdown } from "../util/short-title.js";
|
|
@@ -272,6 +275,11 @@ async function resolveOwnedConversationMessageId(
|
|
|
272
275
|
sourceConversationId: string | undefined,
|
|
273
276
|
summary: string,
|
|
274
277
|
): Promise<string | undefined> {
|
|
278
|
+
// The completed reply is already the source conversation's canonical row.
|
|
279
|
+
// This signal's body is a compact push preview, not conversation content.
|
|
280
|
+
if (signal.sourceEventName === "chat.assistant_reply") {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
275
283
|
if (vellumDelivery?.conversationId) {
|
|
276
284
|
return vellumDelivery.conversationId === sourceConversationId
|
|
277
285
|
? vellumDelivery.messageId
|
|
@@ -495,6 +503,18 @@ function deriveDetailPanelKind(
|
|
|
495
503
|
* `isAsyncBackground` hint. `chat.assistant_reply` also mirrors: it is the
|
|
496
504
|
* durable in-app record for the push sent after a user leaves a chat, while
|
|
497
505
|
* retaining the normal interactive conversation as its navigation target.
|
|
506
|
+
*
|
|
507
|
+
* A delivery that materialized an assistant-initiated thread never mirrors.
|
|
508
|
+
* Under the `assistant-initiated-threads` flag, `conversation-pairing.ts`
|
|
509
|
+
* promotes a background `assistant.share` into a standard conversation
|
|
510
|
+
* stamped {@link ASSISTANT_INITIATED_SOURCE}, and that thread's row in the
|
|
511
|
+
* sidebar's assistant section is the share's one surface. Mirroring it here
|
|
512
|
+
* as well put the same share in the bell a second time, labeled by its
|
|
513
|
+
* source channel ("Heartbeat"). The check reads the paired conversation's
|
|
514
|
+
* `source` rather than re-deriving the promotion rule, so it holds however
|
|
515
|
+
* that rule narrows or widens. Guardian requests are checked first on
|
|
516
|
+
* purpose: the bell is their canonical home whatever conversation they pair
|
|
517
|
+
* with.
|
|
498
518
|
*/
|
|
499
519
|
function resolveHomeFeedMirror(
|
|
500
520
|
signal: NotificationSignal,
|
|
@@ -525,6 +545,15 @@ function resolveHomeFeedMirror(
|
|
|
525
545
|
: fallbackConversationId;
|
|
526
546
|
const sourceScheduleJobId = sourceRow?.scheduleJobId ?? undefined;
|
|
527
547
|
|
|
548
|
+
// Guardian requests always project into the feed: the "Needs
|
|
549
|
+
// attention" item is the request's canonical home, and the request
|
|
550
|
+
// blocks on the guardian whatever kind of conversation raised it.
|
|
551
|
+
if (isGuardianRequestSignalEvent(signal.sourceEventName)) {
|
|
552
|
+
return { mirror: true, sourceConversationId, sourceScheduleJobId };
|
|
553
|
+
}
|
|
554
|
+
if (isAssistantInitiatedThreadDelivery(fallbackConversationId)) {
|
|
555
|
+
return { mirror: false };
|
|
556
|
+
}
|
|
528
557
|
if (
|
|
529
558
|
signal.sourceChannel === "assistant_tool" ||
|
|
530
559
|
signal.sourceEventName === "chat.assistant_reply"
|
|
@@ -534,18 +563,37 @@ function resolveHomeFeedMirror(
|
|
|
534
563
|
if (signal.attentionHints.isAsyncBackground) {
|
|
535
564
|
return { mirror: true, sourceConversationId, sourceScheduleJobId };
|
|
536
565
|
}
|
|
537
|
-
// Guardian requests always project into the feed: the "Needs
|
|
538
|
-
// attention" item is the request's canonical home, and the request
|
|
539
|
-
// blocks on the guardian whatever kind of conversation raised it.
|
|
540
|
-
if (isGuardianRequestSignalEvent(signal.sourceEventName)) {
|
|
541
|
-
return { mirror: true, sourceConversationId, sourceScheduleJobId };
|
|
542
|
-
}
|
|
543
566
|
if (isBackgroundConversationType(sourceRow?.conversationType)) {
|
|
544
567
|
return { mirror: true, sourceConversationId, sourceScheduleJobId };
|
|
545
568
|
}
|
|
546
569
|
return { mirror: false };
|
|
547
570
|
}
|
|
548
571
|
|
|
572
|
+
/**
|
|
573
|
+
* Whether the vellum delivery this signal produced landed in a thread the
|
|
574
|
+
* sidebar's assistant section already shows.
|
|
575
|
+
*
|
|
576
|
+
* Best-effort like the source lookup above: a missing id, a missing row, or
|
|
577
|
+
* a lookup failure all read as "not an assistant thread", so a storage
|
|
578
|
+
* hiccup degrades to the pre-existing behavior (a bell row) rather than
|
|
579
|
+
* dropping the notification everywhere.
|
|
580
|
+
*/
|
|
581
|
+
function isAssistantInitiatedThreadDelivery(
|
|
582
|
+
deliveryConversationId: string | undefined,
|
|
583
|
+
): boolean {
|
|
584
|
+
if (!deliveryConversationId) {
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
try {
|
|
588
|
+
return (
|
|
589
|
+
getConversation(deliveryConversationId)?.source ===
|
|
590
|
+
ASSISTANT_INITIATED_SOURCE
|
|
591
|
+
);
|
|
592
|
+
} catch {
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
549
597
|
/**
|
|
550
598
|
* Routing-intent enforcement can prune `selectedChannels` without also
|
|
551
599
|
* pruning `renderedCopy`, so iterating `renderedCopy` directly risks
|
|
@@ -32,6 +32,7 @@ import { getAttentionStateByConversationIds } from "../persistence/conversation-
|
|
|
32
32
|
import {
|
|
33
33
|
getAssistantMessageIdsInTurn,
|
|
34
34
|
getMessageById,
|
|
35
|
+
getMessagesAfter,
|
|
35
36
|
type MessageRow,
|
|
36
37
|
} from "../persistence/conversation-crud.js";
|
|
37
38
|
import { stringifyMessageContent } from "../persistence/message-content.js";
|
|
@@ -39,7 +40,6 @@ import {
|
|
|
39
40
|
isPrivateAssistantText,
|
|
40
41
|
projectPersistedAssistantContent,
|
|
41
42
|
} from "../persistence/user-facing-content.js";
|
|
42
|
-
import type { ContentBlock } from "../providers/types.js";
|
|
43
43
|
import { emitNotificationSignal } from "./emit-signal.js";
|
|
44
44
|
import { hasNotifiedSourceContextSince } from "./events-store.js";
|
|
45
45
|
import {
|
|
@@ -79,28 +79,70 @@ export interface ScheduleResultNotificationParams {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
|
-
* Whether
|
|
83
|
-
*
|
|
82
|
+
* Whether the run delivered its result through the messaging tool, and the
|
|
83
|
+
* call succeeded.
|
|
84
84
|
*
|
|
85
|
-
* The
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* cannot see them. Without this check a well-authored Slack digest would post
|
|
85
|
+
* The messaging tool is the route the schedule skill prescribes for rich
|
|
86
|
+
* content, and it writes no `notification_events` row, so the pipeline probe
|
|
87
|
+
* cannot see it. Without this check a well-authored Slack digest would post
|
|
89
88
|
* its summary and then get a second notification whose body is "Posted the
|
|
90
|
-
* digest to #general."
|
|
91
|
-
*
|
|
92
|
-
*
|
|
89
|
+
* digest to #general."
|
|
90
|
+
*
|
|
91
|
+
* Both conditions are load-bearing. The route has to be recognized, because
|
|
92
|
+
* this is a known-routes list rather than a general "did the run do anything?"
|
|
93
|
+
* heuristic. And the call has to have succeeded: a `tool_use` block records
|
|
94
|
+
* only that the model called the tool, so a send the channel refused leaves
|
|
95
|
+
* the same block as one that landed, and suppressing on it costs the user both
|
|
96
|
+
* the digest and the run's explanation of the failure. The executor's verdict
|
|
97
|
+
* is on the `tool_result` the call produced, so success is read from there,
|
|
98
|
+
* and a call with no result counts as undelivered. Either condition unmet gets
|
|
99
|
+
* the fallback, which is the safe failure.
|
|
100
|
+
*
|
|
101
|
+
* The result read starts at the run's first row, so it covers the results
|
|
102
|
+
* interleaved through the turn. Rows from a later turn in a reused
|
|
103
|
+
* conversation can come back too and are harmless, because only this run's
|
|
104
|
+
* own call ids are matched.
|
|
105
|
+
*
|
|
106
|
+
* A Slack Web API post through bash is deliberately not a route. It is off
|
|
107
|
+
* the guided path and unrecorded, and recognizing it would mean reading a
|
|
108
|
+
* shell command for a method name. A run that posts that way gets the
|
|
109
|
+
* fallback too: at worst a duplicate, never a silence.
|
|
93
110
|
*/
|
|
94
|
-
function
|
|
95
|
-
|
|
96
|
-
|
|
111
|
+
function deliveredThroughMessagingTool(
|
|
112
|
+
conversationId: string,
|
|
113
|
+
runRows: readonly MessageRow[],
|
|
114
|
+
): boolean {
|
|
115
|
+
const callIds = new Set<string>();
|
|
116
|
+
for (const row of runRows) {
|
|
117
|
+
for (const block of row.content) {
|
|
118
|
+
if (block.type === "tool_use" && block.name === "messaging_send") {
|
|
119
|
+
callIds.add(block.id);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
97
122
|
}
|
|
98
|
-
if (
|
|
99
|
-
return
|
|
123
|
+
if (callIds.size === 0) {
|
|
124
|
+
return false;
|
|
100
125
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
126
|
+
const [firstRunRow] = runRows;
|
|
127
|
+
for (const row of getMessagesAfter(conversationId, {
|
|
128
|
+
id: firstRunRow.id,
|
|
129
|
+
createdAt: firstRunRow.createdAt,
|
|
130
|
+
})) {
|
|
131
|
+
if (row.role !== "user") {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
for (const block of row.content) {
|
|
135
|
+
// guard:allow-tool-result-only: the local executor's verdict on a
|
|
136
|
+
// delivery it ran. A server-side `web_search_tool_result` carries no
|
|
137
|
+
// `is_error` and never delivers anything.
|
|
138
|
+
if (
|
|
139
|
+
block.type === "tool_result" &&
|
|
140
|
+
block.is_error !== true &&
|
|
141
|
+
callIds.has(block.tool_use_id)
|
|
142
|
+
) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
104
146
|
}
|
|
105
147
|
return false;
|
|
106
148
|
}
|
|
@@ -237,11 +279,11 @@ export async function emitScheduleResultNotification(
|
|
|
237
279
|
return;
|
|
238
280
|
}
|
|
239
281
|
|
|
240
|
-
// The run delivered around the pipeline
|
|
241
|
-
//
|
|
242
|
-
//
|
|
282
|
+
// The run delivered around the pipeline through the messaging tool, and
|
|
283
|
+
// the call succeeded. The user has the result; a notification reading
|
|
284
|
+
// "posted it" on top would be the duplicate.
|
|
243
285
|
const runRows = collectRunRows(latestRow, conversationId, runStartedAt);
|
|
244
|
-
if (
|
|
286
|
+
if (deliveredThroughMessagingTool(conversationId, runRows)) {
|
|
245
287
|
return;
|
|
246
288
|
}
|
|
247
289
|
|
package/src/oauth/AGENTS.md
CHANGED
|
@@ -8,6 +8,10 @@ When introducing a new built-in OAuth integration (one that appears in `seed-pro
|
|
|
8
8
|
|
|
9
9
|
Add an entry to `PROVIDER_SEED_DATA`. Required fields: `provider`, `authorizeUrl`, `tokenExchangeUrl`, `defaultScopes`, `displayLabel`, `description`, `dashboardUrl`, `clientIdPlaceholder`, `logoUrl`, and `injectionTemplates`. Optional: `availableScopes` — either a structured array of `{scope, description?}` objects or a URL string pointing to the provider's scope documentation. See existing entries for the full shape. The `provider` key must be snake_case and is used as the canonical identifier everywhere else.
|
|
10
10
|
|
|
11
|
+
`injectionTemplates` is also a security boundary, not only an injection rule. `assistant oauth request` and `assistant channels request` accept an absolute URL only when its host matches one of the entry's `hostPattern` values (`assertOAuthRequestUrlAllowed` in `../runtime/routes/oauth-commands-routes.ts`), so that list is the full set of hosts the credential may be sent to. A pattern matches exactly unless it starts with `*.`; reserve the wildcard for per-tenant hosts that cannot be enumerated (`*.salesforce.com`), and name a fixed host the provider documents (`files.slack.com`) by name. The guard sees only the URL the caller passes: the connection follows a redirect itself, and a cross-origin hop carries no `Authorization`, so a redirect target never receives the credential and never needs listing.
|
|
12
|
+
|
|
13
|
+
`responseOkField` declares the boolean a provider's API puts in every response body to report success (Slack: `ok`), and it is read in one place, `providerReportsFailure` in `identity-verifier.ts`. It is not `identityOkField`, which governs only the identity call and stays the documented `--identity-ok-field` contract. Both request doors and `oauth ping` treat an explicit `false` under a 2xx as a failed call, since such a provider hides refusals behind a successful status; a body without the field leaves the status as the verdict. Declare it on every row that calls such an API, the bot row included.
|
|
14
|
+
|
|
11
15
|
If the provider will support managed mode, set `managedServiceConfigKey` to a slug matching the key you will add to `ServicesSchema` (e.g. `"acme-oauth"`).
|
|
12
16
|
|
|
13
17
|
### 2. _(managed only)_ Add a service schema — `../config/schemas/services.ts`
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
providerReportsFailure,
|
|
5
|
+
verifyIdentity,
|
|
6
|
+
} from "../identity-verifier.js";
|
|
4
7
|
import type { OAuthProviderRow } from "../oauth-store.js";
|
|
5
8
|
|
|
6
9
|
// ---------------------------------------------------------------------------
|
|
@@ -66,6 +69,7 @@ function makeProviderRow(
|
|
|
66
69
|
identityResponsePaths: null,
|
|
67
70
|
identityFormat: null,
|
|
68
71
|
identityOkField: null,
|
|
72
|
+
responseOkField: null,
|
|
69
73
|
featureFlag: null,
|
|
70
74
|
createdAt: now,
|
|
71
75
|
updatedAt: now,
|
|
@@ -469,3 +473,34 @@ describe("verifyIdentity", () => {
|
|
|
469
473
|
});
|
|
470
474
|
});
|
|
471
475
|
});
|
|
476
|
+
|
|
477
|
+
describe("providerReportsFailure", () => {
|
|
478
|
+
test("reads an explicit false in the declared field as failure", () => {
|
|
479
|
+
expect(
|
|
480
|
+
providerReportsFailure(
|
|
481
|
+
{ responseOkField: "ok" },
|
|
482
|
+
{ ok: false, error: "invalid_auth" },
|
|
483
|
+
),
|
|
484
|
+
).toBe(true);
|
|
485
|
+
expect(
|
|
486
|
+
providerReportsFailure({ responseOkField: "ok" }, { ok: true }),
|
|
487
|
+
).toBe(false);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
test("a body without the field says nothing", () => {
|
|
491
|
+
// A download or an endpoint outside the envelope; the status decides.
|
|
492
|
+
expect(providerReportsFailure({ responseOkField: "ok" }, null)).toBe(false);
|
|
493
|
+
expect(
|
|
494
|
+
providerReportsFailure({ responseOkField: "ok" }, Buffer.from("png")),
|
|
495
|
+
).toBe(false);
|
|
496
|
+
expect(
|
|
497
|
+
providerReportsFailure({ responseOkField: "ok" }, { data: {} }),
|
|
498
|
+
).toBe(false);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
test("a provider that declares no field never reports failure", () => {
|
|
502
|
+
expect(
|
|
503
|
+
providerReportsFailure({ responseOkField: null }, { ok: false }),
|
|
504
|
+
).toBe(false);
|
|
505
|
+
});
|
|
506
|
+
});
|
|
@@ -60,6 +60,26 @@ describe("PROVIDER_SEED_DATA managed mode wiring", () => {
|
|
|
60
60
|
);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
// `folders:read` is documented too, but the app's scope picker only
|
|
64
|
+
// offers `folder_metadata:read` under Folders, so Figma answers
|
|
65
|
+
// "Invalid scopes for app" whenever it is requested.
|
|
66
|
+
expect(figma.defaultScopes).not.toContain("folders:read");
|
|
67
|
+
if (Array.isArray(availableScopes)) {
|
|
68
|
+
expect(availableScopes.map(({ scope }) => scope)).toContain(
|
|
69
|
+
"folders:read",
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// `file_code_connect:write` is only offered to apps owned by an
|
|
74
|
+
// Organization-plan team. Managed apps in other environments are not,
|
|
75
|
+
// so requesting it fails the whole authorization there.
|
|
76
|
+
expect(figma.defaultScopes).not.toContain("file_code_connect:write");
|
|
77
|
+
if (Array.isArray(availableScopes)) {
|
|
78
|
+
expect(availableScopes.map(({ scope }) => scope)).toContain(
|
|
79
|
+
"file_code_connect:write",
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
63
83
|
// GET /v1/me backs both the ping and the identity label.
|
|
64
84
|
expect(figma.defaultScopes).toContain("current_user:read");
|
|
65
85
|
});
|
|
@@ -127,6 +147,135 @@ describe("PROVIDER_SEED_DATA managed mode wiring", () => {
|
|
|
127
147
|
expect(figma.featureFlag).toBe("figma-oauth");
|
|
128
148
|
});
|
|
129
149
|
|
|
150
|
+
test("shopify keeps its per-tenant URL templates", () => {
|
|
151
|
+
// Shopify has no global OAuth host: every endpoint lives on the
|
|
152
|
+
// merchant's own domain. Hardcoding any host here would send every
|
|
153
|
+
// merchant's authorization to the wrong store, so the {tenant_host}
|
|
154
|
+
// placeholder the platform substitutes must survive.
|
|
155
|
+
const shopify = PROVIDER_SEED_DATA.shopify;
|
|
156
|
+
expect(shopify).toBeDefined();
|
|
157
|
+
for (const url of [
|
|
158
|
+
shopify.authorizeUrl,
|
|
159
|
+
shopify.tokenExchangeUrl,
|
|
160
|
+
shopify.refreshUrl,
|
|
161
|
+
shopify.baseUrl,
|
|
162
|
+
shopify.identityUrl,
|
|
163
|
+
]) {
|
|
164
|
+
expect(url).toContain("{tenant_host}");
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("shopify requests comma-separated scopes", () => {
|
|
169
|
+
// Shopify parses `scope` as a comma-separated list; the OAuth2 default
|
|
170
|
+
// space separator is read as one malformed scope and the whole
|
|
171
|
+
// authorization request is rejected.
|
|
172
|
+
expect(PROVIDER_SEED_DATA.shopify.scopeSeparator).toBe(",");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("shopify does not request approval-gated scopes", () => {
|
|
176
|
+
// Shopify rejects the entire authorization request when the app cannot
|
|
177
|
+
// grant a requested scope -- the same failure that broke monday and
|
|
178
|
+
// Figma. These scopes all require Shopify's prior approval.
|
|
179
|
+
const gated = [
|
|
180
|
+
"read_all_orders",
|
|
181
|
+
"read_customer_payment_methods",
|
|
182
|
+
"read_own_subscription_contracts",
|
|
183
|
+
"write_own_subscription_contracts",
|
|
184
|
+
"read_shopify_payments_dispute_evidences",
|
|
185
|
+
"write_shopify_payments_dispute_evidences",
|
|
186
|
+
];
|
|
187
|
+
for (const scope of gated) {
|
|
188
|
+
expect(PROVIDER_SEED_DATA.shopify.defaultScopes).not.toContain(scope);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("shopify injects the Admin API's own auth header", () => {
|
|
193
|
+
// The Admin API reads X-Shopify-Access-Token and ignores an
|
|
194
|
+
// Authorization: Bearer header entirely, so a Bearer template here would
|
|
195
|
+
// make every proxied request unauthenticated.
|
|
196
|
+
const templates = PROVIDER_SEED_DATA.shopify.injectionTemplates;
|
|
197
|
+
expect(templates).toBeDefined();
|
|
198
|
+
const [template] = templates ?? [];
|
|
199
|
+
expect(template?.headerName).toBe("X-Shopify-Access-Token");
|
|
200
|
+
expect(template?.valuePrefix).toBe("");
|
|
201
|
+
expect(template?.hostPattern).toBe("*.myshopify.com");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("quickbooks is wired up for managed mode behind its flag", () => {
|
|
205
|
+
const quickbooks = PROVIDER_SEED_DATA.quickbooks;
|
|
206
|
+
expect(quickbooks).toBeDefined();
|
|
207
|
+
expect(quickbooks.managedServiceConfigKey).toBe("quickbooks-oauth");
|
|
208
|
+
expect("quickbooks-oauth" in ServicesSchema.shape).toBe(true);
|
|
209
|
+
// Hidden until the platform side and client ids are live, like Figma
|
|
210
|
+
// and Shopify.
|
|
211
|
+
expect(quickbooks.featureFlag).toBe("quickbooks-oauth");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("quickbooks uses Intuit's OAuth endpoints with HTTP Basic", () => {
|
|
215
|
+
const quickbooks = PROVIDER_SEED_DATA.quickbooks;
|
|
216
|
+
expect(quickbooks.authorizeUrl).toBe(
|
|
217
|
+
"https://appcenter.intuit.com/connect/oauth2",
|
|
218
|
+
);
|
|
219
|
+
expect(quickbooks.tokenExchangeUrl).toBe(
|
|
220
|
+
"https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer",
|
|
221
|
+
);
|
|
222
|
+
expect(quickbooks.refreshUrl).toBe(quickbooks.tokenExchangeUrl);
|
|
223
|
+
expect(quickbooks.tokenEndpointAuthMethod).toBe("client_secret_basic");
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("quickbooks keeps its per-company URL templates", () => {
|
|
227
|
+
// Every Accounting API path is scoped to the realm the user picked on
|
|
228
|
+
// Intuit's consent screen. The platform fills {realm_id} from the
|
|
229
|
+
// connection; a hardcoded realm here would send every company's calls
|
|
230
|
+
// to the wrong books.
|
|
231
|
+
const quickbooks = PROVIDER_SEED_DATA.quickbooks;
|
|
232
|
+
for (const url of [quickbooks.baseUrl, quickbooks.identityUrl]) {
|
|
233
|
+
expect(url).toContain("/v3/company/{realm_id}");
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test("quickbooks seeds no ping or revoke URL", () => {
|
|
238
|
+
// The ping route sends the URL's origin as a base override and cannot
|
|
239
|
+
// fill {realm_id}; the daemon's revoke helper posts an unauthenticated
|
|
240
|
+
// form body that Intuit's JSON + Basic-auth endpoint rejects. Either
|
|
241
|
+
// would fail silently on every your-own connection.
|
|
242
|
+
const quickbooks = PROVIDER_SEED_DATA.quickbooks;
|
|
243
|
+
expect(quickbooks.pingUrl).toBeUndefined();
|
|
244
|
+
expect(quickbooks.revokeUrl).toBeUndefined();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("quickbooks requests only the accounting scope by default", () => {
|
|
248
|
+
// Payments needs a Payments-enabled app and the OpenID scopes only add
|
|
249
|
+
// the user's profile; requesting either widens the consent screen for
|
|
250
|
+
// nothing the Accounting API needs.
|
|
251
|
+
const quickbooks = PROVIDER_SEED_DATA.quickbooks;
|
|
252
|
+
expect(quickbooks.defaultScopes).toEqual([
|
|
253
|
+
"com.intuit.quickbooks.accounting",
|
|
254
|
+
]);
|
|
255
|
+
const available = quickbooks.availableScopes;
|
|
256
|
+
expect(Array.isArray(available)).toBe(true);
|
|
257
|
+
if (Array.isArray(available)) {
|
|
258
|
+
expect(available.map(({ scope }) => scope)).toContain(
|
|
259
|
+
"com.intuit.quickbooks.payment",
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("quickbooks allows the credential on both Intuit API hosts", () => {
|
|
265
|
+
// Development keys only work against sandbox companies, which live on
|
|
266
|
+
// the sandbox host. Both hosts belong to Intuit and take the same
|
|
267
|
+
// bearer token.
|
|
268
|
+
const templates = PROVIDER_SEED_DATA.quickbooks.injectionTemplates ?? [];
|
|
269
|
+
expect(templates.map((t) => t.hostPattern).sort()).toEqual([
|
|
270
|
+
"quickbooks.api.intuit.com",
|
|
271
|
+
"sandbox-quickbooks.api.intuit.com",
|
|
272
|
+
]);
|
|
273
|
+
for (const template of templates) {
|
|
274
|
+
expect(template.headerName).toBe("Authorization");
|
|
275
|
+
expect(template.valuePrefix).toBe("Bearer ");
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
130
279
|
test("every managedServiceConfigKey resolves to a ServicesSchema key", () => {
|
|
131
280
|
// Cross-repo invariant: a provider with managedServiceConfigKey but no
|
|
132
281
|
// matching ServicesSchema entry silently falls back to BYO mode in
|
|
@@ -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.
|