@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
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { Database } from "bun:sqlite";
|
|
4
|
+
|
|
5
|
+
import { getLogger } from "../../util/logger.js";
|
|
6
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
7
|
+
|
|
8
|
+
const log = getLogger("migrations/145-collapse-profile-bindings-to-entries");
|
|
9
|
+
|
|
10
|
+
// Fold each profile's `provider_connection` into its `provider` as a
|
|
11
|
+
// connection entry name. Under the entries model the provider field holds
|
|
12
|
+
// the entry name and the row's own provider (its kind) drives dispatch, so
|
|
13
|
+
// an explicit binding needs no second field.
|
|
14
|
+
//
|
|
15
|
+
// Rules per `llm.profiles.<key>`:
|
|
16
|
+
// - Routing-identity providers ("vellum"/"chatgpt") carry no binding by
|
|
17
|
+
// definition: a stray `provider_connection` is deleted verbatim.
|
|
18
|
+
// - A binding whose row EXISTS and whose kind AGREES with the profile's
|
|
19
|
+
// declared provider becomes the provider value (the entry name), and the
|
|
20
|
+
// binding field is deleted. Kind agreement, with the identity kinds
|
|
21
|
+
// mapped the way dispatch maps them: a row of the same provider, a
|
|
22
|
+
// "chatgpt" row for a declared "openai", or a "vellum" row for a declared
|
|
23
|
+
// managed-routable provider.
|
|
24
|
+
// - A dangling binding (row deleted) or a kind-disagreeing binding keeps
|
|
25
|
+
// the declared provider and deletes only the binding, with a structured
|
|
26
|
+
// warn: dispatch's auto-resolution then behaves the way it always has
|
|
27
|
+
// for an unbound vendor profile, and a rewrite would either strand the
|
|
28
|
+
// profile on a nonexistent entry or silently change vendors.
|
|
29
|
+
// - A binding equal to the declared provider (conventional shapes older
|
|
30
|
+
// migrations kept) is a plain delete: the bare vendor value already
|
|
31
|
+
// means the default entry of that kind.
|
|
32
|
+
//
|
|
33
|
+
// Call-site fragments and `llm.defaultProvider.connectionName` are NOT
|
|
34
|
+
// touched: call-site overrides become model-only together with the
|
|
35
|
+
// resolver's tweak blocks (one coherent change, later), and the default
|
|
36
|
+
// provider keeps its closed vendor enum, so its binding needs its own
|
|
37
|
+
// design before it can collapse.
|
|
38
|
+
//
|
|
39
|
+
// The identity mapping and managed-routable set are frozen snapshots
|
|
40
|
+
// (migrations are self-contained): openai/anthropic/gemini/fireworks/
|
|
41
|
+
// together as of 2026-08-10. A provider outside the snapshot takes the
|
|
42
|
+
// conservative keep-provider path.
|
|
43
|
+
|
|
44
|
+
const ROUTING_IDENTITIES = new Set(["vellum", "chatgpt"]);
|
|
45
|
+
const MANAGED_ROUTABLE = new Set([
|
|
46
|
+
"openai",
|
|
47
|
+
"anthropic",
|
|
48
|
+
"gemini",
|
|
49
|
+
"fireworks",
|
|
50
|
+
"together",
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
// A binding literally named "vellum" or "chatgpt" folds into the ROUTING
|
|
54
|
+
// IDENTITY value, whose model the read-path schema validates (an unroutable
|
|
55
|
+
// pair strips the whole profile on read). Folding is therefore gated on the
|
|
56
|
+
// model being servable by that identity, judged against frozen snapshots of
|
|
57
|
+
// the routing tables as of 2026-08-10; a model outside the snapshot leaves
|
|
58
|
+
// the profile untouched (dispatch keeps honoring the legacy binding).
|
|
59
|
+
const VELLUM_ROUTABLE_MODELS = new Set([
|
|
60
|
+
"claude-fable-5",
|
|
61
|
+
"claude-opus-5",
|
|
62
|
+
"claude-opus-4-8",
|
|
63
|
+
"claude-opus-4-7",
|
|
64
|
+
"claude-opus-4-6",
|
|
65
|
+
"claude-sonnet-5",
|
|
66
|
+
"claude-sonnet-4-6",
|
|
67
|
+
"claude-sonnet-4-5-20250929",
|
|
68
|
+
"claude-opus-4-5-20251101",
|
|
69
|
+
"claude-haiku-4-5-20251001",
|
|
70
|
+
"gpt-5.6-sol",
|
|
71
|
+
"gpt-5.6-terra",
|
|
72
|
+
"gpt-5.6-luna",
|
|
73
|
+
"gpt-5.5",
|
|
74
|
+
"gpt-5.5-pro",
|
|
75
|
+
"gpt-5.4",
|
|
76
|
+
"gpt-5.2",
|
|
77
|
+
"gpt-5.4-mini",
|
|
78
|
+
"gpt-5.4-nano",
|
|
79
|
+
"gemini-3.6-flash",
|
|
80
|
+
"gemini-3.5-flash",
|
|
81
|
+
"gemini-3.5-flash-lite",
|
|
82
|
+
"gemini-3.1-pro-preview",
|
|
83
|
+
"gemini-3.1-pro-preview-customtools",
|
|
84
|
+
"gemini-3-flash-preview",
|
|
85
|
+
"gemini-3.1-flash-lite-preview",
|
|
86
|
+
"gemini-3.1-flash-lite",
|
|
87
|
+
"gemini-2.5-flash",
|
|
88
|
+
"gemini-2.5-flash-lite",
|
|
89
|
+
"gemini-2.5-pro",
|
|
90
|
+
"accounts/fireworks/models/kimi-k3",
|
|
91
|
+
"accounts/fireworks/models/kimi-k2p6",
|
|
92
|
+
"accounts/fireworks/models/glm-5p2",
|
|
93
|
+
"accounts/fireworks/models/minimax-m3",
|
|
94
|
+
"accounts/fireworks/models/minimax-m2p7",
|
|
95
|
+
"accounts/fireworks/models/deepseek-v4-pro",
|
|
96
|
+
"accounts/fireworks/models/deepseek-v4-flash",
|
|
97
|
+
"MiniMaxAI/MiniMax-M3",
|
|
98
|
+
]);
|
|
99
|
+
const CODEX_MODELS = new Set([
|
|
100
|
+
"gpt-5.6-sol",
|
|
101
|
+
"gpt-5.6-terra",
|
|
102
|
+
"gpt-5.6-luna",
|
|
103
|
+
"gpt-5.5",
|
|
104
|
+
"gpt-5.4",
|
|
105
|
+
"gpt-5.4-mini",
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
function identitySafeModel(identity: string, model: unknown): boolean {
|
|
109
|
+
if (typeof model !== "string") {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
return identity === "vellum"
|
|
113
|
+
? VELLUM_ROUTABLE_MODELS.has(model)
|
|
114
|
+
: CODEX_MODELS.has(model);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const collapseProfileBindingsToEntriesMigration: WorkspaceMigration = {
|
|
118
|
+
id: "145-collapse-profile-bindings-to-entries",
|
|
119
|
+
description:
|
|
120
|
+
"Fold llm.profiles.*.provider_connection into provider as connection entry names",
|
|
121
|
+
retryFailedCheckpoint: true,
|
|
122
|
+
run(workspaceDir: string): void {
|
|
123
|
+
const configPath = join(workspaceDir, "config.json");
|
|
124
|
+
if (!existsSync(configPath)) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let config: Record<string, unknown>;
|
|
129
|
+
try {
|
|
130
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
131
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
config = raw as Record<string, unknown>;
|
|
135
|
+
} catch {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const llm = asObject(config.llm);
|
|
140
|
+
const profiles = llm === null ? null : asObject(llm.profiles);
|
|
141
|
+
if (profiles === null) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const bindings = Object.values(profiles).some((value) => {
|
|
146
|
+
const entry = asObject(value);
|
|
147
|
+
return (
|
|
148
|
+
entry !== null &&
|
|
149
|
+
typeof entry.provider_connection === "string" &&
|
|
150
|
+
entry.provider_connection.length > 0
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
if (!bindings) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Bindings exist, so the rewrite needs the rows. An ABSENT DB file is a
|
|
158
|
+
// real state (a config restored into a fresh workspace has no rows, so
|
|
159
|
+
// every binding is genuinely dangling); an unreadable or unqueryable DB
|
|
160
|
+
// must instead fail the run (retried next boot) rather than checkpoint
|
|
161
|
+
// a pass that would take the destructive dangling path for every
|
|
162
|
+
// binding.
|
|
163
|
+
const rows = readConnectionRows(workspaceDir);
|
|
164
|
+
if (rows === null) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
"provider_connections is not readable; retrying the binding collapse on the next run",
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
let changed = false;
|
|
171
|
+
for (const [key, value] of Object.entries(profiles)) {
|
|
172
|
+
const entry = asObject(value);
|
|
173
|
+
if (entry === null) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const binding = entry.provider_connection;
|
|
177
|
+
if (typeof binding !== "string" || binding.length === 0) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const provider =
|
|
181
|
+
typeof entry.provider === "string" ? entry.provider : undefined;
|
|
182
|
+
|
|
183
|
+
if (provider !== undefined && ROUTING_IDENTITIES.has(provider)) {
|
|
184
|
+
delete entry.provider_connection;
|
|
185
|
+
changed = true;
|
|
186
|
+
log.info(
|
|
187
|
+
{ profile: key, provider, binding },
|
|
188
|
+
"Deleted stray binding on a routing-identity profile",
|
|
189
|
+
);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (binding === provider) {
|
|
194
|
+
// A row named exactly like its vendor was an explicit pin under the
|
|
195
|
+
// old create route; with several rows of that kind, the bare vendor
|
|
196
|
+
// would auto-resolve differently, so the pin stays until the entry
|
|
197
|
+
// picker can express it. Without such a row the field is a plain
|
|
198
|
+
// conventional shape and the bare vendor already means the default.
|
|
199
|
+
if (rows.has(binding)) {
|
|
200
|
+
log.info(
|
|
201
|
+
{ profile: key, provider },
|
|
202
|
+
"Kept self-named binding (an explicit pin among possible siblings)",
|
|
203
|
+
);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
delete entry.provider_connection;
|
|
207
|
+
changed = true;
|
|
208
|
+
log.info(
|
|
209
|
+
{ profile: key, provider },
|
|
210
|
+
"Deleted self-referential binding (bare vendor means the default entry)",
|
|
211
|
+
);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (ROUTING_IDENTITIES.has(binding)) {
|
|
216
|
+
// Folding writes the identity VALUE itself; gate on the model the
|
|
217
|
+
// identity can serve, or the read-path schema would strip the
|
|
218
|
+
// profile. An unservable model leaves the profile untouched.
|
|
219
|
+
if (identitySafeModel(binding, entry.model)) {
|
|
220
|
+
entry.provider = binding;
|
|
221
|
+
delete entry.provider_connection;
|
|
222
|
+
changed = true;
|
|
223
|
+
log.info(
|
|
224
|
+
{ profile: key, previousProvider: provider, identity: binding },
|
|
225
|
+
"Folded canonical binding into the routing identity",
|
|
226
|
+
);
|
|
227
|
+
} else {
|
|
228
|
+
log.warn(
|
|
229
|
+
{ profile: key, provider, binding, reason: "model_not_servable" },
|
|
230
|
+
"Left canonical binding in place; the identity cannot serve this model",
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const rowKind = rows.get(binding);
|
|
237
|
+
if (rowKind === undefined) {
|
|
238
|
+
delete entry.provider_connection;
|
|
239
|
+
changed = true;
|
|
240
|
+
log.warn(
|
|
241
|
+
{ profile: key, provider, binding, reason: "row_missing" },
|
|
242
|
+
"Deleted dangling binding; profile keeps its declared provider",
|
|
243
|
+
);
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (!kindAgrees(rowKind, provider)) {
|
|
248
|
+
delete entry.provider_connection;
|
|
249
|
+
changed = true;
|
|
250
|
+
log.warn(
|
|
251
|
+
{ profile: key, provider, binding, rowKind, reason: "kind_mismatch" },
|
|
252
|
+
"Deleted kind-disagreeing binding; profile keeps its declared provider",
|
|
253
|
+
);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
entry.provider = binding;
|
|
258
|
+
delete entry.provider_connection;
|
|
259
|
+
changed = true;
|
|
260
|
+
log.info(
|
|
261
|
+
{ profile: key, previousProvider: provider, entry: binding },
|
|
262
|
+
"Folded profile binding into provider as an entry name",
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (changed) {
|
|
267
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
down(_workspaceDir: string): void {
|
|
271
|
+
// Forward-only.
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
function asObject(value: unknown): Record<string, unknown> | null {
|
|
276
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
277
|
+
? (value as Record<string, unknown>)
|
|
278
|
+
: null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Whether a row of `rowKind` can serve a profile declaring `provider`,
|
|
283
|
+
* mirroring dispatch's identity mapping. An undeclared provider agrees with
|
|
284
|
+
* any row: the entry alone defines the route.
|
|
285
|
+
*/
|
|
286
|
+
function kindAgrees(rowKind: string, provider: string | undefined): boolean {
|
|
287
|
+
if (provider === undefined) {
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
if (rowKind === provider) {
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
if (rowKind === "chatgpt") {
|
|
294
|
+
return provider === "openai";
|
|
295
|
+
}
|
|
296
|
+
if (rowKind === "vellum") {
|
|
297
|
+
return MANAGED_ROUTABLE.has(provider);
|
|
298
|
+
}
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Connection name -> provider kind, or null when the DB or table is not
|
|
304
|
+
* readable. The caller fails the run on null: bindings must be judged
|
|
305
|
+
* against real rows, never guessed.
|
|
306
|
+
*/
|
|
307
|
+
function readConnectionRows(workspaceDir: string): Map<string, string> | null {
|
|
308
|
+
const dbPath = join(workspaceDir, "data", "db", "assistant.db");
|
|
309
|
+
if (!existsSync(dbPath)) {
|
|
310
|
+
return new Map();
|
|
311
|
+
}
|
|
312
|
+
let db: Database;
|
|
313
|
+
try {
|
|
314
|
+
db = new Database(dbPath);
|
|
315
|
+
} catch {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
const rows = db
|
|
320
|
+
.query(`SELECT name, provider FROM provider_connections`)
|
|
321
|
+
.all() as Array<{ name: string; provider: string }>;
|
|
322
|
+
return new Map(rows.map((r) => [r.name, r.provider]));
|
|
323
|
+
} catch {
|
|
324
|
+
return null;
|
|
325
|
+
} finally {
|
|
326
|
+
db.close();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
@@ -10,7 +10,6 @@ import { join } from "node:path";
|
|
|
10
10
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
11
11
|
|
|
12
12
|
import { sttEnglishDefaultToMultilingualMigration as MIG } from "../141-stt-english-default-to-multilingual.js";
|
|
13
|
-
import { WORKSPACE_MIGRATIONS } from "../registry.js";
|
|
14
13
|
|
|
15
14
|
let workspaceDir: string;
|
|
16
15
|
let configPath: string;
|
|
@@ -37,15 +36,6 @@ afterEach(() => {
|
|
|
37
36
|
});
|
|
38
37
|
|
|
39
38
|
describe("141-stt-english-default-to-multilingual", () => {
|
|
40
|
-
test("is registered last, so it defines the registry ceiling", () => {
|
|
41
|
-
// getLastWorkspaceMigrationId() reads the final entry, and the runner
|
|
42
|
-
// executes the array in order, so a new migration belongs at the end.
|
|
43
|
-
expect(MIG.id).toBe("141-stt-english-default-to-multilingual");
|
|
44
|
-
expect(WORKSPACE_MIGRATIONS[WORKSPACE_MIGRATIONS.length - 1]?.id).toBe(
|
|
45
|
-
MIG.id,
|
|
46
|
-
);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
39
|
test("moves an accepted English default to multilingual on deepgram", () => {
|
|
50
40
|
write({ provider: "deepgram", language: "en" });
|
|
51
41
|
MIG.run(workspaceDir);
|
|
@@ -139,6 +139,10 @@ import { backfillHomeFeedTitlesMigration } from "./138-backfill-home-feed-titles
|
|
|
139
139
|
import { clearRenamedCostProfileLabelMigration } from "./139-clear-renamed-cost-profile-label.js";
|
|
140
140
|
import { repairSeedPinnedMemoryV3LiveMigration } from "./140-repair-seed-pinned-memory-v3-live.js";
|
|
141
141
|
import { sttEnglishDefaultToMultilingualMigration } from "./141-stt-english-default-to-multilingual.js";
|
|
142
|
+
import { consolidateVoiceFrontDoorMigration } from "./142-consolidate-voice-front-door.js";
|
|
143
|
+
import { repairDeprecatedCodexModelIdMigration } from "./143-repair-deprecated-codex-model-id.js";
|
|
144
|
+
import { convertStrandedSubscriptionOpenaiProfilesMigration } from "./144-convert-stranded-subscription-openai-profiles.js";
|
|
145
|
+
import { collapseProfileBindingsToEntriesMigration } from "./145-collapse-profile-bindings-to-entries.js";
|
|
142
146
|
import { migrateToWorkspaceVolumeMigration } from "./migrate-to-workspace-volume.js";
|
|
143
147
|
import type { WorkspaceMigration } from "./types.js";
|
|
144
148
|
|
|
@@ -293,4 +297,8 @@ export const WORKSPACE_MIGRATIONS: WorkspaceMigration[] = [
|
|
|
293
297
|
clearRenamedCostProfileLabelMigration,
|
|
294
298
|
repairSeedPinnedMemoryV3LiveMigration,
|
|
295
299
|
sttEnglishDefaultToMultilingualMigration,
|
|
300
|
+
consolidateVoiceFrontDoorMigration,
|
|
301
|
+
repairDeprecatedCodexModelIdMigration,
|
|
302
|
+
convertStrandedSubscriptionOpenaiProfilesMigration,
|
|
303
|
+
collapseProfileBindingsToEntriesMigration,
|
|
296
304
|
];
|
|
@@ -162,13 +162,15 @@ class ProviderCommitMessageGenerator {
|
|
|
162
162
|
const providerName = resolved.configuredProviderName;
|
|
163
163
|
|
|
164
164
|
// Step 2b: API key preflight for the configured provider. Skipped for
|
|
165
|
-
// keyless providers
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
165
|
+
// keyless providers, for routing identities, and for entry-routed
|
|
166
|
+
// results, since all three authenticate through their connection row
|
|
167
|
+
// (platform token / ChatGPT OAuth / the row's own credential slot), so
|
|
168
|
+
// no generic per-vendor key needs to exist, and resolution already
|
|
169
|
+
// verified the connection credential.
|
|
169
170
|
if (
|
|
170
171
|
!KEYLESS_PROVIDERS.has(providerName) &&
|
|
171
|
-
!ROUTING_IDENTITY_PROVIDERS.has(providerName)
|
|
172
|
+
!ROUTING_IDENTITY_PROVIDERS.has(providerName) &&
|
|
173
|
+
!resolved.entryRouted
|
|
172
174
|
) {
|
|
173
175
|
const providerApiKey = await getProviderKeyAsync(providerName);
|
|
174
176
|
if (!providerApiKey) {
|