@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
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type LiveVoiceClientStartFrame,
|
|
7
7
|
type LiveVoiceServerFrame,
|
|
8
8
|
type LiveVoiceServerFramePayload,
|
|
9
|
+
type LiveVoiceSessionHolder,
|
|
9
10
|
} from "./protocol.js";
|
|
10
11
|
|
|
11
12
|
type MaybePromise<T> = T | Promise<T>;
|
|
@@ -157,6 +158,23 @@ function waitForSlotRelease(
|
|
|
157
158
|
});
|
|
158
159
|
}
|
|
159
160
|
|
|
161
|
+
/** {@link LiveVoiceSessionHolder}, as the manager keeps it while filling it in. */
|
|
162
|
+
interface MutableLiveVoiceSessionHolder {
|
|
163
|
+
client?: LiveVoiceSessionHolder["client"];
|
|
164
|
+
conversationId?: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Drop an all-empty holder, so `busy` carries a field or says nothing. */
|
|
168
|
+
function describeHolder(
|
|
169
|
+
holder: MutableLiveVoiceSessionHolder,
|
|
170
|
+
): LiveVoiceSessionHolder | null {
|
|
171
|
+
const described: MutableLiveVoiceSessionHolder = {
|
|
172
|
+
...(holder.client ? { client: holder.client } : {}),
|
|
173
|
+
...(holder.conversationId ? { conversationId: holder.conversationId } : {}),
|
|
174
|
+
};
|
|
175
|
+
return Object.keys(described).length > 0 ? described : null;
|
|
176
|
+
}
|
|
177
|
+
|
|
160
178
|
export class LiveVoiceSessionStartupError extends Error {
|
|
161
179
|
constructor(message: string) {
|
|
162
180
|
super(message);
|
|
@@ -208,6 +226,8 @@ interface ActiveLiveVoiceSession {
|
|
|
208
226
|
sessionId: string;
|
|
209
227
|
session: LiveVoiceSession;
|
|
210
228
|
sink: LiveVoiceServerFrameSink;
|
|
229
|
+
/** What a refused client is told about who holds the slot. */
|
|
230
|
+
holder: MutableLiveVoiceSessionHolder;
|
|
211
231
|
closing: boolean;
|
|
212
232
|
silenceTimer: ReturnType<typeof setTimeout> | null;
|
|
213
233
|
/** Resolves once this session has let go of the slot, however it went. */
|
|
@@ -272,12 +292,15 @@ export class LiveVoiceSessionManager {
|
|
|
272
292
|
}
|
|
273
293
|
}
|
|
274
294
|
|
|
275
|
-
const
|
|
276
|
-
|
|
295
|
+
const existing = this.activeSession;
|
|
296
|
+
const existingSessionId = existing?.sessionId ?? null;
|
|
297
|
+
if (existing !== null && existingSessionId !== null) {
|
|
277
298
|
const busySequencer = createLiveVoiceServerFrameSequencer();
|
|
299
|
+
const holder = describeHolder(existing.holder);
|
|
278
300
|
const frame = busySequencer.next({
|
|
279
301
|
type: "busy",
|
|
280
302
|
activeSessionId: existingSessionId,
|
|
303
|
+
...(holder ? { holder } : {}),
|
|
281
304
|
});
|
|
282
305
|
await sink.sendFrame(frame);
|
|
283
306
|
return {
|
|
@@ -289,10 +312,23 @@ export class LiveVoiceSessionManager {
|
|
|
289
312
|
|
|
290
313
|
const sessionId = this.createSessionId();
|
|
291
314
|
const sequencer = createLiveVoiceServerFrameSequencer();
|
|
315
|
+
const holder: MutableLiveVoiceSessionHolder = {
|
|
316
|
+
...(startFrame.client ? { client: startFrame.client } : {}),
|
|
317
|
+
...(startFrame.conversationId
|
|
318
|
+
? { conversationId: startFrame.conversationId }
|
|
319
|
+
: {}),
|
|
320
|
+
};
|
|
292
321
|
const context: LiveVoiceSessionFactoryContext = {
|
|
293
322
|
sessionId,
|
|
294
323
|
startFrame,
|
|
295
324
|
sendFrame: async (payload) => {
|
|
325
|
+
// `ready` carries the conversation the session actually landed in,
|
|
326
|
+
// which is the only source for one it minted itself. Read in passing
|
|
327
|
+
// rather than through a new session method: every frame already goes
|
|
328
|
+
// through here.
|
|
329
|
+
if (payload.type === "ready") {
|
|
330
|
+
holder.conversationId = payload.conversationId;
|
|
331
|
+
}
|
|
296
332
|
const frame = sequencer.next(payload);
|
|
297
333
|
await sink.sendFrame(frame);
|
|
298
334
|
return frame;
|
|
@@ -310,6 +346,7 @@ export class LiveVoiceSessionManager {
|
|
|
310
346
|
sessionId,
|
|
311
347
|
session,
|
|
312
348
|
sink,
|
|
349
|
+
holder,
|
|
313
350
|
closing: false,
|
|
314
351
|
silenceTimer: null,
|
|
315
352
|
released,
|
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
dominantLanguageTag,
|
|
60
60
|
voteDominantLanguage,
|
|
61
61
|
} from "../stt/language-metadata.js";
|
|
62
|
+
import { sttCatalogKeyForRole } from "../stt/roles.js";
|
|
62
63
|
import {
|
|
63
64
|
DEFAULT_SPEECH_ENERGY_THRESHOLD,
|
|
64
65
|
pcm16MaxNormalizedCorrelation,
|
|
@@ -131,6 +132,7 @@ import {
|
|
|
131
132
|
import {
|
|
132
133
|
type LiveVoiceClientAttachImageFrame,
|
|
133
134
|
type LiveVoiceClientFrame,
|
|
135
|
+
type LiveVoiceClientTextTurnFrame,
|
|
134
136
|
type LiveVoiceClientUpdateConfigFrame,
|
|
135
137
|
LiveVoiceProtocolErrorCode,
|
|
136
138
|
type LiveVoiceServerFramePayload,
|
|
@@ -360,7 +362,8 @@ export interface LiveVoiceSessionOptions {
|
|
|
360
362
|
/**
|
|
361
363
|
* Deepgram Flux turn-detection tuning. The production factory seeds this
|
|
362
364
|
* from `liveVoice.flux` config when unset; absent fields fall back to the
|
|
363
|
-
* schema defaults, which leave `turnEnd.enabled`
|
|
365
|
+
* schema defaults, which leave `turnEnd.enabled` on. A test that needs the
|
|
366
|
+
* local silence boundary has to say so.
|
|
364
367
|
*/
|
|
365
368
|
fluxConfig?: Partial<LiveVoiceFluxConfig>;
|
|
366
369
|
/**
|
|
@@ -705,6 +708,10 @@ interface ActiveAssistantTurn {
|
|
|
705
708
|
// no user utterance behind it — `content` is CONTINUATION_DELIVERY_CONTENT and
|
|
706
709
|
// the answer rides the control prompt (buildLiveDeliveryNote).
|
|
707
710
|
continuationDelivery: ContinuationDelivery | null;
|
|
711
|
+
// The turn's content is an internal instruction rather than user speech (the
|
|
712
|
+
// greeting that opens a session, say). The row still persists and the model
|
|
713
|
+
// still sees it; `hiddenSyntheticPrompt` keeps it out of the transcript.
|
|
714
|
+
hiddenPrompt: boolean;
|
|
708
715
|
// Set when a barge-in handed the interrupted work to a background subagent:
|
|
709
716
|
// that request's transcript, so the model can tell the user the work is
|
|
710
717
|
// still running instead of appearing to have dropped it.
|
|
@@ -993,6 +1000,19 @@ function createSyntheticUtterance(): UtteranceCycle {
|
|
|
993
1000
|
};
|
|
994
1001
|
}
|
|
995
1002
|
|
|
1003
|
+
// Carrier cycle for a turn the user typed rather than spoke. Identical to the
|
|
1004
|
+
// announcement carrier except that it arrives with its transcript already
|
|
1005
|
+
// complete: the text the client sent is the finalized transcript, so the cycle
|
|
1006
|
+
// joins the pipeline exactly where STT would have handed one over. Like the
|
|
1007
|
+
// announcement carrier it is never installed as `currentUtterance`, so a typed
|
|
1008
|
+
// turn taken during a voice session cannot collide with the armed capture cycle.
|
|
1009
|
+
function createTypedUtterance(text: string): UtteranceCycle {
|
|
1010
|
+
return {
|
|
1011
|
+
...createSyntheticUtterance(),
|
|
1012
|
+
finalTranscriptSegments: [text],
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
|
|
996
1016
|
// Objective handed to the background subagent that continues a barged-in turn.
|
|
997
1017
|
// The subagent forks the live conversation, so it already sees the interrupted
|
|
998
1018
|
// turn's completed tool calls in history and resumes from there. The request
|
|
@@ -1143,6 +1163,14 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
1143
1163
|
private receivedAudio = false;
|
|
1144
1164
|
private detectedSpeech = false;
|
|
1145
1165
|
private dispatchedTurn = false;
|
|
1166
|
+
// The client declared a text input affordance on the start frame, so it can
|
|
1167
|
+
// take a turn without the microphone. Governs one thing only: whether a
|
|
1168
|
+
// missing speech-to-text leg is fatal to startup (see start()).
|
|
1169
|
+
private readonly textInput: boolean;
|
|
1170
|
+
// Whether this session's speech-to-text leg came up. False only when the
|
|
1171
|
+
// preflight found it missing and `textInput` let the session open anyway, in
|
|
1172
|
+
// which case nothing arms a transcriber and typed turns are the only input.
|
|
1173
|
+
private audioInput = true;
|
|
1146
1174
|
// Non-null iff the start frame requested turnDetection "server_vad".
|
|
1147
1175
|
private readonly turnDetector: MediaTurnDetector | null;
|
|
1148
1176
|
// Base energy gate for server-VAD speech classification. During estimated
|
|
@@ -1398,6 +1426,7 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
1398
1426
|
context.startFrame.audio.sampleRate *
|
|
1399
1427
|
2 *
|
|
1400
1428
|
SERVER_VAD_PENDING_AUDIO_MAX_SECONDS;
|
|
1429
|
+
this.textInput = context.startFrame.textInput === true;
|
|
1401
1430
|
}
|
|
1402
1431
|
|
|
1403
1432
|
get finalTranscriptText(): string {
|
|
@@ -1418,10 +1447,30 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
1418
1447
|
if (this.resolveCredentialReadiness) {
|
|
1419
1448
|
const readiness = await this.resolveCredentialReadiness();
|
|
1420
1449
|
if (readiness.status === "not-ready") {
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1450
|
+
// A client that can type survives a dead speech-to-text leg: it opens
|
|
1451
|
+
// text-only rather than not at all. Requires the gap to be entirely on
|
|
1452
|
+
// the STT side, because the text-in path still speaks its reply, so a
|
|
1453
|
+
// session missing the TTS leg has nothing left to degrade to.
|
|
1454
|
+
//
|
|
1455
|
+
// A not-ready verdict naming no gap is not a downgrade candidate:
|
|
1456
|
+
// `every` is vacuously true over an empty list, and a resolver bug must
|
|
1457
|
+
// not read as "only STT is missing" and quietly open a half-working
|
|
1458
|
+
// session on the strength of it.
|
|
1459
|
+
const sttOnlyGap =
|
|
1460
|
+
readiness.missing.length > 0 &&
|
|
1461
|
+
readiness.missing.every((gap) => gap.kind === "stt");
|
|
1462
|
+
if (this.textInput && sttOnlyGap) {
|
|
1463
|
+
this.audioInput = false;
|
|
1464
|
+
log.warn(
|
|
1465
|
+
{ sessionId: this.context.sessionId, missing: readiness.missing },
|
|
1466
|
+
"Live voice starting text-only: speech-to-text unavailable",
|
|
1467
|
+
);
|
|
1468
|
+
} else {
|
|
1469
|
+
return await this.failStartup(
|
|
1470
|
+
readiness.userMessage,
|
|
1471
|
+
LiveVoiceProtocolErrorCode.CredentialsUnavailable,
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1425
1474
|
}
|
|
1426
1475
|
}
|
|
1427
1476
|
|
|
@@ -1444,6 +1493,11 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
1444
1493
|
sessionId: this.context.sessionId,
|
|
1445
1494
|
conversationId: this.conversationId,
|
|
1446
1495
|
turnDetection: this.turnDetector ? "server_vad" : "manual",
|
|
1496
|
+
// Unconditional: it answers "does this daemon take text frames", which
|
|
1497
|
+
// is a property of the daemon and not of what this client asked for.
|
|
1498
|
+
// A client reading it back absent is talking to one that predates it.
|
|
1499
|
+
textInput: true,
|
|
1500
|
+
...(this.audioInput ? {} : { audioInput: false }),
|
|
1447
1501
|
});
|
|
1448
1502
|
}
|
|
1449
1503
|
|
|
@@ -1472,7 +1526,64 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
1472
1526
|
case "attach_image":
|
|
1473
1527
|
this.persistPhoto(frame);
|
|
1474
1528
|
return;
|
|
1529
|
+
case "text":
|
|
1530
|
+
await this.handleTextTurn(frame);
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
/**
|
|
1536
|
+
* Run a turn the user typed instead of spoke.
|
|
1537
|
+
*
|
|
1538
|
+
* Joins the pipeline at the point a finished transcript would have: the same
|
|
1539
|
+
* turn runner, the same streaming TTS, the same activity and metrics. The
|
|
1540
|
+
* reply is spoken, which is the whole point: this is the input half being
|
|
1541
|
+
* text, not the session becoming a chat.
|
|
1542
|
+
*
|
|
1543
|
+
* Gated on {@link sessionTurnFloorBlocker}, the same check the continuation
|
|
1544
|
+
* announcement uses: no turn in flight, no assistant audio still draining,
|
|
1545
|
+
* no speech onset, and no utterance mid-capture. A typed turn asks the
|
|
1546
|
+
* identical question, so it asks it in the identical way rather than growing
|
|
1547
|
+
* a second set of conditions that could drift from the first.
|
|
1548
|
+
*
|
|
1549
|
+
* A blocked turn is refused rather than queued, and refused recoverably: the
|
|
1550
|
+
* session is fine and the user can send again. Typing over a reply in
|
|
1551
|
+
* progress reads as an interrupt, but making it one means choosing barge-in
|
|
1552
|
+
* semantics for a modality that has no speech onset to measure, which is its
|
|
1553
|
+
* own decision and not this seam's.
|
|
1554
|
+
*/
|
|
1555
|
+
private async handleTextTurn(
|
|
1556
|
+
frame: LiveVoiceClientTextTurnFrame,
|
|
1557
|
+
): Promise<void> {
|
|
1558
|
+
if (!this.startVoiceTurn) {
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
const blocker = this.sessionTurnFloorBlocker({
|
|
1563
|
+
ignoreManualCapture: true,
|
|
1564
|
+
});
|
|
1565
|
+
if (blocker !== null) {
|
|
1566
|
+
await this.sendFrame({
|
|
1567
|
+
type: "error",
|
|
1568
|
+
code: LiveVoiceProtocolErrorCode.InvalidFrame,
|
|
1569
|
+
message: "The assistant is busy with the current turn. Send again.",
|
|
1570
|
+
frameType: "text",
|
|
1571
|
+
recoverable: true,
|
|
1572
|
+
});
|
|
1573
|
+
log.debug(
|
|
1574
|
+
{ sessionId: this.context.sessionId, blocker },
|
|
1575
|
+
"Typed live-voice turn refused",
|
|
1576
|
+
);
|
|
1577
|
+
return;
|
|
1475
1578
|
}
|
|
1579
|
+
|
|
1580
|
+
const utterance = createTypedUtterance(frame.text);
|
|
1581
|
+
// Before the launch, so the anchor is the moment the text arrived rather
|
|
1582
|
+
// than whatever the dispatch costs.
|
|
1583
|
+
this.markTypedTurnSubmitted(utterance);
|
|
1584
|
+
await this.launchAssistantTurn(utterance, frame.text, {
|
|
1585
|
+
...(frame.hidden === true ? { hiddenPrompt: true } : {}),
|
|
1586
|
+
});
|
|
1476
1587
|
}
|
|
1477
1588
|
|
|
1478
1589
|
/**
|
|
@@ -1546,6 +1657,7 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
1546
1657
|
? null
|
|
1547
1658
|
: liveVoiceSilenceReason({
|
|
1548
1659
|
reachedActive: this.reachedActive,
|
|
1660
|
+
audioInput: this.audioInput,
|
|
1549
1661
|
receivedAudio: this.receivedAudio,
|
|
1550
1662
|
detectedSpeech: this.detectedSpeech,
|
|
1551
1663
|
});
|
|
@@ -1637,9 +1749,17 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
1637
1749
|
// outside: the session looks like a turn-detecting session while its
|
|
1638
1750
|
// opening turn is not one. The resolved provider reconciles this guess
|
|
1639
1751
|
// below.
|
|
1752
|
+
//
|
|
1753
|
+
// The guess reads the live-voice role, which is what the dial below
|
|
1754
|
+
// resolves. Reading the global provider instead makes the guess wrong
|
|
1755
|
+
// in exactly the configuration roles exist for (live voice on flux,
|
|
1756
|
+
// global on base deepgram), which reinstates the race this seed
|
|
1757
|
+
// prevents.
|
|
1640
1758
|
this.setProviderTurnEndActive(
|
|
1641
1759
|
this.fluxConfig.turnEnd.enabled &&
|
|
1642
|
-
supportsProviderTurnDetection(
|
|
1760
|
+
supportsProviderTurnDetection(
|
|
1761
|
+
sttCatalogKeyForRole(stt, "liveVoice"),
|
|
1762
|
+
) &&
|
|
1643
1763
|
this.turnDetector !== null,
|
|
1644
1764
|
);
|
|
1645
1765
|
const transcriber = await this.resolveTranscriber({
|
|
@@ -1838,6 +1958,17 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
1838
1958
|
}
|
|
1839
1959
|
|
|
1840
1960
|
private async armUtterance(): Promise<void> {
|
|
1961
|
+
// Nothing to arm without a speech-to-text leg. Guarded here rather than at
|
|
1962
|
+
// the call sites because there are several, and the post-turn re-arm
|
|
1963
|
+
// reaches this through `scheduleRearmAfterTurn` from five of them: a
|
|
1964
|
+
// text-only session that requested `server_vad` still holds a turn
|
|
1965
|
+
// detector, so the re-arm is not skipped for want of one. Arming anyway
|
|
1966
|
+
// would resolve a transcriber the preflight already said has no
|
|
1967
|
+
// credential, fail the session, and close it after its first typed turn.
|
|
1968
|
+
if (!this.audioInput) {
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1841
1972
|
const result = await this.beginUtterance();
|
|
1842
1973
|
if (result.status === "started" || result.status === "stale") {
|
|
1843
1974
|
return;
|
|
@@ -1867,6 +1998,14 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
1867
1998
|
// open", which a chunk arriving at all settles regardless of what the
|
|
1868
1999
|
// session then does with it.
|
|
1869
2000
|
this.receivedAudio = true;
|
|
2001
|
+
// A text-only session armed no transcriber, so there is nothing downstream
|
|
2002
|
+
// to route this into. Dropped rather than errored: the client was told on
|
|
2003
|
+
// `ready` that the microphone leg is dead, and a client still streaming is
|
|
2004
|
+
// one that has not caught up, not one to end the session over. The flag
|
|
2005
|
+
// above is still set, so telemetry keeps seeing that the mic did open.
|
|
2006
|
+
if (!this.audioInput) {
|
|
2007
|
+
return;
|
|
2008
|
+
}
|
|
1870
2009
|
if (this.turnDetector) {
|
|
1871
2010
|
await this.handleServerVadAudio(this.turnDetector, chunk);
|
|
1872
2011
|
return;
|
|
@@ -3118,6 +3257,41 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
3118
3257
|
if (this.pendingAnnouncement === null) {
|
|
3119
3258
|
return "nothing_pending";
|
|
3120
3259
|
}
|
|
3260
|
+
return this.sessionTurnFloorBlocker();
|
|
3261
|
+
}
|
|
3262
|
+
|
|
3263
|
+
/**
|
|
3264
|
+
* Why the session may not start a turn of its own right now, or null when it
|
|
3265
|
+
* may.
|
|
3266
|
+
*
|
|
3267
|
+
* "Of its own" means a turn with no released utterance behind it: a queued
|
|
3268
|
+
* continuation announcement, or a turn the user typed. Both need the same
|
|
3269
|
+
* assurance, that starting one would not talk over the assistant, cut off the
|
|
3270
|
+
* user, or race a capture cycle already in flight, so both ask here rather
|
|
3271
|
+
* than growing separate condition sets that could drift apart.
|
|
3272
|
+
*/
|
|
3273
|
+
private sessionTurnFloorBlocker(opts?: {
|
|
3274
|
+
/**
|
|
3275
|
+
* Ignore `manualAudioCaptured` when judging whether an utterance is in
|
|
3276
|
+
* flight. Set only for a typed turn.
|
|
3277
|
+
*
|
|
3278
|
+
* That flag means "manual capture routed at least one chunk into this
|
|
3279
|
+
* cycle", and manual capture forwards from the moment the microphone
|
|
3280
|
+
* opens rather than from a push-to-talk press. So in a manual session it
|
|
3281
|
+
* latches on the first chunk of silence and, because a manual cycle only
|
|
3282
|
+
* completes on release, never clears. Honouring it would refuse every
|
|
3283
|
+
* typed turn a few milliseconds into the session.
|
|
3284
|
+
*
|
|
3285
|
+
* Safe to ignore here and nowhere else. The flag exists because
|
|
3286
|
+
* transcript signals trail the provider, which would leave an
|
|
3287
|
+
* announcement talking over someone who is mid-sentence with no text yet.
|
|
3288
|
+
* A typed turn has evidence an announcement does not: the user is at the
|
|
3289
|
+
* keyboard, and the act of sending is itself the signal that they are not
|
|
3290
|
+
* currently speaking. The transcript-derived conditions still apply, so a
|
|
3291
|
+
* cycle that has actually produced words still blocks.
|
|
3292
|
+
*/
|
|
3293
|
+
readonly ignoreManualCapture?: boolean;
|
|
3294
|
+
}): string | null {
|
|
3121
3295
|
if (this.isClosed || this.state === "failed" || !this.startVoiceTurn) {
|
|
3122
3296
|
return "session_unavailable";
|
|
3123
3297
|
}
|
|
@@ -3146,7 +3320,7 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
3146
3320
|
!utterance.completed &&
|
|
3147
3321
|
(utterance.released ||
|
|
3148
3322
|
utterance.assistantTurnStarted ||
|
|
3149
|
-
utterance.manualAudioCaptured ||
|
|
3323
|
+
(utterance.manualAudioCaptured && opts?.ignoreManualCapture !== true) ||
|
|
3150
3324
|
utterance.finalTranscriptSegments.length > 0 ||
|
|
3151
3325
|
utterance.latestPartialText !== null)
|
|
3152
3326
|
) {
|
|
@@ -4544,6 +4718,10 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
4544
4718
|
// leading verdict commits the turn (see commitSpeculativeTurn); a hold
|
|
4545
4719
|
// verdict rolls everything back instead.
|
|
4546
4720
|
speculative?: boolean;
|
|
4721
|
+
// Marks `content` as an internal instruction rather than user speech, so
|
|
4722
|
+
// the row persists and drives the turn but never renders: no echo, and
|
|
4723
|
+
// `/messages` filters it after a reload.
|
|
4724
|
+
hiddenPrompt?: boolean;
|
|
4547
4725
|
},
|
|
4548
4726
|
): Promise<boolean> {
|
|
4549
4727
|
utterance.assistantTurnStarted = true;
|
|
@@ -4604,6 +4782,7 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
4604
4782
|
consumedAnnouncement: pending?.announcement ?? null,
|
|
4605
4783
|
pendingContextStopGeneration: this.detachStopGeneration,
|
|
4606
4784
|
continuationDelivery: opts?.continuationDelivery ?? null,
|
|
4785
|
+
hiddenPrompt: opts?.hiddenPrompt === true,
|
|
4607
4786
|
deltaEpoch: 0,
|
|
4608
4787
|
escalationHandedOff: false,
|
|
4609
4788
|
ttsBuffer: "",
|
|
@@ -4774,10 +4953,21 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
4774
4953
|
// silent" onto a session that has turns, a contradictory row. Setting it
|
|
4775
4954
|
// before can at worst leave a silent session unexplained, which is a gap
|
|
4776
4955
|
// rather than a false statement.
|
|
4777
|
-
|
|
4956
|
+
//
|
|
4957
|
+
// A synthetic prompt does not count. The silence classification answers
|
|
4958
|
+
// "did anything come from the person?", and the session opening by
|
|
4959
|
+
// greeting them is the session talking to itself. Counting it would
|
|
4960
|
+
// retire the whole taxonomy for every greeted session: `no_speech`,
|
|
4961
|
+
// `text_only` and `no_turn` become unreachable, and `no_audio` survives
|
|
4962
|
+
// only where capture failed early enough to withhold the greeting. That
|
|
4963
|
+
// is the one rate the funnel has for "voice didn't work for me".
|
|
4964
|
+
if (!activeTurn.hiddenPrompt) {
|
|
4965
|
+
this.dispatchedTurn = true;
|
|
4966
|
+
}
|
|
4778
4967
|
const handle = await this.startVoiceTurn({
|
|
4779
4968
|
conversationId: this.conversationId,
|
|
4780
4969
|
voiceSessionId: this.context.sessionId,
|
|
4970
|
+
...(activeTurn.hiddenPrompt ? { hiddenSyntheticPrompt: true } : {}),
|
|
4781
4971
|
userMessageChannel: "vellum",
|
|
4782
4972
|
assistantMessageChannel: "vellum",
|
|
4783
4973
|
// Fixed, and NOT the originating client: this pair resolves the turn's
|
|
@@ -5950,6 +6140,29 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
|
|
|
5950
6140
|
);
|
|
5951
6141
|
}
|
|
5952
6142
|
|
|
6143
|
+
/**
|
|
6144
|
+
* Stamp the moment a typed turn's text arrived as the end of the user's
|
|
6145
|
+
* input.
|
|
6146
|
+
*
|
|
6147
|
+
* `roundTripMs` measures from the user finishing their turn to the first
|
|
6148
|
+
* audio they hear, and it anchors on `utteranceEndAtMs ?? pttReleaseAtMs`.
|
|
6149
|
+
* A typed turn produces neither, so without this every typed turn reports a
|
|
6150
|
+
* null round trip and disappears from the latency numbers. That would bias
|
|
6151
|
+
* them toward spoken turns rather than simply losing rows, which is the
|
|
6152
|
+
* worse failure: the metric would still look healthy while describing less
|
|
6153
|
+
* and less of the traffic.
|
|
6154
|
+
*
|
|
6155
|
+
* The text frame landing is the honest equivalent of an utterance closing:
|
|
6156
|
+
* it is the instant the user stopped providing input and started waiting.
|
|
6157
|
+
* `sttMs` and the partial-transcript durations stay null, because nothing
|
|
6158
|
+
* was transcribed and claiming otherwise would invent a measurement.
|
|
6159
|
+
*/
|
|
6160
|
+
private markTypedTurnSubmitted(utterance: UtteranceCycle): void {
|
|
6161
|
+
this.markUtteranceMetric(utterance, "utteranceEndAtMs", (turnId) =>
|
|
6162
|
+
this.metrics.markUtteranceEnd(turnId),
|
|
6163
|
+
);
|
|
6164
|
+
}
|
|
6165
|
+
|
|
5953
6166
|
// Manual mode stamps the PTT release; server_vad stamps the utterance-end
|
|
5954
6167
|
// boundary instead (utteranceEndToFinalTranscript plays the sttMs role).
|
|
5955
6168
|
private markUtteranceReleased(utterance: UtteranceCycle): void {
|
|
@@ -6609,7 +6822,9 @@ async function defaultResolveStreamingTranscriber(
|
|
|
6609
6822
|
await import("../config/managed-speech-defaults.js");
|
|
6610
6823
|
const { resolveStreamingTranscriber } =
|
|
6611
6824
|
await import("../providers/speech-to-text/resolve.js");
|
|
6612
|
-
const { stt } = await resolveEffectiveSpeechProviders(
|
|
6825
|
+
const { stt } = await resolveEffectiveSpeechProviders(undefined, {
|
|
6826
|
+
role: "liveVoice",
|
|
6827
|
+
});
|
|
6613
6828
|
return resolveStreamingTranscriber({ ...options, providerId: stt });
|
|
6614
6829
|
}
|
|
6615
6830
|
|