@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
|
@@ -485,6 +485,7 @@ function winnerConfigFragment(entry: ProfileEntry): Mergeable {
|
|
|
485
485
|
description: _description,
|
|
486
486
|
status: _status,
|
|
487
487
|
mix: _mix,
|
|
488
|
+
fallbackProfile: _fallbackProfile,
|
|
488
489
|
...config
|
|
489
490
|
} = entry;
|
|
490
491
|
return config as Mergeable;
|
|
@@ -642,6 +643,30 @@ export function resolveEffectiveProfileKey(
|
|
|
642
643
|
return selectWinningProfile(callSite, llm, opts).profileName ?? undefined;
|
|
643
644
|
}
|
|
644
645
|
|
|
646
|
+
/**
|
|
647
|
+
* Returns the winning profile key only when it identifies a single provider
|
|
648
|
+
* route: one profile whose own provider/model fragment serves every call it
|
|
649
|
+
* wins. A mix winner returns `undefined` on purpose. Its arm is picked per
|
|
650
|
+
* conversation from a seed (or `Math.random()` unseeded), so a caller
|
|
651
|
+
* recomputing after the fact cannot know which arm actually ran, and arms can
|
|
652
|
+
* span providers, so the outer mix key would put one label on what are
|
|
653
|
+
* several provider routes. Use this where attribution must not claim a
|
|
654
|
+
* broader scope than the route that ran (e.g. failure-notification dedupe);
|
|
655
|
+
* `undefined` means "no single route can be named", and callers fall back to
|
|
656
|
+
* their narrower key.
|
|
657
|
+
*/
|
|
658
|
+
export function resolveSingleRouteProfileKey(
|
|
659
|
+
callSite: LLMCallSite,
|
|
660
|
+
llm: z.infer<typeof LLMSchema>,
|
|
661
|
+
opts: ResolveCallSiteOpts = {},
|
|
662
|
+
): string | undefined {
|
|
663
|
+
const name = selectWinningProfile(callSite, llm, opts).profileName;
|
|
664
|
+
if (name == null) {
|
|
665
|
+
return undefined;
|
|
666
|
+
}
|
|
667
|
+
return providerAwareEntry(llm, name)?.mix == null ? name : undefined;
|
|
668
|
+
}
|
|
669
|
+
|
|
645
670
|
/**
|
|
646
671
|
* Stable non-null identity for profileless configs. Callers should prefer real
|
|
647
672
|
* profile keys first; when no named profile applies, the resolved model id is
|
package/src/config/loader.ts
CHANGED
|
@@ -179,6 +179,9 @@ export function getDeploymentContextDefaults(): Record<string, unknown> {
|
|
|
179
179
|
"todoist-oauth": managed,
|
|
180
180
|
"discord-oauth": managed,
|
|
181
181
|
"hubspot-oauth": managed,
|
|
182
|
+
"monday-oauth": managed,
|
|
183
|
+
"eventbrite-oauth": managed,
|
|
184
|
+
"calendly-oauth": managed,
|
|
182
185
|
},
|
|
183
186
|
};
|
|
184
187
|
}
|
|
@@ -18,8 +18,23 @@
|
|
|
18
18
|
|
|
19
19
|
import { ttsSecretResolves } from "../calls/telephony-tts-capability.js";
|
|
20
20
|
import { managedSpeechAvailable } from "../platform/managed-speech.js";
|
|
21
|
-
import {
|
|
21
|
+
import { fluxModelForLanguage } from "../providers/speech-to-text/deepgram-flux-frames.js";
|
|
22
|
+
import {
|
|
23
|
+
baseModelFamilyFor,
|
|
24
|
+
getProviderEntry,
|
|
25
|
+
isManagedSttProvider,
|
|
26
|
+
resolveSttCatalogKey,
|
|
27
|
+
sttConfigForCatalogKey,
|
|
28
|
+
supportsProviderTurnDetection,
|
|
29
|
+
} from "../providers/speech-to-text/provider-catalog.js";
|
|
22
30
|
import { sttProviderKeyResolves } from "../providers/speech-to-text/resolve.js";
|
|
31
|
+
import {
|
|
32
|
+
STT_ROLES,
|
|
33
|
+
sttCatalogKeyForRole,
|
|
34
|
+
type SttRole,
|
|
35
|
+
sttRoleCapabilityGap,
|
|
36
|
+
sttSelectionForRole,
|
|
37
|
+
} from "../stt/roles.js";
|
|
23
38
|
import type { SttProviderId } from "../stt/types.js";
|
|
24
39
|
import { getCatalogProvider } from "../tts/provider-catalog.js";
|
|
25
40
|
import type { TtsProviderId } from "../tts/types.js";
|
|
@@ -60,6 +75,31 @@ async function ttsByokCredentialsResolve(provider: string): Promise<boolean> {
|
|
|
60
75
|
return true;
|
|
61
76
|
}
|
|
62
77
|
|
|
78
|
+
/**
|
|
79
|
+
* The managed provider that stands in for a configured one whose credential
|
|
80
|
+
* did not resolve.
|
|
81
|
+
*
|
|
82
|
+
* Managed speech has two STT entries, and they are not interchangeable:
|
|
83
|
+
* `vellum-flux` decides end-of-turn itself, `vellum` leaves it to the
|
|
84
|
+
* session's silence timer. Substituting the plain one for a provider that had
|
|
85
|
+
* provider turn detection would keep live voice working while quietly
|
|
86
|
+
* changing how it takes turns, which reads as "Flux is no better" rather than
|
|
87
|
+
* as a missing credential. Match the capability instead.
|
|
88
|
+
*/
|
|
89
|
+
function managedStandInFor(
|
|
90
|
+
configured: SttProviderId,
|
|
91
|
+
language: string | undefined,
|
|
92
|
+
): SttProviderId {
|
|
93
|
+
if (!supportsProviderTurnDetection(configured)) {
|
|
94
|
+
return "vellum";
|
|
95
|
+
}
|
|
96
|
+
// Flux has a model for ten languages. Outside them the relay rejects the
|
|
97
|
+
// dial outright, so standing in with it would hand the speaker a mic that
|
|
98
|
+
// does not work at all rather than one that detects turns less well. The
|
|
99
|
+
// language is the one the resolver will actually send.
|
|
100
|
+
return fluxModelForLanguage(language) === null ? "vellum" : "vellum-flux";
|
|
101
|
+
}
|
|
102
|
+
|
|
63
103
|
/** The speech providers a runtime path uses, after managed-speech defaulting. */
|
|
64
104
|
export interface EffectiveSpeechProviders {
|
|
65
105
|
stt: SttProviderId;
|
|
@@ -70,19 +110,23 @@ export interface EffectiveSpeechProviders {
|
|
|
70
110
|
* Resolve the speech providers the runtime actually uses.
|
|
71
111
|
*
|
|
72
112
|
* A configured service whose BYOK credential does not resolve is reported as
|
|
73
|
-
*
|
|
74
|
-
*
|
|
113
|
+
* its managed stand-in while managed speech is available (see
|
|
114
|
+
* {@link managedStandInFor}, which preserves provider turn detection); every
|
|
115
|
+
* other service keeps its configured provider. Read-only: callers that hold no `settings.write`
|
|
75
116
|
* scope (the live-voice WebSocket transport) resolve the same verdict the
|
|
76
117
|
* preflight route does without persisting anything.
|
|
77
118
|
*
|
|
78
119
|
* `config` selects the configuration to read the configured providers from,
|
|
79
|
-
* for callers already holding one (defaults to the loaded config).
|
|
120
|
+
* for callers already holding one (defaults to the loaded config). `role`
|
|
121
|
+
* selects which consumer's STT override applies; omitting it reads the global
|
|
122
|
+
* provider.
|
|
80
123
|
*/
|
|
81
124
|
export async function resolveEffectiveSpeechProviders(
|
|
82
125
|
config?: AssistantConfig,
|
|
126
|
+
options: { role?: SttRole } = {},
|
|
83
127
|
): Promise<EffectiveSpeechProviders> {
|
|
84
128
|
const services = (config ?? getConfig()).services;
|
|
85
|
-
const configuredStt = services.stt.
|
|
129
|
+
const configuredStt = sttCatalogKeyForRole(services.stt, options.role);
|
|
86
130
|
const configuredTts = services.tts.provider as TtsProviderId;
|
|
87
131
|
|
|
88
132
|
if (!(await managedSpeechAvailable())) {
|
|
@@ -90,9 +134,9 @@ export async function resolveEffectiveSpeechProviders(
|
|
|
90
134
|
}
|
|
91
135
|
|
|
92
136
|
const stt =
|
|
93
|
-
configuredStt
|
|
137
|
+
!isManagedSttProvider(configuredStt) &&
|
|
94
138
|
!(await sttByokCredentialResolves(configuredStt))
|
|
95
|
-
?
|
|
139
|
+
? managedStandInFor(configuredStt, services.stt.language)
|
|
96
140
|
: configuredStt;
|
|
97
141
|
|
|
98
142
|
const tts =
|
|
@@ -101,7 +145,85 @@ export async function resolveEffectiveSpeechProviders(
|
|
|
101
145
|
? "vellum"
|
|
102
146
|
: configuredTts;
|
|
103
147
|
|
|
104
|
-
return {
|
|
148
|
+
return {
|
|
149
|
+
stt: managedLiveVoiceModelFamily(stt, options.role, services.stt),
|
|
150
|
+
tts,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Managed live voice runs Flux.
|
|
156
|
+
*
|
|
157
|
+
* Turn detection is the reason to reach for Flux at all, and managed users
|
|
158
|
+
* cannot ask for it: the provider picker offers no model family, so `vellum`
|
|
159
|
+
* is as specific as they can be. Deciding it here rather than persisting a
|
|
160
|
+
* `services.stt.roles.liveVoice` entry keeps managed opinionated without
|
|
161
|
+
* writing config on anyone's behalf, and reaches installs already on `vellum`
|
|
162
|
+
* that a substitution rule never sees.
|
|
163
|
+
*
|
|
164
|
+
* Two things still win over it. A family the user named is honoured, because
|
|
165
|
+
* `services.stt.providers.vellum.model` accepts `nova-3` and quietly ignoring
|
|
166
|
+
* a valid setting is the silent substitution roles exist to prevent. And a
|
|
167
|
+
* language outside Flux's roster stays on nova-3, since the relay refuses the
|
|
168
|
+
* dial rather than degrading (see `managedStandInFor`).
|
|
169
|
+
*
|
|
170
|
+
* Live voice only: Flux streams and nothing else, so every other consumer
|
|
171
|
+
* would lose its transcriber.
|
|
172
|
+
*/
|
|
173
|
+
function managedLiveVoiceModelFamily(
|
|
174
|
+
resolved: SttProviderId,
|
|
175
|
+
role: SttRole | undefined,
|
|
176
|
+
stt: AssistantConfig["services"]["stt"],
|
|
177
|
+
): SttProviderId {
|
|
178
|
+
if (role !== "liveVoice" || resolved !== "vellum") {
|
|
179
|
+
return resolved;
|
|
180
|
+
}
|
|
181
|
+
if (sttSelectionForRole(stt, role).model !== undefined) {
|
|
182
|
+
return resolved;
|
|
183
|
+
}
|
|
184
|
+
return fluxModelForLanguage(stt.language) === null ? "vellum" : "vellum-flux";
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Whether a provider can serve every consumer, or only the one that chose it.
|
|
189
|
+
*
|
|
190
|
+
* `vellum` covers every boundary, so substituting it is safe everywhere. A
|
|
191
|
+
* narrowing row like `vellum-flux` streams and nothing else, and the roles it
|
|
192
|
+
* fails are exactly the consumers that must not be moved onto it.
|
|
193
|
+
*/
|
|
194
|
+
function servesEveryRole(provider: SttProviderId): boolean {
|
|
195
|
+
const selection = sttConfigForCatalogKey(provider);
|
|
196
|
+
return STT_ROLES.every(
|
|
197
|
+
(role) => sttRoleCapabilityGap(role, selection) === null,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* The writes that put `selection` in force as the global provider.
|
|
203
|
+
*
|
|
204
|
+
* The family is always written, including the base one. Substituting away
|
|
205
|
+
* from a variant leaves its `model` behind otherwise, and the next load
|
|
206
|
+
* resolves straight back to the variant this substitution just rejected: the
|
|
207
|
+
* provider would read as plain vellum while running Flux, with batch and
|
|
208
|
+
* telephony quietly unavailable. A provider with no families has no name to
|
|
209
|
+
* write, and no variant to have left a stale value behind either.
|
|
210
|
+
*/
|
|
211
|
+
function globalSttUpdates(selection: {
|
|
212
|
+
provider: string;
|
|
213
|
+
model?: string | undefined;
|
|
214
|
+
}): { path: string; provider: string }[] {
|
|
215
|
+
const updates = [
|
|
216
|
+
{ path: "services.stt.provider", provider: selection.provider },
|
|
217
|
+
];
|
|
218
|
+
const family =
|
|
219
|
+
selection.model ?? baseModelFamilyFor(selection.provider as SttProviderId);
|
|
220
|
+
if (family !== undefined) {
|
|
221
|
+
updates.push({
|
|
222
|
+
path: `services.stt.providers.${selection.provider}.model`,
|
|
223
|
+
provider: family,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return updates;
|
|
105
227
|
}
|
|
106
228
|
|
|
107
229
|
/**
|
|
@@ -119,8 +241,36 @@ export async function maybeDefaultSpeechToManaged(): Promise<void> {
|
|
|
119
241
|
const effective = await resolveEffectiveSpeechProviders();
|
|
120
242
|
|
|
121
243
|
const updates: { path: string; provider: string }[] = [];
|
|
122
|
-
if (effective.stt !== services.stt
|
|
123
|
-
|
|
244
|
+
if (effective.stt !== resolveSttCatalogKey(services.stt)) {
|
|
245
|
+
// A variant row is not a valid services.stt.provider value, so persist
|
|
246
|
+
// the pair that selects it. Writing the key itself would put an id the
|
|
247
|
+
// schema rejects on disk, and an unparseable services block is how the
|
|
248
|
+
// loader's salvage ladder ends up resetting the whole section.
|
|
249
|
+
const standIn = sttConfigForCatalogKey(effective.stt);
|
|
250
|
+
if (servesEveryRole(effective.stt)) {
|
|
251
|
+
updates.push(...globalSttUpdates(standIn));
|
|
252
|
+
} else {
|
|
253
|
+
// A stand-in that cannot serve every consumer belongs to the one that
|
|
254
|
+
// chose it. Live voice is that consumer: provider turn detection is
|
|
255
|
+
// the only reason managedStandInFor reaches for a narrow row, and
|
|
256
|
+
// writing that row globally would take batch transcription and
|
|
257
|
+
// telephony from every other consumer in a single write.
|
|
258
|
+
updates.push({
|
|
259
|
+
path: "services.stt.roles.liveVoice.provider",
|
|
260
|
+
provider: standIn.provider,
|
|
261
|
+
});
|
|
262
|
+
if (standIn.model !== undefined) {
|
|
263
|
+
updates.push({
|
|
264
|
+
path: "services.stt.roles.liveVoice.model",
|
|
265
|
+
provider: standIn.model,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
// The other consumers still need a provider that answers, and the
|
|
269
|
+
// narrow stand-in's own base is the managed row that serves them.
|
|
270
|
+
if (servesEveryRole(standIn.provider as SttProviderId)) {
|
|
271
|
+
updates.push(...globalSttUpdates({ provider: standIn.provider }));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
124
274
|
}
|
|
125
275
|
if (effective.tts !== services.tts.provider) {
|
|
126
276
|
updates.push({ path: "services.tts.provider", provider: effective.tts });
|
package/src/config/schema.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { BackupConfigSchema } from "./schemas/backup.js";
|
|
|
20
20
|
import { CallsConfigSchema } from "./schemas/calls.js";
|
|
21
21
|
import {
|
|
22
22
|
A2AConfigSchema,
|
|
23
|
+
DiscordConfigSchema,
|
|
23
24
|
SlackConfigSchema,
|
|
24
25
|
TelegramConfigSchema,
|
|
25
26
|
TwilioConfigSchema,
|
|
@@ -120,6 +121,7 @@ export const AssistantConfigSchema = z.object({
|
|
|
120
121
|
whatsapp: WhatsAppConfigSchema.default(WhatsAppConfigSchema.parse({})),
|
|
121
122
|
telegram: TelegramConfigSchema.default(TelegramConfigSchema.parse({})),
|
|
122
123
|
slack: SlackConfigSchema.default(SlackConfigSchema.parse({})),
|
|
124
|
+
discord: DiscordConfigSchema.default(DiscordConfigSchema.parse({})),
|
|
123
125
|
a2a: A2AConfigSchema.default(A2AConfigSchema.parse({})),
|
|
124
126
|
ingress: IngressConfigSchema,
|
|
125
127
|
platform: PlatformConfigSchema.default(PlatformConfigSchema.parse({})),
|
|
@@ -26,10 +26,11 @@ const FRONT_MODEL_DEFAULTS = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
// `eagerEotThreshold` is deliberately absent: it has no default, and leaving it
|
|
29
|
-
// unset is what keeps Deepgram from emitting speculative turn events.
|
|
29
|
+
// unset is what keeps Deepgram from emitting speculative turn events. `model`
|
|
30
|
+
// is absent for a related reason: unset means the spoken language selects the
|
|
31
|
+
// model, and pinning one overrides that.
|
|
30
32
|
const FLUX_DEFAULTS = {
|
|
31
|
-
turnEnd: { enabled:
|
|
32
|
-
model: "flux-general-en",
|
|
33
|
+
turnEnd: { enabled: true },
|
|
33
34
|
eotThreshold: 0.7,
|
|
34
35
|
eotTimeoutMs: 5_000,
|
|
35
36
|
};
|
|
@@ -229,7 +230,7 @@ describe("LiveVoiceFrontModelConfigSchema", () => {
|
|
|
229
230
|
});
|
|
230
231
|
|
|
231
232
|
describe("LiveVoiceFluxConfigSchema", () => {
|
|
232
|
-
test("empty object parses to defaults, with turn-end
|
|
233
|
+
test("empty object parses to defaults, with turn-end on", () => {
|
|
233
234
|
expect(LiveVoiceFluxConfigSchema.parse({})).toEqual(FLUX_DEFAULTS);
|
|
234
235
|
});
|
|
235
236
|
|
|
@@ -257,8 +258,8 @@ describe("LiveVoiceFluxConfigSchema", () => {
|
|
|
257
258
|
test("partial overrides merge with defaults", () => {
|
|
258
259
|
const parsed = LiveVoiceFluxConfigSchema.parse({ eotThreshold: 0.6 });
|
|
259
260
|
expect(parsed.eotThreshold).toBe(0.6);
|
|
260
|
-
expect(parsed.turnEnd.enabled).toBe(
|
|
261
|
-
expect(parsed.model).
|
|
261
|
+
expect(parsed.turnEnd.enabled).toBe(true);
|
|
262
|
+
expect(parsed.model).toBeUndefined();
|
|
262
263
|
expect(parsed.eotTimeoutMs).toBe(5_000);
|
|
263
264
|
});
|
|
264
265
|
|
|
@@ -122,6 +122,100 @@ describe("managed provider", () => {
|
|
|
122
122
|
provider: "vellum",
|
|
123
123
|
language: "multi",
|
|
124
124
|
providers: {},
|
|
125
|
+
roles: {},
|
|
125
126
|
});
|
|
126
127
|
});
|
|
127
128
|
});
|
|
129
|
+
|
|
130
|
+
describe("services.stt.roles", () => {
|
|
131
|
+
test("defaults to empty so an untouched config is unchanged", () => {
|
|
132
|
+
expect(SttServiceSchema.parse({ provider: "deepgram" }).roles).toEqual({});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("accepts a role whose selection covers what it needs", () => {
|
|
136
|
+
const parsed = SttServiceSchema.parse({
|
|
137
|
+
provider: "deepgram",
|
|
138
|
+
roles: { liveVoice: { provider: "deepgram", model: "flux" } },
|
|
139
|
+
});
|
|
140
|
+
expect(parsed.roles).toEqual({
|
|
141
|
+
liveVoice: { provider: "deepgram", model: "flux" },
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("a role can select a different family than the global default", () => {
|
|
146
|
+
// The point of the split: live voice on flux while everything else keeps
|
|
147
|
+
// the batch-capable family.
|
|
148
|
+
const parsed = SttServiceSchema.parse({
|
|
149
|
+
provider: "deepgram",
|
|
150
|
+
roles: { liveVoice: { provider: "deepgram", model: "flux" } },
|
|
151
|
+
});
|
|
152
|
+
expect(parsed.provider).toBe("deepgram");
|
|
153
|
+
expect(parsed.roles.liveVoice?.model).toBe("flux");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("rejects a streaming-only family for a role that batches", () => {
|
|
157
|
+
// The whole point of the split: Flux has no batch endpoint, so it can
|
|
158
|
+
// serve live voice and cannot serve file transcription.
|
|
159
|
+
for (const role of ["batch", "dictation", "telephony"]) {
|
|
160
|
+
const result = SttServiceSchema.safeParse({
|
|
161
|
+
provider: "deepgram",
|
|
162
|
+
roles: { [role]: { provider: "deepgram", model: "flux" } },
|
|
163
|
+
});
|
|
164
|
+
expect(result.success).toBe(false);
|
|
165
|
+
expect(result.error?.issues[0]?.message).toContain(
|
|
166
|
+
`services.stt.roles.${role} cannot be "deepgram" running flux`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("names the first requirement a provider misses", () => {
|
|
172
|
+
// Telephony needs both boundaries AND call ingestion. Flux is missing a
|
|
173
|
+
// boundary, so that is what the message says: the reported reason is the
|
|
174
|
+
// one the user has to act on, not a generic incompatibility.
|
|
175
|
+
const result = SttServiceSchema.safeParse({
|
|
176
|
+
provider: "deepgram",
|
|
177
|
+
roles: { telephony: { provider: "deepgram", model: "flux" } },
|
|
178
|
+
});
|
|
179
|
+
expect(result.success).toBe(false);
|
|
180
|
+
expect(result.error?.issues[0]?.message).toContain(
|
|
181
|
+
"supports no daemon-batch transcription, which telephony needs",
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("rejects an unknown role key", () => {
|
|
186
|
+
expect(
|
|
187
|
+
SttServiceSchema.safeParse({
|
|
188
|
+
provider: "deepgram",
|
|
189
|
+
roles: { transcription: { provider: "deepgram" } },
|
|
190
|
+
}).success,
|
|
191
|
+
).toBe(false);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("normalizes provider aliases inside a role", () => {
|
|
195
|
+
expect(
|
|
196
|
+
SttServiceSchema.parse({
|
|
197
|
+
provider: "deepgram",
|
|
198
|
+
roles: { batch: { provider: "whisper" } },
|
|
199
|
+
}).roles,
|
|
200
|
+
).toEqual({ batch: { provider: "openai-whisper" } });
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe("services.stt.roles model validation", () => {
|
|
205
|
+
test("rejects a family the role's provider cannot serve", () => {
|
|
206
|
+
const result = SttServiceSchema.safeParse({
|
|
207
|
+
provider: "deepgram",
|
|
208
|
+
roles: { liveVoice: { provider: "openai-whisper", model: "flux" } },
|
|
209
|
+
});
|
|
210
|
+
expect(result.success).toBe(false);
|
|
211
|
+
expect(result.error?.issues[0]?.message).toContain("single model");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("a role with no model runs the provider's default family", () => {
|
|
215
|
+
const parsed = SttServiceSchema.parse({
|
|
216
|
+
provider: "deepgram",
|
|
217
|
+
roles: { batch: { provider: "deepgram" } },
|
|
218
|
+
});
|
|
219
|
+
expect(parsed.roles.batch).toEqual({ provider: "deepgram" });
|
|
220
|
+
});
|
|
221
|
+
});
|
|
@@ -29,7 +29,7 @@ export const BackupOffsiteConfigSchema = z
|
|
|
29
29
|
.nullable()
|
|
30
30
|
.default(null)
|
|
31
31
|
.describe(
|
|
32
|
-
"Offsite destinations. null
|
|
32
|
+
"Offsite destinations. null uses encrypted iCloud Drive on macOS and no implicit destination on Windows or Linux; an explicit array (including []) overrides the default.",
|
|
33
33
|
),
|
|
34
34
|
})
|
|
35
35
|
.describe("Offsite backup configuration");
|
|
@@ -121,6 +121,38 @@ export const A2AConfigSchema = z
|
|
|
121
121
|
})
|
|
122
122
|
.describe("Agent-to-Agent protocol channel configuration");
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Discord channel settings.
|
|
126
|
+
*
|
|
127
|
+
* All three are read back from Discord when a bot token validates and written
|
|
128
|
+
* here so a connected panel can name the bot and build an install link
|
|
129
|
+
* without another round trip. The bot user and the application are separate
|
|
130
|
+
* ids: they coincide today, and a link built on that assumption would break
|
|
131
|
+
* silently if they stopped.
|
|
132
|
+
*/
|
|
133
|
+
export const DiscordConfigSchema = z
|
|
134
|
+
.object({
|
|
135
|
+
botUserId: z
|
|
136
|
+
.string({ error: "discord.botUserId must be a string" })
|
|
137
|
+
.default("")
|
|
138
|
+
.describe("Discord bot user ID"),
|
|
139
|
+
botUsername: z
|
|
140
|
+
.string({ error: "discord.botUsername must be a string" })
|
|
141
|
+
.default("")
|
|
142
|
+
.describe("Discord bot display name"),
|
|
143
|
+
applicationId: z
|
|
144
|
+
.string({ error: "discord.applicationId must be a string" })
|
|
145
|
+
.default("")
|
|
146
|
+
.describe("Discord application ID the bot belongs to"),
|
|
147
|
+
inviteUrl: z
|
|
148
|
+
.string({ error: "discord.inviteUrl must be a string" })
|
|
149
|
+
.default("")
|
|
150
|
+
.describe(
|
|
151
|
+
"Install link for the bot's application, computed when a token validates",
|
|
152
|
+
),
|
|
153
|
+
})
|
|
154
|
+
.describe("Discord channel configuration");
|
|
155
|
+
|
|
124
156
|
export const SlackConfigSchema = z
|
|
125
157
|
.object({
|
|
126
158
|
deliverAuthBypass: z
|
|
@@ -219,9 +219,16 @@ const LiveVoiceFluxTurnEndConfigSchema = z
|
|
|
219
219
|
.object({
|
|
220
220
|
enabled: z
|
|
221
221
|
.boolean({ error: "liveVoice.flux.turnEnd.enabled must be a boolean" })
|
|
222
|
-
|
|
222
|
+
// On by default because it is the only reason to select Flux, and
|
|
223
|
+
// because off is not a neutral setting on this provider: managed Flux
|
|
224
|
+
// exposes no `finalizeUtterance` (Flux commits turns itself), so the
|
|
225
|
+
// session's persistence check falls through and it opens a socket per
|
|
226
|
+
// utterance, putting a full Deepgram handshake on every turn. Ignored
|
|
227
|
+
// by any provider that does not own its turn boundary, so the default
|
|
228
|
+
// reaches only the sessions it is about.
|
|
229
|
+
.default(true)
|
|
223
230
|
.describe(
|
|
224
|
-
"Commit the live-voice turn on Flux's EndOfTurn instead of the front-door [0] hold verdict. Requires
|
|
231
|
+
"Commit the live-voice turn on Flux's EndOfTurn instead of the front-door [0] hold verdict. Requires the active STT provider to be running the flux model family (services.stt.providers.<provider>.model); ignored otherwise.",
|
|
225
232
|
),
|
|
226
233
|
})
|
|
227
234
|
.describe(
|
|
@@ -235,8 +242,10 @@ export const LiveVoiceFluxConfigSchema = z
|
|
|
235
242
|
),
|
|
236
243
|
model: z
|
|
237
244
|
.string({ error: "liveVoice.flux.model must be a string" })
|
|
238
|
-
.
|
|
239
|
-
.describe(
|
|
245
|
+
.optional()
|
|
246
|
+
.describe(
|
|
247
|
+
"Deepgram Flux model to pin when opening the STT stream. Unset (the default) selects the model from services.stt.language: English and unset use the English model, everything else uses the multilingual one",
|
|
248
|
+
),
|
|
240
249
|
eotThreshold: z
|
|
241
250
|
.number({ error: "liveVoice.flux.eotThreshold must be a number" })
|
|
242
251
|
.min(0.5, "liveVoice.flux.eotThreshold must be >= 0.5")
|