@bitkyc08/opencodex 2.52.0-preview.20260912 → 2.53.0-preview.20260913
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/index-BBOZWGB6.css +1 -0
- package/gui/dist/assets/index-D7ynYo2K.js +128 -0
- package/gui/dist/index.html +2 -2
- package/native/remote-workspace-helper/Cargo.lock +130 -0
- package/native/remote-workspace-helper/Cargo.toml +24 -0
- package/native/remote-workspace-helper/src/main.rs +49 -0
- package/native/remote-workspace-helper/src/protocol.rs +246 -0
- package/native/remote-workspace-helper/src/sandbox/macos.rs +19 -0
- package/native/remote-workspace-helper/src/sandbox/mod.rs +77 -0
- package/native/remote-workspace-helper/src/sandbox/windows.rs +15 -0
- package/package.json +6 -1
- package/src/adapters/anthropic-image-normalize.ts +30 -2
- package/src/adapters/anthropic.ts +1 -1
- package/src/adapters/base.ts +8 -2
- package/src/adapters/cursor/cursor-errors.ts +12 -0
- package/src/adapters/cursor/thread-continuity.ts +93 -0
- package/src/adapters/cursor.ts +104 -73
- package/src/adapters/devin/cloud-direct/chat.ts +312 -23
- package/src/adapters/devin/cloud-direct/metadata.ts +31 -2
- package/src/adapters/devin/live-models.ts +70 -3
- package/src/adapters/devin.ts +281 -21
- package/src/adapters/google-wire-compiler.ts +14 -6
- package/src/adapters/google.ts +22 -8
- package/src/adapters/kiro/adapter.ts +316 -0
- package/src/adapters/kiro/conversation.ts +136 -0
- package/src/adapters/kiro/payload.ts +432 -0
- package/src/adapters/kiro/reasoning.ts +56 -0
- package/src/adapters/kiro/stream.ts +1153 -0
- package/src/adapters/kiro/usage.ts +223 -0
- package/src/adapters/kiro/wire.ts +76 -0
- package/src/adapters/kiro.ts +8 -2319
- package/src/adapters/mimo-free.ts +1 -1
- package/src/adapters/openai-chat-images.ts +101 -0
- package/src/adapters/openai-chat.ts +201 -181
- package/src/adapters/openai-responses.ts +92 -224
- package/src/adapters/registry.ts +0 -7
- package/src/adapters/run-turn-queue.ts +13 -6
- package/src/bridge.ts +14 -15
- package/src/chat/inbound.ts +29 -4
- package/src/chat/outbound.ts +145 -107
- package/src/claude/desktop-profile.ts +4 -6
- package/src/cli/account-api.ts +14 -0
- package/src/cli/account-extended.ts +1 -1
- package/src/cli/account-history.ts +60 -0
- package/src/cli/account-main.ts +80 -0
- package/src/cli/account.ts +11 -3
- package/src/cli/capabilities.ts +113 -0
- package/src/cli/catalog.ts +109 -0
- package/src/cli/dispatch.ts +9 -0
- package/src/cli/help.ts +2 -0
- package/src/cli/index.ts +2 -2
- package/src/cli/observe.ts +28 -1
- package/src/cli/opencode.ts +42 -8
- package/src/cli/provider-runtime.ts +11 -1
- package/src/cli/provider.ts +22 -2
- package/src/cli/registry.ts +21 -0
- package/src/cli/remote-workspace.ts +154 -0
- package/src/cli/status.ts +39 -7
- package/src/cli/usage-report.ts +14 -2
- package/src/client/hub-client.ts +34 -0
- package/src/client/hub-state.ts +9 -1
- package/src/codex/account-store.ts +78 -0
- package/src/codex/auth-api.ts +81 -54
- package/src/codex/auth-context.ts +45 -16
- package/src/codex/catalog/effort.ts +1 -1
- package/src/codex/catalog/metadata.ts +3 -6
- package/src/codex/catalog/native-models.ts +4 -4
- package/src/codex/catalog/parsing.ts +2 -20
- package/src/codex/catalog/provider-fetch.ts +10 -1
- package/src/codex/catalog/remote.ts +233 -0
- package/src/codex/catalog/sync.ts +403 -35
- package/src/codex/convergence.ts +1 -1
- package/src/codex/history-manifest.ts +36 -0
- package/src/codex/history-provider.ts +32 -5
- package/src/codex/inject.ts +9 -0
- package/src/codex/main-account.ts +113 -0
- package/src/codex/main-device-reauth-api.ts +89 -0
- package/src/codex/main-device-reauth.ts +217 -0
- package/src/codex/native-residue.ts +9 -2
- package/src/codex/quota-auto-refresh.ts +3 -2
- package/src/codex/quota-capacity.ts +98 -0
- package/src/codex/quota-history.ts +160 -0
- package/src/codex/quota-types.ts +8 -0
- package/src/codex/quota.ts +118 -91
- package/src/codex/refresh.ts +2 -1
- package/src/codex/routing.ts +90 -17
- package/src/codex/sync.ts +33 -4
- package/src/combos/request.ts +19 -1
- package/src/config/multi-agent-surface.ts +61 -0
- package/src/config/provider-validation.ts +176 -0
- package/src/config.ts +213 -11
- package/src/generated/compatibility-version.json +436 -168
- package/src/images/loop.ts +119 -36
- package/src/lib/admission.ts +12 -6
- package/src/lib/redact.ts +7 -0
- package/src/lib/translator-budget.ts +4 -3
- package/src/lib/windows-atomic-replace.ts +1 -0
- package/src/lib/windows-elevation.ts +1 -1
- package/src/oauth/chatgpt-device.ts +62 -5
- package/src/oauth/devin/cli-import.ts +130 -0
- package/src/oauth/devin.ts +63 -8
- package/src/oauth/index.ts +29 -14
- package/src/oauth/kiro.ts +18 -6
- package/src/oauth/login-cli.ts +9 -1
- package/src/oauth/meta-muse-device.ts +464 -0
- package/src/oauth/meta-muse.ts +123 -32
- package/src/oauth/pool-kernel.ts +9 -0
- package/src/oauth/pool-settings-capability.ts +2 -2
- package/src/oauth/store.ts +57 -0
- package/src/oauth/types.ts +31 -0
- package/src/providers/derive.ts +13 -3
- package/src/providers/devin-cli-authmode-migration.ts +57 -35
- package/src/providers/devin-provider-merge-migration.ts +240 -0
- package/src/providers/muse-key-quota.ts +117 -0
- package/src/providers/muse-subscription-usage.ts +14 -2
- package/src/providers/openai-sidecar.ts +25 -3
- package/src/providers/opencode-zen-rate-limit.ts +58 -0
- package/src/providers/provider-id-rewrite.ts +20 -5
- package/src/providers/quota-types.ts +12 -0
- package/src/providers/quota.ts +143 -102
- package/src/providers/reasoning-metadata.ts +543 -0
- package/src/providers/registry.ts +80 -49
- package/src/reasoning-effort.ts +26 -2
- package/src/remote/hub-usage.ts +32 -0
- package/src/remote-control/index.ts +192 -41
- package/src/remote-control/workspace-activation.ts +9 -0
- package/src/remote-control/workspace-agent-connection.ts +366 -0
- package/src/remote-control/workspace-claude-runtime.ts +243 -0
- package/src/remote-control/workspace-codex-runtime.ts +531 -0
- package/src/remote-control/workspace-codex-sandbox.ts +115 -0
- package/src/remote-control/workspace-command-runner.ts +748 -0
- package/src/remote-control/workspace-coordinator.ts +231 -0
- package/src/remote-control/workspace-device.ts +585 -0
- package/src/remote-control/workspace-executable.ts +43 -0
- package/src/remote-control/workspace-executor.ts +397 -0
- package/src/remote-control/workspace-hub.ts +519 -0
- package/src/remote-control/workspace-pi-runtime.ts +382 -0
- package/src/remote-control/workspace-process.ts +129 -0
- package/src/remote-control/workspace-rpc.ts +304 -0
- package/src/remote-control/workspace-runtime.ts +60 -0
- package/src/remote-control/workspace-secret-store.ts +39 -0
- package/src/remote-control/workspace-sessions.ts +799 -0
- package/src/remote-control/workspace-tool-bridge.ts +192 -0
- package/src/responses/code-mode-helper-compat.ts +22 -3
- package/src/responses/hosted-tool-policy.ts +0 -1
- package/src/responses/muse-tool-name-alias.ts +379 -0
- package/src/responses/plaintext-v2-agent-messages.ts +902 -0
- package/src/router.ts +7 -0
- package/src/routing/compatibility/behavior.ts +0 -1
- package/src/server/audio-client.ts +64 -0
- package/src/server/audio-dictation.ts +91 -0
- package/src/server/audio-live.ts +185 -0
- package/src/server/audio-transcriptions.ts +183 -0
- package/src/server/audio-upstream.ts +153 -0
- package/src/server/auth-cors.ts +61 -2
- package/src/server/chat-completions.ts +1 -1
- package/src/server/chat-native-sse.ts +92 -48
- package/src/server/chat-native.ts +37 -15
- package/src/server/hub-usage.ts +57 -0
- package/src/server/images.ts +4 -0
- package/src/server/index.ts +722 -57
- package/src/server/lifecycle.ts +5 -6
- package/src/server/live-call-bindings.ts +60 -0
- package/src/server/live.ts +12 -1
- package/src/server/management/agent-settings-routes.ts +25 -4
- package/src/server/management/api-access.ts +37 -0
- package/src/server/management/api-key-usage.ts +7 -2
- package/src/server/management/config-routes.ts +1 -18
- package/src/server/management/context.ts +15 -0
- package/src/server/management/logs-usage-routes.ts +2 -0
- package/src/server/management/oauth-account-routes.ts +39 -12
- package/src/server/management/provider-routes.ts +125 -2
- package/src/server/management/remote-workspace-routes.ts +140 -0
- package/src/server/management/route-registry.ts +15 -0
- package/src/server/management/usage-aggregate-cache.ts +14 -15
- package/src/server/management/usage-summary-cache.ts +2 -0
- package/src/server/management-api.ts +23 -0
- package/src/server/ports.ts +17 -0
- package/src/server/relay-eager.ts +4 -1
- package/src/server/relay.ts +70 -10
- package/src/server/request-decompress.ts +6 -3
- package/src/server/responses/agent-task-recovery.ts +25 -32
- package/src/server/responses/codex-auth-error.ts +11 -0
- package/src/server/responses/codex-ws-exchange.ts +52 -3
- package/src/server/responses/codex-ws-wire.ts +55 -0
- package/src/server/responses/compact.ts +9 -1
- package/src/server/responses/core.ts +337 -73
- package/src/server/responses/encrypted-payload.ts +45 -2
- package/src/server/responses/ws-upstream.ts +4 -1
- package/src/server/responses-self-named-namespace-scrub.ts +1 -3
- package/src/server/responses-undeclared-tool-guard.ts +1 -1
- package/src/server/search.ts +3 -0
- package/src/server/sse-payload-rewrite.ts +136 -51
- package/src/server/ws-bridge.ts +35 -1
- package/src/service/cli.ts +372 -0
- package/src/service/diagnostics.ts +340 -0
- package/src/service/guards.ts +303 -0
- package/src/service/health.ts +222 -0
- package/src/service/launchd.ts +853 -0
- package/src/service/orchestration.ts +617 -0
- package/src/service/repair.ts +334 -0
- package/src/service/state.ts +363 -0
- package/src/service/systemd.ts +229 -0
- package/src/service/windows-ops.ts +690 -0
- package/src/service/windows-scheduler.ts +769 -0
- package/src/service/windows-taskxml.ts +613 -0
- package/src/service.ts +22 -5550
- package/src/storage/cleanup/db.ts +258 -0
- package/src/storage/cleanup/execute.ts +358 -0
- package/src/storage/cleanup/paths.ts +189 -0
- package/src/storage/cleanup/pending.ts +140 -0
- package/src/storage/cleanup/preview.ts +292 -0
- package/src/storage/cleanup/reconcile.ts +347 -0
- package/src/storage/cleanup/restore.ts +932 -0
- package/src/storage/cleanup/satellite.ts +474 -0
- package/src/storage/cleanup/staging.ts +129 -0
- package/src/storage/cleanup/types.ts +98 -0
- package/src/storage/cleanup.ts +49 -3127
- package/src/types/accounts.ts +2 -0
- package/src/types/config.ts +13 -12
- package/src/types/provider.ts +37 -0
- package/src/types/request.ts +2 -0
- package/src/types/tools.ts +17 -5
- package/src/types.ts +1 -0
- package/src/usage/expected-prices.ts +127 -0
- package/src/usage/log.ts +58 -1
- package/src/vision/eligibility.ts +13 -2
- package/src/web-search/loop.ts +56 -3
- package/gui/dist/assets/index-D_t6sCWs.js +0 -115
- package/gui/dist/assets/index-EdoPnm9_.css +0 -1
- package/src/adapters/devin-cli/acp.ts +0 -204
- package/src/adapters/devin-cli/adapter.ts +0 -345
- package/src/adapters/devin-cli/binary.ts +0 -69
- package/src/adapters/devin-cli/models.ts +0 -57
- package/src/oauth/devin-cli.ts +0 -149
- package/src/server/responses-reasoning-summary-rewrite.ts +0 -178
package/src/config.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { modelCapabilitiesConfigError, mergeModelCapabilities, sanitizeModelCapabilitiesForLoad } from "./config/provider-validation";
|
|
1
2
|
import { createHash } from "node:crypto";
|
|
2
3
|
import { chmodSync, constants as fsConstants, copyFileSync, existsSync, linkSync, lstatSync, mkdirSync, readFileSync, truncateSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
4
|
import { dirname, join } from "node:path";
|
|
4
5
|
import { Database } from "bun:sqlite";
|
|
5
6
|
import * as z from "zod/v4";
|
|
6
7
|
import { isValidProviderName, hasOwnProvider } from "./config/provider-name";
|
|
8
|
+
import { MULTI_AGENT_SURFACE_ADVISORY_VERSION } from "./config/multi-agent-surface";
|
|
7
9
|
import { DEFAULT_SUBAGENT_MODELS, SUBAGENT_MODELS_VERSION } from "./config/subagent-models";
|
|
8
10
|
export { DEFAULT_SUBAGENT_MODELS } from "./config/subagent-models";
|
|
9
11
|
import {
|
|
@@ -14,8 +16,11 @@ import {
|
|
|
14
16
|
pinnedReasoningEffortConfigError,
|
|
15
17
|
modelAdapterRecordConfigError,
|
|
16
18
|
modelDisplayNamesConfigError,
|
|
19
|
+
autoReviewModelOverridesConfigError,
|
|
20
|
+
autoReviewModelTargetConfigError,
|
|
17
21
|
nonBlankStringArrayConfigError,
|
|
18
22
|
normalizeNonBlankStringArray,
|
|
23
|
+
normalizeAutoReviewModelOverrides,
|
|
19
24
|
positiveIntegerConfigError,
|
|
20
25
|
positiveIntegerRecordConfigError,
|
|
21
26
|
providerBaseUrlConfigError,
|
|
@@ -573,17 +578,39 @@ const modelPinnedEffortsSchema = z.unknown().superRefine((value, ctx) => {
|
|
|
573
578
|
Object.entries(value as Record<string, string>).map(([key, effort]) => [key.trim(), effort]),
|
|
574
579
|
));
|
|
575
580
|
|
|
581
|
+
const autoReviewModelSchema = z.unknown().superRefine((value, ctx) => {
|
|
582
|
+
const error = autoReviewModelTargetConfigError(value, "autoReviewModel", true);
|
|
583
|
+
if (error) ctx.addIssue({ code: "custom", message: error });
|
|
584
|
+
}).transform(value => {
|
|
585
|
+
if (typeof value !== "string") return undefined;
|
|
586
|
+
const trimmed = value.trim();
|
|
587
|
+
return trimmed ? trimmed : undefined;
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
const autoReviewModelOverridesSchema = z.unknown().superRefine((value, ctx) => {
|
|
591
|
+
const error = autoReviewModelOverridesConfigError(value, "autoReviewModelOverrides", true);
|
|
592
|
+
if (error) ctx.addIssue({ code: "custom", message: error });
|
|
593
|
+
}).transform(value => normalizeAutoReviewModelOverrides(value));
|
|
594
|
+
|
|
595
|
+
const modelCapabilitiesSchema = z.unknown().superRefine((value, ctx) => {
|
|
596
|
+
const error = modelCapabilitiesConfigError(value);
|
|
597
|
+
if (error) ctx.addIssue({ code: "custom", message: error });
|
|
598
|
+
}).transform(value => mergeModelCapabilities(undefined, value));
|
|
599
|
+
|
|
576
600
|
/**
|
|
577
601
|
* Zod schema for one provider entry: known fields are validated strictly while unknown
|
|
578
602
|
* fields pass through (preserved for runtime extensions).
|
|
579
603
|
*/
|
|
580
604
|
const providerConfigSchema = z.object({
|
|
605
|
+
modelCapabilities: modelCapabilitiesSchema.optional(),
|
|
581
606
|
pinnedReasoningEffort: pinnedReasoningEffortSchema.optional(),
|
|
582
607
|
modelPinnedReasoningEfforts: modelPinnedEffortsSchema.optional(),
|
|
583
608
|
// Validated rather than left to passthrough: an unrecognized strategy would otherwise
|
|
584
609
|
// load silently and then be ignored at selection time, which reads as a broken feature
|
|
585
610
|
// rather than a rejected setting.
|
|
586
611
|
apiKeyPoolStrategy: z.enum(["round-robin", "fill-first", "quota"]).optional(),
|
|
612
|
+
autoReviewModel: autoReviewModelSchema.optional(),
|
|
613
|
+
autoReviewModelOverrides: autoReviewModelOverridesSchema.optional(),
|
|
587
614
|
adapter: z.string().min(1),
|
|
588
615
|
baseUrl: z.string().min(1),
|
|
589
616
|
alias: z.string().optional(),
|
|
@@ -665,9 +692,12 @@ export {
|
|
|
665
692
|
apiKeyTransportConfigError,
|
|
666
693
|
booleanRecordConfigError,
|
|
667
694
|
modelAdapterRecordConfigError,
|
|
695
|
+
autoReviewModelOverridesConfigError,
|
|
696
|
+
autoReviewModelTargetConfigError,
|
|
668
697
|
modelDisplayNamesConfigError,
|
|
669
698
|
nonBlankStringArrayConfigError,
|
|
670
699
|
normalizeNonBlankStringArray,
|
|
700
|
+
normalizeAutoReviewModelOverrides,
|
|
671
701
|
positiveIntegerConfigError,
|
|
672
702
|
positiveIntegerRecordConfigError,
|
|
673
703
|
providerBaseUrlConfigError,
|
|
@@ -1041,6 +1071,11 @@ function canonicalHttpOrigin(value: string): string | null {
|
|
|
1041
1071
|
}
|
|
1042
1072
|
}
|
|
1043
1073
|
|
|
1074
|
+
const managementIngressSchema = z.union([
|
|
1075
|
+
z.object({ enabled: z.literal(false) }).strict(),
|
|
1076
|
+
z.object({ enabled: z.literal(true), port: z.number().int().min(1).max(65535) }).strict(),
|
|
1077
|
+
]);
|
|
1078
|
+
|
|
1044
1079
|
const hubConfigSchema = z.object({
|
|
1045
1080
|
managementPublicOrigin: z.string().transform((value, ctx) => {
|
|
1046
1081
|
const origin = canonicalHttpOrigin(value);
|
|
@@ -1064,10 +1099,7 @@ const hubConfigSchema = z.object({
|
|
|
1064
1099
|
}).optional(),
|
|
1065
1100
|
// A malformed hand edit disables only the optional ingress. Live writes are rejected by
|
|
1066
1101
|
// managementIngressConfigError before this load-time degradation can hide the mistake.
|
|
1067
|
-
managementIngress:
|
|
1068
|
-
z.object({ enabled: z.literal(false) }).strict(),
|
|
1069
|
-
z.object({ enabled: z.literal(true), port: z.number().int().min(1).max(65535) }).strict(),
|
|
1070
|
-
]).optional().catch(undefined),
|
|
1102
|
+
managementIngress: managementIngressSchema.optional().catch(undefined),
|
|
1071
1103
|
}).strict();
|
|
1072
1104
|
|
|
1073
1105
|
const tailscaleUserSchema = z.string().trim().min(1).superRefine((value, ctx) => {
|
|
@@ -1253,6 +1285,8 @@ const configSchema = z.object({
|
|
|
1253
1285
|
configRebaseProvenance: z.unknown().optional(),
|
|
1254
1286
|
// A retry can be billable, so absence and malformed hand edits both stay off.
|
|
1255
1287
|
emptyCompletionRetry: z.boolean().optional().catch(false),
|
|
1288
|
+
// Header suppression changes what Codex sees, so absence and malformed edits stay off.
|
|
1289
|
+
dropCodexSafetyBuffering: z.boolean().optional().catch(false),
|
|
1256
1290
|
// A malformed hand edit must not silently stop opening the browser: fall back
|
|
1257
1291
|
// to undefined, which resolves to the historical auto-open behavior.
|
|
1258
1292
|
oauthOpenBrowser: z.boolean().optional().catch(undefined),
|
|
@@ -1261,6 +1295,9 @@ const configSchema = z.object({
|
|
|
1261
1295
|
googleAntigravityStaticCatalogVersion: z.union([z.literal(1), z.literal(2)]).optional().catch(undefined),
|
|
1262
1296
|
subagentModelsVersion: z.number().int().positive().optional().catch(undefined),
|
|
1263
1297
|
subagentModels: z.array(z.string().min(1)).optional().catch(undefined),
|
|
1298
|
+
// A hand-edited advisory version must not cost the operator their providers; a bad
|
|
1299
|
+
// value degrades to undefined, which simply raises the notice again.
|
|
1300
|
+
multiAgentSurfaceAdvisoryVersion: z.number().int().nonnegative().optional().catch(undefined),
|
|
1264
1301
|
clientIntegrations: clientIntegrationsSchema.optional().catch(undefined),
|
|
1265
1302
|
// A malformed profile policy must not fall back to legacy all-profile activation.
|
|
1266
1303
|
asideProfileSync: asideProfileSyncSchema.optional().catch({ allProfiles: false }),
|
|
@@ -1269,6 +1306,7 @@ const configSchema = z.object({
|
|
|
1269
1306
|
contextCapValue: z.number().int().positive().optional(),
|
|
1270
1307
|
multiAgentGuidanceEnabled: z.boolean().optional(),
|
|
1271
1308
|
// Invalid optional recovery config must not discard unrelated provider/account state.
|
|
1309
|
+
plaintextV2AgentMessages: z.boolean().optional().catch(undefined),
|
|
1272
1310
|
agentTaskRecovery: agentTaskRecoverySchema.optional().catch(undefined),
|
|
1273
1311
|
// Same rationale: a bad notify section must not cost the operator their providers.
|
|
1274
1312
|
quotaResetNotify: quotaResetNotifySchema.optional().catch(undefined),
|
|
@@ -1304,9 +1342,6 @@ const configSchema = z.object({
|
|
|
1304
1342
|
// A malformed hand edit must degrade to false without discarding providers, accounts,
|
|
1305
1343
|
// or the exact selector map. Live writes remain strict.
|
|
1306
1344
|
codexAccountPickerEnabled: z.boolean().optional().catch(false),
|
|
1307
|
-
// Same degrade-not-reject rule: a malformed hand edit hides Spark rather than discarding the
|
|
1308
|
-
// whole config. Hidden is also the default, so `catch(false)` and the default agree.
|
|
1309
|
-
showCodexSparkQuota: z.boolean().optional().catch(false),
|
|
1310
1345
|
resetCreditAutoRedeem: z.object({
|
|
1311
1346
|
enabled: z.boolean().optional(),
|
|
1312
1347
|
leadTimeMinutes: z.number().int().min(1).max(60).optional(),
|
|
@@ -1898,6 +1933,23 @@ export function retryOn429PolicyConfigError(policy: unknown): string | null {
|
|
|
1898
1933
|
return `retryOn429.${field} is invalid (${first.message})`;
|
|
1899
1934
|
}
|
|
1900
1935
|
|
|
1936
|
+
function sanitizeCapabilityDeclarationsForLoad(parsed: unknown): void {
|
|
1937
|
+
if (!parsed || typeof parsed !== "object") return;
|
|
1938
|
+
const providers = (parsed as Record<string, unknown>).providers;
|
|
1939
|
+
if (!providers || typeof providers !== "object" || Array.isArray(providers)) return;
|
|
1940
|
+
for (const [name, value] of Object.entries(providers)) {
|
|
1941
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
1942
|
+
const provider = value as Record<string, unknown>;
|
|
1943
|
+
if (provider.modelCapabilities === undefined) continue;
|
|
1944
|
+
if (modelCapabilitiesConfigError(provider.modelCapabilities) !== null) {
|
|
1945
|
+
console.warn(`config.json provider ${JSON.stringify(redactSecretString(name))} has malformed modelCapabilities; retaining valid axes and restricting malformed input modalities to text`);
|
|
1946
|
+
const repaired = sanitizeModelCapabilitiesForLoad(provider.modelCapabilities);
|
|
1947
|
+
if (repaired) provider.modelCapabilities = repaired;
|
|
1948
|
+
else delete provider.modelCapabilities;
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1901
1953
|
/**
|
|
1902
1954
|
* Load-time degradation for `providers.<name>.modelCosts`, mirroring
|
|
1903
1955
|
* {@link sanitizeRetryOn429ForLoad}. A hand-edited malformed display-price row
|
|
@@ -1946,6 +1998,44 @@ function sanitizeModelCostsForLoad(parsed: unknown): void {
|
|
|
1946
1998
|
}
|
|
1947
1999
|
}
|
|
1948
2000
|
|
|
2001
|
+
/**
|
|
2002
|
+
* Load-time degradation for provider-scoped auto-review selectors. A malformed
|
|
2003
|
+
* hand edit must not fail the whole config parse; the management boundary stays
|
|
2004
|
+
* strict and rejects the same shapes before they can be written.
|
|
2005
|
+
*/
|
|
2006
|
+
function sanitizeAutoReviewForLoad(parsed: unknown): void {
|
|
2007
|
+
if (!parsed || typeof parsed !== "object") return;
|
|
2008
|
+
const root = parsed as Record<string, unknown>;
|
|
2009
|
+
const providers = root.providers;
|
|
2010
|
+
if (!providers || typeof providers !== "object" || Array.isArray(providers)) return;
|
|
2011
|
+
for (const [name, providerValue] of Object.entries(providers as Record<string, unknown>)) {
|
|
2012
|
+
if (!providerValue || typeof providerValue !== "object" || Array.isArray(providerValue)) continue;
|
|
2013
|
+
const provider = providerValue as Record<string, unknown>;
|
|
2014
|
+
const safeProviderName = JSON.stringify(redactSecretString(name));
|
|
2015
|
+
if (name === "openai") {
|
|
2016
|
+
delete provider.autoReviewModel;
|
|
2017
|
+
delete provider.autoReviewModelOverrides;
|
|
2018
|
+
continue;
|
|
2019
|
+
}
|
|
2020
|
+
if (provider.autoReviewModel !== undefined
|
|
2021
|
+
&& autoReviewModelTargetConfigError(provider.autoReviewModel, "autoReviewModel", true) !== null) {
|
|
2022
|
+
console.warn(`⚠️ config.json providers.${safeProviderName}.autoReviewModel is invalid — ignoring the selector`);
|
|
2023
|
+
delete provider.autoReviewModel;
|
|
2024
|
+
}
|
|
2025
|
+
if (provider.autoReviewModelOverrides !== undefined) {
|
|
2026
|
+
const overridesError = autoReviewModelOverridesConfigError(
|
|
2027
|
+
provider.autoReviewModelOverrides,
|
|
2028
|
+
"autoReviewModelOverrides",
|
|
2029
|
+
true,
|
|
2030
|
+
);
|
|
2031
|
+
if (overridesError) {
|
|
2032
|
+
console.warn(`⚠️ config.json providers.${safeProviderName}.autoReviewModelOverrides is invalid — ignoring the map`);
|
|
2033
|
+
delete provider.autoReviewModelOverrides;
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
|
|
1949
2039
|
/**
|
|
1950
2040
|
* Companion to {@link warnDegradedStreamMode} for a blank persisted `hostname`. The bind
|
|
1951
2041
|
* falls back to loopback, which is the safe direction but not what the file asked for —
|
|
@@ -1959,6 +2049,26 @@ function warnDegradedHostname(rawParsed: unknown, validated: OcxConfig): void {
|
|
|
1959
2049
|
}
|
|
1960
2050
|
}
|
|
1961
2051
|
|
|
2052
|
+
function degradedListenerWarnings(rawParsed: unknown, validated: OcxConfig): string[] {
|
|
2053
|
+
const raw = rawConfigRecord(rawParsed);
|
|
2054
|
+
if (!raw) return [];
|
|
2055
|
+
const warnings: string[] = [];
|
|
2056
|
+
if (raw.unauthenticatedLoopbackListener !== undefined && validated.unauthenticatedLoopbackListener === undefined) {
|
|
2057
|
+
warnings.push("unauthenticatedLoopbackListener ignored: invalid listener configuration; repair config.json before enabling the listener");
|
|
2058
|
+
}
|
|
2059
|
+
const hub = rawConfigRecord(raw.hub);
|
|
2060
|
+
if (hub?.managementIngress !== undefined && !managementIngressSchema.safeParse(hub.managementIngress).success) {
|
|
2061
|
+
warnings.push("hub.managementIngress ignored: invalid management listener configuration; repair config.json before enabling the listener");
|
|
2062
|
+
}
|
|
2063
|
+
return warnings;
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
function warnDegradedListeners(rawParsed: unknown, validated: OcxConfig): void {
|
|
2067
|
+
for (const warning of degradedListenerWarnings(rawParsed, validated)) {
|
|
2068
|
+
console.warn(`⚠️ config.json ${warning}. Other settings were preserved.`);
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
|
|
1962
2072
|
/**
|
|
1963
2073
|
* Companion to {@link warnDegradedStreamMode} for a malformed selection-order map.
|
|
1964
2074
|
* Priority is a preference, so the schema drops the whole map rather than failing
|
|
@@ -2124,6 +2234,13 @@ function normalizePersistedClaudeCode(claudeCode: unknown): OcxConfig["claudeCod
|
|
|
2124
2234
|
if (kept.length > 0) normalized.classifierFallbacks = kept;
|
|
2125
2235
|
else delete normalized.classifierFallbacks;
|
|
2126
2236
|
}
|
|
2237
|
+
const desktopProfile = normalized.desktopProfile;
|
|
2238
|
+
if (desktopProfile && typeof desktopProfile === "object" && !Array.isArray(desktopProfile)) {
|
|
2239
|
+
const profile = { ...desktopProfile } as Record<string, unknown>;
|
|
2240
|
+
if (typeof profile.appliedFingerprint !== "string") delete profile.appliedFingerprint;
|
|
2241
|
+
if (typeof profile.appliedAt !== "string") delete profile.appliedAt;
|
|
2242
|
+
normalized.desktopProfile = profile;
|
|
2243
|
+
}
|
|
2127
2244
|
return normalized as OcxConfig["claudeCode"];
|
|
2128
2245
|
}
|
|
2129
2246
|
|
|
@@ -2159,6 +2276,17 @@ function warnDegradedUpstreamHostCircuitThreshold(rawParsed: unknown): void {
|
|
|
2159
2276
|
if (warning) console.warn(`⚠️ config.json ${warning}. Other settings were preserved.`);
|
|
2160
2277
|
}
|
|
2161
2278
|
|
|
2279
|
+
function malformedPlaintextV2AgentMessagesWarning(value: unknown): string | null {
|
|
2280
|
+
const raw = rawConfigRecord(value);
|
|
2281
|
+
if (!raw || raw.plaintextV2AgentMessages === undefined || typeof raw.plaintextV2AgentMessages === "boolean") return null;
|
|
2282
|
+
return "plaintextV2AgentMessages ignored: expected a boolean";
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
function warnDegradedPlaintextV2AgentMessages(value: unknown): void {
|
|
2286
|
+
const warning = malformedPlaintextV2AgentMessagesWarning(value);
|
|
2287
|
+
if (warning) console.warn(`⚠️ config.json ${warning}. Other settings were preserved.`);
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2162
2290
|
function malformedAgentTaskRecoveryWarning(rawParsed: unknown): string | null {
|
|
2163
2291
|
const raw = rawConfigRecord(rawParsed);
|
|
2164
2292
|
if (!raw || !Object.hasOwn(raw, "agentTaskRecovery")) return null;
|
|
@@ -2401,14 +2529,17 @@ export function loadConfig(): OcxConfig {
|
|
|
2401
2529
|
sanitizeAliasesForLoad(parsed);
|
|
2402
2530
|
sanitizeReasoningPinsForLoad(parsed);
|
|
2403
2531
|
sanitizeModelDisplayNamesForLoad(parsed);
|
|
2532
|
+
sanitizeAutoReviewForLoad(parsed);
|
|
2404
2533
|
sanitizeRetryOn429ForLoad(parsed);
|
|
2405
2534
|
sanitizeModelCostsForLoad(parsed);
|
|
2535
|
+
sanitizeCapabilityDeclarationsForLoad(parsed);
|
|
2406
2536
|
const result = configSchema.safeParse(parsed);
|
|
2407
2537
|
if (result.success) {
|
|
2408
2538
|
const config = normalizeApiKeyIds(result.data as OcxConfig);
|
|
2409
2539
|
warnInheritedFastWireConflicts(configPath, config);
|
|
2410
2540
|
warnDegradedStreamMode(parsed, config);
|
|
2411
2541
|
warnDegradedHostname(parsed, config);
|
|
2542
|
+
warnDegradedListeners(parsed, config);
|
|
2412
2543
|
warnDegradedApiKeys(parsed, config);
|
|
2413
2544
|
warnDegradedCodexAccountPriorities(parsed, config);
|
|
2414
2545
|
warnDegradedCodexQuotaAutoRefresh(parsed, config);
|
|
@@ -2416,6 +2547,7 @@ export function loadConfig(): OcxConfig {
|
|
|
2416
2547
|
warnDegradedNativeSubagentConfig(parsed, config);
|
|
2417
2548
|
warnDegradedCodexAccountPicker(parsed);
|
|
2418
2549
|
warnDegradedUpstreamHostCircuitThreshold(parsed);
|
|
2550
|
+
warnDegradedPlaintextV2AgentMessages(parsed);
|
|
2419
2551
|
warnDegradedAgentTaskRecovery(parsed);
|
|
2420
2552
|
warnDegradedRuntimeRole(parsed);
|
|
2421
2553
|
warnDegradedOptionalRemoteBlocks(parsed);
|
|
@@ -2427,7 +2559,18 @@ export function loadConfig(): OcxConfig {
|
|
|
2427
2559
|
// discarding it entirely, so pool accounts and providers survive a missing
|
|
2428
2560
|
// field like defaultProvider.
|
|
2429
2561
|
const defaults = getDefaultConfig();
|
|
2430
|
-
|
|
2562
|
+
// Pin the keys whose ABSENCE is meaningful. Spreading defaults underneath means any
|
|
2563
|
+
// key the stored document lacks is inherited, which is right for additive defaults and
|
|
2564
|
+
// wrong for a behavioral mode: a config that reaches this path only because it lost
|
|
2565
|
+
// `defaultProvider` would be repaired into v1 sub-agents and a pre-answered advisory,
|
|
2566
|
+
// silently changing a setting its operator never touched.
|
|
2567
|
+
const merged = {
|
|
2568
|
+
...defaults,
|
|
2569
|
+
...parsed,
|
|
2570
|
+
subagentModelsVersion: parsed.subagentModelsVersion,
|
|
2571
|
+
multiAgentMode: parsed.multiAgentMode,
|
|
2572
|
+
multiAgentSurfaceAdvisoryVersion: parsed.multiAgentSurfaceAdvisoryVersion,
|
|
2573
|
+
};
|
|
2431
2574
|
// Ensure providers from both sides survive
|
|
2432
2575
|
if (parsed.providers && defaults.providers) {
|
|
2433
2576
|
merged.providers = { ...defaults.providers, ...parsed.providers };
|
|
@@ -2438,6 +2581,7 @@ export function loadConfig(): OcxConfig {
|
|
|
2438
2581
|
const config = normalizeApiKeyIds(retryResult.data as OcxConfig);
|
|
2439
2582
|
warnInheritedFastWireConflicts(configPath, config);
|
|
2440
2583
|
warnDegradedHostname(parsed, config);
|
|
2584
|
+
warnDegradedListeners(parsed, config);
|
|
2441
2585
|
warnDegradedApiKeys(parsed, config);
|
|
2442
2586
|
warnDegradedCodexAccountPriorities(parsed, config);
|
|
2443
2587
|
warnDegradedCodexQuotaAutoRefresh(parsed, config);
|
|
@@ -2445,6 +2589,7 @@ export function loadConfig(): OcxConfig {
|
|
|
2445
2589
|
warnDegradedNativeSubagentConfig(parsed, config);
|
|
2446
2590
|
warnDegradedCodexAccountPicker(parsed);
|
|
2447
2591
|
warnDegradedUpstreamHostCircuitThreshold(parsed);
|
|
2592
|
+
warnDegradedPlaintextV2AgentMessages(parsed);
|
|
2448
2593
|
warnDegradedAgentTaskRecovery(parsed);
|
|
2449
2594
|
warnDegradedRuntimeRole(parsed);
|
|
2450
2595
|
warnDegradedOptionalRemoteBlocks(parsed);
|
|
@@ -2463,6 +2608,7 @@ export function loadConfig(): OcxConfig {
|
|
|
2463
2608
|
const config = normalizeApiKeyIds(salvaged.parsed);
|
|
2464
2609
|
warnInheritedFastWireConflicts(configPath, config);
|
|
2465
2610
|
warnDegradedHostname(parsed, config);
|
|
2611
|
+
warnDegradedListeners(parsed, config);
|
|
2466
2612
|
warnDegradedApiKeys(parsed, config);
|
|
2467
2613
|
warnDegradedCodexAccountPriorities(parsed, config);
|
|
2468
2614
|
warnDegradedCodexQuotaAutoRefresh(parsed, config);
|
|
@@ -2470,6 +2616,7 @@ export function loadConfig(): OcxConfig {
|
|
|
2470
2616
|
warnDegradedNativeSubagentConfig(parsed, config);
|
|
2471
2617
|
warnDegradedCodexAccountPicker(parsed);
|
|
2472
2618
|
warnDegradedUpstreamHostCircuitThreshold(parsed);
|
|
2619
|
+
warnDegradedPlaintextV2AgentMessages(parsed);
|
|
2473
2620
|
warnDegradedAgentTaskRecovery(parsed);
|
|
2474
2621
|
warnDegradedRuntimeRole(parsed);
|
|
2475
2622
|
warnDegradedOptionalRemoteBlocks(parsed);
|
|
@@ -2597,6 +2744,7 @@ function validFileConfigDiagnostics(config: OcxConfig, rawParsed: unknown): Conf
|
|
|
2597
2744
|
const warnings = configPlaceholderWarnings(normalized);
|
|
2598
2745
|
warnings.push(...inheritedFastWireConflictProviderNames(normalized).map(inheritedFastWireConflictWarning));
|
|
2599
2746
|
warnings.push(...degradedCodexAccountPriorityWarnings(rawParsed, normalized));
|
|
2747
|
+
warnings.push(...degradedListenerWarnings(rawParsed, normalized));
|
|
2600
2748
|
const quotaAutoRefreshWarning = degradedCodexQuotaAutoRefreshWarning(rawParsed, normalized);
|
|
2601
2749
|
if (quotaAutoRefreshWarning) warnings.push(quotaAutoRefreshWarning);
|
|
2602
2750
|
if (rawEffort !== undefined && !isClaudeSubagentEffort(rawEffort)) {
|
|
@@ -2621,6 +2769,8 @@ function validFileConfigDiagnostics(config: OcxConfig, rawParsed: unknown): Conf
|
|
|
2621
2769
|
if (notifyWarning) warnings.push(notifyWarning);
|
|
2622
2770
|
const codexPoolWarning = malformedCodexPoolWarning(rawParsed);
|
|
2623
2771
|
if (codexPoolWarning) warnings.push(codexPoolWarning);
|
|
2772
|
+
const plaintextWarning = malformedPlaintextV2AgentMessagesWarning(rawParsed);
|
|
2773
|
+
if (plaintextWarning) warnings.push(plaintextWarning);
|
|
2624
2774
|
if (syncDisabledReason) {
|
|
2625
2775
|
warnings.push(`syncCodexSubagentDefaults ignored: ${syncDisabledReason}`);
|
|
2626
2776
|
}
|
|
@@ -2642,7 +2792,14 @@ function mergeConfigDefaults(parsed: unknown): unknown {
|
|
|
2642
2792
|
if (!parsed || typeof parsed !== "object") return parsed;
|
|
2643
2793
|
const defaults = getDefaultConfig();
|
|
2644
2794
|
const raw = parsed as Record<string, unknown>;
|
|
2645
|
-
|
|
2795
|
+
// Same absence-is-meaningful pin as the repair merge above.
|
|
2796
|
+
const merged: Record<string, unknown> = {
|
|
2797
|
+
...defaults,
|
|
2798
|
+
...raw,
|
|
2799
|
+
subagentModelsVersion: raw.subagentModelsVersion,
|
|
2800
|
+
multiAgentMode: raw.multiAgentMode,
|
|
2801
|
+
multiAgentSurfaceAdvisoryVersion: raw.multiAgentSurfaceAdvisoryVersion,
|
|
2802
|
+
};
|
|
2646
2803
|
if (raw.providers && typeof raw.providers === "object" && defaults.providers) {
|
|
2647
2804
|
merged.providers = { ...defaults.providers, ...(raw.providers as Record<string, unknown>) };
|
|
2648
2805
|
}
|
|
@@ -2702,6 +2859,12 @@ function upstreamHostCircuitThresholdError(value: unknown): string | null {
|
|
|
2702
2859
|
return `schema_invalid: upstreamHostCircuitThreshold: must be an integer from 0 to ${UPSTREAM_HOST_CIRCUIT_MAX_THRESHOLD}`;
|
|
2703
2860
|
}
|
|
2704
2861
|
|
|
2862
|
+
function plaintextV2AgentMessagesError(value: unknown): string | null {
|
|
2863
|
+
return malformedPlaintextV2AgentMessagesWarning(value)
|
|
2864
|
+
? "schema_invalid: plaintextV2AgentMessages: must be a boolean or omitted"
|
|
2865
|
+
: null;
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2705
2868
|
function agentTaskRecoveryError(value: unknown): string | null {
|
|
2706
2869
|
const raw = rawConfigRecord(value);
|
|
2707
2870
|
if (!raw || !Object.hasOwn(raw, "agentTaskRecovery") || raw.agentTaskRecovery === undefined) return null;
|
|
@@ -2858,6 +3021,14 @@ function emptyCompletionRetryError(value: unknown): string | null {
|
|
|
2858
3021
|
return "schema_invalid: emptyCompletionRetry: must be a boolean or omitted";
|
|
2859
3022
|
}
|
|
2860
3023
|
|
|
3024
|
+
function dropCodexSafetyBufferingError(value: unknown): string | null {
|
|
3025
|
+
const raw = rawConfigRecord(value);
|
|
3026
|
+
if (!raw || !Object.hasOwn(raw, "dropCodexSafetyBuffering")) return null;
|
|
3027
|
+
const enabled = raw.dropCodexSafetyBuffering;
|
|
3028
|
+
if (enabled === undefined || typeof enabled === "boolean") return null;
|
|
3029
|
+
return "schema_invalid: dropCodexSafetyBuffering: must be a boolean or omitted";
|
|
3030
|
+
}
|
|
3031
|
+
|
|
2861
3032
|
function oauthOpenBrowserError(value: unknown): string | null {
|
|
2862
3033
|
const raw = rawConfigRecord(value);
|
|
2863
3034
|
if (!raw || !Object.hasOwn(raw, "oauthOpenBrowser")) return null;
|
|
@@ -2993,6 +3164,7 @@ export function validateConfigCandidate(value: unknown): { ok: true; config: Ocx
|
|
|
2993
3164
|
?? claudeSubagentEffortError(value)
|
|
2994
3165
|
?? appOwnedMemoryBudgetError(value)
|
|
2995
3166
|
?? upstreamHostCircuitThresholdError(value)
|
|
3167
|
+
?? plaintextV2AgentMessagesError(value)
|
|
2996
3168
|
?? agentTaskRecoveryError(value)
|
|
2997
3169
|
?? quotaResetNotifyError(value)
|
|
2998
3170
|
?? codexPoolError(value)
|
|
@@ -3001,6 +3173,7 @@ export function validateConfigCandidate(value: unknown): { ok: true; config: Ocx
|
|
|
3001
3173
|
?? codexQuotaAutoRefreshError(value)
|
|
3002
3174
|
?? codexAccountPickerEnabledError(value)
|
|
3003
3175
|
?? emptyCompletionRetryError(value)
|
|
3176
|
+
?? dropCodexSafetyBufferingError(value)
|
|
3004
3177
|
?? oauthOpenBrowserError(value)
|
|
3005
3178
|
?? runtimeRoleError(value)
|
|
3006
3179
|
?? remoteGuiConfigError(value)
|
|
@@ -3025,8 +3198,10 @@ function configDiagnosticsFromRaw(raw: string): ConfigDiagnostics {
|
|
|
3025
3198
|
// schema and send the caller a default-config fallback (the config command could then
|
|
3026
3199
|
// persist that fallback over the user's providers/keys).
|
|
3027
3200
|
sanitizeModelDisplayNamesForLoad(parsed);
|
|
3201
|
+
sanitizeAutoReviewForLoad(parsed);
|
|
3028
3202
|
sanitizeRetryOn429ForLoad(parsed);
|
|
3029
3203
|
sanitizeModelCostsForLoad(parsed);
|
|
3204
|
+
sanitizeCapabilityDeclarationsForLoad(parsed);
|
|
3030
3205
|
const result = configSchema.safeParse(parsed);
|
|
3031
3206
|
if (result.success) {
|
|
3032
3207
|
return validFileConfigDiagnostics(normalizeApiKeyIds(result.data as OcxConfig), parsed);
|
|
@@ -3050,10 +3225,13 @@ function configDiagnosticsFromRaw(raw: string): ConfigDiagnostics {
|
|
|
3050
3225
|
// that ignores the error and writes it back preserves what the operator configured.
|
|
3051
3226
|
const salvaged = salvageConfigCandidate(merged, retryResult.error);
|
|
3052
3227
|
if (salvaged) {
|
|
3228
|
+
const config = normalizeApiKeyIds(salvaged.parsed);
|
|
3229
|
+
const warnings = degradedListenerWarnings(parsed, config);
|
|
3053
3230
|
return {
|
|
3054
|
-
config
|
|
3231
|
+
config,
|
|
3055
3232
|
source: "fallback",
|
|
3056
3233
|
error: schemaDiagnosticsError(result.error),
|
|
3234
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
3057
3235
|
};
|
|
3058
3236
|
}
|
|
3059
3237
|
|
|
@@ -4022,6 +4200,7 @@ export function getDefaultConfig(): OcxConfig {
|
|
|
4022
4200
|
return {
|
|
4023
4201
|
port: 10100,
|
|
4024
4202
|
emptyCompletionRetry: false,
|
|
4203
|
+
dropCodexSafetyBuffering: false,
|
|
4025
4204
|
fastRows: true,
|
|
4026
4205
|
managementUsageMaxReadBytes: 64 * 1024 * 1024,
|
|
4027
4206
|
appOwnedMemoryBudgetMb: DEFAULT_APP_OWNED_MEMORY_BUDGET_BYTES / (1024 * 1024),
|
|
@@ -4040,6 +4219,12 @@ export function getDefaultConfig(): OcxConfig {
|
|
|
4040
4219
|
defaultProvider: "openai",
|
|
4041
4220
|
subagentModels: [...DEFAULT_SUBAGENT_MODELS],
|
|
4042
4221
|
subagentModelsVersion: SUBAGENT_MODELS_VERSION,
|
|
4222
|
+
// v1 is the shipped surface while a v2 native-to-routed task is undeliverable
|
|
4223
|
+
// ciphertext. Written explicitly rather than left absent, because an absent key
|
|
4224
|
+
// means base everywhere else. A fresh install starts already acknowledged: there is
|
|
4225
|
+
// nothing to advise an operator who is on the recommended surface.
|
|
4226
|
+
multiAgentMode: "v1",
|
|
4227
|
+
multiAgentSurfaceAdvisoryVersion: MULTI_AGENT_SURFACE_ADVISORY_VERSION,
|
|
4043
4228
|
multiAgentGuidanceEnabled: true,
|
|
4044
4229
|
websockets: false,
|
|
4045
4230
|
codexAutoStart: true,
|
|
@@ -4185,6 +4370,16 @@ function warnConfigRepaired(configPath: string, error: z.ZodError): void {
|
|
|
4185
4370
|
*/
|
|
4186
4371
|
const SALVAGEABLE_CONFIG_SECTIONS = ["routingProfiles", "combos"] as const;
|
|
4187
4372
|
|
|
4373
|
+
/** Optional nested fields that can be dropped whole without changing the rest of the document. */
|
|
4374
|
+
const SALVAGEABLE_OPTIONAL_FIELDS: ReadonlyArray<readonly [string, string]> = [
|
|
4375
|
+
["claudeCode", "desktopProfile"],
|
|
4376
|
+
];
|
|
4377
|
+
|
|
4378
|
+
function isSalvageableConfigPath(section: string, id: string): boolean {
|
|
4379
|
+
if ((SALVAGEABLE_CONFIG_SECTIONS as readonly string[]).includes(section)) return true;
|
|
4380
|
+
return SALVAGEABLE_OPTIONAL_FIELDS.some(path => path[0] === section && path[1] === id);
|
|
4381
|
+
}
|
|
4382
|
+
|
|
4188
4383
|
/**
|
|
4189
4384
|
* Drop just the named entries a parse failure blamed, so the rest of the
|
|
4190
4385
|
* document survives.
|
|
@@ -4210,7 +4405,7 @@ function dropInvalidConfigSections(
|
|
|
4210
4405
|
if (isUnsalvageableIssue(issue)) return null;
|
|
4211
4406
|
const [section, id] = issue.path;
|
|
4212
4407
|
if (typeof section !== "string" || typeof id !== "string") return null;
|
|
4213
|
-
if (!(
|
|
4408
|
+
if (!isSalvageableConfigPath(section, id)) return null;
|
|
4214
4409
|
// A complaint about the container itself ("combos must be an object") is
|
|
4215
4410
|
// not about one entry, so there is nothing selective to drop.
|
|
4216
4411
|
if (issue.path.length < 2) return null;
|
|
@@ -4310,6 +4505,13 @@ function countSalvageableEntries(document: unknown): number {
|
|
|
4310
4505
|
total += Object.keys(value as Record<string, unknown>).length;
|
|
4311
4506
|
}
|
|
4312
4507
|
}
|
|
4508
|
+
for (const [section, id] of SALVAGEABLE_OPTIONAL_FIELDS) {
|
|
4509
|
+
const container = (document as Record<string, unknown>)[section];
|
|
4510
|
+
if (container && typeof container === "object" && !Array.isArray(container)
|
|
4511
|
+
&& Object.hasOwn(container as Record<string, unknown>, id)) {
|
|
4512
|
+
total += 1;
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4313
4515
|
return total;
|
|
4314
4516
|
}
|
|
4315
4517
|
|