@bitkyc08/opencodex 2.57.0 → 2.59.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/README.md +28 -10
- package/gui/dist/assets/index-C5IebErG.js +136 -0
- package/gui/dist/assets/{index-C5-RdDmD.css → index-OESInAjC.css} +1 -1
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/crusoe.svg +1 -0
- package/gui/dist/provider-icons/opper.svg +3 -0
- package/package.json +2 -2
- package/src/adapters/base.ts +11 -1
- package/src/adapters/codebuddy/scaffold-guard.ts +5 -4
- package/src/adapters/command-code.ts +13 -4
- package/src/adapters/cursor/catalog.ts +11 -0
- package/src/adapters/cursor/cursor-errors.ts +15 -0
- package/src/adapters/cursor/discovery.ts +65 -1
- package/src/adapters/cursor/effort-map.ts +16 -2
- package/src/adapters/cursor/envelope-echo.ts +55 -2
- package/src/adapters/cursor/live-transport.ts +5 -1
- package/src/adapters/cursor/message-mapper.ts +3 -2
- package/src/adapters/cursor/protobuf-events.ts +110 -11
- package/src/adapters/cursor/protobuf-request.ts +27 -6
- package/src/adapters/cursor/request-builder.ts +14 -3
- package/src/adapters/cursor/text-toolcall.ts +230 -0
- package/src/adapters/cursor/thread-continuity.ts +141 -0
- package/src/adapters/cursor/tool-guidance.ts +5 -4
- package/src/adapters/cursor/types.ts +5 -0
- package/src/adapters/cursor.ts +97 -6
- package/src/adapters/devin/cloud-direct/chat.ts +11 -2
- package/src/adapters/devin/cloud-direct/index.ts +7 -0
- package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +103 -0
- package/src/adapters/devin.ts +75 -13
- package/src/adapters/google-antigravity-wire.ts +29 -2
- package/src/adapters/google-http.ts +45 -13
- package/src/adapters/google.ts +23 -4
- package/src/adapters/mimo-free.ts +32 -17
- package/src/adapters/ollama-native.ts +42 -8
- package/src/adapters/openai-chat/response-events.ts +61 -0
- package/src/adapters/openai-chat.ts +5 -10
- package/src/adapters/openai-responses/passthrough.ts +40 -5
- package/src/adapters/openai-responses/request-strips.ts +43 -0
- package/src/adapters/openai-responses/tool-output-recovery.ts +75 -0
- package/src/adapters/openai-responses/tool-schema.ts +19 -7
- package/src/adapters/physical-send.ts +50 -0
- package/src/adapters/responses-tool-schema.ts +76 -46
- package/src/adapters/run-turn-queue.ts +17 -4
- package/src/bridge/response-json.ts +2 -2
- package/src/bridge/sse.ts +166 -25
- package/src/claude/context-windows.ts +22 -0
- package/src/claude/outbound.ts +46 -5
- package/src/cli/account-api.ts +4 -3
- package/src/cli/account-extended.ts +22 -2
- package/src/cli/account-orca-import.ts +63 -0
- package/src/cli/account.ts +32 -4
- package/src/cli/capabilities.ts +40 -0
- package/src/cli/claude.ts +29 -1
- package/src/cli/codex-cli-update.ts +97 -2
- package/src/cli/config-command.ts +35 -18
- package/src/cli/dispatch.ts +71 -4
- package/src/cli/doctor.ts +197 -2
- package/src/cli/help.ts +4 -1
- package/src/cli/index.ts +132 -22
- package/src/cli/models-runtime.ts +33 -4
- package/src/cli/registry.ts +11 -1
- package/src/cli/runtime-api.ts +44 -0
- package/src/cli/start-args.ts +94 -0
- package/src/cli/system-command.ts +72 -1
- package/src/cli/uninstall-client-state.ts +12 -0
- package/src/client/machine-api.ts +4 -3
- package/src/client/machine-listener.ts +14 -1
- package/src/clients/config-export/constants.ts +2 -3
- package/src/clients/config-export.ts +5 -5
- package/src/codex/account-store.ts +81 -5
- package/src/codex/auth-api/pool-quota-probe.ts +14 -3
- package/src/codex/auth-api/routes.ts +17 -2
- package/src/codex/auth-context.ts +58 -20
- package/src/codex/catalog/build-entries.ts +25 -4
- package/src/codex/catalog/derive-entry.ts +8 -1
- package/src/codex/catalog/effort.ts +10 -6
- package/src/codex/catalog/gather-capture.ts +1 -0
- package/src/codex/catalog/model-hints.ts +37 -5
- package/src/codex/catalog/parsing.ts +83 -5
- package/src/codex/catalog/reserve-warn.ts +96 -0
- package/src/codex/catalog/retained-sync.ts +19 -0
- package/src/codex/catalog/routed-gather.ts +42 -3
- package/src/codex/cli-installation-identity.ts +210 -0
- package/src/codex/cli-installation-targets.ts +158 -0
- package/src/codex/convergence.ts +5 -0
- package/src/codex/desktop-switches.ts +145 -0
- package/src/codex/history-job.ts +5 -1
- package/src/codex/history-provider.ts +37 -5
- package/src/codex/history-state-open.ts +105 -0
- package/src/codex/history-worker.ts +14 -1
- package/src/codex/inject/config-toml.ts +44 -2
- package/src/codex/inject/remove.ts +145 -7
- package/src/codex/inject/restore.ts +204 -32
- package/src/codex/inject.ts +6 -9
- package/src/codex/lineage.ts +83 -32
- package/src/codex/loopback-target.ts +40 -0
- package/src/codex/main-account-hard-lock.ts +2 -1
- package/src/codex/main-account.ts +10 -3
- package/src/codex/main-device-reauth.ts +17 -9
- package/src/codex/model-entitlements.ts +60 -1
- package/src/codex/native-profile-startup.ts +64 -20
- package/src/codex/observed-model-denials.ts +137 -0
- package/src/codex/orca-auth-source.ts +94 -0
- package/src/codex/orca-import.ts +219 -0
- package/src/codex/prompt-text-probe.ts +282 -12
- package/src/codex/quota-401-recovery.ts +12 -0
- package/src/codex/quota-types.ts +65 -0
- package/src/codex/quota.ts +24 -19
- package/src/codex/routing/cooldown-math.ts +8 -47
- package/src/codex/routing/pin-drain.ts +57 -0
- package/src/codex/routing.ts +13 -15
- package/src/codex/subagent-model-fallback.ts +94 -0
- package/src/codex/windows-installation-files.ts +224 -0
- package/src/combos/failover.ts +122 -5
- package/src/config/atomic-write.ts +83 -8
- package/src/config/diagnostics.ts +21 -0
- package/src/config/load-degrade.ts +15 -0
- package/src/config/pending-teardown.ts +8 -0
- package/src/config/process-state.ts +36 -3
- package/src/config/provider-relative-send-path.ts +16 -0
- package/src/config/proxy-env.ts +23 -5
- package/src/config/schema/config-schema.ts +23 -0
- package/src/config/schema/leaf-validators.ts +65 -17
- package/src/generated/compatibility-version.json +337 -201
- package/src/generated/model-metadata.ts +1 -1
- package/src/lib/bounded-body.ts +4 -2
- package/src/lib/bounded-subprocess.ts +62 -10
- package/src/lib/destination-policy.ts +48 -6
- package/src/lib/errors.ts +3 -15
- package/src/lib/local-destinations.ts +32 -5
- package/src/lib/provider-outbound.ts +3 -3
- package/src/lib/proxy-env.ts +70 -3
- package/src/lib/request-execution-budget.ts +11 -3
- package/src/lib/response-body-inactivity.ts +193 -0
- package/src/lib/retry-delay.ts +69 -0
- package/src/lib/socks5-fetch.ts +631 -0
- package/src/lib/spend-reservation-ledger.ts +115 -9
- package/src/lib/windows-secret-acl.ts +151 -15
- package/src/lib/windows-user-principal.ts +5 -1
- package/src/lib/workflow-budget.ts +145 -8
- package/src/oauth/account-quota-rank.ts +72 -15
- package/src/oauth/generic-account-failover.ts +40 -27
- package/src/oauth/orcarouter.ts +15 -2
- package/src/oauth/store.ts +8 -0
- package/src/providers/codex-capacity.ts +9 -0
- package/src/providers/derive.ts +6 -0
- package/src/providers/devin-provider-merge-migration.ts +33 -12
- package/src/providers/free-directory.ts +20 -2
- package/src/providers/key-failover.ts +261 -7
- package/src/providers/model-discovery.ts +19 -7
- package/src/providers/model-rename-migration.ts +1 -0
- package/src/providers/openai-sidecar.ts +4 -0
- package/src/providers/opencode-go-transport.ts +14 -5
- package/src/providers/quota/report-cache.ts +3 -0
- package/src/providers/registry/entries-core.ts +11 -0
- package/src/providers/registry/entries-extended.ts +146 -28
- package/src/providers/registry/model-seeds.ts +136 -29
- package/src/providers/registry/types.ts +9 -0
- package/src/responses/apply-patch-envelope.ts +44 -11
- package/src/responses/bridge-search-replay-cache.ts +152 -0
- package/src/responses/code-mode-helper-compat.ts +26 -16
- package/src/responses/custom-tool-compat.ts +1 -1
- package/src/responses/hosted-tool-policy.ts +85 -2
- package/src/responses/schema.ts +9 -2
- package/src/responses/spill-store.ts +17 -0
- package/src/responses/state/body-policy.ts +25 -0
- package/src/responses/state/spill-queue.ts +8 -6
- package/src/responses/state.ts +3 -22
- package/src/router.ts +4 -0
- package/src/server/auth-cors.ts +27 -0
- package/src/server/chat-completions.ts +9 -4
- package/src/server/chat-native-sse.ts +26 -9
- package/src/server/chat-native.ts +10 -4
- package/src/server/claude-messages.ts +24 -2
- package/src/server/gui-static.ts +36 -2
- package/src/server/inbound-body-admission.ts +187 -0
- package/src/server/index/websocket-handler.ts +48 -1
- package/src/server/index.ts +15 -19
- package/src/server/management/api-access.ts +3 -4
- package/src/server/management/config-routes.ts +57 -10
- package/src/server/management/provider-capability-config.ts +35 -7
- package/src/server/management/provider-routes.ts +70 -18
- package/src/server/models-capabilities.ts +24 -3
- package/src/server/proxy-liveness.ts +97 -2
- package/src/server/relay.ts +17 -24
- package/src/server/request-log.ts +25 -1
- package/src/server/responses/adapter-continuation.ts +71 -27
- package/src/server/responses/adapter-delivery.ts +39 -8
- package/src/server/responses/adapter-dispatch.ts +52 -24
- package/src/server/responses/codex-ws-exchange.ts +65 -4
- package/src/server/responses/combo-stream-preflight.ts +68 -5
- package/src/server/responses/compact.ts +60 -11
- package/src/server/responses/core-codex-account.ts +83 -22
- package/src/server/responses/core-combo.ts +26 -0
- package/src/server/responses/core-normalize.ts +12 -5
- package/src/server/responses/core-options.ts +3 -0
- package/src/server/responses/fetch-helpers.ts +72 -3
- package/src/server/responses/native-injection-protocol.ts +42 -0
- package/src/server/responses/native-injection-replay.ts +105 -0
- package/src/server/responses/native-injection.ts +242 -0
- package/src/server/responses/native-response-control.ts +56 -0
- package/src/server/responses/native-response-json.ts +14 -0
- package/src/server/responses/native-response-output.ts +37 -0
- package/src/server/responses/native-steering-log.ts +44 -0
- package/src/server/responses/native-steering-policy.ts +49 -0
- package/src/server/responses/native-steering-replay.ts +126 -0
- package/src/server/responses/native-steering-settings.ts +76 -0
- package/src/server/responses/native-steering.ts +400 -0
- package/src/server/responses/native-tool-results.ts +130 -0
- package/src/server/responses/passthrough-delivery.ts +21 -1
- package/src/server/responses/passthrough-dispatch.ts +146 -49
- package/src/server/responses/passthrough-execution.ts +11 -1
- package/src/server/responses/request-prepare.ts +70 -0
- package/src/server/responses/request-send-budget.ts +84 -7
- package/src/server/responses/request-sidecar-auth.ts +16 -8
- package/src/server/responses/request-spend.ts +38 -9
- package/src/server/responses/request-transport.ts +13 -10
- package/src/server/responses/run-turn-execution.ts +20 -5
- package/src/server/responses/sidecar-execution.ts +2 -0
- package/src/server/responses/ws-upstream.ts +23 -2
- package/src/server/responses-custom-tool-repair.ts +2 -2
- package/src/server/sse-frame-buffer.ts +12 -10
- package/src/server/sse-payload-rewrite.ts +36 -9
- package/src/server/stop-teardown.ts +8 -1
- package/src/server/system-env-shell.ts +5 -1
- package/src/server/system-env.ts +7 -1
- package/src/server/workflow-refusal.ts +56 -2
- package/src/server/ws-bridge.ts +16 -1
- package/src/service/cli.ts +29 -7
- package/src/service/guards.ts +10 -0
- package/src/service/health.ts +43 -0
- package/src/service/state.ts +7 -2
- package/src/types/accounts.ts +4 -0
- package/src/types/config.ts +104 -3
- package/src/types/provider.ts +32 -0
- package/src/types/request.ts +7 -1
- package/src/types/wire.ts +9 -1
- package/src/usage/expected-prices.ts +28 -0
- package/src/usage/log.ts +87 -4
- package/src/web-search/passthrough-bridge.ts +39 -5
- package/gui/dist/assets/index-Cz7CLdif.js +0 -128
|
@@ -295,6 +295,14 @@ export function quarantinePendingTeardown(nonce: string): string | null {
|
|
|
295
295
|
* alone. Only an abandoned obligation is a candidate, and a VALID one still has to prove
|
|
296
296
|
* its endpoint is down before anything is restored — an invalid one never can, which is
|
|
297
297
|
* what {@link quarantinePendingTeardown} exists for.
|
|
298
|
+
*
|
|
299
|
+
* `isAlive` must answer "is the OWNER still running", which is not "is this PID alive".
|
|
300
|
+
* PIDs are reused: after the owner exits its number can be handed to an unrelated process,
|
|
301
|
+
* and a bare liveness probe then reports the owner as running for as long as that process
|
|
302
|
+
* lives. The receipt is neither recovered nor quarantined nor reported while both updater
|
|
303
|
+
* gates keep refusing on it, which is a permanent fail-closed with no way forward (#4897).
|
|
304
|
+
* `handleStop` therefore passes an identity-aware predicate; do not substitute a bare
|
|
305
|
+
* `process.kill(pid, 0)` here for cheapness.
|
|
298
306
|
*/
|
|
299
307
|
export function isPendingTeardownAbandoned(
|
|
300
308
|
read: PendingTeardownRead | TeardownScanFailure,
|
|
@@ -110,16 +110,29 @@ export function removeRuntimePortIfPidIs(snapshotPid: number | null): void {
|
|
|
110
110
|
try { unlinkSync(getRuntimePortPath()); } catch { /* ignore */ }
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Does this command line belong to an opencodex process at all, whatever it is doing?
|
|
115
|
+
*
|
|
116
|
+
* {@link isOcxStartCommandLine} answers the narrower question the pidfile needs — "is this
|
|
117
|
+
* the proxy?" — by additionally requiring the `start` verb. Ownership of a pending-teardown
|
|
118
|
+
* receipt is the broader question: the owner is whichever invocation claimed it, which is an
|
|
119
|
+
* `ocx stop` or the `ocx update` worker that drove it, never an `ocx start`. Asking the
|
|
120
|
+
* start-shaped question there would call every real owner foreign.
|
|
121
|
+
*/
|
|
122
|
+
export function isOcxCommandLine(commandLine: string): boolean {
|
|
114
123
|
const normalized = commandLine.toLowerCase().replace(/\\/g, "/");
|
|
115
124
|
// Keep legacy source launches and npm's in-place Windows rename recognizable:
|
|
116
125
|
// a service wrapper may respawn from `.opencodex-*` during a global update.
|
|
117
|
-
|
|
126
|
+
return normalized.includes("src/cli.ts")
|
|
118
127
|
|| normalized.includes("src/cli/index.ts")
|
|
119
128
|
|| normalized.includes("@bitkyc08/opencodex")
|
|
120
129
|
|| /@bitkyc08\/\.opencodex-/.test(normalized)
|
|
121
130
|
|| /(?:^|[\s/"'])(?:ocx|opencodex)(?:\.cmd)?(?:$|[\s"'])/.test(normalized);
|
|
122
|
-
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function isOcxStartCommandLine(commandLine: string): boolean {
|
|
134
|
+
const normalized = commandLine.toLowerCase().replace(/\\/g, "/");
|
|
135
|
+
return isOcxCommandLine(commandLine) && /(?:^|[\s"'])start(?:$|[\s"'])/.test(normalized);
|
|
123
136
|
}
|
|
124
137
|
|
|
125
138
|
/** Avoid spawning WMIC/PowerShell on every short liveness poll. */
|
|
@@ -227,6 +240,26 @@ export function verifyPidIdentity(candidatePid: number): number | null {
|
|
|
227
240
|
return isLikelyOcxStartProcess(candidatePid) ? candidatePid : null;
|
|
228
241
|
}
|
|
229
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Is this PID an opencodex process, rather than merely a live one?
|
|
245
|
+
*
|
|
246
|
+
* `process.kill(pid, 0)` answers "does this number name a process", which is not the same
|
|
247
|
+
* question. PIDs are reused, so a recorded owner that exited can have its number handed to
|
|
248
|
+
* an unrelated process, and bare liveness then reports the owner as still running forever.
|
|
249
|
+
*
|
|
250
|
+
* Deliberately uncached, unlike {@link isLikelyOcxStartProcess}. That cache exists because
|
|
251
|
+
* liveness polling asks about the same PID many times a second; this is asked once per
|
|
252
|
+
* outstanding teardown receipt in a short-lived `ocx stop`, so the cache would only add an
|
|
253
|
+
* unswept map. The Windows WMIC/PowerShell probe cost is the point rather than a regression:
|
|
254
|
+
* the runtime contract requires the identity check, not the cheap one, before acting on
|
|
255
|
+
* stale state.
|
|
256
|
+
*/
|
|
257
|
+
export function isLikelyOcxProcess(pid: number): boolean {
|
|
258
|
+
const commandLine = readProcessCommandLine(pid);
|
|
259
|
+
if (commandLine === undefined) return false;
|
|
260
|
+
return isOcxCommandLine(commandLine);
|
|
261
|
+
}
|
|
262
|
+
|
|
230
263
|
type ProcessCommandLineExec = (
|
|
231
264
|
executable: string,
|
|
232
265
|
args: string[],
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Shared, initialization-independent shape check for provider send-path overrides. */
|
|
2
|
+
export function providerRelativeSendPathConfigError(
|
|
3
|
+
field: "responsesPath" | "chatCompletionsPath",
|
|
4
|
+
value: unknown,
|
|
5
|
+
): string | null {
|
|
6
|
+
if (value === undefined) return null;
|
|
7
|
+
if (typeof value !== "string") return `${field} must be a string`;
|
|
8
|
+
if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(value) || value.includes("://")) {
|
|
9
|
+
return `${field} must be a relative path without a URL scheme`;
|
|
10
|
+
}
|
|
11
|
+
if (!value.startsWith("/")) return `${field} must start with /`;
|
|
12
|
+
if (value.includes("?") || value.includes("#")) {
|
|
13
|
+
return `${field} must not include query strings or fragments`;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
package/src/config/proxy-env.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { configureSocks5Fetch } from "../lib/proxy-env";
|
|
2
|
+
import { redactUrlForLog } from "../lib/redact";
|
|
1
3
|
import { join } from "node:path";
|
|
2
4
|
import { DEFAULT_SUBAGENT_MODELS, SUBAGENT_MODELS_VERSION } from "./subagent-models";
|
|
3
5
|
import { MULTI_AGENT_SURFACE_ADVISORY_VERSION } from "./multi-agent-surface";
|
|
@@ -102,10 +104,15 @@ function warnProxyConfigDiscardOnce(kind: "proxy" | "noProxy" | "noProxyElements
|
|
|
102
104
|
* variables win; config fills missing scheme proxies, which take precedence over ALL_PROXY for WS.
|
|
103
105
|
* localhost/127.0.0.1 are appended to NO_PROXY so the CLI's own health checks and
|
|
104
106
|
* running-proxy API calls stay direct. Call once per process entry that makes outbound provider
|
|
105
|
-
* requests (server start, catalog sync).
|
|
107
|
+
* requests (server start, catalog sync). `announce` prints the one-line startup banner naming
|
|
108
|
+
* the outbound proxy actually in effect; only server start passes it, because on catalog sync
|
|
109
|
+
* that line is noise. The URL is redacted because a proxy URL can carry credentials.
|
|
106
110
|
*/
|
|
107
|
-
export function applyProxyEnv(config: OcxConfig): void {
|
|
111
|
+
export function applyProxyEnv(config: OcxConfig, announce = false): void {
|
|
108
112
|
applyProxyEnvWith(config);
|
|
113
|
+
if (!announce) return;
|
|
114
|
+
const outbound = process.env.ALL_PROXY?.trim() || process.env.HTTPS_PROXY?.trim() || process.env.HTTP_PROXY?.trim();
|
|
115
|
+
if (outbound) console.log(` outbound proxy: ${redactUrlForLog(outbound)}`);
|
|
109
116
|
}
|
|
110
117
|
|
|
111
118
|
/** Test seam for `proxy: "auto"`: the registry reader and platform are injectable. */
|
|
@@ -123,6 +130,7 @@ export function applyProxyEnvWith(
|
|
|
123
130
|
let proxy = typeof rawProxy === "string" ? resolveEnvValue(rawProxy) : undefined;
|
|
124
131
|
if (!proxy) {
|
|
125
132
|
if (rawProxy !== undefined) warnProxyConfigDiscardOnce("proxy");
|
|
133
|
+
configureSocks5Fetch();
|
|
126
134
|
return;
|
|
127
135
|
}
|
|
128
136
|
if (proxy.trim().toLowerCase() === "auto") {
|
|
@@ -151,8 +159,18 @@ export function applyProxyEnvWith(
|
|
|
151
159
|
}
|
|
152
160
|
}
|
|
153
161
|
if (proxy) {
|
|
154
|
-
|
|
155
|
-
|
|
162
|
+
if (/^socks/i.test(proxy.trim()) && !/^socks5h?:\/\//i.test(proxy.trim())) {
|
|
163
|
+
throw new Error("Only SOCKS5 proxy URLs are supported; use socks5://host:port");
|
|
164
|
+
}
|
|
165
|
+
if (/^socks5h?:\/\//i.test(proxy.trim())) {
|
|
166
|
+
for (const key of ["HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy", "ALL_PROXY", "all_proxy"] as const) {
|
|
167
|
+
delete process.env[key];
|
|
168
|
+
}
|
|
169
|
+
process.env.ALL_PROXY = proxy;
|
|
170
|
+
} else {
|
|
171
|
+
if (!process.env.HTTP_PROXY?.trim() && !process.env.http_proxy?.trim()) process.env.HTTP_PROXY = proxy;
|
|
172
|
+
if (!process.env.HTTPS_PROXY?.trim() && !process.env.https_proxy?.trim()) process.env.HTTPS_PROXY = proxy;
|
|
173
|
+
}
|
|
156
174
|
}
|
|
157
175
|
const existing = process.env.NO_PROXY ?? process.env.no_proxy ?? "";
|
|
158
176
|
const entries = existing.split(",").map(s => s.trim()).filter(Boolean);
|
|
@@ -184,5 +202,5 @@ export function applyProxyEnvWith(
|
|
|
184
202
|
}
|
|
185
203
|
}
|
|
186
204
|
process.env.NO_PROXY = entries.join(",");
|
|
205
|
+
configureSocks5Fetch();
|
|
187
206
|
}
|
|
188
|
-
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
quotaResetNotifySchema,
|
|
14
14
|
remoteGuiConfigSchema,
|
|
15
15
|
runtimeRoleSchema,
|
|
16
|
+
spendSchema,
|
|
16
17
|
configuredCodexPoolAccountIds,
|
|
17
18
|
apiKeyEntrySchema,
|
|
18
19
|
asideProfileSyncSchema,
|
|
@@ -59,6 +60,8 @@ import { parseDesktopProfile } from "../../claude/desktop-profile";
|
|
|
59
60
|
import { DEFAULT_APP_OWNED_MEMORY_BUDGET_BYTES, MAX_APP_OWNED_MEMORY_BUDGET_MB, MIN_APP_OWNED_MEMORY_BUDGET_MB } from "../../lib/app-owned-memory";
|
|
60
61
|
|
|
61
62
|
export const configSchema = z.object({
|
|
63
|
+
codexNativeSteering: z.boolean().optional().catch(false),
|
|
64
|
+
codexNativeInjection: z.boolean().optional().catch(false),
|
|
62
65
|
port: z.number().int().min(0).max(65535).default(10100),
|
|
63
66
|
// A malformed hand edit must disable only remote-role behavior, not discard
|
|
64
67
|
// providers or data-plane keys. Live writes are rejected explicitly below.
|
|
@@ -164,6 +167,11 @@ export const configSchema = z.object({
|
|
|
164
167
|
quotaResetNotify: quotaResetNotifySchema.optional().catch(undefined),
|
|
165
168
|
// Same rationale: a bad auto-refresh section must not cost the operator their providers.
|
|
166
169
|
catalogAutoRefresh: catalogAutoRefreshSchema.optional().catch(undefined),
|
|
170
|
+
// Same rationale again, with the failure direction stated: a malformed spend section
|
|
171
|
+
// degrades to "no ceiling", which means observe-only accounting rather than an outage. That
|
|
172
|
+
// is the safe degrade for traffic and the dangerous one for the operator, so the write path
|
|
173
|
+
// rejects it and loadConfig warns -- the same pair codexPool uses below.
|
|
174
|
+
spend: spendSchema.optional().catch(undefined),
|
|
167
175
|
// These selections pre-date schema validation and used to pass through as
|
|
168
176
|
// unknown fields. Invalid hand edits must disable only the optional
|
|
169
177
|
// delegation/native-default feature, not reject the whole config and hide
|
|
@@ -180,6 +188,10 @@ export const configSchema = z.object({
|
|
|
180
188
|
codexShimAutoRestore: z.boolean().optional(),
|
|
181
189
|
codexDesktopAuthless: z.boolean().optional().catch(undefined),
|
|
182
190
|
codexClientCompaction: z.boolean().optional().catch(undefined),
|
|
191
|
+
// Presentation-only label for the injected provider. A malformed value degrades to undefined
|
|
192
|
+
// and the default label is emitted, rather than failing the parse or writing a config Codex
|
|
193
|
+
// would refuse to load — the provider id routing depends on is never derived from it.
|
|
194
|
+
codexProviderDisplayName: z.string().trim().min(1).max(128).optional().catch(undefined),
|
|
183
195
|
pausedCodexAccountIds: z.array(z.string().regex(/^[a-zA-Z0-9._-]{1,64}$/)).optional(),
|
|
184
196
|
// A malformed policy degrades to "no policy" rather than failing the parse, so a hand-edited
|
|
185
197
|
// typo cannot trip the backup-and-defaults repair path and wipe providers or pool accounts.
|
|
@@ -462,6 +474,17 @@ export const configSchema = z.object({
|
|
|
462
474
|
message: reasoningSummariesError,
|
|
463
475
|
});
|
|
464
476
|
}
|
|
477
|
+
const suppressSyntheticMaxError = booleanRecordConfigError(
|
|
478
|
+
(provider as { modelSuppressSyntheticMax?: unknown }).modelSuppressSyntheticMax,
|
|
479
|
+
"modelSuppressSyntheticMax",
|
|
480
|
+
);
|
|
481
|
+
if (suppressSyntheticMaxError) {
|
|
482
|
+
ctx.addIssue({
|
|
483
|
+
code: "custom",
|
|
484
|
+
path: ["providers", redactSecretString(name), "modelSuppressSyntheticMax"],
|
|
485
|
+
message: suppressSyntheticMaxError,
|
|
486
|
+
});
|
|
487
|
+
}
|
|
465
488
|
const verbositySupportError = booleanRecordConfigError(
|
|
466
489
|
(provider as { modelSupportsVerbosity?: unknown }).modelSupportsVerbosity,
|
|
467
490
|
"modelSupportsVerbosity",
|
|
@@ -32,7 +32,11 @@ import { getProviderRegistryEntry, providerMatchesRegistryTransport, providerMod
|
|
|
32
32
|
import { resolveOpenAiVirtualModel } from "../../providers/openai-virtual-models";
|
|
33
33
|
import { COST4_RATE_KEYS, isValidCost4Rate } from "../../usage/user-cost-overlays";
|
|
34
34
|
import { MAX_COST4_RATE } from "../../usage/expected-prices";
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
DECLARABLE_HOSTED_TOOL_TYPES,
|
|
37
|
+
declaredUnsupportedHostedTools,
|
|
38
|
+
isHostedToolUnsupportedForModel,
|
|
39
|
+
} from "../../responses/hosted-tool-policy";
|
|
36
40
|
import { getConfigDir } from "../paths";
|
|
37
41
|
|
|
38
42
|
/** One definition of "usable secret", shared by the schema and the warnings. */
|
|
@@ -243,10 +247,12 @@ export const providerConfigSchema = z.object({
|
|
|
243
247
|
chatCompletionsPath: z.string().min(1).optional(),
|
|
244
248
|
statelessResponses: z.boolean().optional(),
|
|
245
249
|
requiresAdjacentResponsesToolResults: z.boolean().optional(),
|
|
250
|
+
requiresPairedResponsesToolResults: z.boolean().optional(),
|
|
246
251
|
annotateEmptyToolOutputs: z.boolean().optional(),
|
|
247
252
|
fastWire: fastWireSchema.nullable().optional(),
|
|
248
253
|
supportsServiceTier: z.boolean().optional(),
|
|
249
254
|
modelSupportsServiceTier: z.record(z.string().min(1), z.boolean()).optional(),
|
|
255
|
+
modelSuppressSyntheticMax: z.record(z.string().min(1), z.boolean()).optional(),
|
|
250
256
|
preserveResponsesReasoningContent: z.boolean().optional(),
|
|
251
257
|
decodesNativeCompactionBlobs: z.boolean().optional(),
|
|
252
258
|
allowEncryptedV2AgentTasks: z.boolean().optional(),
|
|
@@ -274,6 +280,18 @@ export const providerConfigSchema = z.object({
|
|
|
274
280
|
omitReasoningEffortWithToolsModels: z.array(z.string().min(1))
|
|
275
281
|
.transform(normalizeNonBlankStringArray)
|
|
276
282
|
.optional(),
|
|
283
|
+
// Validated against a closed vocabulary rather than accepted as free strings. This
|
|
284
|
+
// schema ends in `.passthrough()`, so a misspelled `web_serch` would otherwise be
|
|
285
|
+
// accepted, persisted, and strip nothing -- leaving the operator with the upstream 400
|
|
286
|
+
// the field was set to prevent, and no message saying why (the `codexToolMode` lesson,
|
|
287
|
+
// #2106).
|
|
288
|
+
unsupportedHostedTools: z.array(z.string().min(1))
|
|
289
|
+
.transform(normalizeNonBlankStringArray)
|
|
290
|
+
.refine(
|
|
291
|
+
tools => tools.every(tool => DECLARABLE_HOSTED_TOOL_TYPES.has(tool)),
|
|
292
|
+
{ message: `unsupportedHostedTools accepts only hosted tool types: ${[...DECLARABLE_HOSTED_TOOL_TYPES].join(", ")}` },
|
|
293
|
+
)
|
|
294
|
+
.optional(),
|
|
277
295
|
retryOn429: retryOn429PolicySchema.optional(),
|
|
278
296
|
transientRetryOn5xx: transientRetryOn5xxPolicySchema.optional(),
|
|
279
297
|
codexAccountMode: z.enum(["pool", "direct"]).optional(),
|
|
@@ -299,21 +317,7 @@ export const providerConfigSchema = z.object({
|
|
|
299
317
|
}).passthrough();
|
|
300
318
|
|
|
301
319
|
|
|
302
|
-
|
|
303
|
-
* Shared shape check for the two relative send-path overrides. `field` names the
|
|
304
|
-
* offending key so the message stays specific to what the user actually wrote.
|
|
305
|
-
*/
|
|
306
|
-
export function providerRelativeSendPathConfigError(field: string, value: string | undefined): string | null {
|
|
307
|
-
if (value === undefined) return null;
|
|
308
|
-
if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(value) || value.includes("://")) {
|
|
309
|
-
return `${field} must be a relative path without a URL scheme`;
|
|
310
|
-
}
|
|
311
|
-
if (!value.startsWith("/")) return `${field} must start with /`;
|
|
312
|
-
if (value.includes("?") || value.includes("#")) {
|
|
313
|
-
return `${field} must not include query strings or fragments`;
|
|
314
|
-
}
|
|
315
|
-
return null;
|
|
316
|
-
}
|
|
320
|
+
export { providerRelativeSendPathConfigError } from "../provider-relative-send-path";
|
|
317
321
|
|
|
318
322
|
/**
|
|
319
323
|
* Validate `providers.<name>.modelCosts`: a plain object keyed by exact model
|
|
@@ -390,7 +394,13 @@ export function modelPreferHostedToolsConfigError(
|
|
|
390
394
|
value: unknown,
|
|
391
395
|
field: string,
|
|
392
396
|
providerName: string,
|
|
393
|
-
provider: {
|
|
397
|
+
provider: {
|
|
398
|
+
adapter?: unknown;
|
|
399
|
+
authMode?: unknown;
|
|
400
|
+
modelAdapters?: unknown;
|
|
401
|
+
baseUrl?: unknown;
|
|
402
|
+
unsupportedHostedTools?: unknown;
|
|
403
|
+
},
|
|
394
404
|
): string | null {
|
|
395
405
|
if (value === undefined) return null;
|
|
396
406
|
if (!value || typeof value !== "object" || Array.isArray(value)) return `${field} must be a plain object`;
|
|
@@ -398,6 +408,12 @@ export function modelPreferHostedToolsConfigError(
|
|
|
398
408
|
if (prototype !== Object.prototype && prototype !== null) return `${field} must be a plain object with own properties`;
|
|
399
409
|
const entries = Object.entries(value);
|
|
400
410
|
const registry = getProviderRegistryEntry(providerName);
|
|
411
|
+
// A provider that denies a hosted tool cannot also prefer it. Both keys are
|
|
412
|
+
// provider-owned capability statements about the same tool, and the denial wins at
|
|
413
|
+
// request time, so accepting the pair would silently ignore the preference.
|
|
414
|
+
const declaredUnsupported = declaredUnsupportedHostedTools(
|
|
415
|
+
provider as { unsupportedHostedTools?: readonly string[] },
|
|
416
|
+
);
|
|
401
417
|
// Effective transport: a `preserveCustomDestination` registry row reused under a
|
|
402
418
|
// different endpoint keeps its own adapter AND its own auth at runtime, because
|
|
403
419
|
// `routedProviderConfig()` honors `providerMatchesRegistryTransport()`. Both the
|
|
@@ -458,6 +474,9 @@ export function modelPreferHostedToolsConfigError(
|
|
|
458
474
|
if (typeof tool !== "string" || !SUPPORTED_PREFERRED_HOSTED_TOOLS.has(tool)) {
|
|
459
475
|
return `${field}.${key} supports only image_generation`;
|
|
460
476
|
}
|
|
477
|
+
if (declaredUnsupported.has(tool)) {
|
|
478
|
+
return `${field}.${key} cannot prefer ${tool}: unsupportedHostedTools declares it unsupported`;
|
|
479
|
+
}
|
|
461
480
|
if (isHostedToolUnsupportedForModel(key, tool)) {
|
|
462
481
|
return `${field}.${key} cannot prefer ${tool}: the model does not support it`;
|
|
463
482
|
}
|
|
@@ -853,3 +872,32 @@ export const catalogAutoRefreshSchema = z.object({
|
|
|
853
872
|
enabled: z.boolean().optional(),
|
|
854
873
|
intervalMinutes: z.number().int().min(0).max(1440).optional(),
|
|
855
874
|
}).strict();
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* One spend scope's ceiling.
|
|
878
|
+
*
|
|
879
|
+
* `.strict()` for the usual reason and one sharper one. Elsewhere a silently ignored key
|
|
880
|
+
* leaves a feature off that the operator believed was on; here it leaves a BUDGET off, and a
|
|
881
|
+
* budget nobody is enforcing looks exactly like a budget nobody has exceeded. That is #2106 --
|
|
882
|
+
* an undeclared option accepted, persisted, and then read as its default -- aimed at spend.
|
|
883
|
+
*
|
|
884
|
+
* Only positive integers: 0 would read as "no tokens at all" and refuse every request under
|
|
885
|
+
* the scope, which is never what writing a budget means. An operator who wants no ceiling
|
|
886
|
+
* removes the key.
|
|
887
|
+
*/
|
|
888
|
+
const spendScopeSchema = z.object({
|
|
889
|
+
maxTokens: z.number().int().positive().optional(),
|
|
890
|
+
}).strict();
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Durable spend ceilings (#4546).
|
|
894
|
+
*
|
|
895
|
+
* Absent, empty, and all-scopes-absent are the same thing: observe-only accounting. There is
|
|
896
|
+
* no default ceiling anywhere in this section, deliberately.
|
|
897
|
+
*/
|
|
898
|
+
export const spendSchema = z.object({
|
|
899
|
+
root: spendScopeSchema.optional(),
|
|
900
|
+
identity: spendScopeSchema.optional(),
|
|
901
|
+
pool: spendScopeSchema.optional(),
|
|
902
|
+
retentionDays: z.number().int().min(1).max(365).optional(),
|
|
903
|
+
}).strict();
|