@vellumai/assistant 0.11.6 → 0.11.7-staging.2
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/ces-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
- package/node_modules/@vellumai/ces-client/src/http-credentials.ts +161 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- 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/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
- 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/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
- package/openapi.yaml +419 -55
- package/package.json +4 -1
- package/scripts/sync-llm-catalog.ts +6 -0
- 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-record-write-through.test.ts +78 -0
- 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__/delete-propagation.test.ts +92 -2
- package/src/__tests__/discord-callback-round-trip.test.ts +93 -0
- package/src/__tests__/discord-channel-config.test.ts +237 -0
- package/src/__tests__/edit-propagation.test.ts +42 -2
- 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-catalog-parity.test.ts +4 -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__/oauth-connect-orchestrator.test.ts +95 -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-catalog-visibility.test.ts +17 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +8 -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 +57 -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 +19 -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 +34 -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 +263 -15
- 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-session-telemetry.test.ts +64 -0
- 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 +224 -9
- 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/messaging/read-provider-metadata.ts +32 -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/connect-orchestrator.ts +16 -0
- package/src/oauth/seed-providers.ts +262 -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 +88 -134
- 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__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +55 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +21 -3
- 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 +246 -3
- package/src/providers/inference/auth.ts +15 -14
- 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 +100 -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/platform-proxy/constants.ts +5 -0
- package/src/providers/provider-resolvability.ts +20 -0
- package/src/providers/provider-secret-catalog.ts +3 -2
- 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/providers/vellum/client.ts +29 -0
- package/src/providers/vellum-model-routing.test.ts +2 -0
- package/src/providers/vellum-model-routing.ts +3 -3
- 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 +47 -48
- 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/edit-intercept.ts +20 -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/migration-routes.ts +3 -1
- 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/ces-rpc-record-backend.ts +123 -0
- package/src/security/oauth2.ts +7 -0
- package/src/security/openai-device-auth.ts +364 -0
- package/src/security/secure-keys.ts +15 -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/credentials/metadata-store.ts +65 -16
- package/src/tools/credentials/store.ts +2 -0
- 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/watch/__tests__/watch-retro.test.ts +48 -1
- package/src/watch/watch-retro.ts +46 -4
- 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
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Managed Flux STT through the gateway's speech relay
|
|
3
|
+
* (gateway -> velay -> Deepgram Flux; velay contact is gateway-only).
|
|
4
|
+
*
|
|
5
|
+
* Composes {@link DeepgramFluxRealtimeTranscriber} against the relay's
|
|
6
|
+
* managed STT v2 endpoint with `contract=flux`, so Flux's own turn events
|
|
7
|
+
* arrive verbatim. Without that opt-in velay translates them into the
|
|
8
|
+
* released v1 Deepgram dialect, which drops `EagerEndOfTurn` and
|
|
9
|
+
* `TurnResumed` entirely and leaves live voice with no provider turn end to
|
|
10
|
+
* act on.
|
|
11
|
+
*
|
|
12
|
+
* This wrapper owns what is relay-specific, mirroring
|
|
13
|
+
* `VellumManagedRealtimeTranscriber` on the v1 path:
|
|
14
|
+
*
|
|
15
|
+
* - Dialing the gateway (`/v2/speech/stt/stream`, `?key=` carries a
|
|
16
|
+
* daemon-minted service token, no `model` param since velay derives and
|
|
17
|
+
* prices the Flux model from `language`).
|
|
18
|
+
* - Surfacing `velay_error` control frames as categorized SttErrors instead
|
|
19
|
+
* of generic socket failures.
|
|
20
|
+
* - Transparent re-dial on velay's 30-minute session cap, so one live-voice
|
|
21
|
+
* session can outlive it.
|
|
22
|
+
*
|
|
23
|
+
* There is deliberately no `finalizeUtterance`: Flux commits turns itself,
|
|
24
|
+
* and the interface makes the method optional so live voice feature-detects
|
|
25
|
+
* its absence and never asks for a flush.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
type StreamingTranscriber,
|
|
30
|
+
SttError,
|
|
31
|
+
type SttStreamServerEvent,
|
|
32
|
+
} from "../../stt/types.js";
|
|
33
|
+
import { getLogger } from "../../util/logger.js";
|
|
34
|
+
import { DeepgramFluxRealtimeTranscriber } from "./deepgram-flux-realtime.js";
|
|
35
|
+
import {
|
|
36
|
+
mapVelayError,
|
|
37
|
+
probeVelayRejection,
|
|
38
|
+
type SpeechRelayConnection,
|
|
39
|
+
type VelayErrorInfo,
|
|
40
|
+
} from "./vellum-speech-relay-connection.js";
|
|
41
|
+
|
|
42
|
+
const log = getLogger("vellum-managed-flux-realtime");
|
|
43
|
+
|
|
44
|
+
/** Managed STT v2: velay resolves this to Deepgram's Flux `/v2/listen`. */
|
|
45
|
+
const STT_STREAM_PATH = "/v2/speech/stt/stream";
|
|
46
|
+
|
|
47
|
+
export interface VellumManagedFluxRealtimeOptions {
|
|
48
|
+
/** Audio sample rate in Hz (default: 16000). */
|
|
49
|
+
sampleRate?: number;
|
|
50
|
+
/**
|
|
51
|
+
* BCP-47 language code (or `"multi"`), forwarded to the relay. This is
|
|
52
|
+
* how velay picks between `flux-general-en` and `flux-general-multi`, so
|
|
53
|
+
* it is the only lever over the Flux model from this side.
|
|
54
|
+
*/
|
|
55
|
+
language?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class VellumManagedFluxRealtimeTranscriber implements StreamingTranscriber {
|
|
59
|
+
readonly providerId = "vellum-flux" as const;
|
|
60
|
+
readonly boundaryId = "daemon-streaming" as const;
|
|
61
|
+
|
|
62
|
+
private readonly connection: SpeechRelayConnection;
|
|
63
|
+
private readonly options: VellumManagedFluxRealtimeOptions;
|
|
64
|
+
|
|
65
|
+
private onEvent: ((event: SttStreamServerEvent) => void) | null = null;
|
|
66
|
+
/** The current relay session; null while re-dialing after a session cap. */
|
|
67
|
+
private inner: DeepgramFluxRealtimeTranscriber | null = null;
|
|
68
|
+
/**
|
|
69
|
+
* The most recent `velay_error` frame from the current session. Set just
|
|
70
|
+
* before velay closes the socket; consumed when the inner adapter turns
|
|
71
|
+
* that close into an error event.
|
|
72
|
+
*/
|
|
73
|
+
private pendingRelayError: VelayErrorInfo | null = null;
|
|
74
|
+
/**
|
|
75
|
+
* Set when the current session hit velay's duration cap: the swap to a
|
|
76
|
+
* fresh session is deferred to the capped session's `closed` event so its
|
|
77
|
+
* close cleanup drains through first.
|
|
78
|
+
*/
|
|
79
|
+
private redialOnClose = false;
|
|
80
|
+
private stopping = false;
|
|
81
|
+
private closedEmitted = false;
|
|
82
|
+
|
|
83
|
+
constructor(
|
|
84
|
+
connection: SpeechRelayConnection,
|
|
85
|
+
options: VellumManagedFluxRealtimeOptions = {},
|
|
86
|
+
) {
|
|
87
|
+
this.connection = connection;
|
|
88
|
+
this.options = options;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async start(onEvent: (event: SttStreamServerEvent) => void): Promise<void> {
|
|
92
|
+
if (this.onEvent) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
"VellumManagedFluxRealtimeTranscriber: start() called twice",
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
this.onEvent = onEvent;
|
|
98
|
+
try {
|
|
99
|
+
const inner = await this.dial();
|
|
100
|
+
if (this.stopping) {
|
|
101
|
+
// stop() raced the initial dial: it saw no inner to tear down and
|
|
102
|
+
// already emitted closed, so the freshly opened relay session must
|
|
103
|
+
// not be kept (a leaked session keeps velay metering).
|
|
104
|
+
inner.stop();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.inner = inner;
|
|
108
|
+
} catch (err) {
|
|
109
|
+
// A failed WebSocket upgrade exposes no HTTP details; replay the
|
|
110
|
+
// request as a plain GET to recover the relay's {code, detail}
|
|
111
|
+
// rejection.
|
|
112
|
+
const rejection = await probeVelayRejection(this.probeUrl());
|
|
113
|
+
if (rejection) {
|
|
114
|
+
const mapped = mapVelayError(rejection);
|
|
115
|
+
throw new SttError(mapped.category, mapped.message, {
|
|
116
|
+
userFacing: true,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
throw err;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
sendAudio(audio: Buffer, mimeType: string): void {
|
|
124
|
+
// Audio arriving during a session-cap re-dial (sub-second) is dropped,
|
|
125
|
+
// mirroring the inner adapter's backpressure-drop semantics.
|
|
126
|
+
this.inner?.sendAudio(audio, mimeType);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
stop(): void {
|
|
130
|
+
if (this.stopping) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
this.stopping = true;
|
|
134
|
+
if (this.inner) {
|
|
135
|
+
this.inner.stop();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// Mid-redial stop: there is no session to drain.
|
|
139
|
+
this.emitClosedOnce();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// -- Relay session management ------------------------------------------
|
|
143
|
+
|
|
144
|
+
private async dial(): Promise<DeepgramFluxRealtimeTranscriber> {
|
|
145
|
+
this.pendingRelayError = null;
|
|
146
|
+
// Fresh per dial: a session-cap re-dial happens ~30 minutes in, long
|
|
147
|
+
// after any previously minted token expired.
|
|
148
|
+
const inner = new DeepgramFluxRealtimeTranscriber(
|
|
149
|
+
this.connection.mintServiceToken(),
|
|
150
|
+
{
|
|
151
|
+
baseUrl: this.connection.wsBaseUrl,
|
|
152
|
+
path: STT_STREAM_PATH,
|
|
153
|
+
queryAuth: true,
|
|
154
|
+
omitModelParam: true,
|
|
155
|
+
nativeContract: true,
|
|
156
|
+
...(this.options.sampleRate !== undefined
|
|
157
|
+
? { sampleRate: this.options.sampleRate }
|
|
158
|
+
: {}),
|
|
159
|
+
...(this.options.language ? { language: this.options.language } : {}),
|
|
160
|
+
onUnhandledFrame: (frame) => this.handleRelayFrame(frame),
|
|
161
|
+
},
|
|
162
|
+
);
|
|
163
|
+
await inner.start((event) => this.handleInnerEvent(inner, event));
|
|
164
|
+
return inner;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private probeUrl(): string {
|
|
168
|
+
const key = encodeURIComponent(this.connection.mintServiceToken());
|
|
169
|
+
return `${this.connection.httpBaseUrl}${STT_STREAM_PATH}?key=${key}`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private handleRelayFrame(frame: Record<string, unknown>): void {
|
|
173
|
+
if (frame.type !== "velay_error" || typeof frame.code !== "string") {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
this.pendingRelayError = {
|
|
177
|
+
code: frame.code,
|
|
178
|
+
...(typeof frame.detail === "string" ? { detail: frame.detail } : {}),
|
|
179
|
+
};
|
|
180
|
+
log.info(
|
|
181
|
+
{ code: frame.code, detail: frame.detail },
|
|
182
|
+
"Received velay_error frame on managed Flux relay",
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private handleInnerEvent(
|
|
187
|
+
source: DeepgramFluxRealtimeTranscriber,
|
|
188
|
+
event: SttStreamServerEvent,
|
|
189
|
+
): void {
|
|
190
|
+
// Events from a superseded session (the one replaced by a re-dial) must
|
|
191
|
+
// not leak through: its close/error was already accounted for.
|
|
192
|
+
if (source !== this.inner) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (event.type === "error") {
|
|
197
|
+
const relayError = this.pendingRelayError;
|
|
198
|
+
if (relayError?.code === "session_duration_exceeded") {
|
|
199
|
+
// Velay's 30-minute cap, not a failure. Swallow the error but keep
|
|
200
|
+
// the capped session current so its close cleanup drains first; the
|
|
201
|
+
// re-dial happens on its `closed` event.
|
|
202
|
+
this.redialOnClose = !this.stopping;
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (relayError) {
|
|
206
|
+
const mapped = mapVelayError(relayError);
|
|
207
|
+
this.emit({
|
|
208
|
+
type: "error",
|
|
209
|
+
category: mapped.category,
|
|
210
|
+
message: mapped.message,
|
|
211
|
+
});
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
this.emit(event);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (event.type === "closed") {
|
|
219
|
+
if (this.redialOnClose && !this.stopping) {
|
|
220
|
+
this.redialOnClose = false;
|
|
221
|
+
this.beginRedial();
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
this.emitClosedOnce();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
this.emit(event);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Replace the capped session with a fresh one. Detached: audio sent during
|
|
233
|
+
* the swap is dropped (see {@link sendAudio}); a dial failure is a real
|
|
234
|
+
* error and ends the stream.
|
|
235
|
+
*/
|
|
236
|
+
private beginRedial(): void {
|
|
237
|
+
log.info("Managed Flux relay session hit velay's duration cap, re-dialing");
|
|
238
|
+
this.inner = null;
|
|
239
|
+
void (async () => {
|
|
240
|
+
try {
|
|
241
|
+
const inner = await this.dial();
|
|
242
|
+
if (this.stopping) {
|
|
243
|
+
inner.stop();
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
this.inner = inner;
|
|
247
|
+
log.info("Managed Flux relay re-dial complete; session continues");
|
|
248
|
+
} catch (err) {
|
|
249
|
+
if (this.stopping) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const rejection = await probeVelayRejection(this.probeUrl());
|
|
253
|
+
if (rejection) {
|
|
254
|
+
const mapped = mapVelayError(rejection);
|
|
255
|
+
this.emit({
|
|
256
|
+
type: "error",
|
|
257
|
+
category: mapped.category,
|
|
258
|
+
message: mapped.message,
|
|
259
|
+
});
|
|
260
|
+
} else {
|
|
261
|
+
this.emit({
|
|
262
|
+
type: "error",
|
|
263
|
+
category: "provider-error",
|
|
264
|
+
message: `Managed Flux relay re-dial failed after the session cap: ${
|
|
265
|
+
err instanceof Error ? err.message : String(err)
|
|
266
|
+
}`,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
this.emitClosedOnce();
|
|
270
|
+
}
|
|
271
|
+
})();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// -- Event emission -----------------------------------------------------
|
|
275
|
+
|
|
276
|
+
private emit(event: SttStreamServerEvent): void {
|
|
277
|
+
if (!this.onEvent || this.closedEmitted) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (event.type === "closed") {
|
|
281
|
+
this.closedEmitted = true;
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
this.onEvent(event);
|
|
285
|
+
} catch (err) {
|
|
286
|
+
log.warn(
|
|
287
|
+
{ error: err },
|
|
288
|
+
"Listener error in vellum managed Flux realtime adapter",
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private emitClosedOnce(): void {
|
|
294
|
+
this.emit({ type: "closed" });
|
|
295
|
+
}
|
|
296
|
+
}
|
package/src/providers/types.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { ToolDefinition } from "../tools/tool-types.js";
|
|
2
2
|
export type { ToolDefinition };
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* The tool name that activates provider-native web search. Callers append a
|
|
6
|
+
* tool under this name only to instances reporting
|
|
7
|
+
* {@link Provider.supportsNativeWebSearch}; anywhere a request can change
|
|
8
|
+
* routes after that decision (see `RetryProvider`'s backup-profile
|
|
9
|
+
* escalation), the tool has to be dropped again when the new route cannot
|
|
10
|
+
* serve it, or the model answers with a tool call nothing can execute.
|
|
11
|
+
*/
|
|
12
|
+
export const NATIVE_WEB_SEARCH_TOOL_NAME = "web_search";
|
|
13
|
+
|
|
4
14
|
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
5
15
|
import {
|
|
6
16
|
ProviderError,
|
|
@@ -191,6 +201,15 @@ export interface ProviderResponse {
|
|
|
191
201
|
model: string;
|
|
192
202
|
/** Provider that actually produced this response, which may differ from a wrapper provider name. */
|
|
193
203
|
actualProvider?: string;
|
|
204
|
+
/**
|
|
205
|
+
* Inference profile key that actually governed this response when a
|
|
206
|
+
* wrapper re-routed the request away from the caller's own resolution
|
|
207
|
+
* (`RetryProvider`'s fallback-route escalation). `UsageTrackingProvider`
|
|
208
|
+
* prefers this over re-resolving from the original request options, so a
|
|
209
|
+
* successful fallback serve is attributed to the backup profile rather
|
|
210
|
+
* than the failed primary's. Absent on the normal (non-rerouted) path.
|
|
211
|
+
*/
|
|
212
|
+
actualInferenceProfile?: string;
|
|
194
213
|
/**
|
|
195
214
|
* Base URL the provider's HTTP client actually resolved to for this request,
|
|
196
215
|
* read from the live SDK client instance rather than re-derived from config.
|
|
@@ -281,6 +300,18 @@ export interface SendMessageConfig {
|
|
|
281
300
|
* request.
|
|
282
301
|
*/
|
|
283
302
|
forceOverrideProfile?: boolean;
|
|
303
|
+
/**
|
|
304
|
+
* True when the caller appended {@link NATIVE_WEB_SEARCH_TOOL_NAME} purely
|
|
305
|
+
* to activate the route's provider-native web search, rather than passing an
|
|
306
|
+
* app-executed search tool of the same name (which is what runs when a
|
|
307
|
+
* search backend like Brave or the platform search proxy is configured).
|
|
308
|
+
* Only the caller can tell those apart, and a route change after that
|
|
309
|
+
* decision has to: `RetryProvider` drops the tool on a backup that runs no
|
|
310
|
+
* native search, and must not touch it when the daemon executes it itself.
|
|
311
|
+
* A resolution/routing-time concern only; stripped before any provider wire
|
|
312
|
+
* request.
|
|
313
|
+
*/
|
|
314
|
+
nativeWebSearchSentinel?: boolean;
|
|
284
315
|
/**
|
|
285
316
|
* Per-conversation seed for deterministic `mix`-profile expansion. The agent
|
|
286
317
|
* loop sets this to the conversation id so every resolver call this send
|
|
@@ -316,6 +347,14 @@ export interface SendMessageConfig {
|
|
|
316
347
|
* JSON request bodies.
|
|
317
348
|
*/
|
|
318
349
|
usageAttributionHeaders?: Record<string, string>;
|
|
350
|
+
/**
|
|
351
|
+
* Per-request HTTP headers merged onto the transport. `RetryProvider`
|
|
352
|
+
* stamps these for providers that need support-lookup headers (OpenCode
|
|
353
|
+
* `x-opencode-session` / `x-opencode-request`). Provider clients pass
|
|
354
|
+
* them through SDK request options only and must never include this
|
|
355
|
+
* object in provider JSON request bodies.
|
|
356
|
+
*/
|
|
357
|
+
requestHeaders?: Record<string, string>;
|
|
319
358
|
/**
|
|
320
359
|
* Controls local usage-ledger writes for attributed provider calls.
|
|
321
360
|
* Defaults to `auto`; conversation paths that aggregate usage separately
|
|
@@ -66,9 +66,20 @@ export class UsageTrackingProvider implements Provider {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
try {
|
|
69
|
+
// A fallback-serving wrapper (`RetryProvider`'s escalation) stamps the
|
|
70
|
+
// profile that actually governed the response; prefer it over the
|
|
71
|
+
// original request's resolution so a degraded serve bills under the
|
|
72
|
+
// backup profile, not the failed primary's. `forceOverrideProfile`
|
|
73
|
+
// mirrors how the fallback dispatch floated that profile above the
|
|
74
|
+
// call-site layers.
|
|
75
|
+
const appliedOverride =
|
|
76
|
+
response.actualInferenceProfile ?? config.overrideProfile;
|
|
69
77
|
const attribution = resolveUsageAttribution({
|
|
70
78
|
callSite: config.callSite,
|
|
71
|
-
overrideProfile:
|
|
79
|
+
overrideProfile: appliedOverride,
|
|
80
|
+
...(response.actualInferenceProfile !== undefined
|
|
81
|
+
? { forceOverrideProfile: true }
|
|
82
|
+
: {}),
|
|
72
83
|
});
|
|
73
84
|
const providerName = response.actualProvider ?? this.inner.name;
|
|
74
85
|
const pricingUsage = buildPricingUsageFromResponse(
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { OpenAIChatCompletionsProvider } from "../openai/chat-completions-provider.js";
|
|
2
|
+
|
|
3
|
+
export interface VellumProviderOptions {
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
baseURL?: string;
|
|
6
|
+
streamTimeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* OpenAI-compatible client for Vellum-hosted GPU inference (vLLM).
|
|
11
|
+
* Managed requests set `baseURL` to the platform `/v1/runtime-proxy/vellum`
|
|
12
|
+
* path and authenticate with the assistant API key. These models have no
|
|
13
|
+
* bring-your-own-key path.
|
|
14
|
+
*/
|
|
15
|
+
export class VellumProvider extends OpenAIChatCompletionsProvider {
|
|
16
|
+
constructor(
|
|
17
|
+
apiKey: string,
|
|
18
|
+
model: string,
|
|
19
|
+
options: VellumProviderOptions = {},
|
|
20
|
+
) {
|
|
21
|
+
super(apiKey || "not-needed", model, {
|
|
22
|
+
providerName: "vellum",
|
|
23
|
+
providerLabel: "Vellum",
|
|
24
|
+
streamTimeoutMs: options.streamTimeoutMs,
|
|
25
|
+
omitToolChoiceWhenReasoning: true,
|
|
26
|
+
...(options.baseURL ? { baseURL: options.baseURL } : {}),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -41,6 +41,7 @@ describe("vellum-model-routing", () => {
|
|
|
41
41
|
|
|
42
42
|
test("getManagedUpstream resolves a bare catalog id to its owner", () => {
|
|
43
43
|
expect(getManagedUpstream("claude-fable-5")).toBe("anthropic");
|
|
44
|
+
expect(getManagedUpstream("qwen/qwen3-8b")).toBe("vellum");
|
|
44
45
|
});
|
|
45
46
|
|
|
46
47
|
test("getManagedUpstream resolves a routing string by its prefix", () => {
|
|
@@ -72,6 +73,7 @@ describe("vellum-model-routing", () => {
|
|
|
72
73
|
"gemini",
|
|
73
74
|
"openai",
|
|
74
75
|
"together",
|
|
76
|
+
"vellum",
|
|
75
77
|
]);
|
|
76
78
|
});
|
|
77
79
|
});
|
|
@@ -36,9 +36,9 @@ export const MANAGED_ROUTABLE_PROVIDERS: ReadonlySet<string> = new Set(
|
|
|
36
36
|
* Sentinel provider id for the single, provider-agnostic Vellum-managed
|
|
37
37
|
* connection. Unlike the per-provider `*-managed` connections, this one does
|
|
38
38
|
* not name an upstream provider on its DB row — the upstream is determined
|
|
39
|
-
* per-request from the resolving profile.
|
|
40
|
-
*
|
|
41
|
-
*
|
|
39
|
+
* per-request from the resolving profile. The same id is the catalog owner
|
|
40
|
+
* of Vellum-hosted GPU models. Dispatch still substitutes a concrete
|
|
41
|
+
* upstream (including `vellum` for those GPU models) before adapter lookup.
|
|
42
42
|
*/
|
|
43
43
|
export const VELLUM_MANAGED_PROVIDER = "vellum";
|
|
44
44
|
|
package/src/runtime/AGENTS.md
CHANGED
|
@@ -41,9 +41,8 @@ Approvals are **orthogonal to message sending**. The assistant asks for approval
|
|
|
41
41
|
|
|
42
42
|
- **Discovery**: Clients discover pending approvals via SSE events (`confirmation_request`, `secret_request`) which include a `requestId`.
|
|
43
43
|
- **Resolution**: Clients respond via standalone endpoints keyed by `requestId`:
|
|
44
|
-
- `POST /v1/confirm` — `{ requestId, decision
|
|
44
|
+
- `POST /v1/confirm` — `{ requestId, decision }`. Valid decisions: `"allow"`, `"deny"`. The confirm route resolves the pending interaction and nothing else: durable Always-Allow rules are minted through the gateway trust-rules API (`POST /v1/trust-rules`, also served assistant-scoped), which carries the rule's tool, pattern, and risk. The daemon's own `trust-rules` surface is read-only (`GET /v1/trust-rules` list, `POST /v1/trust-rules/suggest`).
|
|
45
45
|
- `POST /v1/secret` — `{ requestId, value, delivery }`
|
|
46
|
-
- `POST /v1/trust-rules` — `{ requestId, pattern, scope, decision }`. Validates pattern/scope against server-provided options. Does not resolve the confirmation itself.
|
|
47
46
|
- **Tracking**: The `pending-interactions` tracker (`assistant/src/runtime/pending-interactions.ts`) maps `requestId → conversation`. Use `register()` to track, `resolve()` to consume, `getByConversation()` to query.
|
|
48
47
|
|
|
49
48
|
Do NOT couple approval handling to message sending. Do NOT add run/status tracking to the send path.
|
|
@@ -266,6 +266,7 @@ const recordRequestLogCalls: Array<{
|
|
|
266
266
|
}> = [];
|
|
267
267
|
const recordUsageCalls: Array<{
|
|
268
268
|
conversationId: string;
|
|
269
|
+
providerName: string | undefined;
|
|
269
270
|
inputTokens: number;
|
|
270
271
|
outputTokens: number;
|
|
271
272
|
model: string;
|
|
@@ -279,7 +280,7 @@ const recordUsageCalls: Array<{
|
|
|
279
280
|
}> = [];
|
|
280
281
|
mock.module("../../daemon/conversation-usage.js", () => ({
|
|
281
282
|
recordUsage: (
|
|
282
|
-
ctx: { conversationId: string },
|
|
283
|
+
ctx: { conversationId: string; providerName?: string },
|
|
283
284
|
inputTokens: number,
|
|
284
285
|
outputTokens: number,
|
|
285
286
|
model: string,
|
|
@@ -300,6 +301,7 @@ mock.module("../../daemon/conversation-usage.js", () => ({
|
|
|
300
301
|
) => {
|
|
301
302
|
recordUsageCalls.push({
|
|
302
303
|
conversationId: ctx.conversationId,
|
|
304
|
+
providerName: ctx.providerName,
|
|
303
305
|
inputTokens,
|
|
304
306
|
outputTokens,
|
|
305
307
|
model,
|
|
@@ -1748,7 +1750,13 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
1748
1750
|
{ resolveTarget: async () => conversation },
|
|
1749
1751
|
);
|
|
1750
1752
|
|
|
1751
|
-
|
|
1753
|
+
// The result carries how the run ended, so a caller that treats output as
|
|
1754
|
+
// a finished answer can see that something else ended this one.
|
|
1755
|
+
expect(result).toEqual({
|
|
1756
|
+
invoked: true,
|
|
1757
|
+
producedToolCalls: false,
|
|
1758
|
+
exitReason: "error",
|
|
1759
|
+
});
|
|
1752
1760
|
// The checkpoint's flush pushed + persisted the partial tail.
|
|
1753
1761
|
expect(conversation.pushedMessages.length).toBeGreaterThan(0);
|
|
1754
1762
|
expect(conversation.persistedTailCalls.length).toBeGreaterThan(0);
|
|
@@ -1770,7 +1778,11 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
1770
1778
|
{ resolveTarget: async () => conversation },
|
|
1771
1779
|
);
|
|
1772
1780
|
|
|
1773
|
-
expect(result).toEqual({
|
|
1781
|
+
expect(result).toEqual({
|
|
1782
|
+
invoked: true,
|
|
1783
|
+
producedToolCalls: false,
|
|
1784
|
+
exitReason: "no_tool_calls",
|
|
1785
|
+
});
|
|
1774
1786
|
});
|
|
1775
1787
|
|
|
1776
1788
|
test("reports no_output on a clean empty reply when the caller requires usable output", async () => {
|
|
@@ -2986,6 +2998,96 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
2986
2998
|
});
|
|
2987
2999
|
});
|
|
2988
3000
|
|
|
3001
|
+
test("rerouted wake records usage under the profile that actually served", async () => {
|
|
3002
|
+
// The wake resolved its own profile before the run; the request then hit
|
|
3003
|
+
// an outage-shaped failure and `RetryProvider` escalated to a backup
|
|
3004
|
+
// profile on a different provider, stamping both `actualProvider` and
|
|
3005
|
+
// `actualInferenceProfile` on the response the loop reports.
|
|
3006
|
+
const usageEvent: AgentEvent = {
|
|
3007
|
+
type: "usage",
|
|
3008
|
+
inputTokens: 100,
|
|
3009
|
+
outputTokens: 5,
|
|
3010
|
+
model: "claude-sonnet-5",
|
|
3011
|
+
actualProvider: "anthropic",
|
|
3012
|
+
actualInferenceProfile: "backupProfile",
|
|
3013
|
+
providerDurationMs: 10,
|
|
3014
|
+
rawRequest: { request: "rerouted wake" },
|
|
3015
|
+
rawResponse: { response: "real reply" },
|
|
3016
|
+
};
|
|
3017
|
+
const conversation = makeWakeConversation({
|
|
3018
|
+
baseline: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
3019
|
+
scriptedEvents: [usageEvent],
|
|
3020
|
+
scriptedAssistant: {
|
|
3021
|
+
role: "assistant",
|
|
3022
|
+
content: [{ type: "text", text: "real reply" }],
|
|
3023
|
+
},
|
|
3024
|
+
});
|
|
3025
|
+
|
|
3026
|
+
await wakeAgentForOpportunity(
|
|
3027
|
+
{
|
|
3028
|
+
conversationId: conversation.conversationId,
|
|
3029
|
+
hint: "do reply",
|
|
3030
|
+
source: "unit-test",
|
|
3031
|
+
callSite: "memoryRetrospective",
|
|
3032
|
+
},
|
|
3033
|
+
{ resolveTarget: async () => conversation },
|
|
3034
|
+
);
|
|
3035
|
+
|
|
3036
|
+
// All three attribution facets of the row must describe the same call.
|
|
3037
|
+
// Provider and model already followed the backup off the event; the
|
|
3038
|
+
// profile has to follow it too or the row contradicts itself.
|
|
3039
|
+
expect(recordUsageCalls).toHaveLength(1);
|
|
3040
|
+
expect(recordUsageCalls[0]).toMatchObject({
|
|
3041
|
+
providerName: "anthropic",
|
|
3042
|
+
model: "claude-sonnet-5",
|
|
3043
|
+
overrideProfile: "backupProfile",
|
|
3044
|
+
forceOverrideProfile: true,
|
|
3045
|
+
selectionSeed: conversation.conversationId,
|
|
3046
|
+
});
|
|
3047
|
+
});
|
|
3048
|
+
|
|
3049
|
+
test("non-rerouted wake keeps its own profile resolution", async () => {
|
|
3050
|
+
// No reroute: the event carries no `actualInferenceProfile`, so the wake's
|
|
3051
|
+
// own pre-run resolution stands and nothing is floated above the call site.
|
|
3052
|
+
const usageEvent: AgentEvent = {
|
|
3053
|
+
type: "usage",
|
|
3054
|
+
inputTokens: 100,
|
|
3055
|
+
outputTokens: 5,
|
|
3056
|
+
model: "test-model",
|
|
3057
|
+
actualProvider: "test-provider",
|
|
3058
|
+
providerDurationMs: 10,
|
|
3059
|
+
rawRequest: { request: "normal wake" },
|
|
3060
|
+
rawResponse: { response: "real reply" },
|
|
3061
|
+
};
|
|
3062
|
+
const conversation = makeWakeConversation({
|
|
3063
|
+
baseline: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
3064
|
+
scriptedEvents: [usageEvent],
|
|
3065
|
+
scriptedAssistant: {
|
|
3066
|
+
role: "assistant",
|
|
3067
|
+
content: [{ type: "text", text: "real reply" }],
|
|
3068
|
+
},
|
|
3069
|
+
});
|
|
3070
|
+
|
|
3071
|
+
await wakeAgentForOpportunity(
|
|
3072
|
+
{
|
|
3073
|
+
conversationId: conversation.conversationId,
|
|
3074
|
+
hint: "do reply",
|
|
3075
|
+
source: "unit-test",
|
|
3076
|
+
callSite: "memoryRetrospective",
|
|
3077
|
+
},
|
|
3078
|
+
{ resolveTarget: async () => conversation },
|
|
3079
|
+
);
|
|
3080
|
+
|
|
3081
|
+
expect(recordUsageCalls).toHaveLength(1);
|
|
3082
|
+
expect(recordUsageCalls[0]).toMatchObject({
|
|
3083
|
+
providerName: "test-provider",
|
|
3084
|
+
model: "test-model",
|
|
3085
|
+
overrideProfile: null,
|
|
3086
|
+
forceOverrideProfile: false,
|
|
3087
|
+
selectionSeed: conversation.conversationId,
|
|
3088
|
+
});
|
|
3089
|
+
});
|
|
3090
|
+
|
|
2989
3091
|
test("silent no-op wake still records usage even though its request log is dropped", async () => {
|
|
2990
3092
|
const usageEvent: AgentEvent = {
|
|
2991
3093
|
type: "usage",
|