@vellumai/assistant 0.6.3 → 0.6.4
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 +273 -10
- package/Dockerfile +2 -3
- package/bun.lock +5 -13
- package/docs/backup-troubleshooting.md +52 -0
- package/docs/browser-use-architecture-phase2.md +174 -0
- package/docs/stt-provider-onboarding.md +120 -0
- package/knip.json +12 -2
- package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
- package/node_modules/@vellumai/ces-contracts/package.json +3 -3
- package/openapi.yaml +982 -72
- package/package.json +4 -6
- package/scripts/generate-openapi.ts +0 -1
- package/scripts/test.sh +73 -18
- package/src/__tests__/agent-image-optimize.test.ts +28 -0
- package/src/__tests__/agent-loop.test.ts +123 -0
- package/src/__tests__/anthropic-provider.test.ts +263 -10
- package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
- package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
- package/src/__tests__/browser-fill-credential.test.ts +11 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/browser-skill-endstate.test.ts +31 -7
- package/src/__tests__/btw-routes.test.ts +7 -0
- package/src/__tests__/call-controller.test.ts +581 -20
- package/src/__tests__/catalog-files.test.ts +138 -0
- package/src/__tests__/channel-invite-transport.test.ts +2 -2
- package/src/__tests__/channel-readiness-routes.test.ts +16 -20
- package/src/__tests__/channel-readiness-service.test.ts +12 -7
- package/src/__tests__/checker.test.ts +157 -10
- package/src/__tests__/clawhub-files.test.ts +347 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
- package/src/__tests__/config-analysis.test.ts +100 -0
- package/src/__tests__/config-schema.test.ts +1013 -66
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
- package/src/__tests__/config-watcher.test.ts +43 -8
- package/src/__tests__/contact-store-user-file.test.ts +512 -0
- package/src/__tests__/contacts-write.test.ts +197 -0
- package/src/__tests__/context-window-manager.test.ts +88 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +98 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
- package/src/__tests__/conversation-error.test.ts +70 -0
- package/src/__tests__/conversation-history-web-search.test.ts +11 -4
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
- package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
- package/src/__tests__/conversation-list-source.test.ts +145 -0
- package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +901 -60
- package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
- package/src/__tests__/conversation-skill-tools.test.ts +7 -4
- package/src/__tests__/conversation-slash-commands.test.ts +33 -0
- package/src/__tests__/conversation-slash-queue.test.ts +89 -18
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-health-service.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/credential-vault-unit.test.ts +379 -3
- package/src/__tests__/credentials-cli.test.ts +40 -16
- package/src/__tests__/cross-provider-web-search.test.ts +146 -35
- package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
- package/src/__tests__/device-id.test.ts +112 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
- package/src/__tests__/email-html-renderer.test.ts +71 -0
- package/src/__tests__/email-invite-adapter.test.ts +36 -32
- package/src/__tests__/emit-event-signal.test.ts +71 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
- package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +64 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
- package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
- package/src/__tests__/gmail-archive-gate.test.ts +246 -0
- package/src/__tests__/gmail-preferences.test.ts +117 -0
- package/src/__tests__/headless-browser-interactions.test.ts +43 -0
- package/src/__tests__/headless-browser-mode.test.ts +614 -0
- package/src/__tests__/headless-browser-navigate.test.ts +142 -5
- package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
- package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
- package/src/__tests__/heartbeat-service.test.ts +70 -17
- package/src/__tests__/home-state-routes.test.ts +162 -0
- package/src/__tests__/host-bash-proxy.test.ts +0 -5
- package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
- package/src/__tests__/host-cu-proxy.test.ts +0 -5
- package/src/__tests__/identity-intro-cache.test.ts +40 -10
- package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
- package/src/__tests__/llm-context-normalization.test.ts +488 -0
- package/src/__tests__/llm-context-route-provider.test.ts +86 -5
- package/src/__tests__/llm-usage-store.test.ts +363 -0
- package/src/__tests__/media-stream-output.test.ts +555 -0
- package/src/__tests__/media-stream-parser.test.ts +374 -0
- package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
- package/src/__tests__/media-stream-stt-session.test.ts +588 -0
- package/src/__tests__/media-turn-detector.test.ts +440 -0
- package/src/__tests__/message-queue.test.ts +125 -0
- package/src/__tests__/migration-export-http.test.ts +6 -6
- package/src/__tests__/migration-import-commit-http.test.ts +8 -6
- package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +151 -0
- package/src/__tests__/model-intents.test.ts +2 -2
- package/src/__tests__/oauth-apps-routes.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +2 -0
- package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
- package/src/__tests__/oauth-providers-routes.test.ts +2 -0
- package/src/__tests__/oauth-store.test.ts +85 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
- package/src/__tests__/onboarding-template-contract.test.ts +6 -13
- package/src/__tests__/openai-provider.test.ts +176 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
- package/src/__tests__/openai-responses-provider.test.ts +1105 -0
- package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
- package/src/__tests__/persona-resolver.test.ts +251 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
- package/src/__tests__/platform.test.ts +92 -1
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
- package/src/__tests__/pricing.test.ts +174 -0
- package/src/__tests__/qdrant-manager.test.ts +29 -8
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
- package/src/__tests__/relationship-state-contract.test.ts +175 -0
- package/src/__tests__/relay-server.test.ts +423 -5
- package/src/__tests__/search-skills-unified.test.ts +118 -0
- package/src/__tests__/secret-scanner-executor.test.ts +4 -0
- package/src/__tests__/secure-keys.test.ts +107 -0
- package/src/__tests__/send-endpoint-busy.test.ts +5 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +49 -0
- package/src/__tests__/settings-routes.test.ts +201 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
- package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
- package/src/__tests__/skills.test.ts +5 -2
- package/src/__tests__/skillssh-files.test.ts +446 -0
- package/src/__tests__/slack-block-formatting.test.ts +110 -0
- package/src/__tests__/slack-channel-config.test.ts +564 -1
- package/src/__tests__/stt-catalog-parity.test.ts +282 -0
- package/src/__tests__/stt-stream-session.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +112 -26
- package/src/__tests__/telephony-stt-routing.test.ts +329 -0
- package/src/__tests__/terminal-tools.test.ts +18 -7
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
- package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +33 -24
- package/src/__tests__/tool-result-truncation.test.ts +36 -0
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
- package/src/__tests__/tts-catalog-parity.test.ts +345 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
- package/src/__tests__/twilio-routes.test.ts +376 -0
- package/src/__tests__/unicode.test.ts +293 -0
- package/src/__tests__/update-bulletin-format.test.ts +59 -0
- package/src/__tests__/update-bulletin.test.ts +206 -5
- package/src/__tests__/usage-routes.test.ts +25 -4
- package/src/__tests__/user-reference.test.ts +46 -61
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
- package/src/__tests__/voice-config-update.test.ts +403 -0
- package/src/__tests__/voice-quality.test.ts +434 -19
- package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
- package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
- package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
- package/src/__tests__/workspace-migration-meets.test.ts +244 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
- package/src/__tests__/workspace-policy.test.ts +2 -0
- package/src/agent/image-optimize.ts +24 -12
- package/src/agent/loop.ts +43 -3
- package/src/backup/__tests__/backup-key.test.ts +152 -0
- package/src/backup/__tests__/backup-worker.test.ts +767 -0
- package/src/backup/__tests__/list-snapshots.test.ts +87 -0
- package/src/backup/__tests__/local-writer.test.ts +218 -0
- package/src/backup/__tests__/offsite-writer.test.ts +641 -0
- package/src/backup/__tests__/paths.test.ts +300 -0
- package/src/backup/__tests__/restore.test.ts +498 -0
- package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
- package/src/backup/__tests__/stream-crypt.test.ts +228 -0
- package/src/backup/backup-key.ts +137 -0
- package/src/backup/backup-worker.ts +459 -0
- package/src/backup/list-snapshots.ts +147 -0
- package/src/backup/local-writer.ts +133 -0
- package/src/backup/offsite-writer.ts +222 -0
- package/src/backup/paths.ts +226 -0
- package/src/backup/restore.ts +322 -0
- package/src/backup/snapshot-lock.ts +431 -0
- package/src/backup/stream-crypt.ts +263 -0
- package/src/bundler/package-resolver.ts +4 -0
- package/src/calls/audio-store.ts +11 -5
- package/src/calls/call-controller.ts +226 -71
- package/src/calls/call-domain.ts +9 -0
- package/src/calls/call-speech-output.ts +190 -0
- package/src/calls/call-transport.ts +77 -0
- package/src/calls/media-stream-audio-transcode.ts +173 -0
- package/src/calls/media-stream-output.ts +660 -0
- package/src/calls/media-stream-parser.ts +300 -0
- package/src/calls/media-stream-protocol.ts +166 -0
- package/src/calls/media-stream-server.ts +592 -0
- package/src/calls/media-stream-stt-session.ts +460 -0
- package/src/calls/media-turn-detector.ts +230 -0
- package/src/calls/relay-server.ts +90 -75
- package/src/calls/resolve-call-tts-provider.ts +136 -0
- package/src/calls/telephony-stt-routing.ts +145 -0
- package/src/calls/tts-call-strategy.ts +161 -0
- package/src/calls/tts-text-sanitizer.ts +32 -16
- package/src/calls/twilio-routes.ts +281 -17
- package/src/calls/voice-quality.ts +78 -35
- package/src/calls/voice-session-bridge.ts +8 -1
- package/src/channels/types.ts +16 -0
- package/src/cli/__tests__/run-assistant-command.ts +11 -1
- package/src/cli/commands/__tests__/backup.test.ts +1165 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
- package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
- package/src/cli/commands/__tests__/email-download.test.ts +16 -1
- package/src/cli/commands/__tests__/email-list.test.ts +22 -4
- package/src/cli/commands/__tests__/email-register.test.ts +4 -4
- package/src/cli/commands/__tests__/email-send.test.ts +37 -4
- package/src/cli/commands/__tests__/email-status.test.ts +5 -1
- package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
- package/src/cli/commands/backup.ts +993 -0
- package/src/cli/commands/conversations.ts +77 -0
- package/src/cli/commands/credentials.ts +0 -1
- package/src/cli/commands/domain.ts +210 -0
- package/src/cli/commands/email.ts +255 -3
- package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
- package/src/cli/commands/oauth/mode.ts +12 -3
- package/src/cli/commands/oauth/providers.ts +15 -0
- package/src/cli/commands/oauth/shared.ts +2 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
- package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
- package/src/cli/program.ts +30 -4
- package/src/config/__tests__/backup-schema.test.ts +134 -0
- package/src/config/assistant-feature-flags.ts +61 -62
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
- package/src/config/bundled-skills/browser/SKILL.md +30 -5
- package/src/config/bundled-skills/browser/TOOLS.json +123 -0
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
- package/src/config/bundled-skills/contacts/SKILL.md +2 -2
- package/src/config/bundled-skills/gmail/SKILL.md +53 -7
- package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
- package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
- package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
- package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/outlook/SKILL.md +2 -2
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
- package/src/config/bundled-skills/slack/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
- package/src/config/bundled-tool-registry.ts +8 -0
- package/src/config/env-registry.ts +24 -0
- package/src/config/env.ts +34 -10
- package/src/config/feature-flag-registry.json +46 -14
- package/src/config/loader.ts +26 -12
- package/src/config/schema.ts +35 -10
- package/src/config/schemas/__tests__/stt.test.ts +43 -0
- package/src/config/schemas/analysis.ts +51 -0
- package/src/config/schemas/backup.ts +72 -0
- package/src/config/schemas/calls.ts +1 -26
- package/src/config/schemas/elevenlabs.ts +0 -59
- package/src/config/schemas/filing.ts +47 -7
- package/src/config/schemas/heartbeat.ts +27 -5
- package/src/config/schemas/host-browser.ts +47 -1
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/memory-lifecycle.ts +14 -2
- package/src/config/schemas/services.ts +44 -0
- package/src/config/schemas/stt.ts +59 -0
- package/src/config/schemas/tts.ts +230 -0
- package/src/config/schemas/updates.ts +14 -0
- package/src/config/skills.ts +4 -0
- package/src/config/types.ts +4 -0
- package/src/contacts/contact-store.ts +56 -11
- package/src/contacts/contacts-write.ts +38 -1
- package/src/context/post-turn-tool-result-truncation.ts +3 -2
- package/src/context/tool-result-truncation.ts +2 -1
- package/src/context/window-manager.ts +45 -12
- package/src/credential-execution/executable-discovery.ts +12 -2
- package/src/credential-execution/process-manager.ts +33 -2
- package/src/credential-health/credential-health-service.ts +366 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
- package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
- package/src/daemon/config-watcher.ts +99 -5
- package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
- package/src/daemon/conversation-agent-loop.ts +101 -24
- package/src/daemon/conversation-error.ts +11 -0
- package/src/daemon/conversation-history.ts +40 -6
- package/src/daemon/conversation-launch.ts +220 -0
- package/src/daemon/conversation-lifecycle.ts +59 -9
- package/src/daemon/conversation-messaging.ts +37 -3
- package/src/daemon/conversation-notifiers.ts +5 -0
- package/src/daemon/conversation-process.ts +581 -19
- package/src/daemon/conversation-queue-manager.ts +24 -0
- package/src/daemon/conversation-runtime-assembly.ts +11 -1
- package/src/daemon/conversation-slash.ts +36 -0
- package/src/daemon/conversation-surfaces.ts +94 -4
- package/src/daemon/conversation-tool-setup.ts +25 -0
- package/src/daemon/conversation-usage.ts +7 -4
- package/src/daemon/conversation.ts +86 -28
- package/src/daemon/handlers/config-slack-channel.ts +269 -94
- package/src/daemon/handlers/conversations.ts +4 -1
- package/src/daemon/handlers/shared.ts +22 -0
- package/src/daemon/handlers/skills.ts +321 -77
- package/src/daemon/host-browser-proxy.ts +2 -1
- package/src/daemon/lifecycle.ts +122 -25
- package/src/daemon/message-protocol.ts +6 -0
- package/src/daemon/message-types/conversations.ts +34 -1
- package/src/daemon/message-types/home.ts +40 -0
- package/src/daemon/message-types/meet.ts +143 -0
- package/src/daemon/message-types/messages.ts +14 -0
- package/src/daemon/message-types/schedules.ts +34 -2
- package/src/daemon/message-types/skills.ts +16 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/server.ts +347 -2
- package/src/daemon/shutdown-handlers.ts +32 -4
- package/src/daemon/shutdown-registry.ts +40 -0
- package/src/daemon/tool-side-effects.ts +9 -0
- package/src/email/html-renderer.ts +76 -0
- package/src/heartbeat/heartbeat-service.ts +93 -7
- package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
- package/src/home/__tests__/emit-feed-event.test.ts +169 -0
- package/src/home/__tests__/feed-scheduler.test.ts +194 -0
- package/src/home/__tests__/feed-types.test.ts +275 -0
- package/src/home/__tests__/feed-writer.test.ts +688 -0
- package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
- package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
- package/src/home/__tests__/progress-formula.test.ts +213 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
- package/src/home/__tests__/rollup-producer.test.ts +398 -0
- package/src/home/assistant-feed-authoring.ts +124 -0
- package/src/home/emit-feed-event.ts +158 -0
- package/src/home/feed-scheduler.ts +247 -0
- package/src/home/feed-types.ts +181 -0
- package/src/home/feed-writer.ts +469 -0
- package/src/home/platform-gmail-digest.ts +163 -0
- package/src/home/progress-formula.ts +86 -0
- package/src/home/relationship-state-writer.ts +824 -0
- package/src/home/relationship-state.ts +143 -0
- package/src/home/rollup-producer.ts +384 -0
- package/src/hooks/runner.ts +7 -0
- package/src/inbound/platform-callback-registration.ts +12 -3
- package/src/inbound/public-ingress-urls.ts +12 -0
- package/src/instrument.ts +1 -1
- package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
- package/src/ipc/cli-client.ts +151 -0
- package/src/ipc/cli-server.ts +234 -0
- package/src/ipc/gateway-client.ts +180 -0
- package/src/ipc/routes/index.ts +5 -0
- package/src/ipc/routes/wake-conversation.ts +19 -0
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
- package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
- package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
- package/src/memory/app-store.ts +1 -1
- package/src/memory/attachments-store.ts +70 -0
- package/src/memory/auto-analysis-enqueue.ts +127 -0
- package/src/memory/auto-analysis-guard.ts +27 -0
- package/src/memory/cleanup-schedule-state.ts +37 -0
- package/src/memory/conversation-analyze-job.ts +73 -0
- package/src/memory/conversation-crud.ts +99 -0
- package/src/memory/conversation-disk-view.ts +7 -0
- package/src/memory/conversation-group-migration.ts +34 -2
- package/src/memory/conversation-queries.ts +6 -5
- package/src/memory/db-init.ts +6 -0
- package/src/memory/db-maintenance.ts +108 -0
- package/src/memory/db.ts +1 -0
- package/src/memory/graph/conversation-graph-memory.ts +15 -0
- package/src/memory/graph/extraction.test.ts +23 -0
- package/src/memory/graph/extraction.ts +8 -0
- package/src/memory/graph/retriever.ts +27 -18
- package/src/memory/graph/scoring.test.ts +186 -0
- package/src/memory/graph/scoring.ts +31 -1
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/group-crud.ts +6 -1
- package/src/memory/indexer.ts +95 -16
- package/src/memory/job-handlers/cleanup.ts +11 -8
- package/src/memory/job-handlers/conversation-starters.ts +16 -10
- package/src/memory/jobs-store.ts +64 -4
- package/src/memory/jobs-worker.ts +22 -9
- package/src/memory/llm-usage-store.ts +92 -56
- package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
- package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/qdrant-manager.ts +43 -16
- package/src/memory/schema/conversations.ts +2 -0
- package/src/memory/schema/oauth.ts +3 -0
- package/src/memory/usage-buckets.ts +396 -0
- package/src/messaging/providers/gmail/client.ts +57 -6
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
- package/src/messaging/providers/slack/adapter.ts +143 -38
- package/src/messaging/providers/slack/client.ts +16 -0
- package/src/messaging/providers/slack/types.ts +4 -0
- package/src/notifications/decision-engine.ts +3 -3
- package/src/notifications/signal.ts +5 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
- package/src/oauth/byo-connection.test.ts +18 -1
- package/src/oauth/byo-connection.ts +3 -1
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.ts +6 -2
- package/src/oauth/connection.ts +2 -0
- package/src/oauth/oauth-store.ts +9 -0
- package/src/oauth/platform-connection.test.ts +98 -0
- package/src/oauth/platform-connection.ts +52 -31
- package/src/oauth/seed-providers.ts +7 -0
- package/src/permissions/checker.ts +16 -6
- package/src/permissions/defaults.ts +49 -1
- package/src/permissions/trust-store.ts +3 -3
- package/src/permissions/workspace-policy.ts +3 -0
- package/src/platform/client.test.ts +10 -0
- package/src/platform/sync-identity.ts +129 -0
- package/src/prompts/persona-resolver.ts +126 -2
- package/src/prompts/system-prompt.ts +59 -18
- package/src/prompts/templates/BOOTSTRAP.md +5 -5
- package/src/prompts/templates/SOUL.md +3 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/templates/channels/slack.md +20 -0
- package/src/prompts/update-bulletin-format.ts +26 -9
- package/src/prompts/update-bulletin.ts +34 -23
- package/src/prompts/user-reference.ts +20 -17
- package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
- package/src/providers/anthropic/client.ts +157 -61
- package/src/providers/fireworks/client.ts +2 -2
- package/src/providers/gemini/client.ts +9 -1
- package/src/providers/model-catalog.ts +6 -0
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/ollama/client.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +474 -0
- package/src/providers/openai/client.ts +25 -440
- package/src/providers/openai/responses-provider.ts +502 -0
- package/src/providers/openrouter/client.ts +101 -4
- package/src/providers/provider-secret-catalog.ts +139 -0
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +14 -3
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
- package/src/providers/speech-to-text/deepgram.test.ts +332 -0
- package/src/providers/speech-to-text/deepgram.ts +115 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
- package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
- package/src/providers/speech-to-text/google-gemini.ts +101 -0
- package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
- package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
- package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
- package/src/providers/speech-to-text/openai-whisper.ts +63 -33
- package/src/providers/speech-to-text/provider-catalog.ts +306 -0
- package/src/providers/speech-to-text/resolve.ts +386 -6
- package/src/providers/types.ts +9 -0
- package/src/runtime/AGENTS.md +43 -1
- package/src/runtime/__tests__/agent-wake.test.ts +831 -0
- package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
- package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
- package/src/runtime/agent-wake.ts +512 -0
- package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
- package/src/runtime/auth/route-policy.ts +30 -5
- package/src/runtime/auth/token-service.ts +56 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/capability-tokens.ts +10 -10
- package/src/runtime/channel-invite-transport.ts +1 -1
- package/src/runtime/channel-invite-transports/email.ts +14 -6
- package/src/runtime/channel-readiness-service.ts +12 -22
- package/src/runtime/chrome-extension-registry.ts +38 -2
- package/src/runtime/http-server.ts +395 -10
- package/src/runtime/http-types.ts +6 -2
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
- package/src/runtime/migrations/migration-transport.ts +1 -0
- package/src/runtime/migrations/migration-wizard.ts +1 -0
- package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
- package/src/runtime/migrations/vbundle-importer.ts +34 -0
- package/src/runtime/pending-interactions.ts +0 -11
- package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
- package/src/runtime/routes/app-management-routes.ts +12 -18
- package/src/runtime/routes/attachment-routes.test.ts +9 -3
- package/src/runtime/routes/attachment-routes.ts +216 -17
- package/src/runtime/routes/backup-routes.ts +519 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
- package/src/runtime/routes/btw-routes.ts +8 -6
- package/src/runtime/routes/contact-routes.test.ts +298 -0
- package/src/runtime/routes/contact-routes.ts +132 -5
- package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
- package/src/runtime/routes/conversation-management-routes.ts +115 -0
- package/src/runtime/routes/conversation-routes.ts +367 -146
- package/src/runtime/routes/filing-routes.ts +93 -0
- package/src/runtime/routes/home-feed-routes.ts +334 -0
- package/src/runtime/routes/home-state-routes.ts +138 -0
- package/src/runtime/routes/host-browser-routes.ts +3 -14
- package/src/runtime/routes/identity-intro-cache.ts +7 -3
- package/src/runtime/routes/identity-routes.ts +3 -17
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
- package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
- package/src/runtime/routes/integrations/slack/channel.ts +11 -3
- package/src/runtime/routes/integrations/slack/share.ts +45 -7
- package/src/runtime/routes/llm-context-normalization.ts +303 -0
- package/src/runtime/routes/memory-item-routes.test.ts +3 -2
- package/src/runtime/routes/migration-routes.ts +40 -5
- package/src/runtime/routes/settings-routes.ts +22 -5
- package/src/runtime/routes/skills-routes.ts +76 -7
- package/src/runtime/routes/stt-routes.ts +233 -0
- package/src/runtime/routes/surface-action-routes.ts +41 -2
- package/src/runtime/routes/tts-routes.ts +108 -24
- package/src/runtime/routes/usage-routes.ts +30 -2
- package/src/runtime/routes/user-route-dispatcher.ts +50 -5
- package/src/runtime/routes/user-routes.ts +13 -1
- package/src/runtime/routes/work-items-routes.ts +8 -1
- package/src/runtime/runtime-mode.ts +33 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
- package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
- package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
- package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
- package/src/runtime/services/analyze-conversation.ts +344 -0
- package/src/runtime/services/analyze-deps-singleton.ts +32 -0
- package/src/runtime/services/auto-analysis-prompt.ts +55 -0
- package/src/runtime/skill-route-registry.ts +49 -0
- package/src/runtime/slack-block-formatting.ts +437 -10
- package/src/schedule/scheduler.ts +50 -0
- package/src/security/oauth2.ts +26 -4
- package/src/security/secure-keys.ts +25 -2
- package/src/security/token-manager.ts +8 -0
- package/src/sequence/engine.ts +23 -0
- package/src/sequence/types.ts +1 -1
- package/src/skills/catalog-files.ts +64 -2
- package/src/skills/category-inference.ts +122 -0
- package/src/skills/clawhub-files.ts +213 -0
- package/src/skills/clawhub.ts +84 -23
- package/src/skills/skill-file-provider.ts +40 -0
- package/src/skills/skillssh-files.ts +395 -0
- package/src/skills/skillssh-registry.ts +4 -4
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
- package/src/stt/__tests__/types.test.ts +89 -0
- package/src/stt/daemon-batch-transcriber.ts +195 -0
- package/src/stt/stt-stream-session.ts +499 -0
- package/src/stt/types.ts +330 -0
- package/src/stt/wav-encoder.test.ts +373 -0
- package/src/stt/wav-encoder.ts +175 -0
- package/src/subagent/manager.ts +38 -14
- package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
- package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
- package/src/tools/browser/browser-execution.ts +1163 -23
- package/src/tools/browser/browser-manager.ts +45 -0
- package/src/tools/browser/browser-mode-constants.ts +12 -0
- package/src/tools/browser/browser-mode.ts +92 -0
- package/src/tools/browser/browser-status-constants.ts +33 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
- package/src/tools/browser/cdp-client/errors.ts +15 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
- package/src/tools/browser/cdp-client/factory.ts +797 -87
- package/src/tools/browser/cdp-client/index.ts +16 -2
- package/src/tools/browser/cdp-client/types.ts +68 -0
- package/src/tools/credentials/vault.ts +35 -6
- package/src/tools/network/web-fetch.ts +5 -2
- package/src/tools/network/web-search.ts +5 -2
- package/src/tools/shared/shell-output.ts +3 -1
- package/src/tools/side-effects.ts +2 -0
- package/src/tools/skills/sandbox-runner.ts +3 -2
- package/src/tools/terminal/safe-env.ts +10 -2
- package/src/tools/terminal/shell.ts +15 -4
- package/src/tools/tool-manifest.ts +21 -0
- package/src/tools/types.ts +17 -0
- package/src/tools/ui-surface/definitions.ts +6 -1
- package/src/tts/__tests__/provider-adapters.test.ts +834 -0
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
- package/src/tts/__tests__/provider-catalog.test.ts +183 -0
- package/src/tts/__tests__/provider-registry.test.ts +90 -0
- package/src/tts/provider-catalog.ts +201 -0
- package/src/tts/provider-registry.ts +73 -0
- package/src/tts/providers/deepgram-provider.ts +219 -0
- package/src/tts/providers/elevenlabs-provider.ts +211 -0
- package/src/tts/providers/fish-audio-provider.ts +183 -0
- package/src/tts/providers/index.ts +42 -0
- package/src/tts/providers/register-builtins.ts +130 -0
- package/src/tts/synthesize-text.ts +110 -0
- package/src/tts/tts-config-resolver.ts +78 -0
- package/src/tts/types.ts +153 -0
- package/src/types/onboarding-context.ts +7 -0
- package/src/util/abort-reasons.ts +58 -0
- package/src/util/device-id.ts +32 -16
- package/src/util/errors.ts +9 -1
- package/src/util/platform.ts +54 -10
- package/src/util/pricing.ts +66 -3
- package/src/util/spawn.ts +1 -1
- package/src/util/truncate.ts +4 -2
- package/src/util/unicode.ts +201 -0
- package/src/version.ts +19 -24
- package/src/watcher/engine.ts +23 -0
- package/src/watcher/watcher-store.ts +31 -0
- package/src/workspace/migrations/003-seed-device-id.ts +9 -3
- package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
- package/src/workspace/migrations/029-seed-pkb.ts +1 -1
- package/src/workspace/migrations/031-drop-user-md.ts +317 -0
- package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
- package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
- package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
- package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
- package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
- package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
- package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/top-level-renderer.ts +13 -1
- package/src/workspace/turn-commit.ts +31 -0
- package/src/__tests__/email-cli.test.ts +0 -297
- package/src/__tests__/email-service-config-fallback.test.ts +0 -102
- package/src/cli/commands/browser-relay.ts +0 -466
- package/src/email/guardrails.ts +0 -221
- package/src/email/provider.ts +0 -117
- package/src/email/providers/agentmail.ts +0 -361
- package/src/email/providers/index.ts +0 -65
- package/src/email/service.ts +0 -384
- package/src/email/types.ts +0 -126
- package/src/prompts/templates/USER.md +0 -13
- package/src/providers/speech-to-text/types.ts +0 -17
- package/src/runtime/routes/browser-cdp-routes.ts +0 -229
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media-stream call server: binds WebSocket lifecycle to call-session
|
|
3
|
+
* lifecycle and wires STT session callbacks to controller entry points.
|
|
4
|
+
*
|
|
5
|
+
* Each active media-stream call has a single `MediaStreamCallSession`
|
|
6
|
+
* instance that:
|
|
7
|
+
*
|
|
8
|
+
* 1. Owns a {@link MediaStreamSttSession} for ingesting raw audio and
|
|
9
|
+
* producing transcripts.
|
|
10
|
+
* 2. Owns a {@link MediaStreamOutput} for sending synthesized audio
|
|
11
|
+
* and lifecycle signals back to Twilio.
|
|
12
|
+
* 3. Creates and registers a {@link CallController} to process
|
|
13
|
+
* transcripts through the conversation pipeline.
|
|
14
|
+
*
|
|
15
|
+
* The server is registered on `/v1/calls/media-stream` and provides
|
|
16
|
+
* full bidirectional call support: inbound audio is transcribed via
|
|
17
|
+
* STT and outbound assistant speech is synthesized via TTS and
|
|
18
|
+
* streamed as media frames back to Twilio.
|
|
19
|
+
*
|
|
20
|
+
* Lifecycle:
|
|
21
|
+
* - WebSocket `open` -> extract callSessionId from upgrade params,
|
|
22
|
+
* create `MediaStreamCallSession`.
|
|
23
|
+
* - Media stream `start` event -> capture streamSid/callSid, wire
|
|
24
|
+
* output adapter, create controller.
|
|
25
|
+
* - Media stream `media` events -> forwarded to STT session for
|
|
26
|
+
* turn detection and transcription.
|
|
27
|
+
* - STT `onTranscriptFinal` -> routed to controller's
|
|
28
|
+
* `handleCallerUtterance()`.
|
|
29
|
+
* - STT `onSpeechStart` -> barge-in: clears outbound audio queue
|
|
30
|
+
* and interrupts the in-flight LLM turn via the controller.
|
|
31
|
+
* - Media stream `stop` event / WebSocket close -> finalize call.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import type { ServerWebSocket } from "bun";
|
|
35
|
+
|
|
36
|
+
import { revokeScopedApprovalGrantsForContext } from "../memory/scoped-approval-grants.js";
|
|
37
|
+
import { toTrustContext } from "../runtime/actor-trust-resolver.js";
|
|
38
|
+
import { getLogger } from "../util/logger.js";
|
|
39
|
+
import { CallController } from "./call-controller.js";
|
|
40
|
+
import { addPointerMessage, formatDuration } from "./call-pointer-messages.js";
|
|
41
|
+
import { speakSystemPrompt } from "./call-speech-output.js";
|
|
42
|
+
import {
|
|
43
|
+
fireCallTranscriptNotifier,
|
|
44
|
+
registerCallController,
|
|
45
|
+
unregisterCallController,
|
|
46
|
+
} from "./call-state.js";
|
|
47
|
+
import { isTerminalState } from "./call-state-machine.js";
|
|
48
|
+
import {
|
|
49
|
+
getCallSession,
|
|
50
|
+
recordCallEvent,
|
|
51
|
+
updateCallSession,
|
|
52
|
+
} from "./call-store.js";
|
|
53
|
+
import { finalizeCall } from "./finalize-call.js";
|
|
54
|
+
import { MediaStreamOutput } from "./media-stream-output.js";
|
|
55
|
+
import { parseMediaStreamFrame } from "./media-stream-parser.js";
|
|
56
|
+
import {
|
|
57
|
+
MediaStreamSttSession,
|
|
58
|
+
type MediaStreamSttSessionCallbacks,
|
|
59
|
+
type MediaStreamSttSessionConfig,
|
|
60
|
+
} from "./media-stream-stt-session.js";
|
|
61
|
+
import { routeSetup } from "./relay-setup-router.js";
|
|
62
|
+
|
|
63
|
+
const log = getLogger("media-stream-server");
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Active sessions registry (keyed by callSessionId)
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Active media-stream call sessions keyed by callSessionId.
|
|
71
|
+
*
|
|
72
|
+
* Exported for use in `call-domain.ts` (cancel call cleanup) and for
|
|
73
|
+
* test assertions. Not intended for general consumption.
|
|
74
|
+
*/
|
|
75
|
+
export const activeMediaStreamSessions = new Map<
|
|
76
|
+
string,
|
|
77
|
+
MediaStreamCallSession
|
|
78
|
+
>();
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Session
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
export class MediaStreamCallSession {
|
|
85
|
+
readonly callSessionId: string;
|
|
86
|
+
private output: MediaStreamOutput;
|
|
87
|
+
private sttSession: MediaStreamSttSession;
|
|
88
|
+
private controller: CallController | null = null;
|
|
89
|
+
private streamSid: string | null = null;
|
|
90
|
+
private callSid: string | null = null;
|
|
91
|
+
private disposed = false;
|
|
92
|
+
|
|
93
|
+
// ── Operational diagnostics counters ──────────────────────────────
|
|
94
|
+
/** Number of barge-in attempts that were accepted (assistant was speaking). */
|
|
95
|
+
private bargeInAccepted = 0;
|
|
96
|
+
/** Number of barge-in attempts that were ignored (assistant not speaking). */
|
|
97
|
+
private bargeInIgnored = 0;
|
|
98
|
+
/** Number of turn-start transitions detected by the STT session. */
|
|
99
|
+
private turnStarts = 0;
|
|
100
|
+
/** Number of transcript finals produced (non-empty). */
|
|
101
|
+
private transcriptFinalsProduced = 0;
|
|
102
|
+
|
|
103
|
+
constructor(
|
|
104
|
+
ws: ServerWebSocket<unknown>,
|
|
105
|
+
callSessionId: string,
|
|
106
|
+
sttConfig?: MediaStreamSttSessionConfig,
|
|
107
|
+
) {
|
|
108
|
+
this.callSessionId = callSessionId;
|
|
109
|
+
|
|
110
|
+
// Create output adapter with a placeholder streamSid — it will be
|
|
111
|
+
// set when the `start` event arrives.
|
|
112
|
+
this.output = new MediaStreamOutput(ws, "");
|
|
113
|
+
|
|
114
|
+
// Create STT session with callbacks wired to the controller.
|
|
115
|
+
const callbacks: MediaStreamSttSessionCallbacks = {
|
|
116
|
+
onSpeechStart: () => this.handleSpeechStart(),
|
|
117
|
+
onTranscriptFinal: (text, durationMs) =>
|
|
118
|
+
this.handleTranscriptFinal(text, durationMs),
|
|
119
|
+
onDtmf: (digit) => this.handleDtmf(digit),
|
|
120
|
+
onStop: () => this.handleStreamStop(),
|
|
121
|
+
onError: (category, message) => this.handleSttError(category, message),
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
this.sttSession = new MediaStreamSttSession(sttConfig ?? {}, callbacks);
|
|
125
|
+
|
|
126
|
+
log.info({ callSessionId }, "Media stream call session created");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get the output adapter (for test assertions).
|
|
131
|
+
*/
|
|
132
|
+
getOutput(): MediaStreamOutput {
|
|
133
|
+
return this.output;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get the controller (for test assertions).
|
|
138
|
+
*/
|
|
139
|
+
getController(): CallController | null {
|
|
140
|
+
return this.controller;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Feed a raw WebSocket message into the session.
|
|
145
|
+
*
|
|
146
|
+
* The message is parsed to intercept `start` events (for session
|
|
147
|
+
* bootstrapping) before being forwarded to the STT session for
|
|
148
|
+
* audio processing.
|
|
149
|
+
*/
|
|
150
|
+
handleMessage(raw: string): void {
|
|
151
|
+
if (this.disposed) return;
|
|
152
|
+
|
|
153
|
+
// Intercept `start` to bootstrap the session before forwarding.
|
|
154
|
+
const parseResult = parseMediaStreamFrame(raw);
|
|
155
|
+
if (parseResult.ok && parseResult.event.event === "start") {
|
|
156
|
+
this.handleStart(parseResult.event);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Always forward to the STT session (it handles all event types).
|
|
160
|
+
this.sttSession.handleMessage(raw);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Handle WebSocket close. Finalizes the call session if not already
|
|
165
|
+
* in a terminal state.
|
|
166
|
+
*/
|
|
167
|
+
handleTransportClosed(code?: number, reason?: string): void {
|
|
168
|
+
if (this.disposed) return;
|
|
169
|
+
|
|
170
|
+
const session = getCallSession(this.callSessionId);
|
|
171
|
+
if (!session) return;
|
|
172
|
+
if (isTerminalState(session.status)) return;
|
|
173
|
+
|
|
174
|
+
const isNormalClose = code === 1000;
|
|
175
|
+
const terminationReason = isNormalClose ? "normal_stop" : "premature_abort";
|
|
176
|
+
log.info(
|
|
177
|
+
{
|
|
178
|
+
callSessionId: this.callSessionId,
|
|
179
|
+
terminationReason,
|
|
180
|
+
closeCode: code,
|
|
181
|
+
closeReason: reason,
|
|
182
|
+
turnStarts: this.turnStarts,
|
|
183
|
+
transcriptFinalsProduced: this.transcriptFinalsProduced,
|
|
184
|
+
bargeInAccepted: this.bargeInAccepted,
|
|
185
|
+
bargeInIgnored: this.bargeInIgnored,
|
|
186
|
+
},
|
|
187
|
+
"Media stream transport closed — session diagnostics",
|
|
188
|
+
);
|
|
189
|
+
if (isNormalClose) {
|
|
190
|
+
updateCallSession(this.callSessionId, {
|
|
191
|
+
status: "completed",
|
|
192
|
+
endedAt: Date.now(),
|
|
193
|
+
});
|
|
194
|
+
recordCallEvent(this.callSessionId, "call_ended", {
|
|
195
|
+
reason: reason || "media_stream_closed",
|
|
196
|
+
closeCode: code,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (session.initiatedFromConversationId) {
|
|
200
|
+
const durationMs = session.startedAt
|
|
201
|
+
? Date.now() - session.startedAt
|
|
202
|
+
: 0;
|
|
203
|
+
addPointerMessage(
|
|
204
|
+
session.initiatedFromConversationId,
|
|
205
|
+
"completed",
|
|
206
|
+
session.toNumber,
|
|
207
|
+
{
|
|
208
|
+
duration: durationMs > 0 ? formatDuration(durationMs) : undefined,
|
|
209
|
+
},
|
|
210
|
+
).catch((err) => {
|
|
211
|
+
log.warn(
|
|
212
|
+
{ conversationId: session.initiatedFromConversationId, err },
|
|
213
|
+
"Skipping pointer write — origin conversation may no longer exist",
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
const detail =
|
|
219
|
+
reason ||
|
|
220
|
+
(code ? `media_stream_closed_${code}` : "media_stream_closed_abnormal");
|
|
221
|
+
updateCallSession(this.callSessionId, {
|
|
222
|
+
status: "failed",
|
|
223
|
+
endedAt: Date.now(),
|
|
224
|
+
lastError: `Media stream WebSocket closed unexpectedly: ${detail}`,
|
|
225
|
+
});
|
|
226
|
+
recordCallEvent(this.callSessionId, "call_failed", {
|
|
227
|
+
reason: detail,
|
|
228
|
+
closeCode: code,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
if (session.initiatedFromConversationId) {
|
|
232
|
+
addPointerMessage(
|
|
233
|
+
session.initiatedFromConversationId,
|
|
234
|
+
"failed",
|
|
235
|
+
session.toNumber,
|
|
236
|
+
{ reason: detail },
|
|
237
|
+
).catch((err) => {
|
|
238
|
+
log.warn(
|
|
239
|
+
{ conversationId: session.initiatedFromConversationId, err },
|
|
240
|
+
"Skipping pointer write — origin conversation may no longer exist",
|
|
241
|
+
);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Revoke any scoped approval grants bound to this call session.
|
|
247
|
+
// Revoke by both callSessionId and conversationId because the
|
|
248
|
+
// guardian-approval-interception minting path sets callSessionId: null
|
|
249
|
+
// but always sets conversationId.
|
|
250
|
+
try {
|
|
251
|
+
revokeScopedApprovalGrantsForContext({
|
|
252
|
+
callSessionId: this.callSessionId,
|
|
253
|
+
});
|
|
254
|
+
revokeScopedApprovalGrantsForContext({
|
|
255
|
+
conversationId: session.conversationId,
|
|
256
|
+
});
|
|
257
|
+
} catch (err) {
|
|
258
|
+
log.warn(
|
|
259
|
+
{ err, callSessionId: this.callSessionId },
|
|
260
|
+
"Failed to revoke scoped grants on media-stream transport close",
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
finalizeCall(this.callSessionId, session.conversationId);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Dispose of the session, cleaning up all resources.
|
|
269
|
+
*/
|
|
270
|
+
destroy(): void {
|
|
271
|
+
if (this.disposed) return;
|
|
272
|
+
this.disposed = true;
|
|
273
|
+
|
|
274
|
+
this.sttSession.dispose();
|
|
275
|
+
|
|
276
|
+
if (this.controller) {
|
|
277
|
+
this.controller.destroy();
|
|
278
|
+
unregisterCallController(this.callSessionId);
|
|
279
|
+
this.controller = null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
this.output.markClosed();
|
|
283
|
+
|
|
284
|
+
log.info(
|
|
285
|
+
{
|
|
286
|
+
callSessionId: this.callSessionId,
|
|
287
|
+
turnStarts: this.turnStarts,
|
|
288
|
+
transcriptFinalsProduced: this.transcriptFinalsProduced,
|
|
289
|
+
bargeInAccepted: this.bargeInAccepted,
|
|
290
|
+
bargeInIgnored: this.bargeInIgnored,
|
|
291
|
+
},
|
|
292
|
+
"Media stream call session destroyed",
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ── Internal: media-stream event handlers ─────────────────────────
|
|
297
|
+
|
|
298
|
+
private handleStart(
|
|
299
|
+
event: import("./media-stream-protocol.js").MediaStreamStartEvent,
|
|
300
|
+
): void {
|
|
301
|
+
this.streamSid = event.streamSid;
|
|
302
|
+
this.callSid = event.start.callSid;
|
|
303
|
+
|
|
304
|
+
// Update the output adapter with the real streamSid.
|
|
305
|
+
this.output.setStreamSid(event.streamSid);
|
|
306
|
+
|
|
307
|
+
// Update the call session with the provider call SID.
|
|
308
|
+
const session = getCallSession(this.callSessionId);
|
|
309
|
+
if (session) {
|
|
310
|
+
const updates: Parameters<typeof updateCallSession>[1] = {
|
|
311
|
+
providerCallSid: event.start.callSid,
|
|
312
|
+
};
|
|
313
|
+
if (
|
|
314
|
+
!isTerminalState(session.status) &&
|
|
315
|
+
session.status !== "in_progress" &&
|
|
316
|
+
session.status !== "waiting_on_user"
|
|
317
|
+
) {
|
|
318
|
+
updates.status = "in_progress";
|
|
319
|
+
if (!session.startedAt) updates.startedAt = Date.now();
|
|
320
|
+
}
|
|
321
|
+
updateCallSession(this.callSessionId, updates);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
recordCallEvent(this.callSessionId, "call_connected", {
|
|
325
|
+
callSid: event.start.callSid,
|
|
326
|
+
streamSid: event.streamSid,
|
|
327
|
+
encoding: event.start.mediaFormat.encoding,
|
|
328
|
+
sampleRate: event.start.mediaFormat.sampleRate,
|
|
329
|
+
transport: "media-stream",
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// ── Setup-policy routing ────────────────────────────────────────
|
|
333
|
+
// Run the same routeSetup() that the ConversationRelay path uses
|
|
334
|
+
// to enforce ACL/deny/escalate, verification, and invite flows.
|
|
335
|
+
// The media-stream transport does not support interactive sub-flows
|
|
336
|
+
// (DTMF entry, name capture, guardian wait), so non-normal outcomes
|
|
337
|
+
// are rejected gracefully with a TTS message and session teardown.
|
|
338
|
+
const from = session?.fromNumber ?? "";
|
|
339
|
+
const to = session?.toNumber ?? "";
|
|
340
|
+
|
|
341
|
+
const { outcome, resolved } = routeSetup({
|
|
342
|
+
callSessionId: this.callSessionId,
|
|
343
|
+
session: session ?? null,
|
|
344
|
+
from,
|
|
345
|
+
to,
|
|
346
|
+
customParameters: event.start.customParameters,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
log.info(
|
|
350
|
+
{
|
|
351
|
+
callSessionId: this.callSessionId,
|
|
352
|
+
streamSid: this.streamSid,
|
|
353
|
+
callSid: this.callSid,
|
|
354
|
+
setupAction: outcome.action,
|
|
355
|
+
},
|
|
356
|
+
"Media stream session started",
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
switch (outcome.action) {
|
|
360
|
+
case "normal_call": {
|
|
361
|
+
// Create the call controller only for normal calls. Deny and
|
|
362
|
+
// unsupported-flow paths speak a message via the output adapter
|
|
363
|
+
// directly and don't need a controller. Creating it eagerly
|
|
364
|
+
// would start duration/silence timers that leak when the
|
|
365
|
+
// session is torn down before destroy() runs.
|
|
366
|
+
const initialTrustContext = toTrustContext(
|
|
367
|
+
resolved.actorTrust,
|
|
368
|
+
resolved.otherPartyNumber,
|
|
369
|
+
);
|
|
370
|
+
this.controller = new CallController(
|
|
371
|
+
this.callSessionId,
|
|
372
|
+
this.output,
|
|
373
|
+
session?.task ?? null,
|
|
374
|
+
{
|
|
375
|
+
assistantId: resolved.assistantId,
|
|
376
|
+
trustContext: initialTrustContext,
|
|
377
|
+
},
|
|
378
|
+
);
|
|
379
|
+
registerCallController(this.callSessionId, this.controller);
|
|
380
|
+
|
|
381
|
+
// Fire the initial greeting.
|
|
382
|
+
this.controller.startInitialGreeting().catch((err) => {
|
|
383
|
+
log.error(
|
|
384
|
+
{ err, callSessionId: this.callSessionId },
|
|
385
|
+
"Failed to start initial greeting on media-stream session",
|
|
386
|
+
);
|
|
387
|
+
});
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
case "deny":
|
|
392
|
+
// Deny — speak the denial message and tear down.
|
|
393
|
+
log.warn(
|
|
394
|
+
{
|
|
395
|
+
callSessionId: this.callSessionId,
|
|
396
|
+
reason: outcome.logReason,
|
|
397
|
+
},
|
|
398
|
+
"Media-stream setup denied by ACL policy",
|
|
399
|
+
);
|
|
400
|
+
recordCallEvent(this.callSessionId, "inbound_acl_denied", {
|
|
401
|
+
from,
|
|
402
|
+
trustClass: resolved.actorTrust.trustClass,
|
|
403
|
+
});
|
|
404
|
+
updateCallSession(this.callSessionId, {
|
|
405
|
+
status: "failed",
|
|
406
|
+
endedAt: Date.now(),
|
|
407
|
+
lastError: outcome.logReason,
|
|
408
|
+
});
|
|
409
|
+
// Run finalization now because handleTransportClosed will see
|
|
410
|
+
// terminal status and exit early when the WebSocket closes.
|
|
411
|
+
this.runFinalizationAndGrantCleanup(session);
|
|
412
|
+
void speakSystemPrompt(this.output, outcome.message).finally(() => {
|
|
413
|
+
setTimeout(() => this.output.endSession(outcome.logReason), 3000);
|
|
414
|
+
});
|
|
415
|
+
return;
|
|
416
|
+
|
|
417
|
+
default:
|
|
418
|
+
// All interactive sub-flows (verification, invite_redemption,
|
|
419
|
+
// name_capture, callee_verification, outbound_verification) are
|
|
420
|
+
// not supported on the media-stream transport. The TwiML preflight
|
|
421
|
+
// in twilio-routes.ts should have caught this and fallen back to
|
|
422
|
+
// ConversationRelay — reaching here indicates the preflight was
|
|
423
|
+
// bypassed or a new setup action was added without updating the
|
|
424
|
+
// preflight guard. Speak a generic apology and end the session
|
|
425
|
+
// rather than silently bypassing policy enforcement.
|
|
426
|
+
log.error(
|
|
427
|
+
{
|
|
428
|
+
callSessionId: this.callSessionId,
|
|
429
|
+
action: outcome.action,
|
|
430
|
+
},
|
|
431
|
+
"Media-stream transport received unsupported setup flow — preflight guard should have prevented this",
|
|
432
|
+
);
|
|
433
|
+
recordCallEvent(this.callSessionId, "call_failed", {
|
|
434
|
+
reason: `Setup flow '${outcome.action}' not supported on media-stream transport (preflight guard bypass)`,
|
|
435
|
+
transport: "media-stream",
|
|
436
|
+
});
|
|
437
|
+
updateCallSession(this.callSessionId, {
|
|
438
|
+
status: "failed",
|
|
439
|
+
endedAt: Date.now(),
|
|
440
|
+
lastError: `Setup flow '${outcome.action}' not supported on media-stream transport — preflight guard should have prevented this`,
|
|
441
|
+
});
|
|
442
|
+
// Run finalization now because handleTransportClosed will see
|
|
443
|
+
// terminal status and exit early when the WebSocket closes.
|
|
444
|
+
this.runFinalizationAndGrantCleanup(session);
|
|
445
|
+
void speakSystemPrompt(
|
|
446
|
+
this.output,
|
|
447
|
+
"Sorry, this call requires additional verification that isn't available right now. Please try calling back. Goodbye.",
|
|
448
|
+
).finally(() => {
|
|
449
|
+
setTimeout(
|
|
450
|
+
() =>
|
|
451
|
+
this.output.endSession(
|
|
452
|
+
`Unsupported setup flow: ${outcome.action} (preflight guard bypass)`,
|
|
453
|
+
),
|
|
454
|
+
3000,
|
|
455
|
+
);
|
|
456
|
+
});
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// ── Finalization helper for early-teardown paths ─────────────────
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Run scoped-grant revocation and call finalization inline. Used by
|
|
465
|
+
* the deny and unsupported-flow branches which set terminal status
|
|
466
|
+
* before `endSession()`. When the WebSocket subsequently closes,
|
|
467
|
+
* {@link handleTransportClosed} sees the terminal status and exits
|
|
468
|
+
* early — so we must perform cleanup here to avoid leaking grants
|
|
469
|
+
* and skipping `finalizeCall()` side-effects.
|
|
470
|
+
*/
|
|
471
|
+
private runFinalizationAndGrantCleanup(
|
|
472
|
+
session: ReturnType<typeof getCallSession>,
|
|
473
|
+
): void {
|
|
474
|
+
try {
|
|
475
|
+
revokeScopedApprovalGrantsForContext({
|
|
476
|
+
callSessionId: this.callSessionId,
|
|
477
|
+
});
|
|
478
|
+
if (session?.conversationId) {
|
|
479
|
+
revokeScopedApprovalGrantsForContext({
|
|
480
|
+
conversationId: session.conversationId,
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
} catch (err) {
|
|
484
|
+
log.warn(
|
|
485
|
+
{ err, callSessionId: this.callSessionId },
|
|
486
|
+
"Failed to revoke scoped grants on early teardown path",
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (session?.conversationId) {
|
|
491
|
+
finalizeCall(this.callSessionId, session.conversationId);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// ── STT callbacks ─────────────────────────────────────────────────
|
|
496
|
+
|
|
497
|
+
private handleSpeechStart(): void {
|
|
498
|
+
this.turnStarts++;
|
|
499
|
+
|
|
500
|
+
// Barge-in: clear queued outbound audio and abort the in-flight LLM
|
|
501
|
+
// turn only when the assistant is actively speaking. Uses the gated
|
|
502
|
+
// handleBargeIn path so initial inbound audio frames do not cancel a
|
|
503
|
+
// still-starting initial turn.
|
|
504
|
+
//
|
|
505
|
+
// clearAudio runs BEFORE handleBargeIn so that the end-of-turn mark
|
|
506
|
+
// enqueued by handleInterrupt (called within handleBargeIn) is not
|
|
507
|
+
// wiped by the queue flush.
|
|
508
|
+
if (this.output && this.controller) {
|
|
509
|
+
this.output.clearAudio();
|
|
510
|
+
const accepted = this.controller.handleBargeIn();
|
|
511
|
+
if (accepted) {
|
|
512
|
+
this.bargeInAccepted++;
|
|
513
|
+
log.info(
|
|
514
|
+
{ callSessionId: this.callSessionId },
|
|
515
|
+
"Media-stream barge-in accepted — cleared outbound audio",
|
|
516
|
+
);
|
|
517
|
+
} else {
|
|
518
|
+
this.bargeInIgnored++;
|
|
519
|
+
log.debug(
|
|
520
|
+
{ callSessionId: this.callSessionId },
|
|
521
|
+
"Media-stream barge-in ignored — assistant not speaking",
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
private handleTranscriptFinal(text: string, _durationMs: number): void {
|
|
528
|
+
if (!text.trim()) return;
|
|
529
|
+
this.transcriptFinalsProduced++;
|
|
530
|
+
|
|
531
|
+
if (!this.controller) {
|
|
532
|
+
log.warn(
|
|
533
|
+
{ callSessionId: this.callSessionId },
|
|
534
|
+
"Transcript received but no controller — dropping",
|
|
535
|
+
);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const session = getCallSession(this.callSessionId);
|
|
540
|
+
if (session) {
|
|
541
|
+
fireCallTranscriptNotifier(
|
|
542
|
+
session.conversationId,
|
|
543
|
+
this.callSessionId,
|
|
544
|
+
"caller",
|
|
545
|
+
text,
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
recordCallEvent(this.callSessionId, "caller_spoke", {
|
|
550
|
+
transcript: text,
|
|
551
|
+
transport: "media-stream",
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
// Route to the controller for conversation-backed response.
|
|
555
|
+
this.controller.handleCallerUtterance(text).catch((err) => {
|
|
556
|
+
log.error(
|
|
557
|
+
{ err, callSessionId: this.callSessionId },
|
|
558
|
+
"Controller failed to handle caller utterance",
|
|
559
|
+
);
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
private handleDtmf(digit: string): void {
|
|
564
|
+
log.info(
|
|
565
|
+
{ callSessionId: this.callSessionId, digit },
|
|
566
|
+
"DTMF digit received on media-stream",
|
|
567
|
+
);
|
|
568
|
+
recordCallEvent(this.callSessionId, "caller_spoke", {
|
|
569
|
+
dtmfDigit: digit,
|
|
570
|
+
transport: "media-stream",
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
private handleStreamStop(): void {
|
|
575
|
+
log.info(
|
|
576
|
+
{ callSessionId: this.callSessionId },
|
|
577
|
+
"Media stream stop event received",
|
|
578
|
+
);
|
|
579
|
+
// The WebSocket close handler will finalize the call session.
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
private handleSttError(category: string, message: string): void {
|
|
583
|
+
log.error(
|
|
584
|
+
{ callSessionId: this.callSessionId, category, message },
|
|
585
|
+
"STT error on media-stream session",
|
|
586
|
+
);
|
|
587
|
+
recordCallEvent(this.callSessionId, "call_failed", {
|
|
588
|
+
reason: `STT error: ${category} — ${message}`,
|
|
589
|
+
transport: "media-stream",
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
}
|