@mycadet/assistant 0.12.7 → 0.12.9
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/README.md +1 -1
- package/docs/activation-funnel-telemetry.md +6 -6
- package/docs/cadet-doctor.md +7 -7
- package/docs/desktop-browser-cli.md +7 -7
- package/docs/skills.md +2 -2
- package/docs/voice-input-diagnostics.md +1 -1
- package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +2 -2
- package/node_modules/@mycadet/avatar-manifest/src/__tests__/notification-avatar.test.ts +1 -1
- package/node_modules/@mycadet/avatar-manifest/src/notification-avatar.ts +2 -2
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/{platform-credential.ts → cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/node_modules/@mycadet/ces-client/package.json +1 -1
- package/node_modules/@mycadet/ces-client/src/__tests__/ces-client.test.ts +14 -14
- package/node_modules/@mycadet/ces-client/src/rpc-client.ts +1 -1
- package/node_modules/@mycadet/credential-storage/src/oauth-runtime.ts +1 -1
- package/node_modules/@mycadet/credential-storage/src/static-credentials.ts +1 -1
- package/node_modules/@mycadet/environments/src/seeds.ts +6 -6
- package/node_modules/@mycadet/environments/src/shell.test.ts +1 -1
- package/node_modules/@mycadet/environments/src/types.ts +6 -6
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/{platform-credential.ts → cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/node_modules/@mycadet/gateway-client/src/binding-strength-contract.ts +5 -5
- package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +1 -1
- package/node_modules/@mycadet/gateway-client/src/inbound-contract.ts +2 -2
- package/node_modules/@mycadet/gateway-client/src/outbound-contract.ts +1 -1
- package/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/service-contracts/src/{platform-credential.ts → cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/openapi.yaml +6804 -799
- package/package.json +2 -2
- package/scripts/build-test-fixtures.ts +1 -1
- package/scripts/probe-skill-retrieval.ts +1 -1
- package/scripts/sync-llm-catalog.ts +2 -2
- package/scripts/voice-ttft-spike.ts +2 -2
- package/src/__tests__/adaptive-thinking-repair.test.ts +4 -4
- package/src/__tests__/agent-loop-override-profile.test.ts +4 -4
- package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +1 -1
- package/src/__tests__/anthropic-provider.test.ts +2 -2
- package/src/__tests__/available-skills.test.ts +11 -12
- package/src/__tests__/avatar-identity-sync.test.ts +1 -1
- package/src/__tests__/byok-default-profile-ensure.test.ts +7 -7
- package/src/__tests__/call-domain.test.ts +1 -1
- package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +2 -2
- package/src/__tests__/catalog-files.test.ts +10 -10
- package/src/__tests__/catalog-install-normalize.test.ts +3 -3
- package/src/__tests__/channel-availability-routes.test.ts +8 -8
- package/src/__tests__/channel-readiness-routes.test.ts +4 -4
- package/src/__tests__/channel-readiness-service.test.ts +14 -14
- package/src/__tests__/channel-readiness-telegram-remote.test.ts +1 -1
- package/src/__tests__/chat-reveal-guard-priming.test.ts +5 -6
- package/src/__tests__/checker.test.ts +6 -6
- package/src/__tests__/clawhub.test.ts +1 -1
- package/src/__tests__/{platform-callback-registration.test.ts → cloud-callback-registration.test.ts} +66 -90
- package/src/__tests__/{platform-client-verify-credential.test.ts → cloud-client-verify-credential.test.ts} +14 -14
- package/src/__tests__/{managed-credential-catalog-cli.test.ts → cloud-credential-catalog-cli.test.ts} +41 -41
- package/src/__tests__/{managed-fallback-dispatch.test.ts → cloud-fallback-dispatch.test.ts} +21 -16
- package/src/__tests__/{managed-profile-guard.test.ts → cloud-profile-guard.test.ts} +77 -75
- package/src/__tests__/{platform-proxy-context.test.ts → cloud-proxy-context.test.ts} +44 -44
- package/src/__tests__/{managed-speech-defaults.test.ts → cloud-speech-defaults.test.ts} +41 -41
- package/src/__tests__/{managed-store.test.ts → cloud-store.test.ts} +99 -99
- package/src/__tests__/compaction-events.test.ts +7 -5
- package/src/__tests__/config-loader-backfill.test.ts +65 -65
- package/src/__tests__/config-loader-cloud-defaults.test.ts +373 -0
- package/src/__tests__/config-loader-corrupt.test.ts +1 -1
- package/src/__tests__/config-loader-validation-reset-notice.test.ts +1 -1
- package/src/__tests__/config-schema.test.ts +3 -3
- package/src/__tests__/{config-set-platform-guard.test.ts → config-set-cloud-guard.test.ts} +6 -6
- package/src/__tests__/config-set-route.test.ts +12 -8
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -8
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/contacts-write.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +2 -2
- package/src/__tests__/conversation-app-control-instantiation.test.ts +18 -18
- package/src/__tests__/conversation-error.test.ts +45 -45
- package/src/__tests__/conversation-inference-profile-route.test.ts +4 -4
- package/src/__tests__/conversation-rendered-delegation-state.test.ts +4 -4
- package/src/__tests__/conversation-skill-tools.test.ts +7 -8
- package/src/__tests__/conversation-slash.test.ts +2 -2
- package/src/__tests__/conversation-speed-override.test.ts +8 -10
- package/src/__tests__/conversation-summarize-up-to.test.ts +4 -4
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -1
- package/src/__tests__/conversation-wait-for-idle.test.ts +7 -5
- package/src/__tests__/credential-execution-api-key-propagation.test.ts +5 -5
- package/src/__tests__/{credential-execution-managed-contract.test.ts → credential-execution-cloud-contract.test.ts} +24 -24
- package/src/__tests__/credential-health-service.test.ts +28 -40
- package/src/__tests__/credential-routes.test.ts +85 -165
- package/src/__tests__/credential-security-invariants.test.ts +6 -7
- package/src/__tests__/custom-profile-ensure.test.ts +1 -1
- package/src/__tests__/date-context.test.ts +1 -1
- package/src/__tests__/default-profile-catalog-fallback.test.ts +15 -15
- package/src/__tests__/delete-managed-skill-tool.test.ts +8 -8
- package/src/__tests__/discord-callback-round-trip.test.ts +1 -1
- package/src/__tests__/disk-usage.test.ts +3 -3
- package/src/__tests__/email-invite-adapter.test.ts +5 -5
- package/src/__tests__/{embedding-managed-proxy-selection.test.ts → embedding-cloud-proxy-selection.test.ts} +44 -43
- package/src/__tests__/emit-signal-routing-intent.test.ts +13 -15
- package/src/__tests__/events-dev-bypass-actor.test.ts +2 -2
- package/src/__tests__/fallback-breaker.test.ts +7 -7
- package/src/__tests__/feature-flag-test-helpers.ts +106 -0
- package/src/__tests__/gateway-flag-listener.test.ts +1 -1
- package/src/__tests__/gemini-image-service.test.ts +2 -2
- package/src/__tests__/gemini-provider.test.ts +5 -5
- package/src/__tests__/get-skill-detail-audit.test.ts +134 -288
- package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -1
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +5 -5
- package/src/__tests__/host-cu-routes-targeted.test.ts +8 -8
- package/src/__tests__/image-credentials.test.ts +13 -13
- package/src/__tests__/inbound-admitted-introduction-nudge.test.ts +14 -6
- package/src/__tests__/inbound-trust-verdict.test.ts +14 -6
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +11 -11
- package/src/__tests__/inference-profile-session-handler.test.ts +7 -7
- package/src/__tests__/inference-profile-session-ipc.test.ts +5 -5
- package/src/__tests__/inference-profile-validation-jev.test.ts +1 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +40 -24
- package/src/__tests__/input-repairs.test.ts +1 -1
- package/src/__tests__/install-skill-routing.test.ts +10 -8
- package/src/__tests__/integration-status.test.ts +5 -5
- package/src/__tests__/internal-telemetry-routes.test.ts +3 -3
- package/src/__tests__/invite-routes-http.test.ts +1 -1
- package/src/__tests__/llm-catalog-parity.test.ts +5 -7
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -2
- package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -3
- package/src/__tests__/llm-resolver.test.ts +1 -1
- package/src/__tests__/llm-schema-fallback-profile.test.ts +25 -19
- package/src/__tests__/llm-usage-store.test.ts +1 -1
- package/src/__tests__/managed-skill-lifecycle.test.ts +12 -12
- package/src/__tests__/mcp-abort-signal.test.ts +1 -2
- package/src/__tests__/mcp-client-auth.test.ts +1 -2
- package/src/__tests__/mcp-config-secret-boundary.test.ts +44 -40
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +1 -2
- package/src/__tests__/media-generate-image.test.ts +30 -30
- package/src/__tests__/memory-config-file-parity.test.ts +6 -6
- package/src/__tests__/migration-export-to-gcs.test.ts +4 -4
- package/src/__tests__/migration-import-commit-http.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +2 -2
- package/src/__tests__/{notification-platform-adapter.test.ts → notification-cloud-adapter.test.ts} +39 -39
- package/src/__tests__/notification-deep-link.test.ts +40 -40
- package/src/__tests__/oauth-apps-routes.test.ts +3 -3
- package/src/__tests__/oauth-cli.test.ts +9 -9
- package/src/__tests__/oauth-commands-routes.test.ts +16 -16
- package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -2
- package/src/__tests__/oauth-provider-serializer.test.ts +11 -11
- package/src/__tests__/oauth-providers-routes.test.ts +12 -12
- package/src/__tests__/oauth-store.test.ts +1 -1
- package/src/__tests__/oauth2-gateway-transport.test.ts +3 -3
- package/src/__tests__/onboarding-persona-write.test.ts +2 -2
- package/src/__tests__/openai-image-service.test.ts +2 -2
- package/src/__tests__/openai-responses-cutover-guard.test.ts +2 -2
- package/src/__tests__/openai-responses-provider.test.ts +1 -1
- package/src/__tests__/path-policy.test.ts +4 -4
- package/src/__tests__/personal-memory-auth-bypass.test.ts +1 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +18 -18
- package/src/__tests__/plugin-api-model-profiles.test.ts +1 -1
- package/src/__tests__/plugin-api-provider.test.ts +1 -1
- package/src/__tests__/plugin-api-store-credential.test.ts +4 -1
- package/src/__tests__/plugin-api-webhook-url.test.ts +4 -5
- package/src/__tests__/plugin-import-boundary-guard.test.ts +8 -39
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -2
- package/src/__tests__/{provider-platform-proxy-integration.test.ts → provider-cloud-proxy-integration.test.ts} +70 -72
- package/src/__tests__/provider-commit-message-generator.test.ts +1 -1
- package/src/__tests__/provider-connections-backfill.test.ts +9 -9
- package/src/__tests__/provider-error-scenarios.test.ts +8 -8
- package/src/__tests__/provider-registry-ollama.test.ts +1 -1
- package/src/__tests__/provider-send-message-override-profile.test.ts +5 -5
- package/src/__tests__/registry.test.ts +3 -3
- package/src/__tests__/requests-mock.ts +972 -0
- package/src/__tests__/resolve-trust-class.test.ts +1 -1
- package/src/__tests__/resource-pressure-guard.test.ts +6 -6
- package/src/__tests__/resource-pressure-routes.test.ts +5 -5
- package/src/__tests__/retry-fallback-escalation.test.ts +5 -5
- package/src/__tests__/sanitize-config-for-transfer.test.ts +1 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +97 -97
- package/src/__tests__/schedule-store.test.ts +1 -1
- package/src/__tests__/search-skills-unified.test.ts +9 -7
- package/src/__tests__/{secret-routes-platform-proxy.test.ts → secret-routes-cloud-proxy.test.ts} +48 -95
- package/src/__tests__/secret-routes-scrub.test.ts +2 -59
- package/src/__tests__/secure-keys.test.ts +1 -1
- package/src/__tests__/skill-load-plugin-scope.test.ts +2 -2
- package/src/__tests__/skill-load-tool.test.ts +1 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -19
- package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
- package/src/__tests__/skill-tool-factory.test.ts +1 -1
- package/src/__tests__/skills-file-content-endpoint.test.ts +222 -536
- package/src/__tests__/skills-files-catalog-fallback.test.ts +259 -577
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slim-skill-category.test.ts +11 -9
- package/src/__tests__/stt-stream-session.test.ts +3 -3
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-disposal.test.ts +5 -5
- package/src/__tests__/subagent-fork-notifications.test.ts +4 -4
- package/src/__tests__/subagent-fork-spawn.test.ts +5 -5
- package/src/__tests__/subagent-manager-notify.test.ts +5 -5
- package/src/__tests__/subagent-tool-gate-mode.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/telegram-bot-username-resolution.test.ts +34 -52
- package/src/__tests__/telegram-config.test.ts +16 -16
- package/src/__tests__/telephony-tts-guard.test.ts +13 -13
- package/src/__tests__/test-preload.ts +17 -7
- package/src/__tests__/tool-audit.test.ts +1 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +7 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/turn-events-store.test.ts +1 -1
- package/src/__tests__/twilio-routes.test.ts +2 -1
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
- package/src/__tests__/unified-turn-context-client-os.test.ts +1 -1
- package/src/__tests__/validate-input.test.ts +5 -5
- package/src/__tests__/verification-outbound-delivery.test.ts +1 -1
- package/src/__tests__/voice-config-update.test.ts +15 -15
- package/src/__tests__/voice-ingress-preflight.test.ts +4 -4
- package/src/__tests__/web-search.test.ts +340 -859
- package/src/__tests__/workspace-git-service.test.ts +1 -1
- package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +12 -12
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +1 -1
- package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +1 -1
- package/src/__tests__/workspace-migration-100-upgrade-quality-profile-to-fable-5.test.ts +1 -1
- package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +1 -1
- package/src/__tests__/workspace-migration-110-flip-balanced-profile-to-together.test.ts +1 -1
- package/src/__tests__/workspace-migration-113-swap-balanced-profile-to-glm-5p2.test.ts +1 -1
- package/src/__tests__/workspace-migration-114-swap-quality-profile-to-opus.test.ts +1 -1
- package/src/__tests__/workspace-migration-123-swap-quality-profile-to-fable.test.ts +1 -1
- package/src/__tests__/workspace-migration-125-repoint-managed-connections-to-cadet.test.ts +3 -3
- package/src/__tests__/workspace-migration-126-strip-managed-profile-bodies.test.ts +9 -9
- package/src/__tests__/workspace-migration-127-backfill-default-provider.test.ts +11 -11
- package/src/__tests__/workspace-migration-130-speech-mode-to-provider.test.ts +3 -3
- package/src/__tests__/workspace-migration-132-web-search-mode-to-provider.test.ts +6 -6
- package/src/__tests__/workspace-migration-133-collapse-provider-connections.test.ts +3 -3
- package/src/__tests__/workspace-migration-134-image-generation-mode-to-provider.test.ts +6 -6
- package/src/__tests__/workspace-migration-136-repair-stale-fireworks-kimi-model-id.test.ts +1 -1
- package/src/__tests__/workspace-migration-137-repair-retired-fireworks-minimax-model-id.test.ts +1 -1
- package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +1 -1
- package/src/__tests__/workspace-migration-147-rename-colliding-backup-profile-names.test.ts +1 -1
- package/src/__tests__/workspace-migration-151-repair-renamed-fireworks-deepseek-pro-model-id.test.ts +1 -1
- package/src/__tests__/workspace-migration-152-repair-retired-fireworks-minimax-m2p7-model-id.test.ts +1 -1
- package/src/__tests__/workspace-migration-159-rename-colliding-jev-profile-name.test.ts +1 -1
- package/src/__tests__/workspace-migration-160-repair-retired-fireworks-model-ids.test.ts +1 -1
- package/src/__tests__/workspace-migration-161-repair-retired-fireworks-glm-5p2-model-id.test.ts +1 -1
- package/src/api/events/cloud-disconnected.ts +21 -0
- package/src/api/events/host-bash.ts +1 -1
- package/src/api/events/host-cu.ts +1 -1
- package/src/api/events/host-file.ts +1 -1
- package/src/api/events/notification-intent.ts +1 -1
- package/src/api/events/show-cloud-login.ts +18 -0
- package/src/api/index.ts +11 -11
- package/src/api/responses/home.ts +1 -1
- package/src/api/surfaces.ts +4 -4
- package/src/approvals/guardian-request-resolvers.ts +2 -2
- package/src/avatar/accent-backfill.ts +1 -1
- package/src/avatar/identity-avatar.ts +1 -1
- package/src/avatar/notification-avatar.test.ts +2 -2
- package/src/avatar/notification-avatar.ts +2 -2
- package/src/avatar/resvg-lazy.test.ts +1 -1
- package/src/avatar/resvg-lazy.ts +3 -3
- package/src/avatar/sync-avatar.test.ts +76 -76
- package/src/avatar/sync-avatar.ts +15 -15
- package/src/avatar/traits-png-sync.ts +1 -1
- package/src/background-wake/background-wake-routes.test.ts +39 -39
- package/src/background-wake/{platform-client.test.ts → cloud-client.test.ts} +11 -11
- package/src/background-wake/{platform-client.ts → cloud-client.ts} +14 -14
- package/src/background-wake/publisher.ts +4 -4
- package/src/background-wake/wake-intent-hooks.test.ts +3 -3
- package/src/calls/__tests__/call-controller.test.ts +2 -2
- package/src/calls/__tests__/voice-session-bridge.test.ts +1 -1
- package/src/calls/call-domain.ts +1 -1
- package/src/calls/resolve-call-tts-provider.ts +6 -6
- package/src/calls/telephony-credential-preflight.ts +3 -3
- package/src/calls/telephony-tts-capability.ts +5 -6
- package/src/calls/tts-call-strategy.ts +1 -1
- package/src/calls/voice-escalation-judge.ts +1 -1
- package/src/calls/voice-ingress-preflight.ts +4 -4
- package/src/cli/bundled-modules.ts +1 -1
- package/src/cli/commands/__tests__/apps.test.ts +18 -53
- package/src/cli/commands/__tests__/plugins.test.ts +13 -13
- package/src/cli/commands/__tests__/tts.test.ts +1 -1
- package/src/cli/commands/auth.help.ts +3 -3
- package/src/cli/commands/auth.ts +2 -4
- package/src/cli/commands/browser.help.ts +2 -2
- package/src/cli/commands/channels/index.help.ts +2 -2
- package/src/cli/commands/channels/index.ts +1 -1
- package/src/cli/commands/channels/send.ts +1 -1
- package/src/cli/commands/cloud/__tests__/callback-routes-list.test.ts +2 -2
- package/src/cli/commands/cloud/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/cloud/__tests__/disconnect.test.ts +3 -4
- package/src/cli/commands/cloud/connect.ts +2 -2
- package/src/cli/commands/cloud/credits-format.ts +1 -1
- package/src/cli/commands/cloud/index.help.ts +35 -35
- package/src/cli/commands/cloud/index.ts +1 -1
- package/src/cli/commands/config.ts +2 -2
- package/src/cli/commands/credentials.ts +10 -12
- package/src/cli/commands/domain.ts +1 -1
- package/src/cli/commands/email.help.ts +1 -1
- package/src/cli/commands/email.ts +2 -2
- package/src/cli/commands/gateway.help.ts +1 -1
- package/src/cli/commands/gateway.ts +1 -1
- package/src/cli/commands/image-generation.help.ts +1 -1
- package/src/cli/commands/inference-providers.ts +1 -1
- package/src/cli/commands/inference.help.ts +2 -2
- package/src/cli/commands/memory/index.help.ts +1 -1
- package/src/cli/commands/oauth/connect.test.ts +2 -2
- package/src/cli/commands/oauth/connect.ts +1 -1
- package/src/cli/commands/oauth/index.help.ts +5 -5
- package/src/cli/commands/oauth/providers.ts +2 -2
- package/src/cli/commands/oauth/shared.ts +3 -3
- package/src/cli/commands/plugins.help.ts +2 -3
- package/src/cli/commands/plugins.ts +5 -5
- package/src/cli/commands/roadmap.help.ts +1 -1
- package/src/cli/commands/telemetry.help.ts +2 -2
- package/src/cli/commands/telemetry.ts +2 -2
- package/src/cli/commands/tts.help.ts +3 -3
- package/src/cli/commands/webhooks.help.ts +9 -9
- package/src/cli/commands/webhooks.ts +1 -1
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +8 -8
- package/src/cli/lib/__tests__/{install-from-platform.test.ts → install-from-cloud.test.ts} +54 -54
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +18 -18
- package/src/cli/lib/__tests__/{plugin-catalog-platform.test.ts → plugin-catalog-cloud.test.ts} +2 -2
- package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +1 -1
- package/src/cli/lib/__tests__/plugin-catalog-resolve.test.ts +9 -9
- package/src/cli/lib/__tests__/plugin-details.test.ts +10 -10
- package/src/cli/lib/__tests__/plugins-install-offline.test.ts +24 -24
- package/src/cli/lib/bundled-marketplace.json +1 -1
- package/src/cli/lib/inspect-plugin.ts +2 -2
- package/src/cli/lib/{install-from-platform.ts → install-from-cloud.ts} +29 -29
- package/src/cli/lib/install-from-github.ts +4 -4
- package/src/cli/lib/plugin-catalog-cache.ts +7 -7
- package/src/cli/lib/{plugin-catalog-platform.ts → plugin-catalog-cloud.ts} +6 -6
- package/src/cli/lib/plugin-catalog-local.ts +2 -2
- package/src/cli/lib/publish-plugin.ts +9 -10
- package/src/cli/lib/search-plugins.ts +2 -2
- package/src/{platform → cloud}/client.test.ts +85 -86
- package/src/{platform → cloud}/client.ts +38 -46
- package/src/{platform/platform-patch-queue.test.ts → cloud/cloud-patch-queue.test.ts} +6 -9
- package/src/{platform/platform-patch-queue.ts → cloud/cloud-patch-queue.ts} +11 -11
- package/src/{platform/managed-speech.test.ts → cloud/cloud-speech.test.ts} +19 -19
- package/src/{platform/managed-speech.ts → cloud/cloud-speech.ts} +41 -41
- package/src/{platform → cloud}/consent-cache.test.ts +5 -5
- package/src/{platform → cloud}/consent-cache.ts +8 -8
- package/src/cloud/feature-gate.ts +8 -0
- package/src/{platform → cloud}/sync-identity.test.ts +4 -4
- package/src/{platform → cloud}/sync-identity.ts +6 -6
- package/src/config/__tests__/{platform-identity.test.ts → cloud-identity.test.ts} +54 -56
- package/src/config/__tests__/{platform-rehydration.test.ts → cloud-rehydration.test.ts} +47 -80
- package/src/config/__tests__/default-profile-catalog.test.ts +12 -12
- package/src/config/__tests__/default-provider.test.ts +4 -4
- package/src/config/__tests__/deployment-context-defaults.test.ts +23 -23
- package/src/config/__tests__/{jev-managed-profile.test.ts → jev-cloud-profile.test.ts} +3 -5
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +2 -2
- package/src/config/__tests__/loader-last-known-good.test.ts +6 -6
- package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +2 -2
- package/src/config/__tests__/profile-materialization.test.ts +7 -7
- package/src/config/__tests__/seed-default-provider.test.ts +17 -17
- package/src/config/__tests__/substrate-twin-shadowing.test.ts +36 -26
- package/src/config/__tests__/sync-gated-profiles.test.ts +20 -20
- package/src/config/__tests__/webhook-routing.test.ts +51 -51
- package/src/config/bundled-skills/computer-use/TOOLS.json +12 -12
- package/src/config/bundled-skills/messaging/SKILL.md +8 -8
- package/src/config/bundled-skills/messaging/TOOLS.json +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +2 -2
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/shared.ts +1 -1
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +12 -12
- package/src/config/bundled-skills/skill-management/SKILL.md +3 -3
- package/src/config/bundled-skills/skill-management/TOOLS.json +8 -8
- package/src/config/bundled-skills/skill-management/tools/{delete-managed.ts → delete-cloud.ts} +2 -2
- package/src/config/bundled-skills/skill-management/tools/{scaffold-managed.ts → scaffold-cloud.ts} +2 -2
- package/src/config/call-site-defaults.ts +6 -6
- package/src/config/{platform-identity.ts → cloud-identity.ts} +39 -42
- package/src/config/cloud-rehydration.ts +35 -0
- package/src/config/{managed-speech-defaults.ts → cloud-speech-defaults.ts} +13 -13
- package/src/config/default-profile-catalog.ts +36 -38
- package/src/config/default-profile-names.ts +11 -11
- package/src/config/default-provider-resolution.ts +2 -2
- package/src/config/default-provider.ts +1 -1
- package/src/config/env-registry.ts +21 -26
- package/src/config/env.ts +33 -46
- package/src/config/feature-flag-registry.json +5 -5
- package/src/config/inference-profile-validation.ts +1 -1
- package/src/config/llm-resolver.ts +9 -10
- package/src/config/loader.ts +33 -17
- package/src/config/profile-materialization.ts +8 -8
- package/src/config/profile-text-generation.test.ts +1 -1
- package/src/config/sanitize-for-transfer.ts +1 -1
- package/src/config/schema.ts +6 -6
- package/src/config/schemas/__tests__/stt.test.ts +2 -2
- package/src/config/schemas/call-site-catalog.ts +1 -1
- package/src/config/schemas/channels.ts +8 -0
- package/src/config/schemas/{platform.ts → cloud.ts} +7 -6
- package/src/config/schemas/host-browser.ts +1 -0
- package/src/config/schemas/ingress.ts +29 -10
- package/src/config/schemas/llm.ts +36 -36
- package/src/config/schemas/memory-lifecycle.ts +11 -1
- package/src/config/schemas/services.ts +4 -4
- package/src/config/schemas/stt.ts +3 -3
- package/src/config/schemas/tts.ts +10 -5
- package/src/config/schemas/voice.ts +1 -1
- package/src/config/seed-inference-profiles.ts +19 -19
- package/src/config/skill-state.ts +2 -2
- package/src/config/skills.ts +4 -4
- package/src/config/substrate-twin-shadowing.ts +12 -17
- package/src/config/sync-gated-profiles.ts +8 -9
- package/src/config/webhook-routing.ts +19 -19
- package/src/contacts/contacts-write.ts +1 -1
- package/src/credential-execution/ces-runtime.ts +6 -6
- package/src/credential-execution/{managed-catalog.ts → cloud-catalog.ts} +24 -24
- package/src/credential-execution/prompted-credential.ts +2 -23
- package/src/credential-health/credential-health-service.ts +59 -70
- package/src/daemon/__tests__/can-spawn-subagents.test.ts +1 -1
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +4 -4
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/conversation-agent-loop.ts +9 -9
- package/src/daemon/conversation-error.ts +36 -36
- package/src/daemon/conversation-skill-tools.ts +7 -7
- package/src/daemon/conversation-surfaces.ts +1 -1
- package/src/daemon/conversation-tool-setup.ts +2 -2
- package/src/daemon/conversation-turn-finalize.ts +1 -1
- package/src/daemon/conversation-workspace.ts +1 -1
- package/src/daemon/conversation.ts +1 -1
- package/src/daemon/credential-transcript-scrub.ts +32 -35
- package/src/daemon/daemon-readiness.ts +1 -1
- package/src/daemon/event-loop-watchdog.ts +2 -2
- package/src/daemon/handlers/config-ingress.ts +9 -9
- package/src/daemon/handlers/config-telegram.ts +6 -6
- package/src/daemon/handlers/skills.ts +20 -20
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +19 -19
- package/src/daemon/parse-actual-tokens-from-error.ts +3 -3
- package/src/daemon/providers-setup.ts +6 -6
- package/src/daemon/resource-pressure-guard-lifecycle.ts +1 -1
- package/src/daemon/resource-pressure-guard.ts +6 -6
- package/src/daemon/shutdown-handlers.ts +1 -1
- package/src/daemon/sqlite-corruption-watchdog.ts +3 -3
- package/src/daemon/trust-context.ts +1 -1
- package/src/desktop/desktop-automation-lease.ts +1 -1
- package/src/desktop/desktop-browser-endpoint.ts +2 -4
- package/src/desktop/desktop-computer-use-routing.test.ts +6 -6
- package/src/desktop/desktop-stream-bridge.test.ts +4 -4
- package/src/desktop/desktop-stream-bridge.ts +1 -1
- package/src/desktop/{virtual-desktop-platform.test.ts → virtual-desktop-cloud.test.ts} +6 -6
- package/src/desktop/virtual-desktop-feature.ts +7 -7
- package/src/email/byo-email-credential.ts +1 -1
- package/src/email/registered-inbox.test.ts +16 -16
- package/src/email/registered-inbox.ts +20 -20
- package/src/heartbeat/heartbeat-service.ts +1 -1
- package/src/hooks/hook-loader.ts +2 -2
- package/src/inbound/__tests__/oauth-callback-url.test.ts +2 -2
- package/src/inbound/__tests__/tunnel-probe.test.ts +8 -8
- package/src/inbound/{platform-callback-registration.ts → cloud-callback-registration.ts} +49 -60
- package/src/inbound/oauth-callback-url.ts +6 -6
- package/src/inbound/pod-webhook-claim.ts +2 -2
- package/src/inbound/public-ingress-urls.ts +3 -3
- package/src/inbound/tunnel-probe.ts +21 -15
- package/src/inbound/tunnel-webhooks-gate.ts +3 -3
- package/src/ipc/__tests__/db-readiness-ipc-gate.test.ts +1 -1
- package/src/ipc/__tests__/email-ipc.test.ts +8 -8
- package/src/ipc/gateway-client.test.ts +55 -59
- package/src/ipc/gateway-flag-listener.ts +3 -3
- package/src/ipc/routes/db-proxy.ts +1 -1
- package/src/live-voice/__tests__/live-activity-reporter.test.ts +2 -2
- package/src/live-voice/__tests__/{live-voice-managed-speech-fallback.test.ts → live-voice-cloud-speech-fallback.test.ts} +15 -15
- package/src/live-voice/__tests__/live-voice-credential-preflight.test.ts +7 -7
- package/src/live-voice/__tests__/live-voice-stt.test.ts +1 -1
- package/src/live-voice/live-activity-reporter.ts +7 -7
- package/src/live-voice/live-voice-credential-preflight.ts +6 -6
- package/src/live-voice/live-voice-session.ts +3 -3
- package/src/live-voice/live-voice-tts.ts +1 -1
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +4 -4
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +1 -1
- package/src/mcp/mcp-oauth-provider.ts +2 -2
- package/src/media/gemini-image-service.ts +11 -13
- package/src/media/image-credentials.ts +9 -9
- package/src/media/openai-image-service.ts +1 -1
- package/src/media/types.ts +3 -3
- package/src/messaging/providers/channel-transport.ts +1 -1
- package/src/messaging/providers/discord/transport.ts +1 -1
- package/src/messaging/providers/gmail/adapter.ts +1 -1
- package/src/messaging/providers/gmail/client.ts +4 -4
- package/src/messaging/providers/outlook/adapter.ts +1 -1
- package/src/messaging/providers/slack/adapter.ts +1 -1
- package/src/messaging/providers/slack/transport.ts +1 -1
- package/src/messaging/providers/telegram-bot/transport.ts +1 -1
- package/src/monitoring/db-integrity-check.ts +1 -1
- package/src/monitoring/db-integrity-sample.ts +2 -2
- package/src/monitoring/mount-watch.ts +1 -1
- package/src/monitoring/oom-kill-reporter.ts +1 -1
- package/src/monitoring/worker.ts +5 -5
- package/src/notifications/README.md +2 -2
- package/src/notifications/__tests__/broadcaster.test.ts +9 -9
- package/src/notifications/__tests__/completion-policy.test.ts +1 -1
- package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +4 -4
- package/src/notifications/activity-failed-dedupe.ts +1 -1
- package/src/notifications/adapters/{platform.ts → cloud.ts} +8 -8
- package/src/notifications/broadcaster.ts +10 -10
- package/src/notifications/decision-engine.ts +1 -1
- package/src/notifications/destination-resolver.ts +1 -1
- package/src/notifications/emit-signal.ts +6 -6
- package/src/notifications/types.ts +2 -2
- package/src/oauth/__tests__/identity-verifier.test.ts +1 -1
- package/src/oauth/__tests__/{seed-providers-managed.test.ts → seed-providers-cloud.test.ts} +13 -13
- package/src/oauth/byo-connection.test.ts +2 -2
- package/src/oauth/{platform-connection.test.ts → cloud-connection.test.ts} +41 -43
- package/src/oauth/{platform-connection.ts → cloud-connection.ts} +19 -19
- package/src/oauth/connection-resolver.test.ts +59 -61
- package/src/oauth/connection-resolver.ts +22 -22
- package/src/oauth/connection.ts +1 -1
- package/src/oauth/oauth-store.ts +8 -8
- package/src/oauth/provider-serializer.ts +4 -4
- package/src/oauth/revoke.ts +1 -1
- package/src/oauth/seed-providers.ts +32 -32
- package/src/permissions/approval-provenance.ts +1 -1
- package/src/permissions/checker.test.ts +18 -16
- package/src/permissions/checker.ts +3 -3
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/embeddings/embedding-backend.test.ts +16 -16
- package/src/persistence/embeddings/embedding-backend.ts +13 -13
- package/src/persistence/embeddings/embedding-gemini.test.ts +9 -12
- package/src/persistence/embeddings/embedding-gemini.ts +11 -11
- package/src/persistence/embeddings/embedding-runtime-manager.ts +2 -2
- package/src/persistence/jobs-store.ts +1 -1
- package/src/persistence/lifecycle-events-store.ts +1 -1
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/migrations/224-oauth-providers-managed-service-is-paid.ts +1 -1
- package/src/persistence/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +1 -1
- package/src/persistence/migrations/296-rewrite-balanced-economy-profile-pins.ts +2 -2
- package/src/persistence/migrations/306-rewrite-frontier-profile-pins.test.ts +1 -1
- package/src/persistence/migrations/306-rewrite-frontier-profile-pins.ts +1 -1
- package/src/persistence/migrations/351-schedule-skill-script-handoff.ts +1 -1
- package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +3 -3
- package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +7 -7
- package/src/persistence/migrations/__tests__/319-remove-legacy-managed-connections.test.ts +2 -2
- package/src/persistence/schema/oauth.ts +1 -1
- package/src/plugin-api/index.ts +4 -4
- package/src/plugin-api/model-profiles.test.ts +1 -1
- package/src/plugin-api/model-profiles.ts +1 -1
- package/src/plugin-api/transcription-session.ts +1 -1
- package/src/plugin-api/vision-support.test.ts +1 -1
- package/src/plugin-api/webhook-url.ts +12 -12
- package/src/plugins/__tests__/source-fingerprint.test.ts +1 -1
- package/src/plugins/defaults/{platform-hosted → cloud-hosted}/__tests__/reengage.test.ts +5 -6
- package/src/plugins/defaults/cloud-hosted/package.json +14 -0
- package/src/plugins/defaults/{platform-hosted → cloud-hosted}/routes/reengage.ts +3 -3
- package/src/plugins/defaults/index.ts +6 -6
- package/src/plugins/defaults/main.ts +5 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +5 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -0
- package/src/plugins/defaults/memory/config.ts +4 -4
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +2 -2
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +7 -7
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +5 -5
- package/src/plugins/defaults/memory/memory-retrospective-skill-card.ts +2 -2
- package/src/plugins/defaults/memory/src/__tests__/memory-v2-routes.test.ts +1 -1
- package/src/plugins/defaults/memory/src/__tests__/memory-worker-routes.test.ts +1 -1
- package/src/plugins/defaults/memory/substrate/__tests__/boot-maintenance.test.ts +20 -20
- package/src/plugins/defaults/memory/substrate/__tests__/cli-command-store.test.ts +1 -1
- package/src/plugins/defaults/memory/substrate/__tests__/skill-store.test.ts +3 -3
- package/src/plugins/defaults/memory/substrate/boot-maintenance.ts +17 -17
- package/src/plugins/defaults/memory/substrate/cli-command-store.ts +2 -2
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +1 -1
- package/src/plugins/defaults/memory/substrate/skill-store.ts +4 -4
- package/src/plugins/defaults/memory/v3/__tests__/maintain-job.test.ts +3 -3
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +2 -2
- package/src/plugins/defaults/memory/v3/maintain-job.ts +10 -10
- package/src/plugins/defaults/memory/v3/orchestrate.ts +3 -3
- package/src/plugins/defaults/memory/worker.ts +5 -5
- package/src/plugins/defaults/workspace/injectors.ts +2 -2
- package/src/plugins/mtime-cache.ts +1 -1
- package/src/plugins/source-versions.ts +1 -1
- package/src/plugins/types.ts +1 -1
- package/src/prompts/__tests__/system-prompt.test.ts +1 -1
- package/src/prompts/normalize-onboarding.ts +1 -1
- package/src/prompts/persona-resolver.ts +1 -1
- package/src/prompts/templates/system-sections.ts +3 -3
- package/src/providers/__tests__/cadet-connection-routing.test.ts +7 -7
- package/src/providers/__tests__/cadet-mismatch-routing.test.ts +12 -12
- package/src/providers/__tests__/connection-model-compat.test.ts +2 -2
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +10 -10
- package/src/providers/__tests__/inference.test.ts +2 -2
- package/src/providers/__tests__/preflight-resolved-config.test.ts +16 -16
- package/src/providers/__tests__/retry-callsite.test.ts +1 -1
- package/src/providers/__tests__/satellite-connection-routing.test.ts +4 -4
- package/src/providers/__tests__/search-provider-catalog.test.ts +2 -2
- package/src/providers/__tests__/voice-error-copy.test.ts +3 -3
- package/src/providers/anthropic/client.ts +1 -1
- package/src/providers/cadet/client.ts +1 -1
- package/src/providers/cadet-model-routing.test.ts +19 -19
- package/src/providers/cadet-model-routing.ts +23 -23
- package/src/providers/call-site-routing.ts +10 -10
- package/src/providers/{platform-proxy → cloud-proxy}/constants.ts +11 -11
- package/src/providers/{platform-proxy → cloud-proxy}/context.ts +26 -26
- package/src/providers/connection-resolution.ts +43 -43
- package/src/providers/fallback-breaker.ts +2 -2
- package/src/providers/fetch-provider-catalog.ts +3 -3
- package/src/providers/gemini/client.ts +5 -5
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +3 -3
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +8 -8
- package/src/providers/inference/__tests__/base-url-security.test.ts +4 -4
- package/src/providers/inference/adapter-factory.ts +35 -35
- package/src/providers/inference/auth.ts +10 -10
- package/src/providers/inference/backfill.ts +11 -11
- package/src/providers/inference/connection-availability.ts +24 -24
- package/src/providers/inference/connections.ts +18 -20
- package/src/providers/inference/profile-probe.ts +4 -4
- package/src/providers/inference/resolve-auth.ts +8 -8
- package/src/providers/jev/ask.test.ts +1 -1
- package/src/providers/jev/ask.ts +2 -2
- package/src/providers/model-catalog.ts +8 -9
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +3 -3
- package/src/providers/openai/api-error-normalization.ts +2 -2
- package/src/providers/provider-availability.ts +4 -4
- package/src/providers/provider-env-vars.ts +1 -1
- package/src/providers/provider-secret-catalog.ts +1 -1
- package/src/providers/registry.ts +20 -20
- package/src/providers/retry.ts +26 -26
- package/src/providers/routing-identity.ts +10 -10
- package/src/providers/search-provider-catalog.ts +2 -2
- package/src/providers/speech-to-text/__tests__/{cadet-managed-realtime.test.ts → cadet-cloud-realtime.test.ts} +3 -3
- package/src/providers/speech-to-text/__tests__/{cadet-managed.test.ts → cadet-cloud.test.ts} +35 -35
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +1 -1
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +27 -27
- package/src/providers/speech-to-text/{cadet-managed-flux-realtime.ts → cadet-cloud-flux-realtime.ts} +6 -8
- package/src/providers/speech-to-text/{cadet-managed-realtime.ts → cadet-cloud-realtime.ts} +3 -3
- package/src/providers/speech-to-text/{cadet-managed.ts → cadet-cloud.ts} +18 -20
- package/src/providers/speech-to-text/cadet-speech-relay-connection.ts +7 -7
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +3 -3
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +4 -4
- package/src/providers/speech-to-text/provider-catalog.ts +3 -3
- package/src/providers/speech-to-text/resolve.ts +26 -28
- package/src/providers/together/client.ts +1 -1
- package/src/providers/types.ts +4 -4
- package/src/providers/voice-error-copy.ts +1 -1
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +10 -10
- package/src/runtime/agent-wake.ts +1 -1
- package/src/runtime/auth/__tests__/middleware.test.ts +1 -1
- package/src/runtime/auth/middleware.ts +1 -1
- package/src/runtime/auth/scopes.ts +1 -1
- package/src/runtime/capabilities.ts +1 -1
- package/src/runtime/channel-invite-transports/discord.ts +1 -1
- package/src/runtime/channel-invite-transports/email.ts +2 -2
- package/src/runtime/channel-readiness-service.ts +15 -13
- package/src/runtime/channel-reply-session.test.ts +1 -1
- package/src/runtime/channel-reply-session.ts +1 -1
- package/src/runtime/http-server.ts +4 -4
- package/src/runtime/introduction-policy.ts +4 -4
- package/src/runtime/migrations/__tests__/origin-mode.test.ts +14 -14
- package/src/runtime/migrations/__tests__/v1-test-helpers.ts +1 -1
- package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +2 -2
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +3 -4
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +6 -7
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +1 -1
- package/src/runtime/migrations/migration-transport.ts +3 -3
- package/src/runtime/migrations/origin-mode.ts +6 -9
- package/src/runtime/migrations/vbundle-import-policy.ts +2 -2
- package/src/runtime/migrations/vbundle-importer.ts +4 -4
- package/src/runtime/migrations/vbundle-streaming-importer.ts +6 -6
- package/src/runtime/migrations/vbundle-validator.ts +1 -1
- package/src/runtime/routes/__tests__/{platform-credits-routes.test.ts → cloud-credits-routes.test.ts} +9 -9
- package/src/runtime/routes/__tests__/{platform-invoice-routes.test.ts → cloud-invoice-routes.test.ts} +13 -13
- package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +126 -82
- package/src/runtime/routes/__tests__/default-plugin-routes.test.ts +5 -5
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +10 -10
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +18 -18
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +18 -16
- package/src/runtime/routes/__tests__/inference-send-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/migration-cadet-metadata-reconcile.test.ts +5 -20
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +19 -22
- package/src/runtime/routes/__tests__/oauth-proxy-routes.test.ts +27 -27
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +5 -5
- package/src/runtime/routes/__tests__/ps-routes.test.ts +5 -3
- package/src/runtime/routes/__tests__/roadmap-routes.test.ts +15 -15
- package/src/runtime/routes/__tests__/webhook-routes.test.ts +31 -31
- package/src/runtime/routes/app-management-routes.ts +1 -1
- package/src/runtime/routes/attachment-routes.ts +1 -1
- package/src/runtime/routes/auth-routes.ts +14 -14
- package/src/runtime/routes/avatar-routes.ts +0 -14
- package/src/runtime/routes/background-wake-routes.ts +4 -4
- package/src/runtime/routes/channel-availability-routes.ts +4 -4
- package/src/runtime/routes/client-routes.ts +5 -5
- package/src/runtime/routes/cloud-credentials.ts +45 -0
- package/src/runtime/routes/{platform-routes.ts → cloud-routes.ts} +86 -119
- package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +345 -667
- package/src/runtime/routes/credential-routes.ts +28 -30
- package/src/runtime/routes/default-provider-routes.ts +1 -1
- package/src/runtime/routes/desktop-setup-routes.test.ts +5 -5
- package/src/runtime/routes/desktop-setup-routes.ts +1 -1
- package/src/runtime/routes/domain-routes.ts +15 -21
- package/src/runtime/routes/email-routes.ts +13 -13
- package/src/runtime/routes/errors.ts +1 -1
- package/src/runtime/routes/gateway-status-routes.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +3 -3
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +1 -1
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
- package/src/runtime/routes/index.ts +1 -1
- package/src/runtime/routes/inference-profile-availability-guard.ts +5 -5
- package/src/runtime/routes/inference-profiles-routes.ts +11 -11
- package/src/runtime/routes/inference-provider-connection-routes.ts +24 -24
- package/src/runtime/routes/ingress-status-routes.ts +1 -1
- package/src/runtime/routes/integrations/a2a.ts +2 -2
- package/src/runtime/routes/integrations/twilio.ts +21 -35
- package/src/runtime/routes/internal-telemetry-routes.ts +2 -2
- package/src/runtime/routes/live-voice-routes.test.ts +4 -4
- package/src/runtime/routes/live-voice-routes.ts +6 -6
- package/src/runtime/routes/migration-routes.ts +34 -40
- package/src/runtime/routes/oauth-commands-routes.ts +31 -31
- package/src/runtime/routes/oauth-providers.ts +5 -5
- package/src/runtime/routes/oauth-proxy-passthrough.test.ts +1 -1
- package/src/runtime/routes/oauth-proxy-passthrough.ts +8 -8
- package/src/runtime/routes/oauth-proxy-routes.ts +15 -17
- package/src/runtime/routes/oauth-request-hints.test.ts +1 -1
- package/src/runtime/routes/oauth-request-hints.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +9 -9
- package/src/runtime/routes/profiler-routes.ts +1 -1
- package/src/runtime/routes/ps-routes.ts +4 -0
- package/src/runtime/routes/roadmap-routes.ts +18 -20
- package/src/runtime/routes/secret-routes.ts +20 -61
- package/src/runtime/routes/settings-routes.ts +18 -23
- package/src/runtime/routes/telemetry-routes.ts +4 -4
- package/src/runtime/routes/tts-routes.ts +4 -4
- package/src/runtime/routes/user-route-resolution.ts +1 -1
- package/src/runtime/routes/webhook-routes.ts +18 -18
- package/src/runtime/routes/workflow-routes.ts +5 -5
- package/src/runtime/sync/resource-sync-events.ts +2 -2
- package/src/runtime/verification-outbound-actions.ts +4 -4
- package/src/schedule/integration-status.ts +6 -6
- package/src/schedule/run-script.ts +1 -1
- package/src/schedule/schedule-timezone.ts +1 -1
- package/src/schedule/worker.ts +5 -5
- package/src/security/auth-fallback-events-store.ts +1 -1
- package/src/security/oauth2.ts +4 -4
- package/src/skills/available-skills.ts +4 -4
- package/src/skills/catalog-files.ts +8 -8
- package/src/skills/catalog-install.ts +12 -12
- package/src/skills/categories-cache.ts +3 -3
- package/src/skills/clawhub.ts +1 -1
- package/src/skills/{platform-compatibility.test.ts → cloud-compatibility.test.ts} +7 -7
- package/src/skills/{platform-compatibility.ts → cloud-compatibility.ts} +2 -4
- package/src/skills/{managed-store.ts → cloud-store.ts} +21 -23
- package/src/skills/inline-command-runner.ts +1 -1
- package/src/skills/install-meta.ts +1 -1
- package/src/skills/path-classifier.ts +4 -4
- package/src/skills/skill-history.ts +4 -4
- package/src/skills/skillssh-registry.ts +1 -1
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +1 -1
- package/src/stt/daemon-batch-transcriber.ts +7 -7
- package/src/stt/types.ts +3 -3
- package/src/subagent/manager.ts +15 -15
- package/src/subagent/types.ts +1 -1
- package/src/telegram/__tests__/webhook-health.test.ts +16 -16
- package/src/telegram/webhook-health.ts +10 -10
- package/src/telemetry/__tests__/config-setting-events-store.test.ts +1 -1
- package/src/telemetry/__tests__/outbox-test-harness.ts +2 -2
- package/src/telemetry/config-setting-events-store.ts +1 -1
- package/src/telemetry/config-setting-snapshot.ts +1 -1
- package/src/telemetry/memory-corpus-size.ts +1 -1
- package/src/telemetry/memory-tier-reporter.test.ts +2 -2
- package/src/telemetry/memory-tier-reporter.ts +3 -3
- package/src/telemetry/telemetry-event-sources.test.ts +2 -2
- package/src/telemetry/telemetry-event-sources.ts +9 -9
- package/src/telemetry/telemetry-events-outbox.ts +2 -2
- package/src/telemetry/telemetry-wire-validation.test.ts +1 -1
- package/src/telemetry/telemetry-wire-validation.ts +3 -3
- package/src/telemetry/telemetry-wire.generated.ts +1 -1
- package/src/telemetry/tool-audit.ts +1 -1
- package/src/telemetry/tool-executed-events-store.test.ts +1 -1
- package/src/telemetry/trace-collection-policy.ts +4 -4
- package/src/telemetry/turn-events-store.ts +1 -1
- package/src/telemetry/turn-outcome.ts +1 -1
- package/src/telemetry/turn-trace-store.ts +2 -2
- package/src/telemetry/types.test.ts +1 -1
- package/src/telemetry/types.ts +19 -19
- package/src/telemetry/usage-telemetry-reporter.test.ts +42 -42
- package/src/telemetry/usage-telemetry-reporter.ts +17 -17
- package/src/telemetry/watchdog-direct-emit.test.ts +9 -9
- package/src/telemetry/watchdog-direct-emit.ts +10 -10
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +2 -2
- package/src/tools/browser/cdp-client/factory.ts +1 -1
- package/src/tools/computer-use/target.ts +2 -2
- package/src/tools/credentials/store.ts +2 -20
- package/src/tools/network/__tests__/{managed-search-proxy.test.ts → cloud-search-proxy.test.ts} +20 -20
- package/src/tools/network/__tests__/web-search.test.ts +14 -14
- package/src/tools/network/{managed-search-proxy.ts → cloud-search-proxy.ts} +13 -13
- package/src/tools/network/web-search.ts +9 -9
- package/src/tools/permission-checker.ts +2 -2
- package/src/tools/shared/input-misuse.ts +1 -1
- package/src/tools/shared/input-repairs.ts +4 -4
- package/src/tools/skills/{delete-managed.ts → delete-cloud.ts} +4 -4
- package/src/tools/skills/find-similar-skills.test.ts +7 -7
- package/src/tools/skills/find-similar-skills.ts +15 -15
- package/src/tools/skills/load.ts +6 -6
- package/src/tools/skills/{scaffold-managed.ts → scaffold-cloud.ts} +16 -16
- package/src/tools/skills/skill-script-runner.ts +2 -2
- package/src/tools/terminal/safe-env.ts +3 -3
- package/src/tools/tool-types.ts +2 -2
- package/src/tools/types.ts +2 -2
- package/src/tools/ui-surface/surface-shape-docs.ts +2 -2
- package/src/tools/workflows/manage-workflows.ts +1 -1
- package/src/tools/workspace-tools/loader.ts +1 -1
- package/src/tts/__tests__/cadet-provider.test.ts +5 -5
- package/src/tts/provider-catalog.ts +1 -1
- package/src/tts/providers/cadet-provider.ts +23 -23
- package/src/tts/providers/cadet-tts-socket.ts +2 -2
- package/src/tts/tts-config-resolver.ts +1 -1
- package/src/tts/tts-voice-field.ts +3 -3
- package/src/usage/subagent-attribution.ts +1 -1
- package/src/util/__tests__/process-table.test.ts +47 -0
- package/src/util/canonicalize-identity.ts +1 -1
- package/src/util/cgroup-cpu.ts +5 -5
- package/src/util/cgroup-memory.ts +2 -2
- package/src/util/container-cpu-sampler.ts +4 -4
- package/src/util/disk-usage.ts +4 -4
- package/src/util/errors.ts +3 -3
- package/src/util/layout.ts +479 -0
- package/src/util/oom-priority.ts +1 -1
- package/src/util/platform.ts +12 -468
- package/src/util/process-table.ts +39 -5
- package/src/util/provider-error-patterns.ts +2 -2
- package/src/watch/__tests__/watch-retro.test.ts +2 -2
- package/src/watch/watch-retro.ts +1 -1
- package/src/watcher/__tests__/telemetry.test.ts +2 -2
- package/src/watcher/telemetry.ts +1 -1
- package/src/windows-compiled-cli.ts +1 -1
- package/src/workspace/adaptive-thinking-repair.ts +4 -4
- package/src/workspace/byok-default-profile-ensure.ts +7 -7
- package/src/workspace/git-service.ts +2 -2
- package/src/workspace/migrations/006-services-config.ts +1 -1
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +1 -1
- package/src/workspace/migrations/052-seed-default-inference-profiles.ts +1 -1
- package/src/workspace/migrations/063-release-notes-dynamic-model-context.ts +1 -1
- package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +1 -1
- package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +4 -4
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +3 -3
- package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +4 -4
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +5 -5
- package/src/workspace/migrations/100-upgrade-quality-profile-to-fable-5.ts +4 -4
- package/src/workspace/migrations/101-upgrade-balanced-economy-to-minimax-m3.ts +4 -4
- package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +5 -5
- package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +1 -1
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +1 -1
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +4 -4
- package/src/workspace/migrations/109-swap-quality-profile-to-glm-5p2.ts +11 -11
- package/src/workspace/migrations/110-flip-balanced-profile-to-together.ts +6 -6
- package/src/workspace/migrations/113-swap-balanced-profile-to-glm-5p2.ts +5 -5
- package/src/workspace/migrations/114-swap-quality-profile-to-opus.ts +4 -4
- package/src/workspace/migrations/115-drop-frontier-profile.ts +5 -5
- package/src/workspace/migrations/123-swap-quality-profile-to-fable.ts +5 -5
- package/src/workspace/migrations/125-repoint-managed-connections-to-cadet.ts +3 -3
- package/src/workspace/migrations/126-strip-managed-profile-bodies.ts +2 -2
- package/src/workspace/migrations/127-backfill-default-provider.ts +6 -6
- package/src/workspace/migrations/130-speech-mode-to-provider.ts +4 -4
- package/src/workspace/migrations/131-drop-web-fetch-mode.ts +1 -1
- package/src/workspace/migrations/132-web-search-mode-to-provider.ts +6 -6
- package/src/workspace/migrations/133-collapse-provider-connections.ts +3 -3
- package/src/workspace/migrations/134-image-generation-mode-to-provider.ts +4 -4
- package/src/workspace/migrations/136-repair-stale-fireworks-kimi-model-id.ts +2 -2
- package/src/workspace/migrations/137-repair-retired-fireworks-minimax-model-id.ts +2 -2
- package/src/workspace/migrations/139-clear-renamed-cost-profile-label.ts +2 -2
- package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +4 -4
- package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +1 -1
- package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +2 -2
- package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +2 -2
- package/src/workspace/migrations/148-strip-unsupported-fallback-profiles.ts +2 -2
- package/src/workspace/migrations/150-stt-flux-provider-to-model-family.ts +1 -1
- package/src/workspace/migrations/151-repair-renamed-fireworks-deepseek-pro-model-id.ts +2 -2
- package/src/workspace/migrations/152-repair-retired-fireworks-minimax-m2p7-model-id.ts +2 -2
- package/src/workspace/migrations/159-rename-colliding-jev-profile-name.ts +2 -2
- package/src/workspace/migrations/160-repair-retired-fireworks-model-ids.ts +2 -2
- package/src/workspace/migrations/161-repair-retired-fireworks-glm-5p2-model-id.ts +2 -2
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +1 -1
- package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +1 -1
- package/src/workspace/migrations/registry.ts +6 -6
- package/src/workspace/top-level-renderer.ts +1 -1
- package/src/workspace/turn-commit.ts +2 -2
- package/src/__tests__/config-loader-platform-defaults.test.ts +0 -606
- package/src/__tests__/mock-gateway-ipc.ts +0 -260
- package/src/api/events/platform-disconnected.ts +0 -21
- package/src/api/events/show-platform-login.ts +0 -20
- package/src/config/platform-rehydration.ts +0 -60
- package/src/platform/feature-gate.ts +0 -8
- package/src/plugins/defaults/platform-hosted/package.json +0 -14
- package/src/runtime/routes/platform-managed-credentials.ts +0 -50
- /package/src/__tests__/{gateway-client-managed-outbound.test.ts → gateway-client-cloud-outbound.test.ts} +0 -0
- /package/src/__tests__/{secure-keys-managed-failover.test.ts → secure-keys-cloud-failover.test.ts} +0 -0
|
@@ -35,19 +35,16 @@ import {
|
|
|
35
35
|
scrubNulledChatSettings,
|
|
36
36
|
validateChatSettingsWrite,
|
|
37
37
|
} from "../../config/chat-settings.js";
|
|
38
|
-
import { DefaultProviderSelectionSchema } from "../../config/default-provider.js";
|
|
39
38
|
import {
|
|
40
39
|
catalogEntryFor,
|
|
41
|
-
type InputModalities,
|
|
42
40
|
modalitiesOf,
|
|
43
41
|
resolveModalityOverride,
|
|
44
42
|
} from "../../config/input-modalities.js";
|
|
45
43
|
import {
|
|
46
44
|
deepMergeOverwrite,
|
|
47
|
-
fillContextDefaultsForMissingKeys,
|
|
48
45
|
getConfig,
|
|
49
|
-
getDeploymentContextDefaults,
|
|
50
46
|
invalidateConfigCache,
|
|
47
|
+
loadConfig,
|
|
51
48
|
loadRawConfig,
|
|
52
49
|
saveRawConfig,
|
|
53
50
|
setNestedValue,
|
|
@@ -58,22 +55,21 @@ import {
|
|
|
58
55
|
completeCustomProfile,
|
|
59
56
|
mergePreservingUnknownKeys,
|
|
60
57
|
} from "../../config/profile-materialization.js";
|
|
61
|
-
import {
|
|
58
|
+
import {
|
|
59
|
+
type AssistantConfig,
|
|
60
|
+
AssistantConfigSchema,
|
|
61
|
+
} from "../../config/schema.js";
|
|
62
62
|
import { getSchemaAtPath } from "../../config/schema-utils.js";
|
|
63
|
-
import { AutoArchiveConfigSchema } from "../../config/schemas/conversations.js";
|
|
64
63
|
import {
|
|
65
64
|
collectFallbackProfileIssues,
|
|
66
|
-
isDefaultProviderChoice,
|
|
67
65
|
LLMConfigBase,
|
|
68
66
|
LLMConfigFragment,
|
|
69
|
-
LLMDefaultConfigSchema,
|
|
70
67
|
LLMSchema,
|
|
71
68
|
ProfileEntry,
|
|
72
69
|
routingIdentityModelIssue,
|
|
73
70
|
unknownLlmProviderIssue,
|
|
74
71
|
} from "../../config/schemas/llm.js";
|
|
75
72
|
import { VALID_MEMORY_EMBEDDING_PROVIDERS } from "../../config/schemas/memory-storage.js";
|
|
76
|
-
import { NotificationsConfigSchema } from "../../config/schemas/notifications.js";
|
|
77
73
|
import { ServiceModeSchema } from "../../config/schemas/services.js";
|
|
78
74
|
import { isSidebarDoneEnabled } from "../../config/sidebar-done-gate.js";
|
|
79
75
|
import {
|
|
@@ -123,12 +119,12 @@ import { type LogRow } from "../../persistence/llm-request-log-store.js";
|
|
|
123
119
|
import { getMemoryRecallLogByMessageIds } from "../../plugins/defaults/memory/memory-recall-log-store.js";
|
|
124
120
|
import { getMemoryV2ActivationLogByMessageIds } from "../../plugins/defaults/memory/v2/activation-log-store.js";
|
|
125
121
|
import { getMemoryV3SelectionForInspectorByMessageIds } from "../../plugins/defaults/memory/v3/selection-log-store.js";
|
|
126
|
-
import {
|
|
122
|
+
import { CLOUD_ROUTABLE_PROVIDERS } from "../../providers/cadet-model-routing.js";
|
|
127
123
|
import { writableProfileProviderIssue } from "../../providers/connection-resolution.js";
|
|
128
124
|
import { ROUTING_IDENTITY_PROVIDERS } from "../../providers/inference/auth.js";
|
|
129
125
|
import { PROVIDERS_REQUIRING_BASE_URL_AND_MODELS } from "../../providers/inference/auth.js";
|
|
130
126
|
import {
|
|
131
|
-
|
|
127
|
+
CADET_CLOUD_CONNECTION_NAME,
|
|
132
128
|
createConnection,
|
|
133
129
|
getConnection,
|
|
134
130
|
LEGACY_MANAGED_CONNECTION_NAMES,
|
|
@@ -527,217 +523,22 @@ async function handleSetEmbeddingConfig({ body }: RouteHandlerArgs) {
|
|
|
527
523
|
}
|
|
528
524
|
|
|
529
525
|
/**
|
|
530
|
-
*
|
|
531
|
-
*
|
|
532
|
-
*
|
|
533
|
-
*
|
|
534
|
-
*
|
|
535
|
-
*
|
|
536
|
-
*
|
|
537
|
-
* absent from disk (only `llm.profiles` was written by
|
|
538
|
-
* `seedInferenceProfiles`); the fill pass ensures clients receive `"cadet"`
|
|
539
|
-
* rather than falling back to their own defaults.
|
|
540
|
-
*
|
|
541
|
-
* Guards against `loadRawConfig()` handing us a value that is technically
|
|
542
|
-
* valid JSON but not a plain object (e.g. literal `null`, a number, or an
|
|
543
|
-
* array). `loadRawConfig` is typed `Record<string, unknown>` but `JSON.parse`
|
|
544
|
-
* itself doesn't enforce that — a malformed-but-parseable `config.json`
|
|
545
|
-
* would blow up `fillContextDefaultsForMissingKeys` on its `target[key]` /
|
|
546
|
-
* `fileConfig[key]` accesses, turning `GET /v1/config` into a 500 where it
|
|
547
|
-
* used to succeed (returning the malformed payload as-is). When `raw` is
|
|
548
|
-
* not a plain object, we return it unchanged.
|
|
549
|
-
*
|
|
550
|
-
* Exported for direct unit testing.
|
|
526
|
+
* Tolerant shape parses for raw config-write fragments — zod replaces the
|
|
527
|
+
* hand-rolled plain-object narrowing (`readPlainObject`) at every guard.
|
|
528
|
+
* They model exactly the fields each guard reads and pass everything else
|
|
529
|
+
* through, so a write guard sees intent even when the surrounding payload
|
|
530
|
+
* is sparse or malformed. A parse copies, so mutating guards write the
|
|
531
|
+
* normalized slice back onto the caller's object instead of relying on
|
|
532
|
+
* shared references.
|
|
551
533
|
*/
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
)
|
|
560
|
-
return raw;
|
|
561
|
-
}
|
|
562
|
-
fillContextDefaultsForMissingKeys(
|
|
563
|
-
raw as Record<string, unknown>,
|
|
564
|
-
raw as Record<string, unknown>,
|
|
565
|
-
contextDefaults,
|
|
566
|
-
);
|
|
567
|
-
synthesizeLegacyInferenceModeForPlatform(raw as Record<string, unknown>);
|
|
568
|
-
return raw;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
/**
|
|
572
|
-
* Backwards-compat wire field for `GET /v1/config`. PR removed
|
|
573
|
-
* `services.inference.mode` from the typed schema (routing is governed by
|
|
574
|
-
* `provider_connections` rows + per-profile `provider_connection`), but
|
|
575
|
-
* the macOS settings client (`SettingsStore.swift:loadServiceModes`) still
|
|
576
|
-
* reads this field and falls back to its `@Published` default of "your-own"
|
|
577
|
-
* when absent. On a platform-managed assistant served by a newer daemon and
|
|
578
|
-
* an older macOS client, that fallback would show the wrong mode in the UI
|
|
579
|
-
* until the user explicitly saved. Synthesize the value here so the wire
|
|
580
|
-
* shape stays compatible during the rollout window. Remove once the macOS
|
|
581
|
-
* Providers UI (the follow-up PR that retires this field on the client) has
|
|
582
|
-
* shipped to the majority of installs.
|
|
583
|
-
*
|
|
584
|
-
* The synthesis is wire-only: it never persists to disk and never reaches
|
|
585
|
-
* the typed `AssistantConfig` consumed by daemon-internal code. The on-disk
|
|
586
|
-
* config is stripped of `mode` by workspace migration 076.
|
|
587
|
-
*
|
|
588
|
-
* Only runs when this function is reached, which is guarded by
|
|
589
|
-
* `getDeploymentContextDefaults()` returning non-empty (IS_PLATFORM=true).
|
|
590
|
-
*/
|
|
591
|
-
function synthesizeLegacyInferenceModeForPlatform(
|
|
592
|
-
root: Record<string, unknown>,
|
|
593
|
-
): void {
|
|
594
|
-
const services = readPlainObject(root.services);
|
|
595
|
-
if (!services) {
|
|
596
|
-
return;
|
|
597
|
-
}
|
|
598
|
-
let inference = readPlainObject(services.inference);
|
|
599
|
-
if (!inference) {
|
|
600
|
-
inference = {};
|
|
601
|
-
services.inference = inference;
|
|
602
|
-
}
|
|
603
|
-
if (inference.mode === undefined) {
|
|
604
|
-
inference.mode = "managed";
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
function readPlainObject(value: unknown): Record<string, unknown> | undefined {
|
|
609
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
610
|
-
return undefined;
|
|
611
|
-
}
|
|
612
|
-
return value as Record<string, unknown>;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
function stripTransportHeadersRecursively(value: unknown): void {
|
|
616
|
-
if (Array.isArray(value)) {
|
|
617
|
-
for (const item of value) {
|
|
618
|
-
stripTransportHeadersRecursively(item);
|
|
619
|
-
}
|
|
620
|
-
return;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
const object = readPlainObject(value);
|
|
624
|
-
if (!object) {
|
|
625
|
-
return;
|
|
626
|
-
}
|
|
627
|
-
const transport = readPlainObject(object.transport);
|
|
628
|
-
if (transport) {
|
|
629
|
-
delete transport.headers;
|
|
630
|
-
}
|
|
631
|
-
for (const child of Object.values(object)) {
|
|
632
|
-
stripTransportHeadersRecursively(child);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
function containsTransportHeadersRecursively(value: unknown): boolean {
|
|
637
|
-
if (Array.isArray(value)) {
|
|
638
|
-
return value.some((item) => containsTransportHeadersRecursively(item));
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
const object = readPlainObject(value);
|
|
642
|
-
if (!object) {
|
|
643
|
-
return false;
|
|
644
|
-
}
|
|
645
|
-
const transport = readPlainObject(object.transport);
|
|
646
|
-
if (transport && Object.hasOwn(transport, "headers")) {
|
|
647
|
-
return true;
|
|
648
|
-
}
|
|
649
|
-
return Object.values(object).some((child) =>
|
|
650
|
-
containsTransportHeadersRecursively(child),
|
|
651
|
-
);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
function sanitizeMcpTransportHeadersForSettingsRead(config: unknown): void {
|
|
655
|
-
const root = readPlainObject(config);
|
|
656
|
-
if (!root) {
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
const mcp = readPlainObject(root.mcp);
|
|
660
|
-
if (!mcp || !Object.hasOwn(mcp, "servers")) {
|
|
661
|
-
return;
|
|
662
|
-
}
|
|
663
|
-
if (Array.isArray(mcp.servers)) {
|
|
664
|
-
stripTransportHeadersRecursively(mcp.servers);
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
const servers = readPlainObject(mcp.servers);
|
|
668
|
-
if (!servers) {
|
|
669
|
-
return;
|
|
670
|
-
}
|
|
671
|
-
for (const server of Object.values(servers)) {
|
|
672
|
-
stripTransportHeadersRecursively(server);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
function patchContainsMcpTransportHeaders(patch: unknown): boolean {
|
|
677
|
-
const root = readPlainObject(patch);
|
|
678
|
-
const mcp = readPlainObject(root?.mcp);
|
|
679
|
-
if (!mcp || !Object.hasOwn(mcp, "servers")) {
|
|
680
|
-
return false;
|
|
681
|
-
}
|
|
682
|
-
if (Array.isArray(mcp.servers)) {
|
|
683
|
-
return containsTransportHeadersRecursively(mcp.servers);
|
|
684
|
-
}
|
|
685
|
-
const servers = readPlainObject(mcp.servers);
|
|
686
|
-
if (!servers) {
|
|
687
|
-
return false;
|
|
688
|
-
}
|
|
689
|
-
return Object.values(servers).some((server) =>
|
|
690
|
-
containsTransportHeadersRecursively(server),
|
|
691
|
-
);
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
function rejectMcpTransportHeaderWrite(patch: unknown): void {
|
|
695
|
-
if (!patchContainsMcpTransportHeaders(patch)) {
|
|
696
|
-
return;
|
|
697
|
-
}
|
|
698
|
-
throw new BadRequestError(
|
|
699
|
-
"MCP authentication headers must be managed through MCP server add/update APIs, not generic config writes.",
|
|
700
|
-
);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
const MCP_CONFIG_WRITE_MESSAGE =
|
|
704
|
-
"MCP servers are stored in mcp.json. Use assistant mcp add or assistant mcp remove.";
|
|
705
|
-
|
|
706
|
-
function stripMcpFromConfigWrite(patch: Record<string, unknown>): void {
|
|
707
|
-
if (Object.hasOwn(patch, "mcp")) {
|
|
708
|
-
delete patch.mcp;
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
function rejectMcpConfigSetPath(path: string, value: unknown): void {
|
|
713
|
-
if (path !== "mcp" && !path.startsWith("mcp.")) {
|
|
714
|
-
return;
|
|
715
|
-
}
|
|
716
|
-
const relative =
|
|
717
|
-
path === "mcp" ? value : nestPath(path.slice("mcp.".length), value);
|
|
718
|
-
if (patchContainsMcpTransportHeaders({ mcp: relative })) {
|
|
719
|
-
throw new BadRequestError(
|
|
720
|
-
"MCP authentication headers must be managed through MCP server add/update APIs, not generic config writes.",
|
|
721
|
-
);
|
|
722
|
-
}
|
|
723
|
-
throw new BadRequestError(MCP_CONFIG_WRITE_MESSAGE);
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
function nestPath(path: string, value: unknown): unknown {
|
|
727
|
-
const segments = path.split(".");
|
|
728
|
-
let current: unknown = value;
|
|
729
|
-
for (let i = segments.length - 1; i >= 0; i--) {
|
|
730
|
-
current = { [segments[i]!]: current };
|
|
731
|
-
}
|
|
732
|
-
return current;
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
const WireProfileEntry = ProfileEntry.extend({
|
|
736
|
-
supportsVision: z.boolean().optional(),
|
|
737
|
-
invariant: z.boolean().optional(),
|
|
738
|
-
})
|
|
739
|
-
.passthrough()
|
|
740
|
-
.meta({ id: "ProfileEntry" });
|
|
534
|
+
const JsonRecordSchema = z.record(z.string(), z.unknown());
|
|
535
|
+
const RawProfileEntrySchema = z.record(z.string(), z.unknown());
|
|
536
|
+
const RawLlmWriteSliceSchema = z
|
|
537
|
+
.object({
|
|
538
|
+
default: z.unknown().optional(),
|
|
539
|
+
profiles: z.record(z.string(), z.unknown()).nullable().optional(),
|
|
540
|
+
})
|
|
541
|
+
.passthrough();
|
|
741
542
|
|
|
742
543
|
/**
|
|
743
544
|
* Wire shape of the `memory` section in config responses. Passthrough
|
|
@@ -752,97 +553,6 @@ const MemoryWireConfigSchema = z
|
|
|
752
553
|
.passthrough()
|
|
753
554
|
.meta({ id: "MemoryConfig" });
|
|
754
555
|
|
|
755
|
-
/**
|
|
756
|
-
* Response schema for `GET /v1/config`.
|
|
757
|
-
*
|
|
758
|
-
* Describes the wire shape of the raw `settings.json` response after
|
|
759
|
-
* context-default filling and vision-flag enrichment. Chat settings include
|
|
760
|
-
* effective defaults. Fields remain optional for older assistants and sparse
|
|
761
|
-
* on-disk config. Additional top-level sections are preserved via
|
|
762
|
-
* passthrough. This schema types the fields that web/macOS clients consume
|
|
763
|
-
* without restricting the full config surface.
|
|
764
|
-
*/
|
|
765
|
-
const ConfigGetResponseSchema = z
|
|
766
|
-
.object({
|
|
767
|
-
// A malformed or absent stored chat-settings subtree falls back to its
|
|
768
|
-
// schema defaults so one bad leaf can't fail the whole wire parse (which
|
|
769
|
-
// would strip every derived field from the response); the chat-settings
|
|
770
|
-
// read overlay refills both from the effective config.
|
|
771
|
-
conversations: z
|
|
772
|
-
.object({
|
|
773
|
-
autoArchive: AutoArchiveConfigSchema.passthrough().catch(
|
|
774
|
-
AutoArchiveConfigSchema.parse({}),
|
|
775
|
-
),
|
|
776
|
-
})
|
|
777
|
-
.passthrough()
|
|
778
|
-
.optional(),
|
|
779
|
-
notifications: NotificationsConfigSchema.passthrough()
|
|
780
|
-
.optional()
|
|
781
|
-
.catch(undefined),
|
|
782
|
-
llm: z
|
|
783
|
-
.object({
|
|
784
|
-
default: LLMDefaultConfigSchema.optional(),
|
|
785
|
-
// Wire-compat view of the default provider, derived from `default`
|
|
786
|
-
// by the read overlay (the key is never stored anymore).
|
|
787
|
-
defaultProvider: DefaultProviderSelectionSchema.optional(),
|
|
788
|
-
profiles: z.record(z.string(), WireProfileEntry).optional(),
|
|
789
|
-
profileOrder: z.array(z.string()).optional(),
|
|
790
|
-
activeProfile: z.string().optional(),
|
|
791
|
-
// The profile the advisor consults; excluded from chat-profile pickers.
|
|
792
|
-
advisorProfile: z.string().optional(),
|
|
793
|
-
callSites: z
|
|
794
|
-
.record(
|
|
795
|
-
z.string(),
|
|
796
|
-
LLMConfigFragment.extend({
|
|
797
|
-
profile: z.string().optional(),
|
|
798
|
-
}).nullable(),
|
|
799
|
-
)
|
|
800
|
-
.optional(),
|
|
801
|
-
profileSession: z
|
|
802
|
-
.object({
|
|
803
|
-
defaultTtlSeconds: z.number().optional(),
|
|
804
|
-
maxTtlSeconds: z.number().optional(),
|
|
805
|
-
})
|
|
806
|
-
.optional(),
|
|
807
|
-
pricingOverrides: z.array(z.unknown()).optional(),
|
|
808
|
-
})
|
|
809
|
-
.passthrough()
|
|
810
|
-
.optional(),
|
|
811
|
-
memory: MemoryWireConfigSchema.optional(),
|
|
812
|
-
services: z
|
|
813
|
-
.object({
|
|
814
|
-
"web-search": z
|
|
815
|
-
.object({
|
|
816
|
-
provider: z.string().optional(),
|
|
817
|
-
})
|
|
818
|
-
.passthrough()
|
|
819
|
-
.optional(),
|
|
820
|
-
"web-fetch": z
|
|
821
|
-
.object({
|
|
822
|
-
provider: z.string().optional(),
|
|
823
|
-
})
|
|
824
|
-
.passthrough()
|
|
825
|
-
.optional(),
|
|
826
|
-
"image-generation": z
|
|
827
|
-
.object({
|
|
828
|
-
provider: z.string().optional(),
|
|
829
|
-
model: z.string().optional(),
|
|
830
|
-
})
|
|
831
|
-
.passthrough()
|
|
832
|
-
.optional(),
|
|
833
|
-
inference: z
|
|
834
|
-
.object({ mode: ServiceModeSchema.optional() })
|
|
835
|
-
.passthrough()
|
|
836
|
-
.optional(),
|
|
837
|
-
})
|
|
838
|
-
.passthrough()
|
|
839
|
-
.optional(),
|
|
840
|
-
})
|
|
841
|
-
.passthrough()
|
|
842
|
-
.meta({ id: "ConfigGetResponse" });
|
|
843
|
-
|
|
844
|
-
type ConfigGetResponse = z.infer<typeof ConfigGetResponseSchema>;
|
|
845
|
-
|
|
846
556
|
/**
|
|
847
557
|
* Given a `z.object(...)` schema, returns a new schema where every property
|
|
848
558
|
* is `.nullable().optional()`. Used to express PATCH body semantics where any
|
|
@@ -880,7 +590,7 @@ const CallSiteOverrideDraftSchema = nullablePartial(
|
|
|
880
590
|
*
|
|
881
591
|
* Mirrors the response shape but every field is `.nullable().optional()`:
|
|
882
592
|
* omitted keys are left unchanged by the daemon's deep-merge, `null` values
|
|
883
|
-
* delete the key. Uses the same Zod enums as `
|
|
593
|
+
* delete the key. Uses the same Zod enums as `AssistantConfigSchema` so
|
|
884
594
|
* the generated SDK produces literal-union types — no hand-written patch
|
|
885
595
|
* types needed downstream.
|
|
886
596
|
*/
|
|
@@ -953,54 +663,39 @@ const ConfigPatchRequestSchema = z
|
|
|
953
663
|
.passthrough()
|
|
954
664
|
.meta({ id: "ConfigPatchRequest" });
|
|
955
665
|
|
|
666
|
+
/**
|
|
667
|
+
* The response contract for `GET`/`PATCH /v1/config` — the five subtrees
|
|
668
|
+
* external clients actually read, picked out of the full graph. Rendering
|
|
669
|
+
* the whole `AssistantConfigSchema` into the spec blasted openapi.yaml past
|
|
670
|
+
* 12K lines; we're not ready to trim the response schema itself yet, so the
|
|
671
|
+
* runtime still returns the full config and the spec documents these.
|
|
672
|
+
*/
|
|
673
|
+
const ConfigGetResponseSchema = AssistantConfigSchema.pick({
|
|
674
|
+
conversations: true,
|
|
675
|
+
notifications: true,
|
|
676
|
+
llm: true,
|
|
677
|
+
memory: true,
|
|
678
|
+
services: true,
|
|
679
|
+
});
|
|
680
|
+
|
|
956
681
|
function handleGetConfig() {
|
|
957
682
|
try {
|
|
958
|
-
|
|
959
|
-
//
|
|
960
|
-
//
|
|
961
|
-
// the
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
overlayWorkspaceMcpForConfigRead(wire);
|
|
969
|
-
sanitizeMcpTransportHeadersForSettingsRead(wire);
|
|
970
|
-
overlayEffectiveProfilesForWire(wire);
|
|
971
|
-
enrichProfilesForWire(wire);
|
|
972
|
-
overlayDefaultProviderForWire(wire);
|
|
973
|
-
overlayChatSettingsForWire(wire);
|
|
974
|
-
return wire;
|
|
683
|
+
// The overlays stamp wire-only state (catalog bodies, effective chat
|
|
684
|
+
// settings, capability flags) onto the returned object. Clone first so
|
|
685
|
+
// the loader's cached config — and every other `getConfig()` reader,
|
|
686
|
+
// including the config-write merge base — never sees the wire view.
|
|
687
|
+
const config = structuredClone(loadConfig());
|
|
688
|
+
overlayWorkspaceMcpForConfigRead(config);
|
|
689
|
+
overlayEffectiveProfilesForWire(config);
|
|
690
|
+
enrichProfilesForWire(config);
|
|
691
|
+
overlayChatSettingsForWire(config);
|
|
692
|
+
return config;
|
|
975
693
|
} catch (err) {
|
|
976
694
|
const message = err instanceof Error ? err.message : String(err);
|
|
977
695
|
throw new InternalError(`Failed to read config: ${message}`);
|
|
978
696
|
}
|
|
979
697
|
}
|
|
980
|
-
|
|
981
|
-
/**
|
|
982
|
-
* Surface the default-provider selection on config reads. The selection lives
|
|
983
|
-
* in the `llm.default` blob; the dedicated `defaultProvider` response field is
|
|
984
|
-
* a derived wire-compat view (readers of the config GET predate the blob) and
|
|
985
|
-
* is never stored on disk — it is stamped here from the blob on every read.
|
|
986
|
-
*/
|
|
987
|
-
function overlayDefaultProviderForWire(config: ConfigGetResponse): void {
|
|
988
|
-
if (!config.llm) {
|
|
989
|
-
return;
|
|
990
|
-
}
|
|
991
|
-
const blob = config.llm.default;
|
|
992
|
-
const provider = blob?.provider;
|
|
993
|
-
if (provider != null && isDefaultProviderChoice(provider)) {
|
|
994
|
-
config.llm.defaultProvider = {
|
|
995
|
-
provider,
|
|
996
|
-
connectionName: blob?.provider_connection,
|
|
997
|
-
};
|
|
998
|
-
} else {
|
|
999
|
-
delete config.llm.defaultProvider;
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
function overlayChatSettingsForWire(config: ConfigGetResponse): void {
|
|
698
|
+
function overlayChatSettingsForWire(config: AssistantConfig): void {
|
|
1004
699
|
const effective = getConfig();
|
|
1005
700
|
config.conversations = {
|
|
1006
701
|
...config.conversations,
|
|
@@ -1023,7 +718,7 @@ function overlayChatSettingsForWire(config: ConfigGetResponse): void {
|
|
|
1023
718
|
* render labels/models — so the wire view materializes them, resolved through
|
|
1024
719
|
* `llm.default`'s column of the intent × provider matrix so a BYO
|
|
1025
720
|
* install sees the provider/model that actually dispatches. Wire-only:
|
|
1026
|
-
* `
|
|
721
|
+
* `normalizeCloudProfileWrites` reduces echoed bodies back to the
|
|
1027
722
|
* workspace-owned fields on the write paths, so a `config get` →
|
|
1028
723
|
* `config set` round-trip never persists catalog content — the two must
|
|
1029
724
|
* resolve through the same column or honest round-trips are rejected.
|
|
@@ -1033,47 +728,13 @@ function overlayChatSettingsForWire(config: ConfigGetResponse): void {
|
|
|
1033
728
|
* shields the matrix lookup from a persisted value the matrix cannot
|
|
1034
729
|
* implement, which a raw read would feed straight into resolution.
|
|
1035
730
|
*/
|
|
1036
|
-
function overlayEffectiveProfilesForWire(config:
|
|
1037
|
-
if (!config.llm) {
|
|
1038
|
-
config.llm = {};
|
|
1039
|
-
}
|
|
731
|
+
function overlayEffectiveProfilesForWire(config: AssistantConfig): void {
|
|
1040
732
|
const parsedLlm = LLMSchema.safeParse(config.llm);
|
|
1041
733
|
config.llm.profiles = getUserSelectableProfilesForProvider(
|
|
1042
734
|
parsedLlm.success ? parsedLlm.data : undefined,
|
|
1043
735
|
);
|
|
1044
736
|
}
|
|
1045
737
|
|
|
1046
|
-
/**
|
|
1047
|
-
* Per-profile keys that exist only on the wire — stamped onto config
|
|
1048
|
-
* responses by {@link enrichProfilesForWire}, never persisted to disk.
|
|
1049
|
-
* {@link stripWireOnlyProfileKeys} removes them from incoming writes so a
|
|
1050
|
-
* `config get` → `config set`/PATCH round-trip isn't rejected for phantom
|
|
1051
|
-
* fields; keep the stamp and strip lists in lock-step.
|
|
1052
|
-
*/
|
|
1053
|
-
const WIRE_ONLY_PROFILE_KEYS = new Set(["invariant", "supportsVision"]);
|
|
1054
|
-
|
|
1055
|
-
/**
|
|
1056
|
-
* Delete the wire-only keys ({@link WIRE_ONLY_PROFILE_KEYS}) from every
|
|
1057
|
-
* profile entry in a config-write fragment, in place.
|
|
1058
|
-
*/
|
|
1059
|
-
function stripWireOnlyProfileKeys(patch: unknown): void {
|
|
1060
|
-
const root = readPlainObject(patch);
|
|
1061
|
-
const llm = readPlainObject(root?.llm);
|
|
1062
|
-
const profiles = readPlainObject(llm?.profiles);
|
|
1063
|
-
if (!profiles) {
|
|
1064
|
-
return;
|
|
1065
|
-
}
|
|
1066
|
-
for (const profile of Object.values(profiles)) {
|
|
1067
|
-
const entry = readPlainObject(profile);
|
|
1068
|
-
if (!entry) {
|
|
1069
|
-
continue;
|
|
1070
|
-
}
|
|
1071
|
-
for (const key of WIRE_ONLY_PROFILE_KEYS) {
|
|
1072
|
-
delete entry[key];
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
738
|
/**
|
|
1078
739
|
* Normalize managed default-profile entries in a config-write fragment, in
|
|
1079
740
|
* place, so a `config get` → write round-trip of the enriched wire view is a
|
|
@@ -1093,33 +754,39 @@ function stripWireOnlyProfileKeys(patch: unknown): void {
|
|
|
1093
754
|
* shadowed or given content. A clean echo reduces to a no-op
|
|
1094
755
|
* `{source: "managed"}` stub.
|
|
1095
756
|
*
|
|
1096
|
-
*
|
|
1097
|
-
*
|
|
1098
|
-
*
|
|
757
|
+
* The llm slice is parsed through {@link RawLlmWriteSliceSchema} and the
|
|
758
|
+
* normalized slice written back — a zod parse copies, so the write-back (not
|
|
759
|
+
* shared references) is what carries the normalization to the caller's patch.
|
|
760
|
+
* `handleSetConfig` reads the written-back shape for entry-level SETs.
|
|
1099
761
|
*/
|
|
1100
|
-
export function
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
const
|
|
1104
|
-
if (!profiles) {
|
|
762
|
+
export function normalizeCloudProfileWrites(
|
|
763
|
+
patch: Record<string, unknown>,
|
|
764
|
+
): void {
|
|
765
|
+
const llm = RawLlmWriteSliceSchema.safeParse(patch.llm);
|
|
766
|
+
if (!llm.success || !llm.data.profiles) {
|
|
1105
767
|
return;
|
|
1106
768
|
}
|
|
769
|
+
const profiles = llm.data.profiles;
|
|
1107
770
|
|
|
1108
|
-
const
|
|
1109
|
-
|
|
1110
|
-
|
|
771
|
+
const currentLlm = RawLlmWriteSliceSchema.safeParse(loadRawConfig().llm);
|
|
772
|
+
const typedCurrent = currentLlm.success
|
|
773
|
+
? LLMSchema.shape.profiles.safeParse(currentLlm.data.profiles)
|
|
774
|
+
: undefined;
|
|
775
|
+
const currentProfiles: Record<string, unknown> | undefined =
|
|
776
|
+
typedCurrent?.success ? typedCurrent.data : undefined;
|
|
1111
777
|
|
|
1112
778
|
for (const name of Object.keys(profiles)) {
|
|
1113
779
|
if (!MANAGED_PROFILE_NAMES.has(name)) {
|
|
1114
780
|
continue;
|
|
1115
781
|
}
|
|
1116
|
-
const entry =
|
|
1117
|
-
if (!entry) {
|
|
782
|
+
const entry = RawProfileEntrySchema.safeParse(profiles[name]);
|
|
783
|
+
if (!entry.success) {
|
|
1118
784
|
continue;
|
|
1119
785
|
}
|
|
1120
786
|
|
|
1121
|
-
const current =
|
|
1122
|
-
|
|
787
|
+
const current = RawProfileEntrySchema.safeParse(currentProfiles?.[name]);
|
|
788
|
+
|
|
789
|
+
if (current.success && current.data.source !== "managed") {
|
|
1123
790
|
// Legacy user-owned shadow: fully editable, normal custom-profile rules.
|
|
1124
791
|
continue;
|
|
1125
792
|
}
|
|
@@ -1127,26 +794,27 @@ export function normalizeManagedProfileWrites(patch: unknown): void {
|
|
|
1127
794
|
// Must resolve through the same provider column as
|
|
1128
795
|
// `overlayEffectiveProfilesForWire`, or an echo of the wire view would
|
|
1129
796
|
// not match `effective` and an honest round-trip would be rejected.
|
|
1130
|
-
const
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
getConfig().llm.default,
|
|
1135
|
-
),
|
|
797
|
+
const resolved = resolveDefaultProfileForProvider(
|
|
798
|
+
typedCurrent?.success ? typedCurrent.data : undefined,
|
|
799
|
+
name,
|
|
800
|
+
getConfig().llm.default,
|
|
1136
801
|
);
|
|
1137
|
-
|
|
802
|
+
const effective: Record<string, unknown> | undefined = resolved
|
|
803
|
+
? { ...resolved }
|
|
804
|
+
: undefined;
|
|
805
|
+
for (const key of Object.keys(entry.data)) {
|
|
1138
806
|
if (key === "source" || key === "status") {
|
|
1139
807
|
continue;
|
|
1140
808
|
}
|
|
1141
809
|
const matchesEffective =
|
|
1142
810
|
effective != null &&
|
|
1143
811
|
key in effective &&
|
|
1144
|
-
isDeepStrictEqual(entry[key], effective[key]);
|
|
1145
|
-
if (current
|
|
812
|
+
isDeepStrictEqual(entry.data[key], effective[key]);
|
|
813
|
+
if (!current.success || !(key in current.data)) {
|
|
1146
814
|
// Not on disk: a value matching the wire view is a client echo of
|
|
1147
815
|
// catalog content — drop it. Anything else stays for the guard.
|
|
1148
816
|
if (matchesEffective) {
|
|
1149
|
-
delete entry[key];
|
|
817
|
+
delete entry.data[key];
|
|
1150
818
|
}
|
|
1151
819
|
continue;
|
|
1152
820
|
}
|
|
@@ -1156,93 +824,101 @@ export function normalizeManagedProfileWrites(patch: unknown): void {
|
|
|
1156
824
|
// from disk is pinned back to the on-disk value (no change for the
|
|
1157
825
|
// guard to reject); everything else is left for the guard's
|
|
1158
826
|
// frozen-field comparison.
|
|
1159
|
-
if (
|
|
1160
|
-
|
|
827
|
+
if (
|
|
828
|
+
matchesEffective &&
|
|
829
|
+
!isDeepStrictEqual(entry.data[key], current.data[key])
|
|
830
|
+
) {
|
|
831
|
+
entry.data[key] = current.data[key];
|
|
1161
832
|
}
|
|
1162
833
|
}
|
|
1163
834
|
|
|
1164
|
-
if (current) {
|
|
835
|
+
if (current.success) {
|
|
1165
836
|
// Existing stub: the guard compares the merged result against it and
|
|
1166
837
|
// rejects everything but the status re-enable.
|
|
1167
|
-
if (entry.source === undefined) {
|
|
1168
|
-
entry.source = "managed";
|
|
838
|
+
if (entry.data.source === undefined) {
|
|
839
|
+
entry.data.source = "managed";
|
|
1169
840
|
}
|
|
841
|
+
profiles[name] = entry.data;
|
|
1170
842
|
continue;
|
|
1171
843
|
}
|
|
1172
844
|
|
|
1173
845
|
// No on-disk entry: the name is catalog-owned. Nothing but a clean echo
|
|
1174
|
-
// (which reduced to source/status above) may pass.
|
|
1175
|
-
|
|
846
|
+
// (which reduced to source/status above) may pass. The wire-only flags
|
|
847
|
+
// ride echoes too — the write path's schema parse strips them before
|
|
848
|
+
// anything persists.
|
|
849
|
+
if ("source" in entry.data && entry.data.source !== "managed") {
|
|
1176
850
|
throw new BadRequestError(
|
|
1177
851
|
`Cannot create profile "${name}" — the name is reserved for a code-defined default profile.`,
|
|
1178
852
|
);
|
|
1179
853
|
}
|
|
1180
854
|
if (
|
|
1181
|
-
"status" in entry &&
|
|
1182
|
-
entry.status != null &&
|
|
1183
|
-
entry.status !== "active"
|
|
855
|
+
"status" in entry.data &&
|
|
856
|
+
entry.data.status != null &&
|
|
857
|
+
entry.data.status !== "active"
|
|
1184
858
|
) {
|
|
1185
|
-
throw new BadRequestError(`Cannot disable
|
|
859
|
+
throw new BadRequestError(`Cannot disable cloud profile "${name}".`);
|
|
1186
860
|
}
|
|
1187
|
-
const residual = Object.keys(entry).filter(
|
|
1188
|
-
(key) =>
|
|
861
|
+
const residual = Object.keys(entry.data).filter(
|
|
862
|
+
(key) =>
|
|
863
|
+
key !== "source" &&
|
|
864
|
+
key !== "status" &&
|
|
865
|
+
key !== "invariant" &&
|
|
866
|
+
key !== "supportsVision",
|
|
1189
867
|
);
|
|
1190
868
|
if (residual.length > 0) {
|
|
1191
869
|
throw new BadRequestError(
|
|
1192
|
-
`Cannot edit
|
|
1193
|
-
`
|
|
870
|
+
`Cannot edit cloud profile "${name}" fields [${residual.join(", ")}]. ` +
|
|
871
|
+
`Cloud profiles are read-only; duplicate to a custom profile to customize.`,
|
|
1194
872
|
);
|
|
1195
873
|
}
|
|
1196
|
-
entry.source = "managed";
|
|
874
|
+
entry.data.source = "managed";
|
|
875
|
+
profiles[name] = entry.data;
|
|
1197
876
|
}
|
|
877
|
+
|
|
878
|
+
patch.llm = llm.data;
|
|
1198
879
|
}
|
|
1199
880
|
|
|
1200
881
|
/**
|
|
1201
|
-
*
|
|
1202
|
-
*
|
|
882
|
+
* The enriched profile shape clients consume: {@link ProfileEntry} plus the
|
|
883
|
+
* wire-only flags `enrichProfilesForWire` stamps onto config-read responses.
|
|
884
|
+
* The flags are deliberately absent from `ProfileEntry` — the persisted
|
|
885
|
+
* schema — so a config write's schema parse strips echoed copies instead of
|
|
886
|
+
* persisting them.
|
|
887
|
+
*/
|
|
888
|
+
type WireProfileEntry = ProfileEntry & {
|
|
889
|
+
invariant?: boolean;
|
|
890
|
+
supportsVision?: boolean;
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Annotate each profile in `config.llm.profiles` with wire-only flags —
|
|
895
|
+
* never persisted to disk:
|
|
1203
896
|
*
|
|
1204
897
|
* - `supportsVision`: resolved from the model catalog, with a profile
|
|
1205
898
|
* `inputModalities.image` override winning when set. Unknown (provider,
|
|
1206
899
|
* model) pairs with no override default to `true` (fail-open) so image
|
|
1207
900
|
* upload remains available for custom / unlisted models.
|
|
1208
|
-
* - `invariant`: `true` for managed-source entries of the
|
|
901
|
+
* - `invariant`: `true` for managed-source entries of the cloud profile
|
|
1209
902
|
* names (`INVARIANT_PROFILE_NAMES`); absent otherwise. Source-gated to
|
|
1210
903
|
* match `assertInvariantProfilesPreserved` — a user-owned profile sharing
|
|
1211
|
-
* a
|
|
904
|
+
* a cloud name is fully editable, so it must render as a normal custom
|
|
1212
905
|
* profile.
|
|
1213
906
|
*/
|
|
1214
|
-
function enrichProfilesForWire(config:
|
|
1215
|
-
const
|
|
1216
|
-
|
|
1217
|
-
return;
|
|
1218
|
-
}
|
|
1219
|
-
const llm = readPlainObject(root.llm);
|
|
1220
|
-
if (!llm) {
|
|
1221
|
-
return;
|
|
1222
|
-
}
|
|
1223
|
-
const profiles = readPlainObject(llm.profiles);
|
|
1224
|
-
if (!profiles) {
|
|
1225
|
-
return;
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
for (const [name, profile] of Object.entries(profiles)) {
|
|
1229
|
-
const entry = readPlainObject(profile);
|
|
1230
|
-
if (!entry) {
|
|
1231
|
-
continue;
|
|
1232
|
-
}
|
|
907
|
+
function enrichProfilesForWire(config: AssistantConfig): void {
|
|
908
|
+
const profiles: Record<string, WireProfileEntry> = config.llm.profiles;
|
|
909
|
+
for (const [name, entry] of Object.entries(profiles)) {
|
|
1233
910
|
if (INVARIANT_PROFILE_NAMES.has(name) && entry.source === "managed") {
|
|
1234
911
|
entry.invariant = true;
|
|
1235
912
|
}
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
if (typeof provider !== "string" || typeof model !== "string") {
|
|
913
|
+
|
|
914
|
+
if (entry.provider == null || entry.model == null) {
|
|
1239
915
|
continue;
|
|
1240
916
|
}
|
|
1241
917
|
|
|
1242
|
-
const catalogModel = catalogEntryFor(provider, model);
|
|
918
|
+
const catalogModel = catalogEntryFor(entry.provider, entry.model);
|
|
1243
919
|
const catalogVision = catalogModel?.supportsVision;
|
|
1244
920
|
const imageOverride = modalitiesOf({
|
|
1245
|
-
inputModalities: entry.inputModalities
|
|
921
|
+
inputModalities: entry.inputModalities,
|
|
1246
922
|
})?.image;
|
|
1247
923
|
entry.supportsVision =
|
|
1248
924
|
resolveModalityOverride(imageOverride, catalogVision) ?? true;
|
|
@@ -1281,44 +957,47 @@ function handleGetConfigSchema({ queryParams = {} }: RouteHandlerArgs) {
|
|
|
1281
957
|
};
|
|
1282
958
|
}
|
|
1283
959
|
|
|
1284
|
-
export function
|
|
960
|
+
export function rejectCloudProfileDeletion(
|
|
1285
961
|
body: Record<string, unknown>,
|
|
1286
962
|
): void {
|
|
1287
|
-
const llm =
|
|
1288
|
-
if (!llm) {
|
|
963
|
+
const llm = RawLlmWriteSliceSchema.safeParse(body.llm);
|
|
964
|
+
if (!llm.success) {
|
|
1289
965
|
return;
|
|
1290
966
|
}
|
|
1291
|
-
if (
|
|
967
|
+
if (llm.data.profiles === null) {
|
|
1292
968
|
throw new BadRequestError(
|
|
1293
|
-
"Cannot null llm.profiles —
|
|
969
|
+
"Cannot null llm.profiles — cloud profiles would be deleted.",
|
|
1294
970
|
);
|
|
1295
971
|
}
|
|
1296
|
-
const profiles =
|
|
972
|
+
const profiles = llm.data.profiles;
|
|
1297
973
|
if (!profiles) {
|
|
1298
974
|
return;
|
|
1299
975
|
}
|
|
1300
|
-
const
|
|
976
|
+
const existingLlm = RawLlmWriteSliceSchema.safeParse(getConfig().llm);
|
|
977
|
+
const existingProfiles = existingLlm.success
|
|
978
|
+
? (existingLlm.data.profiles ?? {})
|
|
979
|
+
: {};
|
|
1301
980
|
for (const name of Object.keys(profiles)) {
|
|
1302
981
|
if (profiles[name] !== null || !MANAGED_PROFILE_NAMES.has(name)) {
|
|
1303
982
|
continue;
|
|
1304
983
|
}
|
|
1305
|
-
// Only block deletion when the on-disk entry is Cadet
|
|
1306
|
-
// user-owned profile sharing a
|
|
984
|
+
// Only block deletion when the on-disk entry is Cadet Cloud. A
|
|
985
|
+
// user-owned profile sharing a cloud name carries a non-managed `source`
|
|
1307
986
|
// and is freely deletable.
|
|
1308
|
-
const existing =
|
|
1309
|
-
if (existing
|
|
1310
|
-
throw new BadRequestError(`Cannot delete
|
|
987
|
+
const existing = RawProfileEntrySchema.safeParse(existingProfiles[name]);
|
|
988
|
+
if (existing.success && existing.data.source === "managed") {
|
|
989
|
+
throw new BadRequestError(`Cannot delete cloud profile "${name}".`);
|
|
1311
990
|
}
|
|
1312
991
|
}
|
|
1313
992
|
}
|
|
1314
993
|
|
|
1315
994
|
/**
|
|
1316
|
-
* Enforce the
|
|
995
|
+
* Enforce the cloud-profile invariants at the config-write choke point.
|
|
1317
996
|
*
|
|
1318
997
|
* Protects the thin managed stubs (`INVARIANT_PROFILE_NAMES`) that live in
|
|
1319
998
|
* workspace config. Default profile CONTENT is code-owned and only exists on
|
|
1320
999
|
* the wire, so the guard's job is the stub itself: it checks entries present
|
|
1321
|
-
* in the OLD config, and `
|
|
1000
|
+
* in the OLD config, and `normalizeCloudProfileWrites` reduces incoming
|
|
1322
1001
|
* managed entries to the workspace-owned fields before this comparison runs.
|
|
1323
1002
|
*
|
|
1324
1003
|
* Invariance is gated on managed ownership: a name is enforced only when the
|
|
@@ -1327,7 +1006,7 @@ export function rejectManagedProfileDeletion(
|
|
|
1327
1006
|
* is real and supported, because the flag-gated reconcile refuses to
|
|
1328
1007
|
* overwrite a same-named user profile.
|
|
1329
1008
|
*
|
|
1330
|
-
* For each invariant name where the OLD raw config carries a
|
|
1009
|
+
* For each invariant name where the OLD raw config carries a cloud-source
|
|
1331
1010
|
* plain-object entry at `llm.profiles[name]`:
|
|
1332
1011
|
*
|
|
1333
1012
|
* - The NEW raw config must still carry a plain-object entry at the same
|
|
@@ -1338,11 +1017,11 @@ export function rejectManagedProfileDeletion(
|
|
|
1338
1017
|
* profile can never be disabled; a changed `status` must be `"active"`,
|
|
1339
1018
|
* `null`, or absent — re-enabling a disabled profile. Any other value is
|
|
1340
1019
|
* rejected.
|
|
1341
|
-
* -
|
|
1342
|
-
*
|
|
1343
|
-
*
|
|
1344
|
-
* as a removed field would reject every
|
|
1345
|
-
* reseeds the profile.
|
|
1020
|
+
* - The wire-only flags (`invariant`, `supportsVision`) are ignored on both
|
|
1021
|
+
* sides: the write path's schema parse strips them from incoming writes,
|
|
1022
|
+
* but configs persisted before that was true may still carry them on
|
|
1023
|
+
* disk, and treating that stale key as a removed field would reject every
|
|
1024
|
+
* round-trip write until reboot reseeds the profile.
|
|
1346
1025
|
* - Every other field is frozen: any changed, added, or removed key across
|
|
1347
1026
|
* the union of both entries' keys (except `status` and the wire-only keys)
|
|
1348
1027
|
* is rejected. A pre-existing on-disk override (e.g. `topP`) is preserved
|
|
@@ -1376,17 +1055,17 @@ function assertInvariantProfilesPreserved(
|
|
|
1376
1055
|
: null;
|
|
1377
1056
|
if (!newEntry) {
|
|
1378
1057
|
throw new BadRequestError(
|
|
1379
|
-
`Cannot delete or replace
|
|
1058
|
+
`Cannot delete or replace cloud profile "${name}". Cloud profiles are read-only.`,
|
|
1380
1059
|
);
|
|
1381
1060
|
}
|
|
1382
1061
|
|
|
1383
1062
|
if (!isDeepStrictEqual(oldEntry.status, newEntry.status)) {
|
|
1384
1063
|
if (newEntry.status === "disabled") {
|
|
1385
|
-
throw new BadRequestError(`Cannot disable
|
|
1064
|
+
throw new BadRequestError(`Cannot disable cloud profile "${name}".`);
|
|
1386
1065
|
}
|
|
1387
1066
|
if (newEntry.status !== "active" && newEntry.status != null) {
|
|
1388
1067
|
throw new BadRequestError(
|
|
1389
|
-
`Cannot set status ${JSON.stringify(newEntry.status)} on
|
|
1068
|
+
`Cannot set status ${JSON.stringify(newEntry.status)} on cloud profile "${name}". ` +
|
|
1390
1069
|
`Only re-enabling (status "active") is allowed.`,
|
|
1391
1070
|
);
|
|
1392
1071
|
}
|
|
@@ -1397,13 +1076,14 @@ function assertInvariantProfilesPreserved(
|
|
|
1397
1076
|
].filter(
|
|
1398
1077
|
(key) =>
|
|
1399
1078
|
key !== "status" &&
|
|
1400
|
-
|
|
1079
|
+
key !== "invariant" &&
|
|
1080
|
+
key !== "supportsVision" &&
|
|
1401
1081
|
!isDeepStrictEqual(oldEntry[key], newEntry[key]),
|
|
1402
1082
|
);
|
|
1403
1083
|
if (changedKeys.length > 0) {
|
|
1404
1084
|
throw new BadRequestError(
|
|
1405
|
-
`Cannot edit
|
|
1406
|
-
`
|
|
1085
|
+
`Cannot edit cloud profile "${name}" fields [${changedKeys.join(", ")}]. ` +
|
|
1086
|
+
`Cloud profiles are read-only; duplicate to a custom profile to customize.`,
|
|
1407
1087
|
);
|
|
1408
1088
|
}
|
|
1409
1089
|
}
|
|
@@ -1422,17 +1102,17 @@ function completeChangedCustomProfiles(
|
|
|
1422
1102
|
preWrite: Record<string, unknown>,
|
|
1423
1103
|
raw: Record<string, unknown>,
|
|
1424
1104
|
): void {
|
|
1425
|
-
const
|
|
1426
|
-
if (!profiles) {
|
|
1105
|
+
const llm = RawLlmWriteSliceSchema.safeParse(raw.llm);
|
|
1106
|
+
if (!llm.success || !llm.data.profiles) {
|
|
1427
1107
|
return;
|
|
1428
1108
|
}
|
|
1429
|
-
const
|
|
1430
|
-
|
|
1431
|
-
);
|
|
1109
|
+
const profiles = llm.data.profiles;
|
|
1110
|
+
const parsedDefault = LLMConfigBase.safeParse(llm.data.default ?? {});
|
|
1432
1111
|
if (!parsedDefault.success) {
|
|
1433
1112
|
return;
|
|
1434
1113
|
}
|
|
1435
|
-
const
|
|
1114
|
+
const priorLlm = RawLlmWriteSliceSchema.safeParse(preWrite.llm);
|
|
1115
|
+
const prior = priorLlm.success ? (priorLlm.data.profiles ?? {}) : {};
|
|
1436
1116
|
for (const [name, entry] of Object.entries(profiles)) {
|
|
1437
1117
|
if (isDeepStrictEqual(entry, prior[name])) {
|
|
1438
1118
|
continue;
|
|
@@ -1452,14 +1132,20 @@ function completeChangedCustomProfiles(
|
|
|
1452
1132
|
// depth: `safeParse` strips keys the schema doesn't know (top-level and
|
|
1453
1133
|
// inside nested objects like `contextWindow`), and dropping them here
|
|
1454
1134
|
// would delete forward-compatible fields whenever the entry is edited.
|
|
1135
|
+
const entryRecord = RawProfileEntrySchema.safeParse(entry);
|
|
1136
|
+
if (!entryRecord.success) {
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1455
1139
|
profiles[name] = mergePreservingUnknownKeys(
|
|
1456
|
-
|
|
1140
|
+
entryRecord.data,
|
|
1457
1141
|
completeCustomProfile(parsedDefault.data, entryData) as Record<
|
|
1458
1142
|
string,
|
|
1459
1143
|
unknown
|
|
1460
1144
|
>,
|
|
1461
1145
|
);
|
|
1462
1146
|
}
|
|
1147
|
+
// A zod parse copies — write the completed slice back so `raw` carries it.
|
|
1148
|
+
raw.llm = llm.data;
|
|
1463
1149
|
}
|
|
1464
1150
|
|
|
1465
1151
|
/**
|
|
@@ -1559,19 +1245,58 @@ function assertRoutableIdentityEntries(
|
|
|
1559
1245
|
* `null`.
|
|
1560
1246
|
*/
|
|
1561
1247
|
function assertCodeOwnedFallbackProfiles(raw: Record<string, unknown>): void {
|
|
1562
|
-
const llm =
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1248
|
+
const llm = RawLlmWriteSliceSchema.safeParse(raw.llm);
|
|
1249
|
+
if (!llm.success) {
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
// The sibling `llm.default` decides whether the cloud backups are valid
|
|
1253
|
+
// fallback targets: they resolve on the cloud column alone.
|
|
1566
1254
|
const issues = collectFallbackProfileIssues(
|
|
1567
|
-
profiles ?? undefined,
|
|
1568
|
-
llm
|
|
1255
|
+
llm.data.profiles ?? undefined,
|
|
1256
|
+
llm.data.default,
|
|
1569
1257
|
);
|
|
1570
1258
|
if (issues.length > 0) {
|
|
1571
1259
|
throw new BadRequestError(issues.map((issue) => issue.message).join(" "));
|
|
1572
1260
|
}
|
|
1573
1261
|
}
|
|
1574
1262
|
|
|
1263
|
+
/**
|
|
1264
|
+
* Stamp the managed marker onto `llm.profiles` entries of catalog-owned
|
|
1265
|
+
* names that a write created without a `source` — a source-less entry reads
|
|
1266
|
+
* as a user shadow that blocks the catalog body. A user-owned entry sharing
|
|
1267
|
+
* the name (a legacy shadow) keeps its source, so its edits stay fully
|
|
1268
|
+
* user-owned.
|
|
1269
|
+
*/
|
|
1270
|
+
function stampManagedProfileEntries(
|
|
1271
|
+
preWrite: Record<string, unknown>,
|
|
1272
|
+
raw: Record<string, unknown>,
|
|
1273
|
+
): void {
|
|
1274
|
+
const newLlm = RawLlmWriteSliceSchema.safeParse(raw.llm);
|
|
1275
|
+
if (!newLlm.success || !newLlm.data.profiles) {
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
const oldLlm = RawLlmWriteSliceSchema.safeParse(preWrite.llm);
|
|
1279
|
+
let stamped = false;
|
|
1280
|
+
for (const name of MANAGED_PROFILE_NAMES) {
|
|
1281
|
+
const written = RawProfileEntrySchema.safeParse(newLlm.data.profiles[name]);
|
|
1282
|
+
if (!written.success || written.data.source !== undefined) {
|
|
1283
|
+
continue;
|
|
1284
|
+
}
|
|
1285
|
+
const prior = RawProfileEntrySchema.safeParse(
|
|
1286
|
+
oldLlm.success ? oldLlm.data.profiles?.[name] : undefined,
|
|
1287
|
+
);
|
|
1288
|
+
if (prior.success && prior.data.source !== "managed") {
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
written.data.source = "managed";
|
|
1292
|
+
newLlm.data.profiles[name] = written.data;
|
|
1293
|
+
stamped = true;
|
|
1294
|
+
}
|
|
1295
|
+
if (stamped) {
|
|
1296
|
+
raw.llm = newLlm.data;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1575
1300
|
export async function commitConfigWrite(
|
|
1576
1301
|
raw: Record<string, unknown>,
|
|
1577
1302
|
opLabel: string,
|
|
@@ -1581,10 +1306,14 @@ export async function commitConfigWrite(
|
|
|
1581
1306
|
// false diffs. Runs before the watcher-suppress/save sequence so a
|
|
1582
1307
|
// rejection needs no suppress-flag or cache cleanup.
|
|
1583
1308
|
const preWrite = loadRawConfig();
|
|
1309
|
+
// Direct callers (the profile routes) skip `handleSetConfig`'s full-schema
|
|
1310
|
+
// parse — this keeps a chat-settings write's invalid values from reaching
|
|
1311
|
+
// disk, while untouched legacy values ride (their issues filter out).
|
|
1584
1312
|
const chatSettings = validateChatSettingsWrite(preWrite, raw);
|
|
1585
1313
|
if (!chatSettings.success) {
|
|
1586
1314
|
throw new BadRequestError(chatSettings.error.message);
|
|
1587
1315
|
}
|
|
1316
|
+
stampManagedProfileEntries(preWrite, raw);
|
|
1588
1317
|
completeChangedCustomProfiles(preWrite, raw);
|
|
1589
1318
|
assertInvariantProfilesPreserved(preWrite, raw);
|
|
1590
1319
|
assertRoutableIdentityEntries(preWrite, raw);
|
|
@@ -1639,95 +1368,66 @@ export async function commitConfigWrite(
|
|
|
1639
1368
|
publishConfigChanged();
|
|
1640
1369
|
}
|
|
1641
1370
|
|
|
1642
|
-
/**
|
|
1643
|
-
* Web clients pair provider writes with a legacy `mode` key so their saves
|
|
1644
|
-
* stay valid on daemons whose schemas still carry it. The provider-only
|
|
1645
|
-
* services (web-search, image-generation) parse-strip that key, but the
|
|
1646
|
-
* deep-merge would still persist it to raw config.json on every save —
|
|
1647
|
-
* scrub it so the removed field cannot be resurrected on disk.
|
|
1648
|
-
*/
|
|
1649
|
-
function scrubRemovedServiceModes(raw: Record<string, unknown>): void {
|
|
1650
|
-
const services = raw.services as
|
|
1651
|
-
| Record<string, Record<string, unknown> | undefined>
|
|
1652
|
-
| undefined;
|
|
1653
|
-
if (!services) {
|
|
1654
|
-
return;
|
|
1655
|
-
}
|
|
1656
|
-
for (const key of ["web-search", "image-generation"]) {
|
|
1657
|
-
const entry = services[key];
|
|
1658
|
-
if (entry && typeof entry === "object" && "mode" in entry) {
|
|
1659
|
-
delete entry.mode;
|
|
1660
|
-
}
|
|
1661
|
-
}
|
|
1662
|
-
}
|
|
1663
|
-
|
|
1664
|
-
/**
|
|
1665
|
-
* A persisted `services.stt` block must satisfy SttServiceSchema, whose
|
|
1666
|
-
* `provider` is required. The services-level default fills the provider only
|
|
1667
|
-
* when `services.stt` is wholly absent, so a sparse patch like
|
|
1668
|
-
* `{ services: { stt: { language } } }` deep-merged into a config with no
|
|
1669
|
-
* stt block persists a provider-less block that fails validation and trips
|
|
1670
|
-
* the loader's salvage ladder: the patched value never applies and the whole
|
|
1671
|
-
* `services` section can reset to defaults on the next load (the LUM-2758
|
|
1672
|
-
* failure family). Seed the effective provider into any stt block that lacks
|
|
1673
|
-
* a non-empty string one so every config_patch writer stays self-consistent.
|
|
1674
|
-
* The seed applies to any stt key, not just language: the schema requires
|
|
1675
|
-
* the provider whenever the block exists. A block carrying a non-empty
|
|
1676
|
-
* string provider is left alone, even an invalid one, so an explicit
|
|
1677
|
-
* provider write is never silently rewritten.
|
|
1678
|
-
*/
|
|
1679
|
-
function seedSttProviderForSparseBlock(raw: Record<string, unknown>): void {
|
|
1680
|
-
const services = readPlainObject(raw.services);
|
|
1681
|
-
const stt = readPlainObject(services?.stt);
|
|
1682
|
-
if (!stt) {
|
|
1683
|
-
return;
|
|
1684
|
-
}
|
|
1685
|
-
const provider = stt.provider;
|
|
1686
|
-
if (typeof provider === "string" && provider.trim().length > 0) {
|
|
1687
|
-
return;
|
|
1688
|
-
}
|
|
1689
|
-
stt.provider = getConfig().services.stt.provider;
|
|
1690
|
-
}
|
|
1691
|
-
|
|
1692
1371
|
async function handlePatchConfig({ body }: RouteHandlerArgs) {
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
typeof body !== "object" ||
|
|
1696
|
-
Array.isArray(body) ||
|
|
1697
|
-
Object.keys(body).length === 0
|
|
1698
|
-
) {
|
|
1372
|
+
const patch = JsonRecordSchema.safeParse(body);
|
|
1373
|
+
if (!patch.success || Object.keys(patch.data).length === 0) {
|
|
1699
1374
|
throw new BadRequestError("Body must be a non-empty JSON object");
|
|
1700
1375
|
}
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
const
|
|
1712
|
-
|
|
1713
|
-
|
|
1376
|
+
// Managed-profile normalization runs on the fragment before it merges:
|
|
1377
|
+
// an honest `config get` → PATCH round-trip reduces to a no-op, and a
|
|
1378
|
+
// genuine edit reaches the invariant guard's precise rejections.
|
|
1379
|
+
normalizeCloudProfileWrites(patch.data);
|
|
1380
|
+
rejectCloudProfileDeletion(patch.data);
|
|
1381
|
+
|
|
1382
|
+
// A patch is partial: merge it onto the current config so the schema
|
|
1383
|
+
// parse in `handleSetConfig` always sees a complete config — untouched
|
|
1384
|
+
// values ride, sparse fragments are completed by the merge base.
|
|
1385
|
+
const merged: AssistantConfig = structuredClone(getConfig());
|
|
1386
|
+
const ingress = z
|
|
1387
|
+
.object({ publicBaseUrl: z.unknown().optional() })
|
|
1388
|
+
.passthrough()
|
|
1389
|
+
.safeParse(patch.data.ingress);
|
|
1390
|
+
const patchedIngressUrl = ingress.success
|
|
1391
|
+
? ingress.data.publicBaseUrl
|
|
1392
|
+
: undefined;
|
|
1714
1393
|
if (patchedIngressUrl !== undefined) {
|
|
1715
|
-
dropTunnelRecordsForNewUrl(
|
|
1394
|
+
dropTunnelRecordsForNewUrl(merged.ingress, patchedIngressUrl);
|
|
1716
1395
|
}
|
|
1717
|
-
deepMergeOverwrite(
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
scrubNulledChatSettings(raw, patch);
|
|
1721
|
-
seedSttProviderForSparseBlock(raw);
|
|
1396
|
+
deepMergeOverwrite(merged, patch.data);
|
|
1397
|
+
scrubNulledAcpAgentLeaves(merged);
|
|
1398
|
+
scrubNulledChatSettings(merged, patch.data);
|
|
1722
1399
|
|
|
1723
|
-
await
|
|
1400
|
+
await handleSetConfig(merged);
|
|
1724
1401
|
|
|
1725
1402
|
return handleGetConfig();
|
|
1726
1403
|
}
|
|
1727
1404
|
|
|
1728
1405
|
/**
|
|
1729
|
-
*
|
|
1730
|
-
*
|
|
1406
|
+
* The one config write: validate the merged config through
|
|
1407
|
+
* `AssistantConfigSchema`, and either reject with `BadRequestError` or save.
|
|
1408
|
+
* `handlePatchConfig` merges the patch with `getConfig()` first;
|
|
1409
|
+
* `handleSetConfigByKey` replaces one path first. Everything the schema
|
|
1410
|
+
* accepts is what lands on disk.
|
|
1411
|
+
*/
|
|
1412
|
+
async function handleSetConfig(merged: AssistantConfig): Promise<void> {
|
|
1413
|
+
const parsed = AssistantConfigSchema.safeParse(merged);
|
|
1414
|
+
if (!parsed.success) {
|
|
1415
|
+
throw new BadRequestError(
|
|
1416
|
+
parsed.error.issues
|
|
1417
|
+
.map((issue) =>
|
|
1418
|
+
issue.path.length > 0
|
|
1419
|
+
? `${issue.path.join(".")}: ${issue.message}`
|
|
1420
|
+
: issue.message,
|
|
1421
|
+
)
|
|
1422
|
+
.join("; "),
|
|
1423
|
+
);
|
|
1424
|
+
}
|
|
1425
|
+
await commitConfigWrite(parsed.data, "save");
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
/**
|
|
1429
|
+
* Direct path assignment - the `assistant config set <key> <value>` CLI
|
|
1430
|
+
* contract.
|
|
1731
1431
|
*
|
|
1732
1432
|
* `config_patch` uses `deepMergeOverwrite` semantics, which strips `null`
|
|
1733
1433
|
* leaves when the target subtree doesn't exist and merges (rather than
|
|
@@ -1737,17 +1437,18 @@ async function handlePatchConfig({ body }: RouteHandlerArgs) {
|
|
|
1737
1437
|
* - `set heartbeat.activeHoursStart null` to persist explicit `null`
|
|
1738
1438
|
* - `set llm {}` to replace `llm`, not merge into it
|
|
1739
1439
|
*
|
|
1740
|
-
* `config_set` performs `setNestedValue` directly on the
|
|
1741
|
-
* (no merge), then
|
|
1440
|
+
* `config_set` performs `setNestedValue` directly on the current config
|
|
1441
|
+
* (no merge), then funnels into the same schema-validated
|
|
1442
|
+
* {@link handleSetConfig} write.
|
|
1742
1443
|
*/
|
|
1743
|
-
async function
|
|
1744
|
-
|
|
1444
|
+
async function handleSetConfigByKey({ body }: RouteHandlerArgs) {
|
|
1445
|
+
const parsedBody = JsonRecordSchema.safeParse(body);
|
|
1446
|
+
if (!parsedBody.success) {
|
|
1745
1447
|
throw new BadRequestError(
|
|
1746
1448
|
"Body must be a JSON object with `path` and `value`",
|
|
1747
1449
|
);
|
|
1748
1450
|
}
|
|
1749
|
-
const
|
|
1750
|
-
const { path, value } = bodyRecord as { path?: unknown; value?: unknown };
|
|
1451
|
+
const path = parsedBody.data.path;
|
|
1751
1452
|
if (typeof path !== "string" || path.length === 0) {
|
|
1752
1453
|
throw new BadRequestError("`path` must be a non-empty string");
|
|
1753
1454
|
}
|
|
@@ -1755,85 +1456,62 @@ async function handleSetConfig({ body }: RouteHandlerArgs) {
|
|
|
1755
1456
|
// this check, `undefined` flows into `setNestedValue` and gets dropped by
|
|
1756
1457
|
// `JSON.stringify` at save time, silently removing the key - which is
|
|
1757
1458
|
// distinct from the documented "set to null" semantics.
|
|
1758
|
-
if (!("value"
|
|
1459
|
+
if (!Object.hasOwn(parsedBody.data, "value")) {
|
|
1759
1460
|
throw new BadRequestError(
|
|
1760
1461
|
"`value` is required (use `null` to clear a key)",
|
|
1761
1462
|
);
|
|
1762
1463
|
}
|
|
1763
|
-
|
|
1764
|
-
// writing — the same treatment PATCH gives wire-only keys embedded in a
|
|
1765
|
-
// profile fragment.
|
|
1766
|
-
const pathSegments = path.split(".");
|
|
1767
|
-
if (
|
|
1768
|
-
pathSegments[0] === "llm" &&
|
|
1769
|
-
pathSegments[1] === "profiles" &&
|
|
1770
|
-
pathSegments.length >= 4 &&
|
|
1771
|
-
WIRE_ONLY_PROFILE_KEYS.has(pathSegments[3]!)
|
|
1772
|
-
) {
|
|
1773
|
-
return { ok: true };
|
|
1774
|
-
}
|
|
1775
|
-
// Build the equivalent patch shape so the managed-profile guard can
|
|
1776
|
-
// inspect the touched subtree. `setNestedValue` places `value` into
|
|
1777
|
-
// `patchShape` by reference, so stripping wire-only profile keys here
|
|
1778
|
-
// also strips the object written to `raw` below.
|
|
1779
|
-
const patchShape: Record<string, unknown> = {};
|
|
1780
|
-
setNestedValue(patchShape, path, value);
|
|
1781
|
-
stripWireOnlyProfileKeys(patchShape);
|
|
1782
|
-
normalizeManagedProfileWrites(patchShape);
|
|
1783
|
-
rejectManagedProfileDeletion(patchShape);
|
|
1784
|
-
rejectMcpConfigSetPath(path, value);
|
|
1785
|
-
rejectMcpTransportHeaderWrite(patchShape);
|
|
1464
|
+
const value = parsedBody.data.value;
|
|
1786
1465
|
|
|
1787
|
-
const
|
|
1788
|
-
// A SET below the entry level (`llm.profiles.<name>.<leaf>`) writes the
|
|
1789
|
-
// primitive leaf directly into `raw`, bypassing the by-reference
|
|
1790
|
-
// normalization above — creating an entry for a managed-owned name would
|
|
1791
|
-
// leave it source-less, and a source-less entry reads as a user shadow
|
|
1792
|
-
// that blocks the catalog body. Stamp the managed marker onto the written
|
|
1793
|
-
// entry when the name was absent or managed-owned before this write.
|
|
1794
|
-
const managedEntryName =
|
|
1795
|
-
pathSegments[0] === "llm" &&
|
|
1796
|
-
pathSegments[1] === "profiles" &&
|
|
1797
|
-
pathSegments.length >= 3 &&
|
|
1798
|
-
MANAGED_PROFILE_NAMES.has(pathSegments[2]!)
|
|
1799
|
-
? pathSegments[2]!
|
|
1800
|
-
: undefined;
|
|
1801
|
-
const priorManagedEntry = managedEntryName
|
|
1802
|
-
? readPlainObject(
|
|
1803
|
-
readPlainObject(readPlainObject(raw.llm)?.profiles)?.[managedEntryName],
|
|
1804
|
-
)
|
|
1805
|
-
: undefined;
|
|
1466
|
+
const merged: AssistantConfig = structuredClone(getConfig());
|
|
1806
1467
|
// `assistant config set ingress.publicBaseUrl <url>`, what the webhook
|
|
1807
1468
|
// diagnostics tell users to run, retargets ingress like the settings route.
|
|
1808
1469
|
if (path === "ingress.publicBaseUrl") {
|
|
1809
|
-
dropTunnelRecordsForNewUrl(
|
|
1810
|
-
}
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1470
|
+
dropTunnelRecordsForNewUrl(merged.ingress, value);
|
|
1471
|
+
}
|
|
1472
|
+
// Any llm-touching SET rides the same normalization the profile routes
|
|
1473
|
+
// use: `normalizeCloudProfileWrites` reduces an echo of the wire view to
|
|
1474
|
+
// the workspace-owned stub and rejects genuine edits to managed entries.
|
|
1475
|
+
// Its normalization lives in the parsed llm slice (a zod parse copies), so
|
|
1476
|
+
// the written value below is the normalized slice/entry — and a leaf write
|
|
1477
|
+
// below the entry level rides raw; the ladder stamps the managed marker
|
|
1478
|
+
// onto an entry the write creates.
|
|
1479
|
+
const pathSegments = path.split(".");
|
|
1480
|
+
let writtenValue: unknown = value;
|
|
1481
|
+
if (pathSegments[0] === "llm") {
|
|
1482
|
+
const patchShape: Record<string, unknown> = {};
|
|
1483
|
+
setNestedValue(patchShape, path, value);
|
|
1484
|
+
normalizeCloudProfileWrites(patchShape);
|
|
1485
|
+
rejectCloudProfileDeletion(patchShape);
|
|
1486
|
+
const patchLlm = RawLlmWriteSliceSchema.safeParse(patchShape.llm);
|
|
1487
|
+
if (patchLlm.success) {
|
|
1488
|
+
if (pathSegments.length === 1) {
|
|
1489
|
+
writtenValue = patchLlm.data;
|
|
1490
|
+
} else if (pathSegments[1] === "profiles") {
|
|
1491
|
+
if (pathSegments.length === 2) {
|
|
1492
|
+
if (patchLlm.data.profiles) {
|
|
1493
|
+
writtenValue = patchLlm.data.profiles;
|
|
1494
|
+
}
|
|
1495
|
+
} else if (pathSegments.length === 3) {
|
|
1496
|
+
const normalized = patchLlm.data.profiles?.[pathSegments[2]!];
|
|
1497
|
+
if (normalized !== undefined) {
|
|
1498
|
+
writtenValue = normalized;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1821
1502
|
}
|
|
1822
1503
|
}
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
// schema-valid.
|
|
1828
|
-
seedSttProviderForSparseBlock(raw);
|
|
1504
|
+
setNestedValue(merged, path, writtenValue);
|
|
1505
|
+
scrubNulledAcpAgentLeaves(merged);
|
|
1506
|
+
|
|
1507
|
+
await handleSetConfig(merged);
|
|
1829
1508
|
|
|
1830
|
-
await commitConfigWrite(raw, "set");
|
|
1831
1509
|
// A `memory.v2` substrate tunable whose `memory.substrate` twin is set does
|
|
1832
1510
|
// not mean what an unpaired write means: the substrate namespace wins in
|
|
1833
1511
|
// `resolveSubstrateTuning`, and for the three twins the v2 injection engine
|
|
1834
1512
|
// reads directly the two namespaces diverge instead. Report which case this
|
|
1835
1513
|
// is alongside the success so the caller can tell the operator.
|
|
1836
|
-
const shadowing = findSubstrateShadowing(
|
|
1514
|
+
const shadowing = findSubstrateShadowing(path);
|
|
1837
1515
|
if (shadowing) {
|
|
1838
1516
|
const warning = describeShadowedConfigSet(shadowing, path);
|
|
1839
1517
|
log.warn({ path, substratePath: shadowing.substratePath }, warning);
|
|
@@ -1890,12 +1568,12 @@ async function handleReplaceInferenceProfile({
|
|
|
1890
1568
|
throw new BadRequestError(`Invalid profile fragment: ${detail}`);
|
|
1891
1569
|
}
|
|
1892
1570
|
// A managed name with no existing entry stays protected so users can't
|
|
1893
|
-
// shadow a seeded
|
|
1571
|
+
// shadow a seeded cloud profile. An existing entry carrying a non-managed
|
|
1894
1572
|
// `source` is user-owned and remains fully editable.
|
|
1895
1573
|
const existingProfile = asMutablePlainObject(
|
|
1896
1574
|
getConfig().llm.profiles?.[name],
|
|
1897
1575
|
);
|
|
1898
|
-
const
|
|
1576
|
+
const isCloud =
|
|
1899
1577
|
MANAGED_PROFILE_NAMES.has(name) &&
|
|
1900
1578
|
(existingProfile == null || existingProfile.source === "managed");
|
|
1901
1579
|
if (CODE_OWNED_PROFILE_NAMES.has(name)) {
|
|
@@ -1924,11 +1602,11 @@ async function handleReplaceInferenceProfile({
|
|
|
1924
1602
|
`Profile "${name}" is not currently available and cannot be edited.`,
|
|
1925
1603
|
);
|
|
1926
1604
|
}
|
|
1927
|
-
if (
|
|
1928
|
-
//
|
|
1605
|
+
if (isCloud) {
|
|
1606
|
+
// Cloud profiles are daemon-seeded and read-only — the commit guard
|
|
1929
1607
|
// (`assertInvariantProfilesPreserved`) rejects every write to them
|
|
1930
1608
|
// except re-enabling a disabled profile. Enforce the same contract up
|
|
1931
|
-
// front: the only body a
|
|
1609
|
+
// front: the only body a cloud PUT accepts is a pure status re-enable
|
|
1932
1610
|
// (`{status: "active"}`, or `{status: null}` to clear back to
|
|
1933
1611
|
// active-by-absence). Rejecting here keeps the error message ahead of
|
|
1934
1612
|
// any side effects and mirrors the guard's wording.
|
|
@@ -1942,8 +1620,8 @@ async function handleReplaceInferenceProfile({
|
|
|
1942
1620
|
const detail =
|
|
1943
1621
|
disallowed.length > 0 ? ` fields [${disallowed.join(", ")}]` : "";
|
|
1944
1622
|
throw new BadRequestError(
|
|
1945
|
-
`Cannot edit
|
|
1946
|
-
`
|
|
1623
|
+
`Cannot edit cloud profile "${name}"${detail}. ` +
|
|
1624
|
+
`Cloud profiles are read-only (a disabled profile can be re-enabled); ` +
|
|
1947
1625
|
`duplicate to a custom profile to customize.`,
|
|
1948
1626
|
);
|
|
1949
1627
|
}
|
|
@@ -2046,10 +1724,10 @@ async function handleReplaceInferenceProfile({
|
|
|
2046
1724
|
|
|
2047
1725
|
// When the UI sends provider but no provider_connection, derive the connection
|
|
2048
1726
|
// now so the config deep-merge doesn't inherit a stale connection from the
|
|
2049
|
-
// default layer. Managed entries are excluded: the
|
|
1727
|
+
// default layer. Managed entries are excluded: the cloud gate above
|
|
2050
1728
|
// already rejected any provider-carrying fragment, so their only surviving
|
|
2051
1729
|
// body is a status re-enable, which derives no connection. A user-owned
|
|
2052
|
-
// profile sharing a
|
|
1730
|
+
// profile sharing a cloud name is fully editable, so it takes the
|
|
2053
1731
|
// derivation like any other custom profile.
|
|
2054
1732
|
const fragment = parsed.data as Record<string, unknown>;
|
|
2055
1733
|
// Routing identities resolve their connection per-request from the
|
|
@@ -2057,7 +1735,7 @@ async function handleReplaceInferenceProfile({
|
|
|
2057
1735
|
// ("cadet" is its stored provider) or create a junk "<identity>-personal"
|
|
2058
1736
|
// connection for chatgpt.
|
|
2059
1737
|
if (
|
|
2060
|
-
!
|
|
1738
|
+
!isCloud &&
|
|
2061
1739
|
fragment.provider &&
|
|
2062
1740
|
!fragment.provider_connection &&
|
|
2063
1741
|
!ROUTING_IDENTITY_PROVIDERS.has(fragment.provider as string)
|
|
@@ -2074,12 +1752,12 @@ async function handleReplaceInferenceProfile({
|
|
|
2074
1752
|
if (active) {
|
|
2075
1753
|
fragment.provider_connection = active.name;
|
|
2076
1754
|
} else if (
|
|
2077
|
-
|
|
2078
|
-
getConnection(db,
|
|
1755
|
+
CLOUD_ROUTABLE_PROVIDERS.has(provider) &&
|
|
1756
|
+
getConnection(db, CADET_CLOUD_CONNECTION_NAME)
|
|
2079
1757
|
) {
|
|
2080
|
-
// Managed-routable providers are served by the single Cadet
|
|
1758
|
+
// Managed-routable providers are served by the single Cadet Cloud
|
|
2081
1759
|
// connection; prefer it over lazily creating a personal connection.
|
|
2082
|
-
fragment.provider_connection =
|
|
1760
|
+
fragment.provider_connection = CADET_CLOUD_CONNECTION_NAME;
|
|
2083
1761
|
} else if (!PROVIDERS_REQUIRING_BASE_URL_AND_MODELS.has(provider)) {
|
|
2084
1762
|
const connectionName = `${provider}-personal`;
|
|
2085
1763
|
const isKeyless = provider === "ollama";
|
|
@@ -2100,13 +1778,13 @@ async function handleReplaceInferenceProfile({
|
|
|
2100
1778
|
}
|
|
2101
1779
|
|
|
2102
1780
|
const raw = loadRawConfig();
|
|
2103
|
-
if (
|
|
1781
|
+
if (isCloud) {
|
|
2104
1782
|
// Partial overlay: keep every existing key intact, only apply the status
|
|
2105
|
-
// re-enable (the sole body the
|
|
1783
|
+
// re-enable (the sole body the cloud gate above admits). Using
|
|
2106
1784
|
// `replaceInferenceProfileConfig` here would wipe the seed-owned fields
|
|
2107
1785
|
// (provider, model, advanced params) because that function assumes the
|
|
2108
1786
|
// body carries the full UI surface.
|
|
2109
|
-
|
|
1787
|
+
applyCloudProfileReenable(raw, name, fragment.status as "active" | null);
|
|
2110
1788
|
} else {
|
|
2111
1789
|
replaceInferenceProfileConfig(raw, name, fragment);
|
|
2112
1790
|
}
|
|
@@ -2115,7 +1793,7 @@ async function handleReplaceInferenceProfile({
|
|
|
2115
1793
|
// `handleSetConfig`: file-watcher suppression so the in-process reload
|
|
2116
1794
|
// doesn't race the explicit reinit, embedding backend cache clear,
|
|
2117
1795
|
// in-process `getConfig` cache invalidation, and provider registry
|
|
2118
|
-
// reinitialization. `status: "disabled"` on a
|
|
1796
|
+
// reinitialization. `status: "disabled"` on a cloud profile (and any
|
|
2119
1797
|
// `provider` / `model` / `provider_connection` change on a custom
|
|
2120
1798
|
// profile) must take effect immediately rather than waiting for the
|
|
2121
1799
|
// next watcher tick.
|
|
@@ -2124,11 +1802,11 @@ async function handleReplaceInferenceProfile({
|
|
|
2124
1802
|
}
|
|
2125
1803
|
|
|
2126
1804
|
/**
|
|
2127
|
-
* Apply the disabled→active re-enable to a
|
|
1805
|
+
* Apply the disabled→active re-enable to a cloud profile entry, preserving
|
|
2128
1806
|
* every other field already on disk (provider, model, advanced params, etc).
|
|
2129
1807
|
* `status: "active"` sets the key; `null` clears it (active-by-absence).
|
|
2130
1808
|
*/
|
|
2131
|
-
function
|
|
1809
|
+
function applyCloudProfileReenable(
|
|
2132
1810
|
raw: Record<string, unknown>,
|
|
2133
1811
|
name: string,
|
|
2134
1812
|
status: "active" | null,
|
|
@@ -2149,7 +1827,7 @@ function applyManagedProfileReenable(
|
|
|
2149
1827
|
...(asMutablePlainObject(profiles[name]) ?? {}),
|
|
2150
1828
|
};
|
|
2151
1829
|
// Only reached for managed-owned names (the route's managed gate), so a
|
|
2152
|
-
// freshly created stub must carry the
|
|
1830
|
+
// freshly created stub must carry the cloud marker the effective view
|
|
2153
1831
|
// and write guards key on.
|
|
2154
1832
|
nextProfile.source = "managed";
|
|
2155
1833
|
if (status === null) {
|
|
@@ -2548,7 +2226,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
2548
2226
|
"(preserves explicit null, replaces object subtrees instead of merging). " +
|
|
2549
2227
|
"Used by the `assistant config set <key> <value>` CLI command.",
|
|
2550
2228
|
tags: ["config"],
|
|
2551
|
-
handler:
|
|
2229
|
+
handler: handleSetConfigByKey,
|
|
2552
2230
|
},
|
|
2553
2231
|
{
|
|
2554
2232
|
operationId: "config_allowlist_validate",
|