@bitkyc08/opencodex 2.86.0 → 2.87.0
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/gui/dist/assets/App-ZfYbLHkB.js +58 -0
- package/gui/dist/assets/App-wT3mXqFz.css +1 -0
- package/gui/dist/assets/{Tray-DyR9yBee.js → Tray-DCd-A1az.js} +1 -1
- package/gui/dist/assets/index-CfYDMj34.js +94 -0
- package/gui/dist/assets/index-DPeB9HpV.css +1 -0
- package/gui/dist/assets/quota-summary-ChvmKGOk.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic/beta-allowlist.ts +9 -8
- package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
- package/src/adapters/anthropic/passthrough.ts +7 -23
- package/src/adapters/anthropic-progress.ts +84 -0
- package/src/adapters/anthropic.ts +108 -21
- package/src/adapters/base.ts +2 -0
- package/src/adapters/claude-cli/adapter.ts +111 -23
- package/src/adapters/claude-cli/profiles.ts +2 -1
- package/src/adapters/claude-cli/stable-replay.ts +108 -0
- package/src/adapters/claude-cli/usage-admission.ts +230 -0
- package/src/adapters/coding-agent/protocol.ts +1 -1
- package/src/adapters/coding-agent/turn.ts +18 -2
- package/src/adapters/dsh-account.ts +97 -0
- package/src/adapters/google.ts +35 -8
- package/src/adapters/openai-chat/messages.ts +4 -3
- package/src/adapters/openai-chat/passthrough.ts +8 -2
- package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
- package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
- package/src/adapters/openai-responses/passthrough.ts +57 -125
- package/src/adapters/openai-responses/request-strips.ts +25 -3
- package/src/adapters/openai-responses/transport.ts +81 -0
- package/src/adapters/qoder/adapter.ts +4 -2
- package/src/adapters/registry.ts +5 -0
- package/src/adapters/zed.ts +26 -1
- package/src/advisor/activity.ts +55 -0
- package/src/advisor/budget.ts +62 -0
- package/src/advisor/consult.ts +4 -1
- package/src/advisor/context.ts +16 -4
- package/src/advisor/evidence-tool.ts +21 -0
- package/src/advisor/evidence.ts +117 -0
- package/src/advisor/runtime.ts +79 -31
- package/src/advisor/settings.ts +11 -2
- package/src/bridge/errors.ts +19 -1
- package/src/bridge/internal.ts +3 -1
- package/src/bridge/response-json.ts +18 -3
- package/src/bridge/sse.ts +58 -8
- package/src/bridge/terminal-refusal.ts +59 -0
- package/src/claude/desktop-3p-library.ts +32 -7
- package/src/claude/desktop-3p.ts +55 -13
- package/src/claude/desktop-first-party.ts +1 -1
- package/src/claude/desktop-picker-platform.ts +4 -0
- package/src/claude/desktop-picker.ts +15 -8
- package/src/claude/desktop-system-proxy.ts +50 -4
- package/src/claude/inbound-content-options.ts +11 -0
- package/src/claude/inbound.ts +33 -0
- package/src/claude/intercept/picker-ca-startup.ts +2 -1
- package/src/claude/intercept/picker-runtime.ts +6 -5
- package/src/claude/intercept/picker-trust-windows.ts +338 -0
- package/src/claude/intercept/picker-trust.ts +19 -0
- package/src/claude/intercept/port-policy.ts +30 -0
- package/src/claude/intercept/runtime.ts +97 -94
- package/src/claude/outbound.ts +38 -13
- package/src/cli/account-api.ts +1 -0
- package/src/cli/account-policy-dto.ts +6 -0
- package/src/cli/account-policy.ts +34 -5
- package/src/cli/account-preservation.ts +44 -0
- package/src/cli/account.ts +8 -2
- package/src/cli/advisor.ts +17 -3
- package/src/cli/capabilities-accounts.ts +20 -1
- package/src/cli/capabilities-agents-routing.ts +5 -2
- package/src/cli/capabilities-base.ts +38 -10
- package/src/cli/capabilities-observe-system.ts +63 -3
- package/src/cli/capabilities-provider-models.ts +11 -0
- package/src/cli/codex-cli-update.ts +151 -41
- package/src/cli/codex-provider.ts +44 -0
- package/src/cli/combo-input.ts +10 -1
- package/src/cli/combo-result.ts +1 -1
- package/src/cli/combo.ts +2 -1
- package/src/cli/cross-home-owner.ts +17 -14
- package/src/cli/dispatch.ts +25 -4
- package/src/cli/doctor.ts +6 -1
- package/src/cli/ensure-desktop-cold-start.ts +60 -7
- package/src/cli/help.ts +3 -2
- package/src/cli/index.ts +1 -1
- package/src/cli/integration-plan-dto.ts +1 -0
- package/src/cli/log-history.ts +172 -0
- package/src/cli/models-runtime-subcommands.ts +1 -0
- package/src/cli/models-runtime.ts +72 -8
- package/src/cli/observe-stream.ts +2 -0
- package/src/cli/observe.ts +16 -4
- package/src/cli/provider.ts +27 -5
- package/src/cli/registry.ts +28 -8
- package/src/cli/runtime-api.ts +5 -3
- package/src/cli/system-command.ts +40 -8
- package/src/cli/uninstall-client-state.ts +32 -0
- package/src/cli/usage-compare.ts +309 -0
- package/src/cli/usage-model-search.ts +4 -4
- package/src/cli/usage-report.ts +2 -2
- package/src/cli/v2.ts +1 -1
- package/src/client/machine-listener.ts +1 -3
- package/src/client/runtime.ts +1 -0
- package/src/clients/client-path.ts +51 -0
- package/src/clients/config-export/contracts.ts +2 -1
- package/src/clients/config-export.ts +18 -44
- package/src/clients/gentle-shell-path.ts +87 -0
- package/src/codex/account-auto-switch.ts +2 -0
- package/src/codex/account-identity.ts +29 -0
- package/src/codex/account-label.ts +1 -1
- package/src/codex/account-lifecycle.ts +21 -5
- package/src/codex/account-preservation.ts +28 -0
- package/src/codex/account-use-remaining.ts +23 -0
- package/src/codex/app-server-processes.ts +97 -7
- package/src/codex/app-server-restart-service.ts +55 -4
- package/src/codex/auth-api/account-list.ts +4 -0
- package/src/codex/auth-api/account-pause-group.ts +6 -50
- package/src/codex/auth-api/account-preservation.ts +57 -0
- package/src/codex/auth-api/routes.ts +8 -0
- package/src/codex/auth-api/use-remaining-route.ts +36 -0
- package/src/codex/auth-api.ts +0 -1
- package/src/codex/auth-context.ts +2 -2
- package/src/codex/catalog/auto-review.ts +20 -3
- package/src/codex/catalog/build-entries.ts +9 -1
- package/src/codex/catalog/combo-member.ts +14 -5
- package/src/codex/catalog/configured-tiers.ts +1 -1
- package/src/codex/catalog/gather-capture.ts +2 -0
- package/src/codex/catalog/metadata.ts +11 -9
- package/src/codex/catalog/model-hints.ts +21 -16
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/parsing.ts +30 -86
- package/src/codex/catalog/provider-models.ts +36 -34
- package/src/codex/catalog/routed-gather.ts +12 -13
- package/src/codex/cli-install-provenance.ts +105 -17
- package/src/codex/cli-update-plan.ts +545 -0
- package/src/codex/config-write-lock.ts +23 -2
- package/src/codex/desktop-app/picker-freshness.ts +96 -0
- package/src/codex/desktop-app-restart.ts +21 -0
- package/src/codex/desktop-compatibility/installed-build.ts +24 -6
- package/src/codex/desktop-compatibility/routing-preflight.ts +1 -1
- package/src/codex/desktop-compatibility/runtime.ts +12 -3
- package/src/codex/desktop-compatibility/windows-key-protection.ts +115 -9
- package/src/codex/features.ts +85 -45
- package/src/codex/inject/history-failure.ts +27 -0
- package/src/codex/inject/restore.ts +24 -2
- package/src/codex/inject/routing-classify.ts +4 -3
- package/src/codex/inject.ts +18 -27
- package/src/codex/main-account-hard-lock.ts +30 -1
- package/src/codex/native-profile-startup.ts +1 -1
- package/src/codex/ocx-compaction-history.ts +12 -10
- package/src/codex/paths.ts +86 -1
- package/src/codex/provider-routing.ts +174 -0
- package/src/codex/refresh.ts +2 -2
- package/src/codex/routing/health-store.ts +1 -1
- package/src/codex/routing/selection.ts +4 -3
- package/src/codex/routing/thread-affinity.ts +1 -1
- package/src/codex/signed-in-identity.ts +43 -0
- package/src/codex/subagent-model-fallback.ts +1 -1
- package/src/codex/sync.ts +6 -1
- package/src/codex/user-identity.ts +16 -5
- package/src/codex/windows-local-appdata.ts +84 -0
- package/src/combos/failover.ts +6 -1
- package/src/combos/index.ts +1 -0
- package/src/combos/resolve.ts +46 -10
- package/src/combos/types.ts +15 -0
- package/src/config/diagnostics.ts +33 -6
- package/src/config/live-reconcile.ts +38 -2
- package/src/config/load-degrade.ts +17 -0
- package/src/config/opencode-wire-upgrade.ts +54 -0
- package/src/config/opencode-zen-upgrade.ts +52 -0
- package/src/config/owner-registry.ts +22 -17
- package/src/config/provider-validation.ts +45 -2
- package/src/config/schema/config-schema.ts +22 -0
- package/src/config/schema/leaf-validators.ts +38 -9
- package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
- package/src/config/schema/native-reasoning-retention.ts +26 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +608 -284
- package/src/grok/sync.ts +1 -1
- package/src/images/loop.ts +46 -8
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/mutation-plan.ts +1 -0
- package/src/integrations/registry.ts +8 -0
- package/src/lib/codex-restart-contract.ts +15 -0
- package/src/lib/desktop-upstream-tunnel.ts +18 -53
- package/src/lib/empty-tool-catalog.ts +62 -0
- package/src/lib/errors.ts +19 -3
- package/src/lib/gcp-adc.ts +96 -9
- package/src/lib/gcp-credential-input.ts +12 -0
- package/src/lib/lab-live-route-production.ts +5 -0
- package/src/lib/opaque-proxy-tunnel.ts +217 -0
- package/src/lib/redact.ts +2 -2
- package/src/oauth/account-quota-rank.ts +69 -2
- package/src/oauth/anthropic-account-refusal.ts +20 -2
- package/src/oauth/anthropic-oauth-definitions.ts +3 -1
- package/src/oauth/anthropic-routing.ts +1 -21
- package/src/oauth/anthropic.ts +38 -7
- package/src/oauth/callback-server.ts +27 -1
- package/src/oauth/chatgpt-device.ts +1 -1
- package/src/oauth/chatgpt-shared.ts +83 -0
- package/src/oauth/chatgpt.ts +8 -72
- package/src/oauth/dsh.ts +203 -0
- package/src/oauth/generic-account-failover.ts +33 -7
- package/src/oauth/generic-session-affinity.ts +125 -0
- package/src/oauth/index.ts +184 -109
- package/src/oauth/local-token-detect.ts +21 -1
- package/src/oauth/login-cli-errors.ts +44 -0
- package/src/oauth/login-cli.ts +69 -7
- package/src/oauth/login-flow-state.ts +371 -23
- package/src/oauth/meta-muse-errors.ts +41 -0
- package/src/oauth/meta-muse-keychain.ts +52 -0
- package/src/oauth/meta-muse.ts +51 -84
- package/src/oauth/model-discovery-auth.ts +3 -0
- package/src/oauth/pool-settings-capability.ts +4 -2
- package/src/oauth/refresh-flight-retirement.ts +48 -0
- package/src/oauth/session-affinity-key.ts +23 -0
- package/src/oauth/store-anthropic-instance.ts +9 -2
- package/src/oauth/store.ts +12 -5
- package/src/protocols/encoders/adapter-events.ts +5 -4
- package/src/protocols/encoders/messages.ts +9 -6
- package/src/protocols/provider-summary.ts +2 -1
- package/src/providers/api-key-cooldowns.ts +49 -0
- package/src/providers/api-key-resolve.ts +11 -6
- package/src/providers/api-key-response-ownership.ts +57 -0
- package/src/providers/api-key-selection.ts +31 -6
- package/src/providers/api-keys.ts +214 -3
- package/src/providers/claude-cli-usage.ts +96 -0
- package/src/providers/dsh-account-destination.ts +31 -0
- package/src/providers/dsh-account-models.ts +41 -0
- package/src/providers/gcp-credential-cleanup.ts +59 -0
- package/src/providers/key-failover.ts +54 -50
- package/src/providers/key-store.ts +7 -2
- package/src/providers/model-rename-fields.ts +3 -0
- package/src/providers/model-rename-migration.ts +2 -2
- package/src/providers/openai-tiers-destination.ts +11 -2
- package/src/providers/openai-tiers.ts +5 -12
- package/src/providers/opencode-go-transport.ts +3 -1
- package/src/providers/quota/account-cache.ts +6 -5
- package/src/providers/quota/account-row.ts +34 -0
- package/src/providers/quota/antigravity.ts +28 -7
- package/src/providers/quota/dsh-account.ts +70 -0
- package/src/providers/quota/vendor-probes-oauth.ts +1 -93
- package/src/providers/quota-types.ts +2 -0
- package/src/providers/quota.ts +14 -20
- package/src/providers/registry/entries-core.ts +37 -8
- package/src/providers/registry/types.ts +1 -1
- package/src/providers/request-pacing.ts +77 -19
- package/src/providers/routed-config.ts +339 -0
- package/src/providers/service-tier.ts +2 -1
- package/src/quota/window-mapping.ts +5 -2
- package/src/responses/anthropic-progress.ts +34 -0
- package/src/responses/assistant-replay.ts +35 -0
- package/src/responses/compaction-history-framing.ts +65 -0
- package/src/responses/compaction.ts +6 -1
- package/src/responses/incomplete-reason.ts +31 -0
- package/src/responses/parser.ts +50 -5
- package/src/responses/reasoning-envelope.ts +4 -0
- package/src/responses/reasoning-replay-cache.ts +24 -2
- package/src/responses/reasoning-retention.ts +173 -0
- package/src/responses/retained-compaction.ts +46 -0
- package/src/responses/schema.ts +23 -0
- package/src/router.ts +8 -321
- package/src/routing/health.ts +37 -22
- package/src/routing/history/indexer.ts +6 -5
- package/src/routing/history/ingest.ts +6 -2
- package/src/routing/profile.ts +1 -1
- package/src/server/admission-model-scope.ts +17 -0
- package/src/server/auth-cors.ts +11 -0
- package/src/server/chat-completions.ts +7 -1
- package/src/server/chat-native-reasoning.ts +123 -0
- package/src/server/chat-native-sse.ts +110 -7
- package/src/server/chat-native.ts +76 -17
- package/src/server/claude-messages.ts +55 -414
- package/src/server/effort-policy.ts +1 -1
- package/src/server/images.ts +15 -2
- package/src/server/index/model-alias-id.ts +36 -0
- package/src/server/index/optional-listeners.ts +2 -0
- package/src/server/index/serve-options.ts +9 -1
- package/src/server/index/startup-warnings.ts +2 -0
- package/src/server/index.ts +1 -1
- package/src/server/inference/advisor-diagnostics.ts +31 -0
- package/src/server/management/advisor-routes.ts +15 -4
- package/src/server/management/claude-desktop-picker-routes.ts +1 -1
- package/src/server/management/combo-routes.ts +176 -50
- package/src/server/management/companion-routes.ts +1 -1
- package/src/server/management/config-routes.ts +75 -4
- package/src/server/management/cursor-integration-routes.ts +1 -1
- package/src/server/management/model-routes.ts +11 -6
- package/src/server/management/model-rows.ts +14 -0
- package/src/server/management/native-integration-routes.ts +2 -7
- package/src/server/management/native-reasoning-retention-routes.ts +41 -0
- package/src/server/management/oauth-account-routes.ts +87 -14
- package/src/server/management/provider-gcp-credential-routes.ts +29 -0
- package/src/server/management/provider-key-pacing-routes.ts +31 -0
- package/src/server/management/provider-overwrite-carry.ts +7 -0
- package/src/server/management/provider-routes.ts +25 -30
- package/src/server/management/route-registry.ts +10 -1
- package/src/server/management-api.ts +3 -1
- package/src/server/messages-native-tool-guard.ts +185 -0
- package/src/server/messages-native.ts +101 -21
- package/src/server/messages-shared.ts +431 -0
- package/src/server/relay.ts +1 -1
- package/src/server/request-log-terminal-status.ts +1 -1
- package/src/server/request-log.ts +6 -1
- package/src/server/responses/account-change-state.ts +8 -2
- package/src/server/responses/adapter-continuation.ts +13 -1
- package/src/server/responses/adapter-delivery.ts +19 -6
- package/src/server/responses/adapter-dispatch.ts +19 -2
- package/src/server/responses/advisor-retention.ts +117 -0
- package/src/server/responses/advisor-slot.ts +73 -41
- package/src/server/responses/codex-ws-wire.ts +15 -8
- package/src/server/responses/compact.ts +303 -125
- package/src/server/responses/compaction-recovery.ts +18 -10
- package/src/server/responses/compaction-retention.ts +61 -0
- package/src/server/responses/context-overflow.ts +11 -6
- package/src/server/responses/core-auth.ts +6 -0
- package/src/server/responses/core-codex-account.ts +11 -1
- package/src/server/responses/core-combo-failure.ts +2 -0
- package/src/server/responses/core-combo.ts +120 -36
- package/src/server/responses/core-errors.ts +3 -3
- package/src/server/responses/core-lifetime.ts +5 -2
- package/src/server/responses/core-opaque-recovery.ts +35 -0
- package/src/server/responses/core-options.ts +5 -0
- package/src/server/responses/core-pipeline.ts +141 -0
- package/src/server/responses/core-replay.ts +18 -2
- package/src/server/responses/core.ts +2 -115
- package/src/server/responses/fetch-helpers.ts +73 -19
- package/src/server/responses/main-hard-lock-routing.ts +109 -0
- package/src/server/responses/native-response-control.ts +4 -2
- package/src/server/responses/passthrough-delivery.ts +3 -1
- package/src/server/responses/passthrough-dispatch.ts +15 -13
- package/src/server/responses/request-prepare.ts +76 -12
- package/src/server/responses/request-sidecar-auth.ts +2 -2
- package/src/server/responses/request-transport.ts +104 -27
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +96 -30
- package/src/server/responses/sidecar-execution.ts +20 -2
- package/src/server/responses/terminal-guard.ts +2 -16
- package/src/service/auth-preflight.ts +71 -0
- package/src/service/cli.ts +4 -2
- package/src/service/definition.ts +97 -0
- package/src/service/guards.ts +1 -61
- package/src/service/health-sentinel-linux.ts +112 -0
- package/src/service/health-sentinel-main.ts +76 -57
- package/src/service/health.ts +5 -89
- package/src/service/launchd.ts +27 -7
- package/src/service/logging.ts +72 -9
- package/src/service/repair.ts +2 -1
- package/src/service/state.ts +7 -8
- package/src/service/systemd.ts +1 -1
- package/src/service/windows-guarded-manager.ts +2 -1
- package/src/service/windows-ops.ts +1 -1
- package/src/service/windows-scheduler.ts +2 -7
- package/src/service/windows-taskxml.ts +2 -2
- package/src/service/windows-wscript.ts +14 -0
- package/src/service.ts +4 -2
- package/src/sidecar/auth.ts +2 -0
- package/src/types/config.ts +64 -3
- package/src/types/opencode-go.ts +61 -0
- package/src/types/opencode-zen.ts +29 -0
- package/src/types/provider.ts +32 -6
- package/src/types/request.ts +23 -2
- package/src/types/wire.ts +29 -37
- package/src/types.ts +1 -0
- package/src/update/npm-invocation.mjs +12 -2
- package/src/usage/cost.ts +48 -12
- package/src/usage/expected-prices.ts +3 -0
- package/src/usage/ledger-retention.ts +2 -1
- package/src/usage/ledger-scanner.ts +6 -5
- package/src/usage/log.ts +17 -8
- package/src/usage/summary.ts +22 -9
- package/src/usage/user-cost-overlays.ts +14 -19
- package/src/usage/user-cost-validation.ts +38 -0
- package/src/vision/eligibility.ts +13 -0
- package/src/web-search/loop.ts +55 -5
- package/src/web-search/progress-stream.ts +4 -1
- package/src/web-search/run-turn-loop.ts +9 -1
- package/gui/dist/assets/App-BjlttVLy.js +0 -58
- package/gui/dist/assets/App-CSJvmjNs.css +0 -1
- package/gui/dist/assets/index-BB6uK6tU.css +0 -1
- package/gui/dist/assets/index-BMssP4k3.js +0 -94
- package/gui/dist/assets/quota-summary-D8M1bc_R.js +0 -1
|
@@ -21,6 +21,7 @@ export { LINK_INGRESS_HOSTNAME } from "./link-listener";
|
|
|
21
21
|
import type { ServerIngress } from "./serve-options";
|
|
22
22
|
|
|
23
23
|
export interface OptionalListenerStartContext<T> {
|
|
24
|
+
startProxy?: typeof import("../../claude/intercept/connect-proxy").startConnectProxy;
|
|
24
25
|
config: OcxConfig;
|
|
25
26
|
publicPort: number;
|
|
26
27
|
requestedPort?: number;
|
|
@@ -106,6 +107,7 @@ export function createOptionalListenerSet<T>(linkDeps: LinkListenerDeps = {}): O
|
|
|
106
107
|
}
|
|
107
108
|
supervisorStop = () => supervisor.stop();
|
|
108
109
|
claudeIntercept.start({
|
|
110
|
+
startProxy: ctx.startProxy,
|
|
109
111
|
config: ctx.config,
|
|
110
112
|
publicPort: ctx.publicPort,
|
|
111
113
|
requestedPort: ctx.requestedPort,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Server, ServerWebSocket } from "bun";
|
|
2
|
+
import { modelAliasIdPromoter } from "./model-alias-id";
|
|
2
3
|
import type { StartServerDeps } from "./startup-warnings";
|
|
3
4
|
import { isLinkManagementPath } from "../../link/management-routes";
|
|
4
5
|
import {
|
|
@@ -1252,6 +1253,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1252
1253
|
effortRowKnownIds,
|
|
1253
1254
|
));
|
|
1254
1255
|
};
|
|
1256
|
+
const promoteAliasId = modelAliasIdPromoter(config);
|
|
1255
1257
|
const routedRows = await Promise.all(uniqueCatalogModelsForRawPublicList(goOrdered)
|
|
1256
1258
|
.filter(m => listAllows(m.provider, m.id))
|
|
1257
1259
|
.map(async m => {
|
|
@@ -1269,8 +1271,11 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1269
1271
|
knownModelIdsForProvider(m.provider, provider, config),
|
|
1270
1272
|
).get(m.id)
|
|
1271
1273
|
: undefined;
|
|
1274
|
+
const effectiveId = !isCombo && !fastModelId && effective
|
|
1275
|
+
? promoteAliasId(m.provider, m.id, effective.alias, catalogRowFastEligible(m)) ?? publicId
|
|
1276
|
+
: publicId;
|
|
1272
1277
|
const row = {
|
|
1273
|
-
id:
|
|
1278
|
+
id: effectiveId,
|
|
1274
1279
|
object: "model",
|
|
1275
1280
|
created: 0,
|
|
1276
1281
|
// This endpoint is an OpenAI-compatible inbound contract. Some clients use
|
|
@@ -1279,6 +1284,9 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1279
1284
|
owned_by: isCombo ? "openai" : (m.owned_by ?? m.provider),
|
|
1280
1285
|
...(isCombo ? { is_combo: true } : {}),
|
|
1281
1286
|
...(effective ? { alias_of: `${provider?.alias || m.provider}/${effective.alias}` } : {}),
|
|
1287
|
+
// When preferModelAliasIds promotes the alias to `id`, retain the original
|
|
1288
|
+
// provider/nativeId so clients that need the upstream identity can read it.
|
|
1289
|
+
...(effectiveId !== publicId ? { native_id: publicId } : {}),
|
|
1282
1290
|
...grokEffortFields(m.reasoningEfforts ?? [], m.defaultReasoningEffort),
|
|
1283
1291
|
...modelCapabilityFields({
|
|
1284
1292
|
reasoningEfforts: m.reasoningEfforts,
|
|
@@ -149,6 +149,8 @@ export function withRequestLogId(response: Response, requestId: string): Respons
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
export interface StartServerDeps {
|
|
152
|
+
/** Test-only CONNECT binder; permits kernel-assigned ports without probe-and-release races. */
|
|
153
|
+
startClaudeInterceptProxy?: typeof import("../../claude/intercept/connect-proxy").startConnectProxy;
|
|
152
154
|
/** Test-only seam; production always initializes its own management credential state. */
|
|
153
155
|
managementAuthState?: ManagementAuthState;
|
|
154
156
|
/** Test-only route dependencies, forwarded only after management admission succeeds. */
|
package/src/server/index.ts
CHANGED
|
@@ -738,7 +738,7 @@ function startServerWithSpendLedgerOwner(port: number | undefined, deps: StartSe
|
|
|
738
738
|
throw new AuxiliaryListenerBindError("hub.managementIngress", managementIngressPort, "127.0.0.1", error);
|
|
739
739
|
}
|
|
740
740
|
}
|
|
741
|
-
optionalListeners.start({ config, publicPort: server.port ?? listenPort, requestedPort: listenPort,
|
|
741
|
+
optionalListeners.start({ config, publicPort: server.port ?? listenPort, requestedPort: listenPort, startProxy: deps.startClaudeInterceptProxy,
|
|
742
742
|
maxRequestBodySize: inboundBodyLimitBytes, dispatch: (req, requestServer) => serveOptions.fetch(req, requestServer) });
|
|
743
743
|
} catch (error) {
|
|
744
744
|
unregisterQuotaAutoRefresh?.();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { RequestLogContext } from "../request-log";
|
|
2
|
+
import { ADVISOR_INTERNAL_CAPABILITY_HEADER, isInternalCallCapability } from "../../lib/local-internal-call-capability";
|
|
3
|
+
|
|
4
|
+
export const ADVISOR_REASONING_HEADER = "x-opencodex-advisor-reasoning";
|
|
5
|
+
const EFFORTS = new Set(["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"]);
|
|
6
|
+
|
|
7
|
+
/** Native Chat sends the finalized body directly instead of the translating adapter builder. */
|
|
8
|
+
export function observeAdvisorNativeReasoning(headers: Headers, log: RequestLogContext, body: string): void {
|
|
9
|
+
if (!isInternalCallCapability(headers.get(ADVISOR_INTERNAL_CAPABILITY_HEADER))) return;
|
|
10
|
+
delete log.effectiveEffort;
|
|
11
|
+
try {
|
|
12
|
+
const effort = JSON.parse(body)?.reasoning_effort;
|
|
13
|
+
if (typeof effort === "string" && EFFORTS.has(effort)) log.effectiveEffort = effort;
|
|
14
|
+
} catch { /* absence of evidence stays unreported */ }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Only the authenticated internal caller receives adapter-owned serialization evidence. */
|
|
18
|
+
export function attachAdvisorReasoning(response: Response, log: RequestLogContext): Response {
|
|
19
|
+
const headers = new Headers(response.headers);
|
|
20
|
+
headers.set(ADVISOR_REASONING_HEADER, JSON.stringify({
|
|
21
|
+
effectiveEffort: log.effectiveEffort && EFFORTS.has(log.effectiveEffort) ? log.effectiveEffort : null,
|
|
22
|
+
}));
|
|
23
|
+
return new Response(response.body, { status: response.status, statusText: response.statusText, headers });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function readAdvisorReasoning(headers: Headers): string | null {
|
|
27
|
+
try {
|
|
28
|
+
const value = JSON.parse(headers.get(ADVISOR_REASONING_HEADER) ?? "null")?.effectiveEffort;
|
|
29
|
+
return typeof value === "string" && EFFORTS.has(value) ? value : null;
|
|
30
|
+
} catch { return null; }
|
|
31
|
+
}
|
|
@@ -20,10 +20,12 @@ import {
|
|
|
20
20
|
isCurrentAdvisorContextSharingConsent,
|
|
21
21
|
isValidAdvisorEffort,
|
|
22
22
|
isValidAdvisorPolicy,
|
|
23
|
+
isValidAdvisorTaskLimit,
|
|
23
24
|
resolveAdvisorSettings,
|
|
24
25
|
type AdvisorEffort,
|
|
25
26
|
type AdvisorPolicy,
|
|
26
27
|
} from "../../advisor/settings";
|
|
28
|
+
import { advisorActivityStore } from "../../advisor/activity";
|
|
27
29
|
|
|
28
30
|
const INVALID_BODY = Symbol("invalid-body");
|
|
29
31
|
|
|
@@ -33,6 +35,7 @@ interface AdvisorPatch {
|
|
|
33
35
|
effort?: AdvisorEffort;
|
|
34
36
|
policy?: AdvisorPolicy;
|
|
35
37
|
timeoutMs?: number;
|
|
38
|
+
maxConsultationsPerTask?: number;
|
|
36
39
|
/** "v1" records current consent. null removes it. Enabling does not imply this field. */
|
|
37
40
|
contextSharingConsent?: typeof ADVISOR_CONTEXT_SHARING_CONSENT_VERSION | null;
|
|
38
41
|
reset?: boolean;
|
|
@@ -40,7 +43,7 @@ interface AdvisorPatch {
|
|
|
40
43
|
|
|
41
44
|
type ParsedPatch = { ok: true; patch: AdvisorPatch } | { ok: false; code: string; message: string };
|
|
42
45
|
|
|
43
|
-
const PATCH_KEYS = new Set(["enabled", "model", "effort", "policy", "timeoutMs", "contextSharingConsent", "reset"]);
|
|
46
|
+
const PATCH_KEYS = new Set(["enabled", "model", "effort", "policy", "timeoutMs", "maxConsultationsPerTask", "contextSharingConsent", "reset"]);
|
|
44
47
|
|
|
45
48
|
type Rec = Record<string, unknown>;
|
|
46
49
|
function isRec(value: unknown): value is Rec {
|
|
@@ -50,9 +53,9 @@ function isRec(value: unknown): value is Rec {
|
|
|
50
53
|
/** Strict: unknown keys and wrong types are refused; messages name the field, never the value. */
|
|
51
54
|
export function parseAdvisorSettingsPatch(body: unknown): ParsedPatch {
|
|
52
55
|
if (!isRec(body)) return { ok: false, code: "invalid_body", message: "body must be a JSON object" };
|
|
53
|
-
if (Object.keys(body).length === 0) return { ok: false, code: "empty_body", message: "body must set at least one
|
|
56
|
+
if (Object.keys(body).length === 0) return { ok: false, code: "empty_body", message: "body must set at least one supported advisor setting or reset" };
|
|
54
57
|
for (const key of Object.keys(body)) {
|
|
55
|
-
if (!PATCH_KEYS.has(key)) return { ok: false, code: "unknown_field", message: "body accepts only enabled, model, effort, policy, timeoutMs, contextSharingConsent and reset" };
|
|
58
|
+
if (!PATCH_KEYS.has(key)) return { ok: false, code: "unknown_field", message: "body accepts only enabled, model, effort, policy, timeoutMs, maxConsultationsPerTask, contextSharingConsent and reset" };
|
|
56
59
|
}
|
|
57
60
|
const patch: AdvisorPatch = {};
|
|
58
61
|
if (body.reset !== undefined) {
|
|
@@ -86,7 +89,7 @@ export function parseAdvisorSettingsPatch(body: unknown): ParsedPatch {
|
|
|
86
89
|
}
|
|
87
90
|
if (body.policy !== undefined) {
|
|
88
91
|
if (!isValidAdvisorPolicy(body.policy)) {
|
|
89
|
-
return { ok: false, code: "invalid_policy", message: 'policy must be "manual" or "
|
|
92
|
+
return { ok: false, code: "invalid_policy", message: 'policy must be "manual", "preflight" or "adaptive"' };
|
|
90
93
|
}
|
|
91
94
|
patch.policy = body.policy;
|
|
92
95
|
}
|
|
@@ -96,6 +99,10 @@ export function parseAdvisorSettingsPatch(body: unknown): ParsedPatch {
|
|
|
96
99
|
}
|
|
97
100
|
patch.timeoutMs = Math.floor(body.timeoutMs);
|
|
98
101
|
}
|
|
102
|
+
if (body.maxConsultationsPerTask !== undefined) {
|
|
103
|
+
if (!isValidAdvisorTaskLimit(body.maxConsultationsPerTask)) return { ok: false, code: "invalid_task_limit", message: "maxConsultationsPerTask must be an integer between 1 and 3" };
|
|
104
|
+
patch.maxConsultationsPerTask = body.maxConsultationsPerTask;
|
|
105
|
+
}
|
|
99
106
|
if (body.contextSharingConsent !== undefined) {
|
|
100
107
|
if (body.contextSharingConsent === null) {
|
|
101
108
|
patch.contextSharingConsent = null;
|
|
@@ -139,6 +146,7 @@ function applyPatchInMemory(config: ManagementContext["config"], patch: AdvisorP
|
|
|
139
146
|
if (patch.effort !== undefined) current.effort = patch.effort;
|
|
140
147
|
if (patch.policy !== undefined) current.policy = patch.policy;
|
|
141
148
|
if (patch.timeoutMs !== undefined) current.timeoutMs = patch.timeoutMs;
|
|
149
|
+
if (patch.maxConsultationsPerTask !== undefined) current.maxConsultationsPerTask = patch.maxConsultationsPerTask;
|
|
142
150
|
if (patch.contextSharingConsent === null) delete current.contextSharingConsent;
|
|
143
151
|
else if (patch.contextSharingConsent !== undefined) current.contextSharingConsent = patch.contextSharingConsent;
|
|
144
152
|
config.advisor = current as ManagementContext["config"]["advisor"];
|
|
@@ -152,6 +160,9 @@ function isConfigLockContention(error: unknown): boolean {
|
|
|
152
160
|
|
|
153
161
|
export async function handleAdvisorRoutes(ctx: ManagementContext): Promise<Response | null> {
|
|
154
162
|
const { url, req, config } = ctx;
|
|
163
|
+
if (url.pathname === "/api/advisor/activity" && req.method === "GET") {
|
|
164
|
+
return jsonResponse({ entries: advisorActivityStore(config).snapshot(), retention: "process", limit: 100 }, 200, req, config);
|
|
165
|
+
}
|
|
155
166
|
if (url.pathname !== "/api/advisor/settings") return null;
|
|
156
167
|
|
|
157
168
|
if (req.method === "GET") return jsonResponse(advisorInfo(config), 200, req, config);
|
|
@@ -44,7 +44,7 @@ export function pickerPreferenceOn(config: Pick<OcxConfig, "claudeCode">): boole
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
const PICKER_BODY_KEYS = new Set(["enabled", "persist", "trustedLocally", "callerAddedTrust"]);
|
|
47
|
-
/** Enable outcomes that are progress, not refusal: on, waiting for a Desktop restart, or for
|
|
47
|
+
/** Enable outcomes that are progress, not refusal: on, waiting for a Desktop restart, or for certificate trust. */
|
|
48
48
|
const ENABLE_ACCEPTED = new Set(["active", "restart_required", "trust_pending"]);
|
|
49
49
|
|
|
50
50
|
interface PickerRequestBody { enabled: boolean; persist: boolean; trustedLocally?: boolean; callerAddedTrust?: boolean }
|
|
@@ -52,6 +52,15 @@ import {
|
|
|
52
52
|
setDebugSettings,
|
|
53
53
|
type DebugFlag,
|
|
54
54
|
} from "../../lib/debug-settings";
|
|
55
|
+
import { mergeModelCapabilities, modelCapabilitiesConfigError } from "../../config/provider-validation";
|
|
56
|
+
import { modelRecordValue } from "../../reasoning-effort";
|
|
57
|
+
import { commitProviderPatch } from "./provider-patch-transaction";
|
|
58
|
+
import { ConfigWritePublishedError } from "../../config/persist-unlocked";
|
|
59
|
+
import {
|
|
60
|
+
customRowInputModalities,
|
|
61
|
+
isVisionSidecarConsumer,
|
|
62
|
+
modelAcceptsImageInput,
|
|
63
|
+
} from "../../vision/eligibility";
|
|
55
64
|
import type { OcxClaudeCodeConfig, OcxComboConfig, OcxConfig, OcxCustomModel, OcxProviderConfig, OcxComboCooldownWaitPolicy } from "../../types";
|
|
56
65
|
import { drainAndShutdown } from "../lifecycle";
|
|
57
66
|
import { reconcileLiveStateStores } from "../../lib/state-store-registrations";
|
|
@@ -116,6 +125,43 @@ function sparseComboConfig<T extends {
|
|
|
116
125
|
};
|
|
117
126
|
}
|
|
118
127
|
|
|
128
|
+
interface VisionSidecarTarget {
|
|
129
|
+
provider: string;
|
|
130
|
+
model: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Request-only PUT field: combo members to declare text-only so the Vision Sidecar
|
|
135
|
+
* covers them when the combo accepts images. Every entry must be an exact target of
|
|
136
|
+
* the submitted combo, and the whole field is validated before any config mutation.
|
|
137
|
+
* Nothing from here is persisted under config.combos.
|
|
138
|
+
*/
|
|
139
|
+
function parseVisionSidecarTargets(
|
|
140
|
+
raw: unknown,
|
|
141
|
+
comboTargetKeys: ReadonlySet<string>,
|
|
142
|
+
): { targets?: VisionSidecarTarget[]; error?: string } {
|
|
143
|
+
if (raw === undefined) return {};
|
|
144
|
+
if (!Array.isArray(raw)) return { error: "visionSidecarTargets must be an array" };
|
|
145
|
+
const seen = new Set<string>();
|
|
146
|
+
const targets: VisionSidecarTarget[] = [];
|
|
147
|
+
for (const entry of raw) {
|
|
148
|
+
if (!isPlainRecord(entry)) return { error: "visionSidecarTargets entries must be objects" };
|
|
149
|
+
const provider = typeof entry.provider === "string" ? entry.provider.trim() : "";
|
|
150
|
+
const model = typeof entry.model === "string" ? entry.model.trim() : "";
|
|
151
|
+
if (!provider || !model) {
|
|
152
|
+
return { error: "visionSidecarTargets entries must have nonblank provider and model" };
|
|
153
|
+
}
|
|
154
|
+
const key = `${provider}/${model}`;
|
|
155
|
+
if (!comboTargetKeys.has(key)) {
|
|
156
|
+
return { error: `visionSidecarTargets entry "${key}" is not a target of this combo` };
|
|
157
|
+
}
|
|
158
|
+
if (seen.has(key)) continue;
|
|
159
|
+
seen.add(key);
|
|
160
|
+
targets.push({ provider, model });
|
|
161
|
+
}
|
|
162
|
+
return { targets };
|
|
163
|
+
}
|
|
164
|
+
|
|
119
165
|
export async function handleComboRoutes(ctx: ManagementContext): Promise<Response | null> {
|
|
120
166
|
const { req, url, config, deps, convergeCodexCatalog, syncClaudeAgentDefsBestEffort } = ctx;
|
|
121
167
|
|
|
@@ -176,6 +222,9 @@ export async function handleComboRoutes(ctx: ManagementContext): Promise<Respons
|
|
|
176
222
|
const requestedCombo: Record<string, unknown> = body.combo;
|
|
177
223
|
const effectiveCombo = {
|
|
178
224
|
...requestedCombo,
|
|
225
|
+
...(!Object.hasOwn(requestedCombo, "allowMeteredTargets") && previous?.allowMeteredTargets === true
|
|
226
|
+
? { allowMeteredTargets: true }
|
|
227
|
+
: {}),
|
|
179
228
|
...(!Object.hasOwn(requestedCombo, "cooldownMs") && previous?.cooldownMs !== undefined
|
|
180
229
|
? { cooldownMs: previous.cooldownMs }
|
|
181
230
|
: {}),
|
|
@@ -203,7 +252,7 @@ export async function handleComboRoutes(ctx: ManagementContext): Promise<Respons
|
|
|
203
252
|
// A non-record entry stays as the client sent it so comboConfigError reports it.
|
|
204
253
|
// Reading `lastResort` off it here would throw in place of that structured 400.
|
|
205
254
|
if (!isPlainRecord(target)) return target;
|
|
206
|
-
if (Object.hasOwn(target,
|
|
255
|
+
if (["lastResort", "metered"].every(key => Object.hasOwn(target, key))) return target;
|
|
207
256
|
const rawProvider = target.provider;
|
|
208
257
|
const rawModel = target.model;
|
|
209
258
|
if (typeof rawProvider !== "string" || typeof rawModel !== "string") return target;
|
|
@@ -214,7 +263,13 @@ export async function handleComboRoutes(ctx: ManagementContext): Promise<Respons
|
|
|
214
263
|
const before = previous.targets.find(
|
|
215
264
|
candidate => candidate.provider === provider && candidate.model === model,
|
|
216
265
|
);
|
|
217
|
-
|
|
266
|
+
// `metered` is carried the same way: dropping it on a GUI round-trip would open a
|
|
267
|
+
// pay-per-use target to every issued key.
|
|
268
|
+
return {
|
|
269
|
+
...target,
|
|
270
|
+
...(!Object.hasOwn(target, "lastResort") && before?.lastResort ? { lastResort: true } : {}),
|
|
271
|
+
...(!Object.hasOwn(target, "metered") && before?.metered ? { metered: true } : {}),
|
|
272
|
+
};
|
|
218
273
|
}),
|
|
219
274
|
}
|
|
220
275
|
: {}),
|
|
@@ -280,6 +335,54 @@ export async function handleComboRoutes(ctx: ManagementContext): Promise<Respons
|
|
|
280
335
|
if (routeError) return jsonResponse({ error: issue ? `combo "${otherId}": ${routeError}` : routeError }, 400);
|
|
281
336
|
}
|
|
282
337
|
const normalized = normalizeComboConfig(effectiveCombo as unknown as OcxComboConfig);
|
|
338
|
+
const sidecarParsed = parseVisionSidecarTargets(
|
|
339
|
+
body.visionSidecarTargets,
|
|
340
|
+
new Set(normalized.targets.map((target) => `${target.provider}/${target.model}`)),
|
|
341
|
+
);
|
|
342
|
+
if (sidecarParsed.error) return jsonResponse({ error: sidecarParsed.error }, 400);
|
|
343
|
+
const sidecarPatches = new Map<string, Record<string, { inputModalities: string[] }>>();
|
|
344
|
+
if (sidecarParsed.targets?.length) {
|
|
345
|
+
if (normalized.imageInput === "disabled") {
|
|
346
|
+
return jsonResponse({ error: "visionSidecarTargets requires imageInput not disabled" }, 400);
|
|
347
|
+
}
|
|
348
|
+
for (const { provider, model } of sidecarParsed.targets) {
|
|
349
|
+
const providerRow = config.providers?.[provider];
|
|
350
|
+
if (!providerRow) {
|
|
351
|
+
return jsonResponse({ error: `visionSidecarTargets entry "${provider}/${model}" has no configured provider` }, 400);
|
|
352
|
+
}
|
|
353
|
+
// A text-only declaration would HIDE a real capability; never overwrite one.
|
|
354
|
+
if (modelAcceptsImageInput(config, { provider, id: model }) === true) {
|
|
355
|
+
return jsonResponse({ error: `visionSidecarTargets entry "${provider}/${model}" already accepts image input` }, 400);
|
|
356
|
+
}
|
|
357
|
+
// Already a consumer through any runtime source (exact declaration, custom row,
|
|
358
|
+
// noVisionModels, legacy record, registry enrichment): the catalog already
|
|
359
|
+
// advertises image for it. Writing the declaration again would be a no-op write.
|
|
360
|
+
if (isVisionSidecarConsumer(config, provider, model)) continue;
|
|
361
|
+
// Read the declaration the way the runtime does: exact capability axis, then the
|
|
362
|
+
// operator's custom row, then the provider's per-model modality hint. A row without
|
|
363
|
+
// text (audio-only) cannot be widened by the sidecar, and writing ["text"] on the
|
|
364
|
+
// capability axis would silently override that declaration.
|
|
365
|
+
const capabilityRow = Object.hasOwn(providerRow.modelCapabilities ?? {}, model)
|
|
366
|
+
? providerRow.modelCapabilities?.[model] : undefined;
|
|
367
|
+
const declared = capabilityRow?.inputModalities
|
|
368
|
+
?? customRowInputModalities(config, provider, model)
|
|
369
|
+
?? modelRecordValue(providerRow.modelInputModalities, model);
|
|
370
|
+
if (declared !== undefined && !declared.includes("text")) {
|
|
371
|
+
return jsonResponse({ error: `visionSidecarTargets entry "${provider}/${model}" is declared without text input; the Vision Sidecar cannot cover it` }, 400);
|
|
372
|
+
}
|
|
373
|
+
const patch = sidecarPatches.get(provider) ?? {};
|
|
374
|
+
patch[model] = { inputModalities: ["text"] };
|
|
375
|
+
sidecarPatches.set(provider, patch);
|
|
376
|
+
}
|
|
377
|
+
for (const [providerName, patch] of sidecarPatches) {
|
|
378
|
+
// Validate the map that will be persisted, not just the patch: a provider already at
|
|
379
|
+
// the modelCapabilities cap would otherwise save an oversized map that load-time
|
|
380
|
+
// sanitization truncates, silently dropping the new text-only declaration.
|
|
381
|
+
const merged = mergeModelCapabilities(config.providers?.[providerName]?.modelCapabilities, patch);
|
|
382
|
+
const error = modelCapabilitiesConfigError(patch) ?? (merged ? modelCapabilitiesConfigError(merged) : null);
|
|
383
|
+
if (error) return jsonResponse({ error: `visionSidecarTargets for provider "${providerName}": ${error}` }, 400);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
283
386
|
// Persist only non-default identity/capability fields so config stays sparse.
|
|
284
387
|
// Capability defaults (`imageInput`, `reasoningEffortMode`) go through the same
|
|
285
388
|
// helper the GET/PUT responses use, so the wire shape and the stored shape cannot drift.
|
|
@@ -295,9 +398,10 @@ export async function handleComboRoutes(ctx: ManagementContext): Promise<Respons
|
|
|
295
398
|
// would add a noise key to every target of every combo, including ones that never use
|
|
296
399
|
// the policy (#5736). Only the opt-in value is stored, matching how the combo-level
|
|
297
400
|
// policy is handled in sparseComboConfig.
|
|
298
|
-
targets: normalizedBase.targets.map(({ lastResort, modelProfile, ...target }) => ({
|
|
401
|
+
targets: normalizedBase.targets.map(({ lastResort, metered, modelProfile, ...target }) => ({
|
|
299
402
|
...target,
|
|
300
403
|
...(lastResort ? { lastResort: true } : {}),
|
|
404
|
+
...(metered ? { metered: true } : {}),
|
|
301
405
|
...(modelProfile ? { modelProfile } : {}),
|
|
302
406
|
})),
|
|
303
407
|
...(normalizedAlias ? { alias: normalizedAlias } : {}),
|
|
@@ -340,58 +444,80 @@ export async function handleComboRoutes(ctx: ManagementContext): Promise<Respons
|
|
|
340
444
|
const targetError = shadowCallTargetError({ ...config, combos: nextCombos }, migratedShadowTarget);
|
|
341
445
|
if (targetError) return jsonResponse({ error: targetError }, 400);
|
|
342
446
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
if (
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
447
|
+
// One atomic transaction: a failed save must restore the combo, the capability
|
|
448
|
+
// declarations, and every migrated reference together, never partially.
|
|
449
|
+
try {
|
|
450
|
+
commitProviderPatch(config, () => {
|
|
451
|
+
config.combos = nextCombos;
|
|
452
|
+
if (migratedModels.size > 0) {
|
|
453
|
+
const migrateReference = (model: string): string => migratedModels.get(model) ?? model;
|
|
454
|
+
const migrateAgentReference = (model: string): string => {
|
|
455
|
+
const migrated = migrateReference(model);
|
|
456
|
+
if (migrated !== model) shouldSyncClaudeAgentDefs = true;
|
|
457
|
+
return migrated;
|
|
458
|
+
};
|
|
459
|
+
if (config.subagentModels) {
|
|
460
|
+
config.subagentModels = [...new Set(config.subagentModels.map(migrateAgentReference))];
|
|
461
|
+
}
|
|
462
|
+
if (config.injectionModel && migratedModels.has(config.injectionModel)) {
|
|
463
|
+
config.injectionModel = migrateReference(config.injectionModel);
|
|
464
|
+
}
|
|
465
|
+
if (config.shadowCallIntercept?.model && migratedModels.has(config.shadowCallIntercept.model)) {
|
|
466
|
+
config.shadowCallIntercept = {
|
|
467
|
+
...config.shadowCallIntercept,
|
|
468
|
+
model: migrateReference(config.shadowCallIntercept.model),
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
if (config.claudeCode) {
|
|
472
|
+
const claudeCode = { ...config.claudeCode };
|
|
473
|
+
for (const field of ["model", "smallFastModel"] as const) {
|
|
474
|
+
if (claudeCode[field]) claudeCode[field] = migrateAgentReference(claudeCode[field]);
|
|
475
|
+
}
|
|
476
|
+
if (claudeCode.tierModels) {
|
|
477
|
+
claudeCode.tierModels = Object.fromEntries(
|
|
478
|
+
Object.entries(claudeCode.tierModels).map(([tier, model]) => [tier, migrateAgentReference(model)]),
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
if (claudeCode.modelMap) {
|
|
482
|
+
claudeCode.modelMap = Object.fromEntries(
|
|
483
|
+
Object.entries(claudeCode.modelMap).map(([source, model]) => [source, migrateAgentReference(model)]),
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
if (claudeCode.intercept?.modelMap) {
|
|
487
|
+
claudeCode.intercept = {
|
|
488
|
+
...claudeCode.intercept,
|
|
489
|
+
modelMap: Object.fromEntries(
|
|
490
|
+
Object.entries(claudeCode.intercept.modelMap).map(([pickerId, route]) => [pickerId, migrateAgentReference(route)]),
|
|
491
|
+
),
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
config.claudeCode = claudeCode;
|
|
495
|
+
}
|
|
372
496
|
}
|
|
373
|
-
if (
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
);
|
|
497
|
+
if (oldDisabledSelectors.size > 0 && config.disabledModels) {
|
|
498
|
+
config.disabledModels = [...new Set(config.disabledModels.map(model => (
|
|
499
|
+
oldDisabledSelectors.has(model) ? newDisabledModel : model
|
|
500
|
+
)))];
|
|
377
501
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
502
|
+
for (const [providerName, patch] of sidecarPatches) {
|
|
503
|
+
const provider = config.providers?.[providerName];
|
|
504
|
+
if (!provider) continue;
|
|
505
|
+
const capabilities = mergeModelCapabilities(provider.modelCapabilities, patch);
|
|
506
|
+
// Re-check under the config lock: a concurrent edit may have grown the map since
|
|
507
|
+
// the pre-check, and an oversized map must never be published.
|
|
508
|
+
if (capabilities && modelCapabilitiesConfigError(capabilities)) {
|
|
509
|
+
throw new Error("modelCapabilities would exceed its limit");
|
|
510
|
+
}
|
|
511
|
+
if (capabilities === undefined) delete provider.modelCapabilities;
|
|
512
|
+
else provider.modelCapabilities = capabilities;
|
|
385
513
|
}
|
|
386
|
-
|
|
514
|
+
}, deps.saveConfigPreservingClaudeCode ?? saveConfigPreservingClaudeCode);
|
|
515
|
+
} catch (error) {
|
|
516
|
+
if (!(error instanceof ConfigWritePublishedError)) {
|
|
517
|
+
return jsonResponse({ error: "combo could not be saved" }, 500);
|
|
387
518
|
}
|
|
519
|
+
throw error;
|
|
388
520
|
}
|
|
389
|
-
if (oldDisabledSelectors.size > 0 && config.disabledModels) {
|
|
390
|
-
config.disabledModels = [...new Set(config.disabledModels.map(model => (
|
|
391
|
-
oldDisabledSelectors.has(model) ? newDisabledModel : model
|
|
392
|
-
)))];
|
|
393
|
-
}
|
|
394
|
-
saveConfigPreservingClaudeCode(config);
|
|
395
521
|
reconcileLiveStateStores();
|
|
396
522
|
clearComboSelectionState(id);
|
|
397
523
|
clearComboTargetCooldowns(id);
|
|
@@ -27,7 +27,7 @@ function response(): Response {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export async function handleCompanionRoutes(ctx: ManagementContext): Promise<Response | null> {
|
|
30
|
-
if (ctx.url.pathname === "/api/advisor/settings") {
|
|
30
|
+
if (ctx.url.pathname === "/api/advisor/settings" || ctx.url.pathname === "/api/advisor/activity") {
|
|
31
31
|
const { handleAdvisorRoutes } = await import("./advisor-routes");
|
|
32
32
|
return handleAdvisorRoutes(ctx);
|
|
33
33
|
}
|