@vellumai/assistant 0.6.2 → 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 +41 -49
- package/bunfig.toml +3 -0
- package/docs/architecture/memory.md +1 -1
- 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/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
- package/openapi.yaml +1111 -86
- package/package.json +40 -42
- package/scripts/generate-openapi.ts +0 -2
- package/scripts/test.sh +73 -18
- package/src/__tests__/acp-session.test.ts +43 -0
- 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__/app-builder-tool-scripts.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +1 -0
- package/src/__tests__/app-source-watcher.test.ts +37 -11
- package/src/__tests__/approval-routes-http.test.ts +178 -1
- 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 +240 -94
- package/src/__tests__/browser-manager.test.ts +40 -27
- 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 +1000 -0
- package/src/__tests__/channel-approvals.test.ts +53 -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-managed-gemini-defaults.test.ts +326 -0
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +1248 -224
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
- package/src/__tests__/config-watcher.test.ts +43 -8
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
- package/src/__tests__/contact-store-user-file.test.ts +512 -0
- package/src/__tests__/contacts-write.test.ts +197 -0
- package/src/__tests__/context-overflow-approval.test.ts +16 -1
- 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 +2 -1
- package/src/__tests__/conversation-agent-loop.test.ts +99 -3
- package/src/__tests__/conversation-analysis-routes.test.ts +2 -2
- package/src/__tests__/conversation-attachments.test.ts +80 -4
- package/src/__tests__/conversation-confirmation-signals.test.ts +290 -0
- package/src/__tests__/conversation-error.test.ts +70 -0
- package/src/__tests__/conversation-fork-crud.test.ts +17 -0
- package/src/__tests__/conversation-history-web-search.test.ts +12 -4
- package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
- package/src/__tests__/conversation-inject-context.test.ts +103 -0
- 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 +946 -62
- package/src/__tests__/conversation-routes-disk-view.test.ts +275 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +324 -46
- 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-starter-routes.test.ts +126 -0
- package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
- package/src/__tests__/conversation-store.test.ts +195 -0
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -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-execution-approval-bridge.test.ts +32 -1
- package/src/__tests__/credential-health-service.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +6 -3
- package/src/__tests__/credential-vault-unit.test.ts +383 -7
- package/src/__tests__/credential-vault.test.ts +152 -13
- package/src/__tests__/credentials-cli.test.ts +42 -18
- package/src/__tests__/cross-provider-web-search.test.ts +146 -35
- package/src/__tests__/date-context.test.ts +4 -4
- 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__/embedding-managed-proxy-selection.test.ts +256 -0
- package/src/__tests__/emit-event-signal.test.ts +71 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +222 -0
- package/src/__tests__/fixtures/mock-chrome-extension.ts +386 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
- package/src/__tests__/gateway-only-guard.test.ts +2 -0
- package/src/__tests__/gemini-provider.test.ts +66 -2
- 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__/guardian-routing-invariants.test.ts +70 -2
- package/src/__tests__/headless-browser-interactions.test.ts +738 -359
- package/src/__tests__/headless-browser-mode.test.ts +614 -0
- package/src/__tests__/headless-browser-navigate.test.ts +528 -49
- package/src/__tests__/headless-browser-read-tools.test.ts +274 -100
- package/src/__tests__/headless-browser-snapshot.test.ts +250 -77
- 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 +145 -1
- package/src/__tests__/host-browser-e2e-cloud.test.ts +596 -0
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
- package/src/__tests__/host-browser-event-routes.test.ts +350 -0
- package/src/__tests__/host-browser-proxy.test.ts +444 -0
- package/src/__tests__/host-browser-routes.test.ts +198 -0
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +423 -0
- package/src/__tests__/host-cu-proxy.test.ts +166 -1
- package/src/__tests__/host-file-proxy.test.ts +185 -1
- package/src/__tests__/host-file-read-tool.test.ts +52 -0
- package/src/__tests__/host-proxy-interface.test.ts +165 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -11
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/identity-intro-cache.test.ts +40 -10
- package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
- package/src/__tests__/integration-status.test.ts +6 -7
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- 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__/mcp-client-auth.test.ts +40 -4
- package/src/__tests__/mcp-health-check.test.ts +10 -3
- 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-cross-version-compatibility.test.ts +3 -1
- package/src/__tests__/migration-export-http.test.ts +67 -8
- package/src/__tests__/migration-export-streaming.test.ts +66 -0
- package/src/__tests__/migration-import-commit-http.test.ts +109 -7
- 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__/native-host-marker-sync-guard.test.ts +157 -0
- package/src/__tests__/oauth-apps-routes.test.ts +18 -12
- package/src/__tests__/oauth-cli.test.ts +709 -60
- package/src/__tests__/oauth-connect-orchestrator.test.ts +118 -24
- package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +147 -10
- package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
- package/src/__tests__/oauth-providers-routes.test.ts +52 -14
- package/src/__tests__/oauth-store.test.ts +1465 -176
- package/src/__tests__/oauth2-gateway-transport.test.ts +460 -26
- package/src/__tests__/onboarding-template-contract.test.ts +81 -70
- package/src/__tests__/openai-provider.test.ts +178 -2
- 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-categories.test.ts +1 -1
- package/src/__tests__/outlook-client-automation.test.ts +1 -1
- package/src/__tests__/outlook-compose-tools.test.ts +1 -1
- package/src/__tests__/outlook-email-watcher.test.ts +1 -1
- package/src/__tests__/outlook-follow-up.test.ts +1 -1
- package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
- package/src/__tests__/outlook-trash.test.ts +1 -1
- package/src/__tests__/outlook-unsubscribe.test.ts +32 -3
- package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
- package/src/__tests__/permission-mode.test.ts +28 -56
- package/src/__tests__/persona-resolver.test.ts +251 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
- package/src/__tests__/platform-callback-registration.test.ts +19 -0
- package/src/__tests__/platform.test.ts +92 -1
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +343 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
- package/src/__tests__/pricing.test.ts +174 -0
- package/src/__tests__/proxy-approval-callback.test.ts +18 -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__/require-fresh-approval.test.ts +40 -1
- package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
- package/src/__tests__/schedule-routes.test.ts +162 -0
- package/src/__tests__/search-skills-unified.test.ts +118 -0
- package/src/__tests__/secret-detection-handler.test.ts +84 -0
- package/src/__tests__/secret-ingress-http.test.ts +1 -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 +8 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +49 -0
- package/src/__tests__/set-permission-mode.test.ts +13 -250
- 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 +801 -0
- package/src/__tests__/skills-files-catalog-fallback.test.ts +738 -0
- 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 +576 -16
- package/src/__tests__/stt-catalog-parity.test.ts +282 -0
- package/src/__tests__/stt-stream-session.test.ts +535 -0
- package/src/__tests__/subagent-detail.test.ts +44 -2
- package/src/__tests__/subagent-disposal.test.ts +1 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
- package/src/__tests__/subagent-manager-notify.test.ts +1 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
- package/src/__tests__/subagent-tools.test.ts +1 -0
- package/src/__tests__/subagent-types.test.ts +1 -0
- package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
- package/src/__tests__/system-prompt.test.ts +184 -27
- package/src/__tests__/task-scheduler.test.ts +32 -6
- package/src/__tests__/telegram-config.test.ts +10 -13
- package/src/__tests__/telephony-stt-routing.test.ts +329 -0
- package/src/__tests__/terminal-tools.test.ts +25 -5
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- 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__/tool-side-effects-slack-dm.test.ts +22 -0
- package/src/__tests__/top-level-renderer.test.ts +73 -1
- package/src/__tests__/transport-hints-queue.test.ts +14 -29
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- 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__/v2-consent-policy.test.ts +103 -0
- 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/acp/client-handler.ts +30 -4
- package/src/agent/image-optimize.ts +24 -12
- package/src/agent/loop.ts +55 -9
- package/src/approvals/guardian-request-resolvers.ts +21 -15
- 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/browser-session/__tests__/manager.test.ts +297 -0
- package/src/browser-session/backends/cdp-inspect.ts +30 -0
- package/src/browser-session/backends/extension.ts +26 -0
- package/src/browser-session/backends/local.ts +24 -0
- package/src/browser-session/events.ts +164 -0
- package/src/browser-session/index.ts +27 -0
- package/src/browser-session/manager.ts +159 -0
- package/src/browser-session/types.ts +28 -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/__tests__/types.test.ts +134 -0
- package/src/channels/types.ts +69 -3
- 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 +3 -4
- package/src/cli/commands/domain.ts +210 -0
- package/src/cli/commands/email.ts +273 -16
- package/src/cli/commands/mcp.ts +16 -4
- package/src/cli/commands/oauth/__tests__/connect.test.ts +56 -44
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
- package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
- package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +32 -33
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +330 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +117 -12
- package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
- package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
- package/src/cli/commands/oauth/apps.ts +7 -4
- package/src/cli/commands/oauth/connect.ts +6 -3
- package/src/cli/commands/oauth/disconnect.ts +1 -1
- package/src/cli/commands/oauth/mode.ts +12 -3
- package/src/cli/commands/oauth/providers.ts +215 -36
- package/src/cli/commands/oauth/shared.ts +7 -6
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +254 -0
- 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/commands/platform/index.ts +107 -10
- package/src/cli/commands/usage.ts +10 -9
- package/src/cli/lib/daemon-credential-client.ts +4 -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/SKILL.md +26 -249
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +141 -0
- package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
- 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 +5 -2
- package/src/config/bundled-skills/document/SKILL.md +4 -0
- package/src/config/bundled-skills/gmail/SKILL.md +54 -8
- 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 +9 -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/subagent/SKILL.md +21 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
- package/src/config/bundled-skills/tasks/SKILL.md +5 -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 +38 -0
- package/src/config/env.ts +49 -4
- package/src/config/feature-flag-registry.json +85 -14
- package/src/config/loader.ts +82 -13
- package/src/config/sanitize-for-transfer.ts +47 -0
- package/src/config/schema.ts +81 -15
- 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 +112 -0
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/memory-lifecycle.ts +14 -2
- package/src/config/schemas/memory-retrieval.ts +103 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +52 -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 -1
- 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 +177 -0
- package/src/context/tool-result-truncation.ts +2 -1
- package/src/context/window-manager.ts +61 -10
- package/src/credential-execution/approval-bridge.ts +49 -15
- 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 +195 -0
- package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/config-watcher.ts +99 -5
- package/src/daemon/context-overflow-approval.ts +5 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +23 -2
- package/src/daemon/conversation-agent-loop.ts +153 -42
- package/src/daemon/conversation-attachments.ts +40 -0
- 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 +622 -13
- package/src/daemon/conversation-queue-manager.ts +24 -0
- package/src/daemon/conversation-runtime-assembly.ts +128 -36
- package/src/daemon/conversation-slash.ts +36 -0
- package/src/daemon/conversation-surfaces.ts +131 -40
- package/src/daemon/conversation-tool-setup.ts +99 -8
- package/src/daemon/conversation-usage.ts +7 -4
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +292 -16
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/config-slack-channel.ts +269 -94
- package/src/daemon/handlers/conversations.ts +13 -141
- package/src/daemon/handlers/shared.ts +80 -0
- package/src/daemon/handlers/skills.ts +483 -44
- package/src/daemon/host-bash-proxy.ts +48 -13
- package/src/daemon/host-browser-proxy.ts +192 -0
- package/src/daemon/host-cu-proxy.ts +36 -11
- package/src/daemon/host-file-proxy.ts +57 -9
- package/src/daemon/lifecycle.ts +179 -28
- package/src/daemon/message-protocol.ts +13 -0
- package/src/daemon/message-types/conversations.ts +89 -14
- package/src/daemon/message-types/home.ts +40 -0
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/meet.ts +143 -0
- package/src/daemon/message-types/messages.ts +19 -5
- package/src/daemon/message-types/schedules.ts +34 -2
- package/src/daemon/message-types/skills.ts +26 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/server.ts +439 -14
- package/src/daemon/shutdown-handlers.ts +32 -4
- package/src/daemon/shutdown-registry.ts +40 -0
- package/src/daemon/tool-side-effects.ts +15 -0
- package/src/daemon/transport-hints.ts +5 -24
- 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 +30 -20
- 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/mcp/client.ts +59 -24
- 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 +31 -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 +122 -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/conversation-starters-cadence.ts +76 -0
- package/src/memory/conversation-title-service.ts +5 -2
- package/src/memory/db-init.ts +18 -0
- package/src/memory/db-maintenance.ts +108 -0
- package/src/memory/db.ts +1 -0
- package/src/memory/embedding-backend.test.ts +75 -0
- package/src/memory/embedding-backend.ts +131 -5
- package/src/memory/embedding-gemini.test.ts +54 -0
- package/src/memory/embedding-gemini.ts +20 -9
- package/src/memory/embedding-local.ts +176 -17
- package/src/memory/graph/consolidation.ts +10 -23
- package/src/memory/graph/conversation-graph-memory.ts +15 -0
- package/src/memory/graph/extraction-job.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 +67 -40
- package/src/memory/graph/scoring.test.ts +186 -0
- package/src/memory/graph/scoring.ts +31 -1
- package/src/memory/graph/store.test.ts +7 -3
- package/src/memory/graph/store.ts +47 -12
- 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 +137 -60
- package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
- package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
- package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
- package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
- package/src/memory/migrations/217-conversation-host-access.ts +40 -0
- package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
- 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 +12 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/qdrant-manager.ts +43 -16
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/oauth.ts +21 -13
- 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/AGENTS.md +76 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +25 -19
- package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
- package/src/oauth/byo-connection.test.ts +26 -9
- package/src/oauth/byo-connection.ts +10 -8
- package/src/oauth/connect-orchestrator.ts +25 -21
- package/src/oauth/connect-types.ts +3 -3
- package/src/oauth/connection-resolver.test.ts +17 -4
- package/src/oauth/connection-resolver.ts +22 -18
- package/src/oauth/connection.ts +3 -1
- package/src/oauth/manual-token-connection.ts +13 -13
- package/src/oauth/oauth-store.ts +223 -100
- package/src/oauth/platform-connection.test.ts +101 -3
- package/src/oauth/platform-connection.ts +56 -35
- package/src/oauth/provider-serializer.ts +31 -5
- package/src/oauth/revoke.ts +76 -0
- package/src/oauth/seed-providers.ts +133 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/checker.ts +16 -6
- package/src/permissions/defaults.ts +49 -1
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -1
- package/src/permissions/trust-store.ts +3 -3
- package/src/permissions/v2-consent-policy.ts +87 -0
- 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 +76 -38
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -105
- 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 -60
- 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 +10 -1
- package/src/runtime/AGENTS.md +65 -0
- package/src/runtime/__tests__/agent-wake.test.ts +831 -0
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
- package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
- package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -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/assistant-event-hub.ts +2 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +48 -0
- package/src/runtime/auth/middleware.ts +98 -0
- package/src/runtime/auth/route-policy.ts +33 -9
- package/src/runtime/auth/token-service.ts +56 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/capability-tokens.ts +414 -0
- package/src/runtime/channel-approvals.ts +18 -5
- 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 +368 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
- package/src/runtime/guardian-decision-types.ts +7 -0
- package/src/runtime/http-server.ts +815 -75
- package/src/runtime/http-types.ts +6 -2
- package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +198 -0
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
- package/src/runtime/migrations/migration-transport.ts +7 -0
- package/src/runtime/migrations/migration-wizard.ts +23 -2
- package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
- package/src/runtime/migrations/vbundle-builder.ts +145 -38
- package/src/runtime/migrations/vbundle-import-analyzer.ts +96 -1
- package/src/runtime/migrations/vbundle-importer.ts +89 -5
- package/src/runtime/pending-interactions.ts +18 -13
- 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/approval-routes.ts +90 -16
- 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 +556 -0
- 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 -141
- package/src/runtime/routes/conversation-management-routes.ts +223 -0
- package/src/runtime/routes/conversation-routes.ts +598 -103
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- package/src/runtime/routes/filing-routes.ts +93 -0
- package/src/runtime/routes/guardian-action-routes.ts +24 -13
- 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 +268 -0
- package/src/runtime/routes/host-file-routes.ts +9 -1
- package/src/runtime/routes/identity-intro-cache.ts +7 -3
- package/src/runtime/routes/identity-routes.ts +262 -33
- 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/log-export-routes.ts +42 -22
- package/src/runtime/routes/memory-item-routes.test.ts +3 -2
- package/src/runtime/routes/memory-item-routes.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +122 -2
- package/src/runtime/routes/oauth-apps.ts +15 -17
- package/src/runtime/routes/oauth-providers.ts +4 -0
- package/src/runtime/routes/schedule-routes.ts +24 -11
- package/src/runtime/routes/settings-routes.ts +31 -102
- package/src/runtime/routes/skills-routes.ts +128 -9
- package/src/runtime/routes/stt-routes.ts +233 -0
- package/src/runtime/routes/subagents-routes.ts +14 -10
- 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 +38 -9
- 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/routes/workspace-routes.test.ts +22 -0
- package/src/runtime/routes/workspace-routes.ts +8 -1
- package/src/runtime/routes/workspace-utils.ts +2 -0
- 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 +57 -5
- package/src/security/ces-credential-client.ts +20 -0
- package/src/security/ces-rpc-credential-backend.ts +17 -0
- package/src/security/credential-backend.ts +5 -0
- package/src/security/oauth2.ts +68 -29
- package/src/security/secure-keys.ts +143 -27
- package/src/security/token-manager.ts +31 -10
- package/src/sequence/engine.ts +23 -0
- package/src/sequence/types.ts +1 -1
- package/src/skills/catalog-files.ts +554 -0
- 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 +169 -40
- package/src/subagent/types.ts +19 -0
- package/src/tools/apps/executors.ts +11 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
- 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/auth-detector.ts +43 -12
- package/src/tools/browser/browser-execution.ts +1787 -342
- package/src/tools/browser/browser-manager.ts +81 -12
- 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__/accessibility-snapshot.test.ts +318 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +1263 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +359 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1993 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
- package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
- package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +1007 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +744 -0
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +868 -0
- package/src/tools/browser/cdp-client/errors.ts +49 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +148 -0
- package/src/tools/browser/cdp-client/factory.ts +914 -0
- package/src/tools/browser/cdp-client/index.ts +28 -0
- package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
- package/src/tools/browser/cdp-client/types.ts +120 -0
- package/src/tools/credentials/vault.ts +35 -6
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +1 -1
- package/src/tools/host-filesystem/read.ts +12 -15
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +21 -16
- package/src/tools/network/web-fetch.ts +5 -2
- package/src/tools/network/web-search.ts +5 -2
- package/src/tools/permission-checker.ts +77 -82
- package/src/tools/registry.ts +0 -2
- package/src/tools/secret-detection-handler.ts +34 -0
- package/src/tools/shared/filesystem/image-read.ts +61 -40
- 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/subagent/spawn.ts +47 -3
- package/src/tools/subagent/status.ts +2 -0
- package/src/tools/system/register.ts +2 -16
- package/src/tools/terminal/safe-env.ts +15 -0
- package/src/tools/terminal/shell.ts +36 -20
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/tool-manifest.ts +21 -0
- package/src/tools/types.ts +19 -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 +63 -24
- 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 +31 -1
- package/src/workspace/turn-commit.ts +31 -0
- package/src/__tests__/chrome-cdp.test.ts +0 -419
- package/src/__tests__/email-cli.test.ts +0 -297
- package/src/__tests__/email-service-config-fallback.test.ts +0 -102
- package/src/__tests__/permission-mode-sse.test.ts +0 -418
- package/src/__tests__/permission-mode-store.test.ts +0 -277
- package/src/browser-extension-relay/protocol.ts +0 -63
- package/src/browser-extension-relay/server.ts +0 -203
- package/src/cli/commands/browser-relay.ts +0 -536
- package/src/config/schemas/sandbox.ts +0 -14
- 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/permissions/permission-mode-store.ts +0 -180
- package/src/prompts/templates/USER.md +0 -13
- package/src/providers/speech-to-text/types.ts +0 -17
- package/src/tools/browser/chrome-cdp.ts +0 -239
- package/src/tools/system/set-permission-mode.ts +0 -103
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output adapter for media-stream call egress.
|
|
3
|
+
*
|
|
4
|
+
* Implements the {@link CallTransport} interface so the call controller
|
|
5
|
+
* can send synthesized audio and lifecycle signals through a Twilio Media
|
|
6
|
+
* Stream WebSocket connection.
|
|
7
|
+
*
|
|
8
|
+
* Unlike the ConversationRelay transport which sends text tokens for
|
|
9
|
+
* Twilio's built-in TTS, the media-stream transport operates on raw
|
|
10
|
+
* audio frames:
|
|
11
|
+
*
|
|
12
|
+
* - `sendTextToken()` — Accumulates text tokens and, on `last: true`,
|
|
13
|
+
* synthesizes the accumulated text via the configured TTS provider,
|
|
14
|
+
* transcodes the resulting audio to mu-law 8 kHz, and streams it as
|
|
15
|
+
* media frames to Twilio. An empty token with `last: true` sends an
|
|
16
|
+
* end-of-turn mark without synthesizing.
|
|
17
|
+
*
|
|
18
|
+
* - `sendPlayUrl()` — Fetches audio from the given URL, transcodes it
|
|
19
|
+
* to mu-law 8 kHz, and streams the resulting frames to Twilio.
|
|
20
|
+
*
|
|
21
|
+
* - `endSession()` — Closes the underlying WebSocket, which triggers
|
|
22
|
+
* Twilio to tear down the media stream and (eventually) the call.
|
|
23
|
+
*
|
|
24
|
+
* - `sendAudioPayload()` — Sends a base64-encoded audio frame to
|
|
25
|
+
* Twilio for playback on the caller's channel.
|
|
26
|
+
*
|
|
27
|
+
* - `sendMark()` — Inserts a named mark into the outbound audio
|
|
28
|
+
* pipeline. Twilio will echo it back as a `mark` event once the
|
|
29
|
+
* caller reaches that point in playback.
|
|
30
|
+
*
|
|
31
|
+
* - `clearAudio()` — Clears any queued outbound audio (barge-in),
|
|
32
|
+
* flushes the internal playback queue, and aborts in-flight synthesis.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import type { ServerWebSocket } from "bun";
|
|
36
|
+
|
|
37
|
+
import { getLogger } from "../util/logger.js";
|
|
38
|
+
import type { CallTransport } from "./call-transport.js";
|
|
39
|
+
import {
|
|
40
|
+
chunkMulawToBase64Frames,
|
|
41
|
+
pcm16ToMulaw,
|
|
42
|
+
} from "./media-stream-audio-transcode.js";
|
|
43
|
+
import type {
|
|
44
|
+
MediaStreamClearCommand,
|
|
45
|
+
MediaStreamSendMarkCommand,
|
|
46
|
+
MediaStreamSendMediaCommand,
|
|
47
|
+
} from "./media-stream-protocol.js";
|
|
48
|
+
|
|
49
|
+
const log = getLogger("media-stream-output");
|
|
50
|
+
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Connection state
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
export type MediaStreamOutputState = "connected" | "closed";
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Playback queue entry
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* A queued playback item. The output adapter processes items sequentially
|
|
63
|
+
* to preserve ordering when multiple TTS segments or play-URL fetches
|
|
64
|
+
* are in flight concurrently.
|
|
65
|
+
*/
|
|
66
|
+
type PlaybackItem =
|
|
67
|
+
| { type: "frames"; frames: string[] }
|
|
68
|
+
| { type: "synthesize"; text: string }
|
|
69
|
+
| { type: "fetch-url"; url: string }
|
|
70
|
+
| { type: "mark"; name: string };
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Output adapter
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
export class MediaStreamOutput implements CallTransport {
|
|
77
|
+
private streamSid: string;
|
|
78
|
+
private ws: ServerWebSocket<unknown>;
|
|
79
|
+
private state: MediaStreamOutputState = "connected";
|
|
80
|
+
|
|
81
|
+
/** Accumulated text from sendTextToken calls before the final `last: true`. */
|
|
82
|
+
private textBuffer = "";
|
|
83
|
+
|
|
84
|
+
/** FIFO queue of playback items awaiting delivery. */
|
|
85
|
+
private playbackQueue: PlaybackItem[] = [];
|
|
86
|
+
|
|
87
|
+
/** True when the queue drain loop is actively running. */
|
|
88
|
+
private draining = false;
|
|
89
|
+
|
|
90
|
+
/** Abort controller for the currently in-flight synthesis/fetch. */
|
|
91
|
+
private activePlaybackAbort: AbortController | null = null;
|
|
92
|
+
|
|
93
|
+
/** Monotonic version counter — incremented on clearAudio to invalidate stale work. */
|
|
94
|
+
private playbackVersion = 0;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The media-stream transport requires WAV (PCM) audio because its
|
|
98
|
+
* mu-law transcoder cannot decode compressed formats (mp3, opus).
|
|
99
|
+
*/
|
|
100
|
+
readonly requiresWavAudio = true;
|
|
101
|
+
|
|
102
|
+
constructor(ws: ServerWebSocket<unknown>, streamSid: string) {
|
|
103
|
+
this.ws = ws;
|
|
104
|
+
this.streamSid = streamSid;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── CallTransport interface ─────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Accumulate text tokens for TTS synthesis. When `last` is true, the
|
|
111
|
+
* accumulated text is queued for synthesis and delivery as media frames.
|
|
112
|
+
*
|
|
113
|
+
* An empty token with `last: true` signals end-of-turn without TTS.
|
|
114
|
+
* This mirrors ConversationRelay semantics where an empty last token
|
|
115
|
+
* transitions the relay from "assistant speaking" to "caller speaking".
|
|
116
|
+
* On the media-stream transport we send a mark instead.
|
|
117
|
+
*/
|
|
118
|
+
sendTextToken(token: string, last: boolean): void {
|
|
119
|
+
if (this.state === "closed") return;
|
|
120
|
+
|
|
121
|
+
this.textBuffer += token;
|
|
122
|
+
|
|
123
|
+
if (last) {
|
|
124
|
+
const text = this.textBuffer.trim();
|
|
125
|
+
this.textBuffer = "";
|
|
126
|
+
|
|
127
|
+
if (text.length > 0) {
|
|
128
|
+
// Queue synthesis of the accumulated text.
|
|
129
|
+
this.enqueuePlayback({ type: "synthesize", text });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Always send an end-of-turn mark so the media-stream server
|
|
133
|
+
// can detect turn boundaries (analogous to ConversationRelay's
|
|
134
|
+
// empty last token).
|
|
135
|
+
this.enqueuePlayback({ type: "mark", name: "end-of-turn" });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Fetch audio from the given URL, transcode, and stream as media frames.
|
|
141
|
+
*
|
|
142
|
+
* The audio store (used by the synthesized-play path in call-controller)
|
|
143
|
+
* serves streaming audio at these URLs. We fetch the content, decode to
|
|
144
|
+
* PCM, and re-encode as mu-law frames for Twilio.
|
|
145
|
+
*/
|
|
146
|
+
sendPlayUrl(url: string): void {
|
|
147
|
+
if (this.state === "closed") return;
|
|
148
|
+
this.enqueuePlayback({ type: "fetch-url", url });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Signal the transport to end the call session by closing the
|
|
153
|
+
* WebSocket. Twilio tears down the media stream when the socket
|
|
154
|
+
* closes.
|
|
155
|
+
*/
|
|
156
|
+
endSession(reason?: string): void {
|
|
157
|
+
if (this.state === "closed") return;
|
|
158
|
+
this.state = "closed";
|
|
159
|
+
|
|
160
|
+
// Cancel any in-flight playback
|
|
161
|
+
this.flushPlaybackQueue();
|
|
162
|
+
|
|
163
|
+
log.info(
|
|
164
|
+
{ streamSid: this.streamSid, reason },
|
|
165
|
+
"Media stream output ending session",
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
this.ws.close(1000, reason ?? "session-ended");
|
|
170
|
+
} catch (err) {
|
|
171
|
+
log.warn(
|
|
172
|
+
{ err, streamSid: this.streamSid },
|
|
173
|
+
"Failed to close media-stream WebSocket",
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Return the current connection-level state. The controller uses this
|
|
180
|
+
* to suppress silence nudges during guardian wait states.
|
|
181
|
+
*/
|
|
182
|
+
getConnectionState(): string {
|
|
183
|
+
return this.state;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── Media-stream specific methods ───────────────────────────────────
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Send a base64-encoded audio frame to Twilio for playback.
|
|
190
|
+
*/
|
|
191
|
+
sendAudioPayload(base64Payload: string): void {
|
|
192
|
+
if (this.state === "closed") return;
|
|
193
|
+
|
|
194
|
+
const command: MediaStreamSendMediaCommand = {
|
|
195
|
+
event: "media",
|
|
196
|
+
streamSid: this.streamSid,
|
|
197
|
+
media: {
|
|
198
|
+
payload: base64Payload,
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
this.ws.send(JSON.stringify(command));
|
|
204
|
+
} catch (err) {
|
|
205
|
+
log.error(
|
|
206
|
+
{ err, streamSid: this.streamSid },
|
|
207
|
+
"Failed to send audio payload",
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Insert a named mark into the outbound audio stream. Twilio echoes
|
|
214
|
+
* back a `mark` event when the caller reaches this point in playback.
|
|
215
|
+
*/
|
|
216
|
+
sendMark(name: string): void {
|
|
217
|
+
if (this.state === "closed") return;
|
|
218
|
+
|
|
219
|
+
const command: MediaStreamSendMarkCommand = {
|
|
220
|
+
event: "mark",
|
|
221
|
+
streamSid: this.streamSid,
|
|
222
|
+
mark: { name },
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
this.ws.send(JSON.stringify(command));
|
|
227
|
+
} catch (err) {
|
|
228
|
+
log.error(
|
|
229
|
+
{ err, streamSid: this.streamSid },
|
|
230
|
+
"Failed to send mark command",
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Clear any queued outbound audio. Used for barge-in scenarios where
|
|
237
|
+
* the caller interrupts the assistant.
|
|
238
|
+
*
|
|
239
|
+
* This performs three actions:
|
|
240
|
+
* 1. Sends a Twilio `clear` command to flush Twilio's outbound buffer.
|
|
241
|
+
* 2. Aborts any in-flight TTS synthesis or URL fetch.
|
|
242
|
+
* 3. Drains the internal playback queue so no further frames are sent.
|
|
243
|
+
*/
|
|
244
|
+
clearAudio(): void {
|
|
245
|
+
if (this.state === "closed") return;
|
|
246
|
+
|
|
247
|
+
// Flush our internal playback queue and abort in-flight work.
|
|
248
|
+
this.flushPlaybackQueue();
|
|
249
|
+
|
|
250
|
+
// Send the Twilio clear command to flush Twilio's outbound buffer.
|
|
251
|
+
const command: MediaStreamClearCommand = {
|
|
252
|
+
event: "clear",
|
|
253
|
+
streamSid: this.streamSid,
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
this.ws.send(JSON.stringify(command));
|
|
258
|
+
} catch (err) {
|
|
259
|
+
log.error(
|
|
260
|
+
{ err, streamSid: this.streamSid },
|
|
261
|
+
"Failed to send clear command",
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Update the stream SID (e.g. after receiving the `start` event).
|
|
268
|
+
*/
|
|
269
|
+
setStreamSid(streamSid: string): void {
|
|
270
|
+
this.streamSid = streamSid;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Get the current stream SID.
|
|
275
|
+
*/
|
|
276
|
+
getStreamSid(): string {
|
|
277
|
+
return this.streamSid;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Mark the output as closed without sending a close frame.
|
|
282
|
+
* Used when the WebSocket is already closed by the remote side.
|
|
283
|
+
*/
|
|
284
|
+
markClosed(): void {
|
|
285
|
+
this.state = "closed";
|
|
286
|
+
this.flushPlaybackQueue();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Returns the number of items currently in the playback queue.
|
|
291
|
+
* Exposed for test assertions.
|
|
292
|
+
*/
|
|
293
|
+
getPlaybackQueueLength(): number {
|
|
294
|
+
return this.playbackQueue.length;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Runtime check for closed state. Used instead of direct property access
|
|
299
|
+
* in async methods because TypeScript's control flow analysis cannot
|
|
300
|
+
* track that `this.state` may change between `await` points.
|
|
301
|
+
*/
|
|
302
|
+
private isClosed(): boolean {
|
|
303
|
+
return this.state === "closed";
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ── Private: playback queue management ──────────────────────────────
|
|
307
|
+
|
|
308
|
+
private enqueuePlayback(item: PlaybackItem): void {
|
|
309
|
+
this.playbackQueue.push(item);
|
|
310
|
+
if (!this.draining) {
|
|
311
|
+
void this.drainPlaybackQueue();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Flush the playback queue and abort in-flight work. Increments the
|
|
317
|
+
* playback version so any stale async work is discarded.
|
|
318
|
+
*/
|
|
319
|
+
private flushPlaybackQueue(): void {
|
|
320
|
+
this.playbackQueue.length = 0;
|
|
321
|
+
this.textBuffer = "";
|
|
322
|
+
this.playbackVersion++;
|
|
323
|
+
if (this.activePlaybackAbort) {
|
|
324
|
+
this.activePlaybackAbort.abort();
|
|
325
|
+
this.activePlaybackAbort = null;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Process playback items sequentially. Each item either sends frames
|
|
331
|
+
* directly (pre-encoded) or performs async work (synthesis, fetch)
|
|
332
|
+
* before sending.
|
|
333
|
+
*/
|
|
334
|
+
private async drainPlaybackQueue(): Promise<void> {
|
|
335
|
+
if (this.draining) return;
|
|
336
|
+
this.draining = true;
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
while (this.playbackQueue.length > 0 && !this.isClosed()) {
|
|
340
|
+
const item = this.playbackQueue.shift()!;
|
|
341
|
+
const version = this.playbackVersion;
|
|
342
|
+
|
|
343
|
+
switch (item.type) {
|
|
344
|
+
case "frames":
|
|
345
|
+
this.sendFrames(item.frames);
|
|
346
|
+
break;
|
|
347
|
+
|
|
348
|
+
case "mark":
|
|
349
|
+
this.sendMark(item.name);
|
|
350
|
+
break;
|
|
351
|
+
|
|
352
|
+
case "synthesize":
|
|
353
|
+
await this.processSynthesizeItem(item.text, version);
|
|
354
|
+
break;
|
|
355
|
+
|
|
356
|
+
case "fetch-url":
|
|
357
|
+
await this.processFetchUrlItem(item.url, version);
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// If the playback version changed (clearAudio was called), stop
|
|
362
|
+
// processing stale items.
|
|
363
|
+
if (version !== this.playbackVersion) break;
|
|
364
|
+
}
|
|
365
|
+
} finally {
|
|
366
|
+
this.draining = false;
|
|
367
|
+
// If items were enqueued during a version-mismatch break (e.g. the
|
|
368
|
+
// end-of-turn mark from handleInterrupt after clearAudio), restart
|
|
369
|
+
// draining so they are not stranded.
|
|
370
|
+
if (this.playbackQueue.length > 0 && !this.isClosed()) {
|
|
371
|
+
void this.drainPlaybackQueue();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Send an array of pre-encoded base64 audio frames to Twilio.
|
|
378
|
+
*/
|
|
379
|
+
private sendFrames(frames: string[]): void {
|
|
380
|
+
for (const frame of frames) {
|
|
381
|
+
this.sendAudioPayload(frame);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Synthesize text via the TTS provider and send resulting audio as
|
|
387
|
+
* mu-law frames. Falls back to a silent frame if synthesis fails.
|
|
388
|
+
*/
|
|
389
|
+
private async processSynthesizeItem(
|
|
390
|
+
text: string,
|
|
391
|
+
version: number,
|
|
392
|
+
): Promise<void> {
|
|
393
|
+
const abortController = new AbortController();
|
|
394
|
+
this.activePlaybackAbort = abortController;
|
|
395
|
+
|
|
396
|
+
try {
|
|
397
|
+
const { resolveCallTtsProvider } =
|
|
398
|
+
await import("./resolve-call-tts-provider.js");
|
|
399
|
+
// Request WAV so audioBufferToFrames gets PCM it can transcode
|
|
400
|
+
// to mu-law. Compressed formats (mp3, opus) would be sent as raw
|
|
401
|
+
// bytes and produce garbled audio.
|
|
402
|
+
const { provider, audioFormat } = resolveCallTtsProvider({
|
|
403
|
+
preferWav: true,
|
|
404
|
+
});
|
|
405
|
+
if (!provider) {
|
|
406
|
+
log.warn(
|
|
407
|
+
{ streamSid: this.streamSid },
|
|
408
|
+
"No TTS provider available for media-stream synthesis",
|
|
409
|
+
);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (version !== this.playbackVersion || this.isClosed()) return;
|
|
414
|
+
|
|
415
|
+
// Synthesize the text. Request PCM output so the media-stream
|
|
416
|
+
// transport receives raw samples it can transcode to mu-law.
|
|
417
|
+
// Providers that support it (e.g. ElevenLabs pcm_16000) will
|
|
418
|
+
// return raw PCM; others fall back to their default format and
|
|
419
|
+
// the content-type sniffing below handles the mismatch.
|
|
420
|
+
const result = await provider.synthesize({
|
|
421
|
+
text,
|
|
422
|
+
useCase: "phone-call",
|
|
423
|
+
outputFormat: "pcm",
|
|
424
|
+
signal: abortController.signal,
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
if (version !== this.playbackVersion || this.isClosed()) return;
|
|
428
|
+
|
|
429
|
+
// Derive the format from the provider's actual content type rather
|
|
430
|
+
// than the declared audioFormat. The declared format may not match
|
|
431
|
+
// reality (e.g. preferWav requests WAV but the provider returns mp3).
|
|
432
|
+
// audioBufferToFrames also sniffs magic bytes as a safety net.
|
|
433
|
+
const actualFormat: "mp3" | "wav" | "opus" | "pcm" =
|
|
434
|
+
result.contentType.includes("wav") ||
|
|
435
|
+
result.contentType.includes("x-wav")
|
|
436
|
+
? "wav"
|
|
437
|
+
: result.contentType.includes("opus")
|
|
438
|
+
? "opus"
|
|
439
|
+
: result.contentType.includes("mpeg") ||
|
|
440
|
+
result.contentType.includes("mp3")
|
|
441
|
+
? "mp3"
|
|
442
|
+
: result.contentType.includes("pcm") ||
|
|
443
|
+
result.contentType.includes("x-raw")
|
|
444
|
+
? "pcm"
|
|
445
|
+
: audioFormat; // fall back to declared format for unknown types
|
|
446
|
+
const frames = this.audioBufferToFrames(result.audio, actualFormat);
|
|
447
|
+
if (version !== this.playbackVersion || this.isClosed()) return;
|
|
448
|
+
|
|
449
|
+
this.sendFrames(frames);
|
|
450
|
+
} catch (err) {
|
|
451
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
452
|
+
log.debug(
|
|
453
|
+
{ streamSid: this.streamSid },
|
|
454
|
+
"Media-stream TTS synthesis aborted (barge-in)",
|
|
455
|
+
);
|
|
456
|
+
} else {
|
|
457
|
+
log.error(
|
|
458
|
+
{ err, streamSid: this.streamSid },
|
|
459
|
+
"Media-stream TTS synthesis failed",
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
} finally {
|
|
463
|
+
if (this.activePlaybackAbort === abortController) {
|
|
464
|
+
this.activePlaybackAbort = null;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Fetch audio from a URL (typically the audio store), transcode to
|
|
471
|
+
* mu-law frames, and send to Twilio.
|
|
472
|
+
*/
|
|
473
|
+
private async processFetchUrlItem(
|
|
474
|
+
url: string,
|
|
475
|
+
version: number,
|
|
476
|
+
): Promise<void> {
|
|
477
|
+
const abortController = new AbortController();
|
|
478
|
+
this.activePlaybackAbort = abortController;
|
|
479
|
+
|
|
480
|
+
try {
|
|
481
|
+
const response = await fetch(url, { signal: abortController.signal });
|
|
482
|
+
if (!response.ok) {
|
|
483
|
+
log.error(
|
|
484
|
+
{ url, status: response.status, streamSid: this.streamSid },
|
|
485
|
+
"Failed to fetch audio from URL for media-stream playback",
|
|
486
|
+
);
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (version !== this.playbackVersion || this.isClosed()) return;
|
|
491
|
+
|
|
492
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
493
|
+
if (version !== this.playbackVersion || this.isClosed()) return;
|
|
494
|
+
|
|
495
|
+
const contentType = response.headers.get("content-type") ?? "audio/mpeg";
|
|
496
|
+
const format: "mp3" | "wav" | "opus" | "pcm" = contentType.includes("wav")
|
|
497
|
+
? "wav"
|
|
498
|
+
: contentType.includes("opus")
|
|
499
|
+
? "opus"
|
|
500
|
+
: contentType.includes("pcm") || contentType.includes("x-raw")
|
|
501
|
+
? "pcm"
|
|
502
|
+
: "mp3";
|
|
503
|
+
|
|
504
|
+
const frames = this.audioBufferToFrames(buffer, format);
|
|
505
|
+
if (version !== this.playbackVersion || this.isClosed()) return;
|
|
506
|
+
|
|
507
|
+
this.sendFrames(frames);
|
|
508
|
+
} catch (err) {
|
|
509
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
510
|
+
log.debug(
|
|
511
|
+
{ streamSid: this.streamSid },
|
|
512
|
+
"Media-stream URL fetch aborted (barge-in)",
|
|
513
|
+
);
|
|
514
|
+
} else {
|
|
515
|
+
log.error(
|
|
516
|
+
{ err, url, streamSid: this.streamSid },
|
|
517
|
+
"Media-stream URL fetch failed",
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
} finally {
|
|
521
|
+
if (this.activePlaybackAbort === abortController) {
|
|
522
|
+
this.activePlaybackAbort = null;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Convert an audio buffer (from TTS synthesis or URL fetch) into
|
|
529
|
+
* base64-encoded mu-law frames.
|
|
530
|
+
*
|
|
531
|
+
* Rather than trusting the declared `format` parameter (which may not
|
|
532
|
+
* match the actual bytes — e.g. when a provider is asked for WAV but
|
|
533
|
+
* returns mp3), this method **sniffs the magic bytes** to detect the
|
|
534
|
+
* real format:
|
|
535
|
+
*
|
|
536
|
+
* - **WAV** (`RIFF` header, bytes `0x52 0x49 0x46 0x46`): extracts
|
|
537
|
+
* raw PCM data from the WAV container and converts to mu-law.
|
|
538
|
+
* - **PCM** (raw 16-bit signed LE at a known sample rate): converts
|
|
539
|
+
* directly to mu-law, downsampling from 16 kHz to 8 kHz if needed.
|
|
540
|
+
* - **Compressed formats** (mp3, opus): cannot be decoded in this
|
|
541
|
+
* path — returns empty frames (silence) with a warning. Compressed
|
|
542
|
+
* formats require the audio-store playback path (`sendPlayUrl`)
|
|
543
|
+
* for correct transcoding. Silence is preferable to garbled audio.
|
|
544
|
+
*/
|
|
545
|
+
private audioBufferToFrames(
|
|
546
|
+
audio: Buffer,
|
|
547
|
+
format: "mp3" | "wav" | "opus" | "pcm",
|
|
548
|
+
): string[] {
|
|
549
|
+
// Sniff the actual bytes rather than trusting the declared format.
|
|
550
|
+
// WAV files always start with the ASCII magic "RIFF" (0x52494646).
|
|
551
|
+
const isWav =
|
|
552
|
+
audio.length >= 44 &&
|
|
553
|
+
audio[0] === 0x52 && // R
|
|
554
|
+
audio[1] === 0x49 && // I
|
|
555
|
+
audio[2] === 0x46 && // F
|
|
556
|
+
audio[3] === 0x46; // F
|
|
557
|
+
|
|
558
|
+
if (isWav) {
|
|
559
|
+
// Extract raw PCM from WAV container. Standard WAV has a 44-byte
|
|
560
|
+
// header; the rest is PCM data (assuming 16-bit signed LE, 8 kHz).
|
|
561
|
+
const pcmData = audio.subarray(44);
|
|
562
|
+
if (pcmData.length < 2) return [];
|
|
563
|
+
const mulawBuffer = pcm16ToMulaw(pcmData);
|
|
564
|
+
return chunkMulawToBase64Frames(mulawBuffer);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// When the declared format is "wav" but the RIFF check failed, the
|
|
568
|
+
// bytes might be either:
|
|
569
|
+
// (a) Raw PCM stored under audio/wav content-type (when
|
|
570
|
+
// outputFormat: "pcm" is used with createStreamingEntry("wav"))
|
|
571
|
+
// (b) Compressed audio (mp3/opus) from a provider that ignores
|
|
572
|
+
// outputFormat (e.g. Fish Audio defaults to mp3)
|
|
573
|
+
//
|
|
574
|
+
// Sniff magic bytes to distinguish: mp3 frames start with 0xFF sync
|
|
575
|
+
// byte or ID3 tag (0x49 0x44 0x33); Ogg/opus starts with "OggS".
|
|
576
|
+
// Anything else is assumed to be raw PCM.
|
|
577
|
+
if (format === "wav") {
|
|
578
|
+
const isMp3 =
|
|
579
|
+
audio.length >= 2 &&
|
|
580
|
+
((audio[0] === 0xff && (audio[1] & 0xe0) === 0xe0) || // MPEG sync
|
|
581
|
+
(audio[0] === 0x49 && audio[1] === 0x44 && audio[2] === 0x33)); // ID3
|
|
582
|
+
const isOgg =
|
|
583
|
+
audio.length >= 4 &&
|
|
584
|
+
audio[0] === 0x4f && // O
|
|
585
|
+
audio[1] === 0x67 && // g
|
|
586
|
+
audio[2] === 0x67 && // g
|
|
587
|
+
audio[3] === 0x53; // S
|
|
588
|
+
|
|
589
|
+
if (isMp3 || isOgg) {
|
|
590
|
+
log.warn(
|
|
591
|
+
{
|
|
592
|
+
streamSid: this.streamSid,
|
|
593
|
+
declaredFormat: format,
|
|
594
|
+
detectedFormat: isMp3 ? "mp3" : "opus",
|
|
595
|
+
audioBytes: audio.length,
|
|
596
|
+
},
|
|
597
|
+
"Declared format is WAV but bytes are compressed — returning silence",
|
|
598
|
+
);
|
|
599
|
+
return [];
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
log.debug(
|
|
603
|
+
{ streamSid: this.streamSid, audioBytes: audio.length },
|
|
604
|
+
"Declared format is WAV but no RIFF header — treating as raw PCM",
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Raw PCM (e.g. from ElevenLabs pcm_16000, or WAV-declared content
|
|
609
|
+
// that is actually headerless PCM): convert directly to mu-law.
|
|
610
|
+
// ElevenLabs pcm_16000 produces 16-bit signed LE at 16 kHz. Twilio
|
|
611
|
+
// needs 8 kHz mu-law, so we downsample by taking every other sample.
|
|
612
|
+
if (format === "pcm" || format === "wav") {
|
|
613
|
+
if (audio.length < 2) return [];
|
|
614
|
+
// Downsample 16 kHz -> 8 kHz by taking every other sample.
|
|
615
|
+
// Each sample is 2 bytes (16-bit LE), so we step by 4 bytes.
|
|
616
|
+
const sampleCount = Math.floor(audio.length / 2);
|
|
617
|
+
const downsampledCount = Math.floor(sampleCount / 2);
|
|
618
|
+
const downsampled = Buffer.alloc(downsampledCount * 2);
|
|
619
|
+
for (let i = 0; i < downsampledCount; i++) {
|
|
620
|
+
// Copy every other 16-bit sample
|
|
621
|
+
downsampled[i * 2] = audio[i * 4];
|
|
622
|
+
downsampled[i * 2 + 1] = audio[i * 4 + 1];
|
|
623
|
+
}
|
|
624
|
+
const mulawBuffer = pcm16ToMulaw(downsampled);
|
|
625
|
+
return chunkMulawToBase64Frames(mulawBuffer);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Compressed formats (mp3, opus) cannot be decoded in this direct
|
|
629
|
+
// synthesis path. Rather than passing compressed bytes through as
|
|
630
|
+
// raw mu-law frames (which produces garbled audio), return empty
|
|
631
|
+
// frames (silence). The caller should use the audio-store playback
|
|
632
|
+
// path (sendPlayUrl) which handles transcoding correctly.
|
|
633
|
+
if (format === "mp3" || format === "opus") {
|
|
634
|
+
log.warn(
|
|
635
|
+
{
|
|
636
|
+
streamSid: this.streamSid,
|
|
637
|
+
format,
|
|
638
|
+
audioBytes: audio.length,
|
|
639
|
+
},
|
|
640
|
+
"Compressed audio format cannot be transcoded to mu-law in the direct synthesis path — " +
|
|
641
|
+
"returning silence. Use the audio-store playback path (sendPlayUrl) for correct transcoding.",
|
|
642
|
+
);
|
|
643
|
+
return [];
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Unknown format — log a warning and attempt raw passthrough. This
|
|
647
|
+
// is a last-resort fallback; callers should ensure they request a
|
|
648
|
+
// format that this transport can handle (WAV or raw PCM).
|
|
649
|
+
log.warn(
|
|
650
|
+
{
|
|
651
|
+
streamSid: this.streamSid,
|
|
652
|
+
declaredFormat: format,
|
|
653
|
+
audioBytes: audio.length,
|
|
654
|
+
headerHex: audio.subarray(0, 4).toString("hex"),
|
|
655
|
+
},
|
|
656
|
+
"Unrecognized audio format — attempting raw passthrough (may produce garbled audio)",
|
|
657
|
+
);
|
|
658
|
+
return chunkMulawToBase64Frames(audio);
|
|
659
|
+
}
|
|
660
|
+
}
|