@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
|
@@ -3,28 +3,9 @@ import { describe, expect, test } from "bun:test";
|
|
|
3
3
|
import {
|
|
4
4
|
LiveVoiceConfigSchema,
|
|
5
5
|
LiveVoiceFluxConfigSchema,
|
|
6
|
-
LiveVoiceFrontModelConfigSchema,
|
|
7
6
|
LiveVoiceVadConfigSchema,
|
|
8
|
-
VALID_LIVE_VOICE_MODES,
|
|
9
7
|
} from "../live-voice.js";
|
|
10
8
|
|
|
11
|
-
const PROGRESS_DEFAULTS = {
|
|
12
|
-
enabled: true,
|
|
13
|
-
opsThreshold: 3,
|
|
14
|
-
idleIntervalMs: 5_000,
|
|
15
|
-
maxSilenceMs: 35_000,
|
|
16
|
-
longOpMs: 15_000,
|
|
17
|
-
minGapMs: 6_000,
|
|
18
|
-
generationTimeoutMs: 1_500,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const FRONT_MODEL_DEFAULTS = {
|
|
22
|
-
endpointDecisionTimeoutMs: 1200,
|
|
23
|
-
endpointExtensionMs: 1500,
|
|
24
|
-
endpointMaxExtensions: 2,
|
|
25
|
-
progress: PROGRESS_DEFAULTS,
|
|
26
|
-
};
|
|
27
|
-
|
|
28
9
|
// `eagerEotThreshold` is deliberately absent: it has no default, and leaving it
|
|
29
10
|
// unset is what keeps Deepgram from emitting speculative turn events. `model`
|
|
30
11
|
// is absent for a related reason: unset means the spoken language selects the
|
|
@@ -130,117 +111,6 @@ describe("LiveVoiceVadConfigSchema", () => {
|
|
|
130
111
|
});
|
|
131
112
|
});
|
|
132
113
|
|
|
133
|
-
describe("LiveVoiceFrontModelConfigSchema", () => {
|
|
134
|
-
test("empty object parses to defaults", () => {
|
|
135
|
-
const parsed = LiveVoiceFrontModelConfigSchema.parse({});
|
|
136
|
-
expect(parsed).toEqual(FRONT_MODEL_DEFAULTS);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
test("accepts overrides", () => {
|
|
140
|
-
const parsed = LiveVoiceFrontModelConfigSchema.parse({
|
|
141
|
-
endpointDecisionTimeoutMs: 400,
|
|
142
|
-
endpointMaxExtensions: 0,
|
|
143
|
-
});
|
|
144
|
-
expect(parsed.endpointDecisionTimeoutMs).toBe(400);
|
|
145
|
-
expect(parsed.endpointMaxExtensions).toBe(0);
|
|
146
|
-
// Unspecified fields still get defaults
|
|
147
|
-
expect(parsed.endpointExtensionMs).toBe(1500);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
test("strips retired generated-ack settings", () => {
|
|
151
|
-
const parsed = LiveVoiceFrontModelConfigSchema.parse({
|
|
152
|
-
ackFirstDeltaTimeoutMs: 2500,
|
|
153
|
-
ackGenerationTimeoutMs: 600,
|
|
154
|
-
});
|
|
155
|
-
expect(parsed).not.toHaveProperty("ackFirstDeltaTimeoutMs");
|
|
156
|
-
expect(parsed).not.toHaveProperty("ackGenerationTimeoutMs");
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
test("rejects non-positive endpointDecisionTimeoutMs", () => {
|
|
160
|
-
const result = LiveVoiceFrontModelConfigSchema.safeParse({
|
|
161
|
-
endpointDecisionTimeoutMs: 0,
|
|
162
|
-
});
|
|
163
|
-
expect(result.success).toBe(false);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
test("rejects negative endpointMaxExtensions", () => {
|
|
167
|
-
const result = LiveVoiceFrontModelConfigSchema.safeParse({
|
|
168
|
-
endpointMaxExtensions: -1,
|
|
169
|
-
});
|
|
170
|
-
expect(result.success).toBe(false);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test("absent progress namespace parses to full progress defaults", () => {
|
|
174
|
-
const parsed = LiveVoiceFrontModelConfigSchema.parse({});
|
|
175
|
-
expect(parsed.progress).toEqual(PROGRESS_DEFAULTS);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
test("partial progress overrides merge with defaults", () => {
|
|
179
|
-
const parsed = LiveVoiceFrontModelConfigSchema.parse({
|
|
180
|
-
progress: { enabled: false, opsThreshold: 5 },
|
|
181
|
-
});
|
|
182
|
-
expect(parsed.progress.enabled).toBe(false);
|
|
183
|
-
expect(parsed.progress.opsThreshold).toBe(5);
|
|
184
|
-
// Unspecified progress fields still get defaults
|
|
185
|
-
expect(parsed.progress.idleIntervalMs).toBe(5_000);
|
|
186
|
-
expect(parsed.progress.maxSilenceMs).toBe(35_000);
|
|
187
|
-
expect(parsed.progress.longOpMs).toBe(15_000);
|
|
188
|
-
expect(parsed.progress.minGapMs).toBe(6_000);
|
|
189
|
-
expect(parsed.progress.generationTimeoutMs).toBe(1_500);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
test("a stale maxPerTurn key is stripped, not rejected", () => {
|
|
193
|
-
// The cap was removed; configs written while it existed must still parse.
|
|
194
|
-
const parsed = LiveVoiceFrontModelConfigSchema.parse({
|
|
195
|
-
progress: { maxPerTurn: 3 },
|
|
196
|
-
});
|
|
197
|
-
expect("maxPerTurn" in parsed.progress).toBe(false);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
test("rejects non-positive progress.opsThreshold", () => {
|
|
201
|
-
const result = LiveVoiceFrontModelConfigSchema.safeParse({
|
|
202
|
-
progress: { opsThreshold: 0 },
|
|
203
|
-
});
|
|
204
|
-
expect(result.success).toBe(false);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
test("rejects a heartbeat ceiling shorter than the idle tick interval", () => {
|
|
208
|
-
// The heartbeat is only checked on the idle tick, so a shorter ceiling
|
|
209
|
-
// could be overshot by a full interval.
|
|
210
|
-
const result = LiveVoiceFrontModelConfigSchema.safeParse({
|
|
211
|
-
progress: { idleIntervalMs: 60_000, maxSilenceMs: 10_000 },
|
|
212
|
-
});
|
|
213
|
-
expect(result.success).toBe(false);
|
|
214
|
-
if (!result.success) {
|
|
215
|
-
const msgs = result.error.issues.map((issue) => issue.message);
|
|
216
|
-
expect(
|
|
217
|
-
msgs.some((msg) =>
|
|
218
|
-
msg.includes("liveVoice.frontModel.progress.maxSilenceMs"),
|
|
219
|
-
),
|
|
220
|
-
).toBe(true);
|
|
221
|
-
}
|
|
222
|
-
// The floor itself is allowed: the tick is exactly on the ceiling.
|
|
223
|
-
expect(
|
|
224
|
-
LiveVoiceFrontModelConfigSchema.safeParse({
|
|
225
|
-
progress: { idleIntervalMs: 60_000, maxSilenceMs: 60_000 },
|
|
226
|
-
}).success,
|
|
227
|
-
).toBe(true);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
test("rejects a non-boolean progress.enabled", () => {
|
|
231
|
-
const result = LiveVoiceFrontModelConfigSchema.safeParse({
|
|
232
|
-
progress: { enabled: "yes" },
|
|
233
|
-
});
|
|
234
|
-
expect(result.success).toBe(false);
|
|
235
|
-
if (!result.success) {
|
|
236
|
-
const msgs = result.error.issues.map((i) => i.message);
|
|
237
|
-
expect(
|
|
238
|
-
msgs.some((m) => m.includes("liveVoice.frontModel.progress.enabled")),
|
|
239
|
-
).toBe(true);
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
|
|
244
114
|
describe("LiveVoiceFluxConfigSchema", () => {
|
|
245
115
|
test("empty object parses to defaults, with turn-end on", () => {
|
|
246
116
|
expect(LiveVoiceFluxConfigSchema.parse({})).toEqual(FLUX_DEFAULTS);
|
|
@@ -326,7 +196,6 @@ describe("LiveVoiceConfigSchema", () => {
|
|
|
326
196
|
test("empty object parses to defaults", () => {
|
|
327
197
|
const parsed = LiveVoiceConfigSchema.parse({});
|
|
328
198
|
expect(parsed).toEqual({
|
|
329
|
-
mode: "open-mic",
|
|
330
199
|
vad: {
|
|
331
200
|
speechEnergyThreshold: 800,
|
|
332
201
|
noiseFloorMargin: 3,
|
|
@@ -337,11 +206,9 @@ describe("LiveVoiceConfigSchema", () => {
|
|
|
337
206
|
echoEmaHalfLifeMs: 400,
|
|
338
207
|
echoDrainSlackMs: 300,
|
|
339
208
|
},
|
|
340
|
-
frontModel: FRONT_MODEL_DEFAULTS,
|
|
341
209
|
// Off by default: Flux turn detection is opt-in, so the front-door hold
|
|
342
210
|
// verdict keeps committing turns until it is enabled.
|
|
343
211
|
flux: FLUX_DEFAULTS,
|
|
344
|
-
maxSessionDurationSeconds: 1800,
|
|
345
212
|
// Off by default: voice turns carry only their transcript, no audio
|
|
346
213
|
// artifacts on the conversation messages (JARVIS-1283).
|
|
347
214
|
archiveAudio: false,
|
|
@@ -365,43 +232,26 @@ describe("LiveVoiceConfigSchema", () => {
|
|
|
365
232
|
|
|
366
233
|
test("accepts overrides", () => {
|
|
367
234
|
const parsed = LiveVoiceConfigSchema.parse({
|
|
368
|
-
mode: "ptt",
|
|
369
235
|
vad: { silenceThresholdMs: 900 },
|
|
370
|
-
frontModel: { endpointDecisionTimeoutMs: 300 },
|
|
371
236
|
flux: { turnEnd: { enabled: true } },
|
|
372
|
-
maxSessionDurationSeconds: 600,
|
|
373
237
|
});
|
|
374
|
-
expect(parsed.mode).toBe("ptt");
|
|
375
238
|
expect(parsed.vad.silenceThresholdMs).toBe(900);
|
|
376
239
|
// Unspecified vad fields still get defaults
|
|
377
240
|
expect(parsed.vad.speechEnergyThreshold).toBe(800);
|
|
378
241
|
expect(parsed.vad.maxTurnDurationMs).toBe(30_000);
|
|
379
|
-
// Partial frontModel overrides merge with defaults
|
|
380
|
-
expect(parsed.frontModel.endpointDecisionTimeoutMs).toBe(300);
|
|
381
|
-
expect(parsed.frontModel.endpointExtensionMs).toBe(1500);
|
|
382
242
|
// Partial flux overrides merge with defaults
|
|
383
243
|
expect(parsed.flux.turnEnd.enabled).toBe(true);
|
|
384
244
|
expect(parsed.flux.eotThreshold).toBe(0.7);
|
|
385
|
-
expect(parsed.maxSessionDurationSeconds).toBe(600);
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
test("rejects invalid mode", () => {
|
|
389
|
-
const result = LiveVoiceConfigSchema.safeParse({ mode: "always-on" });
|
|
390
|
-
expect(result.success).toBe(false);
|
|
391
|
-
if (!result.success) {
|
|
392
|
-
const msgs = result.error.issues.map((i) => i.message);
|
|
393
|
-
expect(msgs.some((m) => m.includes("liveVoice.mode"))).toBe(true);
|
|
394
|
-
}
|
|
395
245
|
});
|
|
396
246
|
|
|
397
|
-
test("
|
|
398
|
-
|
|
399
|
-
|
|
247
|
+
test("persisted configs carrying retired keys keep parsing", () => {
|
|
248
|
+
// Zod strips unrecognized keys, so a persisted config that still carries
|
|
249
|
+
// `mode` or `maxSessionDurationSeconds` parses.
|
|
250
|
+
const parsed = LiveVoiceConfigSchema.parse({
|
|
251
|
+
mode: "ptt",
|
|
252
|
+
maxSessionDurationSeconds: 600,
|
|
400
253
|
});
|
|
401
|
-
expect(
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
test("VALID_LIVE_VOICE_MODES lists ptt and open-mic", () => {
|
|
405
|
-
expect(VALID_LIVE_VOICE_MODES).toEqual(["ptt", "open-mic"]);
|
|
254
|
+
expect(parsed).not.toHaveProperty("mode");
|
|
255
|
+
expect(parsed).not.toHaveProperty("maxSessionDurationSeconds");
|
|
406
256
|
});
|
|
407
257
|
});
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { VoiceConfigSchema, VoiceFrontModelConfigSchema } from "../voice.js";
|
|
4
|
+
|
|
5
|
+
const PROGRESS_DEFAULTS = {
|
|
6
|
+
enabled: true,
|
|
7
|
+
opsThreshold: 3,
|
|
8
|
+
idleIntervalMs: 5_000,
|
|
9
|
+
maxSilenceMs: 35_000,
|
|
10
|
+
longOpMs: 15_000,
|
|
11
|
+
minGapMs: 6_000,
|
|
12
|
+
generationTimeoutMs: 1_500,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const FRONT_MODEL_DEFAULTS = {
|
|
16
|
+
endpointDecisionTimeoutMs: 1200,
|
|
17
|
+
endpointExtensionMs: 1500,
|
|
18
|
+
endpointMaxExtensions: 2,
|
|
19
|
+
progress: PROGRESS_DEFAULTS,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe("VoiceFrontModelConfigSchema", () => {
|
|
23
|
+
test("empty object parses to defaults", () => {
|
|
24
|
+
const parsed = VoiceFrontModelConfigSchema.parse({});
|
|
25
|
+
expect(parsed).toEqual(FRONT_MODEL_DEFAULTS);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("accepts overrides", () => {
|
|
29
|
+
const parsed = VoiceFrontModelConfigSchema.parse({
|
|
30
|
+
endpointDecisionTimeoutMs: 400,
|
|
31
|
+
endpointMaxExtensions: 0,
|
|
32
|
+
});
|
|
33
|
+
expect(parsed.endpointDecisionTimeoutMs).toBe(400);
|
|
34
|
+
expect(parsed.endpointMaxExtensions).toBe(0);
|
|
35
|
+
// Unspecified fields still get defaults
|
|
36
|
+
expect(parsed.endpointExtensionMs).toBe(1500);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("strips retired generated-ack settings", () => {
|
|
40
|
+
const parsed = VoiceFrontModelConfigSchema.parse({
|
|
41
|
+
ackFirstDeltaTimeoutMs: 2500,
|
|
42
|
+
ackGenerationTimeoutMs: 600,
|
|
43
|
+
});
|
|
44
|
+
expect(parsed).not.toHaveProperty("ackFirstDeltaTimeoutMs");
|
|
45
|
+
expect(parsed).not.toHaveProperty("ackGenerationTimeoutMs");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("rejects non-positive endpointDecisionTimeoutMs", () => {
|
|
49
|
+
const result = VoiceFrontModelConfigSchema.safeParse({
|
|
50
|
+
endpointDecisionTimeoutMs: 0,
|
|
51
|
+
});
|
|
52
|
+
expect(result.success).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("rejects negative endpointMaxExtensions", () => {
|
|
56
|
+
const result = VoiceFrontModelConfigSchema.safeParse({
|
|
57
|
+
endpointMaxExtensions: -1,
|
|
58
|
+
});
|
|
59
|
+
expect(result.success).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("absent progress namespace parses to full progress defaults", () => {
|
|
63
|
+
const parsed = VoiceFrontModelConfigSchema.parse({});
|
|
64
|
+
expect(parsed.progress).toEqual(PROGRESS_DEFAULTS);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("partial progress overrides merge with defaults", () => {
|
|
68
|
+
const parsed = VoiceFrontModelConfigSchema.parse({
|
|
69
|
+
progress: { enabled: false, opsThreshold: 5 },
|
|
70
|
+
});
|
|
71
|
+
expect(parsed.progress.enabled).toBe(false);
|
|
72
|
+
expect(parsed.progress.opsThreshold).toBe(5);
|
|
73
|
+
// Unspecified progress fields still get defaults
|
|
74
|
+
expect(parsed.progress.idleIntervalMs).toBe(5_000);
|
|
75
|
+
expect(parsed.progress.maxSilenceMs).toBe(35_000);
|
|
76
|
+
expect(parsed.progress.longOpMs).toBe(15_000);
|
|
77
|
+
expect(parsed.progress.minGapMs).toBe(6_000);
|
|
78
|
+
expect(parsed.progress.generationTimeoutMs).toBe(1_500);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("a stale maxPerTurn key is stripped, not rejected", () => {
|
|
82
|
+
// The cap was removed; configs written while it existed must still parse.
|
|
83
|
+
const parsed = VoiceFrontModelConfigSchema.parse({
|
|
84
|
+
progress: { maxPerTurn: 3 },
|
|
85
|
+
});
|
|
86
|
+
expect("maxPerTurn" in parsed.progress).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("rejects non-positive progress.opsThreshold", () => {
|
|
90
|
+
const result = VoiceFrontModelConfigSchema.safeParse({
|
|
91
|
+
progress: { opsThreshold: 0 },
|
|
92
|
+
});
|
|
93
|
+
expect(result.success).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("rejects a heartbeat ceiling shorter than the idle tick interval", () => {
|
|
97
|
+
// The heartbeat is only checked on the idle tick, so a shorter ceiling
|
|
98
|
+
// could be overshot by a full interval.
|
|
99
|
+
const result = VoiceFrontModelConfigSchema.safeParse({
|
|
100
|
+
progress: { idleIntervalMs: 60_000, maxSilenceMs: 10_000 },
|
|
101
|
+
});
|
|
102
|
+
expect(result.success).toBe(false);
|
|
103
|
+
if (!result.success) {
|
|
104
|
+
const msgs = result.error.issues.map((issue) => issue.message);
|
|
105
|
+
expect(
|
|
106
|
+
msgs.some((msg) =>
|
|
107
|
+
msg.includes("voice.frontModel.progress.maxSilenceMs"),
|
|
108
|
+
),
|
|
109
|
+
).toBe(true);
|
|
110
|
+
}
|
|
111
|
+
// The floor itself is allowed: the tick is exactly on the ceiling.
|
|
112
|
+
expect(
|
|
113
|
+
VoiceFrontModelConfigSchema.safeParse({
|
|
114
|
+
progress: { idleIntervalMs: 60_000, maxSilenceMs: 60_000 },
|
|
115
|
+
}).success,
|
|
116
|
+
).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("rejects a non-boolean progress.enabled", () => {
|
|
120
|
+
const result = VoiceFrontModelConfigSchema.safeParse({
|
|
121
|
+
progress: { enabled: "yes" },
|
|
122
|
+
});
|
|
123
|
+
expect(result.success).toBe(false);
|
|
124
|
+
if (!result.success) {
|
|
125
|
+
const msgs = result.error.issues.map((i) => i.message);
|
|
126
|
+
expect(
|
|
127
|
+
msgs.some((m) => m.includes("voice.frontModel.progress.enabled")),
|
|
128
|
+
).toBe(true);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe("VoiceConfigSchema", () => {
|
|
134
|
+
test("empty object parses to front-model defaults", () => {
|
|
135
|
+
expect(VoiceConfigSchema.parse({})).toEqual({
|
|
136
|
+
frontModel: FRONT_MODEL_DEFAULTS,
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("partial frontModel overrides merge with defaults", () => {
|
|
141
|
+
const parsed = VoiceConfigSchema.parse({
|
|
142
|
+
frontModel: { endpointDecisionTimeoutMs: 300 },
|
|
143
|
+
});
|
|
144
|
+
expect(parsed.frontModel.endpointDecisionTimeoutMs).toBe(300);
|
|
145
|
+
expect(parsed.frontModel.endpointExtensionMs).toBe(1500);
|
|
146
|
+
expect(parsed.frontModel.progress).toEqual(PROGRESS_DEFAULTS);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -26,32 +26,8 @@ const CallsDisclosureConfigSchema = z
|
|
|
26
26
|
"Controls whether and how the assistant discloses its nature at the start of a phone call",
|
|
27
27
|
);
|
|
28
28
|
|
|
29
|
-
const CallsSafetyConfigSchema = z
|
|
30
|
-
.object({
|
|
31
|
-
denyCategories: z
|
|
32
|
-
.array(
|
|
33
|
-
z.string({
|
|
34
|
-
error: "calls.safety.denyCategories values must be strings",
|
|
35
|
-
}),
|
|
36
|
-
)
|
|
37
|
-
.default([])
|
|
38
|
-
.describe(
|
|
39
|
-
"Categories of calls that should be denied (e.g. for safety or compliance reasons)",
|
|
40
|
-
),
|
|
41
|
-
})
|
|
42
|
-
.describe("Safety guardrails for phone calls");
|
|
43
|
-
|
|
44
29
|
const CallsVoiceConfigSchema = z
|
|
45
30
|
.object({
|
|
46
|
-
interruptSensitivity: z
|
|
47
|
-
.enum(["low", "medium", "high"], {
|
|
48
|
-
error:
|
|
49
|
-
"calls.voice.interruptSensitivity must be one of: low, medium, high",
|
|
50
|
-
})
|
|
51
|
-
.default("low")
|
|
52
|
-
.describe(
|
|
53
|
-
"How aggressively the STT provider detects the start of caller speech — low reduces false interrupts from background noise",
|
|
54
|
-
),
|
|
55
31
|
telephonyStreaming: z
|
|
56
32
|
.boolean({
|
|
57
33
|
error: "calls.voice.telephonyStreaming must be a boolean",
|
|
@@ -227,7 +203,6 @@ export const CallsConfigSchema = z
|
|
|
227
203
|
disclosure: CallsDisclosureConfigSchema.default(
|
|
228
204
|
CallsDisclosureConfigSchema.parse({}),
|
|
229
205
|
),
|
|
230
|
-
safety: CallsSafetyConfigSchema.default(CallsSafetyConfigSchema.parse({})),
|
|
231
206
|
voice: CallsVoiceConfigSchema.default(CallsVoiceConfigSchema.parse({})),
|
|
232
207
|
callerIdentity: CallerIdentityConfigSchema.default(
|
|
233
208
|
CallerIdentityConfigSchema.parse({}),
|
|
@@ -236,6 +211,4 @@ export const CallsConfigSchema = z
|
|
|
236
211
|
CallsVerificationConfigSchema.parse({}),
|
|
237
212
|
),
|
|
238
213
|
})
|
|
239
|
-
.describe(
|
|
240
|
-
"Phone call configuration — controls telephony, voice, safety, and call behavior",
|
|
241
|
-
);
|
|
214
|
+
.describe("Phone call configuration: telephony, voice, and call behavior");
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
export const VALID_LIVE_VOICE_MODES = ["ptt", "open-mic"] as const;
|
|
4
|
-
|
|
5
3
|
export const LiveVoiceVadConfigSchema = z
|
|
6
4
|
.object({
|
|
7
5
|
speechEnergyThreshold: z
|
|
@@ -79,149 +77,6 @@ export const LiveVoiceVadConfigSchema = z
|
|
|
79
77
|
"Voice-activity-detection tuning for live voice sessions (open-mic turn segmentation)",
|
|
80
78
|
);
|
|
81
79
|
|
|
82
|
-
export const LiveVoiceProgressConfigSchema = z
|
|
83
|
-
.object({
|
|
84
|
-
enabled: z
|
|
85
|
-
.boolean({
|
|
86
|
-
error: "liveVoice.frontModel.progress.enabled must be a boolean",
|
|
87
|
-
})
|
|
88
|
-
.default(true)
|
|
89
|
-
.describe(
|
|
90
|
-
"Speak short progress updates during long-running tool-heavy turns; the opt-out for progress narration",
|
|
91
|
-
),
|
|
92
|
-
opsThreshold: z
|
|
93
|
-
.number({
|
|
94
|
-
error: "liveVoice.frontModel.progress.opsThreshold must be a number",
|
|
95
|
-
})
|
|
96
|
-
.int("liveVoice.frontModel.progress.opsThreshold must be an integer")
|
|
97
|
-
.positive(
|
|
98
|
-
"liveVoice.frontModel.progress.opsThreshold must be a positive integer",
|
|
99
|
-
)
|
|
100
|
-
.default(3)
|
|
101
|
-
.describe(
|
|
102
|
-
"Narrate after this many tool operations since the last narration",
|
|
103
|
-
),
|
|
104
|
-
idleIntervalMs: z
|
|
105
|
-
.number({
|
|
106
|
-
error: "liveVoice.frontModel.progress.idleIntervalMs must be a number",
|
|
107
|
-
})
|
|
108
|
-
.int("liveVoice.frontModel.progress.idleIntervalMs must be an integer")
|
|
109
|
-
.positive(
|
|
110
|
-
"liveVoice.frontModel.progress.idleIntervalMs must be a positive integer",
|
|
111
|
-
)
|
|
112
|
-
.default(5_000)
|
|
113
|
-
.describe(
|
|
114
|
-
"How often (ms) a running turn's silence is checked, and so the soonest new tool activity is narrated",
|
|
115
|
-
),
|
|
116
|
-
maxSilenceMs: z
|
|
117
|
-
.number({
|
|
118
|
-
error: "liveVoice.frontModel.progress.maxSilenceMs must be a number",
|
|
119
|
-
})
|
|
120
|
-
.int("liveVoice.frontModel.progress.maxSilenceMs must be an integer")
|
|
121
|
-
.positive(
|
|
122
|
-
"liveVoice.frontModel.progress.maxSilenceMs must be a positive integer",
|
|
123
|
-
)
|
|
124
|
-
.default(35_000)
|
|
125
|
-
.describe(
|
|
126
|
-
"Heartbeat ceiling (ms): narrate after this much unbroken silence even when nothing new has happened. Evaluated on the idle tick, so its resolution is idleIntervalMs and it must be at least that long",
|
|
127
|
-
),
|
|
128
|
-
longOpMs: z
|
|
129
|
-
.number({
|
|
130
|
-
error: "liveVoice.frontModel.progress.longOpMs must be a number",
|
|
131
|
-
})
|
|
132
|
-
.int("liveVoice.frontModel.progress.longOpMs must be an integer")
|
|
133
|
-
.positive(
|
|
134
|
-
"liveVoice.frontModel.progress.longOpMs must be a positive integer",
|
|
135
|
-
)
|
|
136
|
-
.default(15_000)
|
|
137
|
-
.describe(
|
|
138
|
-
"A tool operation that ran at least this long (ms) narrates the moment it completes, without waiting for opsThreshold",
|
|
139
|
-
),
|
|
140
|
-
minGapMs: z
|
|
141
|
-
.number({
|
|
142
|
-
error: "liveVoice.frontModel.progress.minGapMs must be a number",
|
|
143
|
-
})
|
|
144
|
-
.int("liveVoice.frontModel.progress.minGapMs must be an integer")
|
|
145
|
-
.positive(
|
|
146
|
-
"liveVoice.frontModel.progress.minGapMs must be a positive integer",
|
|
147
|
-
)
|
|
148
|
-
.default(6_000)
|
|
149
|
-
.describe(
|
|
150
|
-
"Minimum spacing (ms) from any spoken floor-holder — ack or narration",
|
|
151
|
-
),
|
|
152
|
-
generationTimeoutMs: z
|
|
153
|
-
.number({
|
|
154
|
-
error:
|
|
155
|
-
"liveVoice.frontModel.progress.generationTimeoutMs must be a number",
|
|
156
|
-
})
|
|
157
|
-
.int(
|
|
158
|
-
"liveVoice.frontModel.progress.generationTimeoutMs must be an integer",
|
|
159
|
-
)
|
|
160
|
-
.positive(
|
|
161
|
-
"liveVoice.frontModel.progress.generationTimeoutMs must be a positive integer",
|
|
162
|
-
)
|
|
163
|
-
.default(1_500)
|
|
164
|
-
.describe(
|
|
165
|
-
"Budget (ms) for LLM-generated progress text — not latency-critical: it speaks into dead air",
|
|
166
|
-
),
|
|
167
|
-
})
|
|
168
|
-
// The heartbeat is checked when the idle tick finds the turn silent, so a
|
|
169
|
-
// ceiling shorter than the tick interval would be missed by up to a full
|
|
170
|
-
// interval — a promise the cadence cannot keep. Rejecting the combination
|
|
171
|
-
// beats silently overshooting it.
|
|
172
|
-
.refine((progress) => progress.maxSilenceMs >= progress.idleIntervalMs, {
|
|
173
|
-
error:
|
|
174
|
-
"liveVoice.frontModel.progress.maxSilenceMs must be at least idleIntervalMs — the heartbeat is evaluated on the idle tick",
|
|
175
|
-
})
|
|
176
|
-
.describe(
|
|
177
|
-
"Progress-narration tuning for live voice sessions (spoken updates during long-running turns)",
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
export const LiveVoiceFrontModelConfigSchema = z
|
|
181
|
-
.object({
|
|
182
|
-
endpointDecisionTimeoutMs: z
|
|
183
|
-
.number({
|
|
184
|
-
error:
|
|
185
|
-
"liveVoice.frontModel.endpointDecisionTimeoutMs must be a number",
|
|
186
|
-
})
|
|
187
|
-
.int("liveVoice.frontModel.endpointDecisionTimeoutMs must be an integer")
|
|
188
|
-
.positive(
|
|
189
|
-
"liveVoice.frontModel.endpointDecisionTimeoutMs must be a positive integer",
|
|
190
|
-
)
|
|
191
|
-
.default(1200)
|
|
192
|
-
.describe(
|
|
193
|
-
"Hard budget (ms) for the endpoint decision LLM call. This adds to end-of-turn latency when semantic endpointing is on, so keep it as tight as the decider model's real roundtrip allows — measured Haiku roundtrips through the managed proxy run ~670-1130ms (dev), so tighter budgets turn the feature into a fail-open no-op.",
|
|
194
|
-
),
|
|
195
|
-
endpointExtensionMs: z
|
|
196
|
-
.number({
|
|
197
|
-
error: "liveVoice.frontModel.endpointExtensionMs must be a number",
|
|
198
|
-
})
|
|
199
|
-
.int("liveVoice.frontModel.endpointExtensionMs must be an integer")
|
|
200
|
-
.positive(
|
|
201
|
-
"liveVoice.frontModel.endpointExtensionMs must be a positive integer",
|
|
202
|
-
)
|
|
203
|
-
.default(1500)
|
|
204
|
-
.describe(
|
|
205
|
-
"How long (ms) a 'hold' decision keeps the turn open before turn-end replays",
|
|
206
|
-
),
|
|
207
|
-
endpointMaxExtensions: z
|
|
208
|
-
.number({
|
|
209
|
-
error: "liveVoice.frontModel.endpointMaxExtensions must be a number",
|
|
210
|
-
})
|
|
211
|
-
.int("liveVoice.frontModel.endpointMaxExtensions must be an integer")
|
|
212
|
-
.nonnegative(
|
|
213
|
-
"liveVoice.frontModel.endpointMaxExtensions must be a nonnegative integer",
|
|
214
|
-
)
|
|
215
|
-
.default(2)
|
|
216
|
-
.describe("Cap on consecutive 'hold' extensions per utterance"),
|
|
217
|
-
progress: LiveVoiceProgressConfigSchema.default(
|
|
218
|
-
LiveVoiceProgressConfigSchema.parse({}),
|
|
219
|
-
),
|
|
220
|
-
})
|
|
221
|
-
.describe(
|
|
222
|
-
"Voice front-door endpointing and long-turn progress narration tuning",
|
|
223
|
-
);
|
|
224
|
-
|
|
225
80
|
const LiveVoiceFluxTurnEndConfigSchema = z
|
|
226
81
|
.object({
|
|
227
82
|
enabled: z
|
|
@@ -285,31 +140,10 @@ export const LiveVoiceFluxConfigSchema = z
|
|
|
285
140
|
|
|
286
141
|
export const LiveVoiceConfigSchema = z
|
|
287
142
|
.object({
|
|
288
|
-
mode: z
|
|
289
|
-
.enum(VALID_LIVE_VOICE_MODES, {
|
|
290
|
-
error: `liveVoice.mode must be one of: ${VALID_LIVE_VOICE_MODES.join(", ")}`,
|
|
291
|
-
})
|
|
292
|
-
.default("open-mic")
|
|
293
|
-
.describe(
|
|
294
|
-
"Default microphone mode for live voice sessions — hands-free (open-mic) or push-to-talk (ptt)",
|
|
295
|
-
),
|
|
296
143
|
vad: LiveVoiceVadConfigSchema.default(LiveVoiceVadConfigSchema.parse({})),
|
|
297
|
-
frontModel: LiveVoiceFrontModelConfigSchema.default(
|
|
298
|
-
LiveVoiceFrontModelConfigSchema.parse({}),
|
|
299
|
-
),
|
|
300
144
|
flux: LiveVoiceFluxConfigSchema.default(
|
|
301
145
|
LiveVoiceFluxConfigSchema.parse({}),
|
|
302
146
|
),
|
|
303
|
-
maxSessionDurationSeconds: z
|
|
304
|
-
.number({
|
|
305
|
-
error: "liveVoice.maxSessionDurationSeconds must be a number",
|
|
306
|
-
})
|
|
307
|
-
.int("liveVoice.maxSessionDurationSeconds must be an integer")
|
|
308
|
-
.positive(
|
|
309
|
-
"liveVoice.maxSessionDurationSeconds must be a positive integer",
|
|
310
|
-
)
|
|
311
|
-
.default(1800)
|
|
312
|
-
.describe("Maximum duration of a single live voice session in seconds"),
|
|
313
147
|
archiveAudio: z
|
|
314
148
|
.boolean({ error: "liveVoice.archiveAudio must be a boolean" })
|
|
315
149
|
.default(false)
|
|
@@ -318,15 +152,9 @@ export const LiveVoiceConfigSchema = z
|
|
|
318
152
|
),
|
|
319
153
|
})
|
|
320
154
|
.describe(
|
|
321
|
-
"Live voice (in-app duplex audio) configuration
|
|
155
|
+
"Live voice (in-app duplex audio) configuration: VAD tuning, Flux turn detection, and audio archiving",
|
|
322
156
|
);
|
|
323
157
|
|
|
324
158
|
export type LiveVoiceConfig = z.infer<typeof LiveVoiceConfigSchema>;
|
|
325
159
|
export type LiveVoiceVadConfig = z.infer<typeof LiveVoiceVadConfigSchema>;
|
|
326
|
-
export type LiveVoiceFrontModelConfig = z.infer<
|
|
327
|
-
typeof LiveVoiceFrontModelConfigSchema
|
|
328
|
-
>;
|
|
329
|
-
export type LiveVoiceProgressConfig = z.infer<
|
|
330
|
-
typeof LiveVoiceProgressConfigSchema
|
|
331
|
-
>;
|
|
332
160
|
export type LiveVoiceFluxConfig = z.infer<typeof LiveVoiceFluxConfigSchema>;
|
|
@@ -102,7 +102,7 @@ export type McpServerConfig = z.infer<typeof McpServerConfigSchema>;
|
|
|
102
102
|
export type McpConfig = z.infer<typeof McpConfigSchema>;
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
|
-
* Who declared a server: the workspace `
|
|
105
|
+
* Who declared a server: the workspace `mcp.json`, which the user owns,
|
|
106
106
|
* or a plugin's `mcp.json`, which its author owns.
|
|
107
107
|
*
|
|
108
108
|
* Deliberately not a schema field. It is resolved from where the entry was
|
|
@@ -9,6 +9,16 @@ export const MemoryRetrospectiveConfigSchema = z
|
|
|
9
9
|
"Whether the memory-retrospective background pass runs. When false, no retrospective is enqueued by any trigger (interval, message count, pre-compaction, scheduled sweep), the scheduled sweep job stops being queued, and any row already queued completes as a no-op. The rest of the memory system (extraction, retrieval, embeddings, `<memory>` injection) is unaffected. Use `memory.enabled` to disable memory as a whole.",
|
|
10
10
|
),
|
|
11
11
|
|
|
12
|
+
skillImprovement: z
|
|
13
|
+
.boolean({
|
|
14
|
+
error:
|
|
15
|
+
"memory.retrospective.skillImprovement must be a boolean",
|
|
16
|
+
})
|
|
17
|
+
.default(true)
|
|
18
|
+
.describe(
|
|
19
|
+
"Whether retrospectives may discover, refine, and create managed skills from observed procedures. When false, retrospectives still capture ordinary memories through `remember`, but cannot load skill management, search for similar skills, or scaffold managed skills.",
|
|
20
|
+
),
|
|
21
|
+
|
|
12
22
|
timeThresholdMs: z
|
|
13
23
|
.number({
|
|
14
24
|
error: "memory.retrospective.timeThresholdMs must be a number",
|
|
@@ -140,6 +140,11 @@ const MondayOAuthServiceSchema = BaseServiceSchema.extend({
|
|
|
140
140
|
mode: ServiceModeSchema.default("your-own"),
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
+
export const ShopifyOAuthServiceSchema = BaseServiceSchema.extend({
|
|
144
|
+
mode: ServiceModeSchema.default("your-own"),
|
|
145
|
+
});
|
|
146
|
+
export type ShopifyOAuthService = z.infer<typeof ShopifyOAuthServiceSchema>;
|
|
147
|
+
|
|
143
148
|
export const StripeLinkOAuthServiceSchema = BaseServiceSchema.extend({
|
|
144
149
|
mode: ServiceModeSchema.default("your-own"),
|
|
145
150
|
});
|
|
@@ -214,6 +219,9 @@ export const ServicesSchema = z.object({
|
|
|
214
219
|
"monday-oauth": MondayOAuthServiceSchema.default(
|
|
215
220
|
MondayOAuthServiceSchema.parse({}),
|
|
216
221
|
),
|
|
222
|
+
"shopify-oauth": ShopifyOAuthServiceSchema.default(
|
|
223
|
+
ShopifyOAuthServiceSchema.parse({}),
|
|
224
|
+
),
|
|
217
225
|
"stripe-link-oauth": StripeLinkOAuthServiceSchema.default(
|
|
218
226
|
StripeLinkOAuthServiceSchema.parse({}),
|
|
219
227
|
),
|
|
@@ -32,20 +32,6 @@ const SkillsLoadConfigSchema = z
|
|
|
32
32
|
)
|
|
33
33
|
.default([])
|
|
34
34
|
.describe("Additional directories to search for skill definitions"),
|
|
35
|
-
watch: z
|
|
36
|
-
.boolean({ error: "skills.load.watch must be a boolean" })
|
|
37
|
-
.default(true)
|
|
38
|
-
.describe(
|
|
39
|
-
"Whether to watch skill directories for changes and auto-reload",
|
|
40
|
-
),
|
|
41
|
-
watchDebounceMs: z
|
|
42
|
-
.number({ error: "skills.load.watchDebounceMs must be a number" })
|
|
43
|
-
.int()
|
|
44
|
-
.positive()
|
|
45
|
-
.default(250)
|
|
46
|
-
.describe(
|
|
47
|
-
"Debounce delay in milliseconds for skill file change detection",
|
|
48
|
-
),
|
|
49
35
|
})
|
|
50
36
|
.describe("Controls how skills are discovered and loaded");
|
|
51
37
|
|