@yansigit/opencodex 2.33.0 → 2.35.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 +3 -3
- package/gui/dist/assets/index-BjCaHxdz.js +112 -0
- package/gui/dist/assets/index-DLkXOXLC.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic.ts +79 -2
- package/src/adapters/command-code.ts +141 -23
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/checkpoint-store.ts +15 -10
- package/src/adapters/cursor/discovery.ts +60 -2
- package/src/adapters/cursor/effort-map.ts +79 -1
- package/src/adapters/cursor/envelope-echo.ts +162 -0
- package/src/adapters/cursor/live-models.ts +7 -2
- package/src/adapters/cursor/live-transport.ts +17 -1
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-fs.ts +13 -12
- package/src/adapters/cursor/native-exec-network.ts +3 -5
- package/src/adapters/cursor/native-exec-policy.ts +47 -0
- package/src/adapters/cursor/native-exec-shell.ts +116 -31
- package/src/adapters/cursor/native-exec.ts +38 -10
- package/src/adapters/cursor/protobuf-events.ts +28 -2
- package/src/adapters/cursor/protobuf-request.ts +93 -41
- package/src/adapters/cursor/request-builder.ts +39 -10
- package/src/adapters/cursor/tool-definitions.ts +27 -3
- package/src/adapters/cursor/tool-result-normalize.ts +51 -6
- package/src/adapters/cursor/types.ts +23 -4
- package/src/adapters/cursor.ts +170 -29
- package/src/adapters/google-aistudio-parser.ts +49 -0
- package/src/adapters/google-antigravity-replay.ts +105 -25
- package/src/adapters/google-antigravity-wire.ts +5 -0
- package/src/adapters/google-errors.ts +41 -12
- package/src/adapters/google-http.ts +12 -11
- package/src/adapters/google.ts +219 -36
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +15 -0
- package/src/adapters/kiro-tools.ts +43 -15
- package/src/adapters/kiro.ts +54 -9
- package/src/adapters/openai-chat.ts +286 -242
- package/src/adapters/openai-responses.ts +335 -24
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-catalog-nudge.ts +2 -2
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/bridge.ts +67 -26
- package/src/chat/inbound.ts +29 -1
- package/src/chat/outbound.ts +15 -7
- package/src/claude/agents-inject.ts +8 -1
- package/src/claude/outbound.ts +10 -8
- package/src/cli/account-api.ts +27 -7
- package/src/cli/account-extended.ts +10 -3
- package/src/cli/account.ts +29 -5
- package/src/cli/alias.ts +66 -0
- package/src/cli/claude.ts +26 -1
- package/src/cli/dispatch.ts +13 -1
- package/src/cli/help.ts +1 -0
- package/src/cli/index.ts +6 -1
- package/src/cli/init.ts +1 -0
- package/src/cli/models-runtime.ts +95 -0
- package/src/cli/models.ts +13 -7
- package/src/cli/provider-runtime.ts +16 -2
- package/src/cli/registry.ts +6 -1
- package/src/cli/telemetry-commands.ts +25 -0
- package/src/cli/v2.ts +34 -10
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +3 -2
- package/src/codex/app-server-processes.ts +80 -6
- package/src/codex/auth-api.ts +48 -8
- package/src/codex/auth-context.ts +21 -18
- package/src/codex/catalog/aggregation.ts +6 -0
- package/src/codex/catalog/model-metadata.ts +13 -1
- package/src/codex/catalog/native-models.ts +5 -2
- package/src/codex/catalog/parsing.ts +16 -0
- package/src/codex/catalog/provider-fetch.ts +20 -3
- package/src/codex/catalog/sync.ts +127 -2
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-write-lock.ts +3 -1
- package/src/codex/convergence-types.ts +1 -1
- package/src/codex/convergence.ts +22 -2
- package/src/codex/desired-state.ts +2 -2
- package/src/codex/desktop-app-restart.ts +18 -5
- package/src/codex/inject-coordination.ts +83 -0
- package/src/codex/inject.ts +14 -1
- package/src/codex/log-guard/inspect.ts +22 -4
- package/src/codex/model-entitlements.ts +9 -2
- package/src/codex/prompt-layers.ts +371 -25
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota.ts +123 -18
- package/src/codex/routing.ts +9 -0
- package/src/codex/subagent-model-fallback.ts +198 -27
- package/src/codex/transition-state.ts +107 -8
- package/src/combos/types.ts +10 -0
- package/src/compatibility/openai-responses.ts +33 -1
- package/src/config/autonomous-remediation.ts +21 -0
- package/src/config/provider-validation.ts +14 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +191 -17
- package/src/generated/compatibility-version.json +279 -159
- package/src/generated/model-metadata.ts +3 -0
- package/src/images/loop.ts +5 -4
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +1 -1
- package/src/lab/fabric/producer-child.ts +1 -1
- package/src/lib/config-ownership.ts +20 -0
- package/src/lib/errors.ts +11 -2
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/oauth/aistudio-credentials.ts +65 -0
- package/src/oauth/aistudio-native-daemon.ts +116 -0
- package/src/oauth/aistudio-session-sync.ts +95 -0
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/google-aistudio-auth.ts +98 -0
- package/src/oauth/index.ts +57 -5
- package/src/oauth/key-providers.ts +18 -1
- package/src/oauth/kiro.ts +45 -0
- package/src/oauth/login-cli.ts +65 -1
- package/src/oauth/types.ts +15 -0
- package/src/providers/codex-capacity.ts +5 -2
- package/src/providers/command-code-efforts.ts +38 -6
- package/src/providers/context-cap.ts +4 -3
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +29 -1
- package/src/providers/fastwire.ts +7 -1
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/provider-id-rewrite.ts +2 -1
- package/src/providers/quota.ts +157 -46
- package/src/providers/registry.ts +184 -71
- package/src/providers/slug-codec.ts +52 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/custom-tool-compat.ts +34 -10
- package/src/responses/parser.ts +4 -0
- package/src/responses/schema.ts +5 -1
- package/src/responses/thought-signature-replay.ts +17 -0
- package/src/router.ts +43 -2
- package/src/routing/account-pool/cooldown.ts +8 -0
- package/src/routing/account-pool/index.ts +1 -0
- package/src/routing/analytics.ts +1 -0
- package/src/routing/quota.ts +10 -0
- package/src/server/auth-cors.ts +24 -0
- package/src/server/chat-completions.ts +26 -16
- package/src/server/chat-native-sse.ts +3 -3
- package/src/server/chat-native.ts +30 -11
- package/src/server/claude-messages.ts +1 -1
- package/src/server/effort-policy.ts +16 -0
- package/src/server/index.ts +180 -14
- package/src/server/lifecycle.ts +52 -1
- package/src/server/management/agent-settings-routes.ts +31 -15
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +2 -1
- package/src/server/management/config-routes.ts +27 -9
- package/src/server/management/context.ts +9 -0
- package/src/server/management/logs-usage-routes.ts +11 -5
- package/src/server/management/model-routes.ts +266 -0
- package/src/server/management/oauth-account-routes.ts +13 -3
- package/src/server/management/provider-routes.ts +137 -3
- package/src/server/management/routing-profile-routes.ts +2 -2
- package/src/server/management-api.ts +2 -0
- package/src/server/port-reclaim.ts +19 -1
- package/src/server/relay-eager.ts +147 -20
- package/src/server/relay.ts +251 -19
- package/src/server/request-log-conversation.ts +33 -0
- package/src/server/request-log.ts +48 -21
- package/src/server/responses/collaboration.ts +42 -5
- package/src/server/responses/combo-stream-preflight.ts +10 -3
- package/src/server/responses/core.ts +575 -140
- package/src/server/responses/empty-completion-guard.ts +35 -0
- package/src/server/responses/fetch-helpers.ts +14 -6
- package/src/server/responses/input-admission.ts +3 -1
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/policy-fallback.ts +1 -1
- package/src/server/responses/responses-field-backfill.ts +105 -13
- package/src/server/responses/ws-upstream.ts +35 -5
- package/src/server/responses-custom-tool-repair.ts +52 -7
- package/src/server/responses-terminal-repair.ts +25 -4
- package/src/server/sse-frame-buffer.ts +31 -4
- package/src/server/ws-bridge.ts +14 -2
- package/src/smoke/fingerprint-cache.ts +133 -0
- package/src/smoke/live-scenarios.ts +33 -0
- package/src/smoke/runner.ts +119 -0
- package/src/telemetry/dispatcher.ts +44 -0
- package/src/telemetry/fingerprint.ts +24 -0
- package/src/telemetry/hook.ts +43 -0
- package/src/telemetry/ledger.ts +54 -0
- package/src/telemetry/types.ts +23 -0
- package/src/types/config.ts +66 -14
- package/src/types/provider.ts +79 -1
- package/src/types/request.ts +18 -10
- package/src/types/tools.ts +30 -11
- package/src/types.ts +1 -0
- package/src/usage/command-code-manifest.ts +116 -0
- package/src/usage/cost.ts +2 -2
- package/src/usage/expected-prices.ts +126 -24
- package/src/usage/log.ts +18 -8
- package/src/usage/summary.ts +34 -12
- package/src/web-search/exa-executor.ts +40 -9
- package/src/web-search/index.ts +16 -8
- package/src/web-search/loop.ts +5 -4
- package/gui/dist/assets/index-DKLr4LTE.js +0 -102
- package/gui/dist/assets/index-DrSQdTRd.css +0 -1
package/src/types/config.ts
CHANGED
|
@@ -255,8 +255,19 @@ export interface OcxSubagentRole {
|
|
|
255
255
|
enabled?: boolean;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
export interface OcxConfigRebaseProvenance {
|
|
259
|
+
version: 1;
|
|
260
|
+
deletedTopLevelKeys: string[];
|
|
261
|
+
}
|
|
262
|
+
|
|
258
263
|
export interface OcxConfig {
|
|
259
264
|
port: number;
|
|
265
|
+
autonomousRemediation?: {
|
|
266
|
+
enabled?: boolean;
|
|
267
|
+
instanceId?: string;
|
|
268
|
+
threshold?: number;
|
|
269
|
+
rollingWindowMs?: number;
|
|
270
|
+
};
|
|
260
271
|
/** Opt in to one identical-turn retry when a Responses completion has no text or tool call. */
|
|
261
272
|
emptyCompletionRetry?: boolean;
|
|
262
273
|
/**
|
|
@@ -276,6 +287,22 @@ export interface OcxConfig {
|
|
|
276
287
|
managementUsageMaxReadBytes?: number;
|
|
277
288
|
providers: Record<string, OcxProviderConfig>;
|
|
278
289
|
defaultProvider: string;
|
|
290
|
+
/** Persisted state for newly discovered provider models (#2464). Absent keeps legacy "on" behavior. */
|
|
291
|
+
modelDiscovery?: {
|
|
292
|
+
newModelPolicy?: "on" | "off";
|
|
293
|
+
knownModels?: Record<string, {
|
|
294
|
+
ids: string[];
|
|
295
|
+
removed: string[];
|
|
296
|
+
updatedAt: string;
|
|
297
|
+
/** Consecutive successful discoveries in which an active id was absent. */
|
|
298
|
+
missing?: Record<string, number>;
|
|
299
|
+
}>;
|
|
300
|
+
recentArrivals?: Record<string, Array<{ id: string; at: string }>>;
|
|
301
|
+
};
|
|
302
|
+
/** Enable the shipped model alias patterns for providers without an override. */
|
|
303
|
+
defaultModelAliases?: boolean;
|
|
304
|
+
/** Explicit top-level deletion intent used by stale whole-config rebases. */
|
|
305
|
+
configRebaseProvenance?: OcxConfigRebaseProvenance | Record<string, unknown>;
|
|
279
306
|
/** OpenAI provider-contract migration marker (v2 = single `openai` provider with account mode). */
|
|
280
307
|
openaiProviderTierVersion?: 1 | 2;
|
|
281
308
|
/** One-time migration marker for Antigravity's static-catalog defaults. */
|
|
@@ -294,12 +321,7 @@ export interface OcxConfig {
|
|
|
294
321
|
* into a selector-qualified group; Codex still advertises only the first 5 visible rows.
|
|
295
322
|
*/
|
|
296
323
|
subagentModels?: string[];
|
|
297
|
-
/**
|
|
298
|
-
* Named specialist roles the parent may spawn: id, when-to-use description,
|
|
299
|
-
* model, optional effort, and child developer instructions. Max 8 roles and
|
|
300
|
-
* 5 unique enabled models (the spawn picker window). Do not store
|
|
301
|
-
* `model_fallback` here — use `subagentModelFallbackByModel`.
|
|
302
|
-
*/
|
|
324
|
+
/** Named specialist roles the parent may spawn by id. */
|
|
303
325
|
subagentRoles?: OcxSubagentRole[];
|
|
304
326
|
/**
|
|
305
327
|
* Project enabled roles into marker-owned `$CODEX_HOME/agents/ocx-<id>.toml`.
|
|
@@ -338,6 +360,10 @@ export interface OcxConfig {
|
|
|
338
360
|
* reject the whole role file as an unknown field (#1190).
|
|
339
361
|
*/
|
|
340
362
|
subagentModelFallbackByModel?: Record<string, string[]>;
|
|
363
|
+
/**
|
|
364
|
+
* Ordered candidates for spawned sub-agents, globally or keyed by role/model.
|
|
365
|
+
*/
|
|
366
|
+
subagentCandidates?: string[] | Record<string, string[]>;
|
|
341
367
|
/**
|
|
342
368
|
* TTL (ms) for cached sub-agent model availability probes. Default 60_000.
|
|
343
369
|
*/
|
|
@@ -402,9 +428,7 @@ export interface OcxConfig {
|
|
|
402
428
|
* remain unchanged),
|
|
403
429
|
* `{{effort}}` -> injectionEffort, `{{roster}}` -> the resolved sub-agent roster
|
|
404
430
|
* block ("" when nothing resolves), `{{fallback}}` -> the configured subagent
|
|
405
|
-
* model fallback guidance block ("" when unset)
|
|
406
|
-
* enabled role catalog ("" when none). A custom prompt replaces the built-in
|
|
407
|
-
* body; include `{{roles}}` to keep the catalog.
|
|
431
|
+
* model fallback guidance block ("" when unset).
|
|
408
432
|
*/
|
|
409
433
|
injectionPrompt?: string;
|
|
410
434
|
/**
|
|
@@ -468,12 +492,12 @@ export interface OcxConfig {
|
|
|
468
492
|
* Routed parents get v2 tools; Sol/Terra can still spawn Grok/Claude (issue #92).
|
|
469
493
|
*/
|
|
470
494
|
keepNativeChatGptOnV1?: boolean;
|
|
471
|
-
/**
|
|
495
|
+
/** Optional v2-native parent override for spawn_agent routing. */
|
|
472
496
|
v2NativeParentOverride?: { enabled?: boolean; model?: string };
|
|
473
497
|
/** Experimental, default-off ChatGPT recovery for encrypted V2 routed tasks. */
|
|
474
498
|
agentTaskRecovery?: {
|
|
475
499
|
enabled?: boolean;
|
|
476
|
-
/** ChatGPT model used by the recovery request. Default: gpt-5.6-
|
|
500
|
+
/** ChatGPT model used by the recovery request. Default: gpt-5.6-sol. */
|
|
477
501
|
model?: string;
|
|
478
502
|
/** Recovery request timeout in milliseconds. Default: 45000. */
|
|
479
503
|
timeoutMs?: number;
|
|
@@ -516,6 +540,13 @@ export interface OcxConfig {
|
|
|
516
540
|
* those are unset — Bun's fetch honors them for all outbound calls; localhost is excluded.
|
|
517
541
|
*/
|
|
518
542
|
proxy?: string;
|
|
543
|
+
/**
|
|
544
|
+
* Hosts that bypass `proxy` for OpenCodex's own outbound provider calls, merged into
|
|
545
|
+
* NO_PROXY at startup. Accepts a comma-separated string (NO_PROXY syntax) or an array.
|
|
546
|
+
* Loopback is always excluded regardless of this setting, and an inherited NO_PROXY is
|
|
547
|
+
* preserved — this ADDS entries, it never replaces the environment.
|
|
548
|
+
*/
|
|
549
|
+
noProxy?: string | string[];
|
|
519
550
|
/**
|
|
520
551
|
* Upstream stall timeout (seconds). After this many seconds of no upstream data, emits
|
|
521
552
|
* response.incomplete. Default 300. Min 1.
|
|
@@ -587,6 +618,15 @@ export interface OcxConfig {
|
|
|
587
618
|
* selector map remains visible for compatibility with hand-written configurations.
|
|
588
619
|
*/
|
|
589
620
|
codexAccountPickerEnabled?: boolean;
|
|
621
|
+
/**
|
|
622
|
+
* Show the GPT-5.3-Codex-Spark weekly window on Codex quota surfaces. Default false.
|
|
623
|
+
*
|
|
624
|
+
* Spark is a single-model window that reads 0% for most operators, and on a multi-account
|
|
625
|
+
* pool it doubles the bar count for information almost nobody acts on. Hidden by default and
|
|
626
|
+
* revealed by an explicit `true`; a malformed value reads as hidden rather than rejecting the
|
|
627
|
+
* whole config.
|
|
628
|
+
*/
|
|
629
|
+
showCodexSparkQuota?: boolean;
|
|
590
630
|
/** Active pool account id for next session. undefined = main (passthrough as-is). */
|
|
591
631
|
activeCodexAccountId?: string;
|
|
592
632
|
/** Auto-switch threshold (0-100). Default 80. 0 = disabled. */
|
|
@@ -617,10 +657,22 @@ export interface OcxConfig {
|
|
|
617
657
|
stickyLimit?: number;
|
|
618
658
|
};
|
|
619
659
|
/**
|
|
620
|
-
*
|
|
621
|
-
*
|
|
622
|
-
*
|
|
660
|
+
* Generic OAuth multi-account 429 failover (#2568). Presence-driven by default.
|
|
661
|
+
*
|
|
662
|
+
* Rotates to another logged-in account of the SAME provider when one is rate-limited, for
|
|
663
|
+
* OAuth providers that have no pool of their own — xAI, Cursor, Kimi, GitHub Copilot,
|
|
664
|
+
* Antigravity, Nous. The Codex pool and the Anthropic pool own their own rotation and are
|
|
665
|
+
* excluded; this setting changes neither.
|
|
666
|
+
*
|
|
667
|
+
* With the key absent, rotation activates when a provider has 2 or more eligible stored
|
|
668
|
+
* accounts — the same consent rule API-key pools already apply to a 2+ key pool (#2568d). A
|
|
669
|
+
* single account is a strict no-op. Set `false` to keep strict single-account behaviour;
|
|
670
|
+
* `providers.<name>.oauthAccountFailover` overrides this per provider.
|
|
623
671
|
*/
|
|
672
|
+
oauthAccountFailover?: {
|
|
673
|
+
enabled?: boolean;
|
|
674
|
+
};
|
|
675
|
+
/** Cursor OAuth account pool rotation for api2.cursor.sh. */
|
|
624
676
|
cursorAccountPool?: {
|
|
625
677
|
enabled?: boolean;
|
|
626
678
|
};
|
package/src/types/provider.ts
CHANGED
|
@@ -119,6 +119,15 @@ export interface TierObservationContext {
|
|
|
119
119
|
fastWire: FastWire | null;
|
|
120
120
|
demandDecision: "force-fast" | "force-default" | "inherit";
|
|
121
121
|
callerTier?: string;
|
|
122
|
+
/**
|
|
123
|
+
* Whether the destination's echoed `service_tier` is authoritative about Fast scheduling.
|
|
124
|
+
*
|
|
125
|
+
* The ChatGPT-internal Codex backend returns `service_tier: "default"` on turns that were in
|
|
126
|
+
* fact scheduled as priority, so treating its echo as a downgrade produced a false
|
|
127
|
+
* `response-declined` on every Fast request (#2558). Absent means "assume authoritative",
|
|
128
|
+
* preserving the behaviour for the public API where the echo does mean what it says.
|
|
129
|
+
*/
|
|
130
|
+
responseTierAuthoritative?: boolean;
|
|
122
131
|
}
|
|
123
132
|
|
|
124
133
|
export type TierDecision =
|
|
@@ -131,6 +140,12 @@ export type TierDecision =
|
|
|
131
140
|
* retries are allowed; OAuth/forward credentials and local runtimes are never replayed.
|
|
132
141
|
*/
|
|
133
142
|
export interface OcxProviderConfig {
|
|
143
|
+
/** Optional short provider namespace used only at request/catalog presentation time. */
|
|
144
|
+
alias?: string;
|
|
145
|
+
/** Native model id -> short, slash-free request alias. */
|
|
146
|
+
modelAliases?: Record<string, string>;
|
|
147
|
+
/** Override the global built-in model-alias switch for this provider. */
|
|
148
|
+
defaultAliases?: boolean;
|
|
134
149
|
adapter: string;
|
|
135
150
|
/**
|
|
136
151
|
* Codex tool calling mode for routed models.
|
|
@@ -227,6 +242,19 @@ export interface OcxProviderConfig {
|
|
|
227
242
|
* link-local, or unique-local upstreams. Metadata endpoints remain blocked.
|
|
228
243
|
*/
|
|
229
244
|
allowPrivateNetwork?: boolean;
|
|
245
|
+
/**
|
|
246
|
+
* ChatGPT Codex backend WebSocket upstream transport.
|
|
247
|
+
* Defaults to false (routes streaming turns over standard HTTP/SSE).
|
|
248
|
+
* Set `true` to opt into the faster responses_websockets transport.
|
|
249
|
+
* `OCX_CODEX_WS_UPSTREAM=true` or `1` also enables it when this is omitted;
|
|
250
|
+
* `false` and `0` disable it. Invalid or absent values default to HTTP/SSE.
|
|
251
|
+
*/
|
|
252
|
+
wsUpstream?: boolean;
|
|
253
|
+
/**
|
|
254
|
+
* Maximum WebSocket request frame size in bytes before routing over standard HTTP/SSE.
|
|
255
|
+
* Defaults to CODEX_WS_CREATE_FRAME_LIMIT_BYTES (~16 MiB minus margin).
|
|
256
|
+
*/
|
|
257
|
+
maxWsFrameBytes?: number;
|
|
230
258
|
/**
|
|
231
259
|
* Pin the HTTP version used for upstream provider requests. Bun's fetch negotiates
|
|
232
260
|
* HTTP/2 via TLS ALPN by default; some Cloudflare-fronted SSE endpoints hang on
|
|
@@ -284,6 +312,33 @@ export interface OcxProviderConfig {
|
|
|
284
312
|
* full set so the user can pick). See devlog issue_052_provider-model-allowlist.
|
|
285
313
|
*/
|
|
286
314
|
selectedModels?: string[];
|
|
315
|
+
/** Override for newly discovered models. Absent/"inherit" uses the install policy. */
|
|
316
|
+
newModelPolicy?: "on" | "off" | "inherit";
|
|
317
|
+
/**
|
|
318
|
+
* Model-preset marker for `selectedModels` (#2465). Absent means "all", exactly today's
|
|
319
|
+
* semantics — an existing provider is never narrowed by an upgrade.
|
|
320
|
+
*
|
|
321
|
+
* The preset is a SEED, not a lock: `selectedModels` holds concrete ids materialized from
|
|
322
|
+
* the shipped rules, so every existing consumer and older binaries keep working against a
|
|
323
|
+
* plain allowlist. Divergence is detected at the WRITE path rather than by diffing — any user
|
|
324
|
+
* edit while the mode is "preset" flips it to "custom", after which the proxy never
|
|
325
|
+
* re-materializes. That collapses upgrade reconciliation to a version compare.
|
|
326
|
+
*
|
|
327
|
+
* Deliberately distinct from `deriveProviderPresets`, which curates WHICH PROVIDERS to offer.
|
|
328
|
+
* This curates which MODELS a provider exposes; the code says "model preset" throughout.
|
|
329
|
+
*/
|
|
330
|
+
modelPreset?: {
|
|
331
|
+
mode: "preset" | "all" | "custom";
|
|
332
|
+
/** MODEL_PRESETS version materialized into `selectedModels`. */
|
|
333
|
+
appliedVersion?: number;
|
|
334
|
+
appliedAt?: string;
|
|
335
|
+
/**
|
|
336
|
+
* Set when materialization matched nothing and the provider fell back to "all". A preset
|
|
337
|
+
* must never write an empty allowlist, because empty means ALL and would silently
|
|
338
|
+
* un-curate; the fallback marker lets the next convergence retry.
|
|
339
|
+
*/
|
|
340
|
+
fallback?: "preset-empty";
|
|
341
|
+
};
|
|
287
342
|
/** Provider-wide fallback when context metadata is absent; otherwise caps the reported window. */
|
|
288
343
|
contextWindow?: number;
|
|
289
344
|
/** Per-model fallback when context metadata is absent; otherwise caps the reported window. */
|
|
@@ -327,6 +382,16 @@ export interface OcxProviderConfig {
|
|
|
327
382
|
* providers whose registry entry declares authKind "local" (management API enforces).
|
|
328
383
|
*/
|
|
329
384
|
authMode?: "key" | "forward" | "oauth" | "local";
|
|
385
|
+
/**
|
|
386
|
+
* Per-provider override for generic OAuth multi-account 429 failover (#2568).
|
|
387
|
+
*
|
|
388
|
+
* Rotation is presence-driven by default — 2+ logged-in accounts activate it — so this exists
|
|
389
|
+
* for the operator who accepts rotation on one provider and refuses it on another. An explicit
|
|
390
|
+
* boolean here beats the global `oauthAccountFailover` and beats presence.
|
|
391
|
+
*/
|
|
392
|
+
oauthAccountFailover?: {
|
|
393
|
+
enabled?: boolean;
|
|
394
|
+
};
|
|
330
395
|
/** Allow an explicitly key/oauth provider to run without a credential (for keyless local proxies). */
|
|
331
396
|
keyOptional?: boolean;
|
|
332
397
|
/**
|
|
@@ -359,6 +424,18 @@ export interface OcxProviderConfig {
|
|
|
359
424
|
* Responses backend rejects Codex summary-delivery fields for that model.
|
|
360
425
|
*/
|
|
361
426
|
modelSupportsReasoningSummaries?: Record<string, boolean>;
|
|
427
|
+
/**
|
|
428
|
+
* Model-specific Codex Responses verbosity capability. Set false when the upstream ignores
|
|
429
|
+
* `text.verbosity`; the catalog hides the no-op picker and the Responses adapter strips stale
|
|
430
|
+
* or caller-supplied values while preserving other `text` fields.
|
|
431
|
+
*/
|
|
432
|
+
modelSupportsVerbosity?: Record<string, boolean>;
|
|
433
|
+
/**
|
|
434
|
+
* Provider-wide Codex Responses verbosity capability, applied to models the per-model map
|
|
435
|
+
* does not enumerate (a live-discovered id, for example). Materialized from the registry at
|
|
436
|
+
* seed/enrich time so the catalog hint pass never has to read PROVIDER_REGISTRY.
|
|
437
|
+
*/
|
|
438
|
+
supportsVerbosity?: boolean;
|
|
362
439
|
/**
|
|
363
440
|
* Per-model wire value for Responses `stream_options.reasoning_summary_delivery`.
|
|
364
441
|
* Presence also advertises reasoning-summary support for that routed model.
|
|
@@ -526,8 +603,9 @@ export interface OcxProviderConfig {
|
|
|
526
603
|
* Google adapter mode. "ai-studio" (default) = Generative Language API + x-goog-api-key.
|
|
527
604
|
* "vertex" = Vertex AI project/location endpoints with GCP ADC (or x-goog-api-key).
|
|
528
605
|
* "cloud-code-assist" = Google Antigravity (Cloud Code Assist) OAuth + CCA envelope.
|
|
606
|
+
* "ai-studio-web" = browser-relayed AI Studio Playground/Build session.
|
|
529
607
|
*/
|
|
530
|
-
googleMode?: "ai-studio" | "vertex" | "cloud-code-assist";
|
|
608
|
+
googleMode?: "ai-studio" | "vertex" | "cloud-code-assist" | "ai-studio-web";
|
|
531
609
|
/** Vertex AI GCP project id (or GOOGLE_CLOUD_PROJECT / GCLOUD_PROJECT env). */
|
|
532
610
|
project?: string;
|
|
533
611
|
/** Vertex AI location, e.g. "us-central1" or "global" (or GOOGLE_CLOUD_LOCATION env). */
|
package/src/types/request.ts
CHANGED
|
@@ -70,6 +70,10 @@ export interface OcxParsedRequest {
|
|
|
70
70
|
_clientThreadId?: string;
|
|
71
71
|
/** True when promptCacheKey is a shared cache cohort rather than a conversation identity. */
|
|
72
72
|
_promptCacheKeyIsSharedCohort?: boolean;
|
|
73
|
+
/** Provider-private Command Code session affinity id, stable across in-process request mutation. */
|
|
74
|
+
_commandCodeSessionId?: string;
|
|
75
|
+
/** Cursor-only thread owner; may be an opaque process-local Desktop session/thread identity. */
|
|
76
|
+
_cursorClientThreadId?: string;
|
|
73
77
|
/** Conversation/provider/account/model-bound namespace for reasoning replay state. */
|
|
74
78
|
_reasoningReplayScope?: OcxReasoningReplayScopeRef;
|
|
75
79
|
/**
|
|
@@ -88,7 +92,7 @@ export interface OcxParsedRequest {
|
|
|
88
92
|
*/
|
|
89
93
|
_cursorIsolateConversation?: boolean;
|
|
90
94
|
/** Account-scoped, non-secret Kiro request metadata selected with the OAuth access token. */
|
|
91
|
-
_kiroAuthContext?: Pick<KiroOAuthMetadata, "profileArn" | "apiRegion" | "ssoRegion">;
|
|
95
|
+
_kiroAuthContext?: Pick<KiroOAuthMetadata, "profileArn" | "apiRegion" | "ssoRegion" | "authType">;
|
|
92
96
|
/** Provider-private continuation metadata resolved from the Responses previous_response_id chain. */
|
|
93
97
|
_providerContinuation?: OcxProviderContinuationState;
|
|
94
98
|
/** Persisted continuation considered only after the final physical route is known. */
|
|
@@ -196,8 +200,14 @@ export interface OcxImageContent {
|
|
|
196
200
|
detail?: string;
|
|
197
201
|
}
|
|
198
202
|
|
|
199
|
-
|
|
200
|
-
|
|
203
|
+
export interface OcxVideoContent {
|
|
204
|
+
type: "video";
|
|
205
|
+
/** A base64 `data:` URL from an OpenAI-compatible `video_url` part. */
|
|
206
|
+
videoUrl: string;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** A user/developer message content part: text or native media. */
|
|
210
|
+
export type OcxContentPart = OcxTextContent | OcxImageContent | OcxVideoContent;
|
|
201
211
|
|
|
202
212
|
export interface OcxThinkingContent {
|
|
203
213
|
type: "thinking";
|
|
@@ -258,13 +268,9 @@ export interface OcxRequestOptions {
|
|
|
258
268
|
/**
|
|
259
269
|
* Responses `text.format` (json_schema / json_object), preserved for adapters whose
|
|
260
270
|
* upstream wire has an equivalent. The openai-chat adapter re-nests it as chat
|
|
261
|
-
* `response_format`, the exact inverse of responseFormatToText in src/chat/inbound.ts
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
* The `openai-chat` adapter can omit it for models in `noStructuredOutputModels`; Kiro
|
|
265
|
-
* rejects structured output via `_structuredOutput`; and Cursor has no structured-output
|
|
266
|
-
* wire field and rejects the request before transport.
|
|
267
|
-
* Native passthrough does not consume this option and forwards `_rawBody.text` verbatim.
|
|
271
|
+
* `response_format`, the exact inverse of responseFormatToText in src/chat/inbound.ts.
|
|
272
|
+
* The native passthrough ignores it (it forwards `_rawBody.text` verbatim) and Kiro
|
|
273
|
+
* keeps rejecting structured output via `_structuredOutput`.
|
|
268
274
|
*/
|
|
269
275
|
textFormat?: {
|
|
270
276
|
type: "json_schema" | "json_object";
|
|
@@ -290,10 +296,12 @@ export type GoogleSafetyThreshold =
|
|
|
290
296
|
| "BLOCK_ONLY_HIGH"
|
|
291
297
|
| "BLOCK_NONE"
|
|
292
298
|
| "OFF";
|
|
299
|
+
|
|
293
300
|
export interface GoogleSafetySetting {
|
|
294
301
|
category: GoogleSafetyCategory;
|
|
295
302
|
threshold: GoogleSafetyThreshold;
|
|
296
303
|
}
|
|
304
|
+
|
|
297
305
|
export interface GoogleProviderOptions {
|
|
298
306
|
thinkingBudget?: number;
|
|
299
307
|
includeThoughts?: boolean;
|
package/src/types/tools.ts
CHANGED
|
@@ -32,30 +32,49 @@ export function namespacedToolName(namespace: string | undefined, name: string):
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* Codex
|
|
35
|
+
* Codex unified-exec name normalization.
|
|
36
36
|
*
|
|
37
37
|
* Codex's code-mode shell tool is declared as `exec` (a freeform custom tool whose own
|
|
38
38
|
* description mentions the nested `await tools.exec_command(...)` helper). Routed models —
|
|
39
39
|
* DeepSeek in particular — sometimes echo that helper name as the tool-call name, emitting
|
|
40
|
-
* `exec_command` instead of the declared `exec`. Accept
|
|
41
|
-
* when the request catalog actually declares `exec` and does not itself declare the
|
|
42
|
-
* name (an MCP server may legitimately advertise
|
|
40
|
+
* `exec_command` or `apply_patch` instead of the declared `exec`. Accept these nested helper
|
|
41
|
+
* names only when the request catalog actually declares `exec` and does not itself declare the
|
|
42
|
+
* emitted name (an MCP server may legitimately advertise one under its own namespace). Some
|
|
43
|
+
* hosted adapters also strip a unique namespace prefix or replace its separator with `_`; those
|
|
44
|
+
* forms are accepted only when one declaration can be identified unambiguously.
|
|
43
45
|
*/
|
|
44
46
|
const LEGACY_SHELL_BRIDGE_TOOL_NAMES = ["exec_command", "shell_command"] as const;
|
|
47
|
+
const CODE_MODE_HELPER_TOOL_NAMES = [...LEGACY_SHELL_BRIDGE_TOOL_NAMES, "apply_patch"] as const;
|
|
45
48
|
|
|
46
49
|
export function normalizeDeclaredToolName(
|
|
47
50
|
name: string,
|
|
48
51
|
declared: ReadonlySet<string> | undefined,
|
|
49
52
|
): string {
|
|
50
|
-
if (!declared
|
|
53
|
+
if (!declared) return name;
|
|
51
54
|
if (declared.has(name)) return name;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
if (declared.has("exec")) {
|
|
56
|
+
// When the catalog explicitly declares any legacy shell bridge name, the environment
|
|
57
|
+
// genuinely exposes that tool — turn normalization off so a call is never mis-routed
|
|
58
|
+
// to `exec`.
|
|
59
|
+
if ((LEGACY_SHELL_BRIDGE_TOOL_NAMES as readonly string[]).some(legacy => declared.has(legacy))) {
|
|
60
|
+
return name;
|
|
61
|
+
}
|
|
62
|
+
if ((CODE_MODE_HELPER_TOOL_NAMES as readonly string[]).includes(name)) {
|
|
63
|
+
return "exec";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Models may strip a namespace prefix or replace separators with underscores. Resolve only
|
|
67
|
+
// when exactly one declaration matches, so an ambiguous bare name remains fail-closed.
|
|
68
|
+
let matched: string | undefined;
|
|
69
|
+
for (const dec of declared) {
|
|
70
|
+
const matchesSuffix = !name.includes(":") && !name.includes("__")
|
|
71
|
+
&& (dec.endsWith(`:${name}`) || dec.endsWith(`__${name}`));
|
|
72
|
+
const matchesSanitized = dec.replace(/[^A-Za-z0-9_-]/g, "_") === name;
|
|
73
|
+
if (!matchesSuffix && !matchesSanitized) continue;
|
|
74
|
+
if (matched !== undefined) return name;
|
|
75
|
+
matched = dec;
|
|
57
76
|
}
|
|
58
|
-
return
|
|
77
|
+
return matched ?? name;
|
|
59
78
|
}
|
|
60
79
|
|
|
61
80
|
export function toolChoiceAliases(tool: Pick<OcxTool, "namespace" | "name">): string[] {
|
package/src/types.ts
CHANGED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { Cost4, ExpectedPriceOverlay } from "./expected-prices";
|
|
2
|
+
|
|
3
|
+
export interface CommandCodeModelPricing {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
slug?: string;
|
|
7
|
+
contextWindow?: number;
|
|
8
|
+
cost4: Cost4;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const COMMAND_CODE_MODELS_DATA_URL = "https://commandcode.ai/models.data";
|
|
12
|
+
export const COMMAND_CODE_PRICING_SOURCE = "https://commandcode.ai/models + https://commandcode.ai/models.data";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parse Command Code models and 4-tuple per-1M token rates from the Turbo-stream
|
|
16
|
+
* payload served by https://commandcode.ai/models.data.
|
|
17
|
+
*/
|
|
18
|
+
export function parseCommandCodeModelsData(raw: unknown): CommandCodeModelPricing[] {
|
|
19
|
+
if (!Array.isArray(raw) || raw.length < 10) return [];
|
|
20
|
+
const modelIndices = raw[9];
|
|
21
|
+
if (!Array.isArray(modelIndices)) return [];
|
|
22
|
+
|
|
23
|
+
const resolve = (val: unknown): unknown => {
|
|
24
|
+
if (typeof val === "number") {
|
|
25
|
+
if (val < 0) return val === -5 ? null : undefined;
|
|
26
|
+
if (val < raw.length) return raw[val];
|
|
27
|
+
}
|
|
28
|
+
return val;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const results: CommandCodeModelPricing[] = [];
|
|
32
|
+
for (const idx of modelIndices) {
|
|
33
|
+
if (typeof idx !== "number" || idx < 0 || idx >= raw.length) continue;
|
|
34
|
+
const obj = raw[idx];
|
|
35
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj)) continue;
|
|
36
|
+
|
|
37
|
+
const fields: Record<string, unknown> = {};
|
|
38
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
39
|
+
if (!k.startsWith("_")) continue;
|
|
40
|
+
const keyIdx = Number.parseInt(k.slice(1), 10);
|
|
41
|
+
if (!Number.isFinite(keyIdx) || keyIdx < 0 || keyIdx >= raw.length) continue;
|
|
42
|
+
const keyName = raw[keyIdx];
|
|
43
|
+
if (typeof keyName === "string" && keyName.length > 0) {
|
|
44
|
+
fields[keyName] = resolve(v);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const id = typeof fields.id === "string" ? fields.id.trim() : "";
|
|
49
|
+
if (!id) continue;
|
|
50
|
+
|
|
51
|
+
const name = typeof fields.name === "string" && fields.name.trim() ? fields.name.trim() : id;
|
|
52
|
+
const slug = typeof fields.slug === "string" && fields.slug.trim() ? fields.slug.trim() : undefined;
|
|
53
|
+
const contextWindow = typeof fields.contextWindow === "number" && Number.isFinite(fields.contextWindow)
|
|
54
|
+
? fields.contextWindow
|
|
55
|
+
: undefined;
|
|
56
|
+
|
|
57
|
+
const input = typeof fields.inputCost === "number" && Number.isFinite(fields.inputCost) && fields.inputCost >= 0
|
|
58
|
+
? fields.inputCost
|
|
59
|
+
: 0;
|
|
60
|
+
const output = typeof fields.outputCost === "number" && Number.isFinite(fields.outputCost) && fields.outputCost >= 0
|
|
61
|
+
? fields.outputCost
|
|
62
|
+
: 0;
|
|
63
|
+
const cacheRead = typeof fields.cacheReadCost === "number" && Number.isFinite(fields.cacheReadCost) && fields.cacheReadCost >= 0
|
|
64
|
+
? fields.cacheReadCost
|
|
65
|
+
: 0;
|
|
66
|
+
const cacheWrite = typeof fields.cacheWriteCost === "number" && Number.isFinite(fields.cacheWriteCost) && fields.cacheWriteCost >= 0
|
|
67
|
+
? fields.cacheWriteCost
|
|
68
|
+
: 0;
|
|
69
|
+
|
|
70
|
+
results.push({
|
|
71
|
+
id,
|
|
72
|
+
name,
|
|
73
|
+
...(slug ? { slug } : {}),
|
|
74
|
+
...(contextWindow !== undefined ? { contextWindow } : {}),
|
|
75
|
+
cost4: { input, output, cacheRead, cacheWrite },
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return results;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Fetch and decode the latest Command Code model pricing from commandcode.ai.
|
|
84
|
+
*/
|
|
85
|
+
export async function fetchCommandCodeModelPricing(options?: {
|
|
86
|
+
url?: string;
|
|
87
|
+
signal?: AbortSignal;
|
|
88
|
+
fetchFn?: typeof fetch;
|
|
89
|
+
}): Promise<CommandCodeModelPricing[]> {
|
|
90
|
+
const fetcher = options?.fetchFn ?? fetch;
|
|
91
|
+
const url = options?.url ?? COMMAND_CODE_MODELS_DATA_URL;
|
|
92
|
+
const res = await fetcher(url, { signal: options?.signal });
|
|
93
|
+
if (!res.ok) throw new Error(`Failed to fetch Command Code models data: ${res.status}`);
|
|
94
|
+
const data = await res.json();
|
|
95
|
+
return parseCommandCodeModelsData(data);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Convert parsed Command Code models to opencodex ExpectedPriceOverlay entries.
|
|
100
|
+
*/
|
|
101
|
+
export function commandCodePricingToExpectedOverlays(
|
|
102
|
+
models: readonly CommandCodeModelPricing[],
|
|
103
|
+
verifiedAt = "2026-08-26",
|
|
104
|
+
): ExpectedPriceOverlay[] {
|
|
105
|
+
return models
|
|
106
|
+
.filter(m => m.cost4.input > 0 || m.cost4.output > 0 || m.cost4.cacheRead > 0 || m.cost4.cacheWrite > 0)
|
|
107
|
+
.map(m => ({
|
|
108
|
+
provider: "command-code",
|
|
109
|
+
modelId: m.id,
|
|
110
|
+
cost4: m.cost4,
|
|
111
|
+
source: COMMAND_CODE_PRICING_SOURCE,
|
|
112
|
+
verifiedAt,
|
|
113
|
+
status: "verified",
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
|
package/src/usage/cost.ts
CHANGED
|
@@ -253,7 +253,7 @@ function resolveMatchedPriceExact(
|
|
|
253
253
|
const verifiedOverride = overlays === EXPECTED_PRICE_OVERLAYS
|
|
254
254
|
? findVerifiedPriceOverride(provider, modelId)
|
|
255
255
|
: undefined;
|
|
256
|
-
if (verifiedOverride && validCost4(verifiedOverride.cost4) && hasNonZeroCost(verifiedOverride.cost4)) {
|
|
256
|
+
if (verifiedOverride && verifiedOverride.status !== "unverified" && validCost4(verifiedOverride.cost4) && hasNonZeroCost(verifiedOverride.cost4)) {
|
|
257
257
|
return {
|
|
258
258
|
provider,
|
|
259
259
|
modelId,
|
|
@@ -261,7 +261,7 @@ function resolveMatchedPriceExact(
|
|
|
261
261
|
source: "expected",
|
|
262
262
|
sourceRef: verifiedOverride.source,
|
|
263
263
|
verifiedAt: verifiedOverride.verifiedAt,
|
|
264
|
-
status:
|
|
264
|
+
status: verifiedOverride.status,
|
|
265
265
|
};
|
|
266
266
|
}
|
|
267
267
|
const metadataProvider = resolveMetadataProvider(provider);
|