@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
|
@@ -54,7 +54,10 @@ let requestedSttProviders: (string | undefined)[];
|
|
|
54
54
|
|
|
55
55
|
/** A provider yields a streaming transcriber when its credential resolves. */
|
|
56
56
|
function transcriberFor(providerId: string | undefined): boolean {
|
|
57
|
-
|
|
57
|
+
// Both managed rows ride the same platform connection, so neither has a
|
|
58
|
+
// credential of its own. Live voice asks for the flux row specifically,
|
|
59
|
+
// since managed live voice resolves to it when no family is named.
|
|
60
|
+
return providerId === "vellum" || providerId === "vellum-flux"
|
|
58
61
|
? managedAvailable
|
|
59
62
|
: providerId !== undefined && providerKeys[providerId] !== undefined;
|
|
60
63
|
}
|
|
@@ -218,8 +221,10 @@ describe("live voice on managed speech", () => {
|
|
|
218
221
|
expect(readiness).toEqual({ status: "ready" });
|
|
219
222
|
|
|
220
223
|
// The transcriber the session arms is asked for the same provider the
|
|
221
|
-
// readiness verdict was earned on.
|
|
222
|
-
|
|
224
|
+
// readiness verdict was earned on. Live voice resolves the flux row while
|
|
225
|
+
// the roleless verdict above stays on the base one, which is the whole
|
|
226
|
+
// point of resolving per role.
|
|
227
|
+
expect(requestedSttProviders).toEqual(["vellum-flux"]);
|
|
223
228
|
expect(readConfigFile()).toBe(configBefore);
|
|
224
229
|
});
|
|
225
230
|
|
|
@@ -122,24 +122,86 @@ describe("LiveVoiceSessionManager", () => {
|
|
|
122
122
|
const rejected = await manager.startSession(START_FRAME, second.sink);
|
|
123
123
|
|
|
124
124
|
expect(accepted).toEqual({ status: "accepted", sessionId: "session-1" });
|
|
125
|
+
const busyFrame = {
|
|
126
|
+
type: "busy",
|
|
127
|
+
seq: 1,
|
|
128
|
+
activeSessionId: "session-1",
|
|
129
|
+
// Names where the blocking session is, so the refused client has
|
|
130
|
+
// somewhere to point the user.
|
|
131
|
+
holder: { conversationId: "conversation-123" },
|
|
132
|
+
} as const satisfies LiveVoiceServerFrame;
|
|
125
133
|
expect(rejected).toEqual({
|
|
126
134
|
status: "busy",
|
|
127
135
|
activeSessionId: "session-1",
|
|
136
|
+
frame: busyFrame,
|
|
137
|
+
});
|
|
138
|
+
expect(second.frames).toEqual([busyFrame]);
|
|
139
|
+
expect(sessions).toHaveLength(1);
|
|
140
|
+
expect(sessions[0]?.start).toHaveBeenCalledTimes(1);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("names the holder's device and the conversation it landed in", async () => {
|
|
144
|
+
const sessions: TestSession[] = [];
|
|
145
|
+
const manager = new LiveVoiceSessionManager({
|
|
146
|
+
createSessionId: mock(() => `session-${sessions.length + 1}`),
|
|
147
|
+
createSession: (context) => {
|
|
148
|
+
const session = createTestSession({
|
|
149
|
+
start: mock(async () => {
|
|
150
|
+
await context.sendFrame({
|
|
151
|
+
type: "ready",
|
|
152
|
+
sessionId: context.sessionId,
|
|
153
|
+
// The session minted its own conversation, so the start frame's
|
|
154
|
+
// is not the one to send anybody to.
|
|
155
|
+
conversationId: "conversation-minted",
|
|
156
|
+
});
|
|
157
|
+
}),
|
|
158
|
+
});
|
|
159
|
+
sessions.push(session);
|
|
160
|
+
return session;
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
const second = createSink();
|
|
164
|
+
|
|
165
|
+
await manager.startSession(
|
|
166
|
+
{ ...START_FRAME, client: "ios" },
|
|
167
|
+
createSink().sink,
|
|
168
|
+
);
|
|
169
|
+
const rejected = await manager.startSession(START_FRAME, second.sink);
|
|
170
|
+
|
|
171
|
+
expect(rejected).toMatchObject({
|
|
172
|
+
status: "busy",
|
|
128
173
|
frame: {
|
|
129
|
-
|
|
130
|
-
seq: 1,
|
|
131
|
-
activeSessionId: "session-1",
|
|
174
|
+
holder: { client: "ios", conversationId: "conversation-minted" },
|
|
132
175
|
},
|
|
133
176
|
});
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("says nothing about a holder that revealed nothing", async () => {
|
|
180
|
+
const sessions: TestSession[] = [];
|
|
181
|
+
const manager = new LiveVoiceSessionManager({
|
|
182
|
+
createSessionId: mock(() => `session-${sessions.length + 1}`),
|
|
183
|
+
createSession: () => {
|
|
184
|
+
const session = createTestSession();
|
|
185
|
+
sessions.push(session);
|
|
186
|
+
return session;
|
|
139
187
|
},
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
188
|
+
});
|
|
189
|
+
const anonymous = {
|
|
190
|
+
type: "start",
|
|
191
|
+
audio: START_FRAME.audio,
|
|
192
|
+
} as const satisfies LiveVoiceClientStartFrame;
|
|
193
|
+
const second = createSink();
|
|
194
|
+
|
|
195
|
+
await manager.startSession(anonymous, createSink().sink);
|
|
196
|
+
const rejected = await manager.startSession(anonymous, second.sink);
|
|
197
|
+
|
|
198
|
+
// An empty holder would read as "we know where it is" while naming
|
|
199
|
+
// nothing, so the field is dropped and the client uses its vaguer copy.
|
|
200
|
+
expect(rejected).toEqual({
|
|
201
|
+
status: "busy",
|
|
202
|
+
activeSessionId: "session-1",
|
|
203
|
+
frame: { type: "busy", seq: 1, activeSessionId: "session-1" },
|
|
204
|
+
});
|
|
143
205
|
});
|
|
144
206
|
|
|
145
207
|
test("releases the active session once for repeated close events", async () => {
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
* the start frame with a `credentials_unavailable` error frame carrying
|
|
8
8
|
* the preflight's user message, before any transcriber is resolved, and
|
|
9
9
|
* leaves the session manager free for a retry.
|
|
10
|
+
*
|
|
11
|
+
* They also pin the one exception to that rule: a client that declared
|
|
12
|
+
* `textInput` opens text-only when the *only* thing missing is the
|
|
13
|
+
* speech-to-text leg, because a session it can type into beats no session.
|
|
10
14
|
*/
|
|
11
15
|
|
|
12
16
|
import { describe, expect, mock, test } from "bun:test";
|
|
@@ -35,6 +39,24 @@ const START_FRAME = {
|
|
|
35
39
|
},
|
|
36
40
|
} as const satisfies LiveVoiceClientStartFrame;
|
|
37
41
|
|
|
42
|
+
const START_FRAME_TEXT_INPUT = {
|
|
43
|
+
...START_FRAME,
|
|
44
|
+
textInput: true,
|
|
45
|
+
} as const satisfies LiveVoiceClientStartFrame;
|
|
46
|
+
|
|
47
|
+
const STT_GAP = {
|
|
48
|
+
kind: "stt",
|
|
49
|
+
providerId: "deepgram",
|
|
50
|
+
reason: 'STT provider "deepgram" is missing credentials (Deepgram API Key)',
|
|
51
|
+
} as const;
|
|
52
|
+
|
|
53
|
+
const NOT_READY_STT_ONLY: LiveVoiceCredentialReadiness = {
|
|
54
|
+
status: "not-ready",
|
|
55
|
+
missing: [STT_GAP],
|
|
56
|
+
userMessage:
|
|
57
|
+
'Live voice is unavailable because it requires an API key for the speech-to-text provider "deepgram" (Deepgram API Key).',
|
|
58
|
+
};
|
|
59
|
+
|
|
38
60
|
const NOT_READY: LiveVoiceCredentialReadiness = {
|
|
39
61
|
status: "not-ready",
|
|
40
62
|
missing: [
|
|
@@ -60,7 +82,7 @@ class MockStreamingTranscriber implements StreamingTranscriber {
|
|
|
60
82
|
stop(): void {}
|
|
61
83
|
}
|
|
62
84
|
|
|
63
|
-
function createContext(): {
|
|
85
|
+
function createContext(startFrame: LiveVoiceClientStartFrame = START_FRAME): {
|
|
64
86
|
context: LiveVoiceSessionFactoryContext;
|
|
65
87
|
frames: LiveVoiceServerFrame[];
|
|
66
88
|
} {
|
|
@@ -71,7 +93,7 @@ function createContext(): {
|
|
|
71
93
|
frames,
|
|
72
94
|
context: {
|
|
73
95
|
sessionId: "session-123",
|
|
74
|
-
startFrame
|
|
96
|
+
startFrame,
|
|
75
97
|
sendFrame: mock(async (payload) => {
|
|
76
98
|
const frame = sequencer.next(payload);
|
|
77
99
|
frames.push(frame);
|
|
@@ -128,6 +150,130 @@ describe("live-voice session credential preflight gating", () => {
|
|
|
128
150
|
await session.close("websocket_close");
|
|
129
151
|
});
|
|
130
152
|
|
|
153
|
+
test("ready preflight leaves audioInput off the ready frame", async () => {
|
|
154
|
+
// Absent means "the microphone leg is live", which is what every session
|
|
155
|
+
// that passes the preflight has. Only a downgraded session says otherwise.
|
|
156
|
+
const { context, frames } = createContext();
|
|
157
|
+
const session = new LiveVoiceSession(context, {
|
|
158
|
+
resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
|
|
159
|
+
resolveCredentialReadiness: mock(
|
|
160
|
+
async (): Promise<LiveVoiceCredentialReadiness> => ({
|
|
161
|
+
status: "ready",
|
|
162
|
+
}),
|
|
163
|
+
),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
await session.start();
|
|
167
|
+
|
|
168
|
+
expect(frames[0]).toMatchObject({ type: "ready", textInput: true });
|
|
169
|
+
expect("audioInput" in (frames[0] ?? {})).toBe(false);
|
|
170
|
+
|
|
171
|
+
await session.close("websocket_close");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("a text-input client opens text-only when only the STT leg is missing", async () => {
|
|
175
|
+
const { context, frames } = createContext(START_FRAME_TEXT_INPUT);
|
|
176
|
+
const resolveTranscriber = mock(async () => new MockStreamingTranscriber());
|
|
177
|
+
const session = new LiveVoiceSession(context, {
|
|
178
|
+
resolveTranscriber,
|
|
179
|
+
resolveCredentialReadiness: mock(async () => NOT_READY_STT_ONLY),
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
await session.start();
|
|
183
|
+
|
|
184
|
+
expect(frames).toHaveLength(1);
|
|
185
|
+
expect(frames[0]).toMatchObject({
|
|
186
|
+
type: "ready",
|
|
187
|
+
sessionId: "session-123",
|
|
188
|
+
conversationId: "conversation-123",
|
|
189
|
+
textInput: true,
|
|
190
|
+
audioInput: false,
|
|
191
|
+
});
|
|
192
|
+
// Nothing may arm: resolving a transcriber the preflight just said has no
|
|
193
|
+
// credential is what would fail the session through armUtterance.
|
|
194
|
+
expect(resolveTranscriber).not.toHaveBeenCalled();
|
|
195
|
+
|
|
196
|
+
await session.close("websocket_close");
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("a client without textInput is still rejected on a missing STT leg", async () => {
|
|
200
|
+
// The downgrade is only safe for a client that can take a turn some other
|
|
201
|
+
// way. Opening a mute, deaf session for one that cannot is worse than
|
|
202
|
+
// failing, because nothing in it can ever work.
|
|
203
|
+
const { context, frames } = createContext();
|
|
204
|
+
const session = new LiveVoiceSession(context, {
|
|
205
|
+
resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
|
|
206
|
+
resolveCredentialReadiness: mock(async () => NOT_READY_STT_ONLY),
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
await expect(session.start()).rejects.toBeInstanceOf(
|
|
210
|
+
LiveVoiceSessionStartupError,
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
expect(frames).toEqual([
|
|
214
|
+
{
|
|
215
|
+
type: "error",
|
|
216
|
+
seq: 1,
|
|
217
|
+
code: "credentials_unavailable",
|
|
218
|
+
message: NOT_READY_STT_ONLY.userMessage,
|
|
219
|
+
},
|
|
220
|
+
]);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("a text-input client is still rejected when the TTS leg is missing", async () => {
|
|
224
|
+
// Text in, voice out: without the out half there is nothing to downgrade
|
|
225
|
+
// to, so this fails exactly as it would for any other client.
|
|
226
|
+
const { context, frames } = createContext(START_FRAME_TEXT_INPUT);
|
|
227
|
+
const session = new LiveVoiceSession(context, {
|
|
228
|
+
resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
|
|
229
|
+
resolveCredentialReadiness: mock(async () => NOT_READY),
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
await expect(session.start()).rejects.toBeInstanceOf(
|
|
233
|
+
LiveVoiceSessionStartupError,
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
expect(frames[0]).toMatchObject({ code: "credentials_unavailable" });
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("a text-input client is still rejected when both legs are missing", async () => {
|
|
240
|
+
const { context } = createContext(START_FRAME_TEXT_INPUT);
|
|
241
|
+
const session = new LiveVoiceSession(context, {
|
|
242
|
+
resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
|
|
243
|
+
resolveCredentialReadiness: mock(
|
|
244
|
+
async (): Promise<LiveVoiceCredentialReadiness> => ({
|
|
245
|
+
...NOT_READY,
|
|
246
|
+
missing: [...NOT_READY.missing, STT_GAP],
|
|
247
|
+
}),
|
|
248
|
+
),
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
await expect(session.start()).rejects.toBeInstanceOf(
|
|
252
|
+
LiveVoiceSessionStartupError,
|
|
253
|
+
);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("a text-input client is still rejected when not-ready names no gap", async () => {
|
|
257
|
+
// A verdict that says not-ready and lists nothing is a resolver bug. It
|
|
258
|
+
// must not be read as "only STT is missing" and quietly opened half-working
|
|
259
|
+
// on the strength of a vacuously true check.
|
|
260
|
+
const { context } = createContext(START_FRAME_TEXT_INPUT);
|
|
261
|
+
const session = new LiveVoiceSession(context, {
|
|
262
|
+
resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
|
|
263
|
+
resolveCredentialReadiness: mock(
|
|
264
|
+
async (): Promise<LiveVoiceCredentialReadiness> => ({
|
|
265
|
+
status: "not-ready",
|
|
266
|
+
missing: [],
|
|
267
|
+
userMessage: "Live voice is unavailable.",
|
|
268
|
+
}),
|
|
269
|
+
),
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
await expect(session.start()).rejects.toBeInstanceOf(
|
|
273
|
+
LiveVoiceSessionStartupError,
|
|
274
|
+
);
|
|
275
|
+
});
|
|
276
|
+
|
|
131
277
|
test("a rejected start frees the manager slot for a retry", async () => {
|
|
132
278
|
const manager = new LiveVoiceSessionManager({
|
|
133
279
|
createSession: (context) =>
|
|
@@ -157,6 +157,70 @@ describe("live-voice session telemetry", () => {
|
|
|
157
157
|
});
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Run one typed turn against a session with a real turn starter, then close.
|
|
162
|
+
*
|
|
163
|
+
* `readySession` wires none, so `handleTextTurn` returns before dispatching
|
|
164
|
+
* and every silence assertion against it passes for the wrong reason.
|
|
165
|
+
*/
|
|
166
|
+
async function endAfterTypedTurn(frame: {
|
|
167
|
+
text: string;
|
|
168
|
+
hidden?: boolean;
|
|
169
|
+
}): Promise<void> {
|
|
170
|
+
let started = false;
|
|
171
|
+
const session = new LiveVoiceSession(createContext(), {
|
|
172
|
+
resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
|
|
173
|
+
resolveCredentialReadiness: mock(
|
|
174
|
+
async (): Promise<LiveVoiceCredentialReadiness> => ({
|
|
175
|
+
status: "ready",
|
|
176
|
+
}),
|
|
177
|
+
),
|
|
178
|
+
startVoiceTurn: mock(async () => {
|
|
179
|
+
started = true;
|
|
180
|
+
return { turnId: "bridge-turn-1", abort: mock() };
|
|
181
|
+
}),
|
|
182
|
+
emitMetrics: false,
|
|
183
|
+
});
|
|
184
|
+
await session.start();
|
|
185
|
+
await session.handleClientFrame({ type: "text", ...frame });
|
|
186
|
+
for (let attempt = 0; attempt < 40 && !started; attempt += 1) {
|
|
187
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
188
|
+
}
|
|
189
|
+
if (!started) {
|
|
190
|
+
throw new Error("the typed turn never reached the bridge");
|
|
191
|
+
}
|
|
192
|
+
await session.close("client_end");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
test("the greeting that opens a session does not spend its silence reason", async () => {
|
|
196
|
+
// The classification answers "did anything come from the person?". A
|
|
197
|
+
// session that greets and hears nothing back is silent, and counting the
|
|
198
|
+
// greeting would retire the taxonomy for every greeted session.
|
|
199
|
+
await endAfterTypedTurn({
|
|
200
|
+
text: "this message is sent automatically",
|
|
201
|
+
hidden: true,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
expect(recordLiveVoiceSessionEnded).toHaveBeenCalledWith({
|
|
205
|
+
sessionId: "session-123",
|
|
206
|
+
screen: "ended_client_end:silent_no_audio",
|
|
207
|
+
outcome: "completed",
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("a turn the user really took clears the silence reason", async () => {
|
|
212
|
+
// The other half, and what keeps the test above honest: the same path with
|
|
213
|
+
// an ordinary typed turn is the person taking a turn, so the session is
|
|
214
|
+
// not silent and carries no classification at all.
|
|
215
|
+
await endAfterTypedTurn({ text: "typed by hand" });
|
|
216
|
+
|
|
217
|
+
expect(recordLiveVoiceSessionEnded).toHaveBeenCalledWith({
|
|
218
|
+
sessionId: "session-123",
|
|
219
|
+
screen: "ended_client_end",
|
|
220
|
+
outcome: "completed",
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
160
224
|
test("a dropped socket ends as completed, not failed", async () => {
|
|
161
225
|
const session = readySession();
|
|
162
226
|
await session.start();
|
|
@@ -257,6 +257,7 @@ describe("LiveVoiceSession STT", () => {
|
|
|
257
257
|
sessionId: "session-123",
|
|
258
258
|
conversationId: "conversation-123",
|
|
259
259
|
turnDetection: "manual",
|
|
260
|
+
textInput: true,
|
|
260
261
|
},
|
|
261
262
|
]);
|
|
262
263
|
});
|
|
@@ -611,6 +612,7 @@ describe("LiveVoiceSession STT", () => {
|
|
|
611
612
|
sessionId: "session-123",
|
|
612
613
|
conversationId: "conversation-123",
|
|
613
614
|
turnDetection: "server_vad",
|
|
615
|
+
textInput: true,
|
|
614
616
|
},
|
|
615
617
|
]);
|
|
616
618
|
|
|
@@ -700,6 +702,7 @@ describe("LiveVoiceSession STT", () => {
|
|
|
700
702
|
sessionId: "session-123",
|
|
701
703
|
conversationId: "conversation-123",
|
|
702
704
|
turnDetection: "manual",
|
|
705
|
+
textInput: true,
|
|
703
706
|
},
|
|
704
707
|
{
|
|
705
708
|
type: "error",
|