@vellumai/assistant 0.12.1-staging.1 → 0.12.1
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/AGENTS.md +0 -4
- package/ARCHITECTURE.md +12 -11
- package/docs/architecture/turn-actor.md +8 -15
- package/openapi.yaml +1 -291
- package/package.json +1 -1
- package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
- package/src/__tests__/app-compiler.test.ts +0 -6
- package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
- package/src/__tests__/app-dir-path-guard.test.ts +3 -1
- package/src/__tests__/app-source-watcher.test.ts +218 -0
- package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
- package/src/__tests__/computer-use-tools.test.ts +5 -17
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +29 -28
- package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
- package/src/__tests__/config-watcher.test.ts +8 -23
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +0 -26
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
- package/src/__tests__/conversation-queue.test.ts +207 -48
- package/src/__tests__/conversation-retry-discard.test.ts +1 -54
- package/src/__tests__/conversation-skill-tools.test.ts +0 -27
- package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
- package/src/__tests__/conversation-title-service.test.ts +10 -14
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
- package/src/__tests__/credential-record-retire.test.ts +1 -171
- package/src/__tests__/credential-routes.test.ts +3 -212
- package/src/__tests__/credential-security-invariants.test.ts +1 -2
- package/src/__tests__/cu-unified-flow.test.ts +2 -8
- package/src/__tests__/drain-kick-guard.test.ts +2 -0
- package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/host-bash-proxy.test.ts +45 -26
- package/src/__tests__/host-browser-proxy.test.ts +17 -6
- package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
- package/src/__tests__/host-file-proxy.test.ts +23 -11
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
- package/src/__tests__/llm-catalog-parity.test.ts +0 -25
- package/src/__tests__/mcp-auth-routes.test.ts +9 -6
- package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
- package/src/__tests__/mcp-health-check.test.ts +11 -8
- package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
- package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
- package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
- package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
- package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +548 -355
- package/src/__tests__/model-intents.test.ts +0 -3
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
- package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
- package/src/__tests__/script-proxy-certs.test.ts +0 -35
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/speaker-identification.test.ts +56 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
- package/src/__tests__/subagent-tools.test.ts +2 -3
- package/src/__tests__/telephony-tts-guard.test.ts +3 -38
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
- package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
- package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
- package/src/__tests__/workspace-git-service.test.ts +0 -26
- package/src/acp/__tests__/agent-process.test.ts +4 -258
- package/src/acp/__tests__/auth-required.test.ts +0 -96
- package/src/acp/__tests__/client-handler.test.ts +2 -90
- package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
- package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
- package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
- package/src/acp/__tests__/session-manager.test.ts +4 -895
- package/src/acp/agent-process.test.ts +1 -4
- package/src/acp/agent-process.ts +9 -94
- package/src/acp/auth-required.ts +1 -50
- package/src/acp/auto-install.test.ts +7 -9
- package/src/acp/client-handler.ts +3 -16
- package/src/acp/resolve-agent.test.ts +6 -220
- package/src/acp/resolve-agent.ts +23 -88
- package/src/acp/session-manager.test.ts +3 -196
- package/src/acp/session-manager.ts +18 -441
- package/src/acp/types.ts +5 -50
- package/src/api/events/app-files-changed.ts +3 -4
- package/src/api/index.ts +0 -6
- package/src/apps/app-store.ts +1 -1
- package/src/bundler/app-compiler.ts +0 -15
- package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
- package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
- package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
- package/src/calls/call-constants.ts +4 -24
- package/src/calls/call-controller.ts +199 -499
- package/src/calls/call-transport.ts +0 -16
- package/src/calls/media-stream-output.ts +0 -32
- package/src/calls/media-stream-server.ts +30 -82
- package/src/calls/media-stream-stt-session.ts +0 -15
- package/src/calls/resolve-call-tts-provider.ts +2 -33
- package/src/calls/speaker-identification.ts +233 -0
- package/src/calls/tts-call-strategy.ts +5 -9
- package/src/calls/types.ts +0 -2
- package/src/calls/voice-control-protocol.ts +21 -53
- package/src/calls/voice-session-bridge.ts +46 -59
- package/src/calls/voice-triage-escalate.ts +56 -175
- package/src/cli/commands/apps.help.ts +13 -67
- package/src/cli/commands/apps.ts +0 -183
- package/src/cli/commands/channels/index.help.ts +2 -70
- package/src/cli/commands/channels/index.ts +3 -16
- package/src/cli/commands/credentials.help.ts +2 -6
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/roadmap.help.ts +1 -5
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +90 -103
- package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
- package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
- package/src/config/__tests__/platform-rehydration.test.ts +0 -93
- package/src/config/acp-defaults.test.ts +3 -13
- package/src/config/acp-defaults.ts +6 -23
- package/src/config/acp-schema.ts +2 -30
- package/src/config/bundled-skills/acp/SKILL.md +14 -22
- package/src/config/bundled-skills/acp/TOOLS.json +1 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +12 -8
- package/src/config/memory-v3-gate.test.ts +0 -31
- package/src/config/memory-v3-gate.ts +16 -20
- package/src/config/platform-rehydration.ts +21 -75
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
- package/src/config/schemas/calls.ts +28 -1
- package/src/config/schemas/live-voice.ts +173 -1
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +0 -10
- package/src/config/schemas/skills.ts +14 -0
- package/src/credential-execution/process-manager.test.ts +19 -18
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
- package/src/daemon/app-source-watcher.ts +218 -0
- package/src/daemon/config-watcher.ts +194 -13
- package/src/daemon/conversation-process.ts +64 -35
- package/src/daemon/conversation-skill-tools.ts +31 -13
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +8 -10
- package/src/daemon/conversation-tool-setup.ts +43 -37
- package/src/daemon/conversation.ts +3 -0
- package/src/daemon/host-bash-proxy.ts +12 -8
- package/src/daemon/host-browser-proxy.ts +1 -15
- package/src/daemon/host-file-proxy.ts +10 -8
- package/src/daemon/host-transfer-proxy.ts +18 -14
- package/src/daemon/lifecycle.ts +5 -0
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/shutdown-handlers.ts +9 -1
- package/src/daemon/skill-memory-refresh.ts +0 -64
- package/src/daemon/tool-side-effects.ts +18 -7
- package/src/hooks/types.ts +0 -33
- package/src/inbound/platform-callback-registration.ts +5 -4
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
- package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
- package/src/live-voice/live-voice-session.ts +693 -213
- package/src/{calls → live-voice}/progress-narration.ts +2 -2
- package/src/live-voice/protocol.ts +2 -0
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +45 -29
- package/src/mcp/startup.ts +4 -8
- package/src/messaging/provider.ts +3 -10
- package/src/messaging/providers/callback-routing.ts +0 -18
- package/src/messaging/providers/channel-transport.ts +0 -56
- package/src/messaging/providers/discord/transport.ts +0 -17
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
- package/src/messaging/providers/slack/adapter.ts +44 -7
- package/src/messaging/providers/slack/transport.ts +0 -16
- package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
- package/src/messaging/providers/telegram-bot/client.ts +26 -0
- package/src/messaging/providers/telegram-bot/transport.ts +0 -20
- package/src/messaging/providers/whatsapp/adapter.ts +48 -1
- package/src/messaging/providers/whatsapp/client.ts +29 -0
- package/src/messaging/providers/whatsapp/transport.ts +0 -18
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
- package/src/notifications/assistant-reply-producer.ts +3 -8
- package/src/notifications/conversation-pairing.ts +16 -36
- package/src/notifications/delivered-post-record.ts +1 -11
- package/src/notifications/home-feed-side-effect.ts +7 -55
- package/src/outbound-proxy/certs.ts +5 -3
- package/src/permissions/checker.test.ts +6 -11
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +63 -71
- package/src/persistence/conversation-lineage.ts +4 -12
- package/src/persistence/conversation-title-service.ts +26 -9
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-reads.ts +0 -30
- package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
- package/src/persistence/schema/memory-core.ts +0 -5
- package/src/platform/client.test.ts +0 -2
- package/src/platform/client.ts +10 -2
- package/src/plugin-api/constants.ts +0 -2
- package/src/plugin-api/index.ts +0 -1
- package/src/plugin-api/types.ts +0 -2
- package/src/plugins/defaults/index.ts +0 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
- package/src/plugins/defaults/memory/hooks/init.ts +0 -10
- package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
- package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
- package/src/plugins/defaults/memory/startup.ts +1 -6
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
- package/src/plugins/defaults/memory/worker.ts +0 -6
- package/src/plugins/mcp-servers.ts +75 -15
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/gemini/client.ts +23 -9
- package/src/providers/model-catalog.ts +0 -34
- package/src/providers/model-intents.ts +5 -10
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
- package/src/providers/openai/chat-completions-provider.ts +78 -192
- package/src/providers/openai/codex-models.ts +4 -0
- package/src/runtime/AGENTS.md +0 -2
- package/src/runtime/auth/same-actor.ts +3 -38
- package/src/runtime/http-server.ts +151 -160
- package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
- package/src/runtime/routes/acp-routes.test.ts +0 -15
- package/src/runtime/routes/acp-routes.ts +9 -72
- package/src/runtime/routes/app-management-routes.ts +0 -73
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +3 -14
- package/src/runtime/routes/conversation-query-routes.ts +0 -40
- package/src/runtime/routes/credential-routes.ts +10 -19
- package/src/runtime/routes/diagnostics-routes.ts +3 -55
- package/src/runtime/routes/index.ts +0 -4
- package/src/runtime/routes/log-export-routes.ts +0 -2
- package/src/runtime/routes/mcp-auth-routes.ts +47 -39
- package/src/runtime/routes/migration-routes.ts +21 -8
- package/src/runtime/routes/platform-routes.ts +15 -18
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
- package/src/runtime/services/conversation-serializer.ts +3 -16
- package/src/runtime/sync/resource-sync-events.ts +1 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/ces-rpc-record-backend.ts +0 -7
- package/src/subagent/consult-context.ts +25 -12
- package/src/tools/acp/list-agents.test.ts +1 -3
- package/src/tools/acp/spawn.test.ts +11 -146
- package/src/tools/acp/spawn.ts +3 -21
- package/src/tools/acp/status.ts +3 -40
- package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
- package/src/tools/capability-offer.test.ts +2 -22
- package/src/tools/capability-offer.ts +1 -16
- package/src/tools/computer-use/definitions.ts +2 -4
- package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
- package/src/tools/credentials/metadata-store.ts +0 -100
- package/src/tools/host-filesystem/edit.test.ts +1 -2
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +1 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +1 -2
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +1 -2
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +0 -16
- package/src/tools/host-terminal/host-shell.ts +20 -4
- package/src/tools/policy-context.ts +7 -6
- package/src/tools/subagent/spawn.ts +8 -5
- package/src/tools/watch/watch-retro-report.ts +8 -0
- package/src/util/__tests__/language-subtag.test.ts +1 -23
- package/src/util/fs-watcher-error.ts +36 -0
- package/src/util/language-subtag.ts +0 -16
- package/src/util/platform.ts +0 -8
- package/src/workspace/git-service.ts +54 -5
- package/src/workspace/migrations/registry.ts +0 -6
- package/scripts/smoke-desktop-window-theme.ts +0 -186
- package/src/__tests__/git-porcelain.test.ts +0 -47
- package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
- package/src/__tests__/resolve-app.test.ts +0 -135
- package/src/__tests__/source-fingerprint.test.ts +0 -184
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
- package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
- package/src/acp/model-config.test.ts +0 -166
- package/src/acp/model-config.ts +0 -138
- package/src/api/events/acp-session-model-update.test.ts +0 -88
- package/src/api/events/acp-session-model-update.ts +0 -46
- package/src/apps/resolve-app.ts +0 -81
- package/src/apps/source-fingerprint.ts +0 -266
- package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
- package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
- package/src/calls/barge-in-guard.ts +0 -118
- package/src/calls/voice-leg-coordinator.ts +0 -189
- package/src/calls/voice-progress-cadence.ts +0 -423
- package/src/cli/commands/__tests__/apps.test.ts +0 -245
- package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
- package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
- package/src/cli/commands/channels/send.ts +0 -128
- package/src/config/__tests__/platform-identity.test.ts +0 -117
- package/src/config/acp-agent-write.ts +0 -43
- package/src/config/acp-schema.test.ts +0 -81
- package/src/config/platform-identity.ts +0 -144
- package/src/config/schemas/__tests__/voice.test.ts +0 -148
- package/src/config/schemas/voice.ts +0 -150
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
- package/src/daemon/app-change-notify.ts +0 -45
- package/src/desktop/__tests__/fake-desktop.ts +0 -119
- package/src/desktop/desktop-chrome-policy.test.ts +0 -71
- package/src/desktop/desktop-chrome-policy.ts +0 -42
- package/src/desktop/desktop-dependencies.test.ts +0 -102
- package/src/desktop/desktop-dependencies.ts +0 -306
- package/src/desktop/desktop-feature.test.ts +0 -18
- package/src/desktop/desktop-feature.ts +0 -19
- package/src/desktop/desktop-panel-config.test.ts +0 -135
- package/src/desktop/desktop-panel-config.ts +0 -149
- package/src/desktop/desktop-session-manager.test.ts +0 -627
- package/src/desktop/desktop-session-manager.ts +0 -814
- package/src/desktop/desktop-stream-bridge.test.ts +0 -395
- package/src/desktop/desktop-stream-bridge.ts +0 -214
- package/src/desktop/desktop-wallpaper.test.ts +0 -191
- package/src/desktop/desktop-wallpaper.ts +0 -113
- package/src/desktop/desktop-window-theme.ts +0 -170
- package/src/i18n/AGENTS.md +0 -22
- package/src/i18n/__tests__/i18n.test.ts +0 -108
- package/src/i18n/index.ts +0 -16
- package/src/i18n/locales.ts +0 -93
- package/src/i18n/messages.ts +0 -61
- package/src/i18n/title.ts +0 -25
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
- package/src/mcp/spec-schema.ts +0 -111
- package/src/mcp/workspace-mcp-config.ts +0 -188
- package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
- package/src/persistence/conversation-title-placeholders.ts +0 -23
- package/src/persistence/message-cursor.ts +0 -78
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
- package/src/providers/gemini-thought-signature.test.ts +0 -63
- package/src/providers/gemini-thought-signature.ts +0 -44
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
- package/src/providers/openai/google-thought-signature.ts +0 -320
- package/src/runtime/__tests__/channel-send.test.ts +0 -443
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
- package/src/runtime/channel-send.ts +0 -316
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
- package/src/runtime/routes/channel-send-routes.ts +0 -85
- package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
- package/src/runtime/routes/desktop-setup-routes.ts +0 -35
- package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
- package/src/security/ces-http-record-backend.ts +0 -120
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
- package/src/skills/workspace-skill-md-signature.ts +0 -50
- package/src/tools/acp/status.test.ts +0 -94
- package/src/util/ca-bundle.ts +0 -13
- package/src/workspace/git-porcelain.ts +0 -66
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
- /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
|
@@ -98,20 +98,4 @@ export interface CallTransport {
|
|
|
98
98
|
* this — the controller then treats playback as already drained.
|
|
99
99
|
*/
|
|
100
100
|
awaitPlaybackDrained?(): Promise<void>;
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Whether no assistant speech is buffered, queued, in flight, or
|
|
104
|
-
* (estimatedly) still playing to the caller. Progress narration speaks
|
|
105
|
-
* only into audible silence, so it asks before every update. Transports
|
|
106
|
-
* that play speech synchronously may omit this; the controller then
|
|
107
|
-
* treats the transport as idle.
|
|
108
|
-
*/
|
|
109
|
-
isPlaybackIdle?(): boolean;
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Wall-clock estimate of when audio already sent finishes playing to the
|
|
113
|
-
* caller, 0 when nothing is playing. Anchors the dead-air countdown for
|
|
114
|
-
* progress narration to when the caller last heard something.
|
|
115
|
-
*/
|
|
116
|
-
playbackTailUntilMs?(): number;
|
|
117
101
|
}
|
|
@@ -327,14 +327,6 @@ export class MediaStreamOutput implements CallTransport {
|
|
|
327
327
|
/** True when the queue drain loop is actively running. */
|
|
328
328
|
private draining = false;
|
|
329
329
|
|
|
330
|
-
/**
|
|
331
|
-
* Estimated wall-clock instant the audio already sent to Twilio finishes
|
|
332
|
-
* playing to the caller. Frames leave faster than real time and Twilio
|
|
333
|
-
* buffers them, so the only local view of "still audible" is this running
|
|
334
|
-
* sum of sent frame durations; a `clear` drops the buffer and resets it.
|
|
335
|
-
*/
|
|
336
|
-
private playbackTailAtMs = 0;
|
|
337
|
-
|
|
338
330
|
/** Abort controller for the currently in-flight synthesis/fetch. */
|
|
339
331
|
private activePlaybackAbort: AbortController | null = null;
|
|
340
332
|
|
|
@@ -667,7 +659,6 @@ export class MediaStreamOutput implements CallTransport {
|
|
|
667
659
|
return;
|
|
668
660
|
}
|
|
669
661
|
this.sendClearCommand();
|
|
670
|
-
this.playbackTailAtMs = 0;
|
|
671
662
|
// Twilio drops its buffered audio (and the marks within it) on `clear`, so
|
|
672
663
|
// any end-of-turn mark already sent will never echo. Treat those as drained
|
|
673
664
|
// so a pending end-call drain wait resolves instead of stalling on the cap.
|
|
@@ -725,24 +716,6 @@ export class MediaStreamOutput implements CallTransport {
|
|
|
725
716
|
return this.playbackQueue.length;
|
|
726
717
|
}
|
|
727
718
|
|
|
728
|
-
/**
|
|
729
|
-
* Whether no speech is buffered toward a sentence, queued, being
|
|
730
|
-
* synthesized or fetched, or (estimatedly) still playing at Twilio.
|
|
731
|
-
*/
|
|
732
|
-
isPlaybackIdle(): boolean {
|
|
733
|
-
return (
|
|
734
|
-
this.textBuffer.length === 0 &&
|
|
735
|
-
this.playbackQueue.length === 0 &&
|
|
736
|
-
!this.draining &&
|
|
737
|
-
Date.now() >= this.playbackTailAtMs
|
|
738
|
-
);
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
/** See {@link CallTransport.playbackTailUntilMs}. */
|
|
742
|
-
playbackTailUntilMs(): number {
|
|
743
|
-
return this.playbackTailAtMs;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
719
|
/**
|
|
747
720
|
* Runtime check for closed state. Used instead of direct property access
|
|
748
721
|
* in async methods because TypeScript's control flow analysis cannot
|
|
@@ -799,7 +772,6 @@ export class MediaStreamOutput implements CallTransport {
|
|
|
799
772
|
this.playbackQueue.length = 0;
|
|
800
773
|
this.playbackVersion++;
|
|
801
774
|
this.audioStartCallback = null;
|
|
802
|
-
this.playbackTailAtMs = 0;
|
|
803
775
|
if (this.activePlaybackAbort) {
|
|
804
776
|
this.activePlaybackAbort.abort();
|
|
805
777
|
this.activePlaybackAbort = null;
|
|
@@ -882,10 +854,6 @@ export class MediaStreamOutput implements CallTransport {
|
|
|
882
854
|
for (const frame of frames) {
|
|
883
855
|
this.sendAudioPayload(frame);
|
|
884
856
|
}
|
|
885
|
-
const durationMs =
|
|
886
|
-
(frames.length * MULAW_FRAME_SIZE * 1000) / TELEPHONY_SAMPLE_RATE_HZ;
|
|
887
|
-
this.playbackTailAtMs =
|
|
888
|
-
Math.max(Date.now(), this.playbackTailAtMs) + durationMs;
|
|
889
857
|
}
|
|
890
858
|
|
|
891
859
|
/**
|
|
@@ -47,11 +47,6 @@ import { addMessage } from "../persistence/conversation-crud.js";
|
|
|
47
47
|
import { resolveGuardianName } from "../prompts/user-reference.js";
|
|
48
48
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
49
49
|
import { getLogger } from "../util/logger.js";
|
|
50
|
-
import {
|
|
51
|
-
type BargeInGuard,
|
|
52
|
-
createBargeInGuard,
|
|
53
|
-
DEFAULT_BARGE_IN_MIN_SPEECH_MS,
|
|
54
|
-
} from "./barge-in-guard.js";
|
|
55
50
|
import { CallController } from "./call-controller.js";
|
|
56
51
|
import {
|
|
57
52
|
formatDuration,
|
|
@@ -193,8 +188,6 @@ export class MediaStreamCallSession {
|
|
|
193
188
|
private bargeInAccepted = 0;
|
|
194
189
|
/** Number of barge-in attempts that were ignored (assistant not speaking). */
|
|
195
190
|
private bargeInIgnored = 0;
|
|
196
|
-
/** Armed by caller speech while a turn or its tail is interruptible. */
|
|
197
|
-
private bargeInGuard: BargeInGuard | null = null;
|
|
198
191
|
/** Number of turn-start transitions detected by the STT session. */
|
|
199
192
|
private turnStarts = 0;
|
|
200
193
|
/** Number of transcript finals produced (non-empty). */
|
|
@@ -222,9 +215,6 @@ export class MediaStreamCallSession {
|
|
|
222
215
|
// Create STT session with callbacks wired to the controller.
|
|
223
216
|
const callbacks: MediaStreamSttSessionCallbacks = {
|
|
224
217
|
onSpeechStart: () => this.handleSpeechStart(),
|
|
225
|
-
onMediaFrame: (hasSpeech, durationMs) =>
|
|
226
|
-
this.handleMediaFrame(hasSpeech, durationMs),
|
|
227
|
-
onSpeechEnd: () => this.handleSpeechEnd(),
|
|
228
218
|
onTranscriptFinal: (text, durationMs) =>
|
|
229
219
|
this.handleTranscriptFinal(text, durationMs),
|
|
230
220
|
onDtmf: (digit) => this.handleDtmf(digit),
|
|
@@ -778,80 +768,38 @@ export class MediaStreamCallSession {
|
|
|
778
768
|
|
|
779
769
|
private handleSpeechStart(): void {
|
|
780
770
|
this.turnStarts++;
|
|
781
|
-
}
|
|
782
771
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
if (
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
772
|
+
// Barge-in: clear queued outbound audio and abort the in-flight LLM
|
|
773
|
+
// turn only when the assistant is actively speaking. Uses the gated
|
|
774
|
+
// handleBargeIn path so initial inbound audio frames do not cancel a
|
|
775
|
+
// still-starting initial turn.
|
|
776
|
+
//
|
|
777
|
+
// clearAudio runs via the onAccepted hook so it only fires when the
|
|
778
|
+
// barge-in passes the speaking gate — an ignored barge-in (controller
|
|
779
|
+
// idle/processing) must not flush queued/in-flight TTS such as a
|
|
780
|
+
// buffered greeting. The hook runs before handleInterrupt so the
|
|
781
|
+
// end-of-turn mark it enqueues survives the queue flush.
|
|
782
|
+
if (this.output && this.controller) {
|
|
783
|
+
const output = this.output;
|
|
784
|
+
const accepted = this.controller.handleBargeIn(() => output.clearAudio());
|
|
785
|
+
if (accepted) {
|
|
786
|
+
this.bargeInAccepted++;
|
|
787
|
+
log.info(
|
|
788
|
+
{ callSessionId: this.callSessionId },
|
|
789
|
+
"Media-stream barge-in accepted — cleared outbound audio",
|
|
790
|
+
);
|
|
791
|
+
} else {
|
|
792
|
+
// No turn to abort, but a completed turn's tail can still be
|
|
793
|
+
// playing from Twilio's buffer — flush only that buffer so the
|
|
794
|
+
// caller isn't talked over. Queued speech that hasn't reached
|
|
795
|
+
// Twilio yet (greeting, handoff prompt) is preserved.
|
|
796
|
+
output.clearBufferedAudio();
|
|
797
|
+
this.bargeInIgnored++;
|
|
798
|
+
log.debug(
|
|
799
|
+
{ callSessionId: this.callSessionId },
|
|
800
|
+
"Media-stream barge-in ignored — assistant not speaking",
|
|
801
|
+
);
|
|
805
802
|
}
|
|
806
|
-
this.bargeInGuard = createBargeInGuard(DEFAULT_BARGE_IN_MIN_SPEECH_MS);
|
|
807
|
-
}
|
|
808
|
-
const step = this.bargeInGuard.track(
|
|
809
|
-
hasSpeech ? "speech" : "silence",
|
|
810
|
-
durationMs,
|
|
811
|
-
);
|
|
812
|
-
if (step === "fired") {
|
|
813
|
-
this.bargeInGuard = null;
|
|
814
|
-
this.fireBargeIn();
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
private handleSpeechEnd(): void {
|
|
819
|
-
// An untripped guard was noise or a fragment: the run never reached the
|
|
820
|
-
// threshold before the caller's turn ended.
|
|
821
|
-
this.bargeInGuard = null;
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
// Sustained caller speech landed: clear queued outbound audio and abort
|
|
825
|
-
// the in-flight LLM turn when one is in flight (thinking or speaking).
|
|
826
|
-
//
|
|
827
|
-
// clearAudio runs via the onAccepted hook so it only fires when the
|
|
828
|
-
// barge-in passes the controller's gate; an ignored barge-in (controller
|
|
829
|
-
// idle) must not flush queued/in-flight TTS such as a buffered greeting.
|
|
830
|
-
// The hook runs before handleInterrupt so the end-of-turn mark it
|
|
831
|
-
// enqueues survives the queue flush.
|
|
832
|
-
private fireBargeIn(): void {
|
|
833
|
-
if (!this.output || !this.controller) {
|
|
834
|
-
return;
|
|
835
|
-
}
|
|
836
|
-
const output = this.output;
|
|
837
|
-
const accepted = this.controller.handleBargeIn(() => output.clearAudio());
|
|
838
|
-
if (accepted) {
|
|
839
|
-
this.bargeInAccepted++;
|
|
840
|
-
log.info(
|
|
841
|
-
{ callSessionId: this.callSessionId },
|
|
842
|
-
"Media-stream barge-in accepted: cleared outbound audio",
|
|
843
|
-
);
|
|
844
|
-
} else {
|
|
845
|
-
// No turn to abort, but a completed turn's tail can still be
|
|
846
|
-
// playing from Twilio's buffer, so flush only that buffer so the
|
|
847
|
-
// caller isn't talked over. Queued speech that hasn't reached
|
|
848
|
-
// Twilio yet (greeting, handoff prompt) is preserved.
|
|
849
|
-
output.clearBufferedAudio();
|
|
850
|
-
this.bargeInIgnored++;
|
|
851
|
-
log.debug(
|
|
852
|
-
{ callSessionId: this.callSessionId },
|
|
853
|
-
"Media-stream barge-in ignored: no turn in flight",
|
|
854
|
-
);
|
|
855
803
|
}
|
|
856
804
|
}
|
|
857
805
|
|
|
@@ -120,16 +120,6 @@ export interface MediaStreamSttSessionCallbacks {
|
|
|
120
120
|
/** Called when the turn detector transitions to active (first speech-bearing chunk). */
|
|
121
121
|
onSpeechStart?: () => void;
|
|
122
122
|
|
|
123
|
-
/**
|
|
124
|
-
* Called for every inbound frame with the local VAD's verdict on it and
|
|
125
|
-
* the frame's duration. Drives the sustained-speech barge-in guard, which
|
|
126
|
-
* needs the silence frames inside a detector turn as well as the speech.
|
|
127
|
-
*/
|
|
128
|
-
onMediaFrame?: (hasSpeech: boolean, durationMs: number) => void;
|
|
129
|
-
|
|
130
|
-
/** Called when the turn detector ends the caller's turn (silence or cap). */
|
|
131
|
-
onSpeechEnd?: () => void;
|
|
132
|
-
|
|
133
123
|
/**
|
|
134
124
|
* Called when a completed caller utterance has been transcribed.
|
|
135
125
|
*
|
|
@@ -265,7 +255,6 @@ export class MediaStreamSttSession {
|
|
|
265
255
|
this.callbacks.onSpeechStart?.();
|
|
266
256
|
},
|
|
267
257
|
onTurnEnd: (reason, durationMs) => {
|
|
268
|
-
this.callbacks.onSpeechEnd?.();
|
|
269
258
|
void this.handleTurnEnd(reason, durationMs);
|
|
270
259
|
},
|
|
271
260
|
});
|
|
@@ -388,10 +377,6 @@ export class MediaStreamSttSession {
|
|
|
388
377
|
const raw = Buffer.from(event.media.payload, "base64");
|
|
389
378
|
const hasSpeech = detectSpeechActivity(raw);
|
|
390
379
|
this.turnDetector.onMediaChunk(hasSpeech);
|
|
391
|
-
this.callbacks.onMediaFrame?.(
|
|
392
|
-
hasSpeech,
|
|
393
|
-
raw.length / (TELEPHONY_SOURCE_SAMPLE_RATE / 1000),
|
|
394
|
-
);
|
|
395
380
|
|
|
396
381
|
if (this.mode === "batch") {
|
|
397
382
|
this.currentTurnChunks.push(event.media.payload);
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { loadConfig } from "../config/loader.js";
|
|
11
|
-
import { resolveEffectiveSpeechProviders } from "../config/managed-speech-defaults.js";
|
|
12
11
|
import {
|
|
13
12
|
getCatalogProvider,
|
|
14
13
|
getTtsProvider,
|
|
@@ -74,13 +73,6 @@ export interface ResolveCallTtsOptions {
|
|
|
74
73
|
/**
|
|
75
74
|
* Resolve the active TTS provider via the global provider abstraction.
|
|
76
75
|
*
|
|
77
|
-
* The provider is the effective one after managed-speech defaulting
|
|
78
|
-
* ({@link resolveEffectiveSpeechProviders}): a configured BYOK provider
|
|
79
|
-
* whose credential does not resolve is stood in for by managed speech
|
|
80
|
-
* while the platform connection is usable, the same substitution live
|
|
81
|
-
* voice makes, so a fresh Vellum connection speaks on the phone with zero
|
|
82
|
-
* TTS configuration.
|
|
83
|
-
*
|
|
84
76
|
* The native-vs-synthesized decision is driven by the catalog's
|
|
85
77
|
* `callMode` field via {@link resolveCallStrategy}. Providers with
|
|
86
78
|
* `callMode: "synthesized-play"` have their audio streamed through the
|
|
@@ -102,11 +94,10 @@ export async function resolveCallTtsProvider(
|
|
|
102
94
|
): Promise<ResolvedCallTts> {
|
|
103
95
|
try {
|
|
104
96
|
const config = loadConfig();
|
|
105
|
-
const
|
|
106
|
-
const resolved = resolveTtsConfig(config, effectiveProviderId);
|
|
97
|
+
const resolved = resolveTtsConfig(config);
|
|
107
98
|
|
|
108
99
|
// Use the catalog's callMode to decide the call path.
|
|
109
|
-
const strategy = resolveCallStrategy(config
|
|
100
|
+
const strategy = resolveCallStrategy(config);
|
|
110
101
|
|
|
111
102
|
let providerId = resolved.provider;
|
|
112
103
|
let useSynthesizedPath = strategy.callMode === "synthesized-play";
|
|
@@ -184,28 +175,6 @@ export async function resolveCallTtsProvider(
|
|
|
184
175
|
}
|
|
185
176
|
}
|
|
186
177
|
|
|
187
|
-
/**
|
|
188
|
-
* The TTS provider the call actually uses after managed-speech defaulting.
|
|
189
|
-
* A failure inside the managed lookup (platform client, credential store)
|
|
190
|
-
* keeps the configured provider: a managed-speech hiccup must not demote a
|
|
191
|
-
* working BYOK provider, and the outer resolver's own fallback is the
|
|
192
|
-
* silent native path.
|
|
193
|
-
*/
|
|
194
|
-
async function effectiveTtsProviderId(
|
|
195
|
-
config: ReturnType<typeof loadConfig>,
|
|
196
|
-
): Promise<TtsProviderId | undefined> {
|
|
197
|
-
try {
|
|
198
|
-
const { tts } = await resolveEffectiveSpeechProviders(config);
|
|
199
|
-
return tts;
|
|
200
|
-
} catch (err) {
|
|
201
|
-
log.warn(
|
|
202
|
-
{ err },
|
|
203
|
-
"Managed-speech defaulting failed for call TTS; keeping the configured provider",
|
|
204
|
-
);
|
|
205
|
-
return undefined;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
178
|
/**
|
|
210
179
|
* Decode a resolved call audio format into the provider request format
|
|
211
180
|
* and the audio-store format.
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
export interface PromptSpeakerContext {
|
|
2
|
+
speakerId: string;
|
|
3
|
+
speakerLabel: string;
|
|
4
|
+
speakerConfidence: number | null;
|
|
5
|
+
source: "provider" | "inferred";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface PromptSpeakerMetadata {
|
|
9
|
+
speakerId?: string;
|
|
10
|
+
speakerLabel?: string;
|
|
11
|
+
speakerName?: string;
|
|
12
|
+
speakerConfidence?: number;
|
|
13
|
+
participantId?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface SpeakerProfile {
|
|
17
|
+
speakerId: string;
|
|
18
|
+
speakerLabel: string;
|
|
19
|
+
speakerConfidence: number | null;
|
|
20
|
+
source: "provider" | "inferred";
|
|
21
|
+
utteranceCount: number;
|
|
22
|
+
firstSeenAt: number;
|
|
23
|
+
lastSeenAt: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function toCleanString(value: unknown): string | null {
|
|
27
|
+
if (typeof value !== "string") {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const trimmed = value.trim();
|
|
31
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function toNumber(value: unknown): number | null {
|
|
35
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
if (typeof value === "string") {
|
|
39
|
+
const parsed = Number(value);
|
|
40
|
+
if (Number.isFinite(parsed)) {
|
|
41
|
+
return parsed;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getObject(value: unknown): Record<string, unknown> | null {
|
|
48
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
return value as Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function normalizeSpeakerLabel(
|
|
55
|
+
metadata: PromptSpeakerMetadata,
|
|
56
|
+
fallbackIndex: number,
|
|
57
|
+
): string {
|
|
58
|
+
const preferredLabel =
|
|
59
|
+
toCleanString(metadata.speakerName) ?? toCleanString(metadata.speakerLabel);
|
|
60
|
+
if (preferredLabel) {
|
|
61
|
+
return preferredLabel;
|
|
62
|
+
}
|
|
63
|
+
return `Speaker ${fallbackIndex}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function extractPromptSpeakerMetadata(
|
|
67
|
+
message: Record<string, unknown>,
|
|
68
|
+
): PromptSpeakerMetadata {
|
|
69
|
+
const providerMetadata = getObject(message.providerMetadata);
|
|
70
|
+
const metadata = getObject(message.metadata);
|
|
71
|
+
const participant = getObject(message.participant);
|
|
72
|
+
const speaker = getObject(message.speaker);
|
|
73
|
+
|
|
74
|
+
const pick = (...values: unknown[]): string | undefined => {
|
|
75
|
+
for (const value of values) {
|
|
76
|
+
const cleaned = toCleanString(value);
|
|
77
|
+
if (cleaned) {
|
|
78
|
+
return cleaned;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const pickNumber = (...values: unknown[]): number | undefined => {
|
|
85
|
+
for (const value of values) {
|
|
86
|
+
const parsed = toNumber(value);
|
|
87
|
+
if (parsed != null) {
|
|
88
|
+
return parsed;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
speakerId: pick(
|
|
96
|
+
message.speakerId,
|
|
97
|
+
message.speaker_id,
|
|
98
|
+
speaker?.id,
|
|
99
|
+
speaker?.speakerId,
|
|
100
|
+
metadata?.speakerId,
|
|
101
|
+
providerMetadata?.speakerId,
|
|
102
|
+
metadata?.speaker_id,
|
|
103
|
+
providerMetadata?.speaker_id,
|
|
104
|
+
),
|
|
105
|
+
speakerLabel: pick(
|
|
106
|
+
message.speakerLabel,
|
|
107
|
+
message.speaker_label,
|
|
108
|
+
message.speaker,
|
|
109
|
+
speaker?.label,
|
|
110
|
+
speaker?.name,
|
|
111
|
+
metadata?.speakerLabel,
|
|
112
|
+
providerMetadata?.speakerLabel,
|
|
113
|
+
metadata?.speaker_label,
|
|
114
|
+
providerMetadata?.speaker_label,
|
|
115
|
+
),
|
|
116
|
+
speakerName: pick(
|
|
117
|
+
message.speakerName,
|
|
118
|
+
message.speaker_name,
|
|
119
|
+
participant?.name,
|
|
120
|
+
metadata?.speakerName,
|
|
121
|
+
providerMetadata?.speakerName,
|
|
122
|
+
metadata?.speaker_name,
|
|
123
|
+
providerMetadata?.speaker_name,
|
|
124
|
+
),
|
|
125
|
+
speakerConfidence: pickNumber(
|
|
126
|
+
message.speakerConfidence,
|
|
127
|
+
message.speaker_confidence,
|
|
128
|
+
message.confidence,
|
|
129
|
+
speaker?.confidence,
|
|
130
|
+
metadata?.speakerConfidence,
|
|
131
|
+
providerMetadata?.speakerConfidence,
|
|
132
|
+
metadata?.speaker_confidence,
|
|
133
|
+
providerMetadata?.speaker_confidence,
|
|
134
|
+
),
|
|
135
|
+
participantId: pick(
|
|
136
|
+
message.participantId,
|
|
137
|
+
message.participant_id,
|
|
138
|
+
participant?.id,
|
|
139
|
+
metadata?.participantId,
|
|
140
|
+
providerMetadata?.participantId,
|
|
141
|
+
metadata?.participant_id,
|
|
142
|
+
providerMetadata?.participant_id,
|
|
143
|
+
),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export class SpeakerIdentityTracker {
|
|
148
|
+
private profiles = new Map<string, SpeakerProfile>();
|
|
149
|
+
private nextInferredIndex = 1;
|
|
150
|
+
|
|
151
|
+
identifySpeaker(metadata: PromptSpeakerMetadata): PromptSpeakerContext {
|
|
152
|
+
const providerSpeakerId =
|
|
153
|
+
toCleanString(metadata.speakerId) ??
|
|
154
|
+
toCleanString(metadata.participantId) ??
|
|
155
|
+
null;
|
|
156
|
+
|
|
157
|
+
if (providerSpeakerId) {
|
|
158
|
+
const existing = this.profiles.get(providerSpeakerId);
|
|
159
|
+
if (existing) {
|
|
160
|
+
existing.lastSeenAt = Date.now();
|
|
161
|
+
existing.utteranceCount += 1;
|
|
162
|
+
if (metadata.speakerConfidence !== undefined) {
|
|
163
|
+
existing.speakerConfidence = metadata.speakerConfidence;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
speakerId: existing.speakerId,
|
|
167
|
+
speakerLabel: existing.speakerLabel,
|
|
168
|
+
speakerConfidence: existing.speakerConfidence,
|
|
169
|
+
source: existing.source,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const profile: SpeakerProfile = {
|
|
174
|
+
speakerId: providerSpeakerId,
|
|
175
|
+
speakerLabel: normalizeSpeakerLabel(metadata, this.nextInferredIndex),
|
|
176
|
+
speakerConfidence: metadata.speakerConfidence ?? null,
|
|
177
|
+
source: "provider",
|
|
178
|
+
utteranceCount: 1,
|
|
179
|
+
firstSeenAt: Date.now(),
|
|
180
|
+
lastSeenAt: Date.now(),
|
|
181
|
+
};
|
|
182
|
+
this.profiles.set(providerSpeakerId, profile);
|
|
183
|
+
this.nextInferredIndex += 1;
|
|
184
|
+
return {
|
|
185
|
+
speakerId: profile.speakerId,
|
|
186
|
+
speakerLabel: profile.speakerLabel,
|
|
187
|
+
speakerConfidence: profile.speakerConfidence,
|
|
188
|
+
source: profile.source,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const inferredSpeakerId = "primary-speaker";
|
|
193
|
+
const existingPrimary = this.profiles.get(inferredSpeakerId);
|
|
194
|
+
if (existingPrimary) {
|
|
195
|
+
existingPrimary.lastSeenAt = Date.now();
|
|
196
|
+
existingPrimary.utteranceCount += 1;
|
|
197
|
+
return {
|
|
198
|
+
speakerId: existingPrimary.speakerId,
|
|
199
|
+
speakerLabel: existingPrimary.speakerLabel,
|
|
200
|
+
speakerConfidence: existingPrimary.speakerConfidence,
|
|
201
|
+
source: existingPrimary.source,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const inferredProfile: SpeakerProfile = {
|
|
206
|
+
speakerId: inferredSpeakerId,
|
|
207
|
+
speakerLabel: normalizeSpeakerLabel(metadata, this.nextInferredIndex),
|
|
208
|
+
speakerConfidence: metadata.speakerConfidence ?? null,
|
|
209
|
+
source: "inferred",
|
|
210
|
+
utteranceCount: 1,
|
|
211
|
+
firstSeenAt: Date.now(),
|
|
212
|
+
lastSeenAt: Date.now(),
|
|
213
|
+
};
|
|
214
|
+
this.profiles.set(inferredSpeakerId, inferredProfile);
|
|
215
|
+
this.nextInferredIndex += 1;
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
speakerId: inferredProfile.speakerId,
|
|
219
|
+
speakerLabel: inferredProfile.speakerLabel,
|
|
220
|
+
speakerConfidence: inferredProfile.speakerConfidence,
|
|
221
|
+
source: inferredProfile.source,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
listProfiles(): PromptSpeakerContext[] {
|
|
226
|
+
return [...this.profiles.values()].map((profile) => ({
|
|
227
|
+
speakerId: profile.speakerId,
|
|
228
|
+
speakerLabel: profile.speakerLabel,
|
|
229
|
+
speakerConfidence: profile.speakerConfidence,
|
|
230
|
+
source: profile.source,
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -40,21 +40,17 @@ export interface TtsCallStrategy {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
* Resolve the call strategy for the
|
|
43
|
+
* Resolve the call strategy for the currently configured TTS provider.
|
|
44
44
|
*
|
|
45
|
-
* Reads the provider from config via {@link resolveTtsConfig}
|
|
46
|
-
*
|
|
47
|
-
* managed-speech defaulting), then looks up its `callMode` in the catalog.
|
|
45
|
+
* Reads the active provider from config via {@link resolveTtsConfig},
|
|
46
|
+
* then looks up the provider's `callMode` in the catalog.
|
|
48
47
|
*
|
|
49
48
|
* Falls back to `native-twilio` with `"elevenlabs"` when the config
|
|
50
49
|
* or catalog is unavailable (e.g. test mocks, pre-migration configs).
|
|
51
50
|
*/
|
|
52
|
-
export function resolveCallStrategy(
|
|
53
|
-
config: AssistantConfig,
|
|
54
|
-
providerOverride?: TtsProviderId,
|
|
55
|
-
): TtsCallStrategy {
|
|
51
|
+
export function resolveCallStrategy(config: AssistantConfig): TtsCallStrategy {
|
|
56
52
|
try {
|
|
57
|
-
const resolved = resolveTtsConfig(config
|
|
53
|
+
const resolved = resolveTtsConfig(config);
|
|
58
54
|
const catalogEntry = getCatalogProvider(resolved.provider);
|
|
59
55
|
return {
|
|
60
56
|
providerId: catalogEntry.id,
|