@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,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-consumer STT role selection.
|
|
3
|
+
*
|
|
4
|
+
* These cover the two pure functions every consumer routes through: which
|
|
5
|
+
* `{provider, model}` pair a role names, and which catalog row that pair
|
|
6
|
+
* resolves to. The resolvers, the config schema and the capability gate all
|
|
7
|
+
* read the same pair, so a drift here is a drift everywhere.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, expect, test } from "bun:test";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
sttCatalogKeyForRole,
|
|
13
|
+
sttRoleCapabilityGap,
|
|
14
|
+
sttSelectionForRole,
|
|
15
|
+
} from "../roles.js";
|
|
16
|
+
|
|
17
|
+
describe("sttSelectionForRole", () => {
|
|
18
|
+
test("a role's own entry wins over the global provider", () => {
|
|
19
|
+
expect(
|
|
20
|
+
sttSelectionForRole(
|
|
21
|
+
{
|
|
22
|
+
provider: "openai-whisper",
|
|
23
|
+
roles: { dictation: { provider: "deepgram" } },
|
|
24
|
+
},
|
|
25
|
+
"dictation",
|
|
26
|
+
),
|
|
27
|
+
).toEqual({ provider: "deepgram" });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("an unset role falls back to the global provider and its family", () => {
|
|
31
|
+
expect(
|
|
32
|
+
sttSelectionForRole(
|
|
33
|
+
{
|
|
34
|
+
provider: "deepgram",
|
|
35
|
+
providers: { deepgram: { model: "flux" } },
|
|
36
|
+
roles: { dictation: { provider: "openai-whisper" } },
|
|
37
|
+
},
|
|
38
|
+
"liveVoice",
|
|
39
|
+
),
|
|
40
|
+
).toEqual({ provider: "deepgram", model: "flux" });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("a roleless caller reads the global provider", () => {
|
|
44
|
+
expect(
|
|
45
|
+
sttSelectionForRole(
|
|
46
|
+
{
|
|
47
|
+
provider: "openai-whisper",
|
|
48
|
+
roles: { dictation: { provider: "deepgram" } },
|
|
49
|
+
},
|
|
50
|
+
undefined,
|
|
51
|
+
),
|
|
52
|
+
).toEqual({ provider: "openai-whisper" });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("a role override does not inherit the global provider's family", () => {
|
|
56
|
+
// The override names the whole pair. Carrying `flux` across from the
|
|
57
|
+
// global would pin the override to a family it never asked for.
|
|
58
|
+
expect(
|
|
59
|
+
sttSelectionForRole(
|
|
60
|
+
{
|
|
61
|
+
provider: "deepgram",
|
|
62
|
+
providers: { deepgram: { model: "flux" } },
|
|
63
|
+
roles: { batch: { provider: "deepgram" } },
|
|
64
|
+
},
|
|
65
|
+
"batch",
|
|
66
|
+
),
|
|
67
|
+
).toEqual({ provider: "deepgram" });
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe("sttCatalogKeyForRole", () => {
|
|
72
|
+
test("a role's family selects the variant row", () => {
|
|
73
|
+
expect(
|
|
74
|
+
sttCatalogKeyForRole(
|
|
75
|
+
{
|
|
76
|
+
provider: "deepgram",
|
|
77
|
+
roles: { liveVoice: { provider: "deepgram", model: "flux" } },
|
|
78
|
+
},
|
|
79
|
+
"liveVoice",
|
|
80
|
+
),
|
|
81
|
+
).toBe("deepgram-flux");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("two roles on one provider resolve to different rows", () => {
|
|
85
|
+
const stt = {
|
|
86
|
+
provider: "deepgram",
|
|
87
|
+
roles: {
|
|
88
|
+
liveVoice: { provider: "deepgram", model: "flux" },
|
|
89
|
+
batch: { provider: "deepgram" },
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
expect(sttCatalogKeyForRole(stt, "liveVoice")).toBe("deepgram-flux");
|
|
94
|
+
expect(sttCatalogKeyForRole(stt, "batch")).toBe("deepgram");
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe("sttRoleCapabilityGap", () => {
|
|
99
|
+
test("live voice accepts a streaming-only family", () => {
|
|
100
|
+
expect(
|
|
101
|
+
sttRoleCapabilityGap("liveVoice", {
|
|
102
|
+
provider: "deepgram",
|
|
103
|
+
model: "flux",
|
|
104
|
+
}),
|
|
105
|
+
).toBeNull();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("batch rejects the same family, which has no batch endpoint", () => {
|
|
109
|
+
const gap = sttRoleCapabilityGap("batch", {
|
|
110
|
+
provider: "deepgram",
|
|
111
|
+
model: "flux",
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
expect(gap).toContain("deepgram-flux");
|
|
115
|
+
expect(gap).toContain("batch");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("capability is judged on the resolved row, not the bare provider", () => {
|
|
119
|
+
// The pair is what distinguishes them: base deepgram batches and the
|
|
120
|
+
// flux family does not, under one provider id.
|
|
121
|
+
expect(sttRoleCapabilityGap("batch", { provider: "deepgram" })).toBeNull();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("telephony rejects a provider that does not transcribe calls", () => {
|
|
125
|
+
const gap = sttRoleCapabilityGap("telephony", {
|
|
126
|
+
provider: "deepgram",
|
|
127
|
+
model: "flux",
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
expect(gap).not.toBeNull();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("watch accepts a streaming-only family, having no batch leg", () => {
|
|
134
|
+
// Watch shares dictation's transport but not its batch fallback, so a
|
|
135
|
+
// family dictation must reject is legal here.
|
|
136
|
+
expect(
|
|
137
|
+
sttRoleCapabilityGap("watch", { provider: "deepgram", model: "flux" }),
|
|
138
|
+
).toBeNull();
|
|
139
|
+
expect(
|
|
140
|
+
sttRoleCapabilityGap("dictation", {
|
|
141
|
+
provider: "deepgram",
|
|
142
|
+
model: "flux",
|
|
143
|
+
}),
|
|
144
|
+
).not.toBeNull();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("an unknown provider is named rather than silently accepted", () => {
|
|
148
|
+
expect(sttRoleCapabilityGap("batch", { provider: "not-a-provider" })).toBe(
|
|
149
|
+
'"not-a-provider" is not in the STT provider catalog',
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -273,8 +273,11 @@ export function createDaemonBatchTranscriber(
|
|
|
273
273
|
case "xai":
|
|
274
274
|
return new XAIBatchTranscriber(apiKey, language);
|
|
275
275
|
case "deepgram-flux":
|
|
276
|
+
case "vellum-flux":
|
|
276
277
|
// Same copy the resolver raises, so a direct factory caller and a
|
|
277
|
-
// config-driven one report the mismatch identically.
|
|
278
|
+
// config-driven one report the mismatch identically. Both Flux
|
|
279
|
+
// variants are streaming-only: the model has no batch endpoint, BYOK
|
|
280
|
+
// or managed.
|
|
278
281
|
throw new SttError("provider-error", batchBoundaryGapReason(providerId), {
|
|
279
282
|
userFacing: true,
|
|
280
283
|
});
|
package/src/stt/roles.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STT consumer roles.
|
|
3
|
+
*
|
|
4
|
+
* `services.stt.provider` is one global that every STT caller reads, while the
|
|
5
|
+
* provider catalog already models capability per boundary. A provider that is
|
|
6
|
+
* right for one consumer is therefore wrong for another: Flux has no batch
|
|
7
|
+
* endpoint, so a global set to it breaks file transcription and telephony at
|
|
8
|
+
* the same moment it makes live voice better.
|
|
9
|
+
*
|
|
10
|
+
* A role names the consumer, not the boundary. Telephony is why: a single call
|
|
11
|
+
* resolves a streaming transcriber and falls back to a batch one within the
|
|
12
|
+
* same session, so a `{batch, streaming}` split would put one call on two
|
|
13
|
+
* roles. Each role instead declares the boundaries its call sites actually
|
|
14
|
+
* use, and a provider must satisfy all of them to be selectable for it.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
getProviderEntry,
|
|
19
|
+
sttCatalogKeyFor,
|
|
20
|
+
} from "../providers/speech-to-text/provider-catalog.js";
|
|
21
|
+
import type { SttBoundaryId, SttProviderId } from "./types.js";
|
|
22
|
+
|
|
23
|
+
/** The consumers that may select their own STT provider. */
|
|
24
|
+
export const STT_ROLES = [
|
|
25
|
+
"liveVoice",
|
|
26
|
+
"telephony",
|
|
27
|
+
"dictation",
|
|
28
|
+
"watch",
|
|
29
|
+
"batch",
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
32
|
+
export type SttRole = (typeof STT_ROLES)[number];
|
|
33
|
+
|
|
34
|
+
/** What a provider must support to serve a role. */
|
|
35
|
+
export interface SttRoleRequirements {
|
|
36
|
+
/** Every boundary the role's call sites resolve a transcriber on. */
|
|
37
|
+
readonly boundaries: readonly SttBoundaryId[];
|
|
38
|
+
/** Whether the role additionally requires telephony ingestion. */
|
|
39
|
+
readonly requiresTelephony: boolean;
|
|
40
|
+
/** Human-readable name for config errors and resolver logs. */
|
|
41
|
+
readonly label: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const STT_ROLE_REQUIREMENTS: Record<SttRole, SttRoleRequirements> = {
|
|
45
|
+
/**
|
|
46
|
+
* The live-voice transport arms a streaming transcriber per utterance and
|
|
47
|
+
* has no batch fallback, so streaming alone is the whole requirement.
|
|
48
|
+
*/
|
|
49
|
+
liveVoice: {
|
|
50
|
+
boundaries: ["daemon-streaming"],
|
|
51
|
+
requiresTelephony: false,
|
|
52
|
+
label: "live voice",
|
|
53
|
+
},
|
|
54
|
+
/**
|
|
55
|
+
* A call starts on streaming ingestion and falls back to per-turn batch
|
|
56
|
+
* when no streaming transcriber resolves, so both boundaries are load
|
|
57
|
+
* bearing inside one session. `telephonyMode` is a separate axis from the
|
|
58
|
+
* boundaries and is checked on top of them.
|
|
59
|
+
*/
|
|
60
|
+
telephony: {
|
|
61
|
+
boundaries: ["daemon-streaming", "daemon-batch"],
|
|
62
|
+
requiresTelephony: true,
|
|
63
|
+
label: "telephony",
|
|
64
|
+
},
|
|
65
|
+
/**
|
|
66
|
+
* The composer mic reaches the daemon two ways: a streaming WebSocket for
|
|
67
|
+
* live partials, and a batch POST of the recorded blob where that socket
|
|
68
|
+
* is unavailable. Both are the same user-facing feature, so a provider
|
|
69
|
+
* that serves only one would leave dictation half broken.
|
|
70
|
+
*/
|
|
71
|
+
dictation: {
|
|
72
|
+
boundaries: ["daemon-streaming", "daemon-batch"],
|
|
73
|
+
requiresTelephony: false,
|
|
74
|
+
label: "dictation",
|
|
75
|
+
},
|
|
76
|
+
/**
|
|
77
|
+
* A watch session streams the user's narration continuously while they
|
|
78
|
+
* work and files each final on the timeline. It shares dictation's
|
|
79
|
+
* transport but none of its fallback: there is no batch leg to catch a
|
|
80
|
+
* provider that cannot stream, so streaming alone is the requirement.
|
|
81
|
+
*/
|
|
82
|
+
watch: {
|
|
83
|
+
boundaries: ["daemon-streaming"],
|
|
84
|
+
requiresTelephony: false,
|
|
85
|
+
label: "watch",
|
|
86
|
+
},
|
|
87
|
+
/** File, attachment and skill transcription. Batch only. */
|
|
88
|
+
batch: {
|
|
89
|
+
boundaries: ["daemon-batch"],
|
|
90
|
+
requiresTelephony: false,
|
|
91
|
+
label: "batch transcription",
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Why a provider cannot serve a role, or null when it can.
|
|
97
|
+
*
|
|
98
|
+
* The catalog already knows every provider's boundaries and telephony mode;
|
|
99
|
+
* this only asks whether they cover what the role needs. Config validation
|
|
100
|
+
* rejects a pair on this, and the resolver logs it, so the same sentence
|
|
101
|
+
* explains the failure wherever it surfaces.
|
|
102
|
+
*/
|
|
103
|
+
export function sttRoleCapabilityGap(
|
|
104
|
+
role: SttRole,
|
|
105
|
+
selection: SttRoleSelection,
|
|
106
|
+
): string | null {
|
|
107
|
+
// Capability belongs to the resolved row: "deepgram" batches and
|
|
108
|
+
// "deepgram" running flux does not, and only the pair distinguishes them.
|
|
109
|
+
const provider = sttCatalogKeyFor(
|
|
110
|
+
selection.provider as SttProviderId,
|
|
111
|
+
selection.model,
|
|
112
|
+
);
|
|
113
|
+
const entry = getProviderEntry(provider);
|
|
114
|
+
if (!entry) {
|
|
115
|
+
return `"${selection.provider}" is not in the STT provider catalog`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const requirements = STT_ROLE_REQUIREMENTS[role];
|
|
119
|
+
const missing = requirements.boundaries.filter(
|
|
120
|
+
(boundary) => !entry.supportedBoundaries.has(boundary),
|
|
121
|
+
);
|
|
122
|
+
if (missing.length > 0) {
|
|
123
|
+
return `"${provider}" supports no ${missing.join(" or ")} transcription, which ${requirements.label} needs`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (requirements.requiresTelephony && entry.telephonyMode === "none") {
|
|
127
|
+
return `"${provider}" does not transcribe calls, which ${requirements.label} needs`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** What a role names: a provider, and optionally which of its model families. */
|
|
134
|
+
export interface SttRoleSelection {
|
|
135
|
+
readonly provider: string;
|
|
136
|
+
readonly model?: string | undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The STT config a consumer reads: its own `services.stt.roles` entry when it
|
|
141
|
+
* has one, otherwise the global provider and its configured model family.
|
|
142
|
+
*
|
|
143
|
+
* A role names the pair rather than a bare provider because the family is
|
|
144
|
+
* where capability actually differs. A role that could only name a provider
|
|
145
|
+
* could not say "live voice on flux", which is the whole reason to have one.
|
|
146
|
+
*
|
|
147
|
+
* The parameter is structural rather than the config type on purpose. This
|
|
148
|
+
* module sits under the config schema (which imports it to validate role
|
|
149
|
+
* entries), so importing the schema back would close a cycle, and the cycle
|
|
150
|
+
* would only surface as a module-init failure far from here.
|
|
151
|
+
*/
|
|
152
|
+
export function sttSelectionForRole(
|
|
153
|
+
stt: {
|
|
154
|
+
readonly provider: string;
|
|
155
|
+
readonly providers?:
|
|
156
|
+
| Record<string, { readonly model?: unknown } | undefined>
|
|
157
|
+
| undefined;
|
|
158
|
+
readonly roles?: Partial<Record<SttRole, SttRoleSelection>> | undefined;
|
|
159
|
+
},
|
|
160
|
+
role: SttRole | undefined,
|
|
161
|
+
): SttRoleSelection {
|
|
162
|
+
const override = role === undefined ? undefined : stt.roles?.[role];
|
|
163
|
+
if (override !== undefined) {
|
|
164
|
+
return override;
|
|
165
|
+
}
|
|
166
|
+
const model = stt.providers?.[stt.provider]?.model;
|
|
167
|
+
return {
|
|
168
|
+
provider: stt.provider,
|
|
169
|
+
...(typeof model === "string" ? { model } : {}),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The catalog row a role resolves to, via the pair it names. */
|
|
174
|
+
export function sttCatalogKeyForRole(
|
|
175
|
+
stt: Parameters<typeof sttSelectionForRole>[0],
|
|
176
|
+
role: SttRole | undefined,
|
|
177
|
+
): SttProviderId {
|
|
178
|
+
const selection = sttSelectionForRole(stt, role);
|
|
179
|
+
return sttCatalogKeyFor(selection.provider as SttProviderId, selection.model);
|
|
180
|
+
}
|
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
* Session lifecycle:
|
|
15
15
|
* 1. Client opens a WebSocket to `/v1/stt/stream` with required `mimeType`
|
|
16
16
|
* query parameter and optional `provider` metadata. The runtime is
|
|
17
|
-
* config-authoritative
|
|
18
|
-
* `services.stt.
|
|
17
|
+
* config-authoritative: it resolves the transcriber from the dictation
|
|
18
|
+
* role (`services.stt.roles.dictation`, else `services.stt.provider`)
|
|
19
|
+
* regardless of the requested provider.
|
|
19
20
|
* 2. The orchestrator resolves a {@link StreamingTranscriber} via
|
|
20
21
|
* `resolveStreamingTranscriber()` and starts the provider session.
|
|
21
22
|
* 3. The client sends `audio` frames (binary or base64-encoded JSON) and
|
package/src/stt/types.ts
CHANGED
|
@@ -17,8 +17,31 @@
|
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
20
|
+
* Model families a provider may be asked for, via
|
|
21
|
+
* `services.stt.providers.<id>.model`.
|
|
22
|
+
*
|
|
23
|
+
* A family is not a provider model string: `"flux"` selects the conversational
|
|
24
|
+
* endpoint family, and which member runs is decided downstream from the spoken
|
|
25
|
+
* language. Providers offering a single family ignore the setting.
|
|
26
|
+
*/
|
|
27
|
+
export const VALID_STT_MODELS = ["nova-3", "flux"] as const;
|
|
28
|
+
|
|
29
|
+
export type SttModelFamily = (typeof VALID_STT_MODELS)[number];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Catalog identity for a daemon-hosted STT backend.
|
|
33
|
+
*
|
|
34
|
+
* This is the key capability is stored under, NOT the set of values a user may
|
|
35
|
+
* put in `services.stt.provider`. A provider that offers more than one model
|
|
36
|
+
* family with genuinely different capabilities gets a row per family, because
|
|
37
|
+
* the catalog is a capability matrix and those capabilities differ. Users
|
|
38
|
+
* select the family with `services.stt.providers.<id>.model`, and
|
|
39
|
+
* {@link resolveSttCatalogKey} maps that pair onto the row.
|
|
40
|
+
*
|
|
41
|
+
* Keeping the two apart bounds the user-facing surface: adding a model family
|
|
42
|
+
* costs a catalog row and nothing else, while adding a provider id costs an
|
|
43
|
+
* enum entry, a settings-picker row, and a place in every predicate that
|
|
44
|
+
* classifies providers.
|
|
22
45
|
*/
|
|
23
46
|
export type SttProviderId =
|
|
24
47
|
| "openai-whisper"
|
|
@@ -30,7 +53,14 @@ export type SttProviderId =
|
|
|
30
53
|
| "deepgram-flux"
|
|
31
54
|
| "google-gemini"
|
|
32
55
|
| "xai"
|
|
33
|
-
| "vellum"
|
|
56
|
+
| "vellum"
|
|
57
|
+
/**
|
|
58
|
+
* Managed Flux: the same conversational model as `deepgram-flux`, reached
|
|
59
|
+
* through the platform's speech relay instead of a Deepgram key. Dials the
|
|
60
|
+
* relay's STT v2 endpoint asking for native Flux frames, so it keeps the
|
|
61
|
+
* turn events the relay's default v1-shaped translation drops.
|
|
62
|
+
*/
|
|
63
|
+
| "vellum-flux";
|
|
34
64
|
|
|
35
65
|
/**
|
|
36
66
|
* Telephony-specific STT capability class.
|
package/src/subagent/manager.ts
CHANGED
|
@@ -1478,6 +1478,18 @@ export class SubagentManager {
|
|
|
1478
1478
|
.filter((s): s is SubagentState => s !== undefined);
|
|
1479
1479
|
}
|
|
1480
1480
|
|
|
1481
|
+
/**
|
|
1482
|
+
* True when this parent still has a child that is not terminal (`pending`,
|
|
1483
|
+
* `running`, or `awaiting_input`). Idle-eviction and config-reload rebuild
|
|
1484
|
+
* skip those parents so an otherwise-idle conversation does not abort
|
|
1485
|
+
* mid-task children.
|
|
1486
|
+
*/
|
|
1487
|
+
hasActiveChildren(parentConversationId: string): boolean {
|
|
1488
|
+
return this.getChildrenOf(parentConversationId).some(
|
|
1489
|
+
(child) => !TERMINAL_STATUSES.has(child.status),
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1481
1493
|
/** Total number of active (non-terminal) subagents. */
|
|
1482
1494
|
get activeCount(): number {
|
|
1483
1495
|
return [...this.subagents.values()].filter(
|
|
@@ -12,6 +12,7 @@ describe("liveVoiceSilenceReason", () => {
|
|
|
12
12
|
expect(
|
|
13
13
|
liveVoiceSilenceReason({
|
|
14
14
|
reachedActive: false,
|
|
15
|
+
audioInput: true,
|
|
15
16
|
receivedAudio: false,
|
|
16
17
|
detectedSpeech: false,
|
|
17
18
|
}),
|
|
@@ -25,6 +26,7 @@ describe("liveVoiceSilenceReason", () => {
|
|
|
25
26
|
expect(
|
|
26
27
|
liveVoiceSilenceReason({
|
|
27
28
|
reachedActive: false,
|
|
29
|
+
audioInput: true,
|
|
28
30
|
receivedAudio: true,
|
|
29
31
|
detectedSpeech: true,
|
|
30
32
|
}),
|
|
@@ -35,6 +37,7 @@ describe("liveVoiceSilenceReason", () => {
|
|
|
35
37
|
expect(
|
|
36
38
|
liveVoiceSilenceReason({
|
|
37
39
|
reachedActive: true,
|
|
40
|
+
audioInput: true,
|
|
38
41
|
receivedAudio: false,
|
|
39
42
|
detectedSpeech: false,
|
|
40
43
|
}),
|
|
@@ -48,16 +51,47 @@ describe("liveVoiceSilenceReason", () => {
|
|
|
48
51
|
expect(
|
|
49
52
|
liveVoiceSilenceReason({
|
|
50
53
|
reachedActive: true,
|
|
54
|
+
audioInput: true,
|
|
51
55
|
receivedAudio: true,
|
|
52
56
|
detectedSpeech: false,
|
|
53
57
|
}),
|
|
54
58
|
).toBe("no_speech");
|
|
55
59
|
});
|
|
56
60
|
|
|
61
|
+
it("does not blame the microphone on a session that never had one", () => {
|
|
62
|
+
// A text-only session opened because speech-to-text was unavailable and
|
|
63
|
+
// the client could type. Scoring the absent microphone as `no_audio` would
|
|
64
|
+
// file it under a failure it does not have, and inflate the very rate this
|
|
65
|
+
// taxonomy exists to explain.
|
|
66
|
+
expect(
|
|
67
|
+
liveVoiceSilenceReason({
|
|
68
|
+
reachedActive: true,
|
|
69
|
+
audioInput: false,
|
|
70
|
+
receivedAudio: false,
|
|
71
|
+
detectedSpeech: false,
|
|
72
|
+
}),
|
|
73
|
+
).toBe("text_only");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("still reports text_only when a text-only session streamed stray audio", () => {
|
|
77
|
+
// A client that has not caught up with `audioInput: false` may keep
|
|
78
|
+
// sending chunks. Nothing transcribes them, so they say nothing about why
|
|
79
|
+
// the session was silent, and the reason must not flip on their account.
|
|
80
|
+
expect(
|
|
81
|
+
liveVoiceSilenceReason({
|
|
82
|
+
reachedActive: true,
|
|
83
|
+
audioInput: false,
|
|
84
|
+
receivedAudio: true,
|
|
85
|
+
detectedSpeech: true,
|
|
86
|
+
}),
|
|
87
|
+
).toBe("text_only");
|
|
88
|
+
});
|
|
89
|
+
|
|
57
90
|
it("reports speech that never became a turn", () => {
|
|
58
91
|
expect(
|
|
59
92
|
liveVoiceSilenceReason({
|
|
60
93
|
reachedActive: true,
|
|
94
|
+
audioInput: true,
|
|
61
95
|
receivedAudio: true,
|
|
62
96
|
detectedSpeech: true,
|
|
63
97
|
}),
|
|
@@ -66,11 +66,16 @@ export type LiveVoiceSessionOutcome = "completed" | "failed";
|
|
|
66
66
|
* microphone from a muted one from someone deliberately backing out. Those have
|
|
67
67
|
* completely different fixes, so the reason is what makes the rate actionable.
|
|
68
68
|
*
|
|
69
|
-
* The
|
|
70
|
-
*
|
|
69
|
+
* The values are ordered by how far the session got, and each one narrows the
|
|
70
|
+
* cause to a different layer:
|
|
71
71
|
*
|
|
72
72
|
* - `no_ready` never reached `active`: died in the credential preflight or
|
|
73
73
|
* the transport, before the user could have said anything.
|
|
74
|
+
* - `text_only` opened without a speech-to-text leg because the client could
|
|
75
|
+
* type, and then the user never typed. Ranked ahead of the audio
|
|
76
|
+
* signals because they cannot mean anything here: no microphone
|
|
77
|
+
* was ever expected, so scoring such a session `no_audio` would
|
|
78
|
+
* read as a failure the session does not have.
|
|
74
79
|
* - `no_audio` reached `active` but not one audio chunk ever arrived, so the
|
|
75
80
|
* microphone never opened. Permission denial looks like this.
|
|
76
81
|
* - `no_speech` audio arrived but the detector never classified any of it as
|
|
@@ -80,6 +85,7 @@ export type LiveVoiceSessionOutcome = "completed" | "failed";
|
|
|
80
85
|
*/
|
|
81
86
|
export type LiveVoiceSilenceReason =
|
|
82
87
|
| "no_ready"
|
|
88
|
+
| "text_only"
|
|
83
89
|
| "no_audio"
|
|
84
90
|
| "no_speech"
|
|
85
91
|
| "no_turn";
|
|
@@ -91,12 +97,16 @@ export type LiveVoiceSilenceReason =
|
|
|
91
97
|
*/
|
|
92
98
|
export function liveVoiceSilenceReason(signals: {
|
|
93
99
|
reachedActive: boolean;
|
|
100
|
+
audioInput: boolean;
|
|
94
101
|
receivedAudio: boolean;
|
|
95
102
|
detectedSpeech: boolean;
|
|
96
103
|
}): LiveVoiceSilenceReason {
|
|
97
104
|
if (!signals.reachedActive) {
|
|
98
105
|
return "no_ready";
|
|
99
106
|
}
|
|
107
|
+
if (!signals.audioInput) {
|
|
108
|
+
return "text_only";
|
|
109
|
+
}
|
|
100
110
|
if (!signals.receivedAudio) {
|
|
101
111
|
return "no_audio";
|
|
102
112
|
}
|
|
@@ -97,6 +97,7 @@ export const llmUsageTelemetryEventSchema = z.object({
|
|
|
97
97
|
parent_turn_index: z.number().int().min(0).nullable().optional(),
|
|
98
98
|
subagent_role: z.string().trim().min(1).max(64).nullable().optional(),
|
|
99
99
|
subagent_spawn_mode: z.string().trim().min(1).max(64).nullable().optional(),
|
|
100
|
+
cron_run_id: z.string().trim().min(1).max(64).nullable().optional(),
|
|
100
101
|
});
|
|
101
102
|
export type LlmUsageTelemetryEvent = z.infer<
|
|
102
103
|
typeof llmUsageTelemetryEventSchema
|
|
@@ -177,6 +178,11 @@ export const lifecycleTelemetryEventSchema = z.object({
|
|
|
177
178
|
recorded_at: z.number().int(),
|
|
178
179
|
assistant_version: z.string().trim().min(1).max(64).nullable().optional(),
|
|
179
180
|
event_name: z.string().trim().min(1).max(64),
|
|
181
|
+
tool_name: z.string().trim().min(1).max(255).optional(),
|
|
182
|
+
risk_level: z.string().trim().min(1).max(32).optional(),
|
|
183
|
+
risk_threshold: z.string().trim().min(1).max(32).optional(),
|
|
184
|
+
surface: z.string().trim().min(1).max(64).optional(),
|
|
185
|
+
conversation_id: z.string().trim().min(1).max(128).optional(),
|
|
180
186
|
});
|
|
181
187
|
export type LifecycleTelemetryEvent = z.infer<
|
|
182
188
|
typeof lifecycleTelemetryEventSchema
|