@bitkyc08/opencodex 2.55.0 → 2.57.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/bin/ocx.mjs +10 -0
- package/gui/dist/assets/{index-BBOZWGB6.css → index-C5-RdDmD.css} +1 -1
- package/gui/dist/assets/{index-VuoiWj9J.js → index-Cz7CLdif.js} +21 -21
- package/gui/dist/index.html +2 -2
- package/package.json +4 -3
- package/src/adapters/base.ts +21 -0
- package/src/adapters/codebuddy/adapter.ts +2 -1
- package/src/adapters/codebuddy/scaffold-guard.ts +248 -0
- package/src/adapters/command-code.ts +1 -1
- package/src/adapters/cursor/envelope-echo.ts +8 -2
- package/src/adapters/cursor/transport-retry.ts +46 -1
- package/src/adapters/cursor.ts +4 -0
- package/src/adapters/google.ts +7 -7
- package/src/adapters/kiro/adapter.ts +42 -1
- package/src/adapters/kiro/payload.ts +17 -3
- package/src/adapters/kiro/reasoning.ts +70 -7
- package/src/adapters/kiro/stream.ts +8 -2
- package/src/adapters/kiro/wire.ts +2 -1
- package/src/adapters/kiro-events.ts +21 -13
- package/src/adapters/kiro-retry.ts +23 -4
- package/src/adapters/openai-chat/errors.ts +116 -0
- package/src/adapters/openai-chat/messages.ts +346 -0
- package/src/adapters/openai-chat/passthrough.ts +146 -0
- package/src/adapters/openai-chat/response-events.ts +117 -0
- package/src/adapters/openai-chat/tool-call-validation.ts +200 -0
- package/src/adapters/openai-chat/tool-name-registry.ts +166 -0
- package/src/adapters/openai-chat/tool-schema.ts +495 -0
- package/src/adapters/openai-chat/wire.ts +50 -0
- package/src/adapters/openai-chat.ts +40 -1452
- package/src/adapters/openai-responses/canonical-forward.ts +202 -0
- package/src/adapters/openai-responses/image-gen.ts +406 -0
- package/src/adapters/openai-responses/internal.ts +3 -0
- package/src/adapters/openai-responses/passthrough.ts +642 -0
- package/src/adapters/openai-responses/prompt-cache.ts +83 -0
- package/src/adapters/openai-responses/reasoning.ts +220 -0
- package/src/adapters/openai-responses/request-strips.ts +185 -0
- package/src/adapters/openai-responses/tool-output-recovery.ts +509 -0
- package/src/adapters/openai-responses/tool-schema.ts +293 -0
- package/src/adapters/openai-responses/web-search.ts +156 -0
- package/src/adapters/openai-responses.ts +4 -2625
- package/src/bridge/errors.ts +58 -0
- package/src/bridge/internal.ts +174 -0
- package/src/bridge/response-json.ts +630 -0
- package/src/bridge/sse.ts +1462 -0
- package/src/bridge.ts +5 -2204
- package/src/chat/inbound.ts +12 -1
- package/src/claude/desktop-profile.ts +66 -9
- package/src/claude/outbound.ts +18 -0
- package/src/cli/account-main.ts +1 -1
- package/src/cli/capabilities.ts +2 -2
- package/src/cli/combo.ts +10 -1
- package/src/cli/index.ts +48 -5
- package/src/cli/registry.ts +2 -1
- package/src/cli/system-command.ts +4 -4
- package/src/clients/config-export.ts +7 -3
- package/src/codex/account-label.ts +14 -3
- package/src/codex/account-lifecycle.ts +3 -0
- package/src/codex/account-store.ts +184 -35
- package/src/codex/account-usability.ts +21 -0
- package/src/codex/auth-api/account-list.ts +507 -0
- package/src/codex/auth-api/http.ts +32 -0
- package/src/codex/auth-api/login-flow.ts +566 -0
- package/src/codex/auth-api/login-state.ts +64 -0
- package/src/codex/auth-api/main-account-probe.ts +331 -0
- package/src/codex/auth-api/pool-mode-gate.ts +274 -0
- package/src/codex/auth-api/pool-quota-probe.ts +512 -0
- package/src/codex/auth-api/reset-credit-service.ts +431 -0
- package/src/codex/auth-api/routes.ts +425 -0
- package/src/codex/auth-api/runtime-config.ts +48 -0
- package/src/codex/auth-api.ts +27 -3118
- package/src/codex/auth-context.ts +252 -35
- package/src/codex/catalog/aggregation.ts +80 -1
- package/src/codex/catalog/auto-review.ts +507 -0
- package/src/codex/catalog/build-entries.ts +981 -0
- package/src/codex/catalog/combo-member.ts +375 -0
- package/src/codex/catalog/derive-entry.ts +229 -0
- package/src/codex/catalog/effort.ts +0 -1
- package/src/codex/catalog/gated-native-warn.ts +63 -0
- package/src/codex/catalog/gather-capture.ts +533 -0
- package/src/codex/catalog/model-hints.ts +691 -0
- package/src/codex/catalog/model-visibility.ts +305 -0
- package/src/codex/catalog/provider-fetch.ts +52 -2942
- package/src/codex/catalog/provider-models.ts +685 -0
- package/src/codex/catalog/remote.ts +30 -0
- package/src/codex/catalog/restore.ts +132 -0
- package/src/codex/catalog/retained-sync.ts +714 -0
- package/src/codex/catalog/routed-gather.ts +895 -0
- package/src/codex/catalog/subagent-roster.ts +176 -0
- package/src/codex/catalog/sync.ts +52 -2698
- package/src/codex/cli-install-provenance.ts +7 -1
- package/src/codex/convergence.ts +7 -2
- package/src/codex/desktop-app/types.ts +11 -2
- package/src/codex/desktop-app/windows.ts +5 -5
- package/src/codex/inject/config-toml.ts +563 -0
- package/src/codex/inject/remove.ts +192 -0
- package/src/codex/inject/restore.ts +567 -0
- package/src/codex/inject/routing-classify.ts +109 -0
- package/src/codex/inject/routing-target.ts +125 -0
- package/src/codex/inject.ts +89 -1444
- package/src/codex/lineage.ts +458 -0
- package/src/codex/model-entitlements.ts +152 -15
- package/src/codex/pool-refresh-backoff.ts +161 -0
- package/src/codex/quota-rejection.ts +104 -15
- package/src/codex/routing/active-account.ts +194 -0
- package/src/codex/routing/cache-affinity.ts +70 -0
- package/src/codex/routing/cooldown-math.ts +285 -0
- package/src/codex/routing/health-store.ts +402 -0
- package/src/codex/routing/probe-lease.ts +358 -0
- package/src/codex/routing/selection.ts +780 -0
- package/src/codex/routing/thread-affinity.ts +586 -0
- package/src/codex/routing/transient-hold-dispatch.ts +141 -0
- package/src/codex/routing.ts +370 -2271
- package/src/codex/shim-fingerprint.ts +223 -0
- package/src/codex/shim-inspect.ts +175 -0
- package/src/codex/shim-probe.ts +367 -0
- package/src/codex/shim-restore-lock.ts +169 -0
- package/src/codex/shim-state-file.ts +151 -0
- package/src/codex/shim-templates.ts +265 -0
- package/src/codex/shim.ts +48 -1268
- package/src/codex/warmup.ts +1 -1
- package/src/combos/failover.ts +85 -0
- package/src/combos/request.ts +17 -10
- package/src/combos/types.ts +23 -2
- package/src/config/diagnostics.ts +705 -0
- package/src/config/feature-flags.ts +55 -0
- package/src/config/live-reconcile.ts +403 -0
- package/src/config/load-degrade.ts +880 -0
- package/src/config/mutation-lock.ts +244 -0
- package/src/config/openai-tier-backup.ts +268 -0
- package/src/config/pending-teardown.ts +31 -0
- package/src/config/persist-unlocked.ts +92 -0
- package/src/config/proxy-env.ts +188 -0
- package/src/config/salvage.ts +244 -0
- package/src/config/schema/config-schema.ts +640 -0
- package/src/config/schema/leaf-validators.ts +855 -0
- package/src/config/warn-memo.ts +28 -0
- package/src/config.ts +234 -4481
- package/src/generated/compatibility-version.json +649 -121
- package/src/images/loop.ts +1 -1
- package/src/lib/errors.ts +17 -0
- package/src/lib/request-execution-budget.ts +198 -23
- package/src/lib/spend-reservation-ledger.ts +958 -0
- package/src/lib/state-store-registrations.ts +6 -2
- package/src/lib/test-home-guard.ts +85 -1
- package/src/lib/upstream-retry.ts +132 -21
- package/src/lib/windows-elevation.ts +76 -14
- package/src/lib/workflow-budget.ts +553 -30
- package/src/oauth/index.ts +2 -2
- package/src/oauth/key-providers.ts +2 -2
- package/src/providers/kiro-models.ts +4 -3
- package/src/providers/label.ts +19 -1
- package/src/providers/model-discovery.ts +16 -0
- package/src/providers/quota/account-cache.ts +441 -0
- package/src/providers/quota/antigravity.ts +295 -0
- package/src/providers/quota/report-cache.ts +320 -0
- package/src/providers/quota/vendor-probes-key.ts +1243 -0
- package/src/providers/quota/vendor-probes-oauth.ts +590 -0
- package/src/providers/quota.ts +324 -3079
- package/src/providers/registry/entries-core.ts +1228 -0
- package/src/providers/registry/entries-extended.ts +1213 -0
- package/src/providers/registry/model-seeds.ts +912 -0
- package/src/providers/registry/types.ts +352 -0
- package/src/providers/registry.ts +24 -3536
- package/src/responses/continuation-ownership.ts +29 -0
- package/src/responses/reasoning-envelope.ts +6 -3
- package/src/responses/state/replay-fingerprint.ts +80 -0
- package/src/responses/state/snapshot-codec.ts +104 -0
- package/src/responses/state/spill-failure.ts +118 -0
- package/src/responses/state/spill-queue.ts +665 -0
- package/src/responses/state/temp-recovery.ts +257 -0
- package/src/responses/state.ts +82 -1143
- package/src/routing/identity-domains.ts +456 -0
- package/src/routing/probe-lease.ts +613 -0
- package/src/server/chat-completions.ts +3 -1
- package/src/server/chat-native.ts +37 -9
- package/src/server/index/bounded-request.ts +88 -0
- package/src/server/index/live-sideband.ts +601 -0
- package/src/server/index/serve-options.ts +1766 -0
- package/src/server/index/startup-warnings.ts +213 -0
- package/src/server/index/websocket-handler.ts +339 -0
- package/src/server/index.ts +45 -2552
- package/src/server/inspection-tee.ts +107 -0
- package/src/server/live.ts +46 -1
- package/src/server/management/combo-routes.ts +10 -1
- package/src/server/management/route-registry.ts +26 -23
- package/src/server/management/shared.ts +8 -5
- package/src/server/management/workflow-budget-routes.ts +133 -0
- package/src/server/management-api.ts +12 -0
- package/src/server/relay-eager.ts +2 -0
- package/src/server/relay.ts +14 -19
- package/src/server/request-log-conversation.ts +9 -7
- package/src/server/request-log.ts +372 -4
- package/src/server/response-log-body.ts +153 -0
- package/src/server/responses/account-change-state.ts +307 -0
- package/src/server/responses/adapter-continuation.ts +540 -0
- package/src/server/responses/adapter-delivery.ts +208 -0
- package/src/server/responses/adapter-dispatch.ts +1042 -0
- package/src/server/responses/codex-ws-wire.ts +5 -0
- package/src/server/responses/collaboration.ts +74 -4
- package/src/server/responses/combo-session-recall.ts +68 -8
- package/src/server/responses/compact.ts +113 -17
- package/src/server/responses/completion-policy.ts +33 -0
- package/src/server/responses/core-auth.ts +529 -0
- package/src/server/responses/core-codex-account.ts +907 -0
- package/src/server/responses/core-combo-failure.ts +210 -0
- package/src/server/responses/core-combo.ts +787 -0
- package/src/server/responses/core-errors.ts +170 -0
- package/src/server/responses/core-lifetime.ts +95 -0
- package/src/server/responses/core-normalize.ts +350 -0
- package/src/server/responses/core-opaque-recovery.ts +380 -0
- package/src/server/responses/core-options.ts +159 -0
- package/src/server/responses/core-replay.ts +298 -0
- package/src/server/responses/core.ts +192 -8893
- package/src/server/responses/encrypted-payload.ts +0 -1
- package/src/server/responses/input-admission.ts +126 -6
- package/src/server/responses/passthrough-delivery.ts +869 -0
- package/src/server/responses/passthrough-dispatch.ts +1494 -0
- package/src/server/responses/passthrough-error.ts +38 -2
- package/src/server/responses/passthrough-execution.ts +54 -0
- package/src/server/responses/request-prepare.ts +1080 -0
- package/src/server/responses/request-send-budget.ts +259 -0
- package/src/server/responses/request-sidecar-auth.ts +149 -0
- package/src/server/responses/request-spend.ts +147 -0
- package/src/server/responses/request-transport.ts +803 -0
- package/src/server/responses/response-effects.ts +157 -0
- package/src/server/responses/run-turn-execution.ts +476 -0
- package/src/server/responses/sidecar-execution.ts +463 -0
- package/src/server/responses/terminal-guard.ts +65 -4
- package/src/server/responses-image-gen-repair.ts +1 -1
- package/src/server/responses-undeclared-tool-guard.ts +9 -5
- package/src/server/workflow-refusal.ts +84 -0
- package/src/service/windows-ops.ts +210 -16
- package/src/service/windows-scheduler.ts +28 -21
- package/src/service.ts +1 -1
- package/src/types/config.ts +34 -1
- package/src/types/request.ts +8 -5
- package/src/types/tools.ts +24 -0
- package/src/types.ts +2 -0
- package/src/update/index.ts +10 -0
- package/src/update/stop-contract.d.mts +1 -0
- package/src/update/stop-contract.mjs +19 -0
- package/src/update/stop-decision.d.mts +1 -1
- package/src/update/stop-decision.mjs +12 -3
- package/src/usage/log.ts +147 -1
- package/src/usage/summary.ts +171 -21
- package/src/vision/anthropic-describe.ts +1 -1
- package/src/vision/describe.ts +5 -5
- package/src/web-search/anthropic-executor.ts +1 -1
- package/src/web-search/exa-executor.ts +1 -1
- package/src/web-search/executor.ts +1 -1
- package/src/web-search/gemini-executor.ts +1 -1
- package/src/web-search/loop.ts +1 -1
- package/src/web-search/ollama-executor.ts +1 -1
- package/src/web-search/parse.ts +67 -14
- package/src/web-search/passthrough-bridge.ts +64 -31
- package/src/web-search/xai-executor.ts +1 -1
package/src/codex/warmup.ts
CHANGED
|
@@ -44,7 +44,7 @@ async function drainErrorBody(res: Response, signal: AbortSignal): Promise<void>
|
|
|
44
44
|
fatalUtf8: true,
|
|
45
45
|
});
|
|
46
46
|
} catch (error) {
|
|
47
|
-
if (signal.aborted) {
|
|
47
|
+
if (signal.aborted && res.status !== 429) {
|
|
48
48
|
throw new CodexWarmupError("transport", "Codex warmup request failed", {
|
|
49
49
|
cause: error,
|
|
50
50
|
});
|
package/src/combos/failover.ts
CHANGED
|
@@ -401,11 +401,15 @@ export function comboFailureCooldownScope(
|
|
|
401
401
|
): ComboFailureCooldownScope {
|
|
402
402
|
const code = normalizedFailureCode(options?.code);
|
|
403
403
|
// Request-shape refusals first: an oversized request must not cool a healthy target.
|
|
404
|
+
// A native transport can surface a zero-output model overflow as a generic
|
|
405
|
+
// upstream_server_error carrying precise context-window prose, so consult the bounded
|
|
406
|
+
// message classifier too: that target is healthy, the turn was simply too large for it.
|
|
404
407
|
if (
|
|
405
408
|
status === 413
|
|
406
409
|
|| REQUEST_SHAPE_FAILURE_CODES.has(code)
|
|
407
410
|
|| isRequestLocalFreePromptCap(status, message, options?.code)
|
|
408
411
|
|| isProviderTargetContextOverflow(status, message, options?.code)
|
|
412
|
+
|| isDefiniteContextOverflow(status, message)
|
|
409
413
|
|| isRequestLocalTargetIncompatibility(status, message, options?.code)
|
|
410
414
|
) return "none";
|
|
411
415
|
if (isProviderScopedQuotaCap(status, message, options?.code)) return "provider";
|
|
@@ -451,6 +455,74 @@ function isProviderTargetContextOverflow(
|
|
|
451
455
|
&& /\bprompt\s+\d+\s*>\s*\d+\s+maximum context length\b/i.test(message);
|
|
452
456
|
}
|
|
453
457
|
|
|
458
|
+
/** A status can carry a verdict about the REQUEST; 401/403/429 speak about the credential. */
|
|
459
|
+
const CONTEXT_VERDICT_STATUSES: ReadonlySet<number> = new Set([400, 413, 422]);
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Phrases a provider emits when the INPUT does not fit this model's context window. Matched
|
|
463
|
+
* against the innermost provider message only, so an unrelated refusal that merely quotes one
|
|
464
|
+
* of these tokens in a code field cannot authorize a replay.
|
|
465
|
+
*/
|
|
466
|
+
const DEFINITE_CONTEXT_OVERFLOW_PHRASES = [
|
|
467
|
+
"exceeds the context window",
|
|
468
|
+
"exceed the context window",
|
|
469
|
+
"context window exceeded",
|
|
470
|
+
"context length exceeded",
|
|
471
|
+
"maximum context length",
|
|
472
|
+
"maximum context window",
|
|
473
|
+
"too many tokens",
|
|
474
|
+
];
|
|
475
|
+
|
|
476
|
+
/** Wrapper envelopes unwrapped before the leaf message is read. */
|
|
477
|
+
const MAX_CONTEXT_OVERFLOW_ENVELOPES = 4;
|
|
478
|
+
|
|
479
|
+
function isDefiniteContextOverflowMessage(text: string): boolean {
|
|
480
|
+
const normalized = text.toLowerCase();
|
|
481
|
+
return normalized === "context_length_exceeded"
|
|
482
|
+
|| DEFINITE_CONTEXT_OVERFLOW_PHRASES.some(phrase => normalized.includes(phrase));
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Confirm a context overflow from the provider MESSAGE rather than from a code token that
|
|
487
|
+
* merely appears somewhere in the envelope. An upstream controls both fields and can emit a
|
|
488
|
+
* contradictory pair -- `context_length_exceeded` beside `Unsupported parameter: user` -- and
|
|
489
|
+
* that is not evidence the turn is too large for this model. `classifyError` reads the whole
|
|
490
|
+
* blob, which is exactly the looseness this must not inherit.
|
|
491
|
+
*
|
|
492
|
+
* A JSON-shaped body that fails to parse is truncated or corrupt, not prose: `classificationText`
|
|
493
|
+
* is capped at 500 characters by `normalizeUpstreamErrorText` before it reaches this function, so
|
|
494
|
+
* a long envelope arrives here as a JSON prefix. Reading that prefix as plain text would let an
|
|
495
|
+
* arbitrary field that happens to sit in the first 500 bytes authorize a hop, so it fails closed.
|
|
496
|
+
*
|
|
497
|
+
* Only the exact proxy wrapper is unwrapped, within a fixed envelope budget and 16,384 characters.
|
|
498
|
+
*/
|
|
499
|
+
function isDefiniteContextOverflow(status: number, message: string): boolean {
|
|
500
|
+
if (!CONTEXT_VERDICT_STATUSES.has(status) && status < 500) return false;
|
|
501
|
+
if (message.length > 16_384) return false;
|
|
502
|
+
let text = message.trim();
|
|
503
|
+
// One pass per unwrapped envelope, plus one for the leaf the last envelope yields.
|
|
504
|
+
for (let unwrapped = 0; unwrapped <= MAX_CONTEXT_OVERFLOW_ENVELOPES; unwrapped += 1) {
|
|
505
|
+
const providerPrefix = /^Provider error \d{3}:\s*/.exec(text);
|
|
506
|
+
if (providerPrefix) text = text.slice(providerPrefix[0].length).trim();
|
|
507
|
+
if (!text.startsWith("{")) return isDefiniteContextOverflowMessage(text);
|
|
508
|
+
if (unwrapped === MAX_CONTEXT_OVERFLOW_ENVELOPES) return false;
|
|
509
|
+
let payload: unknown;
|
|
510
|
+
try { payload = JSON.parse(text); } catch { return false; }
|
|
511
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return false;
|
|
512
|
+
const record = payload as Record<string, unknown>;
|
|
513
|
+
const response = record.response && typeof record.response === "object" && !Array.isArray(record.response)
|
|
514
|
+
? record.response as Record<string, unknown>
|
|
515
|
+
: undefined;
|
|
516
|
+
const source = [record.error, response?.error, response?.last_error, record.last_error, record]
|
|
517
|
+
.find((candidate): candidate is Record<string, unknown> =>
|
|
518
|
+
!!candidate && typeof candidate === "object" && !Array.isArray(candidate)
|
|
519
|
+
&& typeof (candidate as Record<string, unknown>).message === "string");
|
|
520
|
+
if (!source) return false;
|
|
521
|
+
text = (source.message as string).trim();
|
|
522
|
+
}
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
|
|
454
526
|
export function comboFailureDecision(
|
|
455
527
|
status: number,
|
|
456
528
|
message: string,
|
|
@@ -458,6 +530,10 @@ export function comboFailureDecision(
|
|
|
458
530
|
): ComboFailureDecision {
|
|
459
531
|
if (status === 499) return "stop";
|
|
460
532
|
if (message.toLowerCase().includes("origin_rejected")) return "stop";
|
|
533
|
+
// Structured form of the same hard refusal. The prose test above misses it when the origin
|
|
534
|
+
// reports the code out of band, and every hop rule below -- including the context-overflow
|
|
535
|
+
// one -- must stay subordinate to it.
|
|
536
|
+
if (normalizedFailureCode(options?.code) === "origin_rejected") return "stop";
|
|
461
537
|
// The origin may already be executing this turn (the Codex WebSocket relay sent the create
|
|
462
538
|
// frame and never saw a response event). Hopping would send the same request to a second
|
|
463
539
|
// target while the first may still be generating; the honest status goes to the client.
|
|
@@ -476,6 +552,15 @@ export function comboFailureDecision(
|
|
|
476
552
|
// (for example 5059 + invalid_request_prompt_too_long). That is evidence that this
|
|
477
553
|
// target is too small, not that every later combo target is incapable of serving it.
|
|
478
554
|
if (isProviderTargetContextOverflow(status, message, options?.code)) return "hop";
|
|
555
|
+
// A definite context-window refusal is target-local inside a heterogeneous combo: this model
|
|
556
|
+
// cannot hold the turn, but a later target may have a larger window. Two boundaries keep this
|
|
557
|
+
// safe. It is reached only after cancellation, structured origin/cyber refusals and
|
|
558
|
+
// non-replayable post-send codes have already stopped. And it only ever classifies a failure
|
|
559
|
+
// the combo stream preflight already proved emitted no output: `comboStreamPayloadCommitsOutput`
|
|
560
|
+
// commits the child on any text, tool call or unknown event, and only a zero-output terminal
|
|
561
|
+
// becomes a failure response at all, so a turn whose text the client already saw is never
|
|
562
|
+
// reclassified here.
|
|
563
|
+
if (isDefiniteContextOverflow(status, message)) return "hop";
|
|
479
564
|
// A local input-admission refusal (#1524) says "this candidate cannot fit the request",
|
|
480
565
|
// not "the request is impossible": the next candidate may have a larger context window.
|
|
481
566
|
//
|
package/src/combos/request.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { OcxComboDefaultEffort, OcxComboReasoningEffortMode, OcxComboTarget, OcxConfig } from "../types";
|
|
2
|
-
import { resolveEffortAtOrBelow } from "../reasoning-effort";
|
|
1
|
+
import type { OcxComboDefaultEffort, OcxComboDefaultEffortMode, OcxComboReasoningEffortMode, OcxComboTarget, OcxConfig } from "../types";
|
|
2
|
+
import { isCodexReasoningEffort, resolveEffortAtOrBelow } from "../reasoning-effort";
|
|
3
3
|
import { resolveComboId } from "./types";
|
|
4
4
|
|
|
5
5
|
const warnedUnsupportedDefaults = new Set<string>();
|
|
@@ -60,22 +60,29 @@ export function concreteComboRequestBody(
|
|
|
60
60
|
defaultEffort: OcxComboDefaultEffort | null,
|
|
61
61
|
targetReasoningEfforts: readonly string[] | undefined,
|
|
62
62
|
reasoningEffortMode: OcxComboReasoningEffortMode = "strict",
|
|
63
|
+
defaultEffortMode: OcxComboDefaultEffortMode = "fallback",
|
|
63
64
|
): Record<string, unknown> {
|
|
64
65
|
const clone = structuredClone(body) as Record<string, unknown>;
|
|
65
66
|
clone.model = `${target.provider}/${target.model}`;
|
|
67
|
+
if (defaultEffortMode === "force" && (!defaultEffort || !isCodexReasoningEffort(defaultEffort))) {
|
|
68
|
+
throw new Error("force combo default effort requires a valid defaultEffort");
|
|
69
|
+
}
|
|
66
70
|
if (targetReasoningEfforts?.length === 0
|
|
67
71
|
|| (reasoningEffortMode === "adaptive" && targetReasoningEfforts === undefined)) {
|
|
68
72
|
stripUnsupportedReasoningControls(clone);
|
|
69
73
|
}
|
|
70
|
-
if (!defaultEffort) return clone;
|
|
74
|
+
if (!defaultEffort || !isCodexReasoningEffort(defaultEffort)) return clone;
|
|
71
75
|
const reasoning = clone.reasoning;
|
|
72
|
-
const
|
|
73
|
-
reasoning
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
&&
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
const reasoningRecord = reasoning && typeof reasoning === "object" && !Array.isArray(reasoning)
|
|
77
|
+
? reasoning as Record<string, unknown>
|
|
78
|
+
: undefined;
|
|
79
|
+
const hasEffort = reasoningRecord !== undefined
|
|
80
|
+
&& Object.prototype.hasOwnProperty.call(reasoningRecord, "effort");
|
|
81
|
+
const callerEffort = reasoningRecord?.effort;
|
|
82
|
+
const validCallerEffort = typeof callerEffort === "string" && isCodexReasoningEffort(callerEffort);
|
|
83
|
+
const needsDefault = reasoning === undefined || (reasoningRecord !== undefined && !hasEffort);
|
|
84
|
+
const shouldForce = defaultEffortMode === "force" && validCallerEffort;
|
|
85
|
+
if (!needsDefault && !shouldForce) return clone;
|
|
79
86
|
// Picker availability treats an unknown ladder as a wildcard, but runtime
|
|
80
87
|
// injection stays fail-closed until this concrete target advertises support.
|
|
81
88
|
//
|
package/src/combos/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isCodexReasoningEffort } from "../reasoning-effort";
|
|
2
2
|
import { SUPPORTED_NATIVE_OPENAI_SLUGS } from "../codex/catalog/native-models";
|
|
3
|
-
import type { OcxComboConfig, OcxComboDefaultEffort, OcxComboReasoningEffortMode, OcxComboStrategy, OcxComboTarget, OcxProviderConfig } from "../types";
|
|
3
|
+
import type { OcxComboConfig, OcxComboDefaultEffort, OcxComboDefaultEffortMode, OcxComboReasoningEffortMode, OcxComboStrategy, OcxComboTarget, OcxProviderConfig } from "../types";
|
|
4
4
|
import { COMBO_NAMESPACE, isValidComboId, targetKey } from "./identifiers";
|
|
5
5
|
|
|
6
6
|
export const COMBO_DEFAULT_WAIT_FOR_COOLDOWN_MS = 0;
|
|
@@ -26,6 +26,8 @@ export interface NormalizedComboConfig {
|
|
|
26
26
|
cooldownMs?: number;
|
|
27
27
|
waitForCooldownMs: number;
|
|
28
28
|
defaultEffort: OcxComboDefaultEffort | null;
|
|
29
|
+
/** Client-precedence policy; `fallback` preserves legacy behavior. */
|
|
30
|
+
defaultEffortMode: OcxComboDefaultEffortMode;
|
|
29
31
|
/** Picker-ladder derivation policy; `strict` preserves the legacy intersection rule. */
|
|
30
32
|
reasoningEffortMode: OcxComboReasoningEffortMode;
|
|
31
33
|
/** Disable image input; `auto` preserves the intersection derived from all targets. */
|
|
@@ -167,6 +169,21 @@ export function comboConfigIssues(
|
|
|
167
169
|
message: "defaultEffort must be one of: low, medium, high, xhigh, max, ultra",
|
|
168
170
|
});
|
|
169
171
|
}
|
|
172
|
+
if (body.defaultEffortMode !== undefined
|
|
173
|
+
&& body.defaultEffortMode !== "fallback"
|
|
174
|
+
&& body.defaultEffortMode !== "force") {
|
|
175
|
+
issues.push({
|
|
176
|
+
path: ["defaultEffortMode"],
|
|
177
|
+
message: 'defaultEffortMode must be "fallback" or "force"',
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (body.defaultEffortMode === "force"
|
|
181
|
+
&& (typeof body.defaultEffort !== "string" || !isCodexReasoningEffort(body.defaultEffort))) {
|
|
182
|
+
issues.push({
|
|
183
|
+
path: ["defaultEffort"],
|
|
184
|
+
message: "defaultEffort is required when defaultEffortMode is force",
|
|
185
|
+
});
|
|
186
|
+
}
|
|
170
187
|
if (body.imageInput !== undefined && body.imageInput !== "auto" && body.imageInput !== "disabled") {
|
|
171
188
|
issues.push({ path: ["imageInput"], message: 'imageInput must be "auto" or "disabled"' });
|
|
172
189
|
}
|
|
@@ -293,12 +310,16 @@ export function comboConfigError(
|
|
|
293
310
|
export function normalizeComboConfig(raw: OcxComboConfig): NormalizedComboConfig {
|
|
294
311
|
const alias = typeof raw.alias === "string" ? raw.alias.trim() : "";
|
|
295
312
|
const displayName = typeof raw.displayName === "string" ? raw.displayName.trim() : "";
|
|
313
|
+
const defaultEffort = typeof raw.defaultEffort === "string" && isCodexReasoningEffort(raw.defaultEffort)
|
|
314
|
+
? raw.defaultEffort
|
|
315
|
+
: null;
|
|
296
316
|
return {
|
|
297
317
|
strategy: raw.strategy ?? "failover",
|
|
298
318
|
stickyLimit: raw.stickyLimit ?? 1,
|
|
299
319
|
cooldownMs: raw.cooldownMs,
|
|
300
320
|
waitForCooldownMs: raw.waitForCooldownMs ?? COMBO_DEFAULT_WAIT_FOR_COOLDOWN_MS,
|
|
301
|
-
defaultEffort
|
|
321
|
+
defaultEffort,
|
|
322
|
+
defaultEffortMode: raw.defaultEffortMode === "force" && defaultEffort !== null ? "force" : "fallback",
|
|
302
323
|
reasoningEffortMode: raw.reasoningEffortMode === "adaptive" ? "adaptive" : "strict",
|
|
303
324
|
imageInput: raw.imageInput === "disabled" ? "disabled" : "auto",
|
|
304
325
|
alias: alias || null,
|