@vellumai/assistant 0.11.3 → 0.11.4-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/ARCHITECTURE.md +11 -6
- package/docs/architecture/memory.md +11 -0
- package/docs/architecture/turn-actor.md +70 -0
- package/docs/flux-turn-detection-spike.md +243 -0
- package/docs/stt-provider-onboarding.md +3 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/openapi.yaml +139 -37
- package/package.json +1 -1
- package/scripts/voice-ttft-spike.ts +3 -3
- package/src/__tests__/app-compiler.test.ts +38 -3
- package/src/__tests__/attachments-store.test.ts +21 -12
- package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
- package/src/__tests__/call-site-routing-provider.test.ts +1 -1
- package/src/__tests__/channel-availability-routes.test.ts +14 -1
- package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
- package/src/__tests__/channel-delivery-store.test.ts +14 -14
- package/src/__tests__/config-loader-backfill.test.ts +3 -3
- package/src/__tests__/config-schema.test.ts +25 -10
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop.test.ts +28 -20
- package/src/__tests__/conversation-attention-store.test.ts +63 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
- package/src/__tests__/conversation-fork-crud.test.ts +69 -0
- package/src/__tests__/conversation-fork-referential.test.ts +67 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
- package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
- package/src/__tests__/conversation-queue.test.ts +177 -4
- package/src/__tests__/conversation-runtime-assembly.test.ts +134 -102
- package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
- package/src/__tests__/credential-prompt-route.test.ts +7 -10
- package/src/__tests__/custom-profile-ensure.test.ts +5 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
- package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
- package/src/__tests__/document-append-idempotency.test.ts +233 -0
- package/src/__tests__/edit-propagation.test.ts +0 -7
- package/src/__tests__/helpers/mock-actor-context.ts +49 -0
- package/src/__tests__/helpers/mock-conversation.ts +13 -1
- package/src/__tests__/injector-chain.test.ts +63 -41
- package/src/__tests__/injector-disk-pressure.test.ts +11 -23
- package/src/__tests__/llm-context-resolution.test.ts +73 -1
- package/src/__tests__/llm-schema.test.ts +5 -2
- package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +6 -5
- package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
- package/src/__tests__/mtime-cache.test.ts +1 -1
- package/src/__tests__/non-member-access-request.test.ts +0 -20
- package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -0
- package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
- package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +434 -105
- package/src/__tests__/scoped-approval-grants.test.ts +11 -6
- package/src/__tests__/secret-ingress-channel.test.ts +0 -1
- package/src/__tests__/skills.test.ts +32 -0
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
- package/src/__tests__/subagent-spawn-and-await.test.ts +14 -10
- package/src/__tests__/turn-events-store.test.ts +43 -0
- package/src/__tests__/ui-shape-teaching.test.ts +33 -0
- package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
- package/src/__tests__/user-plugin-loader.test.ts +1 -1
- package/src/__tests__/visible-app-context.test.ts +16 -9
- package/src/__tests__/voice-config-update.test.ts +40 -0
- package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
- package/src/__tests__/worker-plugin-surface.test.ts +77 -0
- package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
- package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
- package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
- package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
- package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
- package/src/acp/__tests__/auth-required.test.ts +161 -0
- package/src/acp/acp-claude-oauth.ts +19 -2
- package/src/acp/agent-process.test.ts +100 -0
- package/src/acp/agent-process.ts +29 -26
- package/src/acp/auth-required.ts +102 -0
- package/src/acp/session-manager.test.ts +119 -0
- package/src/acp/session-manager.ts +68 -2
- package/src/api/events/acp-auth-required.ts +55 -0
- package/src/api/index.ts +7 -0
- package/src/api/surfaces.ts +7 -3
- package/src/apps/app-store.ts +3 -0
- package/src/bundler/package-resolver.ts +2 -30
- package/src/calls/__tests__/voice-session-bridge.test.ts +173 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +94 -0
- package/src/calls/call-controller.ts +19 -3
- package/src/calls/call-setup-flow.ts +0 -1
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/calls/voice-session-bridge.ts +71 -16
- package/src/calls/voice-triage-escalate.ts +104 -2
- package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
- package/src/channels/config.ts +13 -0
- package/src/channels/plugin-channel-declarations.ts +108 -0
- package/src/channels/types.ts +30 -0
- package/src/cli/AGENTS.md +5 -2
- package/src/cli/commands/credentials.help.ts +2 -2
- package/src/cli/commands/inference-providers.ts +1 -1
- package/src/cli/commands/mcp.help.ts +13 -4
- package/src/cli/commands/mcp.ts +9 -0
- package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
- package/src/cli/commands/memory/index.help.ts +43 -1
- package/src/cli/commands/memory/memory-v3.ts +64 -0
- package/src/cli/commands/stt.help.ts +27 -2
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
- package/src/cli/lib/bundled-marketplace.json +13 -0
- package/src/cli/lib/upgrade-plugin.ts +42 -0
- package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
- package/src/config/__tests__/default-provider.test.ts +6 -1
- package/src/config/__tests__/profile-materialization.test.ts +75 -19
- package/src/config/bundled-skills/acp/SKILL.md +6 -7
- package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
- package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
- package/src/config/call-site-defaults.ts +4 -5
- package/src/config/default-profile-catalog.ts +83 -12
- package/src/config/default-profile-names.ts +4 -1
- package/src/config/default-provider-resolution.ts +4 -0
- package/src/config/llm-context-resolution.ts +11 -3
- package/src/config/llm-resolver.ts +28 -1
- package/src/config/profile-materialization.ts +70 -22
- package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
- package/src/config/schemas/call-site-catalog.ts +4 -4
- package/src/config/schemas/live-voice.ts +57 -23
- package/src/config/schemas/llm.ts +59 -32
- package/src/config/schemas/mcp.ts +23 -0
- package/src/config/schemas/plugin-updates.ts +6 -2
- package/src/config/schemas/stt.ts +1 -0
- package/src/context/outbound-sanitize.ts +96 -1
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
- package/src/daemon/conversation-agent-loop.ts +17 -6
- package/src/daemon/conversation-messaging.ts +5 -1
- package/src/daemon/conversation-notifiers.ts +9 -1
- package/src/daemon/conversation-process.ts +36 -6
- package/src/daemon/conversation-runtime-assembly.ts +3 -4
- package/src/daemon/conversation-surfaces.ts +27 -5
- package/src/daemon/conversation-tool-setup.ts +1 -2
- package/src/daemon/conversation.ts +48 -0
- package/src/daemon/interactive-turn-sender.ts +59 -0
- package/src/daemon/mcp-reload-service.ts +36 -6
- package/src/daemon/process-message.ts +24 -24
- package/src/daemon/providers-setup.ts +6 -3
- package/src/daemon/trust-context-types.ts +29 -0
- package/src/daemon/wake-conversation-ops.ts +3 -2
- package/src/documents/document-store.ts +138 -5
- package/src/hooks/hook-loader.ts +3 -3
- package/src/hooks/registry.ts +50 -6
- package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
- package/src/inbound/oauth-callback-url.ts +61 -0
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
- package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +932 -0
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
- package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
- package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
- package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
- package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
- package/src/live-voice/live-voice-archive.ts +2 -0
- package/src/live-voice/live-voice-metrics.ts +57 -32
- package/src/live-voice/live-voice-photo.ts +1 -2
- package/src/live-voice/live-voice-session.ts +535 -314
- package/src/live-voice/progress-narration.ts +277 -0
- package/src/live-voice/protocol.ts +21 -1
- package/src/mcp/__tests__/effective-config.test.ts +238 -0
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
- package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
- package/src/mcp/client.ts +16 -11
- package/src/mcp/effective-config.ts +113 -0
- package/src/mcp/manager.ts +11 -6
- package/src/mcp/mcp-auth-orchestrator.ts +13 -22
- package/src/mcp/mcp-oauth-provider.ts +205 -240
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
- package/src/monitoring/plugin-auto-update.ts +128 -24
- package/src/notifications/signal.ts +1 -0
- package/src/permissions/confirmation-guardian-request.test.ts +15 -11
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/question-guardian-request.test.ts +14 -6
- package/src/permissions/question-guardian-request.ts +1 -2
- package/src/persistence/attachments-store.ts +8 -1
- package/src/persistence/bookmark-crud.ts +3 -7
- package/src/persistence/conversation-attention-store.ts +16 -45
- package/src/persistence/conversation-crud.ts +33 -4
- package/src/persistence/conversation-lineage.ts +9 -0
- package/src/persistence/conversation-queries.ts +108 -41
- package/src/persistence/delivery-crud.ts +38 -29
- package/src/persistence/external-conversation-store.ts +32 -4
- package/src/persistence/llm-request-log-store.ts +4 -10
- package/src/persistence/llm-usage-store.ts +8 -3
- package/src/persistence/message-reads.test.ts +197 -0
- package/src/persistence/message-reads.ts +211 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
- package/src/persistence/real-user-turn-filter.ts +27 -3
- package/src/persistence/steps.ts +9 -0
- package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
- package/src/plugin-api/conversation-turn.ts +168 -5
- package/src/plugin-api/index.ts +21 -5
- package/src/plugin-api/vision-support.test.ts +1 -1
- package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
- package/src/plugins/defaults/memory/AGENTS.md +4 -0
- package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
- package/src/plugins/defaults/memory/buffer-format.ts +165 -0
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
- package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +8 -12
- package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
- package/src/plugins/defaults/memory/indexer.ts +3 -1
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
- package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
- package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +33 -0
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +199 -2
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +25 -18
- package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
- package/src/plugins/defaults/memory/substrate/static-context.ts +160 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
- package/src/plugins/defaults/memory/v3/prune.ts +2 -0
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
- package/src/plugins/defaults/memory/worker.ts +6 -3
- package/src/plugins/external-plugin-loader.ts +47 -0
- package/src/plugins/mcp-servers.ts +361 -0
- package/src/plugins/mtime-cache.ts +23 -49
- package/src/plugins/worker-plugin-surface.ts +33 -0
- package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
- package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
- package/src/providers/__tests__/retry-callsite.test.ts +5 -2
- package/src/providers/call-site-routing.ts +30 -3
- package/src/providers/connection-resolution.ts +194 -11
- package/src/providers/inference/auth.ts +6 -0
- package/src/providers/inference/connection-availability.ts +24 -2
- package/src/providers/inference/connections.ts +2 -0
- package/src/providers/model-catalog.ts +3 -3
- package/src/providers/model-intents.ts +28 -8
- package/src/providers/openai/chat-completions-provider.ts +5 -6
- package/src/providers/openai/codex-models.ts +2 -1
- package/src/providers/provider-send-message.ts +32 -3
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +620 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +719 -0
- package/src/providers/speech-to-text/provider-catalog.ts +99 -8
- package/src/providers/speech-to-text/resolve.ts +25 -2
- package/src/routes/worker.ts +17 -5
- package/src/runtime/access-request-helper.ts +9 -12
- package/src/runtime/agent-wake.ts +3 -3
- package/src/runtime/pre-first-message-gate.ts +4 -0
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +219 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +62 -1
- package/src/runtime/routes/channel-availability-routes.ts +32 -14
- package/src/runtime/routes/channel-route-shared.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
- package/src/runtime/routes/conversation-list-routes.ts +112 -1
- package/src/runtime/routes/conversation-query-routes.ts +40 -27
- package/src/runtime/routes/credential-prompt-routes.ts +4 -7
- package/src/runtime/routes/default-provider-routes.ts +15 -0
- package/src/runtime/routes/inbound-message-handler.ts +17 -41
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
- package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +1 -3
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -4
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +11 -20
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
- package/src/runtime/routes/inference-profiles-routes.ts +20 -11
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/mcp-auth-routes.ts +148 -57
- package/src/runtime/routes/plugins-routes.ts +21 -3
- package/src/runtime/routes/stt-routes.ts +31 -25
- package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
- package/src/runtime/routes/user-route-dispatcher.ts +39 -14
- package/src/runtime/routes/user-route-import.ts +108 -0
- package/src/schedule/worker.ts +6 -0
- package/src/security/oauth2.ts +6 -22
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
- package/src/stt/__tests__/types.test.ts +94 -0
- package/src/stt/daemon-batch-transcriber.ts +10 -0
- package/src/stt/stt-stream-session.ts +8 -4
- package/src/stt/types.ts +103 -0
- package/src/subagent/manager.ts +1 -3
- package/src/subagent/types.ts +7 -6
- package/src/tools/acp/spawn.test.ts +97 -0
- package/src/tools/acp/spawn.ts +32 -0
- package/src/tools/document/document-tool.ts +12 -3
- package/src/tools/registry.ts +2 -1
- package/src/tools/ui-surface/surface-shape-docs.ts +11 -0
- package/src/tools/workflows/run-workflow.ts +1 -2
- package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
- package/src/tts/reasoning-tag-filter.ts +89 -0
- package/src/util/think-tag-stream.ts +95 -0
- package/src/workspace/byok-default-profile-ensure.ts +76 -24
- package/src/workspace/custom-profile-ensure.ts +4 -24
- package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
- package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
- package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
- package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
- package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/provider-commit-message-generator.ts +7 -5
- package/src/live-voice/__tests__/front-decision.test.ts +0 -645
- package/src/live-voice/front-decision.ts +0 -476
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
// Entry-name folds are gated on the named row existing with an agreeing
|
|
4
|
+
// kind, so the tests control the row store directly.
|
|
5
|
+
const connectionRows = new Map<string, { name: string; provider: string }>([
|
|
6
|
+
["anthropic-personal", { name: "anthropic-personal", provider: "anthropic" }],
|
|
7
|
+
]);
|
|
8
|
+
mock.module("../../persistence/db-connection.js", () => ({
|
|
9
|
+
getDb: () => ({}),
|
|
10
|
+
}));
|
|
11
|
+
mock.module("../../providers/inference/connections.js", () => ({
|
|
12
|
+
getConnection: (_db: unknown, name: string) =>
|
|
13
|
+
connectionRows.get(name) ?? null,
|
|
14
|
+
}));
|
|
2
15
|
|
|
3
16
|
import { VELLUM_MANAGED_CONNECTION_NAME } from "../../providers/vellum-model-routing.js";
|
|
4
17
|
import { completeCustomProfile } from "../profile-materialization.js";
|
|
@@ -28,8 +41,10 @@ describe("completeCustomProfile", () => {
|
|
|
28
41
|
model: "claude-fable-5",
|
|
29
42
|
});
|
|
30
43
|
expect(completed.model).toBe("claude-fable-5");
|
|
31
|
-
|
|
32
|
-
|
|
44
|
+
// The default's explicit binding is inherited IN the provider value
|
|
45
|
+
// (the entries-model representation), never as a stamped binding.
|
|
46
|
+
expect(completed.provider).toBe("anthropic-personal");
|
|
47
|
+
expect(completed.provider_connection).toBeUndefined();
|
|
33
48
|
expect(completed.maxTokens).toBe(64000);
|
|
34
49
|
expect(completed.effort).toBe("max");
|
|
35
50
|
expect(completed.speed).toBe("standard");
|
|
@@ -78,12 +93,12 @@ describe("completeCustomProfile", () => {
|
|
|
78
93
|
expect(completed.contextWindow?.overflowRecovery?.enabled).toBe(true);
|
|
79
94
|
});
|
|
80
95
|
|
|
81
|
-
test("
|
|
96
|
+
test("inherits the default's binding as the entry name when the provider serves the model", () => {
|
|
82
97
|
const completed = completeCustomProfile(fullDefault, {
|
|
83
98
|
model: "claude-fable-5",
|
|
84
99
|
});
|
|
85
|
-
expect(completed.provider).toBe("anthropic");
|
|
86
|
-
expect(completed.provider_connection).
|
|
100
|
+
expect(completed.provider).toBe("anthropic-personal");
|
|
101
|
+
expect(completed.provider_connection).toBeUndefined();
|
|
87
102
|
});
|
|
88
103
|
|
|
89
104
|
test("stamps the catalog owner for a model the default provider does not serve, and drops the default's connection", () => {
|
|
@@ -123,36 +138,77 @@ describe("completeCustomProfile", () => {
|
|
|
123
138
|
expect(completed.provider_connection).toBeUndefined();
|
|
124
139
|
});
|
|
125
140
|
|
|
126
|
-
test("
|
|
141
|
+
test("a managed default's binding becomes the routing identity, never a stamped field", () => {
|
|
127
142
|
const managedDefault = LLMConfigBase.parse({
|
|
128
143
|
...fullDefault,
|
|
129
144
|
provider_connection: VELLUM_MANAGED_CONNECTION_NAME,
|
|
130
145
|
});
|
|
131
146
|
const implied = completeCustomProfile(managedDefault, { model: "gpt-5.5" });
|
|
132
|
-
expect(implied.provider).toBe("
|
|
133
|
-
expect(implied.provider_connection).
|
|
147
|
+
expect(implied.provider).toBe("vellum");
|
|
148
|
+
expect(implied.provider_connection).toBeUndefined();
|
|
134
149
|
|
|
135
150
|
const explicit = completeCustomProfile(managedDefault, {
|
|
136
151
|
provider: "openai",
|
|
137
152
|
model: "gpt-5.4",
|
|
138
153
|
});
|
|
139
|
-
expect(explicit.
|
|
154
|
+
expect(explicit.provider).toBe("vellum");
|
|
155
|
+
expect(explicit.provider_connection).toBeUndefined();
|
|
156
|
+
});
|
|
140
157
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
provider: "openrouter",
|
|
145
|
-
model: "minimax/minimax-m3",
|
|
158
|
+
test("inherits the binding as the entry name even for a model unknown to the catalog", () => {
|
|
159
|
+
const completed = completeCustomProfile(fullDefault, {
|
|
160
|
+
model: "totally-custom-model",
|
|
146
161
|
});
|
|
147
|
-
expect(
|
|
162
|
+
expect(completed.provider).toBe("anthropic-personal");
|
|
163
|
+
expect(completed.provider_connection).toBeUndefined();
|
|
148
164
|
});
|
|
149
165
|
|
|
150
|
-
test("
|
|
151
|
-
const
|
|
166
|
+
test("a dangling default binding passes through as the legacy field", () => {
|
|
167
|
+
const dangling = LLMConfigBase.parse({
|
|
168
|
+
...fullDefault,
|
|
169
|
+
provider_connection: "deleted-row",
|
|
170
|
+
});
|
|
171
|
+
const completed = completeCustomProfile(dangling, {
|
|
172
|
+
model: "claude-fable-5",
|
|
173
|
+
});
|
|
174
|
+
// Folding an unverifiable binding would hide it from the collapse
|
|
175
|
+
// migration's dangling recovery; the legacy field keeps it visible.
|
|
176
|
+
expect(completed.provider).toBe("anthropic");
|
|
177
|
+
expect(completed.provider_connection).toBe("deleted-row");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("a kind-disagreeing default binding passes through as the legacy field", () => {
|
|
181
|
+
connectionRows.set("mislabeled", {
|
|
182
|
+
name: "mislabeled",
|
|
183
|
+
provider: "openai",
|
|
184
|
+
});
|
|
185
|
+
try {
|
|
186
|
+
const mismatched = LLMConfigBase.parse({
|
|
187
|
+
...fullDefault,
|
|
188
|
+
provider_connection: "mislabeled",
|
|
189
|
+
});
|
|
190
|
+
const completed = completeCustomProfile(mismatched, {
|
|
191
|
+
model: "claude-fable-5",
|
|
192
|
+
});
|
|
193
|
+
expect(completed.provider).toBe("anthropic");
|
|
194
|
+
expect(completed.provider_connection).toBe("mislabeled");
|
|
195
|
+
} finally {
|
|
196
|
+
connectionRows.delete("mislabeled");
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("a managed default binding is not inherited when the identity cannot serve the model", () => {
|
|
201
|
+
const managedDefault = LLMConfigBase.parse({
|
|
202
|
+
...fullDefault,
|
|
203
|
+
provider_connection: VELLUM_MANAGED_CONNECTION_NAME,
|
|
204
|
+
});
|
|
205
|
+
const completed = completeCustomProfile(managedDefault, {
|
|
152
206
|
model: "totally-custom-model",
|
|
153
207
|
});
|
|
208
|
+
// Dispatch auto-resolves by vendor instead of pinning an unservable
|
|
209
|
+
// managed route.
|
|
154
210
|
expect(completed.provider).toBe("anthropic");
|
|
155
|
-
expect(completed.provider_connection).
|
|
211
|
+
expect(completed.provider_connection).toBeUndefined();
|
|
156
212
|
});
|
|
157
213
|
|
|
158
214
|
test("passes mix profiles through untouched", () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: acp
|
|
3
|
-
description:
|
|
3
|
+
description: Set up, authenticate, and run external coding agents (Claude Code, Codex) via the Agent Client Protocol
|
|
4
4
|
compatibility: "Designed for Vellum personal assistants"
|
|
5
5
|
metadata:
|
|
6
6
|
emoji: "🔗"
|
|
@@ -8,13 +8,12 @@ metadata:
|
|
|
8
8
|
display-name: "ACP"
|
|
9
9
|
category: "development"
|
|
10
10
|
activation-hints:
|
|
11
|
-
- "User
|
|
12
|
-
- "User
|
|
13
|
-
- "User wants
|
|
14
|
-
- "User
|
|
15
|
-
- "User mentions ACP, claude-agent-acp, codex-acp, or running multiple coding agents in parallel"
|
|
11
|
+
- "User wants to set up, install, configure, authenticate, or connect Claude Code or Codex"
|
|
12
|
+
- "User asks to use Claude Code or Codex, or delegate a coding task to an ACP agent"
|
|
13
|
+
- "User wants an agent to work autonomously and report back later"
|
|
14
|
+
- "User mentions ACP, claude-agent-acp, or codex-acp"
|
|
16
15
|
avoid-when:
|
|
17
|
-
- "
|
|
16
|
+
- "The task is small enough to do inline"
|
|
18
17
|
---
|
|
19
18
|
|
|
20
19
|
ACP agent orchestration - spawn external coding agents (Claude Code, Codex) to work on tasks via the Agent Client Protocol. Each agent runs as its own subprocess speaking ACP over stdio and streams results back into the conversation.
|
|
@@ -33,9 +33,9 @@ Write and edit long-form documents using the built-in rich text editor. Document
|
|
|
33
33
|
|
|
34
34
|
This is the default path when the user asks you to write something.
|
|
35
35
|
|
|
36
|
-
1. **Create the document**: Call `document_create` with a title (inferred from the request). Call the tool immediately, not after conversational preamble.
|
|
36
|
+
1. **Create the document**: Call `document_create` with a title (inferred from the request). Call the tool immediately, not after conversational preamble. Anything you pass as `initial_content` is saved right then, so the first `document_update` must start with the next chunk rather than repeating it.
|
|
37
37
|
2. **Write content in Markdown**: Use proper structure (`#` for titles, `##` for sections), **bold**, _italic_, code blocks, tables, lists, blockquotes as appropriate.
|
|
38
|
-
3. **CRITICAL - Stream content in chunks**: Call `document_update` MULTIPLE times, not just once. Break content into logical chunks (paragraphs, sections, or every 200-300 words). Call `document_update` with `mode: "append"` for EACH chunk separately. When you are streaming into the document you just created, `surface_id` is optional
|
|
38
|
+
3. **CRITICAL - Stream content in chunks**: Call `document_update` MULTIPLE times, not just once. Break content into logical chunks (paragraphs, sections, or every 200-300 words). Call `document_update` with `mode: "append"` for EACH chunk separately. Each append carries ONLY that chunk: content already in the document is committed, and resending it would print it twice. When you are streaming into the document you just created, `surface_id` is optional: omit it and pass only `content`, and the update targets that document. The user experiences real-time content appearing as you write.
|
|
39
39
|
|
|
40
40
|
### Recovering from a failed update
|
|
41
41
|
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"initial_content": {
|
|
35
35
|
"type": "string",
|
|
36
|
-
"description": "Initial Markdown content to populate the editor (optional)"
|
|
36
|
+
"description": "Initial Markdown content to populate the editor (optional). It is saved as soon as the document is created, so the first document_update append must start with the NEXT chunk, never with this text again."
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
},
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
},
|
|
55
55
|
"content": {
|
|
56
56
|
"type": "string",
|
|
57
|
-
"description": "Markdown content to set or append"
|
|
57
|
+
"description": "Markdown content to set or append. In append mode, send only the new chunk: whatever is already in the document, including document_create's initial_content, is committed and must not be resent."
|
|
58
58
|
},
|
|
59
59
|
"mode": {
|
|
60
60
|
"type": "string",
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
updateProcessingStage,
|
|
20
20
|
} from "../../../../persistence/media-store.js";
|
|
21
21
|
import { resolveBatchTranscriber } from "../../../../providers/speech-to-text/resolve.js";
|
|
22
|
+
import type { BatchTranscriber } from "../../../../stt/types.js";
|
|
22
23
|
import { silentlyWithLog } from "../../../../util/silently.js";
|
|
23
24
|
import {
|
|
24
25
|
FFMPEG_PALETTE_TIMEOUT_MS,
|
|
@@ -459,10 +460,19 @@ export async function preprocessForAsset(
|
|
|
459
460
|
const allFramePaths: string[] = [];
|
|
460
461
|
|
|
461
462
|
// Resolve the STT transcriber once for all segments to avoid repeated
|
|
462
|
-
// credential lookups in the per-segment loop.
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
463
|
+
// credential lookups in the per-segment loop. A resolver failure degrades
|
|
464
|
+
// to transcript-less segments like an absent provider does, reporting the
|
|
465
|
+
// reason on the progress stream rather than killing the whole run.
|
|
466
|
+
let transcriber: BatchTranscriber | null = null;
|
|
467
|
+
if (options.includeAudio) {
|
|
468
|
+
try {
|
|
469
|
+
transcriber = await resolveBatchTranscriber();
|
|
470
|
+
} catch (err) {
|
|
471
|
+
onProgress?.(
|
|
472
|
+
`Audio transcription unavailable: ${(err as Error).message}\n`,
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
466
476
|
|
|
467
477
|
const scaleFilter = `scale='if(gt(iw,ih),-1,${config.shortEdge})':'if(gt(iw,ih),${config.shortEdge},-1)'`;
|
|
468
478
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"tools": [
|
|
4
4
|
{
|
|
5
5
|
"name": "voice_config_update",
|
|
6
|
-
"description": "Update a voice configuration setting. Use tts_provider / stt_provider to switch the active TTS / STT provider. Provider \"vellum\" is Vellum-managed speech (billed to your organization; requires a Vellum platform connection via 'assistant platform connect'); any other provider uses the user's own API key. Valid TTS providers come from the provider catalog (vellum, elevenlabs, fish-audio, deepgram, xai); valid STT providers: vellum, deepgram, google-gemini, openai-whisper, xai. Use tts_voice_id to change the voice; it targets whichever TTS provider is currently active. For elevenlabs, pass an ElevenLabs voice ID. For vellum (managed), pass a managed voice model ID: this may be an ElevenLabs voice ID (managed speech serves the same ElevenLabs voices) or a Deepgram Aura model ID (e.g. aura-2-thalia-en); only rate-carded voices synthesize, so prefer models from the managed catalog (
|
|
6
|
+
"description": "Update a voice configuration setting. Use tts_provider / stt_provider to switch the active TTS / STT provider. Provider \"vellum\" is Vellum-managed speech (billed to your organization; requires a Vellum platform connection via 'assistant platform connect'); any other provider uses the user's own API key. Valid TTS providers come from the provider catalog (vellum, elevenlabs, fish-audio, deepgram, xai); valid STT providers: vellum, deepgram, deepgram-flux, google-gemini, openai-whisper, xai. deepgram-flux is streaming-only: it serves live speech (voice mode, dictation) but cannot transcribe audio files or voice messages, so do not recommend it when the user wants file transcription. Use tts_voice_id to change the voice; it targets whichever TTS provider is currently active. For elevenlabs, pass an ElevenLabs voice ID. For vellum (managed), pass a managed voice model ID: this may be an ElevenLabs voice ID (managed speech serves the same ElevenLabs voices) or a Deepgram Aura model ID (e.g. aura-2-thalia-en); only rate-carded voices synthesize, so prefer models from the managed catalog (assistant route GET tts/managed-voices, also used by the web voice picker). For deepgram, pass a Deepgram Aura model ID. Use fish_audio_reference_id for Fish Audio voice reference. Use stt_language to set the spoken language for speech recognition: one of the 50 base language codes on the verified Deepgram nova-3 monolingual roster (e.g. en, es, hi, ta, zh, ko), or \"multi\" for code-switching mid-sentence across its 10-language roster (English, Spanish, French, German, Hindi, Russian, Portuguese, Japanese, Italian, Dutch; e.g. Hinglish); plain language names like \"tamil\" or \"multilingual\" are accepted and normalized. Accepted values follow the configured STT provider: vellum-managed and deepgram accept the full roster plus \"multi\"; xai accepts only the 10 multilingual-roster codes (en, es, fr, de, hi, ru, pt, ja, it, nl) and rejects \"multi\" and the extended codes (both are verified for Deepgram nova-3 only); google-gemini / openai-whisper auto-detect natively and deepgram-flux runs an English-only model, so the value persists but is ignored while they are active. Deepgram uses the same API key for TTS and STT, and deepgram-flux shares it too. Changes persist to services.stt / services.tts config and take effect immediately.",
|
|
7
7
|
"category": "system",
|
|
8
8
|
"risk": "low",
|
|
9
9
|
"input_schema": {
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
"tts_provider",
|
|
21
21
|
"tts_voice_id"
|
|
22
22
|
],
|
|
23
|
-
"description": "The voice setting to change. tts_provider / stt_provider select the active provider for each service (\"vellum\" = Vellum-managed speech; anything else = the user's own API key). tts_voice_id sets the voice for the currently active TTS provider (ElevenLabs voice ID for elevenlabs; a managed voice model ID for vellum, meaning an ElevenLabs voice ID or a Deepgram Aura model ID like aura-2-thalia-en; a Deepgram Aura model ID for deepgram; voice ID for xai). fish_audio_reference_id sets the Fish Audio voice reference. stt_language sets the spoken language for speech recognition (vellum-managed/deepgram: the full roster plus \"multi\"; xai: only the 10 multilingual-roster codes, with \"multi\" and the extended codes rejected; google-gemini and openai-whisper auto-detect and ignore it). Deepgram shares one API key across TTS and STT."
|
|
23
|
+
"description": "The voice setting to change. tts_provider / stt_provider select the active provider for each service (\"vellum\" = Vellum-managed speech; anything else = the user's own API key). tts_voice_id sets the voice for the currently active TTS provider (ElevenLabs voice ID for elevenlabs; a managed voice model ID for vellum, meaning an ElevenLabs voice ID or a Deepgram Aura model ID like aura-2-thalia-en; a Deepgram Aura model ID for deepgram; voice ID for xai). fish_audio_reference_id sets the Fish Audio voice reference. stt_language sets the spoken language for speech recognition (vellum-managed/deepgram: the full roster plus \"multi\"; xai: only the 10 multilingual-roster codes, with \"multi\" and the extended codes rejected; google-gemini and openai-whisper auto-detect and ignore it; deepgram-flux is English-only and ignores it). Deepgram shares one API key across TTS and STT, and deepgram-flux uses that same key."
|
|
24
24
|
},
|
|
25
25
|
"value": {
|
|
26
|
-
"description": "The new value for the setting. For tts_provider: one of vellum, elevenlabs, fish-audio, deepgram, xai. For stt_provider: one of vellum, deepgram, google-gemini, openai-whisper, xai. For tts_voice_id: a voice ID for the active TTS provider: an alphanumeric ElevenLabs voice ID (elevenlabs), a managed voice model ID which may be an ElevenLabs voice ID or a Deepgram Aura model ID like aura-2-thalia-en (vellum), or a Deepgram Aura model ID (deepgram). For fish_audio_reference_id: a Fish Audio voice reference ID. For stt_language: one of the 50 base codes on the nova-3 monolingual roster (e.g. en, es, hi, ta, zh, ko), or multi (code-switching across its 10-language roster); language names like \"hindi\", \"tamil\", or \"multilingual\" are also accepted and normalized; when the configured STT provider is xai, only the 10 multilingual-roster codes (en, es, fr, de, hi, ru, pt, ja, it, nl) are accepted. For conversation_timeout: seconds (5, 10, 15, 30, or 60). For activation_key: key identifier string."
|
|
26
|
+
"description": "The new value for the setting. For tts_provider: one of vellum, elevenlabs, fish-audio, deepgram, xai. For stt_provider: one of vellum, deepgram, deepgram-flux, google-gemini, openai-whisper, xai; deepgram-flux serves live speech only and cannot transcribe files. For tts_voice_id: a voice ID for the active TTS provider: an alphanumeric ElevenLabs voice ID (elevenlabs), a managed voice model ID which may be an ElevenLabs voice ID or a Deepgram Aura model ID like aura-2-thalia-en (vellum), or a Deepgram Aura model ID (deepgram). For fish_audio_reference_id: a Fish Audio voice reference ID. For stt_language: one of the 50 base codes on the nova-3 monolingual roster (e.g. en, es, hi, ta, zh, ko), or multi (code-switching across its 10-language roster); language names like \"hindi\", \"tamil\", or \"multilingual\" are also accepted and normalized; when the configured STT provider is xai, only the 10 multilingual-roster codes (en, es, fr, de, hi, ru, pt, ja, it, nl) are accepted. For conversation_timeout: seconds (5, 10, 15, 30, or 60). For activation_key: key identifier string."
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
},
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import type { ToolContext } from "../../../../tools/types.js";
|
|
4
|
+
import { run } from "./navigate-settings-tab.js";
|
|
5
|
+
|
|
6
|
+
let sentMessages: Array<{ type: string; [key: string]: unknown }> = [];
|
|
7
|
+
|
|
8
|
+
function makeContext(overrides?: Partial<ToolContext>): ToolContext {
|
|
9
|
+
return {
|
|
10
|
+
workingDir: "/tmp",
|
|
11
|
+
conversationId: "conv-xyz",
|
|
12
|
+
trustClass: "guardian",
|
|
13
|
+
sendToClient: (msg) => {
|
|
14
|
+
sentMessages.push(msg);
|
|
15
|
+
},
|
|
16
|
+
...overrides,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe("navigate_settings_tab tool", () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
sentMessages = [];
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("navigates to the Voice tab and hints the inline picker", async () => {
|
|
26
|
+
const result = await run({ tab: "Voice" }, makeContext());
|
|
27
|
+
|
|
28
|
+
expect(result.isError).toBe(false);
|
|
29
|
+
expect(sentMessages).toEqual([{ type: "navigate_settings", tab: "Voice" }]);
|
|
30
|
+
expect(result.content).toStartWith("Opened settings to the Voice tab.");
|
|
31
|
+
expect(result.content).toContain(
|
|
32
|
+
'ui_show { surface_type: "voice_picker", data: {} }',
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("leaves a non-Voice tab's result unchanged", async () => {
|
|
37
|
+
const result = await run({ tab: "Billing" }, makeContext());
|
|
38
|
+
|
|
39
|
+
expect(result.isError).toBe(false);
|
|
40
|
+
expect(sentMessages).toEqual([
|
|
41
|
+
{ type: "navigate_settings", tab: "Billing" },
|
|
42
|
+
]);
|
|
43
|
+
expect(result.content).toBe("Opened settings to the Billing tab.");
|
|
44
|
+
expect(result.content).not.toContain("voice_picker");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("resolves legacy tab aliases without hinting", async () => {
|
|
48
|
+
const result = await run({ tab: "Archived Conversations" }, makeContext());
|
|
49
|
+
|
|
50
|
+
expect(result.isError).toBe(false);
|
|
51
|
+
expect(sentMessages).toEqual([
|
|
52
|
+
{ type: "navigate_settings", tab: "Archive" },
|
|
53
|
+
]);
|
|
54
|
+
expect(result.content).toBe("Opened settings to the Archive tab.");
|
|
55
|
+
expect(result.content).not.toContain("voice_picker");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("rejects an unknown tab without navigating", async () => {
|
|
59
|
+
const result = await run({ tab: "Telepathy" }, makeContext());
|
|
60
|
+
|
|
61
|
+
expect(result.isError).toBe(true);
|
|
62
|
+
expect(result.content).toContain('unknown tab "Telepathy"');
|
|
63
|
+
expect(sentMessages).toEqual([]);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
ToolContext,
|
|
3
3
|
ToolExecutionResult,
|
|
4
4
|
} from "../../../../tools/types.js";
|
|
5
|
+
import { voicePickerHint } from "./shared.js";
|
|
5
6
|
|
|
6
7
|
const SETTINGS_TABS = [
|
|
7
8
|
"General",
|
|
@@ -23,6 +24,10 @@ const LEGACY_TAB_ALIASES: Record<string, SettingsTab> = {
|
|
|
23
24
|
"Archived Conversations": "Archive",
|
|
24
25
|
};
|
|
25
26
|
|
|
27
|
+
const VOICE_TAB_PICKER_HINT = voicePickerHint(
|
|
28
|
+
"which puts the picker in the conversation without navigating away. Navigating here is right only when the user explicitly asked to open Settings.",
|
|
29
|
+
);
|
|
30
|
+
|
|
26
31
|
export async function run(
|
|
27
32
|
input: Record<string, unknown>,
|
|
28
33
|
context: ToolContext,
|
|
@@ -45,8 +50,9 @@ export async function run(
|
|
|
45
50
|
});
|
|
46
51
|
}
|
|
47
52
|
|
|
53
|
+
const opened = `Opened settings to the ${tab} tab.`;
|
|
48
54
|
return {
|
|
49
|
-
content:
|
|
55
|
+
content: tab === "Voice" ? `${opened} ${VOICE_TAB_PICKER_HINT}` : opened,
|
|
50
56
|
isError: false,
|
|
51
57
|
};
|
|
52
58
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for the settings skill's tool executors.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Steer the model back to the inline voice picker.
|
|
7
|
+
*
|
|
8
|
+
* Both settings tools a "change my voice" request can land on (the Voice tab
|
|
9
|
+
* navigation and a managed `tts_voice_id` write) end by naming the picker, so
|
|
10
|
+
* the invocation itself lives here: one edit keeps both tools teaching the same
|
|
11
|
+
* call. Each caller supplies its own tail, because the reason the picker is
|
|
12
|
+
* better differs by the tool the model just reached for.
|
|
13
|
+
*/
|
|
14
|
+
export function voicePickerHint(tail: string): string {
|
|
15
|
+
return `Next time the user wants to change or hear a voice, prefer \`ui_show { surface_type: "voice_picker", data: {} }\`, ${tail}`;
|
|
16
|
+
}
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "../../../loader.js";
|
|
20
20
|
import { VALID_CONVERSATION_TIMEOUTS } from "../../../schemas/elevenlabs.js";
|
|
21
21
|
import { VALID_STT_PROVIDERS } from "../../../schemas/stt.js";
|
|
22
|
+
import { voicePickerHint } from "./shared.js";
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Valid voice config settings and their UserDefaults key mappings.
|
|
@@ -187,6 +188,19 @@ const STT_LANGUAGE_ALIASES: Record<
|
|
|
187
188
|
"code-switching": "multi",
|
|
188
189
|
};
|
|
189
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Same steer `navigate_settings_tab` puts on the Voice tab, applied to the
|
|
193
|
+
* strongest competing affordance: this tool's description coaches the model to
|
|
194
|
+
* read the managed catalog and set `tts_voice_id` itself, which is exactly the
|
|
195
|
+
* "assistant names voices in prose" outcome the inline picker exists to
|
|
196
|
+
* replace. Managed only, because that is the catalog the picker renders. The
|
|
197
|
+
* write still happens and the result stays non-error; this only redirects the
|
|
198
|
+
* next request.
|
|
199
|
+
*/
|
|
200
|
+
const MANAGED_VOICE_PICKER_HINT = voicePickerHint(
|
|
201
|
+
"which lets them hear and pick from the managed catalog in the conversation. Set the id here only when the user named a specific voice.",
|
|
202
|
+
);
|
|
203
|
+
|
|
190
204
|
function validateSetting(
|
|
191
205
|
setting: string,
|
|
192
206
|
value: unknown,
|
|
@@ -530,10 +544,14 @@ export async function run(
|
|
|
530
544
|
AUTO_DETECT_STT_PROVIDERS.has(activeSttProviderId)
|
|
531
545
|
? ` Note: the configured STT provider (${activeSttProviderId}) auto-detects the spoken language natively and ignores this setting.`
|
|
532
546
|
: "";
|
|
547
|
+
const pickerNote =
|
|
548
|
+
setting === "tts_voice_id" && activeTtsProviderId === "vellum"
|
|
549
|
+
? ` ${MANAGED_VOICE_PICKER_HINT}`
|
|
550
|
+
: "";
|
|
533
551
|
return {
|
|
534
552
|
content: `${friendlyName} updated to ${JSON.stringify(
|
|
535
553
|
validation.coerced,
|
|
536
|
-
)}.${broadcastNote}${autoDetectNote}`,
|
|
554
|
+
)}.${broadcastNote}${autoDetectNote}${pickerNote}`,
|
|
537
555
|
isError: false,
|
|
538
556
|
};
|
|
539
557
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
3
|
import type { BatchTranscriber } from "../../../../stt/types.js";
|
|
4
|
+
import { SttError } from "../../../../stt/types.js";
|
|
4
5
|
import type { ToolContext } from "../../../../tools/types.js";
|
|
5
6
|
|
|
6
7
|
// ---------------------------------------------------------------------------
|
|
@@ -8,9 +9,15 @@ import type { ToolContext } from "../../../../tools/types.js";
|
|
|
8
9
|
// ---------------------------------------------------------------------------
|
|
9
10
|
|
|
10
11
|
let mockTranscriber: BatchTranscriber | null = null;
|
|
12
|
+
let mockResolveError: Error | null = null;
|
|
11
13
|
|
|
12
14
|
mock.module("../../../../providers/speech-to-text/resolve.js", () => ({
|
|
13
|
-
resolveBatchTranscriber: async () =>
|
|
15
|
+
resolveBatchTranscriber: async () => {
|
|
16
|
+
if (mockResolveError) {
|
|
17
|
+
throw mockResolveError;
|
|
18
|
+
}
|
|
19
|
+
return mockTranscriber;
|
|
20
|
+
},
|
|
14
21
|
}));
|
|
15
22
|
|
|
16
23
|
// Track calls to spawnWithTimeout so we can simulate ffmpeg/ffprobe results.
|
|
@@ -103,6 +110,7 @@ function makeMockTranscriber(
|
|
|
103
110
|
describe("transcribe_media tool", () => {
|
|
104
111
|
beforeEach(() => {
|
|
105
112
|
mockTranscriber = null;
|
|
113
|
+
mockResolveError = null;
|
|
106
114
|
spawnResults = {};
|
|
107
115
|
accessiblePaths = new Set();
|
|
108
116
|
mockFileContents = {};
|
|
@@ -119,6 +127,19 @@ describe("transcribe_media tool", () => {
|
|
|
119
127
|
"No speech-to-text provider is configured",
|
|
120
128
|
);
|
|
121
129
|
});
|
|
130
|
+
|
|
131
|
+
test("surfaces a resolver error verbatim rather than the not-configured copy", async () => {
|
|
132
|
+
const reason =
|
|
133
|
+
'Deepgram Flux is streaming-only. Batch transcription requires the deepgram provider: set services.stt.provider to "deepgram".';
|
|
134
|
+
mockResolveError = new SttError("provider-error", reason, {
|
|
135
|
+
userFacing: true,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const result = await run({ file_path: "/tmp/test.mp3" }, makeContext());
|
|
139
|
+
|
|
140
|
+
expect(result.isError).toBe(true);
|
|
141
|
+
expect(result.content).toBe(reason);
|
|
142
|
+
});
|
|
122
143
|
});
|
|
123
144
|
|
|
124
145
|
describe("input validation", () => {
|
|
@@ -234,8 +234,15 @@ export async function run(
|
|
|
234
234
|
};
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
// Resolve the configured STT provider
|
|
238
|
-
|
|
237
|
+
// Resolve the configured STT provider. A typed resolver error already names
|
|
238
|
+
// the mismatch (e.g. a streaming-only provider) and its fix, so it reaches
|
|
239
|
+
// the model verbatim rather than as the "nothing configured" copy below.
|
|
240
|
+
let transcriber: BatchTranscriber | null;
|
|
241
|
+
try {
|
|
242
|
+
transcriber = await resolveBatchTranscriber();
|
|
243
|
+
} catch (err) {
|
|
244
|
+
return { content: (err as Error).message, isError: true };
|
|
245
|
+
}
|
|
239
246
|
if (!transcriber) {
|
|
240
247
|
return {
|
|
241
248
|
content:
|
|
@@ -121,22 +121,21 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
|
|
|
121
121
|
effort: "low",
|
|
122
122
|
thinking: { enabled: false },
|
|
123
123
|
},
|
|
124
|
-
//
|
|
125
|
-
// upstream cannot fit any usable decision budget (~1s+ per forced tool call).
|
|
124
|
+
// Progress narration only helps when it arrives before the next real output.
|
|
126
125
|
// `latency-optimized` is the latency-class profile (see
|
|
127
126
|
// default-profile-catalog.ts): managed installs get the pinned latency model,
|
|
128
127
|
// BYOK installs resolve their own provider's latency model through the intent
|
|
129
128
|
// table rather than a model id they may hold no credential for. The profile
|
|
130
129
|
// is user-facing ("Speed"), so a user edit to it moves this call site too.
|
|
131
|
-
|
|
130
|
+
voiceProgressNarration: {
|
|
132
131
|
profile: "latency-optimized",
|
|
133
132
|
effort: "low",
|
|
134
133
|
thinking: { enabled: false },
|
|
135
134
|
},
|
|
136
135
|
// The front-door leg fronts EVERY unified live-voice turn and its leading
|
|
137
136
|
// tokens ARE the endpointing/triage verdict, so both TTFT variance and
|
|
138
|
-
// judgment quality gate the whole call.
|
|
139
|
-
//
|
|
137
|
+
// judgment quality gate the whole call. Live drives showed the
|
|
138
|
+
// cost-optimized upstream with multi-second
|
|
140
139
|
// cross-session TTFT tails and over-escalation of small talk under open-task
|
|
141
140
|
// context pressure.
|
|
142
141
|
voiceFrontDoor: {
|