@vellumai/assistant 0.12.1 → 0.12.2-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +13 -8
- package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
- package/openapi.yaml +360 -51
- package/package.json +3 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- 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__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- 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-runtime-assembly.test.ts +28 -0
- 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-point-at-budget.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
- 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 +250 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/cu-unified-flow.test.ts +14 -4
- 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__/heartbeat-service.test.ts +56 -40
- 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-cu-proxy.test.ts +596 -0
- 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__/list-messages-camera-frame.test.ts +121 -0
- 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__/oauth-commands-routes.test.ts +168 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +22 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- 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 +14 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/secret-routes-acp-guard.test.ts +59 -1
- 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__/skills.test.ts +9 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +70 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/ui-channel-variants.test.ts +1 -56
- package/src/__tests__/unicode.test.ts +13 -0
- 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__/acp-claude-oauth.test.ts +257 -6
- package/src/acp/__tests__/acp-credentials.test.ts +13 -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__/claude-token-refresh.test.ts +257 -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__/prepare-agent-env.test.ts +60 -1
- 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/acp-claude-oauth.ts +328 -14
- package/src/acp/acp-credentials.ts +19 -0
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +96 -10
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/claude-token-refresh.ts +150 -0
- 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/prepare-agent-env.ts +21 -6
- 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/agent/loop.ts +1 -0
- 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/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -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/avatar/identity-avatar.ts +7 -2
- 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 +120 -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 +56 -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 +155 -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 +92 -72
- package/src/calls/voice-triage-escalate.ts +178 -59
- 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/oauth/status.ts +5 -0
- 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/computer-use/SKILL.md +29 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -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/playbooks/tools/playbook-create.ts +2 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -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 +6 -2
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +33 -13
- package/src/config/loader.ts +2 -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 +18 -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/credential-health/credential-health-service.ts +11 -17
- 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-error.ts +3 -2
- package/src/daemon/conversation-media-retry.ts +2 -1
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-runtime-assembly.ts +20 -7
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +21 -10
- package/src/daemon/conversation-tool-setup.ts +45 -59
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/dictation-profile-store.ts +2 -1
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +6 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-cu-proxy.ts +224 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- 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/reaction-history-render.ts +2 -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 +122 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.ts +37 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +308 -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 +730 -0
- package/src/desktop/desktop-session-manager.ts +850 -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-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- 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-agent-turn.test.ts +121 -0
- package/src/live-voice/__tests__/live-voice-events.test.ts +8 -7
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +94 -15
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/protocol.test.ts +39 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/__tests__/session-controls.test.ts +79 -0
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +297 -699
- package/src/live-voice/protocol.ts +60 -2
- package/src/live-voice/session-controls.ts +111 -0
- 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/AGENTS.md +2 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +149 -0
- package/src/oauth/byo-connection.test.ts +105 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +100 -0
- package/src/oauth/connection-resolver.ts +120 -3
- package/src/oauth/provider-serializer.ts +9 -0
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +314 -27
- 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-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- 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-prompt.test.ts +5 -0
- 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/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- 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/host-utils.ts +10 -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-prompt.ts +1 -1
- 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 +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- 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/__tests__/retry-callsite.test.ts +41 -0
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- 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/__tests__/endpoint-probe.test.ts +42 -3
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +10 -1
- 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-output-token-limit.test.ts +141 -0
- 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 +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/opencode/client.test.ts +73 -4
- package/src/providers/opencode/client.ts +60 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- 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__/host-observe.test.ts +40 -1
- 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/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +16 -5
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +275 -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-claude-auth-routes.ts +11 -3
- 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 +90 -2
- 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/conversation-routes.ts +16 -3
- package/src/runtime/routes/credential-routes.ts +20 -14
- 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 +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- 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/oauth-commands-routes.ts +50 -0
- package/src/runtime/routes/oauth-providers.ts +7 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- 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/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- 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/security/untrusted-content.ts +3 -1
- 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/__tests__/side-effect-abort-guard.test.ts +1 -0
- 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/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.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 +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- 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/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/ui-surface/channel-variants.ts +10 -59
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/types/host-capabilities.ts +5 -3
- 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/util/unicode.ts +15 -1
- package/src/watch/watch-retro.ts +6 -8
- package/src/watch/watch-timeline.ts +2 -2
- package/src/workflows/run-manager.ts +2 -1
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +6 -55
- 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,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bound platform identity (assistant, organization, user).
|
|
3
|
+
*
|
|
4
|
+
* Django maps an assistant API key to these ids via
|
|
5
|
+
* `POST /v1/internal/assistants/validate/`. Process startup rehydrates from
|
|
6
|
+
* that endpoint into the in-memory overrides. Resolution reads those
|
|
7
|
+
* overrides (and `PLATFORM_ORGANIZATION_ID` / `PLATFORM_USER_ID` when set).
|
|
8
|
+
* When the in-memory assistant id is empty, the next resolve retries
|
|
9
|
+
* validate (single-flight, with a cooldown after a failed attempt).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
13
|
+
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
14
|
+
import { getLogger } from "../util/logger.js";
|
|
15
|
+
import {
|
|
16
|
+
getPlatformAssistantId,
|
|
17
|
+
getPlatformBaseUrl,
|
|
18
|
+
getPlatformOrganizationId,
|
|
19
|
+
getPlatformUserId,
|
|
20
|
+
setPlatformAssistantId,
|
|
21
|
+
setPlatformOrganizationId,
|
|
22
|
+
setPlatformUserId,
|
|
23
|
+
} from "./env.js";
|
|
24
|
+
|
|
25
|
+
const log = getLogger("platform-identity");
|
|
26
|
+
|
|
27
|
+
export const PLATFORM_IDENTITY_VALIDATE_PATH =
|
|
28
|
+
"/v1/internal/assistants/validate/";
|
|
29
|
+
|
|
30
|
+
const VALIDATE_TIMEOUT_MS = 5_000;
|
|
31
|
+
const ENSURE_COOLDOWN_MS = 10_000;
|
|
32
|
+
|
|
33
|
+
export type PlatformIdentityIds = {
|
|
34
|
+
assistantId: string;
|
|
35
|
+
organizationId: string;
|
|
36
|
+
userId: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function asNonEmptyString(value: unknown): string | undefined {
|
|
40
|
+
if (typeof value !== "string") {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
const trimmed = value.trim();
|
|
44
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function applyPlatformIdentityIds(ids: PlatformIdentityIds): void {
|
|
48
|
+
if (ids.assistantId) {
|
|
49
|
+
setPlatformAssistantId(ids.assistantId);
|
|
50
|
+
}
|
|
51
|
+
if (ids.organizationId) {
|
|
52
|
+
setPlatformOrganizationId(ids.organizationId);
|
|
53
|
+
}
|
|
54
|
+
if (ids.userId) {
|
|
55
|
+
setPlatformUserId(ids.userId);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Trade an assistant API key for the bound platform ids.
|
|
61
|
+
*
|
|
62
|
+
* Returns null when the platform is unreachable, refuses the key, or the
|
|
63
|
+
* body has no ids. Never throws.
|
|
64
|
+
*/
|
|
65
|
+
export async function fetchPlatformIdentityIds(
|
|
66
|
+
baseUrl: string,
|
|
67
|
+
apiKey: string,
|
|
68
|
+
): Promise<PlatformIdentityIds | null> {
|
|
69
|
+
const url = `${baseUrl.replace(/\/+$/, "")}${PLATFORM_IDENTITY_VALIDATE_PATH}`;
|
|
70
|
+
try {
|
|
71
|
+
const res = await fetch(url, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: {
|
|
74
|
+
Authorization: `Api-Key ${apiKey}`,
|
|
75
|
+
"Content-Type": "application/json",
|
|
76
|
+
},
|
|
77
|
+
body: "{}",
|
|
78
|
+
signal: AbortSignal.timeout(VALIDATE_TIMEOUT_MS),
|
|
79
|
+
});
|
|
80
|
+
if (!res.ok) {
|
|
81
|
+
log.warn(
|
|
82
|
+
{ status: res.status },
|
|
83
|
+
"platform identity validate returned non-2xx",
|
|
84
|
+
);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const body: unknown = await res.json();
|
|
88
|
+
if (!body || typeof body !== "object") {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const obj = body as Record<string, unknown>;
|
|
92
|
+
const assistantId = asNonEmptyString(obj.assistant_id);
|
|
93
|
+
const organizationId = asNonEmptyString(obj.organization_id);
|
|
94
|
+
const userId = asNonEmptyString(obj.user_id);
|
|
95
|
+
if (!assistantId && !organizationId && !userId) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
assistantId: assistantId ?? "",
|
|
100
|
+
organizationId: organizationId ?? "",
|
|
101
|
+
userId: userId ?? "",
|
|
102
|
+
};
|
|
103
|
+
} catch (err) {
|
|
104
|
+
log.warn({ err }, "platform identity validate failed");
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function readAssistantApiKey(): Promise<string> {
|
|
110
|
+
try {
|
|
111
|
+
const stored = (
|
|
112
|
+
await getSecureKeyAsync(credentialKey("vellum", "assistant_api_key"))
|
|
113
|
+
)?.trim();
|
|
114
|
+
if (stored) {
|
|
115
|
+
return stored;
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
log.warn({ err }, "failed to read assistant API key from credential store");
|
|
119
|
+
}
|
|
120
|
+
return process.env.ASSISTANT_API_KEY?.trim() ?? "";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let ensureInFlight: Promise<void> | null = null;
|
|
124
|
+
let nextEnsureAttemptAt = 0;
|
|
125
|
+
|
|
126
|
+
export function _resetPlatformIdentityEnsureForTests(): void {
|
|
127
|
+
ensureInFlight = null;
|
|
128
|
+
nextEnsureAttemptAt = 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Load in-memory platform ids from validate when they are missing.
|
|
133
|
+
*
|
|
134
|
+
* No-ops when the assistant id is already set, when auth prerequisites are
|
|
135
|
+
* missing, or when a failed attempt is still inside the cooldown window.
|
|
136
|
+
* Concurrent callers share one in-flight request.
|
|
137
|
+
*/
|
|
138
|
+
export async function ensurePlatformIdentityIds(): Promise<void> {
|
|
139
|
+
if (getPlatformAssistantId().trim()) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (Date.now() < nextEnsureAttemptAt) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (!ensureInFlight) {
|
|
146
|
+
ensureInFlight = (async () => {
|
|
147
|
+
try {
|
|
148
|
+
const apiKey = await readAssistantApiKey();
|
|
149
|
+
const baseUrl = getPlatformBaseUrl();
|
|
150
|
+
if (!apiKey || !baseUrl) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const ids = await fetchPlatformIdentityIds(baseUrl, apiKey);
|
|
154
|
+
if (!ids) {
|
|
155
|
+
nextEnsureAttemptAt = Date.now() + ENSURE_COOLDOWN_MS;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
applyPlatformIdentityIds(ids);
|
|
159
|
+
nextEnsureAttemptAt = 0;
|
|
160
|
+
log.info("Loaded platform identity from platform validate");
|
|
161
|
+
} finally {
|
|
162
|
+
ensureInFlight = null;
|
|
163
|
+
}
|
|
164
|
+
})();
|
|
165
|
+
}
|
|
166
|
+
await ensureInFlight;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export async function resolvePlatformAssistantId(): Promise<string> {
|
|
170
|
+
const existing = getPlatformAssistantId().trim();
|
|
171
|
+
if (existing) {
|
|
172
|
+
return existing;
|
|
173
|
+
}
|
|
174
|
+
await ensurePlatformIdentityIds();
|
|
175
|
+
return getPlatformAssistantId().trim();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export async function resolvePlatformAssistantIdOrNull(): Promise<
|
|
179
|
+
string | null
|
|
180
|
+
> {
|
|
181
|
+
const id = await resolvePlatformAssistantId();
|
|
182
|
+
return id || null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export async function resolvePlatformOrganizationId(): Promise<string> {
|
|
186
|
+
const existing = getPlatformOrganizationId().trim();
|
|
187
|
+
if (existing) {
|
|
188
|
+
return existing;
|
|
189
|
+
}
|
|
190
|
+
await ensurePlatformIdentityIds();
|
|
191
|
+
return getPlatformOrganizationId().trim();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function resolvePlatformUserId(): Promise<string> {
|
|
195
|
+
const existing = getPlatformUserId().trim();
|
|
196
|
+
if (existing) {
|
|
197
|
+
return existing;
|
|
198
|
+
}
|
|
199
|
+
await ensurePlatformIdentityIds();
|
|
200
|
+
return getPlatformUserId().trim();
|
|
201
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Rehydrate the in-memory platform identity/URL overrides
|
|
3
|
-
* store at process startup.
|
|
2
|
+
* Rehydrate the in-memory platform identity/URL overrides at process startup.
|
|
4
3
|
*
|
|
5
4
|
* These overrides (`setPlatformBaseUrl` and the platform ID setters in
|
|
6
5
|
* `config/env.ts`) are normally only populated at runtime by the secret-routes
|
|
@@ -10,18 +9,17 @@
|
|
|
10
9
|
* environment default (e.g. dev-platform) while the credential store holds the
|
|
11
10
|
* real production values, and requests are sent to the wrong environment.
|
|
12
11
|
*
|
|
12
|
+
* Bound platform ids come from `POST /v1/internal/assistants/validate/`,
|
|
13
|
+
* which trades the assistant API key for assistant/organization/user ids.
|
|
14
|
+
*
|
|
13
15
|
* Each field is best-effort: a credential-store read failure is logged and
|
|
14
16
|
* skipped so a single missing value never blocks startup.
|
|
15
17
|
*/
|
|
16
18
|
import { credentialKey } from "../security/credential-key.js";
|
|
17
19
|
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
18
20
|
import { getLogger } from "../util/logger.js";
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
setPlatformBaseUrl,
|
|
22
|
-
setPlatformOrganizationId,
|
|
23
|
-
setPlatformUserId,
|
|
24
|
-
} from "./env.js";
|
|
21
|
+
import { setPlatformBaseUrl } from "./env.js";
|
|
22
|
+
import { ensurePlatformIdentityIds } from "./platform-identity.js";
|
|
25
23
|
|
|
26
24
|
const log = getLogger("platform-rehydration");
|
|
27
25
|
|
|
@@ -47,30 +45,16 @@ async function rehydrateField(
|
|
|
47
45
|
|
|
48
46
|
/**
|
|
49
47
|
* Rehydrate the platform base URL and the related platform IDs (assistant,
|
|
50
|
-
* organization, user)
|
|
51
|
-
* overrides. Safe to call more than once.
|
|
48
|
+
* organization, user). Safe to call more than once.
|
|
52
49
|
*/
|
|
53
50
|
export async function rehydratePlatformCredentials(): Promise<void> {
|
|
54
|
-
// Base URL first so managed proxy activation
|
|
55
|
-
//
|
|
51
|
+
// Base URL first so managed proxy activation and identity validate resolve
|
|
52
|
+
// the correct environment for every request that follows.
|
|
56
53
|
await rehydrateField(
|
|
57
54
|
"platform_base_url",
|
|
58
55
|
setPlatformBaseUrl,
|
|
59
56
|
"platform base URL",
|
|
60
57
|
);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
setPlatformAssistantId,
|
|
64
|
-
"platform assistant ID",
|
|
65
|
-
);
|
|
66
|
-
await rehydrateField(
|
|
67
|
-
"platform_organization_id",
|
|
68
|
-
setPlatformOrganizationId,
|
|
69
|
-
"platform organization ID",
|
|
70
|
-
);
|
|
71
|
-
await rehydrateField(
|
|
72
|
-
"platform_user_id",
|
|
73
|
-
setPlatformUserId,
|
|
74
|
-
"platform user ID",
|
|
75
|
-
);
|
|
58
|
+
|
|
59
|
+
await ensurePlatformIdentityIds();
|
|
76
60
|
}
|
package/src/config/schema.ts
CHANGED
|
@@ -41,7 +41,6 @@ import {
|
|
|
41
41
|
AuditLogConfigSchema,
|
|
42
42
|
LogFileConfigSchema,
|
|
43
43
|
} from "./schemas/logging.js";
|
|
44
|
-
import { McpConfigSchema } from "./schemas/mcp.js";
|
|
45
44
|
import { MemoryConfigSchema } from "./schemas/memory.js";
|
|
46
45
|
import { MigrationsConfigSchema } from "./schemas/migrations.js";
|
|
47
46
|
import { MonitoringConfigSchema } from "./schemas/monitoring.js";
|
|
@@ -62,6 +61,7 @@ import {
|
|
|
62
61
|
} from "./schemas/timeouts.js";
|
|
63
62
|
import { ToolsConfigSchema } from "./schemas/tools.js";
|
|
64
63
|
import { UserRoutesConfigSchema } from "./schemas/user-routes.js";
|
|
64
|
+
import { VoiceConfigSchema } from "./schemas/voice.js";
|
|
65
65
|
import { WorkflowsConfigSchema } from "./schemas/workflows.js";
|
|
66
66
|
import { WorkspaceGitConfigSchema } from "./schemas/workspace-git.js";
|
|
67
67
|
|
|
@@ -108,7 +108,6 @@ export const AssistantConfigSchema = z.object({
|
|
|
108
108
|
),
|
|
109
109
|
journal: JournalConfigSchema.default(JournalConfigSchema.parse({})),
|
|
110
110
|
backup: BackupConfigSchema.default(BackupConfigSchema.parse({})),
|
|
111
|
-
mcp: McpConfigSchema.default(McpConfigSchema.parse({})),
|
|
112
111
|
acp: AcpConfigSchema.default(AcpConfigSchema.parse({})),
|
|
113
112
|
skills: SkillsConfigSchema.default(SkillsConfigSchema.parse({})),
|
|
114
113
|
workspaceGit: WorkspaceGitConfigSchema.default(
|
|
@@ -119,6 +118,7 @@ export const AssistantConfigSchema = z.object({
|
|
|
119
118
|
twilio: TwilioConfigSchema.default(TwilioConfigSchema.parse({})),
|
|
120
119
|
calls: CallsConfigSchema.default(CallsConfigSchema.parse({})),
|
|
121
120
|
liveVoice: LiveVoiceConfigSchema.default(LiveVoiceConfigSchema.parse({})),
|
|
121
|
+
voice: VoiceConfigSchema.default(VoiceConfigSchema.parse({})),
|
|
122
122
|
sight: SightConfigSchema.default(SightConfigSchema.parse({})),
|
|
123
123
|
whatsapp: WhatsAppConfigSchema.default(WhatsAppConfigSchema.parse({})),
|
|
124
124
|
telegram: TelegramConfigSchema.default(TelegramConfigSchema.parse({})),
|
|
@@ -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
|
+
});
|