@vellumai/assistant 0.12.0 → 0.12.1-dev.202609141913.5e16607
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 +5 -1
- package/ARCHITECTURE.md +11 -12
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/openapi.yaml +292 -50
- package/package.json +1 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +17 -5
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- 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 +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +212 -3
- package/src/__tests__/credential-security-invariants.test.ts +4 -2
- package/src/__tests__/cu-unified-flow.test.ts +8 -2
- package/src/__tests__/document-sync-tags.test.ts +25 -0
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +8 -16
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +13 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/service-account-token.test.ts +247 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +19 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +94 -9
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/index.ts +6 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +58 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +37 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +53 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +59 -46
- package/src/calls/voice-triage-escalate.ts +175 -56
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/bash.test.ts +53 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/bash.help.ts +8 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- 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 +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +255 -0
- package/src/config/__tests__/platform-rehydration.test.ts +99 -9
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- 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 +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +3 -4
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- 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 +24 -12
- package/src/config/loader.ts +1 -0
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +201 -0
- package/src/config/platform-rehydration.ts +11 -27
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +10 -8
- package/src/daemon/conversation-tool-setup.ts +37 -43
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +119 -0
- package/src/desktop/desktop-chrome-policy.test.ts +71 -0
- package/src/desktop/desktop-chrome-policy.ts +42 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +306 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +627 -0
- package/src/desktop/desktop-session-manager.ts +814 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- 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/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/live-voice-session.ts +213 -693
- package/src/live-voice/protocol.ts +0 -2
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/guardian-feed-projection.ts +125 -5
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/notifications/schedule-result-producer.ts +66 -22
- package/src/oauth/__tests__/seed-providers-managed.test.ts +54 -0
- package/src/oauth/byo-connection.test.ts +45 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +73 -0
- package/src/oauth/connection-resolver.ts +95 -2
- package/src/oauth/seed-providers.ts +165 -0
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- 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-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/graph/capability-seed.ts +4 -6
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +6 -1
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +89 -0
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +192 -78
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/runtime/AGENTS.md +2 -0
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +73 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/credential-routes.ts +19 -10
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +55 -3
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +4 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/safe-env.test.ts +6 -6
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/safe-env.ts +23 -3
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +5 -54
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/runtime/routes/debug-bash-routes.ts +0 -173
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_LOCALE,
|
|
5
|
+
isMessageKey,
|
|
6
|
+
localeFromAcceptLanguage,
|
|
7
|
+
MESSAGE_CATALOGS,
|
|
8
|
+
MESSAGE_KEYS,
|
|
9
|
+
negotiateLocale,
|
|
10
|
+
resolveConversationTitle,
|
|
11
|
+
SUPPORTED_LOCALES,
|
|
12
|
+
t,
|
|
13
|
+
} from "../index.js";
|
|
14
|
+
|
|
15
|
+
describe("negotiateLocale", () => {
|
|
16
|
+
test("matches a full shipped tag", () => {
|
|
17
|
+
expect(negotiateLocale(["es"])).toBe("es");
|
|
18
|
+
expect(negotiateLocale(["zh-TW"])).toBe("zh-TW");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("falls back from a regional tag to the primary language", () => {
|
|
22
|
+
expect(negotiateLocale(["es-MX", "en"])).toBe("es");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("maps Traditional Chinese tags to zh-TW", () => {
|
|
26
|
+
expect(negotiateLocale(["zh-Hant"])).toBe("zh-TW");
|
|
27
|
+
expect(negotiateLocale(["zh-HK"])).toBe("zh-TW");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("defaults when nothing matches", () => {
|
|
31
|
+
expect(negotiateLocale([])).toBe(DEFAULT_LOCALE);
|
|
32
|
+
expect(negotiateLocale(["xx"])).toBe(DEFAULT_LOCALE);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("localeFromAcceptLanguage", () => {
|
|
37
|
+
test("honors quality values", () => {
|
|
38
|
+
expect(localeFromAcceptLanguage("fr;q=0.8, es;q=0.9, en;q=0.5")).toBe(
|
|
39
|
+
"es",
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("defaults on missing or empty", () => {
|
|
44
|
+
expect(localeFromAcceptLanguage(null)).toBe(DEFAULT_LOCALE);
|
|
45
|
+
expect(localeFromAcceptLanguage("")).toBe(DEFAULT_LOCALE);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe("catalog completeness", () => {
|
|
50
|
+
test("every locale has every key", () => {
|
|
51
|
+
const keys = Object.values(MESSAGE_KEYS);
|
|
52
|
+
for (const locale of SUPPORTED_LOCALES) {
|
|
53
|
+
for (const key of keys) {
|
|
54
|
+
expect(MESSAGE_CATALOGS[locale][key].length).toBeGreaterThan(0);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("t", () => {
|
|
61
|
+
test("resolves generating and untitled per locale", () => {
|
|
62
|
+
expect(t(MESSAGE_KEYS.CONVERSATION_TITLE_GENERATING, "en")).toBe(
|
|
63
|
+
"Generating title...",
|
|
64
|
+
);
|
|
65
|
+
expect(t(MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED, "en")).toBe(
|
|
66
|
+
"Untitled Conversation",
|
|
67
|
+
);
|
|
68
|
+
expect(t(MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED, "es")).toBe(
|
|
69
|
+
"Sin título",
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("resolveConversationTitle", () => {
|
|
75
|
+
test("resolves a stored key and an empty title", () => {
|
|
76
|
+
expect(
|
|
77
|
+
resolveConversationTitle(MESSAGE_KEYS.CONVERSATION_TITLE_GENERATING, "zh"),
|
|
78
|
+
).toBe("标题生成中...");
|
|
79
|
+
expect(resolveConversationTitle(null, "es")).toBe("Sin título");
|
|
80
|
+
expect(resolveConversationTitle("")).toBe("Untitled Conversation");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("passes stored display strings through, including English placeholders", () => {
|
|
84
|
+
expect(resolveConversationTitle("Untitled Conversation", "es")).toBe(
|
|
85
|
+
"Untitled Conversation",
|
|
86
|
+
);
|
|
87
|
+
expect(resolveConversationTitle("Generating title...", "zh")).toBe(
|
|
88
|
+
"Generating title...",
|
|
89
|
+
);
|
|
90
|
+
expect(resolveConversationTitle("Auth Middleware Rewrite", "es")).toBe(
|
|
91
|
+
"Auth Middleware Rewrite",
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("defaults locale inside the helper", () => {
|
|
96
|
+
expect(
|
|
97
|
+
resolveConversationTitle(MESSAGE_KEYS.CONVERSATION_TITLE_GENERATING),
|
|
98
|
+
).toBe("Generating title...");
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe("isMessageKey", () => {
|
|
103
|
+
test("recognizes catalog keys only", () => {
|
|
104
|
+
expect(isMessageKey(MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED)).toBe(true);
|
|
105
|
+
expect(isMessageKey("Untitled Conversation")).toBe(false);
|
|
106
|
+
expect(isMessageKey("Untitled")).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export {
|
|
2
|
+
DEFAULT_LOCALE,
|
|
3
|
+
isSupportedLocale,
|
|
4
|
+
localeFromAcceptLanguage,
|
|
5
|
+
negotiateLocale,
|
|
6
|
+
SUPPORTED_LOCALES,
|
|
7
|
+
type SupportedLocale,
|
|
8
|
+
} from "./locales.js";
|
|
9
|
+
export {
|
|
10
|
+
isMessageKey,
|
|
11
|
+
MESSAGE_CATALOGS,
|
|
12
|
+
MESSAGE_KEYS,
|
|
13
|
+
type MessageKey,
|
|
14
|
+
t,
|
|
15
|
+
} from "./messages.js";
|
|
16
|
+
export { resolveConversationTitle } from "./title.js";
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locales the daemon can resolve user-facing copy into.
|
|
3
|
+
*
|
|
4
|
+
* Keep this set aligned with `clients/web/src/i18n/supported-locales.ts`.
|
|
5
|
+
* A locale that exists only here cannot be requested by the web app; a
|
|
6
|
+
* locale that exists only there cannot be resolved for CLI or HTTP
|
|
7
|
+
* consumers that go through this module.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const DEFAULT_LOCALE = "en";
|
|
11
|
+
|
|
12
|
+
export const SUPPORTED_LOCALES = ["en", "es", "ru", "zh", "zh-TW"] as const;
|
|
13
|
+
|
|
14
|
+
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
|
|
15
|
+
|
|
16
|
+
export function isSupportedLocale(value: unknown): value is SupportedLocale {
|
|
17
|
+
return (
|
|
18
|
+
typeof value === "string" &&
|
|
19
|
+
(SUPPORTED_LOCALES as readonly string[]).includes(value)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Pick the best shipped locale for an ordered list of preferred BCP 47 tags.
|
|
25
|
+
*
|
|
26
|
+
* Full tag first, then the primary language subtag, so `es-MX` resolves to
|
|
27
|
+
* `es`. `zh-Hant` / `zh-HK` / `zh-MO` map to `zh-TW`.
|
|
28
|
+
*/
|
|
29
|
+
export function negotiateLocale(preferred: readonly string[]): SupportedLocale {
|
|
30
|
+
for (const tag of preferred) {
|
|
31
|
+
const normalized = tag.trim();
|
|
32
|
+
if (normalized === "") {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const exact = SUPPORTED_LOCALES.find(
|
|
36
|
+
(locale) => locale.toLowerCase() === normalized.toLowerCase(),
|
|
37
|
+
);
|
|
38
|
+
if (exact) {
|
|
39
|
+
return exact;
|
|
40
|
+
}
|
|
41
|
+
const lower = normalized.toLowerCase();
|
|
42
|
+
if (
|
|
43
|
+
lower === "zh-hant" ||
|
|
44
|
+
lower.startsWith("zh-hant-") ||
|
|
45
|
+
lower === "zh-hk" ||
|
|
46
|
+
lower === "zh-mo"
|
|
47
|
+
) {
|
|
48
|
+
return "zh-TW";
|
|
49
|
+
}
|
|
50
|
+
const base = normalized.split("-")[0]?.toLowerCase();
|
|
51
|
+
const baseMatched = SUPPORTED_LOCALES.find(
|
|
52
|
+
(locale) => locale.toLowerCase() === base,
|
|
53
|
+
);
|
|
54
|
+
if (baseMatched) {
|
|
55
|
+
return baseMatched;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return DEFAULT_LOCALE;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Parse an `Accept-Language` header into preference order and negotiate.
|
|
63
|
+
*
|
|
64
|
+
* Quality values are honored; the header is a hint, not a required input.
|
|
65
|
+
* Missing or empty headers resolve to {@link DEFAULT_LOCALE}.
|
|
66
|
+
*/
|
|
67
|
+
export function localeFromAcceptLanguage(
|
|
68
|
+
header: string | null | undefined,
|
|
69
|
+
): SupportedLocale {
|
|
70
|
+
if (header == null || header.trim() === "") {
|
|
71
|
+
return DEFAULT_LOCALE;
|
|
72
|
+
}
|
|
73
|
+
const tags = header
|
|
74
|
+
.split(",")
|
|
75
|
+
.map((part) => {
|
|
76
|
+
const [tag, ...params] = part.trim().split(";");
|
|
77
|
+
let quality = 1;
|
|
78
|
+
for (const param of params) {
|
|
79
|
+
const [name, value] = param.trim().split("=");
|
|
80
|
+
if (name === "q" && value !== undefined) {
|
|
81
|
+
const parsed = Number.parseFloat(value);
|
|
82
|
+
if (Number.isFinite(parsed)) {
|
|
83
|
+
quality = parsed;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return { tag: tag?.trim() ?? "", quality };
|
|
88
|
+
})
|
|
89
|
+
.filter((entry) => entry.tag.length > 0)
|
|
90
|
+
.sort((a, b) => b.quality - a.quality)
|
|
91
|
+
.map((entry) => entry.tag);
|
|
92
|
+
return negotiateLocale(tags);
|
|
93
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon message catalog.
|
|
3
|
+
*
|
|
4
|
+
* Keys are the stable identity of a hardcoded constant. English is the
|
|
5
|
+
* source language. Add a key here, then add the same key to every locale
|
|
6
|
+
* catalog. A key present in a translated catalog but missing from English
|
|
7
|
+
* is a test failure.
|
|
8
|
+
*
|
|
9
|
+
* Persist the key when a column holds a system constant. Never persist a
|
|
10
|
+
* translated string, and never treat a stored display string as a key:
|
|
11
|
+
* a user title may be in any language.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
DEFAULT_LOCALE,
|
|
16
|
+
type SupportedLocale,
|
|
17
|
+
} from "./locales.js";
|
|
18
|
+
|
|
19
|
+
export const MESSAGE_KEYS = {
|
|
20
|
+
CONVERSATION_TITLE_GENERATING: "conversation.title.generating",
|
|
21
|
+
CONVERSATION_TITLE_UNTITLED: "conversation.title.untitled",
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
export type MessageKey = (typeof MESSAGE_KEYS)[keyof typeof MESSAGE_KEYS];
|
|
25
|
+
|
|
26
|
+
export const MESSAGE_CATALOGS: Record<
|
|
27
|
+
SupportedLocale,
|
|
28
|
+
Record<MessageKey, string>
|
|
29
|
+
> = {
|
|
30
|
+
en: {
|
|
31
|
+
"conversation.title.generating": "Generating title...",
|
|
32
|
+
"conversation.title.untitled": "Untitled Conversation",
|
|
33
|
+
},
|
|
34
|
+
es: {
|
|
35
|
+
"conversation.title.generating": "Generando título...",
|
|
36
|
+
"conversation.title.untitled": "Sin título",
|
|
37
|
+
},
|
|
38
|
+
ru: {
|
|
39
|
+
"conversation.title.generating": "Создание названия...",
|
|
40
|
+
"conversation.title.untitled": "Без названия",
|
|
41
|
+
},
|
|
42
|
+
zh: {
|
|
43
|
+
"conversation.title.generating": "标题生成中...",
|
|
44
|
+
"conversation.title.untitled": "无标题",
|
|
45
|
+
},
|
|
46
|
+
"zh-TW": {
|
|
47
|
+
"conversation.title.generating": "標題產生中...",
|
|
48
|
+
"conversation.title.untitled": "未命名",
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export function t(
|
|
53
|
+
key: MessageKey,
|
|
54
|
+
locale: SupportedLocale = DEFAULT_LOCALE,
|
|
55
|
+
): string {
|
|
56
|
+
return MESSAGE_CATALOGS[locale][key] ?? MESSAGE_CATALOGS[DEFAULT_LOCALE][key];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function isMessageKey(value: string): value is MessageKey {
|
|
60
|
+
return (Object.values(MESSAGE_KEYS) as string[]).includes(value);
|
|
61
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a conversation title for a locale.
|
|
3
|
+
*
|
|
4
|
+
* A stored message key (or an empty title) is a hardcoded constant and
|
|
5
|
+
* resolves through the catalog. Any other stored string is user or model
|
|
6
|
+
* copy and passes through, including English placeholders written before
|
|
7
|
+
* keys were persisted.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { DEFAULT_LOCALE, type SupportedLocale } from "./locales.js";
|
|
11
|
+
import { isMessageKey, MESSAGE_KEYS, t } from "./messages.js";
|
|
12
|
+
|
|
13
|
+
export function resolveConversationTitle(
|
|
14
|
+
title: string | null | undefined,
|
|
15
|
+
locale: SupportedLocale = DEFAULT_LOCALE,
|
|
16
|
+
): string {
|
|
17
|
+
const trimmed = title?.trim() ?? "";
|
|
18
|
+
if (trimmed === "") {
|
|
19
|
+
return t(MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED, locale);
|
|
20
|
+
}
|
|
21
|
+
if (isMessageKey(trimmed)) {
|
|
22
|
+
return t(trimmed, locale);
|
|
23
|
+
}
|
|
24
|
+
return trimmed;
|
|
25
|
+
}
|
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
* the platform can register a route that points at this instance.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import {
|
|
21
|
+
import { getPlatformBaseUrl } from "../config/env.js";
|
|
22
22
|
import { getIsPlatform } from "../config/env-registry.js";
|
|
23
23
|
import { getConfig } from "../config/loader.js";
|
|
24
|
+
import { resolvePlatformAssistantId } from "../config/platform-identity.js";
|
|
24
25
|
import { ipcRegisterWebhookRoute } from "../ipc/gateway-client.js";
|
|
25
26
|
import { credentialKey } from "../security/credential-key.js";
|
|
26
27
|
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
@@ -45,11 +46,11 @@ export interface PlatformCallbackRegistrationContext {
|
|
|
45
46
|
|
|
46
47
|
export async function resolvePlatformCallbackRegistrationContext(): Promise<PlatformCallbackRegistrationContext> {
|
|
47
48
|
const platform = getIsPlatform();
|
|
48
|
-
const [storedBaseUrlRaw,
|
|
49
|
+
const [storedBaseUrlRaw, storedAssistantApiKeyRaw, assistantId] =
|
|
49
50
|
await Promise.all([
|
|
50
51
|
getSecureKeyAsync(credentialKey("vellum", "platform_base_url")),
|
|
51
|
-
getSecureKeyAsync(credentialKey("vellum", "platform_assistant_id")),
|
|
52
52
|
getSecureKeyAsync(credentialKey("vellum", "assistant_api_key")),
|
|
53
|
+
resolvePlatformAssistantId(),
|
|
53
54
|
]);
|
|
54
55
|
|
|
55
56
|
const storedBaseUrl = storedBaseUrlRaw?.trim();
|
|
@@ -57,8 +58,6 @@ export async function resolvePlatformCallbackRegistrationContext(): Promise<Plat
|
|
|
57
58
|
/\/+$/,
|
|
58
59
|
"",
|
|
59
60
|
);
|
|
60
|
-
const assistantId =
|
|
61
|
-
getPlatformAssistantId().trim() || storedAssistantIdRaw?.trim() || "";
|
|
62
61
|
const envAssistantCredential = process.env.ASSISTANT_API_KEY?.trim();
|
|
63
62
|
const assistantCredential =
|
|
64
63
|
storedAssistantApiKeyRaw?.trim() || envAssistantCredential || undefined;
|
|
@@ -329,7 +329,6 @@ describe("LiveVoiceMetricsCollector", () => {
|
|
|
329
329
|
endpointHoldCount: 0,
|
|
330
330
|
endpointDecisionMaxLatencyMs: 95,
|
|
331
331
|
});
|
|
332
|
-
expect(completed).not.toHaveProperty("ackSpoken");
|
|
333
332
|
});
|
|
334
333
|
|
|
335
334
|
test("decisions without a source are attributed to the front door", () => {
|
|
@@ -435,7 +434,6 @@ describe("LiveVoiceMetricsCollector", () => {
|
|
|
435
434
|
expect(completed).not.toHaveProperty("endpointHoldCount");
|
|
436
435
|
expect(completed).not.toHaveProperty("endpointDecisionMaxLatencyMs");
|
|
437
436
|
expect(completed).not.toHaveProperty("endpointDecisionSource");
|
|
438
|
-
expect(completed).not.toHaveProperty("ackSpoken");
|
|
439
437
|
expect(completed).not.toHaveProperty("progressUpdatesSpoken");
|
|
440
438
|
|
|
441
439
|
const aggregateFields = getLiveVoiceMetricsAggregateFields(
|
|
@@ -446,7 +444,6 @@ describe("LiveVoiceMetricsCollector", () => {
|
|
|
446
444
|
expect(aggregateFields).not.toHaveProperty("endpointHoldCount");
|
|
447
445
|
expect(aggregateFields).not.toHaveProperty("endpointDecisionMaxLatencyMs");
|
|
448
446
|
expect(aggregateFields).not.toHaveProperty("endpointDecisionSource");
|
|
449
|
-
expect(aggregateFields).not.toHaveProperty("ackSpoken");
|
|
450
447
|
expect(aggregateFields).not.toHaveProperty("progressUpdatesSpoken");
|
|
451
448
|
});
|
|
452
449
|
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import type {
|
|
4
|
+
VoiceProgressNarrator,
|
|
5
|
+
VoiceProgressTextInput,
|
|
6
|
+
} from "../../calls/progress-narration.js";
|
|
7
|
+
import {
|
|
8
|
+
pickProgressPhrase,
|
|
9
|
+
PROGRESS_FALLBACK_PHRASES,
|
|
10
|
+
PROGRESS_FALLBACK_PHRASES_BY_LANGUAGE,
|
|
11
|
+
} from "../../calls/progress-phrases.js";
|
|
3
12
|
import { sanitizeForTts } from "../../calls/tts-text-sanitizer.js";
|
|
4
13
|
import type {
|
|
5
14
|
VoiceTurnCallbacks,
|
|
@@ -7,9 +16,9 @@ import type {
|
|
|
7
16
|
} from "../../calls/voice-session-bridge.js";
|
|
8
17
|
import { loadRawConfig, saveRawConfig } from "../../config/loader.js";
|
|
9
18
|
import type {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} from "../../config/schemas/
|
|
19
|
+
VoiceFrontModelConfig,
|
|
20
|
+
VoiceProgressConfig,
|
|
21
|
+
} from "../../config/schemas/voice.js";
|
|
13
22
|
import type {
|
|
14
23
|
StreamingTranscriber,
|
|
15
24
|
SttStreamServerEvent,
|
|
@@ -21,15 +30,6 @@ import {
|
|
|
21
30
|
} from "../live-voice-session.js";
|
|
22
31
|
import type { LiveVoiceSessionFactoryContext } from "../live-voice-session-manager.js";
|
|
23
32
|
import type { LiveVoiceTtsOptions } from "../live-voice-tts.js";
|
|
24
|
-
import type {
|
|
25
|
-
VoiceProgressNarrator,
|
|
26
|
-
VoiceProgressTextInput,
|
|
27
|
-
} from "../progress-narration.js";
|
|
28
|
-
import {
|
|
29
|
-
pickProgressPhrase,
|
|
30
|
-
PROGRESS_FALLBACK_PHRASES,
|
|
31
|
-
PROGRESS_FALLBACK_PHRASES_BY_LANGUAGE,
|
|
32
|
-
} from "../progress-phrases.js";
|
|
33
33
|
import {
|
|
34
34
|
createLiveVoiceServerFrameSequencer,
|
|
35
35
|
type LiveVoiceClientStartFrame,
|
|
@@ -142,8 +142,8 @@ function makeProgressNarrator(
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
function progressConfig(
|
|
145
|
-
overrides: Partial<
|
|
146
|
-
): Partial<
|
|
145
|
+
overrides: Partial<VoiceProgressConfig> = {},
|
|
146
|
+
): Partial<VoiceFrontModelConfig> {
|
|
147
147
|
const idleIntervalMs = overrides.idleIntervalMs ?? 60_000;
|
|
148
148
|
return {
|
|
149
149
|
progress: {
|
|
@@ -163,7 +163,7 @@ function progressConfig(
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
function createProgressHarness(options: {
|
|
166
|
-
frontModelConfig: Partial<
|
|
166
|
+
frontModelConfig: Partial<VoiceFrontModelConfig>;
|
|
167
167
|
progressNarrator: VoiceProgressNarrator;
|
|
168
168
|
emitMetrics?: boolean;
|
|
169
169
|
gateTtsText?: (text: string) => Promise<void> | null;
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
loadRawConfig,
|
|
13
13
|
saveRawConfig,
|
|
14
14
|
} from "../../config/loader.js";
|
|
15
|
-
import type {
|
|
15
|
+
import type { VoiceFrontModelConfig } from "../../config/schemas/voice.js";
|
|
16
16
|
import type {
|
|
17
17
|
StreamingTranscriber,
|
|
18
18
|
SttStreamServerEvent,
|
|
@@ -183,7 +183,7 @@ function createHarness(options: {
|
|
|
183
183
|
echoBargeInMargin?: number;
|
|
184
184
|
echoEmaHalfLifeMs?: number;
|
|
185
185
|
echoDrainSlackMs?: number;
|
|
186
|
-
frontModelConfig?: Partial<
|
|
186
|
+
frontModelConfig?: Partial<VoiceFrontModelConfig>;
|
|
187
187
|
emitMetrics?: boolean;
|
|
188
188
|
metricsClock?: () => number;
|
|
189
189
|
// Return a promise to hold a frame's transport write open (a backed-up
|
|
@@ -92,36 +92,17 @@ mock.module("../live-voice-credential-preflight.js", () => ({
|
|
|
92
92
|
: realPreflightModule.resolveLiveVoiceCredentialReadiness(),
|
|
93
93
|
}));
|
|
94
94
|
|
|
95
|
-
import {
|
|
96
|
-
|
|
95
|
+
import {
|
|
96
|
+
mintActorToken,
|
|
97
|
+
mintGatewayToken,
|
|
98
|
+
requireHttpAuth,
|
|
99
|
+
upgradeHeaders,
|
|
100
|
+
waitForClose,
|
|
101
|
+
} from "../../runtime/__tests__/runtime-ws-test-utils.js";
|
|
97
102
|
import { RuntimeHttpServer } from "../../runtime/http-server.js";
|
|
98
103
|
|
|
99
104
|
type JsonFrame = Record<string, unknown>;
|
|
100
105
|
|
|
101
|
-
const savedAuthEnv = {
|
|
102
|
-
DISABLE_HTTP_AUTH: process.env.DISABLE_HTTP_AUTH,
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
function mintGatewayToken(): string {
|
|
106
|
-
return mintToken({
|
|
107
|
-
aud: "vellum-daemon",
|
|
108
|
-
sub: "svc:gateway:self",
|
|
109
|
-
scope_profile: "gateway_ingress_v1",
|
|
110
|
-
policy_epoch: CURRENT_POLICY_EPOCH,
|
|
111
|
-
ttlSeconds: 3600,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function mintActorToken(): string {
|
|
116
|
-
return mintToken({
|
|
117
|
-
aud: "vellum-daemon",
|
|
118
|
-
sub: "actor:self:user-123",
|
|
119
|
-
scope_profile: "actor_client_v1",
|
|
120
|
-
policy_epoch: CURRENT_POLICY_EPOCH,
|
|
121
|
-
ttlSeconds: 3600,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
106
|
function startFrame(conversationId = "conversation-123"): string {
|
|
126
107
|
return JSON.stringify({
|
|
127
108
|
type: "start",
|
|
@@ -161,33 +142,6 @@ async function waitForOpen(ws: WebSocket, timeoutMs = 2000): Promise<void> {
|
|
|
161
142
|
});
|
|
162
143
|
}
|
|
163
144
|
|
|
164
|
-
async function waitForClose(ws: WebSocket, timeoutMs = 2000): Promise<void> {
|
|
165
|
-
if (ws.readyState === WebSocket.CLOSED) {
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
await new Promise<void>((resolve, reject) => {
|
|
169
|
-
const timer = setTimeout(() => {
|
|
170
|
-
cleanup();
|
|
171
|
-
reject(new Error("Timed out waiting for WebSocket close"));
|
|
172
|
-
}, timeoutMs);
|
|
173
|
-
const cleanup = () => {
|
|
174
|
-
clearTimeout(timer);
|
|
175
|
-
ws.removeEventListener("close", onClose);
|
|
176
|
-
ws.removeEventListener("error", onError);
|
|
177
|
-
};
|
|
178
|
-
const onClose = () => {
|
|
179
|
-
cleanup();
|
|
180
|
-
resolve();
|
|
181
|
-
};
|
|
182
|
-
const onError = () => {
|
|
183
|
-
cleanup();
|
|
184
|
-
reject(new Error("WebSocket close failed"));
|
|
185
|
-
};
|
|
186
|
-
ws.addEventListener("close", onClose);
|
|
187
|
-
ws.addEventListener("error", onError);
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
|
|
191
145
|
async function waitForJsonFrame(
|
|
192
146
|
ws: WebSocket,
|
|
193
147
|
timeoutMs = 2000,
|
|
@@ -241,6 +195,7 @@ describe("RuntimeHttpServer live voice WebSocket shell", () => {
|
|
|
241
195
|
let baseUrl: string;
|
|
242
196
|
let wsBaseUrl: string;
|
|
243
197
|
let clients: WebSocket[];
|
|
198
|
+
let restoreAuthEnv: () => void;
|
|
244
199
|
|
|
245
200
|
beforeAll(() => {
|
|
246
201
|
shellMocksActive = true;
|
|
@@ -251,7 +206,7 @@ describe("RuntimeHttpServer live voice WebSocket shell", () => {
|
|
|
251
206
|
});
|
|
252
207
|
|
|
253
208
|
beforeEach(async () => {
|
|
254
|
-
|
|
209
|
+
restoreAuthEnv = requireHttpAuth();
|
|
255
210
|
resolveStreamingTranscriberImpl = async () => createResolvedTranscriber();
|
|
256
211
|
resolveStreamingTranscriberMock.mockClear();
|
|
257
212
|
resolvedTranscribers.length = 0;
|
|
@@ -268,11 +223,7 @@ describe("RuntimeHttpServer live voice WebSocket shell", () => {
|
|
|
268
223
|
closeClient(client);
|
|
269
224
|
}
|
|
270
225
|
await server.stop();
|
|
271
|
-
|
|
272
|
-
delete process.env.DISABLE_HTTP_AUTH;
|
|
273
|
-
} else {
|
|
274
|
-
process.env.DISABLE_HTTP_AUTH = savedAuthEnv.DISABLE_HTTP_AUTH;
|
|
275
|
-
}
|
|
226
|
+
restoreAuthEnv();
|
|
276
227
|
});
|
|
277
228
|
|
|
278
229
|
function openLiveVoiceClient(token = mintGatewayToken()): WebSocket {
|
|
@@ -284,21 +235,14 @@ describe("RuntimeHttpServer live voice WebSocket shell", () => {
|
|
|
284
235
|
}
|
|
285
236
|
|
|
286
237
|
test("rejects unauthorized upgrades before creating a WebSocket", async () => {
|
|
287
|
-
const baseHeaders = {
|
|
288
|
-
Upgrade: "websocket",
|
|
289
|
-
Connection: "Upgrade",
|
|
290
|
-
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
|
|
291
|
-
"Sec-WebSocket-Version": "13",
|
|
292
|
-
};
|
|
293
|
-
|
|
294
238
|
const missingToken = await fetch(`${baseUrl}/v1/live-voice`, {
|
|
295
|
-
headers:
|
|
239
|
+
headers: upgradeHeaders,
|
|
296
240
|
});
|
|
297
241
|
expect(missingToken.status).toBe(401);
|
|
298
242
|
|
|
299
243
|
const actorToken = await fetch(
|
|
300
244
|
`${baseUrl}/v1/live-voice?token=${mintActorToken()}`,
|
|
301
|
-
{ headers:
|
|
245
|
+
{ headers: upgradeHeaders },
|
|
302
246
|
);
|
|
303
247
|
expect(actorToken.status).toBe(401);
|
|
304
248
|
|
|
@@ -306,7 +250,7 @@ describe("RuntimeHttpServer live voice WebSocket shell", () => {
|
|
|
306
250
|
`${baseUrl}/v1/live-voice?token=${mintGatewayToken()}`,
|
|
307
251
|
{
|
|
308
252
|
headers: {
|
|
309
|
-
...
|
|
253
|
+
...upgradeHeaders,
|
|
310
254
|
Origin: "https://external.example.com",
|
|
311
255
|
},
|
|
312
256
|
},
|