@vellumai/assistant 0.11.6 → 0.11.7-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +31 -19
- package/Dockerfile +1 -0
- package/docs/architecture/security.md +3 -3
- package/docs/flux-turn-detection-spike.md +8 -7
- package/docs/runbook-trusted-contacts.md +14 -0
- package/docs/stt-provider-onboarding.md +8 -4
- package/knip.json +1 -0
- package/node_modules/@vellumai/avatar-manifest/package.json +18 -0
- package/node_modules/@vellumai/avatar-manifest/src/__tests__/manifest.test.ts +173 -0
- package/node_modules/@vellumai/avatar-manifest/src/__tests__/read.test.ts +114 -0
- package/node_modules/@vellumai/avatar-manifest/src/index.ts +29 -0
- package/node_modules/@vellumai/avatar-manifest/src/layout.ts +18 -0
- package/node_modules/@vellumai/avatar-manifest/src/manifest.ts +156 -0
- package/node_modules/@vellumai/avatar-manifest/src/read.ts +109 -0
- package/node_modules/@vellumai/avatar-manifest/tsconfig.json +20 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/shell.test.ts +56 -0
- package/node_modules/@vellumai/environments/src/shell.ts +65 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +32 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +82 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/plugin-admission-denied-contract.test.ts +56 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +18 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +5 -11
- package/node_modules/@vellumai/gateway-client/src/channel-permission-contract.ts +3 -1
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +9 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +75 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +17 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +8 -0
- package/node_modules/@vellumai/gateway-client/src/plugin-admission-denied-contract.ts +47 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +11 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/openapi.yaml +419 -55
- package/package.json +4 -1
- package/src/__tests__/approval-card-edit-inbound-handoff.test.ts +199 -0
- package/src/__tests__/approval-card-edit.test.ts +153 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +1 -1
- package/src/__tests__/available-skills.test.ts +54 -0
- package/src/__tests__/avatar-identity-sync.test.ts +5 -1
- package/src/__tests__/catalog-install-normalize.test.ts +9 -1
- package/src/__tests__/channel-policy.test.ts +1 -3
- package/src/__tests__/channel-readiness-discord.test.ts +9 -70
- package/src/__tests__/compactor-call-site-logging.test.ts +61 -0
- package/src/__tests__/config-loader-backfill.test.ts +69 -0
- package/src/__tests__/config-schema.test.ts +2 -3
- package/src/__tests__/conversation-agent-loop.test.ts +238 -0
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-error.test.ts +44 -0
- package/src/__tests__/conversation-evictor.test.ts +19 -0
- package/src/__tests__/conversation-fork-referential.test.ts +73 -63
- package/src/__tests__/conversation-fork-retrospective.test.ts +99 -74
- package/src/__tests__/conversation-runtime-assembly.test.ts +23 -0
- package/src/__tests__/conversation-slash.test.ts +9 -0
- package/src/__tests__/conversation-title-service.test.ts +97 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +16 -0
- package/src/__tests__/credential-broker-server-use.test.ts +0 -218
- package/src/__tests__/credential-broker.test.ts +43 -420
- package/src/__tests__/credential-routes.test.ts +17 -1
- package/src/__tests__/credential-security-invariants.test.ts +8 -9
- package/src/__tests__/default-profile-catalog-fallback.test.ts +289 -0
- package/src/__tests__/discord-callback-round-trip.test.ts +93 -0
- package/src/__tests__/discord-channel-config.test.ts +237 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +51 -1
- package/src/__tests__/fallback-breaker.test.ts +1335 -0
- package/src/__tests__/gateway-threshold-reader-mock.ts +6 -0
- package/src/__tests__/guardian-card-withdrawal.test.ts +30 -0
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -2
- package/src/__tests__/image-conversion.test.ts +38 -4
- package/src/__tests__/install-skill-routing.test.ts +25 -0
- package/src/__tests__/llm-resolver.test.ts +74 -0
- package/src/__tests__/llm-schema-fallback-profile.test.ts +359 -0
- package/src/__tests__/managed-fallback-dispatch.test.ts +430 -0
- package/src/__tests__/managed-profile-guard.test.ts +28 -0
- package/src/__tests__/managed-speech-defaults.test.ts +196 -1
- package/src/__tests__/mcp-cli.test.ts +23 -1
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +43 -34
- package/src/__tests__/media-stream-stt-session.test.ts +2 -0
- package/src/__tests__/notification-decision-fallback.test.ts +106 -0
- package/src/__tests__/notification-discord-adapter.test.ts +162 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +6 -8
- package/src/__tests__/persist-unsendable-image.test.ts +3 -3
- package/src/__tests__/platform.test.ts +19 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
- package/src/__tests__/provider-usage-tracking.test.ts +56 -0
- package/src/__tests__/require-fresh-approval.test.ts +5 -2
- package/src/__tests__/retry-fallback-escalation.test.ts +1093 -0
- package/src/__tests__/schedule-retry.test.ts +183 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +6 -6
- package/src/__tests__/secret-routes-platform-proxy.test.ts +55 -0
- package/src/__tests__/skill-load-tool.test.ts +34 -0
- package/src/__tests__/skill-tool-factory.test.ts +6 -1
- package/src/__tests__/skill-tool-manifest.test.ts +20 -0
- package/src/__tests__/skills.test.ts +25 -0
- package/src/__tests__/subagent-manager-notify.test.ts +38 -0
- package/src/__tests__/terminal-tools.test.ts +2 -0
- package/src/__tests__/tool-approval-handler.test.ts +146 -2
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +108 -2
- package/src/__tests__/tool-audit.test.ts +111 -40
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +228 -18
- package/src/__tests__/ui-shape-teaching.test.ts +1 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +6 -2
- package/src/__tests__/voice-config-update.test.ts +43 -0
- package/src/__tests__/workspace-migration-147-rename-colliding-backup-profile-names.test.ts +480 -0
- package/src/__tests__/workspace-migration-148-strip-unsupported-fallback-profiles.test.ts +167 -0
- package/src/__tests__/workspace-migration-149-repoint-backup-profile-selections.test.ts +144 -0
- package/src/agent/loop.ts +31 -2
- package/src/approvals/guardian-card-withdrawal.ts +22 -0
- package/src/avatar/__tests__/ensure-raster.test.ts +291 -0
- package/src/avatar/avatar-manifest.ts +48 -100
- package/src/avatar/avatar-store.ts +7 -7
- package/src/avatar/ensure-raster.ts +146 -0
- package/src/avatar/resvg-lazy.test.ts +0 -1
- package/src/avatar/traits-png-sync.ts +9 -10
- package/src/background-wake/wake-intent-hooks.test.ts +0 -1
- package/src/backup/__tests__/paths.test.ts +6 -2
- package/src/backup/paths.ts +6 -4
- package/src/bundler/app-compiler.ts +2 -2
- package/src/bundler/compiler-tools.test.ts +36 -0
- package/src/bundler/compiler-tools.ts +54 -18
- package/src/calls/__tests__/telephony-synthesis-language.test.ts +81 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +20 -0
- package/src/calls/media-stream-stt-session.ts +2 -1
- package/src/calls/telephony-synthesis-language.ts +13 -5
- package/src/calls/voice-session-bridge.ts +6 -4
- package/src/channels/config.ts +23 -12
- package/src/cli/AGENTS.md +3 -1
- package/src/cli/commands/backup.help.ts +10 -13
- package/src/cli/commands/contacts.help.ts +16 -4
- package/src/cli/commands/contacts.ts +5 -1
- package/src/cli/commands/credentials.help.ts +1 -1
- package/src/cli/commands/inference-providers.ts +18 -53
- package/src/cli/commands/mcp.help.ts +6 -5
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/memory/memory-retrospective.ts +1 -0
- package/src/cli/commands/stt.help.ts +2 -2
- package/src/config/__tests__/backup-schema.test.ts +1 -1
- package/src/config/__tests__/default-profile-catalog.test.ts +83 -2
- package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +1 -1
- package/src/config/__tests__/memory-retrospective-schema.test.ts +12 -15
- package/src/config/bundled-skills/computer-use/SKILL.md +6 -6
- package/src/config/bundled-skills/computer-use/TOOLS.json +9 -6
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +3 -1
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +16 -15
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +2 -1
- package/src/config/bundled-skills/settings/TOOLS.json +11 -5
- package/src/config/bundled-skills/settings/tools/open-system-settings.test.ts +85 -0
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +33 -8
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +54 -3
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +1 -1
- package/src/config/default-profile-catalog.ts +195 -10
- package/src/config/default-profile-names.ts +41 -0
- package/src/config/feature-flag-registry.json +18 -2
- package/src/config/inference-profile-validation.ts +11 -2
- package/src/config/llm-resolver.ts +25 -0
- package/src/config/loader.ts +3 -0
- package/src/config/managed-speech-defaults.ts +160 -10
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/live-voice.test.ts +7 -6
- package/src/config/schemas/__tests__/stt.test.ts +94 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/channels.ts +32 -0
- package/src/config/schemas/live-voice.ts +13 -4
- package/src/config/schemas/llm.ts +259 -11
- package/src/config/schemas/mcp.ts +10 -2
- package/src/config/schemas/memory-retrospective.ts +1 -19
- package/src/config/schemas/services.ts +24 -0
- package/src/config/schemas/stt.ts +128 -5
- package/src/config/seed-inference-profiles.ts +32 -3
- package/src/config/skill-state.ts +5 -0
- package/src/config/skills.ts +21 -0
- package/src/contacts/notify-contacts-changed.ts +5 -2
- package/src/context/compactor.ts +74 -1
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +138 -1
- package/src/daemon/config-watcher.ts +2 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +27 -0
- package/src/daemon/conversation-agent-loop.ts +86 -10
- package/src/daemon/conversation-error.ts +25 -0
- package/src/daemon/conversation-evictor.ts +2 -4
- package/src/daemon/conversation-runtime-assembly.ts +19 -7
- package/src/daemon/conversation-skill-tools.ts +5 -1
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-store.ts +8 -1
- package/src/daemon/conversation-tool-setup.ts +37 -0
- package/src/daemon/conversation.ts +0 -4
- package/src/daemon/handlers/channel-config-result.ts +30 -0
- package/src/daemon/handlers/config-discord-channel.ts +301 -0
- package/src/daemon/handlers/config-ingress.ts +9 -0
- package/src/daemon/handlers/config-slack-channel.ts +13 -15
- package/src/daemon/handlers/config-telegram.ts +2 -5
- package/src/daemon/handlers/conversations.ts +0 -2
- package/src/daemon/handlers/skills.ts +25 -7
- package/src/daemon/lifecycle.ts +3 -1
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/tool-setup-types.ts +5 -3
- package/src/daemon/trust-context-types.test.ts +33 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +58 -4
- package/src/live-voice/__tests__/live-voice-managed-speech-fallback.test.ts +8 -3
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +73 -11
- package/src/live-voice/__tests__/live-voice-session-preflight.test.ts +148 -2
- package/src/live-voice/__tests__/live-voice-stt.test.ts +3 -0
- package/src/live-voice/__tests__/live-voice-text-turn.test.ts +391 -0
- package/src/live-voice/__tests__/protocol.test.ts +224 -0
- package/src/live-voice/live-voice-credential-preflight.ts +5 -1
- package/src/live-voice/live-voice-session-manager.ts +39 -2
- package/src/live-voice/live-voice-session.ts +213 -8
- package/src/live-voice/protocol.ts +187 -1
- package/src/messaging/providers/discord/api.ts +2 -1
- package/src/messaging/providers/discord/withdraw.ts +62 -0
- package/src/monitoring/__tests__/file-descriptors.test.ts +32 -0
- package/src/monitoring/__tests__/process-memory.test.ts +34 -1
- package/src/monitoring/db-integrity-sample.ts +1 -0
- package/src/monitoring/file-descriptors.ts +39 -6
- package/src/monitoring/process-memory.ts +37 -5
- package/src/monitoring/resource-sampler.ts +25 -8
- package/src/notifications/__tests__/activity-failed-dedupe.test.ts +161 -0
- package/src/notifications/__tests__/background-failure-signal.test.ts +107 -0
- package/src/notifications/__tests__/connected-channels.test.ts +12 -0
- package/src/notifications/__tests__/copy-composer.test.ts +83 -0
- package/src/notifications/__tests__/destination-resolver.test.ts +38 -0
- package/src/notifications/activity-failed-dedupe.ts +95 -0
- package/src/notifications/adapters/discord.ts +132 -0
- package/src/notifications/adapters/shared.ts +16 -0
- package/src/notifications/adapters/slack.ts +6 -1
- package/src/notifications/adapters/telegram.ts +5 -8
- package/src/notifications/approval-card-data.ts +94 -1
- package/src/notifications/background-failure-signal.ts +116 -0
- package/src/notifications/broadcaster.ts +3 -9
- package/src/notifications/copy-composer.ts +41 -8
- package/src/notifications/decision-engine.ts +55 -0
- package/src/notifications/destination-resolver.ts +45 -5
- package/src/notifications/emit-signal.ts +19 -5
- package/src/notifications/guardian-question-mode.ts +57 -0
- package/src/notifications/types.ts +2 -8
- package/src/oauth/__tests__/seed-providers-managed.test.ts +18 -0
- package/src/oauth/seed-providers.ts +261 -0
- package/src/permissions/checker.test.ts +21 -0
- package/src/permissions/checker.ts +6 -0
- package/src/permissions/gateway-threshold-reader.test.ts +184 -0
- package/src/permissions/gateway-threshold-reader.ts +144 -10
- package/src/permissions/prompter.ts +0 -6
- package/src/permissions/types.ts +5 -4
- package/src/persistence/__tests__/bulk-write-gate.test.ts +0 -38
- package/src/persistence/bulk-write-gate.ts +9 -10
- package/src/persistence/conversation-crud.ts +85 -133
- package/src/persistence/conversation-title-service.ts +43 -29
- package/src/persistence/embeddings/embedding-local.ts +14 -85
- package/src/persistence/embeddings/embedding-runtime-manager.ts +1 -0
- package/src/persistence/lifecycle-events-store.test.ts +50 -0
- package/src/persistence/lifecycle-events-store.ts +75 -5
- package/src/platform/platform-patch-queue.test.ts +368 -0
- package/src/platform/platform-patch-queue.ts +190 -0
- package/src/platform/sync-avatar.test.ts +527 -0
- package/src/platform/sync-avatar.ts +209 -0
- package/src/platform/sync-identity.test.ts +75 -0
- package/src/platform/sync-identity.ts +17 -110
- package/src/plugin-api/model-profiles.test.ts +2 -2
- package/src/plugin-api/model-profiles.ts +7 -7
- package/src/plugin-api/transcription-session.ts +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +4 -0
- package/src/plugins/defaults/image-recovery/recover.ts +2 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +133 -34
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +21 -31
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +13 -9
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +31 -4
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +0 -11
- package/src/plugins/defaults/memory/memory-retrospective-fork-boundary.ts +27 -13
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +183 -113
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +13 -13
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +2 -2
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +1 -1
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +4 -2
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +9 -42
- package/src/plugins/defaults/memory/v2/rerank-local.ts +1 -0
- package/src/plugins/defaults/memory/worker.ts +7 -0
- package/src/providers/__tests__/retry-callsite.test.ts +55 -0
- package/src/providers/connection-resolution.ts +2 -17
- package/src/providers/fallback-breaker.ts +389 -0
- package/src/providers/inference/__tests__/adapter-factory-opencode.test.ts +92 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +16 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +12 -0
- package/src/providers/inference/adapter-factory.ts +231 -3
- package/src/providers/inference/auth.ts +1 -0
- package/src/providers/inference/chatgpt-subscription-auth.ts +70 -0
- package/src/providers/inference/chatgpt-subscription-credentials.ts +84 -0
- package/src/providers/inference/resolve-auth.ts +1 -1
- package/src/providers/media-resolve.ts +2 -3
- package/src/providers/model-catalog.ts +75 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +22 -0
- package/src/providers/openai/__tests__/chat-template-flatten.test.ts +198 -0
- package/src/providers/openai/api-error-normalization.ts +13 -0
- package/src/providers/openai/chat-completions-provider.ts +84 -0
- package/src/providers/opencode/client.test.ts +150 -0
- package/src/providers/opencode/client.ts +61 -0
- package/src/providers/provider-resolvability.ts +20 -0
- package/src/providers/retry.ts +956 -68
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +42 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +136 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +120 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +316 -12
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +83 -3
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +132 -4
- package/src/providers/speech-to-text/provider-catalog.ts +191 -3
- package/src/providers/speech-to-text/resolve.ts +132 -24
- package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +296 -0
- package/src/providers/types.ts +39 -0
- package/src/providers/usage-tracking.ts +12 -1
- package/src/runtime/AGENTS.md +1 -2
- package/src/runtime/__tests__/agent-wake.test.ts +105 -3
- package/src/runtime/__tests__/background-job-runner.test.ts +89 -3
- package/src/runtime/agent-wake.ts +44 -5
- package/src/runtime/background-job-runner.ts +68 -50
- package/src/runtime/channel-readiness-service.ts +0 -39
- package/src/runtime/http-server.ts +12 -4
- package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +18 -18
- package/src/runtime/routes/__tests__/backup-routes.test.ts +9 -5
- package/src/runtime/routes/__tests__/chatgpt-subscription-auth-routes.test.ts +372 -0
- package/src/runtime/routes/__tests__/contact-routes.test.ts +7 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +96 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +37 -0
- package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +2 -1
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +87 -0
- package/src/runtime/routes/acp-claude-auth-routes.ts +25 -54
- package/src/runtime/routes/approval-routes.ts +0 -12
- package/src/runtime/routes/avatar-routes.ts +17 -30
- package/src/runtime/routes/backup-routes.ts +2 -2
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +296 -113
- package/src/runtime/routes/contact-prompt-routes.ts +65 -6
- package/src/runtime/routes/contact-routes.ts +20 -15
- package/src/runtime/routes/conversation-query-routes.ts +98 -10
- package/src/runtime/routes/conversation-routes.ts +2 -2
- package/src/runtime/routes/credential-routes.ts +2 -1
- package/src/runtime/routes/guardian-approval-interception.ts +12 -9
- package/src/runtime/routes/inbound-message-handler.ts +21 -9
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +18 -13
- package/src/runtime/routes/inbound-stages/admission-policy.ts +2 -2
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +9 -8
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +2 -2
- package/src/runtime/routes/inference-profiles-routes.ts +12 -7
- package/src/runtime/routes/inference-provider-connection-routes.ts +2 -2
- package/src/runtime/routes/inference-send-routes.ts +2 -2
- package/src/runtime/routes/ingress-status-routes.ts +2 -2
- package/src/runtime/routes/integrations/discord.ts +119 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +2 -2
- package/src/runtime/routes/mcp-auth-routes.ts +12 -6
- package/src/runtime/routes/oauth-pending-flows.ts +136 -0
- package/src/runtime/routes/secret-routes.ts +7 -0
- package/src/runtime/routes/stt-routes.ts +38 -6
- package/src/runtime/routes/wake-conversation-routes.ts +8 -1
- package/src/runtime/routes/watch-routes.ts +9 -5
- package/src/runtime/sync/resource-sync-events.ts +2 -0
- package/src/runtime/trust-verdict-consumer.ts +3 -3
- package/src/schedule/__tests__/worker-feature-flags.test.ts +58 -0
- package/src/schedule/retry-policy.ts +8 -8
- package/src/schedule/run-script.ts +14 -23
- package/src/schedule/schedule-recovery.ts +0 -1
- package/src/schedule/scheduler.ts +89 -51
- package/src/schedule/worker.ts +57 -0
- package/src/security/__tests__/openai-device-auth.test.ts +355 -0
- package/src/security/oauth2.ts +7 -0
- package/src/security/openai-device-auth.ts +364 -0
- package/src/skills/available-skills.ts +10 -1
- package/src/skills/catalog-install.ts +19 -1
- package/src/skills/inline-command-runner.ts +6 -2
- package/src/skills/platform-compatibility.test.ts +49 -0
- package/src/skills/platform-compatibility.ts +65 -0
- package/src/skills/tool-manifest.ts +20 -0
- package/src/stt/__tests__/roles.test.ts +152 -0
- package/src/stt/daemon-batch-transcriber.ts +4 -1
- package/src/stt/roles.ts +180 -0
- package/src/stt/stt-stream-session.ts +3 -2
- package/src/stt/types.ts +33 -3
- package/src/subagent/manager.ts +12 -0
- package/src/telemetry/__tests__/live-voice-funnel.test.ts +34 -0
- package/src/telemetry/live-voice-funnel.ts +12 -2
- package/src/telemetry/telemetry-wire.generated.ts +6 -0
- package/src/telemetry/tool-audit.ts +83 -30
- package/src/telemetry/tool-executed-events-store.test.ts +0 -1
- package/src/telemetry/turn-outcome.ts +75 -4
- package/src/tools/client-os.ts +115 -0
- package/src/tools/computer-use/definitions.ts +11 -7
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -2
- package/src/tools/credentials/broker-types.ts +0 -71
- package/src/tools/credentials/broker.ts +6 -201
- package/src/tools/executor.ts +2 -8
- package/src/tools/host-shell.test.ts +75 -0
- package/src/tools/host-shell.ts +24 -0
- package/src/tools/host-terminal/host-shell.ts +15 -44
- package/src/tools/mcp/mcp-tool-factory.ts +44 -9
- package/src/tools/permission-checker.ts +73 -21
- package/src/tools/policy-context.ts +1 -0
- package/src/tools/shared/filesystem/image-read.ts +1 -1
- package/src/tools/skills/find-similar-skills.ts +10 -2
- package/src/tools/skills/load.ts +41 -6
- package/src/tools/skills/sandbox-runner.ts +8 -16
- package/src/tools/skills/skill-tool-factory.ts +1 -0
- package/src/tools/terminal/__tests__/safe-env.test.ts +64 -1
- package/src/tools/terminal/safe-env.ts +39 -9
- package/src/tools/terminal/shell.ts +12 -31
- package/src/tools/tool-approval-handler.ts +64 -10
- package/src/tools/tool-types.ts +10 -0
- package/src/tools/types.ts +19 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +4 -3
- package/src/tools/workflows/manage-workflows.ts +2 -2
- package/src/tools/workflows/run-workflow.test.ts +2 -1
- package/src/util/__tests__/file-use.test.ts +61 -0
- package/src/util/__tests__/process-table.test.ts +66 -0
- package/src/util/__tests__/process-tree.test.ts +46 -0
- package/src/util/errors.ts +1 -0
- package/src/util/file-use.ts +91 -0
- package/src/util/host-process.test.ts +17 -0
- package/src/util/host-process.ts +75 -0
- package/src/util/image-conversion.ts +39 -7
- package/src/util/platform.ts +32 -13
- package/src/util/process-table.ts +249 -0
- package/src/util/process-tree.ts +85 -129
- package/src/util/provider-error-patterns.ts +20 -0
- package/src/util/spawn.ts +9 -2
- package/src/workspace/git-service.ts +4 -14
- package/src/workspace/migrations/147-rename-colliding-backup-profile-names.ts +360 -0
- package/src/workspace/migrations/148-strip-unsupported-fallback-profiles.ts +117 -0
- package/src/workspace/migrations/149-repoint-backup-profile-selections.ts +204 -0
- package/src/workspace/migrations/150-stt-flux-provider-to-model-family.ts +139 -0
- package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +160 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/permission-types.test.ts +0 -17
- package/src/channels/gateway-discord-admission.ts +0 -32
- package/src/persistence/fork-message-copy.ts +0 -239
- package/src/plugins/defaults/memory/__tests__/fork-message-copy.test.ts +0 -221
- package/src/security/__tests__/oauth2-device-code.test.ts +0 -431
- package/src/security/oauth2-device-code.ts +0 -312
|
@@ -184,6 +184,22 @@ describe("base_url provider-type gate (create)", () => {
|
|
|
184
184
|
);
|
|
185
185
|
});
|
|
186
186
|
|
|
187
|
+
test("accepts base_url on opencode provider", async () => {
|
|
188
|
+
mockResolvedAddresses = ["93.184.216.34"];
|
|
189
|
+
const result = await handleCreate({
|
|
190
|
+
body: {
|
|
191
|
+
name: "valid-opencode-go",
|
|
192
|
+
provider: "opencode",
|
|
193
|
+
auth: { type: "api_key", credential: "cred-opencode" },
|
|
194
|
+
base_url: "https://opencode.ai/zen/go/v1",
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
expect(result).toBeDefined();
|
|
198
|
+
expect((result as { baseUrl: string }).baseUrl).toBe(
|
|
199
|
+
"https://opencode.ai/zen/go/v1",
|
|
200
|
+
);
|
|
201
|
+
});
|
|
202
|
+
|
|
187
203
|
test("accepts base_url on ollama provider", async () => {
|
|
188
204
|
mockResolvedAddresses = ["93.184.216.34"];
|
|
189
205
|
const result = await handleCreate({
|
|
@@ -174,6 +174,18 @@ describe("resolveAuth defense-in-depth", () => {
|
|
|
174
174
|
}
|
|
175
175
|
});
|
|
176
176
|
|
|
177
|
+
test("preserves baseUrl for opencode provider", async () => {
|
|
178
|
+
const result = await resolveAuth(
|
|
179
|
+
{ type: "api_key", credential: "cred-test" },
|
|
180
|
+
"opencode",
|
|
181
|
+
{ baseUrl: "https://opencode.ai/zen/go/v1" },
|
|
182
|
+
);
|
|
183
|
+
expect(result.ok).toBe(true);
|
|
184
|
+
if (result.ok && result.resolved.kind === "header") {
|
|
185
|
+
expect(result.resolved.baseUrl).toBe("https://opencode.ai/zen/go/v1");
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
177
189
|
test("preserves baseUrl for ollama none-auth", async () => {
|
|
178
190
|
const result = await resolveAuth({ type: "none" }, "ollama", {
|
|
179
191
|
baseUrl: "http://192.168.1.50:11434/v1",
|
|
@@ -18,7 +18,18 @@
|
|
|
18
18
|
* 3. Register the client in `ADAPTER_FACTORIES` below.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
import { resolveDefaultProfileForProvider } from "../../config/default-profile-catalog.js";
|
|
22
|
+
import {
|
|
23
|
+
FALLBACK_PROFILE_BY_KEY,
|
|
24
|
+
isDefaultProfileKey,
|
|
25
|
+
} from "../../config/default-profile-names.js";
|
|
26
|
+
import {
|
|
27
|
+
resolveCallSiteConfigWithProfile,
|
|
28
|
+
selectWinningProfile,
|
|
29
|
+
} from "../../config/llm-resolver.js";
|
|
30
|
+
import { getConfig } from "../../config/loader.js";
|
|
21
31
|
import { normalizeCredentialRef } from "../../security/credential-key.js";
|
|
32
|
+
import { getLogger } from "../../util/logger.js";
|
|
22
33
|
import { AnthropicProvider } from "../anthropic/client.js";
|
|
23
34
|
import { AtlasCloudProvider } from "../atlascloud/client.js";
|
|
24
35
|
import { BasetenProvider } from "../baseten/client.js";
|
|
@@ -29,13 +40,20 @@ import { PROVIDER_CATALOG } from "../model-catalog.js";
|
|
|
29
40
|
import { OllamaProvider } from "../ollama/client.js";
|
|
30
41
|
import { OpenAIChatCompletionsProvider } from "../openai/chat-completions-provider.js";
|
|
31
42
|
import { OpenAIResponsesProvider } from "../openai/responses-provider.js";
|
|
43
|
+
import { OpenCodeProvider } from "../opencode/client.js";
|
|
32
44
|
import { OpenRouterProvider } from "../openrouter/client.js";
|
|
33
45
|
import { PoolsideProvider } from "../poolside/client.js";
|
|
46
|
+
import { dispatchProviderResolvable } from "../provider-resolvability.js";
|
|
34
47
|
import { RetryProvider } from "../retry.js";
|
|
35
48
|
import { TogetherProvider } from "../together/client.js";
|
|
36
|
-
import type { Provider } from "../types.js";
|
|
49
|
+
import type { Provider, SendMessageOptions } from "../types.js";
|
|
37
50
|
import { UsageTrackingProvider } from "../usage-tracking.js";
|
|
38
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
getManagedUpstream,
|
|
53
|
+
isVellumManagedConnection,
|
|
54
|
+
MANAGED_ROUTABLE_PROVIDERS,
|
|
55
|
+
VELLUM_MANAGED_PROVIDER,
|
|
56
|
+
} from "../vellum-model-routing.js";
|
|
39
57
|
import { VercelAIGatewayProvider } from "../vercel-ai-gateway/client.js";
|
|
40
58
|
import type { ResolvedAuth } from "./auth.js";
|
|
41
59
|
import type { ProviderConnection } from "./auth.js";
|
|
@@ -58,6 +76,15 @@ export interface AdapterCreateOpts {
|
|
|
58
76
|
|
|
59
77
|
type AdapterFactory = (opts: AdapterCreateOpts) => Provider;
|
|
60
78
|
|
|
79
|
+
const log = getLogger("adapter-factory");
|
|
80
|
+
|
|
81
|
+
/** The backup route `RetryProvider` escalates to; see {@link makeFallbackRouteResolver}. */
|
|
82
|
+
interface FallbackRoute {
|
|
83
|
+
provider: Provider;
|
|
84
|
+
overrideProfile: string;
|
|
85
|
+
forwardUsageAttributionHeaders: boolean;
|
|
86
|
+
}
|
|
87
|
+
|
|
61
88
|
/**
|
|
62
89
|
* Catalog-keyed factory table. Each entry takes a unified
|
|
63
90
|
* `AdapterCreateOpts` and constructs the underlying provider client. The
|
|
@@ -138,6 +165,11 @@ const ADAPTER_FACTORIES: Record<string, AdapterFactory> = {
|
|
|
138
165
|
assistantReasoningField: "reasoning_content",
|
|
139
166
|
...(baseURL ? { baseURL } : {}),
|
|
140
167
|
}),
|
|
168
|
+
opencode: ({ apiKey, model, streamTimeoutMs, baseURL }) =>
|
|
169
|
+
new OpenCodeProvider(apiKey, model, {
|
|
170
|
+
streamTimeoutMs,
|
|
171
|
+
...(baseURL ? { baseURL } : {}),
|
|
172
|
+
}),
|
|
141
173
|
// Keyless openai-compatible endpoints (e.g. LM Studio) ignore the key; the
|
|
142
174
|
// placeholder satisfies the OpenAI SDK, which requires a non-empty key.
|
|
143
175
|
"openai-compatible": ({ apiKey, model, streamTimeoutMs, baseURL }) =>
|
|
@@ -275,6 +307,195 @@ export function createAdapterFromConnection(
|
|
|
275
307
|
};
|
|
276
308
|
}
|
|
277
309
|
|
|
310
|
+
/**
|
|
311
|
+
* Resolve the backup-profile route for a request whose primary route just
|
|
312
|
+
* failed with an outage-shaped error, so `RetryProvider` can serve it on a
|
|
313
|
+
* different provider instead of surfacing the outage.
|
|
314
|
+
*
|
|
315
|
+
* The winning profile of the FAILED call is re-resolved from the same
|
|
316
|
+
* inputs `normalizeSendMessageOptions` uses (call site plus the request's
|
|
317
|
+
* own override/seed fields), and its `fallbackProfile` pointer names the
|
|
318
|
+
* backup. The upstream comes from the backup's resolved provider; only the
|
|
319
|
+
* `vellum` routing identity carries no upstream of its own, and there the
|
|
320
|
+
* backup's model determines it.
|
|
321
|
+
*
|
|
322
|
+
* Returns a RAW adapter, exactly like `makeCredentialRefresher` above:
|
|
323
|
+
* `RetryProvider` hands the backup route options it has ALREADY normalized
|
|
324
|
+
* (call site consumed, wire params resolved from the backup profile, usage
|
|
325
|
+
* attribution headers stamped). A second `RetryProvider` in between would
|
|
326
|
+
* re-normalize that call-site-less config and strip those headers, leaving
|
|
327
|
+
* degraded traffic unattributed on the platform's billing events.
|
|
328
|
+
*
|
|
329
|
+
* Never throws: a broken backup lookup must surface the original provider
|
|
330
|
+
* error, not a new one.
|
|
331
|
+
*/
|
|
332
|
+
function makeFallbackRouteResolver(): (
|
|
333
|
+
failedOptions: SendMessageOptions | undefined,
|
|
334
|
+
) => Promise<FallbackRoute | null> {
|
|
335
|
+
return async (failedOptions): Promise<FallbackRoute | null> => {
|
|
336
|
+
const failedConfig = failedOptions?.config;
|
|
337
|
+
const callSite = failedConfig?.callSite;
|
|
338
|
+
if (callSite === undefined) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
try {
|
|
342
|
+
const { llm } = getConfig();
|
|
343
|
+
const winner = selectWinningProfile(callSite, llm, {
|
|
344
|
+
overrideProfile: failedConfig?.overrideProfile,
|
|
345
|
+
forceOverrideProfile: failedConfig?.forceOverrideProfile,
|
|
346
|
+
selectionSeed: failedConfig?.selectionSeed,
|
|
347
|
+
isResolvableProvider: dispatchProviderResolvable,
|
|
348
|
+
});
|
|
349
|
+
const primaryProfile =
|
|
350
|
+
winner.profileName ??
|
|
351
|
+
(winner.source === "default" ? "balanced" : undefined);
|
|
352
|
+
if (
|
|
353
|
+
primaryProfile === undefined ||
|
|
354
|
+
!isDefaultProfileKey(primaryProfile)
|
|
355
|
+
) {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
const expectedFallback = FALLBACK_PROFILE_BY_KEY[primaryProfile];
|
|
359
|
+
if (
|
|
360
|
+
winner.entry?.source !== "managed" ||
|
|
361
|
+
winner.entry.fallbackProfile !== expectedFallback
|
|
362
|
+
) {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
const overrideProfile = expectedFallback;
|
|
366
|
+
// Resolved the provider-aware way the runtime resolver resolves every
|
|
367
|
+
// profile name: the backups are companions of the managed column and
|
|
368
|
+
// must not materialize under a BYOK or ChatGPT default provider.
|
|
369
|
+
const backup = resolveDefaultProfileForProvider(
|
|
370
|
+
llm.profiles,
|
|
371
|
+
overrideProfile,
|
|
372
|
+
llm.defaultProvider ?? null,
|
|
373
|
+
);
|
|
374
|
+
if (backup?.model == null) {
|
|
375
|
+
log.warn(
|
|
376
|
+
{ connectionName: connection.name, overrideProfile },
|
|
377
|
+
"Backup profile does not resolve on this default provider; keeping the original error",
|
|
378
|
+
);
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
const { config: resolvedBackup, profileName: resolvedBackupProfile } =
|
|
382
|
+
resolveCallSiteConfigWithProfile(callSite, llm, {
|
|
383
|
+
overrideProfile,
|
|
384
|
+
forceOverrideProfile: true,
|
|
385
|
+
selectionSeed: failedConfig?.selectionSeed,
|
|
386
|
+
isResolvableProvider: dispatchProviderResolvable,
|
|
387
|
+
});
|
|
388
|
+
if (resolvedBackupProfile !== overrideProfile) {
|
|
389
|
+
log.warn(
|
|
390
|
+
{
|
|
391
|
+
connectionName: connection.name,
|
|
392
|
+
overrideProfile,
|
|
393
|
+
selectedProfile: resolvedBackupProfile,
|
|
394
|
+
},
|
|
395
|
+
"Backup profile is not dispatch-resolvable; keeping the original error",
|
|
396
|
+
);
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
// The model the adapter serves must be the model the fallback send
|
|
400
|
+
// carries, so it comes from the same forced-profile resolution.
|
|
401
|
+
// This callback authenticates the backup with the managed connection
|
|
402
|
+
// it was built for, so it can only serve a backup that routes through
|
|
403
|
+
// the managed column. Refusing any other route keeps managed billing
|
|
404
|
+
// and credentials from being applied to user-owned traffic.
|
|
405
|
+
// Keyed on the connection, not the provider: a call-site fragment that
|
|
406
|
+
// pins a concrete upstream over a managed winner keeps the managed
|
|
407
|
+
// routing and is stamped with this connection's name by the resolver
|
|
408
|
+
// (see `llm-resolver.ts`), so a concrete `provider` here is still
|
|
409
|
+
// managed traffic. Only a profile that names a different connection,
|
|
410
|
+
// or a concrete provider carrying no managed connection at all (a BYOK
|
|
411
|
+
// winner), routes somewhere this callback cannot authenticate.
|
|
412
|
+
const backupConnection = resolvedBackup.provider_connection;
|
|
413
|
+
const routesThroughThisConnection =
|
|
414
|
+
backupConnection === undefined
|
|
415
|
+
? resolvedBackup.provider === VELLUM_MANAGED_PROVIDER
|
|
416
|
+
: backupConnection === connection.name;
|
|
417
|
+
if (!routesThroughThisConnection) {
|
|
418
|
+
log.warn(
|
|
419
|
+
{
|
|
420
|
+
connectionName: connection.name,
|
|
421
|
+
overrideProfile,
|
|
422
|
+
backupProvider: resolvedBackup.provider,
|
|
423
|
+
backupConnection,
|
|
424
|
+
},
|
|
425
|
+
"Backup profile routes outside the managed connection; keeping the original error",
|
|
426
|
+
);
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
// The upstream is the resolved provider, the same way normal dispatch
|
|
430
|
+
// picks it for this connection: `connection-resolution.ts` threads the
|
|
431
|
+
// resolved provider through as `providerOverride` for a `vellum`
|
|
432
|
+
// connection and derives the upstream from the model only when the
|
|
433
|
+
// provider is the routing-identity sentinel (`resolveRoutingIdentity`,
|
|
434
|
+
// and the `isVellum && !expectedProvider` branch). Deriving it from the
|
|
435
|
+
// model here regardless would send a provider-pinned call-site fragment
|
|
436
|
+
// to a different upstream than a primary send of the same resolution,
|
|
437
|
+
// and would refuse a managed model the catalog does not list yet even
|
|
438
|
+
// though normal dispatch serves it. A provider the managed proxy cannot
|
|
439
|
+
// front (a non-routable id, or another routing identity such as
|
|
440
|
+
// `chatgpt`) is refused rather than routed.
|
|
441
|
+
const declaredProvider = resolvedBackup.provider;
|
|
442
|
+
const upstream =
|
|
443
|
+
declaredProvider === VELLUM_MANAGED_PROVIDER
|
|
444
|
+
? getManagedUpstream(resolvedBackup.model)
|
|
445
|
+
: MANAGED_ROUTABLE_PROVIDERS.has(declaredProvider)
|
|
446
|
+
? declaredProvider
|
|
447
|
+
: null;
|
|
448
|
+
if (upstream === null) {
|
|
449
|
+
log.warn(
|
|
450
|
+
{
|
|
451
|
+
connectionName: connection.name,
|
|
452
|
+
overrideProfile,
|
|
453
|
+
backupProvider: declaredProvider,
|
|
454
|
+
backupModel: resolvedBackup.model,
|
|
455
|
+
},
|
|
456
|
+
"Backup profile does not resolve to a managed upstream; keeping the original error",
|
|
457
|
+
);
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
// Re-resolve auth for the BACKUP upstream: the managed proxy's base URL
|
|
461
|
+
// is per-upstream, so the primary's resolved auth cannot be reused.
|
|
462
|
+
const backupAuth = await resolveAuth(
|
|
463
|
+
effectiveConnectionAuth(connection),
|
|
464
|
+
upstream,
|
|
465
|
+
{ baseUrl: connection.baseUrl },
|
|
466
|
+
);
|
|
467
|
+
if (!backupAuth.ok) {
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
const backupAdapter = buildConnectionAdapter(
|
|
471
|
+
connection,
|
|
472
|
+
backupAuth.resolved,
|
|
473
|
+
// `useNativeWebSearch` carries over from the primary: it is a
|
|
474
|
+
// property of this request's web-search config, and every managed
|
|
475
|
+
// backup pin either supports native search or ignores the flag.
|
|
476
|
+
{ ...opts, model: resolvedBackup.model, provider: upstream },
|
|
477
|
+
);
|
|
478
|
+
if (backupAdapter === null) {
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
return {
|
|
482
|
+
provider: backupAdapter,
|
|
483
|
+
overrideProfile,
|
|
484
|
+
// Always true: this callback is wired only on the managed proxy, so
|
|
485
|
+
// the backup route runs through it too and its `X-Vellum-*` billing
|
|
486
|
+
// metadata reaches our own backend, never a third party.
|
|
487
|
+
forwardUsageAttributionHeaders: true,
|
|
488
|
+
};
|
|
489
|
+
} catch (err) {
|
|
490
|
+
log.warn(
|
|
491
|
+
{ err, connectionName: connection.name, callSite },
|
|
492
|
+
"Failed to resolve a backup profile route; keeping the original error",
|
|
493
|
+
);
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
278
499
|
// Usage-attribution headers (`X-Vellum-*`) are only meaningful when the
|
|
279
500
|
// request is routed through the Vellum-managed proxy. They carry billing
|
|
280
501
|
// metadata for our own backend. Forwarding them to a third-party endpoint
|
|
@@ -295,8 +516,15 @@ export function createAdapterFromConnection(
|
|
|
295
516
|
? "no-auth"
|
|
296
517
|
: undefined,
|
|
297
518
|
connectionName: connection.name,
|
|
519
|
+
// Both escalations are managed-route only. A BYOK, oauth-subscription,
|
|
520
|
+
// or no-auth connection must never fall back: the install may hold no
|
|
521
|
+
// credential for the backup profile's provider, and only the managed
|
|
522
|
+
// column carries `fallbackProfile` pointers in the first place.
|
|
298
523
|
...(isManagedProxy
|
|
299
|
-
? {
|
|
524
|
+
? {
|
|
525
|
+
refreshCredentialProvider: makeCredentialRefresher(),
|
|
526
|
+
resolveFallbackRoute: makeFallbackRouteResolver(),
|
|
527
|
+
}
|
|
300
528
|
: {}),
|
|
301
529
|
}),
|
|
302
530
|
);
|
|
@@ -190,6 +190,7 @@ export const PROVIDERS_REQUIRING_BASE_URL_AND_MODELS: ReadonlySet<string> =
|
|
|
190
190
|
export const PROVIDERS_ALLOWING_CUSTOM_BASE_URL: ReadonlySet<string> = new Set([
|
|
191
191
|
...PROVIDERS_REQUIRING_BASE_URL_AND_MODELS,
|
|
192
192
|
"ollama",
|
|
193
|
+
"opencode",
|
|
193
194
|
]);
|
|
194
195
|
|
|
195
196
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-daemon completion of the "ChatGPT Subscription" sign-in: store the tokens
|
|
3
|
+
* and point the provider connection at them in one step.
|
|
4
|
+
*
|
|
5
|
+
* The OAuth client config and the credential writes live in
|
|
6
|
+
* `chatgpt-subscription-credentials.ts`, which depends on nothing but the
|
|
7
|
+
* credential store so the CLI can import it without pulling in the database.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { getDb } from "../../persistence/db-connection.js";
|
|
11
|
+
import type { OAuth2TokenResult } from "../../security/oauth2.js";
|
|
12
|
+
import { getLogger } from "../../util/logger.js";
|
|
13
|
+
import {
|
|
14
|
+
CHATGPT_SUBSCRIPTION_AUTH_INPUT,
|
|
15
|
+
CHATGPT_SUBSCRIPTION_CONNECTION_NAME,
|
|
16
|
+
storeChatgptSubscriptionCredentials,
|
|
17
|
+
} from "./chatgpt-subscription-credentials.js";
|
|
18
|
+
import {
|
|
19
|
+
createConnection,
|
|
20
|
+
getConnection,
|
|
21
|
+
updateConnection,
|
|
22
|
+
} from "./connections.js";
|
|
23
|
+
|
|
24
|
+
const log = getLogger("chatgpt-subscription-auth");
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Store the subscription's tokens and point the `chatgpt-subscription` provider
|
|
28
|
+
* connection at them, creating the connection when it does not exist yet.
|
|
29
|
+
*/
|
|
30
|
+
export async function storeChatgptSubscriptionTokens(
|
|
31
|
+
tokens: OAuth2TokenResult,
|
|
32
|
+
): Promise<void> {
|
|
33
|
+
await storeChatgptSubscriptionCredentials(tokens);
|
|
34
|
+
|
|
35
|
+
const db = getDb();
|
|
36
|
+
const existing = getConnection(db, CHATGPT_SUBSCRIPTION_CONNECTION_NAME);
|
|
37
|
+
|
|
38
|
+
if (existing) {
|
|
39
|
+
// Stamp the provider with the auth: this row IS the subscription route
|
|
40
|
+
// (auth modality = provider identity), and dispatch derives the openai
|
|
41
|
+
// upstream from the identity per-request. Stamping also heals a
|
|
42
|
+
// claiming row whose provider would otherwise misroute the fresh token.
|
|
43
|
+
const updateResult = updateConnection(
|
|
44
|
+
db,
|
|
45
|
+
CHATGPT_SUBSCRIPTION_CONNECTION_NAME,
|
|
46
|
+
{ auth: CHATGPT_SUBSCRIPTION_AUTH_INPUT, provider: "chatgpt" },
|
|
47
|
+
);
|
|
48
|
+
if (!updateResult.ok) {
|
|
49
|
+
log.error(
|
|
50
|
+
{ error: updateResult.error },
|
|
51
|
+
"Failed to update chatgpt-subscription connection",
|
|
52
|
+
);
|
|
53
|
+
throw new Error("Failed to update connection");
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const createResult = createConnection(db, {
|
|
59
|
+
name: CHATGPT_SUBSCRIPTION_CONNECTION_NAME,
|
|
60
|
+
provider: "chatgpt",
|
|
61
|
+
auth: CHATGPT_SUBSCRIPTION_AUTH_INPUT,
|
|
62
|
+
});
|
|
63
|
+
if (!createResult.ok) {
|
|
64
|
+
log.error(
|
|
65
|
+
{ error: createResult.error },
|
|
66
|
+
"Failed to create chatgpt-subscription connection",
|
|
67
|
+
);
|
|
68
|
+
throw new Error("Failed to create connection");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth client config and credential writes for the "ChatGPT Subscription"
|
|
3
|
+
* sign-in.
|
|
4
|
+
*
|
|
5
|
+
* Every entry point into the flow (the daemon's device-code routes, the
|
|
6
|
+
* daemon's copy-paste PKCE routes, and the `assistant inference providers
|
|
7
|
+
* login-chatgpt` CLI command) authenticates against the same OAuth client and
|
|
8
|
+
* lands its tokens under the same credential prefix, so the client config and
|
|
9
|
+
* the credential writes live here rather than in each caller.
|
|
10
|
+
*
|
|
11
|
+
* This module deliberately depends on nothing but the credential store: the
|
|
12
|
+
* CLI is a separate process with no database handle, so it imports this leaf
|
|
13
|
+
* and pairs it with its own IPC connection upsert. In-daemon callers use
|
|
14
|
+
* `storeChatgptSubscriptionTokens` from `chatgpt-subscription-auth.ts`, which
|
|
15
|
+
* writes the credentials and the provider connection together.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { OAuth2Config, OAuth2TokenResult } from "../../security/oauth2.js";
|
|
19
|
+
import { setSecureKeyAsync } from "../../security/secure-keys.js";
|
|
20
|
+
import { getLogger } from "../../util/logger.js";
|
|
21
|
+
import { CHATGPT_SUBSCRIPTION_CONNECTION_NAME } from "./auth.js";
|
|
22
|
+
|
|
23
|
+
const log = getLogger("chatgpt-subscription-credentials");
|
|
24
|
+
|
|
25
|
+
export { CHATGPT_SUBSCRIPTION_CONNECTION_NAME };
|
|
26
|
+
|
|
27
|
+
/** OpenAI's Codex OAuth client. PKCE only, no client secret. */
|
|
28
|
+
export const OPENAI_OAUTH_CONFIG: OAuth2Config = {
|
|
29
|
+
authorizeUrl: "https://auth.openai.com/oauth/authorize",
|
|
30
|
+
tokenExchangeUrl: "https://auth.openai.com/oauth/token",
|
|
31
|
+
clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
|
|
32
|
+
scopes: ["openid", "profile", "email", "offline_access"],
|
|
33
|
+
scopeSeparator: " ",
|
|
34
|
+
authorizeParams: { id_token_add_organizations: "true" },
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** Credential-store prefix holding the subscription's access/refresh tokens. */
|
|
38
|
+
export const CHATGPT_CREDENTIAL_PREFIX = "credential/chatgpt";
|
|
39
|
+
|
|
40
|
+
export const CHATGPT_ACCESS_TOKEN_CREDENTIAL = `${CHATGPT_CREDENTIAL_PREFIX}/access_token`;
|
|
41
|
+
|
|
42
|
+
/** Auth block stamped on the `chatgpt-subscription` provider connection. */
|
|
43
|
+
export const CHATGPT_SUBSCRIPTION_AUTH_INPUT = {
|
|
44
|
+
type: "oauth_subscription" as const,
|
|
45
|
+
credential: CHATGPT_ACCESS_TOKEN_CREDENTIAL,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Write the subscription's tokens to the credential store.
|
|
50
|
+
*
|
|
51
|
+
* Throws when a write fails: a half-stored credential set leaves the provider
|
|
52
|
+
* connection pointing at a token the assistant cannot use.
|
|
53
|
+
*/
|
|
54
|
+
export async function storeChatgptSubscriptionCredentials(
|
|
55
|
+
tokens: OAuth2TokenResult,
|
|
56
|
+
): Promise<void> {
|
|
57
|
+
const accessStored = await setSecureKeyAsync(
|
|
58
|
+
CHATGPT_ACCESS_TOKEN_CREDENTIAL,
|
|
59
|
+
tokens.accessToken,
|
|
60
|
+
);
|
|
61
|
+
if (!accessStored) {
|
|
62
|
+
log.error("Failed to store ChatGPT access token in CES");
|
|
63
|
+
throw new Error("Failed to store access token");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (tokens.refreshToken) {
|
|
67
|
+
const refreshStored = await setSecureKeyAsync(
|
|
68
|
+
`${CHATGPT_CREDENTIAL_PREFIX}/refresh_token`,
|
|
69
|
+
tokens.refreshToken,
|
|
70
|
+
);
|
|
71
|
+
if (!refreshStored) {
|
|
72
|
+
log.error("Failed to store ChatGPT refresh token in CES");
|
|
73
|
+
throw new Error("Failed to store refresh token");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (tokens.expiresIn) {
|
|
78
|
+
const expiresAt = Math.floor(Date.now() / 1000 + tokens.expiresIn);
|
|
79
|
+
await setSecureKeyAsync(
|
|
80
|
+
`${CHATGPT_CREDENTIAL_PREFIX}/expires_at`,
|
|
81
|
+
String(expiresAt),
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -43,7 +43,7 @@ export async function resolveAuth(
|
|
|
43
43
|
if (safeBaseUrl && !PROVIDERS_ALLOWING_CUSTOM_BASE_URL.has(provider)) {
|
|
44
44
|
log.warn(
|
|
45
45
|
{ provider, baseUrl: safeBaseUrl },
|
|
46
|
-
`Stripping baseUrl for provider "${provider}": base_url is only valid for openai-compatible and
|
|
46
|
+
`Stripping baseUrl for provider "${provider}": base_url is only valid for openai-compatible, ollama, and opencode providers.`,
|
|
47
47
|
);
|
|
48
48
|
safeBaseUrl = null;
|
|
49
49
|
}
|
|
@@ -79,9 +79,8 @@ export function isUnsendableImageSource(source: MediaSource): boolean {
|
|
|
79
79
|
* `image/heic` and `image/heif` directly
|
|
80
80
|
* (https://ai.google.dev/gemini-api/docs/image-understanding), while
|
|
81
81
|
* OpenAI-compatible and Anthropic endpoints answer HTTP 400 for the whole
|
|
82
|
-
* request. HEIF bytes reach a provider whenever transcoding
|
|
83
|
-
*
|
|
84
|
-
* on Gemini depends on them passing through.
|
|
82
|
+
* request. HEIF bytes reach a provider whenever transcoding cannot decode the
|
|
83
|
+
* input, so Gemini depends on them passing through.
|
|
85
84
|
*/
|
|
86
85
|
export interface MediaResolveOptions {
|
|
87
86
|
acceptsHeif?: boolean;
|
|
@@ -1374,6 +1374,32 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
1374
1374
|
// usage never includes cached tokens. `supportsCaching` therefore stays
|
|
1375
1375
|
// false; the `cacheReadPer1mTokens` rates below only apply if OpenRouter
|
|
1376
1376
|
// starts reporting cached tokens in usage.
|
|
1377
|
+
{
|
|
1378
|
+
id: "x-ai/grok-4.6",
|
|
1379
|
+
displayName: "Grok 4.6",
|
|
1380
|
+
contextWindowTokens: 500000,
|
|
1381
|
+
// xAI publishes no completion cap; 30K is the tracker-reported
|
|
1382
|
+
// single-response limit used for sibling Grok 4.x catalog rows.
|
|
1383
|
+
maxOutputTokens: 30000,
|
|
1384
|
+
supportsThinking: true,
|
|
1385
|
+
supportsCaching: false,
|
|
1386
|
+
supportsVision: true,
|
|
1387
|
+
supportsToolUse: true,
|
|
1388
|
+
longContextPricingThresholdTokens: 200000,
|
|
1389
|
+
pricing: {
|
|
1390
|
+
inputPer1mTokens: 2,
|
|
1391
|
+
outputPer1mTokens: 6,
|
|
1392
|
+
cacheReadPer1mTokens: 0.5,
|
|
1393
|
+
tiers: [
|
|
1394
|
+
{
|
|
1395
|
+
inputTokenThreshold: 200000,
|
|
1396
|
+
inputPer1mTokens: 4,
|
|
1397
|
+
outputPer1mTokens: 12,
|
|
1398
|
+
cacheReadPer1mTokens: 1,
|
|
1399
|
+
},
|
|
1400
|
+
],
|
|
1401
|
+
},
|
|
1402
|
+
},
|
|
1377
1403
|
{
|
|
1378
1404
|
id: "x-ai/grok-4.5",
|
|
1379
1405
|
displayName: "Grok 4.5",
|
|
@@ -1697,6 +1723,36 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
1697
1723
|
pricing: { inputPer1mTokens: 0.2, outputPer1mTokens: 1.1 },
|
|
1698
1724
|
},
|
|
1699
1725
|
// Z.ai
|
|
1726
|
+
{
|
|
1727
|
+
id: "z-ai/glm-5.3",
|
|
1728
|
+
displayName: "GLM-5.3",
|
|
1729
|
+
contextWindowTokens: 1048576,
|
|
1730
|
+
maxOutputTokens: 131072,
|
|
1731
|
+
supportsThinking: true,
|
|
1732
|
+
supportsCaching: true,
|
|
1733
|
+
supportsVision: false,
|
|
1734
|
+
supportsToolUse: true,
|
|
1735
|
+
pricing: {
|
|
1736
|
+
inputPer1mTokens: 1.4,
|
|
1737
|
+
outputPer1mTokens: 4.4,
|
|
1738
|
+
cacheReadPer1mTokens: 0.26,
|
|
1739
|
+
},
|
|
1740
|
+
},
|
|
1741
|
+
{
|
|
1742
|
+
id: "z-ai/glm-5.3-flash",
|
|
1743
|
+
displayName: "GLM-5.3 Flash",
|
|
1744
|
+
contextWindowTokens: 1310720,
|
|
1745
|
+
maxOutputTokens: 131072,
|
|
1746
|
+
supportsThinking: true,
|
|
1747
|
+
supportsCaching: true,
|
|
1748
|
+
supportsVision: true,
|
|
1749
|
+
supportsToolUse: true,
|
|
1750
|
+
pricing: {
|
|
1751
|
+
inputPer1mTokens: 0.075,
|
|
1752
|
+
outputPer1mTokens: 0.25,
|
|
1753
|
+
cacheReadPer1mTokens: 0.015,
|
|
1754
|
+
},
|
|
1755
|
+
},
|
|
1700
1756
|
{
|
|
1701
1757
|
id: "z-ai/glm-5.2",
|
|
1702
1758
|
displayName: "GLM-5.2",
|
|
@@ -2038,6 +2094,25 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
2038
2094
|
models: [],
|
|
2039
2095
|
defaultModel: "",
|
|
2040
2096
|
},
|
|
2097
|
+
{
|
|
2098
|
+
id: "opencode",
|
|
2099
|
+
displayName: "OpenCode",
|
|
2100
|
+
subtitle:
|
|
2101
|
+
"OpenCode Zen and OpenCode Go. Models come from the endpoint, not a fixed catalog.",
|
|
2102
|
+
setupMode: "api-key",
|
|
2103
|
+
setupHint:
|
|
2104
|
+
"Enter your OpenCode API key. Leave the base URL empty for Zen, or set it to the Go endpoint.",
|
|
2105
|
+
envVar: "OPENCODE_API_KEY",
|
|
2106
|
+
credentialsGuide: {
|
|
2107
|
+
description:
|
|
2108
|
+
"Create an OpenCode API key, then choose Zen or Go as the endpoint.",
|
|
2109
|
+
url: "https://opencode.ai",
|
|
2110
|
+
linkLabel: "OpenCode",
|
|
2111
|
+
},
|
|
2112
|
+
apiKeyPlaceholder: "sk-...",
|
|
2113
|
+
models: [],
|
|
2114
|
+
defaultModel: "",
|
|
2115
|
+
},
|
|
2041
2116
|
{
|
|
2042
2117
|
id: "openai-compatible",
|
|
2043
2118
|
displayName: "OpenAI-compatible",
|
|
@@ -387,6 +387,28 @@ describe("deriveReason", () => {
|
|
|
387
387
|
).toBe("vision_unsupported");
|
|
388
388
|
});
|
|
389
389
|
|
|
390
|
+
test("chat-template failure prose on a 400 → request_shape_unsupported", () => {
|
|
391
|
+
// Together's server-side renderer error for MiniMax M3, verbatim.
|
|
392
|
+
expect(
|
|
393
|
+
deriveReason(
|
|
394
|
+
n({
|
|
395
|
+
message:
|
|
396
|
+
"Failed to apply chat template: invalid operation: object is not callable (in chat:22)",
|
|
397
|
+
}),
|
|
398
|
+
400,
|
|
399
|
+
),
|
|
400
|
+
).toBe("request_shape_unsupported");
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test("chat-template prose on a 5xx stays server-side, not request_shape_unsupported", () => {
|
|
404
|
+
expect(
|
|
405
|
+
deriveReason(
|
|
406
|
+
n({ message: "Failed to apply chat template: renderer crashed" }),
|
|
407
|
+
500,
|
|
408
|
+
),
|
|
409
|
+
).not.toBe("request_shape_unsupported");
|
|
410
|
+
});
|
|
411
|
+
|
|
390
412
|
test("402 → insufficient_credits", () => {
|
|
391
413
|
expect(deriveReason(n(), 402)).toBe("insufficient_credits");
|
|
392
414
|
});
|