@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,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-aware speech output for deterministic call prompts.
|
|
3
|
+
*
|
|
4
|
+
* Deterministic call prompts (verification codes, guardian wait updates,
|
|
5
|
+
* timeout copy, failure copy, etc.) are routed through the same provider
|
|
6
|
+
* abstraction used by the call controller so that configured synthesized
|
|
7
|
+
* providers (e.g. Fish Audio) are respected for all spoken output.
|
|
8
|
+
*
|
|
9
|
+
* Two output paths (determined by the catalog's `callMode`):
|
|
10
|
+
* - **Native**: `callMode: "native-twilio"` — text is sent via
|
|
11
|
+
* `sendTextToken()` for Twilio's built-in TTS engine.
|
|
12
|
+
* - **Synthesized**: `callMode: "synthesized-play"` — text is synthesized
|
|
13
|
+
* via the provider API, streamed through the audio store, and played
|
|
14
|
+
* via `sendPlayUrl()`.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { loadConfig } from "../config/loader.js";
|
|
18
|
+
import { getPublicBaseUrl } from "../inbound/public-ingress-urls.js";
|
|
19
|
+
import { getCatalogProvider } from "../tts/provider-catalog.js";
|
|
20
|
+
import type { TtsProvider, TtsProviderId } from "../tts/types.js";
|
|
21
|
+
import { getLogger } from "../util/logger.js";
|
|
22
|
+
import { createStreamingEntry } from "./audio-store.js";
|
|
23
|
+
import type { CallTransport } from "./call-transport.js";
|
|
24
|
+
import { resolveCallTtsProvider } from "./resolve-call-tts-provider.js";
|
|
25
|
+
|
|
26
|
+
const log = getLogger("call-speech-output");
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Public API
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Speak a deterministic text prompt through the active TTS provider.
|
|
34
|
+
*
|
|
35
|
+
* For native providers this is equivalent to `relay.sendTextToken(text, true)`
|
|
36
|
+
* and resolves immediately (synchronous send).
|
|
37
|
+
*
|
|
38
|
+
* For synthesized providers this synthesizes audio via the provider API,
|
|
39
|
+
* sends the play URL to the relay, and resolves once synthesis is complete.
|
|
40
|
+
* Callers in disconnect/teardown flows should `await` the returned promise
|
|
41
|
+
* before starting teardown timers so that the play URL is delivered to
|
|
42
|
+
* Twilio before the session ends. Interactive mid-call callers can
|
|
43
|
+
* fire-and-forget with `void speakSystemPrompt(...)`.
|
|
44
|
+
*/
|
|
45
|
+
export function speakSystemPrompt(
|
|
46
|
+
relay: CallTransport,
|
|
47
|
+
text: string,
|
|
48
|
+
): Promise<void> {
|
|
49
|
+
// When the transport requires WAV (media-stream), request WAV so
|
|
50
|
+
// the audio store entry contains PCM that audioBufferToFrames can
|
|
51
|
+
// transcode to mu-law. Without this, compressed formats (mp3, opus)
|
|
52
|
+
// are fetched by processFetchUrlItem and produce garbled audio.
|
|
53
|
+
const { provider, useSynthesizedPath, audioFormat } = resolveCallTtsProvider({
|
|
54
|
+
preferWav: relay.requiresWavAudio,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (!useSynthesizedPath || !provider) {
|
|
58
|
+
// Native path — send text for Twilio's built-in TTS.
|
|
59
|
+
relay.sendTextToken(text, true);
|
|
60
|
+
return Promise.resolve();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Synthesized path — synthesize audio and send play URL.
|
|
64
|
+
return synthesizeAndPlay(relay, provider, text, audioFormat);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Internal helpers
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Synthesize text via a streaming TTS provider and send the play URL
|
|
73
|
+
* to the relay.
|
|
74
|
+
*
|
|
75
|
+
* On synthesis failure the behavior depends on the provider:
|
|
76
|
+
* - Providers with a native Twilio TTS fallback (e.g. Fish Audio) fall
|
|
77
|
+
* back to `sendTextToken(text)` so the caller still hears the message.
|
|
78
|
+
* - Providers without a native fallback (e.g. Deepgram) log the error
|
|
79
|
+
* and send only an empty end-of-turn signal — the caller hears nothing
|
|
80
|
+
* but the relay transitions back to listening state.
|
|
81
|
+
*/
|
|
82
|
+
async function synthesizeAndPlay(
|
|
83
|
+
relay: CallTransport,
|
|
84
|
+
provider: TtsProvider,
|
|
85
|
+
text: string,
|
|
86
|
+
format: "mp3" | "wav" | "opus",
|
|
87
|
+
): Promise<void> {
|
|
88
|
+
let handle: ReturnType<typeof createStreamingEntry> | null = null;
|
|
89
|
+
let playUrlSent = false;
|
|
90
|
+
try {
|
|
91
|
+
// When format is WAV (media-stream transport), request raw PCM from
|
|
92
|
+
// the provider so the audio bytes match the store's content-type.
|
|
93
|
+
// Without this, providers like Fish Audio still return mp3 and the
|
|
94
|
+
// downstream mu-law transcoder fails on the format mismatch.
|
|
95
|
+
const outputFormat = format === "wav" ? ("pcm" as const) : undefined;
|
|
96
|
+
|
|
97
|
+
// Use "pcm" as the store format when requesting PCM output so the
|
|
98
|
+
// audio store entry's content-type (audio/pcm) matches the raw PCM
|
|
99
|
+
// bytes providers return. Without this, the store says "audio/wav"
|
|
100
|
+
// but the bytes have no RIFF header, causing audioBufferToFrames to
|
|
101
|
+
// fall through to the wrong decode path.
|
|
102
|
+
const storeFormat = outputFormat ? "pcm" : format;
|
|
103
|
+
handle = createStreamingEntry(storeFormat);
|
|
104
|
+
const config = loadConfig();
|
|
105
|
+
const baseUrl = getPublicBaseUrl(config);
|
|
106
|
+
const url = `${baseUrl}/v1/audio/${handle.audioId}`;
|
|
107
|
+
const sendPlayUrlOnce = (): void => {
|
|
108
|
+
if (playUrlSent) return;
|
|
109
|
+
relay.sendPlayUrl(url);
|
|
110
|
+
playUrlSent = true;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
if (provider.synthesizeStream) {
|
|
114
|
+
let streamedChunk = false;
|
|
115
|
+
await provider.synthesizeStream(
|
|
116
|
+
{ text, useCase: "phone-call", outputFormat },
|
|
117
|
+
(chunk) => {
|
|
118
|
+
if (chunk.byteLength === 0) return;
|
|
119
|
+
if (!streamedChunk) {
|
|
120
|
+
sendPlayUrlOnce();
|
|
121
|
+
streamedChunk = true;
|
|
122
|
+
}
|
|
123
|
+
handle!.push(chunk);
|
|
124
|
+
},
|
|
125
|
+
);
|
|
126
|
+
if (!streamedChunk) {
|
|
127
|
+
throw new Error("Streaming TTS returned no audio chunks");
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
const result = await provider.synthesize({
|
|
131
|
+
text,
|
|
132
|
+
useCase: "phone-call",
|
|
133
|
+
outputFormat,
|
|
134
|
+
});
|
|
135
|
+
if (result.audio.byteLength === 0) {
|
|
136
|
+
throw new Error("Buffer TTS returned an empty audio payload");
|
|
137
|
+
}
|
|
138
|
+
sendPlayUrlOnce();
|
|
139
|
+
handle.push(result.audio);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Signal end of this turn's speech. An empty token with `last: true`
|
|
143
|
+
// tells ConversationRelay to start listening — it does NOT trigger TTS
|
|
144
|
+
// synthesis. This is required even when a synthesized provider handled
|
|
145
|
+
// all audio playback, because ConversationRelay still needs the
|
|
146
|
+
// end-of-turn signal to transition from "assistant speaking" to
|
|
147
|
+
// "caller speaking" state.
|
|
148
|
+
relay.sendTextToken("", true);
|
|
149
|
+
} catch (err) {
|
|
150
|
+
// Extract error class and code for diagnosable log entries.
|
|
151
|
+
const errName = err instanceof Error ? err.name : String(err);
|
|
152
|
+
const errCode =
|
|
153
|
+
err instanceof Error && "code" in err
|
|
154
|
+
? (err as Error & { code?: string }).code
|
|
155
|
+
: undefined;
|
|
156
|
+
|
|
157
|
+
// `allowNativeFallback` controls whether the system prompt text
|
|
158
|
+
// should be sent via native Twilio token-based TTS when synthesis
|
|
159
|
+
// fails. When false (e.g. Deepgram), the design choice is to
|
|
160
|
+
// send only an end-of-turn signal — the caller hears nothing for
|
|
161
|
+
// this prompt — rather than degrading to a mismatched voice.
|
|
162
|
+
// Callers use fire-and-forget (`void speakSystemPrompt(...)`) so
|
|
163
|
+
// throwing here would produce an unhandled promise rejection.
|
|
164
|
+
const catalogEntry = getCatalogProvider(provider.id as TtsProviderId);
|
|
165
|
+
if (!catalogEntry.allowNativeFallback) {
|
|
166
|
+
log.error(
|
|
167
|
+
{ err, provider: provider.id, errName, errCode },
|
|
168
|
+
"System prompt TTS synthesis failed — native fallback disabled for this provider",
|
|
169
|
+
);
|
|
170
|
+
// Send the end-of-turn signal so ConversationRelay transitions from
|
|
171
|
+
// "assistant speaking" to "caller speaking" state. Without this, the
|
|
172
|
+
// relay hangs waiting for the prompt to complete and the caller
|
|
173
|
+
// cannot interact.
|
|
174
|
+
relay.sendTextToken("", true);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
log.error(
|
|
179
|
+
{ err, provider: provider.id, errName, errCode },
|
|
180
|
+
"System prompt TTS synthesis failed — falling back to native TTS",
|
|
181
|
+
);
|
|
182
|
+
// Fallback: send text via native TTS so the caller still hears the message.
|
|
183
|
+
// sendTextToken with last:true includes the end-of-turn signal inherently.
|
|
184
|
+
// This fallback is only used for providers whose catalog entry allows
|
|
185
|
+
// native fallback.
|
|
186
|
+
relay.sendTextToken(text, true);
|
|
187
|
+
} finally {
|
|
188
|
+
handle?.finalize();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport interface consumed by CallController for sending voice output
|
|
3
|
+
* and controlling call lifecycle.
|
|
4
|
+
*
|
|
5
|
+
* Decouples the controller from any specific wire protocol (e.g.
|
|
6
|
+
* ConversationRelay) so that alternative transports (media-stream, etc.)
|
|
7
|
+
* can be introduced without modifying controller logic.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ── Transport interface ──────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Minimal output surface that CallController uses to send speech,
|
|
14
|
+
* audio, and lifecycle signals to the caller.
|
|
15
|
+
*/
|
|
16
|
+
export interface CallTransport {
|
|
17
|
+
/**
|
|
18
|
+
* Send a text token for TTS playback. When `last` is true the
|
|
19
|
+
* transport should signal end-of-turn to the caller.
|
|
20
|
+
*/
|
|
21
|
+
sendTextToken(token: string, last: boolean): void;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Send a pre-synthesized audio URL for playback.
|
|
25
|
+
*/
|
|
26
|
+
sendPlayUrl(url: string): void;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Signal the transport to end the call session.
|
|
30
|
+
*/
|
|
31
|
+
endSession(reason?: string): void;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Return the current connection-level state. The controller uses this
|
|
35
|
+
* to suppress silence nudges during guardian wait states.
|
|
36
|
+
*/
|
|
37
|
+
getConnectionState(): string;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* When true, the transport requires WAV (PCM) audio for playback.
|
|
41
|
+
*
|
|
42
|
+
* The media-stream transport sets this because its mu-law transcoder
|
|
43
|
+
* can only decode WAV (raw PCM) — compressed formats (mp3, opus)
|
|
44
|
+
* produce garbled audio. The call controller uses this to request
|
|
45
|
+
* WAV from TTS providers and the audio store.
|
|
46
|
+
*/
|
|
47
|
+
readonly requiresWavAudio?: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ── ConversationRelay adapter ────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
import type { RelayConnection } from "./relay-server.js";
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Adapts a RelayConnection (Twilio ConversationRelay WebSocket) to the
|
|
56
|
+
* CallTransport interface. All calls are forwarded 1:1 — no behavioral
|
|
57
|
+
* changes from the pre-abstraction path.
|
|
58
|
+
*/
|
|
59
|
+
export class ConversationRelayTransport implements CallTransport {
|
|
60
|
+
constructor(private relay: RelayConnection) {}
|
|
61
|
+
|
|
62
|
+
sendTextToken(token: string, last: boolean): void {
|
|
63
|
+
this.relay.sendTextToken(token, last);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
sendPlayUrl(url: string): void {
|
|
67
|
+
this.relay.sendPlayUrl(url);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
endSession(reason?: string): void {
|
|
71
|
+
this.relay.endSession(reason);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getConnectionState(): string {
|
|
75
|
+
return this.relay.getConnectionState();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio transcoding helpers for media-stream outbound playback.
|
|
3
|
+
*
|
|
4
|
+
* Twilio media streams send and receive audio as base64-encoded mu-law
|
|
5
|
+
* (audio/x-mulaw) at 8 kHz mono. This module provides utilities for:
|
|
6
|
+
*
|
|
7
|
+
* 1. Converting linear PCM audio (from TTS providers) to mu-law encoding.
|
|
8
|
+
* 2. Chunking a contiguous audio buffer into Twilio-compatible frame sizes.
|
|
9
|
+
* 3. Encoding frames as base64 strings ready for the `media` outbound command.
|
|
10
|
+
*
|
|
11
|
+
* The chunk size is aligned to Twilio's expected frame duration (~20 ms at
|
|
12
|
+
* 8 kHz = 160 samples per frame). Larger payloads are split into multiple
|
|
13
|
+
* frames to avoid buffering delays on the Twilio side.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Constants
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Twilio media streams expect 20 ms frames at 8 kHz mono mu-law.
|
|
24
|
+
* 8000 samples/sec * 0.020 sec = 160 samples per frame.
|
|
25
|
+
* Each mu-law sample is 1 byte, so each frame is 160 bytes.
|
|
26
|
+
*/
|
|
27
|
+
export const MULAW_FRAME_SIZE = 160;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Bias constant used in the linear-to-mu-law compression formula
|
|
31
|
+
* (ITU-T G.711).
|
|
32
|
+
*/
|
|
33
|
+
const MULAW_BIAS = 0x84;
|
|
34
|
+
|
|
35
|
+
/** Maximum value for the mu-law compression input. */
|
|
36
|
+
const MULAW_CLIP = 32635;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Lookup table for the segment number in mu-law encoding.
|
|
40
|
+
* Maps magnitude ranges to segment indices.
|
|
41
|
+
*/
|
|
42
|
+
const MULAW_SEG_TABLE = [0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3];
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Linear PCM to mu-law conversion
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Compress a single 16-bit linear PCM sample to 8-bit mu-law.
|
|
50
|
+
*
|
|
51
|
+
* Implements the ITU-T G.711 mu-law encoding algorithm. The input is a
|
|
52
|
+
* signed 16-bit integer (range: -32768 to +32767). The output is an
|
|
53
|
+
* unsigned 8-bit mu-law value.
|
|
54
|
+
*/
|
|
55
|
+
export function linearToMulaw(sample: number): number {
|
|
56
|
+
// Determine sign and clamp magnitude
|
|
57
|
+
const sign = (sample >> 8) & 0x80;
|
|
58
|
+
if (sign !== 0) sample = -sample;
|
|
59
|
+
if (sample > MULAW_CLIP) sample = MULAW_CLIP;
|
|
60
|
+
sample += MULAW_BIAS;
|
|
61
|
+
|
|
62
|
+
// Determine segment
|
|
63
|
+
const exponent = segmentSearch(sample);
|
|
64
|
+
const mantissa = (sample >> (exponent + 3)) & 0x0f;
|
|
65
|
+
const mulawByte = ~(sign | (exponent << 4) | mantissa) & 0xff;
|
|
66
|
+
|
|
67
|
+
return mulawByte;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function segmentSearch(val: number): number {
|
|
71
|
+
let shifted = val >> 7;
|
|
72
|
+
if (shifted >= 16) {
|
|
73
|
+
shifted >>= 4;
|
|
74
|
+
if (shifted >= 16) return 7;
|
|
75
|
+
return MULAW_SEG_TABLE[shifted] + 4;
|
|
76
|
+
}
|
|
77
|
+
return MULAW_SEG_TABLE[shifted];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// PCM buffer to mu-law buffer conversion
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Convert a buffer of 16-bit signed little-endian PCM samples to mu-law.
|
|
86
|
+
*
|
|
87
|
+
* @param pcm - Raw PCM audio buffer. Every 2 bytes is one sample (LE).
|
|
88
|
+
* @returns A Buffer of mu-law encoded bytes (half the length of the input).
|
|
89
|
+
*/
|
|
90
|
+
export function pcm16ToMulaw(pcm: Uint8Array): Buffer {
|
|
91
|
+
const sampleCount = Math.floor(pcm.length / 2);
|
|
92
|
+
const mulaw = Buffer.alloc(sampleCount);
|
|
93
|
+
const view = new DataView(pcm.buffer, pcm.byteOffset, pcm.byteLength);
|
|
94
|
+
|
|
95
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
96
|
+
const sample = view.getInt16(i * 2, true); // little-endian
|
|
97
|
+
mulaw[i] = linearToMulaw(sample);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return mulaw;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Chunking
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Split a mu-law audio buffer into base64-encoded frames suitable for
|
|
109
|
+
* Twilio media stream `media` commands.
|
|
110
|
+
*
|
|
111
|
+
* Each frame is {@link MULAW_FRAME_SIZE} bytes (160 bytes = 20 ms at 8 kHz).
|
|
112
|
+
* The final frame may be shorter if the buffer length is not an exact
|
|
113
|
+
* multiple of the frame size.
|
|
114
|
+
*
|
|
115
|
+
* @param mulawBuffer - Contiguous mu-law audio bytes.
|
|
116
|
+
* @returns Array of base64-encoded frame strings.
|
|
117
|
+
*/
|
|
118
|
+
export function chunkMulawToBase64Frames(mulawBuffer: Buffer): string[] {
|
|
119
|
+
const frames: string[] = [];
|
|
120
|
+
let offset = 0;
|
|
121
|
+
|
|
122
|
+
while (offset < mulawBuffer.length) {
|
|
123
|
+
const end = Math.min(offset + MULAW_FRAME_SIZE, mulawBuffer.length);
|
|
124
|
+
const frame = mulawBuffer.subarray(offset, end);
|
|
125
|
+
frames.push(Buffer.from(frame).toString("base64"));
|
|
126
|
+
offset = end;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return frames;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
// High-level: raw audio bytes to sendable base64 frames
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Accepted audio formats for transcoding into mu-law frames.
|
|
138
|
+
*
|
|
139
|
+
* Currently only `mulaw` (already in the target format) and `pcm16` (16-bit
|
|
140
|
+
* signed LE PCM at 8 kHz) are supported. Higher-level formats (mp3, wav,
|
|
141
|
+
* opus) require decoding to PCM first -- callers should handle that
|
|
142
|
+
* conversion before invoking this helper.
|
|
143
|
+
*/
|
|
144
|
+
export type TranscodeInputFormat = "mulaw" | "pcm16";
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Transcode raw audio bytes into Twilio-compatible base64-encoded mu-law
|
|
148
|
+
* frames.
|
|
149
|
+
*
|
|
150
|
+
* @param audio - Raw audio bytes in the specified format.
|
|
151
|
+
* @param format - The format of the input audio.
|
|
152
|
+
* @returns Array of base64-encoded mu-law frames ready for
|
|
153
|
+
* `MediaStreamOutput.sendAudioPayload()`.
|
|
154
|
+
*/
|
|
155
|
+
export function transcodeToMulawFrames(
|
|
156
|
+
audio: Uint8Array,
|
|
157
|
+
format: TranscodeInputFormat,
|
|
158
|
+
): string[] {
|
|
159
|
+
let mulawBuffer: Buffer;
|
|
160
|
+
|
|
161
|
+
switch (format) {
|
|
162
|
+
case "mulaw":
|
|
163
|
+
mulawBuffer = Buffer.from(audio);
|
|
164
|
+
break;
|
|
165
|
+
case "pcm16":
|
|
166
|
+
mulawBuffer = pcm16ToMulaw(audio);
|
|
167
|
+
break;
|
|
168
|
+
default:
|
|
169
|
+
throw new Error(`Unsupported transcode format: ${format as string}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return chunkMulawToBase64Frames(mulawBuffer);
|
|
173
|
+
}
|