@vellumai/assistant 0.11.3 → 0.11.4-staging.1
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 +140 -38
- 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 +2 -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 +55 -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__/user-plugin-loader.test.ts +1 -1
- package/src/__tests__/visible-app-context.test.ts +16 -9
- 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/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/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 +9 -2
- 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/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 +82 -11
- 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 +9 -6
- package/src/daemon/conversation-runtime-assembly.ts +3 -4
- package/src/daemon/conversation-tool-setup.ts +1 -2
- package/src/daemon/conversation.ts +48 -0
- package/src/daemon/mcp-reload-service.ts +36 -6
- package/src/daemon/process-message.ts +13 -3
- 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/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-intents.ts +26 -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/workflows/run-workflow.ts +1 -2
- package/src/tts/__tests__/reasoning-tag-filter.test.ts +63 -0
- package/src/tts/reasoning-tag-filter.ts +110 -0
- package/src/workspace/byok-default-profile-ensure.ts +61 -18
- 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/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +8 -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
|
@@ -25,6 +25,7 @@ import type {
|
|
|
25
25
|
MemoryEvalTallyResult,
|
|
26
26
|
} from "../../../plugins/defaults/memory/src/memory-eval-routes.js";
|
|
27
27
|
import type {
|
|
28
|
+
GateStatsResponse,
|
|
28
29
|
MemoryV3BackfillSectionsResult,
|
|
29
30
|
MemoryV3RebuildIndexResult,
|
|
30
31
|
} from "../../../plugins/defaults/memory/src/memory-v3-routes.js";
|
|
@@ -52,6 +53,36 @@ function collectRepeatable(value: string, acc: string[]): string[] {
|
|
|
52
53
|
return [...acc, value];
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
/** Render gate-stats as a human-readable table. */
|
|
57
|
+
function formatGateStats(r: GateStatsResponse): string {
|
|
58
|
+
const lines: string[] = [
|
|
59
|
+
`Gate-stats (last ${r.lookbackDays} day${r.lookbackDays === 1 ? "" : "s"}, ${r.totalRuns} run${r.totalRuns === 1 ? "" : "s"})`,
|
|
60
|
+
"",
|
|
61
|
+
`Bucket Total Scored Passed ScoredPassRate Top reasons`,
|
|
62
|
+
];
|
|
63
|
+
for (const b of r.buckets) {
|
|
64
|
+
const rate =
|
|
65
|
+
b.scoredPassRate !== null
|
|
66
|
+
? `${(b.scoredPassRate * 100).toFixed(1)}%`
|
|
67
|
+
: "n/a";
|
|
68
|
+
const topReasons = Object.entries(b.reasons)
|
|
69
|
+
.sort(([, a], [, bv]) => (bv as number) - (a as number))
|
|
70
|
+
.slice(0, 3)
|
|
71
|
+
.map(([k, v]) => `${k}: ${v}`)
|
|
72
|
+
.join(", ");
|
|
73
|
+
lines.push(
|
|
74
|
+
`${b.pageCountRange.padEnd(10)} ${String(b.total).padStart(5)} ${String(b.scored).padStart(6)} ${String(b.passed).padStart(6)} ${rate.padStart(14)} ${topReasons}`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
if (r.unknownPageCount.total > 0) {
|
|
78
|
+
lines.push("");
|
|
79
|
+
lines.push(
|
|
80
|
+
`Unknown page count: ${r.unknownPageCount.total} total, ${r.unknownPageCount.passed} passed`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return lines.join("\n");
|
|
84
|
+
}
|
|
85
|
+
|
|
55
86
|
/**
|
|
56
87
|
* Read the `turn` ids from a prior run's `key.json` or `packets.json` (both are
|
|
57
88
|
* arrays of objects carrying a `turn` field). Used to pin `--turns-file` so a
|
|
@@ -271,4 +302,37 @@ export function registerMemoryV3Command(memory: Command): void {
|
|
|
271
302
|
log.info(formatTally(payload));
|
|
272
303
|
},
|
|
273
304
|
);
|
|
305
|
+
|
|
306
|
+
// ── gate-stats ────────────────────────────────────────────────────────
|
|
307
|
+
// Reads the telemetry DB directly — no daemon required.
|
|
308
|
+
|
|
309
|
+
subcommand(v3, "gate-stats").action(
|
|
310
|
+
async (opts: { lookbackDays: string; json?: boolean }) => {
|
|
311
|
+
const lookbackDays = Number(opts.lookbackDays);
|
|
312
|
+
const [{ handleMemoryV3GateStats }, { getTelemetrySqlite }] =
|
|
313
|
+
await Promise.all([
|
|
314
|
+
import("../../../plugins/defaults/memory/src/memory-v3-routes.js"),
|
|
315
|
+
import("../../../persistence/db-connection.js"),
|
|
316
|
+
]);
|
|
317
|
+
let payload;
|
|
318
|
+
try {
|
|
319
|
+
payload = handleMemoryV3GateStats(
|
|
320
|
+
Number.isFinite(lookbackDays) ? lookbackDays : 30,
|
|
321
|
+
getTelemetrySqlite(),
|
|
322
|
+
);
|
|
323
|
+
} catch (err) {
|
|
324
|
+
log.error(
|
|
325
|
+
"Failed to read gate stats: %s",
|
|
326
|
+
err instanceof Error ? err.message : String(err),
|
|
327
|
+
);
|
|
328
|
+
process.exitCode = 1;
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (opts.json === true) {
|
|
332
|
+
log.info(JSON.stringify(payload, null, 2));
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
log.info(formatGateStats(payload));
|
|
336
|
+
},
|
|
337
|
+
);
|
|
274
338
|
}
|
|
@@ -1,7 +1,31 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Declarative help for the `assistant stt` command.
|
|
3
|
+
*
|
|
4
|
+
* The advertised provider list is derived from the STT provider catalog, the
|
|
5
|
+
* single source of truth for which providers exist and which boundaries they
|
|
6
|
+
* serve, so the help cannot drift from what the daemon actually accepts.
|
|
7
|
+
*/
|
|
2
8
|
|
|
9
|
+
// provider-catalog is an execution-free data leaf (a literal map behind
|
|
10
|
+
// type-only imports, no adapter or credential graph), consumed synchronously
|
|
11
|
+
// to derive this module's constant. Pure data from pure data, so a lazy
|
|
12
|
+
// `import()` has nothing to defer.
|
|
13
|
+
// eslint-disable-next-line cli/no-daemon-internals
|
|
14
|
+
import {
|
|
15
|
+
listProviderIds,
|
|
16
|
+
supportsBoundary,
|
|
17
|
+
} from "../../providers/speech-to-text/provider-catalog.js";
|
|
3
18
|
import type { CliCommandHelp } from "../lib/cli-command-help.js";
|
|
4
19
|
|
|
20
|
+
/**
|
|
21
|
+
* `stt transcribe` runs the `daemon-batch` boundary, so streaming-only
|
|
22
|
+
* providers are omitted: naming one would advertise a configuration that
|
|
23
|
+
* fails on every file.
|
|
24
|
+
*/
|
|
25
|
+
const batchProviders = listProviderIds()
|
|
26
|
+
.filter((id) => supportsBoundary(id, "daemon-batch"))
|
|
27
|
+
.join(", ");
|
|
28
|
+
|
|
5
29
|
export const sttHelp: CliCommandHelp = {
|
|
6
30
|
name: "stt",
|
|
7
31
|
description: "Speech-to-text operations",
|
|
@@ -11,7 +35,8 @@ audio and video files. The provider is set via:
|
|
|
11
35
|
|
|
12
36
|
$ assistant config set services.stt.provider <provider>
|
|
13
37
|
|
|
14
|
-
Supported providers:
|
|
38
|
+
Supported providers: ${batchProviders}.
|
|
39
|
+
Streaming-only providers serve live speech and cannot transcribe files.
|
|
15
40
|
|
|
16
41
|
Examples:
|
|
17
42
|
$ assistant stt transcribe --file /path/to/meeting.wav
|
|
@@ -32,6 +32,7 @@ import { computeFingerprint } from "../plugin-fingerprint.js";
|
|
|
32
32
|
import { PluginNotInstalledError } from "../uninstall-plugin.js";
|
|
33
33
|
import {
|
|
34
34
|
PluginMergeBaselineError,
|
|
35
|
+
PluginNotCuratedError,
|
|
35
36
|
PluginNotUpgradableError,
|
|
36
37
|
upgradePlugin,
|
|
37
38
|
} from "../upgrade-plugin.js";
|
|
@@ -574,6 +575,44 @@ describe("upgradePlugin — direct GitHub-URL installs", () => {
|
|
|
574
575
|
expect(calls[0]?.[0]).toBe("ls-remote");
|
|
575
576
|
});
|
|
576
577
|
|
|
578
|
+
test("marketplaceOnly refuses the direct path instead of following the ref", async () => {
|
|
579
|
+
// GIVEN a direct install tracking `main` at SHA_A, absent from the
|
|
580
|
+
// marketplace, whose branch has advanced to SHA_B
|
|
581
|
+
installCopy(pluginsDir, "level-up", { commit: SHA_A, ref: "main" });
|
|
582
|
+
const fetch = makeFetch({ manifest: undefined });
|
|
583
|
+
const calls: string[][] = [];
|
|
584
|
+
const runGit = directGitRunner({ main: SHA_B }, SHA_B, { calls });
|
|
585
|
+
|
|
586
|
+
// WHEN a caller that only accepts a curated pin asks for the upgrade
|
|
587
|
+
const upgrade = upgradePlugin(
|
|
588
|
+
{ name: "level-up", marketplaceOnly: true },
|
|
589
|
+
{ fetch, runGit, workspacePluginsDir: pluginsDir },
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
// THEN it is refused, and the mutable ref is never even resolved
|
|
593
|
+
await expect(upgrade).rejects.toThrow(PluginNotCuratedError);
|
|
594
|
+
expect(calls).toEqual([]);
|
|
595
|
+
// AND the install stays exactly where it was
|
|
596
|
+
expect(sidecarCommit(pluginsDir, "level-up")).toBe(SHA_A);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
test("marketplaceOnly still upgrades a plugin the catalog claims", async () => {
|
|
600
|
+
// GIVEN an install the marketplace pins at SHA_B
|
|
601
|
+
installCopy(pluginsDir, "level-up", { commit: SHA_A });
|
|
602
|
+
const fetch = makeFetch({ manifest: manifestWith("level-up", SHA_B) });
|
|
603
|
+
const runGit = fakeGitRunner(SHA_B);
|
|
604
|
+
|
|
605
|
+
// WHEN the same curated-only caller asks for the upgrade
|
|
606
|
+
const result = await upgradePlugin(
|
|
607
|
+
{ name: "level-up", marketplaceOnly: true },
|
|
608
|
+
{ fetch, runGit, workspacePluginsDir: pluginsDir },
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
// THEN the flag is inert: the curated pin is taken as usual
|
|
612
|
+
expect(result.outcome).toBe("upgraded");
|
|
613
|
+
expect(result.toCommit).toBe(SHA_B);
|
|
614
|
+
});
|
|
615
|
+
|
|
577
616
|
test("is a no-op when the recorded branch still points at the installed commit", async () => {
|
|
578
617
|
// GIVEN a direct install tracking `main` at SHA_A
|
|
579
618
|
installCopy(pluginsDir, "level-up", { commit: SHA_A, ref: "main" });
|
|
@@ -422,6 +422,19 @@
|
|
|
422
422
|
"license": "MIT",
|
|
423
423
|
"icon": "✈️"
|
|
424
424
|
},
|
|
425
|
+
{
|
|
426
|
+
"name": "unabyss",
|
|
427
|
+
"source": {
|
|
428
|
+
"source": "github",
|
|
429
|
+
"repo": "Unabyss/unabyss-vellum",
|
|
430
|
+
"ref": "9f0bb0753dad09dace8578e242961bc2da912af3"
|
|
431
|
+
},
|
|
432
|
+
"description": "Personal and company context from Unabyss, available to your agent through the Unabyss MCP server.",
|
|
433
|
+
"category": "memory",
|
|
434
|
+
"homepage": "https://unabyss.com",
|
|
435
|
+
"license": "MIT",
|
|
436
|
+
"icon": "🌊"
|
|
437
|
+
},
|
|
425
438
|
{
|
|
426
439
|
"name": "vellum-client-qa",
|
|
427
440
|
"source": {
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
* verbatim, with no curated adapter overlay, exactly as the original untrusted
|
|
18
18
|
* install was (see {@link directUpgrade}).
|
|
19
19
|
*
|
|
20
|
+
* Because that target is a mutable ref, taking it means running whatever
|
|
21
|
+
* upstream pushed since, which is a choice only a human should make. A caller
|
|
22
|
+
* with nobody in the loop passes `marketplaceOnly` to rule it out: the direct
|
|
23
|
+
* path then throws {@link PluginNotCuratedError} instead of advancing.
|
|
24
|
+
*
|
|
20
25
|
* This is deliberately a distinct operation from install: `install` is
|
|
21
26
|
* first-time materialization (and errors on an existing install unless
|
|
22
27
|
* `--force` is passed), whereas `upgrade` moves an existing install forward.
|
|
@@ -114,6 +119,20 @@ export interface UpgradePluginOptions {
|
|
|
114
119
|
* {@link DEFAULT_PLUGIN_UPGRADE_STRATEGY}.
|
|
115
120
|
*/
|
|
116
121
|
readonly strategy?: PluginUpgradeStrategy;
|
|
122
|
+
/**
|
|
123
|
+
* Refuse to advance an install the marketplace does not claim, instead of
|
|
124
|
+
* falling back to {@link directUpgrade}.
|
|
125
|
+
*
|
|
126
|
+
* A direct install's upgrade target is a mutable upstream ref, so taking it
|
|
127
|
+
* means fetching and executing whatever was pushed since. Callers with
|
|
128
|
+
* nobody in the loop (the unattended auto-update sweep) set this so that
|
|
129
|
+
* outcome is impossible for them, and it is enforced here rather than by the
|
|
130
|
+
* caller's own pre-check: the catalog is fetched again inside this call, so
|
|
131
|
+
* an entry that disappears in between would otherwise silently reroute a
|
|
132
|
+
* curated upgrade into a direct one. Defaults to false, which is what an
|
|
133
|
+
* interactive `assistant plugins upgrade` wants.
|
|
134
|
+
*/
|
|
135
|
+
readonly marketplaceOnly?: boolean;
|
|
117
136
|
}
|
|
118
137
|
|
|
119
138
|
/** Dependencies injected by the caller. */
|
|
@@ -208,6 +227,23 @@ export class PluginNotUpgradableError extends Error {
|
|
|
208
227
|
}
|
|
209
228
|
}
|
|
210
229
|
|
|
230
|
+
/**
|
|
231
|
+
* A `marketplaceOnly` upgrade was requested for an install the marketplace does
|
|
232
|
+
* not claim, so the only revision available is a mutable upstream ref the
|
|
233
|
+
* caller refuses to take.
|
|
234
|
+
*
|
|
235
|
+
* Distinct from {@link PluginNotUpgradableError}: the install *can* be
|
|
236
|
+
* upgraded, just not without a human choosing to accept unreviewed code.
|
|
237
|
+
*/
|
|
238
|
+
export class PluginNotCuratedError extends Error {
|
|
239
|
+
constructor(readonly pluginName: string) {
|
|
240
|
+
super(
|
|
241
|
+
`Plugin "${pluginName}" cannot be upgraded automatically: it has no marketplace entry, so its only upgrade target is a mutable upstream ref. Run 'assistant plugins upgrade ${pluginName}' to move it deliberately.`,
|
|
242
|
+
);
|
|
243
|
+
this.name = "PluginNotCuratedError";
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
211
247
|
/**
|
|
212
248
|
* A merge strategy (`ours`/`theirs`) was requested but the install-time
|
|
213
249
|
* baseline needed for a three-way merge cannot be reconstructed.
|
|
@@ -244,6 +280,8 @@ function pluginTarget(name: string, deps: UpgradePluginDeps): string {
|
|
|
244
280
|
* Throws {@link PluginNotInstalledError} when no copy is installed,
|
|
245
281
|
* {@link PluginNotUpgradableError} when the install is neither in the
|
|
246
282
|
* marketplace nor carries a recorded GitHub source to advance,
|
|
283
|
+
* {@link PluginNotCuratedError} when `marketplaceOnly` was requested for an
|
|
284
|
+
* install the marketplace does not claim,
|
|
247
285
|
* {@link PluginMergeBaselineError} when a merge strategy is requested but the
|
|
248
286
|
* install-time baseline cannot be reconstructed,
|
|
249
287
|
* {@link PluginSourceUnavailableError} when the marketplace catalog or the
|
|
@@ -302,6 +340,10 @@ export async function upgradePlugin(
|
|
|
302
340
|
"it has no marketplace entry and no installed copy to upgrade",
|
|
303
341
|
);
|
|
304
342
|
}
|
|
343
|
+
if (opts.marketplaceOnly) {
|
|
344
|
+
// The caller only accepts a curated pin, and this install has none.
|
|
345
|
+
throw new PluginNotCuratedError(name);
|
|
346
|
+
}
|
|
305
347
|
return directUpgrade({ name, local, dryRun, strategy }, deps);
|
|
306
348
|
}
|
|
307
349
|
case "remote-unavailable":
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
CODE_DEFAULT_PROFILE_ENTRIES,
|
|
8
8
|
getEffectiveProfile,
|
|
9
9
|
getEffectiveProfiles,
|
|
10
|
+
getEffectiveProfilesForProvider,
|
|
10
11
|
PROFILE_IMPLS,
|
|
11
12
|
resolveDefaultProfileForProvider,
|
|
12
13
|
} from "../default-profile-catalog.js";
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
import {
|
|
23
24
|
type DefaultProviderConfig,
|
|
24
25
|
type LLMCallSite,
|
|
26
|
+
LLMCallSiteEnum,
|
|
25
27
|
LLMSchema,
|
|
26
28
|
type ProfileEntry,
|
|
27
29
|
} from "../schemas/llm.js";
|
|
@@ -245,7 +247,10 @@ describe("resolver integration", () => {
|
|
|
245
247
|
},
|
|
246
248
|
});
|
|
247
249
|
const body = CODE_DEFAULT_PROFILE_ENTRIES["latency-optimized"]!;
|
|
248
|
-
for (const callSite of [
|
|
250
|
+
for (const callSite of [
|
|
251
|
+
"voiceFrontDoor",
|
|
252
|
+
"voiceProgressNarration",
|
|
253
|
+
] as const) {
|
|
249
254
|
const resolved = resolveCallSiteConfig(callSite, llm);
|
|
250
255
|
expect(resolved.model).toBe(body.model as string);
|
|
251
256
|
expect(resolved.model).not.toBe("claude-opus-4-6");
|
|
@@ -265,6 +270,12 @@ describe("resolver integration", () => {
|
|
|
265
270
|
});
|
|
266
271
|
|
|
267
272
|
describe("schema validation", () => {
|
|
273
|
+
test("voiceFrontDoor is the only front callsite", () => {
|
|
274
|
+
expect(
|
|
275
|
+
LLMCallSiteEnum.options.filter((callSite) => callSite.includes("Front")),
|
|
276
|
+
).toEqual(["voiceFrontDoor"]);
|
|
277
|
+
});
|
|
278
|
+
|
|
268
279
|
test("always-available default names are valid references; os-beta only when materialized", () => {
|
|
269
280
|
expect(() => LLMSchema.parse({ activeProfile: "balanced" })).not.toThrow();
|
|
270
281
|
expect(() =>
|
|
@@ -323,7 +334,7 @@ describe("resolveDefaultProfileForProvider", () => {
|
|
|
323
334
|
expect(typeof entry?.model).toBe("string");
|
|
324
335
|
expect(entry?.provider).toBeDefined();
|
|
325
336
|
// Identity columns stamp no connection; BYOK columns always do.
|
|
326
|
-
if (entry?.provider === "vellum") {
|
|
337
|
+
if (entry?.provider === "vellum" || entry?.provider === "chatgpt") {
|
|
327
338
|
expect(entry?.provider_connection).toBeUndefined();
|
|
328
339
|
} else {
|
|
329
340
|
expect(entry?.provider_connection).toBeDefined();
|
|
@@ -333,6 +344,27 @@ describe("resolveDefaultProfileForProvider", () => {
|
|
|
333
344
|
}
|
|
334
345
|
});
|
|
335
346
|
|
|
347
|
+
test("the chatgpt column resolves Codex-pinned models with no connection stamp", () => {
|
|
348
|
+
const byKey: Record<string, string> = {
|
|
349
|
+
balanced: "gpt-5.6-luna",
|
|
350
|
+
"quality-optimized": "gpt-5.6-sol",
|
|
351
|
+
"cost-optimized": "gpt-5.6-luna",
|
|
352
|
+
"latency-optimized": "gpt-5.6-luna",
|
|
353
|
+
};
|
|
354
|
+
const effective = getEffectiveProfilesForProvider(undefined, dp("chatgpt"));
|
|
355
|
+
for (const [key, model] of Object.entries(byKey)) {
|
|
356
|
+
const entry = effective[key];
|
|
357
|
+
expect(entry?.provider).toBe("chatgpt");
|
|
358
|
+
expect(entry?.model).toBe(model);
|
|
359
|
+
expect(entry?.provider_connection).toBeUndefined();
|
|
360
|
+
expect(entry?.source).toBe("managed");
|
|
361
|
+
}
|
|
362
|
+
// Cost and Speed both opt fully out of reasoning.
|
|
363
|
+
expect(effective["cost-optimized"]?.effort).toBe("none");
|
|
364
|
+
expect(effective["latency-optimized"]?.effort).toBe("none");
|
|
365
|
+
expect(effective.balanced?.thinking?.enabled).toBe(true);
|
|
366
|
+
});
|
|
367
|
+
|
|
336
368
|
test("a provider without a named matrix column materializes from the shared BYOK templates", () => {
|
|
337
369
|
const entry = resolveDefaultProfileForProvider(
|
|
338
370
|
undefined,
|
|
@@ -157,6 +157,12 @@ describe("resolveDefaultConnectionName", () => {
|
|
|
157
157
|
);
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
+
test("chatgpt resolves to the canonical subscription connection name", () => {
|
|
161
|
+
expect(resolveDefaultConnectionName({ provider: "chatgpt" })).toBe(
|
|
162
|
+
"chatgpt-subscription",
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
|
|
160
166
|
test("every other provider resolves to its personal connection", () => {
|
|
161
167
|
expect(resolveDefaultConnectionName({ provider: "anthropic" })).toBe(
|
|
162
168
|
"anthropic-personal",
|
|
@@ -221,7 +227,6 @@ describe("getDefaultProvider / setDefaultProvider", () => {
|
|
|
221
227
|
test("setDefaultProvider validates the provider before writing", () => {
|
|
222
228
|
expect(() =>
|
|
223
229
|
setDefaultProvider({
|
|
224
|
-
// @ts-expect-error deliberately invalid for the test
|
|
225
230
|
provider: "not-a-provider",
|
|
226
231
|
}),
|
|
227
232
|
).toThrow();
|
|
@@ -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
|
},
|