@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
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
} from "./call-constants.js";
|
|
44
44
|
import { CallController } from "./call-controller.js";
|
|
45
45
|
import { addPointerMessage, formatDuration } from "./call-pointer-messages.js";
|
|
46
|
+
import { speakSystemPrompt } from "./call-speech-output.js";
|
|
46
47
|
import { fireCallTranscriptNotifier } from "./call-state.js";
|
|
47
48
|
import { isTerminalState } from "./call-state-machine.js";
|
|
48
49
|
import {
|
|
@@ -50,6 +51,7 @@ import {
|
|
|
50
51
|
recordCallEvent,
|
|
51
52
|
updateCallSession,
|
|
52
53
|
} from "./call-store.js";
|
|
54
|
+
import { ConversationRelayTransport } from "./call-transport.js";
|
|
53
55
|
import { finalizeCall } from "./finalize-call.js";
|
|
54
56
|
import {
|
|
55
57
|
classifyWaitUtterance,
|
|
@@ -570,9 +572,10 @@ export class RelayConnection {
|
|
|
570
572
|
resolved.actorTrust,
|
|
571
573
|
resolved.otherPartyNumber,
|
|
572
574
|
);
|
|
575
|
+
const transport = new ConversationRelayTransport(this);
|
|
573
576
|
const controller = new CallController(
|
|
574
577
|
this.callSessionId,
|
|
575
|
-
|
|
578
|
+
transport,
|
|
576
579
|
session?.task ?? null,
|
|
577
580
|
{
|
|
578
581
|
broadcast: globalBroadcast,
|
|
@@ -594,7 +597,7 @@ export class RelayConnection {
|
|
|
594
597
|
await this.startVerification(session, outcome.verificationConfig);
|
|
595
598
|
return;
|
|
596
599
|
case "deny":
|
|
597
|
-
this.denyInboundCall(msg.from, resolved, outcome);
|
|
600
|
+
await this.denyInboundCall(msg.from, resolved, outcome);
|
|
598
601
|
return;
|
|
599
602
|
case "invite_redemption":
|
|
600
603
|
this.startInviteRedemption(
|
|
@@ -690,24 +693,24 @@ export class RelayConnection {
|
|
|
690
693
|
}
|
|
691
694
|
|
|
692
695
|
/** Deny an inbound call with a TTS message and schedule disconnect. */
|
|
693
|
-
private denyInboundCall(
|
|
696
|
+
private async denyInboundCall(
|
|
694
697
|
from: string,
|
|
695
698
|
resolved: import("./relay-setup-router.js").SetupResolved,
|
|
696
699
|
outcome: { message: string; logReason: string },
|
|
697
|
-
): void {
|
|
700
|
+
): Promise<void> {
|
|
698
701
|
recordCallEvent(this.callSessionId, "inbound_acl_denied", {
|
|
699
702
|
from,
|
|
700
703
|
trustClass: resolved.actorTrust.trustClass,
|
|
701
704
|
channelId: resolved.actorTrust.memberRecord?.channel.id,
|
|
702
705
|
memberPolicy: resolved.actorTrust.memberRecord?.channel.policy,
|
|
703
706
|
});
|
|
704
|
-
this.sendTextToken(outcome.message, true);
|
|
705
707
|
this.connectionState = "disconnecting";
|
|
706
708
|
updateCallSession(this.callSessionId, {
|
|
707
709
|
status: "failed",
|
|
708
710
|
endedAt: Date.now(),
|
|
709
711
|
lastError: outcome.logReason,
|
|
710
712
|
});
|
|
713
|
+
await speakSystemPrompt(this, outcome.message);
|
|
711
714
|
setTimeout(() => {
|
|
712
715
|
this.endSession(outcome.logReason);
|
|
713
716
|
}, getTtsPlaybackDelayMs());
|
|
@@ -740,9 +743,9 @@ export class RelayConnection {
|
|
|
740
743
|
|
|
741
744
|
// Send a TTS prompt with the code spoken digit by digit
|
|
742
745
|
const spokenCode = code.split("").join(". ");
|
|
743
|
-
|
|
746
|
+
void speakSystemPrompt(
|
|
747
|
+
this,
|
|
744
748
|
`Please enter the verification code: ${spokenCode}.`,
|
|
745
|
-
true,
|
|
746
749
|
);
|
|
747
750
|
|
|
748
751
|
// Post the verification code to the initiating conversation so the
|
|
@@ -861,7 +864,7 @@ export class RelayConnection {
|
|
|
861
864
|
handoffText = `Great! ${guardianLabel} said I can speak with you. How can I help?`;
|
|
862
865
|
}
|
|
863
866
|
|
|
864
|
-
this
|
|
867
|
+
void speakSystemPrompt(this, handoffText);
|
|
865
868
|
|
|
866
869
|
recordCallEvent(this.callSessionId, "assistant_spoke", {
|
|
867
870
|
text: handoffText,
|
|
@@ -904,9 +907,9 @@ export class RelayConnection {
|
|
|
904
907
|
maxAttempts: this.verificationMaxAttempts,
|
|
905
908
|
});
|
|
906
909
|
|
|
907
|
-
|
|
910
|
+
void speakSystemPrompt(
|
|
911
|
+
this,
|
|
908
912
|
"Welcome. Please enter your six-digit verification code using your keypad, or speak the digits now.",
|
|
909
|
-
true,
|
|
910
913
|
);
|
|
911
914
|
|
|
912
915
|
log.info(
|
|
@@ -946,7 +949,7 @@ export class RelayConnection {
|
|
|
946
949
|
GUARDIAN_VERIFY_TEMPLATE_KEYS.VOICE_CALL_INTRO,
|
|
947
950
|
{ codeDigits: this.verificationCodeLength },
|
|
948
951
|
);
|
|
949
|
-
this
|
|
952
|
+
void speakSystemPrompt(this, introText);
|
|
950
953
|
|
|
951
954
|
log.info(
|
|
952
955
|
{
|
|
@@ -963,7 +966,9 @@ export class RelayConnection {
|
|
|
963
966
|
* Delegates to the extracted attemptVerificationCode() and
|
|
964
967
|
* interprets the structured result to drive side-effects.
|
|
965
968
|
*/
|
|
966
|
-
private handleVerificationCodeResult(
|
|
969
|
+
private async handleVerificationCodeResult(
|
|
970
|
+
enteredCode: string,
|
|
971
|
+
): Promise<void> {
|
|
967
972
|
if (!this.verificationAssistantId || !this.verificationFromNumber) {
|
|
968
973
|
return;
|
|
969
974
|
}
|
|
@@ -1102,8 +1107,6 @@ export class RelayConnection {
|
|
|
1102
1107
|
"Guardian voice verification failed — max attempts reached",
|
|
1103
1108
|
);
|
|
1104
1109
|
|
|
1105
|
-
this.sendTextToken(result.ttsMessage, true);
|
|
1106
|
-
|
|
1107
1110
|
updateCallSession(this.callSessionId, {
|
|
1108
1111
|
status: "failed",
|
|
1109
1112
|
endedAt: Date.now(),
|
|
@@ -1135,6 +1138,7 @@ export class RelayConnection {
|
|
|
1135
1138
|
}
|
|
1136
1139
|
}
|
|
1137
1140
|
|
|
1141
|
+
await speakSystemPrompt(this, result.ttsMessage);
|
|
1138
1142
|
setTimeout(() => {
|
|
1139
1143
|
this.endSession("Verification failed — challenge rejected");
|
|
1140
1144
|
}, getTtsPlaybackDelayMs());
|
|
@@ -1151,7 +1155,7 @@ export class RelayConnection {
|
|
|
1151
1155
|
},
|
|
1152
1156
|
"Guardian voice verification attempt failed — retrying",
|
|
1153
1157
|
);
|
|
1154
|
-
this
|
|
1158
|
+
void speakSystemPrompt(this, result.ttsMessage);
|
|
1155
1159
|
}
|
|
1156
1160
|
}
|
|
1157
1161
|
|
|
@@ -1196,7 +1200,7 @@ export class RelayConnection {
|
|
|
1196
1200
|
} else {
|
|
1197
1201
|
promptText = `Welcome ${displayFriend}. Please enter the 6-digit code that ${displayGuardian} provided you to verify your identity.`;
|
|
1198
1202
|
}
|
|
1199
|
-
this
|
|
1203
|
+
void speakSystemPrompt(this, promptText);
|
|
1200
1204
|
|
|
1201
1205
|
log.info(
|
|
1202
1206
|
{ callSessionId: this.callSessionId, assistantId },
|
|
@@ -1221,7 +1225,7 @@ export class RelayConnection {
|
|
|
1221
1225
|
? `Hi, this is ${assistantName}, ${guardianLabel}'s assistant. Sorry, I don't recognize this number. I'll let ${guardianLabel} know you called and see if I have permission to speak with you. Can I get your name?`
|
|
1222
1226
|
: `Hi, this is ${guardianLabel}'s assistant. Sorry, I don't recognize this number. I'll let ${guardianLabel} know you called and see if I have permission to speak with you. Can I get your name?`;
|
|
1223
1227
|
|
|
1224
|
-
this
|
|
1228
|
+
void speakSystemPrompt(this, greeting);
|
|
1225
1229
|
|
|
1226
1230
|
// Start a timeout so silent callers don't keep the call open indefinitely.
|
|
1227
1231
|
// Uses a 30-second window — enough time to speak a name but short enough
|
|
@@ -1229,7 +1233,7 @@ export class RelayConnection {
|
|
|
1229
1233
|
const NAME_CAPTURE_TIMEOUT_MS = 30_000;
|
|
1230
1234
|
this.nameCaptureTimeoutTimer = setTimeout(() => {
|
|
1231
1235
|
if (this.connectionState !== "awaiting_name") return;
|
|
1232
|
-
this.handleNameCaptureTimeout();
|
|
1236
|
+
void this.handleNameCaptureTimeout();
|
|
1233
1237
|
}, NAME_CAPTURE_TIMEOUT_MS);
|
|
1234
1238
|
|
|
1235
1239
|
log.info(
|
|
@@ -1307,7 +1311,7 @@ export class RelayConnection {
|
|
|
1307
1311
|
{ callSessionId: this.callSessionId },
|
|
1308
1312
|
"Access request ID is null after notification attempt — failing closed",
|
|
1309
1313
|
);
|
|
1310
|
-
this.handleAccessRequestTimeout();
|
|
1314
|
+
void this.handleAccessRequestTimeout();
|
|
1311
1315
|
return;
|
|
1312
1316
|
}
|
|
1313
1317
|
|
|
@@ -1327,9 +1331,9 @@ export class RelayConnection {
|
|
|
1327
1331
|
const pollIntervalMs = getAccessRequestPollIntervalMs();
|
|
1328
1332
|
|
|
1329
1333
|
const guardianLabel = this.resolveGuardianLabel();
|
|
1330
|
-
|
|
1334
|
+
void speakSystemPrompt(
|
|
1335
|
+
this,
|
|
1331
1336
|
`Thank you. I've let ${guardianLabel} know. Please hold while I check if I have permission to speak with you.`,
|
|
1332
|
-
true,
|
|
1333
1337
|
);
|
|
1334
1338
|
|
|
1335
1339
|
updateCallSession(this.callSessionId, { status: "waiting_on_user" });
|
|
@@ -1363,7 +1367,7 @@ export class RelayConnection {
|
|
|
1363
1367
|
if (request.status === "approved") {
|
|
1364
1368
|
this.handleAccessRequestApproved();
|
|
1365
1369
|
} else if (request.status === "denied") {
|
|
1366
|
-
this.handleAccessRequestDenied();
|
|
1370
|
+
void this.handleAccessRequestDenied();
|
|
1367
1371
|
}
|
|
1368
1372
|
// 'pending' continues polling; 'expired'/'cancelled' handled by timeout
|
|
1369
1373
|
}, pollIntervalMs);
|
|
@@ -1377,7 +1381,7 @@ export class RelayConnection {
|
|
|
1377
1381
|
"Access request in-call wait timed out",
|
|
1378
1382
|
);
|
|
1379
1383
|
|
|
1380
|
-
this.handleAccessRequestTimeout();
|
|
1384
|
+
void this.handleAccessRequestTimeout();
|
|
1381
1385
|
}, timeoutMs);
|
|
1382
1386
|
|
|
1383
1387
|
log.info(
|
|
@@ -1450,7 +1454,7 @@ export class RelayConnection {
|
|
|
1450
1454
|
/**
|
|
1451
1455
|
* Handle a denied access request: deliver deterministic copy and hang up.
|
|
1452
1456
|
*/
|
|
1453
|
-
private handleAccessRequestDenied(): void {
|
|
1457
|
+
private async handleAccessRequestDenied(): Promise<void> {
|
|
1454
1458
|
this.clearAccessRequestWait();
|
|
1455
1459
|
|
|
1456
1460
|
const guardianLabel = this.resolveGuardianLabel();
|
|
@@ -1460,11 +1464,6 @@ export class RelayConnection {
|
|
|
1460
1464
|
requestId: this.accessRequestId,
|
|
1461
1465
|
});
|
|
1462
1466
|
|
|
1463
|
-
this.sendTextToken(
|
|
1464
|
-
`Sorry, ${guardianLabel} says I'm not allowed to speak with you. Goodbye.`,
|
|
1465
|
-
true,
|
|
1466
|
-
);
|
|
1467
|
-
|
|
1468
1467
|
this.connectionState = "disconnecting";
|
|
1469
1468
|
|
|
1470
1469
|
updateCallSession(this.callSessionId, {
|
|
@@ -1478,6 +1477,10 @@ export class RelayConnection {
|
|
|
1478
1477
|
"Access request denied — ending call",
|
|
1479
1478
|
);
|
|
1480
1479
|
|
|
1480
|
+
await speakSystemPrompt(
|
|
1481
|
+
this,
|
|
1482
|
+
`Sorry, ${guardianLabel} says I'm not allowed to speak with you. Goodbye.`,
|
|
1483
|
+
);
|
|
1481
1484
|
setTimeout(() => {
|
|
1482
1485
|
this.endSession("Access request denied");
|
|
1483
1486
|
}, getTtsPlaybackDelayMs());
|
|
@@ -1486,7 +1489,7 @@ export class RelayConnection {
|
|
|
1486
1489
|
/**
|
|
1487
1490
|
* Handle an access request timeout: deliver deterministic copy and hang up.
|
|
1488
1491
|
*/
|
|
1489
|
-
private handleAccessRequestTimeout(): void {
|
|
1492
|
+
private async handleAccessRequestTimeout(): Promise<void> {
|
|
1490
1493
|
// Emit callback handoff notification before clearing wait state
|
|
1491
1494
|
this.emitAccessRequestCallbackHandoffForReason("timeout");
|
|
1492
1495
|
|
|
@@ -1503,10 +1506,6 @@ export class RelayConnection {
|
|
|
1503
1506
|
const callbackNote = this.callbackOptIn
|
|
1504
1507
|
? ` I've noted that you'd like a callback — I'll pass that along to ${guardianLabel}.`
|
|
1505
1508
|
: "";
|
|
1506
|
-
this.sendTextToken(
|
|
1507
|
-
`Sorry, I can't get ahold of ${guardianLabel} right now. I'll let them know you called.${callbackNote}`,
|
|
1508
|
-
true,
|
|
1509
|
-
);
|
|
1510
1509
|
|
|
1511
1510
|
this.connectionState = "disconnecting";
|
|
1512
1511
|
|
|
@@ -1521,6 +1520,10 @@ export class RelayConnection {
|
|
|
1521
1520
|
"Access request timed out — ending call",
|
|
1522
1521
|
);
|
|
1523
1522
|
|
|
1523
|
+
await speakSystemPrompt(
|
|
1524
|
+
this,
|
|
1525
|
+
`Sorry, I can't get ahold of ${guardianLabel} right now. I'll let them know you called.${callbackNote}`,
|
|
1526
|
+
);
|
|
1524
1527
|
setTimeout(() => {
|
|
1525
1528
|
this.endSession("Access request timed out");
|
|
1526
1529
|
}, getTtsPlaybackDelayMs());
|
|
@@ -1546,7 +1549,7 @@ export class RelayConnection {
|
|
|
1546
1549
|
* Handle a name capture timeout: the caller never provided their name
|
|
1547
1550
|
* within the allotted window. Deliver deterministic copy and hang up.
|
|
1548
1551
|
*/
|
|
1549
|
-
private handleNameCaptureTimeout(): void {
|
|
1552
|
+
private async handleNameCaptureTimeout(): Promise<void> {
|
|
1550
1553
|
if (this.nameCaptureTimeoutTimer) {
|
|
1551
1554
|
clearTimeout(this.nameCaptureTimeoutTimer);
|
|
1552
1555
|
this.nameCaptureTimeoutTimer = null;
|
|
@@ -1556,11 +1559,6 @@ export class RelayConnection {
|
|
|
1556
1559
|
from: this.accessRequestFromNumber,
|
|
1557
1560
|
});
|
|
1558
1561
|
|
|
1559
|
-
this.sendTextToken(
|
|
1560
|
-
"Sorry, I didn't catch your name. Please try calling back. Goodbye.",
|
|
1561
|
-
true,
|
|
1562
|
-
);
|
|
1563
|
-
|
|
1564
1562
|
this.connectionState = "disconnecting";
|
|
1565
1563
|
|
|
1566
1564
|
updateCallSession(this.callSessionId, {
|
|
@@ -1574,6 +1572,10 @@ export class RelayConnection {
|
|
|
1574
1572
|
"Name capture timed out — ending call",
|
|
1575
1573
|
);
|
|
1576
1574
|
|
|
1575
|
+
await speakSystemPrompt(
|
|
1576
|
+
this,
|
|
1577
|
+
"Sorry, I didn't catch your name. Please try calling back. Goodbye.",
|
|
1578
|
+
);
|
|
1577
1579
|
setTimeout(() => {
|
|
1578
1580
|
this.endSession("Name capture timed out");
|
|
1579
1581
|
}, getTtsPlaybackDelayMs());
|
|
@@ -1584,7 +1586,9 @@ export class RelayConnection {
|
|
|
1584
1586
|
* Delegates to the extracted attemptInviteCodeRedemption() and
|
|
1585
1587
|
* interprets the structured result to drive side-effects.
|
|
1586
1588
|
*/
|
|
1587
|
-
private handleInviteCodeRedemptionResult(
|
|
1589
|
+
private async handleInviteCodeRedemptionResult(
|
|
1590
|
+
enteredCode: string,
|
|
1591
|
+
): Promise<void> {
|
|
1588
1592
|
if (!this.inviteRedemptionAssistantId || !this.inviteRedemptionFromNumber) {
|
|
1589
1593
|
return;
|
|
1590
1594
|
}
|
|
@@ -1634,8 +1638,6 @@ export class RelayConnection {
|
|
|
1634
1638
|
"Voice invite redemption failed — invalid or expired code",
|
|
1635
1639
|
);
|
|
1636
1640
|
|
|
1637
|
-
this.sendTextToken(result.ttsMessage, true);
|
|
1638
|
-
|
|
1639
1641
|
this.connectionState = "disconnecting";
|
|
1640
1642
|
|
|
1641
1643
|
updateCallSession(this.callSessionId, {
|
|
@@ -1649,6 +1651,7 @@ export class RelayConnection {
|
|
|
1649
1651
|
finalizeCall(this.callSessionId, failSession.conversationId);
|
|
1650
1652
|
}
|
|
1651
1653
|
|
|
1654
|
+
await speakSystemPrompt(this, result.ttsMessage);
|
|
1652
1655
|
setTimeout(() => {
|
|
1653
1656
|
this.endSession("Invite redemption failed");
|
|
1654
1657
|
}, getTtsPlaybackDelayMs());
|
|
@@ -1659,8 +1662,9 @@ export class RelayConnection {
|
|
|
1659
1662
|
|
|
1660
1663
|
/**
|
|
1661
1664
|
* Resolve a human-readable guardian label for voice wait copy.
|
|
1662
|
-
* Delegates to the shared resolveGuardianName() which checks
|
|
1663
|
-
*
|
|
1665
|
+
* Delegates to the shared resolveGuardianName() which checks the
|
|
1666
|
+
* guardian's per-user persona file (users/<slug>.md) first, then falls
|
|
1667
|
+
* back to Contact.displayName, then DEFAULT_USER_REFERENCE.
|
|
1664
1668
|
*/
|
|
1665
1669
|
private resolveGuardianLabel(): string {
|
|
1666
1670
|
// Look up the guardian contact for a displayName fallback
|
|
@@ -1691,7 +1695,7 @@ export class RelayConnection {
|
|
|
1691
1695
|
callSessionId: this.callSessionId,
|
|
1692
1696
|
consumeSequence: () => this.heartbeatSequence++,
|
|
1693
1697
|
resolveGuardianLabel: () => this.resolveGuardianLabel(),
|
|
1694
|
-
sendTextToken: (text,
|
|
1698
|
+
sendTextToken: (text, _last) => void speakSystemPrompt(this, text),
|
|
1695
1699
|
scheduleNext: () => this.scheduleNextHeartbeat(),
|
|
1696
1700
|
});
|
|
1697
1701
|
}
|
|
@@ -1737,9 +1741,9 @@ export class RelayConnection {
|
|
|
1737
1741
|
clearTimeout(this.accessRequestHeartbeatTimer);
|
|
1738
1742
|
this.accessRequestHeartbeatTimer = null;
|
|
1739
1743
|
}
|
|
1740
|
-
|
|
1744
|
+
void speakSystemPrompt(
|
|
1745
|
+
this,
|
|
1741
1746
|
`Noted, I'll make sure ${guardianLabel} knows you'd like a callback. For now, I'll keep trying to reach them.`,
|
|
1742
|
-
true,
|
|
1743
1747
|
);
|
|
1744
1748
|
this.scheduleNextHeartbeat();
|
|
1745
1749
|
return;
|
|
@@ -1756,9 +1760,9 @@ export class RelayConnection {
|
|
|
1756
1760
|
clearTimeout(this.accessRequestHeartbeatTimer);
|
|
1757
1761
|
this.accessRequestHeartbeatTimer = null;
|
|
1758
1762
|
}
|
|
1759
|
-
|
|
1763
|
+
void speakSystemPrompt(
|
|
1764
|
+
this,
|
|
1760
1765
|
`No problem, I'll keep holding. Still waiting on ${guardianLabel}.`,
|
|
1761
|
-
true,
|
|
1762
1766
|
);
|
|
1763
1767
|
this.scheduleNextHeartbeat();
|
|
1764
1768
|
return;
|
|
@@ -1793,15 +1797,15 @@ export class RelayConnection {
|
|
|
1793
1797
|
"voice_guardian_wait_callback_offer_sent",
|
|
1794
1798
|
{},
|
|
1795
1799
|
);
|
|
1796
|
-
|
|
1800
|
+
void speakSystemPrompt(
|
|
1801
|
+
this,
|
|
1797
1802
|
`I understand this is taking a while. I can have ${guardianLabel} call you back once I hear from them. Would you like that, or would you prefer to keep holding?`,
|
|
1798
|
-
true,
|
|
1799
1803
|
);
|
|
1800
1804
|
} else {
|
|
1801
1805
|
// Already offered callback — just reassure
|
|
1802
|
-
|
|
1806
|
+
void speakSystemPrompt(
|
|
1807
|
+
this,
|
|
1803
1808
|
`I hear you, I'm sorry for the wait. Still trying to reach ${guardianLabel}.`,
|
|
1804
|
-
true,
|
|
1805
1809
|
);
|
|
1806
1810
|
}
|
|
1807
1811
|
this.scheduleNextHeartbeat();
|
|
@@ -1814,9 +1818,9 @@ export class RelayConnection {
|
|
|
1814
1818
|
clearTimeout(this.accessRequestHeartbeatTimer);
|
|
1815
1819
|
this.accessRequestHeartbeatTimer = null;
|
|
1816
1820
|
}
|
|
1817
|
-
|
|
1821
|
+
void speakSystemPrompt(
|
|
1822
|
+
this,
|
|
1818
1823
|
`Yes, I'm still here. Still waiting to hear back from ${guardianLabel}.`,
|
|
1819
|
-
true,
|
|
1820
1824
|
);
|
|
1821
1825
|
this.scheduleNextHeartbeat();
|
|
1822
1826
|
break;
|
|
@@ -1827,9 +1831,9 @@ export class RelayConnection {
|
|
|
1827
1831
|
clearTimeout(this.accessRequestHeartbeatTimer);
|
|
1828
1832
|
this.accessRequestHeartbeatTimer = null;
|
|
1829
1833
|
}
|
|
1830
|
-
|
|
1834
|
+
void speakSystemPrompt(
|
|
1835
|
+
this,
|
|
1831
1836
|
`Thanks for that. I'm still waiting on ${guardianLabel}. I'll let you know as soon as I hear back.`,
|
|
1832
|
-
true,
|
|
1833
1837
|
);
|
|
1834
1838
|
this.scheduleNextHeartbeat();
|
|
1835
1839
|
break;
|
|
@@ -1888,11 +1892,11 @@ export class RelayConnection {
|
|
|
1888
1892
|
);
|
|
1889
1893
|
if (spokenDigits.length >= this.verificationCodeLength) {
|
|
1890
1894
|
const enteredCode = spokenDigits.slice(0, this.verificationCodeLength);
|
|
1891
|
-
this.handleVerificationCodeResult(enteredCode);
|
|
1895
|
+
void this.handleVerificationCodeResult(enteredCode);
|
|
1892
1896
|
} else if (spokenDigits.length > 0) {
|
|
1893
|
-
|
|
1897
|
+
void speakSystemPrompt(
|
|
1898
|
+
this,
|
|
1894
1899
|
`I heard ${spokenDigits.length} digits. Please enter all ${this.verificationCodeLength} digits of your code.`,
|
|
1895
|
-
true,
|
|
1896
1900
|
);
|
|
1897
1901
|
}
|
|
1898
1902
|
return;
|
|
@@ -1918,11 +1922,11 @@ export class RelayConnection {
|
|
|
1918
1922
|
0,
|
|
1919
1923
|
this.inviteRedemptionCodeLength,
|
|
1920
1924
|
);
|
|
1921
|
-
this.handleInviteCodeRedemptionResult(enteredCode);
|
|
1925
|
+
void this.handleInviteCodeRedemptionResult(enteredCode);
|
|
1922
1926
|
} else if (spokenDigits.length > 0) {
|
|
1923
|
-
|
|
1927
|
+
void speakSystemPrompt(
|
|
1928
|
+
this,
|
|
1924
1929
|
`I heard ${spokenDigits.length} digits. Please enter all ${this.inviteRedemptionCodeLength} digits of your code.`,
|
|
1925
|
-
true,
|
|
1926
1930
|
);
|
|
1927
1931
|
}
|
|
1928
1932
|
return;
|
|
@@ -2014,7 +2018,7 @@ export class RelayConnection {
|
|
|
2014
2018
|
);
|
|
2015
2019
|
}
|
|
2016
2020
|
}
|
|
2017
|
-
this
|
|
2021
|
+
void speakSystemPrompt(this, "I'm still setting up. Please hold.");
|
|
2018
2022
|
}
|
|
2019
2023
|
}
|
|
2020
2024
|
|
|
@@ -2073,7 +2077,7 @@ export class RelayConnection {
|
|
|
2073
2077
|
this.verificationCodeLength,
|
|
2074
2078
|
);
|
|
2075
2079
|
this.dtmfBuffer = "";
|
|
2076
|
-
this.handleVerificationCodeResult(enteredCode);
|
|
2080
|
+
void this.handleVerificationCodeResult(enteredCode);
|
|
2077
2081
|
}
|
|
2078
2082
|
return;
|
|
2079
2083
|
}
|
|
@@ -2092,7 +2096,7 @@ export class RelayConnection {
|
|
|
2092
2096
|
this.inviteRedemptionCodeLength,
|
|
2093
2097
|
);
|
|
2094
2098
|
this.dtmfBuffer = "";
|
|
2095
|
-
this.handleInviteCodeRedemptionResult(enteredCode);
|
|
2099
|
+
void this.handleInviteCodeRedemptionResult(enteredCode);
|
|
2096
2100
|
}
|
|
2097
2101
|
return;
|
|
2098
2102
|
}
|
|
@@ -2155,8 +2159,6 @@ export class RelayConnection {
|
|
|
2155
2159
|
"Callee verification failed — max attempts reached",
|
|
2156
2160
|
);
|
|
2157
2161
|
|
|
2158
|
-
this.sendTextToken("Verification failed. Goodbye.", true);
|
|
2159
|
-
|
|
2160
2162
|
// Mark failed immediately so a relay close during the goodbye TTS
|
|
2161
2163
|
// window cannot race this into a terminal "completed" status.
|
|
2162
2164
|
updateCallSession(this.callSessionId, {
|
|
@@ -2188,10 +2190,23 @@ export class RelayConnection {
|
|
|
2188
2190
|
}
|
|
2189
2191
|
}
|
|
2190
2192
|
|
|
2191
|
-
//
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2193
|
+
// Wait for synthesis to complete before starting teardown timer
|
|
2194
|
+
// so the caller hears the goodbye message.
|
|
2195
|
+
void speakSystemPrompt(this, "Verification failed. Goodbye.")
|
|
2196
|
+
.then(() => {
|
|
2197
|
+
setTimeout(() => {
|
|
2198
|
+
this.endSession("Verification failed");
|
|
2199
|
+
}, getTtsPlaybackDelayMs());
|
|
2200
|
+
})
|
|
2201
|
+
.catch((err) => {
|
|
2202
|
+
log.error(
|
|
2203
|
+
{ err, callSessionId: this.callSessionId },
|
|
2204
|
+
"System prompt TTS failed during verification teardown",
|
|
2205
|
+
);
|
|
2206
|
+
setTimeout(() => {
|
|
2207
|
+
this.endSession("Verification failed");
|
|
2208
|
+
}, getTtsPlaybackDelayMs());
|
|
2209
|
+
});
|
|
2195
2210
|
} else {
|
|
2196
2211
|
// Allow another attempt
|
|
2197
2212
|
log.info(
|
|
@@ -2202,9 +2217,9 @@ export class RelayConnection {
|
|
|
2202
2217
|
},
|
|
2203
2218
|
"Callee verification attempt failed — retrying",
|
|
2204
2219
|
);
|
|
2205
|
-
|
|
2220
|
+
void speakSystemPrompt(
|
|
2221
|
+
this,
|
|
2206
2222
|
"That code was incorrect. Please try again.",
|
|
2207
|
-
true,
|
|
2208
2223
|
);
|
|
2209
2224
|
}
|
|
2210
2225
|
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared call TTS provider resolution.
|
|
3
|
+
*
|
|
4
|
+
* Both the call controller (LLM turn speech) and deterministic system
|
|
5
|
+
* prompts (verification codes, guardian wait updates, timeout copy) use
|
|
6
|
+
* this helper so that provider selection, format fallback, and the
|
|
7
|
+
* native-vs-synthesized strategy decision stay in one implementation.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { loadConfig } from "../config/loader.js";
|
|
11
|
+
import { getTtsProvider } from "../tts/provider-registry.js";
|
|
12
|
+
import { resolveTtsConfig } from "../tts/tts-config-resolver.js";
|
|
13
|
+
import type { TtsProvider } from "../tts/types.js";
|
|
14
|
+
import { getLogger } from "../util/logger.js";
|
|
15
|
+
import { resolveCallStrategy } from "./tts-call-strategy.js";
|
|
16
|
+
|
|
17
|
+
const log = getLogger("resolve-call-tts-provider");
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Public types
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
export interface ResolvedCallTts {
|
|
24
|
+
/** The resolved TTS provider, or null when config/registry is unavailable. */
|
|
25
|
+
provider: TtsProvider | null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* True when the catalog's `callMode` is `"synthesized-play"` -- audio
|
|
29
|
+
* is synthesized via the provider API and streamed through the audio
|
|
30
|
+
* store. False when `callMode` is `"native-twilio"` -- text tokens are
|
|
31
|
+
* sent directly to the relay for Twilio's built-in TTS engine.
|
|
32
|
+
*/
|
|
33
|
+
useSynthesizedPath: boolean;
|
|
34
|
+
|
|
35
|
+
/** Audio format to use for synthesized audio. */
|
|
36
|
+
audioFormat: "mp3" | "wav" | "opus";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Options
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
export interface ResolveCallTtsOptions {
|
|
44
|
+
/**
|
|
45
|
+
* When true, force `audioFormat` to `"wav"` regardless of the
|
|
46
|
+
* provider's configured format. The media-stream transport sets this
|
|
47
|
+
* because its {@link audioBufferToFrames} can only correctly
|
|
48
|
+
* transcode WAV (PCM) to mu-law -- compressed formats (mp3, opus)
|
|
49
|
+
* are sent as raw bytes and produce garbled audio.
|
|
50
|
+
*/
|
|
51
|
+
preferWav?: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Public API
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Resolve the active TTS provider via the global provider abstraction.
|
|
60
|
+
*
|
|
61
|
+
* The native-vs-synthesized decision is driven by the catalog's
|
|
62
|
+
* `callMode` field via {@link resolveCallStrategy} -- the same single
|
|
63
|
+
* decision path used by `voice-quality.ts`. Providers with
|
|
64
|
+
* `callMode: "synthesized-play"` have their audio streamed through the
|
|
65
|
+
* audio store and played via `sendPlayUrl`. Providers with
|
|
66
|
+
* `callMode: "native-twilio"` stream text tokens directly to the relay
|
|
67
|
+
* for Twilio's built-in TTS.
|
|
68
|
+
*
|
|
69
|
+
* Falls back to the native path with `mp3` format when the config is
|
|
70
|
+
* missing a `services.tts` block or the provider is not registered
|
|
71
|
+
* (e.g. unit tests or early startup).
|
|
72
|
+
*/
|
|
73
|
+
export function resolveCallTtsProvider(
|
|
74
|
+
options?: ResolveCallTtsOptions,
|
|
75
|
+
): ResolvedCallTts {
|
|
76
|
+
try {
|
|
77
|
+
const config = loadConfig();
|
|
78
|
+
const resolved = resolveTtsConfig(config);
|
|
79
|
+
const provider = getTtsProvider(resolved.provider);
|
|
80
|
+
|
|
81
|
+
// Use the catalog's callMode to decide the call path -- the same
|
|
82
|
+
// decision path used by voice-quality.ts via resolveCallStrategy().
|
|
83
|
+
const strategy = resolveCallStrategy(config);
|
|
84
|
+
const useSynthesizedPath = strategy.callMode === "synthesized-play";
|
|
85
|
+
|
|
86
|
+
// For synthesized providers, preflight provider-specific config
|
|
87
|
+
// invariants that would otherwise fail only at first synthesis call.
|
|
88
|
+
// If required config is missing, degrade to the native token path
|
|
89
|
+
// (Twilio TTS) rather than letting the call stay silent.
|
|
90
|
+
//
|
|
91
|
+
// Fish Audio requires a reference ID when no per-request voiceId is
|
|
92
|
+
// supplied (which is the telephony default).
|
|
93
|
+
if (useSynthesizedPath && resolved.provider === "fish-audio") {
|
|
94
|
+
const referenceId = (resolved.providerConfig as { referenceId?: string })
|
|
95
|
+
.referenceId;
|
|
96
|
+
if (!referenceId?.trim()) {
|
|
97
|
+
log.warn(
|
|
98
|
+
{ provider: resolved.provider },
|
|
99
|
+
"Synthesized call TTS disabled: fish-audio.referenceId is not configured; falling back to native token path",
|
|
100
|
+
);
|
|
101
|
+
return {
|
|
102
|
+
provider: null,
|
|
103
|
+
useSynthesizedPath: false,
|
|
104
|
+
audioFormat: "mp3",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Read the user-configured audio format from the resolved provider
|
|
110
|
+
// config so the streaming store entry's content-type matches the
|
|
111
|
+
// actual audio bytes the provider produces.
|
|
112
|
+
//
|
|
113
|
+
// When preferWav is set (media-stream transport), force WAV so
|
|
114
|
+
// audioBufferToFrames receives PCM it can transcode to mu-law.
|
|
115
|
+
const audioFormat: "mp3" | "wav" | "opus" = options?.preferWav
|
|
116
|
+
? "wav"
|
|
117
|
+
: (() => {
|
|
118
|
+
const configuredFormat = (
|
|
119
|
+
resolved.providerConfig as { format?: string }
|
|
120
|
+
).format;
|
|
121
|
+
return (
|
|
122
|
+
configuredFormat &&
|
|
123
|
+
["mp3", "wav", "opus"].includes(configuredFormat)
|
|
124
|
+
? configuredFormat
|
|
125
|
+
: "mp3"
|
|
126
|
+
) as "mp3" | "wav" | "opus";
|
|
127
|
+
})();
|
|
128
|
+
|
|
129
|
+
return { provider, useSynthesizedPath, audioFormat };
|
|
130
|
+
} catch {
|
|
131
|
+
// Config missing `services.tts` block or provider not registered
|
|
132
|
+
// (e.g. unit tests or early startup) -- fall back to the native
|
|
133
|
+
// path where the provider object is not used.
|
|
134
|
+
return { provider: null, useSynthesizedPath: false, audioFormat: "mp3" };
|
|
135
|
+
}
|
|
136
|
+
}
|