@vellumai/assistant 0.12.1-dev.202609141913.5e16607 → 0.12.1-staging.2
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/docs/guardian-request-flow.md +0 -16
- package/openapi.yaml +49 -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 +2 -4
- package/src/__tests__/cu-unified-flow.test.ts +2 -8
- package/src/__tests__/document-sync-tags.test.ts +0 -25
- 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__/notification-vellum-adapter.test.ts +2 -85
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +16 -8
- 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/events/notification-intent.ts +0 -2
- 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/bash.help.ts +13 -8
- package/src/cli/commands/bash.ts +25 -7
- 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 +9 -99
- 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/SKILL.md +3 -3
- 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/schedule/SKILL.md +4 -3
- 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 -24
- package/src/config/loader.ts +0 -1
- 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 +27 -11
- 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/services.ts +0 -8
- 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/orphan-reaper.ts +1 -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/home/__tests__/feed-writer.test.ts +0 -22
- package/src/home/feed-writer.ts +2 -11
- 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__/guardian-feed-projection.test.ts +2 -158
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
- package/src/notifications/__tests__/schedule-result-producer.test.ts +3 -99
- package/src/notifications/adapters/macos.ts +0 -3
- 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/guardian-feed-projection.ts +5 -125
- package/src/notifications/home-feed-side-effect.ts +7 -55
- package/src/notifications/schedule-result-producer.ts +22 -66
- package/src/oauth/__tests__/seed-providers-managed.test.ts +0 -54
- package/src/oauth/byo-connection.test.ts +0 -45
- package/src/oauth/byo-connection.ts +6 -36
- package/src/oauth/connection-resolver.test.ts +0 -73
- package/src/oauth/connection-resolver.ts +2 -95
- package/src/oauth/seed-providers.ts +0 -165
- 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 +2 -4
- 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/inference/auth.ts +3 -2
- package/src/providers/inference/connection-availability.ts +8 -27
- package/src/providers/inference/resolve-auth.ts +4 -22
- 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__/default-provider-routes.test.ts +2 -2
- 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/debug-bash-routes.ts +173 -0
- package/src/runtime/routes/diagnostics-routes.ts +3 -55
- package/src/runtime/routes/index.ts +2 -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 +20 -6
- 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__/service-account-token.test.ts +0 -247
- 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__/bash.test.ts +0 -53
- 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 -255
- 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 -201
- 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/inference/service-account-token.ts +0 -248
- 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/tools/terminal/__tests__/sanitized-bash.test.ts +0 -57
- package/src/tools/terminal/sanitized-bash.ts +0 -105
- 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
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
createFrontDoorLegCoordinator,
|
|
5
|
-
type EscalatedLeg,
|
|
6
|
-
escalatedLegFor,
|
|
7
|
-
type FrontDoorLegHost,
|
|
8
|
-
type SpokenEscalationBridge,
|
|
9
|
-
} from "../voice-leg-coordinator.js";
|
|
10
|
-
import {
|
|
11
|
-
ESCALATION_CONTINUATION_CONTENT,
|
|
12
|
-
FALLBACK_ESCALATION_BRIDGE,
|
|
13
|
-
FALLBACK_ESCALATION_BRIDGE_BY_LANGUAGE,
|
|
14
|
-
} from "../voice-triage-escalate.js";
|
|
15
|
-
|
|
16
|
-
interface Recorded {
|
|
17
|
-
events: string[];
|
|
18
|
-
answers: string[];
|
|
19
|
-
bridges: SpokenEscalationBridge[];
|
|
20
|
-
escalated: EscalatedLeg[];
|
|
21
|
-
holds: number;
|
|
22
|
-
commits: number;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function harness(opts?: {
|
|
26
|
-
holdEnabled?: boolean;
|
|
27
|
-
live?: () => boolean;
|
|
28
|
-
language?: string;
|
|
29
|
-
commit?: () => boolean;
|
|
30
|
-
withProgress?: boolean;
|
|
31
|
-
withHold?: boolean;
|
|
32
|
-
}) {
|
|
33
|
-
const recorded: Recorded = {
|
|
34
|
-
events: [],
|
|
35
|
-
answers: [],
|
|
36
|
-
bridges: [],
|
|
37
|
-
escalated: [],
|
|
38
|
-
holds: 0,
|
|
39
|
-
commits: 0,
|
|
40
|
-
};
|
|
41
|
-
const host: FrontDoorLegHost = {
|
|
42
|
-
isLive: opts?.live ?? (() => true),
|
|
43
|
-
language: () => opts?.language,
|
|
44
|
-
...(opts?.withProgress === false
|
|
45
|
-
? {}
|
|
46
|
-
: {
|
|
47
|
-
progress: {
|
|
48
|
-
clear: () => recorded.events.push("progress.clear"),
|
|
49
|
-
noteFloorHolder: () => recorded.events.push("progress.floor"),
|
|
50
|
-
arm: () => recorded.events.push("progress.arm"),
|
|
51
|
-
},
|
|
52
|
-
}),
|
|
53
|
-
...(opts?.withHold
|
|
54
|
-
? {
|
|
55
|
-
onHold: () => {
|
|
56
|
-
recorded.holds += 1;
|
|
57
|
-
recorded.events.push("hold");
|
|
58
|
-
},
|
|
59
|
-
}
|
|
60
|
-
: {}),
|
|
61
|
-
...(opts?.commit
|
|
62
|
-
? {
|
|
63
|
-
commit: () => {
|
|
64
|
-
recorded.commits += 1;
|
|
65
|
-
recorded.events.push("commit");
|
|
66
|
-
return opts.commit!();
|
|
67
|
-
},
|
|
68
|
-
}
|
|
69
|
-
: {}),
|
|
70
|
-
onAnswerText: (text) => {
|
|
71
|
-
recorded.answers.push(text);
|
|
72
|
-
recorded.events.push(`answer:${text}`);
|
|
73
|
-
},
|
|
74
|
-
abortLeg: () => recorded.events.push("abort"),
|
|
75
|
-
speakBridge: (bridge) => {
|
|
76
|
-
recorded.bridges.push(bridge);
|
|
77
|
-
recorded.events.push(`speak:${bridge.spokenBridge}`);
|
|
78
|
-
},
|
|
79
|
-
startEscalatedLeg: (leg) => {
|
|
80
|
-
recorded.escalated.push(leg);
|
|
81
|
-
recorded.events.push("start-escalated");
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
const coordinator = createFrontDoorLegCoordinator({
|
|
85
|
-
holdEnabled: opts?.holdEnabled ?? false,
|
|
86
|
-
host,
|
|
87
|
-
});
|
|
88
|
-
return { coordinator, recorded };
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
describe("escalatedLegFor", () => {
|
|
92
|
-
test("continues on the escalated leg with the spoken bridge quoted", () => {
|
|
93
|
-
expect(escalatedLegFor("One moment.")).toEqual({
|
|
94
|
-
content: ESCALATION_CONTINUATION_CONTENT,
|
|
95
|
-
routingLeg: "escalated",
|
|
96
|
-
spokenEscalationBridge: "One moment.",
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
describe("createFrontDoorLegCoordinator", () => {
|
|
102
|
-
test("an answer releases the held leading text, then streams", () => {
|
|
103
|
-
const { coordinator, recorded } = harness();
|
|
104
|
-
coordinator.push("[");
|
|
105
|
-
expect(recorded.answers).toEqual([]);
|
|
106
|
-
coordinator.push("END_CALL] Bye");
|
|
107
|
-
coordinator.push(" now.");
|
|
108
|
-
|
|
109
|
-
expect(recorded.answers).toEqual(["[END_CALL] Bye", " now."]);
|
|
110
|
-
expect(coordinator.complete()).toBe(false);
|
|
111
|
-
expect(coordinator.handedOff).toBe(false);
|
|
112
|
-
expect(recorded.escalated).toEqual([]);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test("an escalate verdict hands off in order: pause, abort, speak, floor, start, re-arm", () => {
|
|
116
|
-
const { coordinator, recorded } = harness();
|
|
117
|
-
coordinator.push("[1] Let me check");
|
|
118
|
-
expect(coordinator.handedOff).toBe(false);
|
|
119
|
-
coordinator.push(" that.");
|
|
120
|
-
|
|
121
|
-
expect(coordinator.handedOff).toBe(true);
|
|
122
|
-
expect(recorded.events).toEqual([
|
|
123
|
-
"progress.clear",
|
|
124
|
-
"abort",
|
|
125
|
-
"speak:Let me check that.",
|
|
126
|
-
"progress.floor",
|
|
127
|
-
"start-escalated",
|
|
128
|
-
"progress.arm",
|
|
129
|
-
]);
|
|
130
|
-
expect(recorded.bridges[0]).toEqual({
|
|
131
|
-
spokenBridge: "Let me check that.",
|
|
132
|
-
usesFallback: false,
|
|
133
|
-
});
|
|
134
|
-
expect(recorded.escalated).toEqual([escalatedLegFor("Let me check that.")]);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
test("after the hand-off the leg's stream and completion are ignored", () => {
|
|
138
|
-
const { coordinator, recorded } = harness();
|
|
139
|
-
coordinator.push("[1] One moment.");
|
|
140
|
-
coordinator.push(" Ignored past the cap.");
|
|
141
|
-
expect(coordinator.complete()).toBe(true);
|
|
142
|
-
|
|
143
|
-
expect(recorded.answers).toEqual([]);
|
|
144
|
-
expect(recorded.escalated.length).toBe(1);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test("a leg that stops mid-bridge hands off on completion with the canned bridge", () => {
|
|
148
|
-
const { coordinator, recorded } = harness({ language: "es" });
|
|
149
|
-
coordinator.push("[1]");
|
|
150
|
-
expect(coordinator.handedOff).toBe(false);
|
|
151
|
-
|
|
152
|
-
expect(coordinator.complete()).toBe(true);
|
|
153
|
-
expect(recorded.bridges[0]).toEqual({
|
|
154
|
-
spokenBridge: FALLBACK_ESCALATION_BRIDGE_BY_LANGUAGE.es,
|
|
155
|
-
usesFallback: true,
|
|
156
|
-
});
|
|
157
|
-
expect(recorded.escalated[0].spokenEscalationBridge).toBe(
|
|
158
|
-
FALLBACK_ESCALATION_BRIDGE_BY_LANGUAGE.es,
|
|
159
|
-
);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
test("the canned bridge for a language the table lacks is English and says so", () => {
|
|
163
|
-
const { coordinator, recorded } = harness({ language: "ko" });
|
|
164
|
-
coordinator.push("[1]");
|
|
165
|
-
coordinator.complete();
|
|
166
|
-
|
|
167
|
-
expect(recorded.bridges[0]).toEqual({
|
|
168
|
-
spokenBridge: FALLBACK_ESCALATION_BRIDGE,
|
|
169
|
-
usesFallback: true,
|
|
170
|
-
language: "en",
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test("a dead turn never hands off", () => {
|
|
175
|
-
let live = true;
|
|
176
|
-
const { coordinator, recorded } = harness({ live: () => live });
|
|
177
|
-
coordinator.push("[1] One");
|
|
178
|
-
live = false;
|
|
179
|
-
coordinator.push(" moment.");
|
|
180
|
-
expect(coordinator.handedOff).toBe(false);
|
|
181
|
-
expect(coordinator.complete()).toBe(false);
|
|
182
|
-
|
|
183
|
-
expect(recorded.escalated).toEqual([]);
|
|
184
|
-
expect(recorded.events).toEqual([]);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
test("a hold verdict on a speculative leg discards the leg", () => {
|
|
188
|
-
const { coordinator, recorded } = harness({
|
|
189
|
-
holdEnabled: true,
|
|
190
|
-
withHold: true,
|
|
191
|
-
});
|
|
192
|
-
coordinator.push("[0]");
|
|
193
|
-
|
|
194
|
-
expect(recorded.holds).toBe(1);
|
|
195
|
-
expect(recorded.answers).toEqual([]);
|
|
196
|
-
expect(coordinator.handedOff).toBe(false);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
test("a leg that does not teach the hold token reads [0] as an answer", () => {
|
|
200
|
-
const { coordinator, recorded } = harness({ withHold: true });
|
|
201
|
-
coordinator.push("[0] Sure.");
|
|
202
|
-
|
|
203
|
-
expect(recorded.holds).toBe(0);
|
|
204
|
-
expect(recorded.answers).toEqual(["[0] Sure."]);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
test("answer and escalate verdicts commit a speculative turn first; a failed commit stops the verdict", () => {
|
|
208
|
-
const committed = harness({ commit: () => true });
|
|
209
|
-
committed.coordinator.push("Hello.");
|
|
210
|
-
expect(committed.recorded.events).toEqual(["commit", "answer:Hello."]);
|
|
211
|
-
|
|
212
|
-
const gone = harness({ commit: () => false });
|
|
213
|
-
gone.coordinator.push("[1] One moment.");
|
|
214
|
-
expect(gone.recorded.commits).toBe(1);
|
|
215
|
-
expect(gone.coordinator.handedOff).toBe(false);
|
|
216
|
-
expect(gone.recorded.escalated).toEqual([]);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
test("works without a progress cadence", () => {
|
|
220
|
-
const { coordinator, recorded } = harness({ withProgress: false });
|
|
221
|
-
coordinator.push("[1] One moment.");
|
|
222
|
-
|
|
223
|
-
expect(recorded.events).toEqual([
|
|
224
|
-
"abort",
|
|
225
|
-
"speak:One moment.",
|
|
226
|
-
"start-escalated",
|
|
227
|
-
]);
|
|
228
|
-
});
|
|
229
|
-
});
|
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { VoiceProgressConfigSchema } from "../../config/schemas/voice.js";
|
|
4
|
-
import type { VoiceProgressNarrator } from "../progress-narration.js";
|
|
5
|
-
import {
|
|
6
|
-
PROGRESS_FALLBACK_PHRASES,
|
|
7
|
-
PROGRESS_FALLBACK_PHRASES_BY_LANGUAGE,
|
|
8
|
-
} from "../progress-phrases.js";
|
|
9
|
-
import {
|
|
10
|
-
createProgressCadence,
|
|
11
|
-
type ProgressCadence,
|
|
12
|
-
type ProgressCadenceHost,
|
|
13
|
-
} from "../voice-progress-cadence.js";
|
|
14
|
-
|
|
15
|
-
const sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
|
|
16
|
-
|
|
17
|
-
async function waitFor(predicate: () => boolean, attempts = 60): Promise<void> {
|
|
18
|
-
for (let i = 0; i < attempts; i += 1) {
|
|
19
|
-
if (predicate()) {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
await sleep(5);
|
|
23
|
-
}
|
|
24
|
-
throw new Error("waitFor timed out");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface Harness {
|
|
28
|
-
cadence: ProgressCadence;
|
|
29
|
-
spoken: Array<{ phrase: string; language: string | undefined }>;
|
|
30
|
-
host: {
|
|
31
|
-
live: boolean;
|
|
32
|
-
audioIdle: boolean;
|
|
33
|
-
tailUntilMs: number;
|
|
34
|
-
language: string | undefined;
|
|
35
|
-
deltaEpoch: number;
|
|
36
|
-
narrationsSpoken: number;
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function harness(opts: {
|
|
41
|
-
config?: Partial<ReturnType<typeof VoiceProgressConfigSchema.parse>>;
|
|
42
|
-
narrator?: VoiceProgressNarrator | null;
|
|
43
|
-
language?: string;
|
|
44
|
-
}): Harness {
|
|
45
|
-
const state = {
|
|
46
|
-
live: true,
|
|
47
|
-
audioIdle: true,
|
|
48
|
-
tailUntilMs: 0,
|
|
49
|
-
language: opts.language,
|
|
50
|
-
deltaEpoch: 0,
|
|
51
|
-
narrationsSpoken: 0,
|
|
52
|
-
};
|
|
53
|
-
const spoken: Harness["spoken"] = [];
|
|
54
|
-
const host: ProgressCadenceHost = {
|
|
55
|
-
turnId: "turn-1",
|
|
56
|
-
launchedAtMs: Date.now(),
|
|
57
|
-
signal: new AbortController().signal,
|
|
58
|
-
canNarrate: () => state.live,
|
|
59
|
-
isAudioIdle: () => state.audioIdle,
|
|
60
|
-
playbackTailUntilMs: () => state.tailUntilMs,
|
|
61
|
-
transcriptSoFar: () => "what is on my calendar",
|
|
62
|
-
language: () => state.language,
|
|
63
|
-
deltaEpoch: () => state.deltaEpoch,
|
|
64
|
-
speak: (phrase, language) => {
|
|
65
|
-
spoken.push({ phrase, language });
|
|
66
|
-
return true;
|
|
67
|
-
},
|
|
68
|
-
onNarrationSpoken: () => {
|
|
69
|
-
state.narrationsSpoken += 1;
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
let counter = 0;
|
|
73
|
-
const cadence = createProgressCadence({
|
|
74
|
-
config: VoiceProgressConfigSchema.parse({
|
|
75
|
-
opsThreshold: 3,
|
|
76
|
-
idleIntervalMs: 60_000,
|
|
77
|
-
maxSilenceMs: 60_000,
|
|
78
|
-
longOpMs: 15_000,
|
|
79
|
-
minGapMs: 10,
|
|
80
|
-
generationTimeoutMs: 1_500,
|
|
81
|
-
...opts.config,
|
|
82
|
-
}),
|
|
83
|
-
narrator:
|
|
84
|
-
opts.narrator === undefined
|
|
85
|
-
? { generateProgressText: async () => "Checking your calendar now." }
|
|
86
|
-
: opts.narrator,
|
|
87
|
-
host,
|
|
88
|
-
nextFallbackPhraseIndex: () => counter++,
|
|
89
|
-
});
|
|
90
|
-
return { cadence, spoken, host: state };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
describe("createProgressCadence", () => {
|
|
94
|
-
test("the ops trigger narrates once the threshold of started tools is reached", async () => {
|
|
95
|
-
const h = harness({});
|
|
96
|
-
h.cadence.toolStarted("web_search", "t1");
|
|
97
|
-
h.cadence.toolStarted("web_fetch", "t2");
|
|
98
|
-
await sleep(15);
|
|
99
|
-
expect(h.spoken).toEqual([]);
|
|
100
|
-
|
|
101
|
-
h.cadence.toolStarted("file_read", "t3");
|
|
102
|
-
await waitFor(() => h.spoken.length === 1);
|
|
103
|
-
|
|
104
|
-
expect(h.spoken[0]).toEqual({
|
|
105
|
-
phrase: "Checking your calendar now.",
|
|
106
|
-
language: undefined,
|
|
107
|
-
});
|
|
108
|
-
expect(h.cadence.updatesSpoken).toBe(1);
|
|
109
|
-
expect(h.host.narrationsSpoken).toBe(1);
|
|
110
|
-
h.cadence.clear();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test("a long operation completing narrates on its own beat", async () => {
|
|
114
|
-
const h = harness({ config: { longOpMs: 20 } });
|
|
115
|
-
h.cadence.toolStarted("web_search", "t1");
|
|
116
|
-
await sleep(30);
|
|
117
|
-
h.cadence.toolFinished({
|
|
118
|
-
toolName: "web_search",
|
|
119
|
-
toolUseId: "t1",
|
|
120
|
-
resultPreview: "three results",
|
|
121
|
-
});
|
|
122
|
-
await waitFor(() => h.spoken.length === 1);
|
|
123
|
-
|
|
124
|
-
expect(h.cadence.ops[0].completedAtMs).toBeDefined();
|
|
125
|
-
expect(h.cadence.ops[0].resultPreview).toBe("three results");
|
|
126
|
-
h.cadence.clear();
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
test("a quick operation completing is only news, not a narration", async () => {
|
|
130
|
-
const h = harness({});
|
|
131
|
-
h.cadence.toolStarted("web_search", "t1");
|
|
132
|
-
h.cadence.toolFinished({ toolName: "web_search", toolUseId: "t1" });
|
|
133
|
-
await sleep(20);
|
|
134
|
-
|
|
135
|
-
expect(h.spoken).toEqual([]);
|
|
136
|
-
h.cadence.clear();
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
test("a tool_result with no id matches the newest incomplete op of that name", () => {
|
|
140
|
-
const h = harness({});
|
|
141
|
-
h.cadence.toolStarted("web_search", "t1");
|
|
142
|
-
h.cadence.toolStarted("web_search", "t2");
|
|
143
|
-
h.cadence.toolFinished({ toolName: "web_search", isError: true });
|
|
144
|
-
|
|
145
|
-
expect(h.cadence.ops[1].completedAtMs).toBeDefined();
|
|
146
|
-
expect(h.cadence.ops[1].isError).toBe(true);
|
|
147
|
-
expect(h.cadence.ops[0].completedAtMs).toBeUndefined();
|
|
148
|
-
h.cadence.clear();
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
test("the idle tick narrates news since the last update, and the static fallback covers a null generation", async () => {
|
|
152
|
-
const h = harness({
|
|
153
|
-
config: { idleIntervalMs: 30, maxSilenceMs: 60_000 },
|
|
154
|
-
narrator: { generateProgressText: async () => null },
|
|
155
|
-
language: "es",
|
|
156
|
-
});
|
|
157
|
-
h.cadence.arm();
|
|
158
|
-
await sleep(60);
|
|
159
|
-
// Nothing observable happened: the tick stays quiet.
|
|
160
|
-
expect(h.spoken).toEqual([]);
|
|
161
|
-
|
|
162
|
-
h.cadence.toolStarted("web_search", "t1");
|
|
163
|
-
await waitFor(() => h.spoken.length === 1);
|
|
164
|
-
|
|
165
|
-
expect(
|
|
166
|
-
PROGRESS_FALLBACK_PHRASES_BY_LANGUAGE.es.map((p) => p.trim()),
|
|
167
|
-
).toContain(h.spoken[0].phrase.trim());
|
|
168
|
-
// The Spanish table has an entry: the phrase rides the turn's language.
|
|
169
|
-
expect(h.spoken[0].language).toBeUndefined();
|
|
170
|
-
h.cadence.clear();
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test("a static fallback the table lacks in the turn's language is pinned to English", async () => {
|
|
174
|
-
const h = harness({
|
|
175
|
-
config: { idleIntervalMs: 30 },
|
|
176
|
-
narrator: { generateProgressText: async () => null },
|
|
177
|
-
language: "ko",
|
|
178
|
-
});
|
|
179
|
-
h.cadence.arm();
|
|
180
|
-
h.cadence.toolStarted("web_search", "t1");
|
|
181
|
-
await waitFor(() => h.spoken.length === 1);
|
|
182
|
-
|
|
183
|
-
expect(PROGRESS_FALLBACK_PHRASES).toContain(h.spoken[0].phrase);
|
|
184
|
-
expect(h.spoken[0].language).toBe("en");
|
|
185
|
-
h.cadence.clear();
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
test("the heartbeat speaks after maxSilenceMs even with nothing new", async () => {
|
|
189
|
-
const h = harness({
|
|
190
|
-
config: { idleIntervalMs: 30, maxSilenceMs: 30 },
|
|
191
|
-
});
|
|
192
|
-
h.cadence.arm();
|
|
193
|
-
await waitFor(() => h.spoken.length === 1);
|
|
194
|
-
h.cadence.clear();
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test("nothing speaks while audio is still playing; the countdown restarts once it drains", async () => {
|
|
198
|
-
const h = harness({ config: { idleIntervalMs: 30, maxSilenceMs: 30 } });
|
|
199
|
-
h.host.audioIdle = false;
|
|
200
|
-
h.cadence.arm();
|
|
201
|
-
await sleep(80);
|
|
202
|
-
expect(h.spoken).toEqual([]);
|
|
203
|
-
|
|
204
|
-
h.host.audioIdle = true;
|
|
205
|
-
h.cadence.noteAudioSettled();
|
|
206
|
-
await waitFor(() => h.spoken.length === 1);
|
|
207
|
-
h.cadence.clear();
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
test("a narration generated while the model resumed speaking is dropped", async () => {
|
|
211
|
-
let release: () => void = () => {};
|
|
212
|
-
const h = harness({
|
|
213
|
-
config: { opsThreshold: 1 },
|
|
214
|
-
narrator: {
|
|
215
|
-
generateProgressText: () =>
|
|
216
|
-
new Promise<string | null>((resolve) => {
|
|
217
|
-
release = () => resolve("Still on it.");
|
|
218
|
-
}),
|
|
219
|
-
},
|
|
220
|
-
});
|
|
221
|
-
h.cadence.toolStarted("web_search", "t1");
|
|
222
|
-
await sleep(10);
|
|
223
|
-
h.host.deltaEpoch += 1;
|
|
224
|
-
release();
|
|
225
|
-
await sleep(20);
|
|
226
|
-
|
|
227
|
-
expect(h.spoken).toEqual([]);
|
|
228
|
-
h.cadence.clear();
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
test("minGapMs spaces narration from another floor holder", async () => {
|
|
232
|
-
const h = harness({ config: { opsThreshold: 1, minGapMs: 200 } });
|
|
233
|
-
h.cadence.noteFloorHolder();
|
|
234
|
-
h.cadence.toolStarted("web_search", "t1");
|
|
235
|
-
await sleep(30);
|
|
236
|
-
|
|
237
|
-
expect(h.spoken).toEqual([]);
|
|
238
|
-
h.cadence.clear();
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
test("a null narrator or a disabled config never speaks or arms", async () => {
|
|
242
|
-
const off = harness({ narrator: null, config: { idleIntervalMs: 20 } });
|
|
243
|
-
off.cadence.arm();
|
|
244
|
-
off.cadence.toolStarted("a", "1");
|
|
245
|
-
off.cadence.toolStarted("b", "2");
|
|
246
|
-
off.cadence.toolStarted("c", "3");
|
|
247
|
-
const disabled = harness({
|
|
248
|
-
config: { enabled: false, idleIntervalMs: 20 },
|
|
249
|
-
});
|
|
250
|
-
disabled.cadence.arm();
|
|
251
|
-
disabled.cadence.toolStarted("a", "1");
|
|
252
|
-
disabled.cadence.toolStarted("b", "2");
|
|
253
|
-
disabled.cadence.toolStarted("c", "3");
|
|
254
|
-
await sleep(60);
|
|
255
|
-
|
|
256
|
-
expect(off.spoken).toEqual([]);
|
|
257
|
-
expect(disabled.spoken).toEqual([]);
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
test("clear stops the dead-air timer and a dead turn is never narrated", async () => {
|
|
261
|
-
const h = harness({ config: { idleIntervalMs: 20, maxSilenceMs: 20 } });
|
|
262
|
-
h.cadence.arm();
|
|
263
|
-
h.cadence.clear();
|
|
264
|
-
await sleep(60);
|
|
265
|
-
expect(h.spoken).toEqual([]);
|
|
266
|
-
|
|
267
|
-
h.host.live = false;
|
|
268
|
-
h.cadence.toolStarted("a", "1");
|
|
269
|
-
h.cadence.toolStarted("b", "2");
|
|
270
|
-
h.cadence.toolStarted("c", "3");
|
|
271
|
-
await sleep(20);
|
|
272
|
-
expect(h.spoken).toEqual([]);
|
|
273
|
-
});
|
|
274
|
-
});
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The sustained-speech barge-in guard, shared by the live-voice session and
|
|
3
|
-
* the phone media-stream path.
|
|
4
|
-
*
|
|
5
|
-
* A single above-gate audio chunk is not an interruption: brief TTS bleed
|
|
6
|
-
* through imperfect echo cancellation, a cough, a line click. Speech has to
|
|
7
|
-
* accumulate to `minSpeechMs` before the assistant is cut off. Short gaps
|
|
8
|
-
* inside a run are tolerated (a browser's half-duplex echo canceller ducks
|
|
9
|
-
* the near-end voice into intermittent above-gate chunks; a phone line
|
|
10
|
-
* drops frames), but a longer continuous silence, or a run that is mostly
|
|
11
|
-
* silence, resets it, so sparse periodic blips can never sum into a
|
|
12
|
-
* barge-in.
|
|
13
|
-
*
|
|
14
|
-
* The guard is pure accounting over classified chunks. What arms it, what
|
|
15
|
-
* classifies a chunk, and what happens when it fires belong to the driver.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
/** Sustained speech required before a barge-in fires. */
|
|
19
|
-
export const DEFAULT_BARGE_IN_MIN_SPEECH_MS = 250;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Longest continuous sub-threshold gap tolerated inside a run. A gap of
|
|
23
|
-
* exactly this length still counts as inside the run: the web client
|
|
24
|
-
* batches PCM into 50 ms frames, so a run of ducked frames lands on the
|
|
25
|
-
* boundary exactly (four frames = 200 ms).
|
|
26
|
-
*/
|
|
27
|
-
export const BARGE_IN_GAP_TOLERANCE_MS = 200;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Ceiling on cumulative sub-threshold time across a whole run, as a
|
|
31
|
-
* multiple of `minSpeechMs`. Per-gap tolerance alone lets sparse isolated
|
|
32
|
-
* blips (a 10 ms echo spike every 200 ms) each clear the consecutive-gap
|
|
33
|
-
* timer while retaining prior speech, so they would sum to the guard over
|
|
34
|
-
* several seconds. Capping total tolerated silence imposes a minimum
|
|
35
|
-
* above-gate duty cycle (1 / (1 + ratio), about 20%): once the run is mostly
|
|
36
|
-
* silence it resets, so genuine choppy speech still lands but periodic noise
|
|
37
|
-
* cannot accumulate into one.
|
|
38
|
-
*/
|
|
39
|
-
export const BARGE_IN_MAX_TOLERATED_SILENCE_RATIO = 4;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* How a chunk reads to the guard. `echo` is the assistant's own playback
|
|
43
|
-
* heard back through the microphone, which live voice classifies; a phone
|
|
44
|
-
* line carries only the caller's track and never produces it.
|
|
45
|
-
*/
|
|
46
|
-
export type BargeInChunkClassification = "speech" | "silence" | "echo";
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* What one chunk did to the run: `pending` (still accumulating, or a
|
|
50
|
-
* tolerated gap), `reset` (echo, or a gap past the limits, dropped the run),
|
|
51
|
-
* `fired` (sustained speech reached the threshold; the guard is spent).
|
|
52
|
-
*/
|
|
53
|
-
export type BargeInGuardStep = "pending" | "reset" | "fired";
|
|
54
|
-
|
|
55
|
-
export interface BargeInGuard {
|
|
56
|
-
/** Speech accumulated in the current run. */
|
|
57
|
-
readonly speechMs: number;
|
|
58
|
-
/** Feed one classified chunk of `chunkMs` audio. */
|
|
59
|
-
track(
|
|
60
|
-
classification: BargeInChunkClassification,
|
|
61
|
-
chunkMs: number,
|
|
62
|
-
): BargeInGuardStep;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Build a guard that fires after `minSpeechMs` of sustained speech. A zero
|
|
67
|
-
* threshold fires on the first speech chunk (instant barge-in).
|
|
68
|
-
*/
|
|
69
|
-
export function createBargeInGuard(minSpeechMs: number): BargeInGuard {
|
|
70
|
-
let speechMs = 0;
|
|
71
|
-
// Consecutive sub-threshold time since the last speech chunk.
|
|
72
|
-
let silenceMs = 0;
|
|
73
|
-
// Cumulative sub-threshold time over the whole run, never reset by speech.
|
|
74
|
-
let toleratedSilenceMs = 0;
|
|
75
|
-
let fired = false;
|
|
76
|
-
|
|
77
|
-
const reset = (): BargeInGuardStep => {
|
|
78
|
-
speechMs = 0;
|
|
79
|
-
silenceMs = 0;
|
|
80
|
-
toleratedSilenceMs = 0;
|
|
81
|
-
return "reset";
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
get speechMs() {
|
|
86
|
-
return speechMs;
|
|
87
|
-
},
|
|
88
|
-
track(classification, chunkMs) {
|
|
89
|
-
if (fired) {
|
|
90
|
-
return "fired";
|
|
91
|
-
}
|
|
92
|
-
if (classification === "echo") {
|
|
93
|
-
return reset();
|
|
94
|
-
}
|
|
95
|
-
if (classification === "silence") {
|
|
96
|
-
silenceMs += chunkMs;
|
|
97
|
-
toleratedSilenceMs += chunkMs;
|
|
98
|
-
// Strictly greater on the per-gap check: a gap of exactly the
|
|
99
|
-
// tolerance is still inside the run.
|
|
100
|
-
if (
|
|
101
|
-
silenceMs > BARGE_IN_GAP_TOLERANCE_MS ||
|
|
102
|
-
toleratedSilenceMs >
|
|
103
|
-
minSpeechMs * BARGE_IN_MAX_TOLERATED_SILENCE_RATIO
|
|
104
|
-
) {
|
|
105
|
-
return reset();
|
|
106
|
-
}
|
|
107
|
-
return "pending";
|
|
108
|
-
}
|
|
109
|
-
silenceMs = 0;
|
|
110
|
-
speechMs += chunkMs;
|
|
111
|
-
if (speechMs < minSpeechMs) {
|
|
112
|
-
return "pending";
|
|
113
|
-
}
|
|
114
|
-
fired = true;
|
|
115
|
-
return "fired";
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
}
|