@bitkyc08/opencodex 2.60.0 → 2.61.0-preview.20260922
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/AGENTS_INSTALL.md +64 -0
- package/README.md +28 -1
- package/bin/ocx.mjs +382 -209
- package/gui/dist/assets/App-E64Rzjap.js +50 -0
- package/gui/dist/assets/Tray-_nfzD8k4.js +1 -0
- package/gui/dist/assets/index-DpdfZWMK.js +86 -0
- package/gui/dist/assets/index-_bpvxJu0.css +1 -0
- package/gui/dist/assets/usage-companion-chart-DtoK7T6h.js +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/stepfun-color.svg +1 -0
- package/package.json +5 -1
- package/src/adapters/anthropic.ts +16 -0
- package/src/adapters/coding-agent/protocol.ts +36 -6
- package/src/adapters/coding-agent/turn.ts +10 -2
- package/src/adapters/command-code.ts +2 -1
- package/src/adapters/cursor/catalog.ts +51 -7
- package/src/adapters/cursor/protobuf-request.ts +6 -3
- package/src/adapters/cursor/request-builder.ts +13 -3
- package/src/adapters/cursor.ts +11 -2
- package/src/adapters/declaration-carrier.ts +45 -0
- package/src/adapters/devin.ts +75 -23
- package/src/adapters/google-antigravity-wire.ts +5 -2
- package/src/adapters/google-errors.ts +7 -1
- package/src/adapters/google.ts +29 -5
- package/src/adapters/image.ts +4 -1
- package/src/adapters/input-media-guard.ts +21 -9
- package/src/adapters/kiro/usage.ts +3 -2
- package/src/adapters/kiro-tool-fallback.ts +1 -1
- package/src/adapters/ollama-native.ts +6 -0
- package/src/adapters/openai-chat/developer-role.ts +61 -0
- package/src/adapters/openai-chat/messages.ts +46 -27
- package/src/adapters/openai-chat/parallel-tool-calls.ts +32 -0
- package/src/adapters/openai-chat/passthrough.ts +33 -9
- package/src/adapters/openai-chat/reasoning-wire.ts +89 -0
- package/src/adapters/openai-chat.ts +18 -57
- package/src/adapters/openai-responses/passthrough.ts +2 -0
- package/src/adapters/registry.ts +3 -2
- package/src/adapters/run-turn-queue.ts +178 -29
- package/src/adapters/xai-web-search.ts +16 -1
- package/src/bridge/errors.ts +8 -2
- package/src/bridge/response-json.ts +9 -1
- package/src/bridge/sse.ts +10 -0
- package/src/chat/inbound.ts +141 -5
- package/src/claude/desktop-3p.ts +7 -1
- package/src/claude/desktop-first-party.ts +183 -0
- package/src/claude/desktop-gateway-state.ts +41 -0
- package/src/claude/inbound-content-options.ts +6 -0
- package/src/claude/inbound.ts +32 -6
- package/src/claude/intercept/connect-proxy.ts +179 -0
- package/src/claude/intercept/listener.ts +122 -0
- package/src/claude/intercept/local-ca.ts +298 -0
- package/src/claude/intercept/runtime.ts +98 -0
- package/src/claude/intercept/settings.ts +189 -0
- package/src/cli/access.ts +87 -0
- package/src/cli/account-auth.ts +19 -0
- package/src/cli/capabilities.ts +31 -0
- package/src/cli/claude-desktop.ts +206 -16
- package/src/cli/codex-shim-autorestore.ts +3 -0
- package/src/cli/companion.ts +56 -0
- package/src/cli/dispatch.ts +43 -4
- package/src/cli/ensure-desired-integrations.ts +43 -5
- package/src/cli/help.ts +7 -9
- package/src/cli/index.ts +200 -61
- package/src/cli/init.ts +8 -0
- package/src/cli/integrations.ts +7 -1
- package/src/cli/registry.ts +41 -2
- package/src/cli/resolve.ts +230 -0
- package/src/cli/root.ts +24 -1
- package/src/cli/start-ownership-publication.ts +56 -0
- package/src/cli/status-probes.ts +2 -18
- package/src/cli/status.ts +62 -0
- package/src/cli/stop-report.ts +143 -0
- package/src/cli/uninstall-plan.ts +9 -0
- package/src/client/machine-listener.ts +2 -5
- package/src/clients/aside-profiles.ts +4 -0
- package/src/clients/config-export/zcode-store.ts +157 -0
- package/src/clients/config-export.ts +36 -0
- package/src/codex/app-server-processes.ts +72 -40
- package/src/codex/auth-api/login-flow.ts +6 -1
- package/src/codex/autostart-health.ts +28 -0
- package/src/codex/catalog/build-entries.ts +2 -2
- package/src/codex/catalog/effort.ts +3 -3
- package/src/codex/catalog/provider-models.ts +24 -15
- package/src/codex/catalog/retained-sync.ts +2 -2
- package/src/codex/convergence.ts +2 -2
- package/src/codex/history-provider.ts +12 -1
- package/src/codex/inject/config-toml.ts +41 -6
- package/src/codex/inject/paginated-openai-compat.ts +90 -0
- package/src/codex/inject.ts +18 -15
- package/src/codex/injected-marker.ts +18 -0
- package/src/codex/main-account.ts +6 -0
- package/src/codex/model-cache.ts +52 -6
- package/src/codex/model-entitlement-admission.ts +59 -0
- package/src/codex/model-entitlements.ts +87 -44
- package/src/codex/native-main-admission.ts +83 -0
- package/src/codex/routing/health-store.ts +39 -0
- package/src/codex/routing/selection.ts +37 -1
- package/src/codex/routing.ts +5 -41
- package/src/codex/shim-templates.ts +29 -3
- package/src/companion/settings.ts +132 -0
- package/src/config/atomic-write.ts +117 -5
- package/src/config/load-degrade.ts +34 -7
- package/src/config/process-state.ts +1 -1
- package/src/config/schema/config-schema.ts +27 -1
- package/src/config/schema/leaf-validators.ts +47 -0
- package/src/config.ts +1 -1
- package/src/generated/compatibility-version.json +418 -174
- package/src/integrations/config-io.ts +44 -10
- package/src/integrations/merge.ts +120 -13
- package/src/integrations/mutation-plan.ts +124 -18
- package/src/integrations/registry.ts +38 -0
- package/src/integrations/state.ts +78 -45
- package/src/integrations/target.ts +208 -0
- package/src/integrations/writer.ts +49 -11
- package/src/lab/conformance/fixture-provider.ts +5 -0
- package/src/lib/browser-launch-notice.ts +59 -0
- package/src/lib/bun-runtime.ts +6 -2
- package/src/lib/debug.ts +40 -0
- package/src/lib/open-url.ts +51 -7
- package/src/lib/package-tree-integrity.ts +2 -1
- package/src/lib/package-version.ts +8 -0
- package/src/lib/provider-egress.ts +310 -0
- package/src/lib/provider-outbound.ts +59 -14
- package/src/lib/proxy-env.ts +82 -7
- package/src/lib/request-execution-budget.ts +72 -0
- package/src/lib/request-failure-attribution.ts +183 -0
- package/src/lib/request-failure-model.ts +236 -0
- package/src/lib/request-resend-gate.ts +138 -0
- package/src/lib/standalone.ts +16 -0
- package/src/lib/upstream-retry.ts +167 -16
- package/src/lib/winsw.ts +2 -2
- package/src/oauth/index.ts +24 -1
- package/src/oauth/login-cli.ts +80 -29
- package/src/providers/api-key-resolve.ts +133 -0
- package/src/providers/api-key-selection.ts +5 -1
- package/src/providers/key-failover.ts +31 -1
- package/src/providers/key-store.ts +34 -110
- package/src/providers/model-rename-fields.ts +147 -0
- package/src/providers/model-rename-migration.ts +124 -37
- package/src/providers/quota/vendor-probes-key.ts +37 -22
- package/src/providers/reasoning-metadata.ts +43 -18
- package/src/providers/registry/entries-core.ts +9 -4
- package/src/providers/registry/entries-extended.ts +29 -4
- package/src/providers/registry/model-seeds.ts +47 -10
- package/src/providers/xai-transport.ts +12 -1
- package/src/reasoning-effort.ts +8 -0
- package/src/responses/function-call-compat.ts +38 -1
- package/src/responses/inline-document.ts +65 -0
- package/src/responses/input-media.ts +42 -8
- package/src/responses/muse-tool-name-alias.ts +19 -0
- package/src/responses/parser-content.ts +8 -2
- package/src/responses/parser-tools.ts +3 -0
- package/src/responses/parser.ts +3 -1
- package/src/responses/schema.ts +3 -0
- package/src/router.ts +17 -2
- package/src/server/admission-model-scope.ts +219 -0
- package/src/server/audio-live.ts +9 -3
- package/src/server/audio-upstream.ts +18 -0
- package/src/server/auth-cors.ts +26 -0
- package/src/server/chat-completions.ts +55 -2
- package/src/server/chat-native.ts +19 -4
- package/src/server/claude-messages.ts +55 -17
- package/src/server/grok-responses-snapshot-repair.ts +113 -11
- package/src/server/gui-freshness.ts +103 -0
- package/src/server/gui-static.ts +7 -9
- package/src/server/images.ts +59 -6
- package/src/server/index/claude-intercept-lifecycle.ts +49 -0
- package/src/server/index/serve-options.ts +56 -10
- package/src/server/index/spend-ledger-lifecycle.ts +34 -8
- package/src/server/index/startup-warnings.ts +24 -0
- package/src/server/index.ts +21 -28
- package/src/server/lifecycle.ts +4 -4
- package/src/server/live-call-bindings.ts +6 -0
- package/src/server/live.ts +88 -3
- package/src/server/management/agent-settings-routes.ts +121 -36
- package/src/server/management/companion-routes.ts +77 -0
- package/src/server/management/logs-usage-routes.ts +19 -0
- package/src/server/management/native-integration-routes.ts +103 -6
- package/src/server/management/oauth-account-routes.ts +45 -7
- package/src/server/management/route-registry.ts +6 -0
- package/src/server/management/shared.ts +18 -1
- package/src/server/management/usage-timeline-routes.ts +44 -0
- package/src/server/management-api.ts +8 -9
- package/src/server/proxy-liveness.ts +75 -0
- package/src/server/relay.ts +19 -2
- package/src/server/request-log-failure-attribution.ts +99 -0
- package/src/server/request-log.ts +114 -0
- package/src/server/request-metrics.ts +92 -30
- package/src/server/responses/codex-ws-wire.ts +34 -8
- package/src/server/responses/combo-stream-preflight.ts +168 -6
- package/src/server/responses/compact.ts +11 -0
- package/src/server/responses/core-opaque-recovery.ts +90 -0
- package/src/server/responses/fetch-helpers.ts +124 -8
- package/src/server/responses/input-admission.ts +10 -0
- package/src/server/responses/passthrough-delivery.ts +14 -1
- package/src/server/responses/passthrough-dispatch.ts +179 -35
- package/src/server/responses/passthrough-error.ts +27 -8
- package/src/server/responses/request-prepare.ts +42 -1
- package/src/server/responses/request-send-budget.ts +12 -0
- package/src/server/responses/request-transport.ts +24 -4
- package/src/server/responses/reset-replay.ts +108 -0
- package/src/server/responses-request-tool-scope.ts +214 -0
- package/src/server/responses-undeclared-tool-guard.ts +4 -1
- package/src/server/search.ts +25 -1
- package/src/server/usage-ledger-retention.ts +73 -0
- package/src/service/cli.ts +48 -2
- package/src/service/health.ts +3 -2
- package/src/service/install-state-contract.d.mts +27 -0
- package/src/service/install-state-contract.mjs +34 -0
- package/src/service/launchd.ts +1 -1
- package/src/service/orchestration.ts +2 -4
- package/src/service/ownership-compatibility.ts +164 -0
- package/src/service/ownership-mutation-lease.d.mts +32 -0
- package/src/service/ownership-mutation-lease.mjs +211 -0
- package/src/service/repair.ts +45 -1
- package/src/service/state-lock.ts +269 -0
- package/src/service/state-record.d.mts +36 -0
- package/src/service/state-record.mjs +138 -0
- package/src/service/state.ts +582 -68
- package/src/service/windows-taskxml.ts +11 -10
- package/src/service.ts +7 -3
- package/src/tray/windows-tray.ps1 +1 -1
- package/src/types/config.ts +37 -0
- package/src/types/provider.ts +73 -0
- package/src/types/request.ts +28 -2
- package/src/types/tools.ts +19 -0
- package/src/types.ts +3 -0
- package/src/update/index.ts +207 -63
- package/src/update/job.ts +9 -5
- package/src/update/ownership-transaction.ts +47 -0
- package/src/update/restart-ownership.ts +54 -0
- package/src/update/runtime-ownership.d.mts +40 -0
- package/src/update/runtime-ownership.mjs +122 -0
- package/src/usage/attempt-delivery.ts +198 -0
- package/src/usage/cache-diagnostic.ts +305 -0
- package/src/usage/failure-fingerprint.ts +118 -0
- package/src/usage/failure-projection-cache.ts +174 -0
- package/src/usage/failure-projection.ts +174 -0
- package/src/usage/ledger-retention.ts +165 -0
- package/src/usage/log.ts +126 -79
- package/src/usage/request-outcome.ts +150 -0
- package/src/usage/retention-contract.ts +28 -0
- package/src/usage/summary.ts +2 -2
- package/src/usage/telemetry-contract.ts +237 -0
- package/src/usage/timeline.ts +236 -0
- package/src/web-search/alpha-search.ts +21 -1
- package/gui/dist/assets/index-BTuCbqQd.css +0 -1
- package/gui/dist/assets/index-DoBVdPHP.js +0 -134
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
quotaResetNotifySchema,
|
|
33
33
|
remoteGuiConfigSchema,
|
|
34
34
|
retryOn429PolicySchema,
|
|
35
|
+
retryOnResetPolicySchema,
|
|
35
36
|
runtimeRoleSchema,
|
|
36
37
|
spendSchema,
|
|
37
38
|
} from "./schema/leaf-validators";
|
|
@@ -195,18 +196,44 @@ export function sanitizeRetryOn429ForLoad(parsed: unknown): void {
|
|
|
195
196
|
* redacted (a malformed write can place a secret in a property name).
|
|
196
197
|
*/
|
|
197
198
|
export function retryOn429PolicyConfigError(policy: unknown): string | null {
|
|
199
|
+
return strictPolicyConfigError("retryOn429", retryOn429PolicySchema, policy);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Management write-boundary validation for `retryOnReset`, with the same fail-closed contract
|
|
204
|
+
* as `retryOn429PolicyConfigError`: the load-time schema degrades a malformed block to
|
|
205
|
+
* "absent", so this is the one place a bad value is refused instead of silently dropped.
|
|
206
|
+
*/
|
|
207
|
+
export function retryOnResetPolicyConfigError(policy: unknown): string | null {
|
|
208
|
+
return strictPolicyConfigError("retryOnReset", retryOnResetPolicySchema, policy);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* The shared body of both. Written once because the two differ only in the field name they
|
|
213
|
+
* report, and a second hand-copied formatter is a second place for the redaction to be
|
|
214
|
+
* forgotten.
|
|
215
|
+
*/
|
|
216
|
+
function strictPolicyConfigError(
|
|
217
|
+
field: string,
|
|
218
|
+
schema: {
|
|
219
|
+
safeParse: (value: unknown) => { success: true } | {
|
|
220
|
+
success: false;
|
|
221
|
+
error: { issues: Array<{ code: string; message: string; path: PropertyKey[]; keys?: string[] }> };
|
|
222
|
+
};
|
|
223
|
+
},
|
|
224
|
+
policy: unknown,
|
|
225
|
+
): string | null {
|
|
198
226
|
if (policy === undefined) return null;
|
|
199
|
-
const result =
|
|
227
|
+
const result = schema.safeParse(policy);
|
|
200
228
|
if (result.success) return null;
|
|
201
229
|
const first = result.error.issues[0];
|
|
202
|
-
if (!first) return
|
|
203
|
-
if (first.code === "unrecognized_keys") {
|
|
230
|
+
if (!first) return `${field} is invalid`;
|
|
231
|
+
if (first.code === "unrecognized_keys" && first.keys) {
|
|
204
232
|
const names = first.keys.map(key => JSON.stringify(redactSecretString(key))).join(", ");
|
|
205
|
-
return
|
|
233
|
+
return `${field} has unrecognized field${first.keys.length > 1 ? "s" : ""}: ${names}`;
|
|
206
234
|
}
|
|
207
|
-
if (first.path.length === 0) return
|
|
208
|
-
|
|
209
|
-
return `retryOn429.${field} is invalid (${first.message})`;
|
|
235
|
+
if (first.path.length === 0) return `${field} is invalid (${first.message})`;
|
|
236
|
+
return `${field}.${String(first.path[first.path.length - 1])} is invalid (${first.message})`;
|
|
210
237
|
}
|
|
211
238
|
|
|
212
239
|
export function sanitizeCapabilityDeclarationsForLoad(parsed: unknown): void {
|
|
@@ -127,7 +127,7 @@ export function isOcxCommandLine(commandLine: string): boolean {
|
|
|
127
127
|
|| normalized.includes("src/cli/index.ts")
|
|
128
128
|
|| normalized.includes("@bitkyc08/opencodex")
|
|
129
129
|
|| /@bitkyc08\/\.opencodex-/.test(normalized)
|
|
130
|
-
|| /(?:^|[\s/"'])(?:ocx|opencodex)(?:\.cmd)?(?:$|[\s"'])/.test(normalized);
|
|
130
|
+
|| /(?:^|[\s/"'])(?:ocx|opencodex)(?:\.cmd|\.exe)?(?:$|[\s"'])/.test(normalized);
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
export function isOcxStartCommandLine(commandLine: string): boolean {
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
MAIN_CODEX_ACCOUNT_NAMESPACE_TARGET,
|
|
47
47
|
} from "../../codex/account-namespace-match";
|
|
48
48
|
import { UPSTREAM_HOST_CIRCUIT_MAX_THRESHOLD } from "../../codex/upstream-host-health";
|
|
49
|
+
import { MIN_USAGE_LEDGER_MAX_BYTES } from "../../usage/retention-contract";
|
|
49
50
|
import { COMBO_NAMESPACE, comboConfigIssues } from "../../combos/types";
|
|
50
51
|
import { routingProfileIssues } from "../../routing/profile";
|
|
51
52
|
import { POLICY_NAMESPACE } from "../../routing/profile-namespace";
|
|
@@ -82,6 +83,14 @@ export const configSchema = z.object({
|
|
|
82
83
|
managementUsageMaxReadBytes: z.number().int().positive().default(64 * 1024 * 1024).describe(
|
|
83
84
|
"Deprecated compatibility limit for bounded legacy usage readers; GET /api/usage always aggregates the complete ledger",
|
|
84
85
|
),
|
|
86
|
+
// Opt-in ledger ceiling. A hand edit below the floor, or a non-safe integer, disables only
|
|
87
|
+
// this limit rather than failing the config: refusing to start because history retention was
|
|
88
|
+
// mistyped would be a worse outcome than not trimming history.
|
|
89
|
+
usageLedgerMaxBytes: z.number().int()
|
|
90
|
+
.min(MIN_USAGE_LEDGER_MAX_BYTES)
|
|
91
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
92
|
+
.optional()
|
|
93
|
+
.catch(undefined),
|
|
85
94
|
// Invalid hand edits disable only this opt-in circuit. Live writes remain strict.
|
|
86
95
|
upstreamHostCircuitThreshold: z.number().int()
|
|
87
96
|
.min(0)
|
|
@@ -261,7 +270,24 @@ export const configSchema = z.object({
|
|
|
261
270
|
if (claudeCode !== undefined && (!claudeCode || typeof claudeCode !== "object" || Array.isArray(claudeCode))) {
|
|
262
271
|
ctx.addIssue({ code: "custom", path: ["claudeCode"], message: "claudeCode must be an object" });
|
|
263
272
|
} else if (claudeCode) {
|
|
264
|
-
const claude = claudeCode as { desktopProfile?: unknown };
|
|
273
|
+
const claude = claudeCode as { desktopProfile?: unknown; desktopMode?: unknown; intercept?: unknown };
|
|
274
|
+
if (claude.desktopMode !== undefined && claude.desktopMode !== "first-party" && claude.desktopMode !== "gateway") {
|
|
275
|
+
ctx.addIssue({ code: "custom", path: ["claudeCode", "desktopMode"], message: "desktopMode must be \"first-party\" or \"gateway\"" });
|
|
276
|
+
}
|
|
277
|
+
if (claude.intercept !== undefined) {
|
|
278
|
+
const intercept = claude.intercept;
|
|
279
|
+
if (!intercept || typeof intercept !== "object" || Array.isArray(intercept)) {
|
|
280
|
+
ctx.addIssue({ code: "custom", path: ["claudeCode", "intercept"], message: "intercept must be an object" });
|
|
281
|
+
} else {
|
|
282
|
+
const { enabled, port } = intercept as { enabled?: unknown; port?: unknown };
|
|
283
|
+
if (enabled !== undefined && typeof enabled !== "boolean") {
|
|
284
|
+
ctx.addIssue({ code: "custom", path: ["claudeCode", "intercept", "enabled"], message: "intercept.enabled must be a boolean" });
|
|
285
|
+
}
|
|
286
|
+
if (port !== undefined && (typeof port !== "number" || !Number.isInteger(port) || port < 1 || port > 65535)) {
|
|
287
|
+
ctx.addIssue({ code: "custom", path: ["claudeCode", "intercept", "port"], message: "intercept.port must be an integer between 1 and 65535" });
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
265
291
|
if (claude.desktopProfile !== undefined) {
|
|
266
292
|
try {
|
|
267
293
|
parseDesktopProfile(claude.desktopProfile);
|
|
@@ -17,6 +17,7 @@ import { isCodexAccountPriorityKey } from "../../codex/account-priority";
|
|
|
17
17
|
import { parseAccountPriority } from "../../codex/pool-rotation";
|
|
18
18
|
import { credentialGroupIssues } from "../../routing/identity-domains";
|
|
19
19
|
import { providerDestinationConfigError } from "../../lib/destination-policy";
|
|
20
|
+
import { providerEgressConfigError } from "../../lib/provider-egress";
|
|
20
21
|
import { redactSecretString } from "../../lib/redact";
|
|
21
22
|
import {
|
|
22
23
|
MODEL_ADAPTER_OVERRIDE_ALLOWED,
|
|
@@ -80,6 +81,17 @@ const transientRetryOn5xxPolicySchema = z.object({
|
|
|
80
81
|
attempts: z.number().int().min(1).max(10).optional(),
|
|
81
82
|
}).strict();
|
|
82
83
|
|
|
84
|
+
/**
|
|
85
|
+
* `retryOnReset` accepts only these keys. `replacements` counts DUPLICATE inferences the
|
|
86
|
+
* operator is willing to risk for one logical request, so the ceiling is two rather than a
|
|
87
|
+
* send budget: this is the one send the proxy otherwise refuses outright, and a third of them
|
|
88
|
+
* says the connection, not the retry policy, is the problem.
|
|
89
|
+
*/
|
|
90
|
+
export const retryOnResetPolicySchema = z.object({
|
|
91
|
+
enabled: z.boolean().optional(),
|
|
92
|
+
replacements: z.number().int().min(1).max(2).optional(),
|
|
93
|
+
}).strict();
|
|
94
|
+
|
|
83
95
|
const requestPacingRuleSchema = z.object({
|
|
84
96
|
// Keep the RPM-derived timer within the same one-hour bound as minIntervalMs.
|
|
85
97
|
requestsPerMinute: z.number().min(1 / 60).max(60_000).optional(),
|
|
@@ -221,6 +233,25 @@ const modelCapabilitiesSchema = z.unknown().superRefine((value, ctx) => {
|
|
|
221
233
|
if (error) ctx.addIssue({ code: "custom", message: error });
|
|
222
234
|
}).transform(value => mergeModelCapabilities(undefined, value));
|
|
223
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Per-provider egress fields, validated by the resolver the transports themselves use.
|
|
238
|
+
*
|
|
239
|
+
* Calling `providerEgressConfigError` rather than restating the accepted forms keeps one
|
|
240
|
+
* definition of a usable value: a proxy the config loader admits is one the transport can
|
|
241
|
+
* carry, and a value rejected here is rejected at request time for the identical reason.
|
|
242
|
+
* Each field is checked on its own because neither depends on the other's value to be
|
|
243
|
+
* well-formed; how they combine is decided per request against the destination.
|
|
244
|
+
*/
|
|
245
|
+
const providerProxySchema = z.unknown().superRefine((value, ctx) => {
|
|
246
|
+
const error = providerEgressConfigError({ proxy: value as string | null | undefined });
|
|
247
|
+
if (error) ctx.addIssue({ code: "custom", message: error });
|
|
248
|
+
}).transform(value => value as string | null | undefined);
|
|
249
|
+
|
|
250
|
+
const providerNoProxySchema = z.unknown().superRefine((value, ctx) => {
|
|
251
|
+
const error = providerEgressConfigError({ noProxy: value as string | string[] | undefined });
|
|
252
|
+
if (error) ctx.addIssue({ code: "custom", message: error });
|
|
253
|
+
}).transform(value => value as string | string[] | undefined);
|
|
254
|
+
|
|
224
255
|
/**
|
|
225
256
|
* Zod schema for one provider entry: known fields are validated strictly while unknown
|
|
226
257
|
* fields pass through (preserved for runtime extensions).
|
|
@@ -258,6 +289,7 @@ export const providerConfigSchema = z.object({
|
|
|
258
289
|
requiresAdjacentResponsesToolResults: z.boolean().optional(),
|
|
259
290
|
requiresPairedResponsesToolResults: z.boolean().optional(),
|
|
260
291
|
annotateEmptyToolOutputs: z.boolean().optional(),
|
|
292
|
+
foldDeveloperRoleToSystem: z.boolean().optional(),
|
|
261
293
|
fastWire: fastWireSchema.nullable().optional(),
|
|
262
294
|
supportsServiceTier: z.boolean().optional(),
|
|
263
295
|
modelSupportsServiceTier: z.record(z.string().min(1), z.boolean()).optional(),
|
|
@@ -268,6 +300,10 @@ export const providerConfigSchema = z.object({
|
|
|
268
300
|
decodesNativeCompactionBlobs: z.boolean().optional(),
|
|
269
301
|
allowEncryptedV2AgentTasks: z.boolean().optional(),
|
|
270
302
|
allowPrivateNetwork: z.boolean().optional(),
|
|
303
|
+
// Per-provider egress (#2894): absent inherits the global proxy decision, "direct"/null
|
|
304
|
+
// refuses it, and an http(s) or socks5 URL replaces it for this provider only.
|
|
305
|
+
proxy: providerProxySchema.optional(),
|
|
306
|
+
noProxy: providerNoProxySchema.optional(),
|
|
271
307
|
// The management API accepts `null` as "clear this", so a config written before the POST
|
|
272
308
|
// canonicalization below can hold one on disk. Rejecting it here would send the operator
|
|
273
309
|
// through invalid-config recovery for a value the API told them was fine.
|
|
@@ -306,6 +342,10 @@ export const providerConfigSchema = z.object({
|
|
|
306
342
|
.optional(),
|
|
307
343
|
retryOn429: retryOn429PolicySchema.optional(),
|
|
308
344
|
transientRetryOn5xx: transientRetryOn5xxPolicySchema.optional(),
|
|
345
|
+
// Degrades to "absent" like `webSearchBridge`: a malformed hand edit of an opt-in feature
|
|
346
|
+
// that is off by default must not send the operator through invalid-config recovery. The
|
|
347
|
+
// management write boundary still rejects it loudly (`retryOnResetPolicyConfigError`).
|
|
348
|
+
retryOnReset: retryOnResetPolicySchema.optional().catch(undefined),
|
|
309
349
|
codexAccountMode: z.enum(["pool", "direct"]).optional(),
|
|
310
350
|
// Validated rather than passed through: this schema ends in `.passthrough()`, so an
|
|
311
351
|
// undeclared key survives verbatim. A misspelled `codexToolMode` therefore used to be
|
|
@@ -649,6 +689,13 @@ export const apiKeyEntrySchema = z.object({
|
|
|
649
689
|
createdAt: z.string().catch(""),
|
|
650
690
|
// A damaged overlap record must never discard the still-authoritative key.
|
|
651
691
|
pendingRotation: pendingApiKeyRotationSchema.optional().catch(undefined),
|
|
692
|
+
// Deliberately NOT `.catch`ed, unlike every field above. Degrading a damaged
|
|
693
|
+
// scope to `undefined` would silently widen the key to the whole catalog,
|
|
694
|
+
// which is the one direction a permission field must never fail. Letting the
|
|
695
|
+
// record fail instead drops the key, so a corrupted scope stops that client
|
|
696
|
+
// rather than promoting it.
|
|
697
|
+
allowedProviders: z.array(z.string().trim().min(1).max(256)).optional(),
|
|
698
|
+
allowedModels: z.array(z.string().trim().min(1).max(256)).optional(),
|
|
652
699
|
}).passthrough();
|
|
653
700
|
|
|
654
701
|
/**
|
package/src/config.ts
CHANGED
|
@@ -110,7 +110,7 @@ export {
|
|
|
110
110
|
sanitizeModelCostsForDisplay,
|
|
111
111
|
modelPreferHostedToolsConfigError,
|
|
112
112
|
} from "./config/schema/leaf-validators";
|
|
113
|
-
export { hardenExistingSecret, retryOn429PolicyConfigError } from "./config/load-degrade";
|
|
113
|
+
export { hardenExistingSecret, retryOn429PolicyConfigError, retryOnResetPolicyConfigError } from "./config/load-degrade";
|
|
114
114
|
export { backupInvalidConfig } from "./config/salvage";
|
|
115
115
|
export type { ConfigDiagnostics, ConfigAdmissionSnapshot } from "./config/diagnostics";
|
|
116
116
|
export {
|