@bitkyc08/opencodex 2.64.0 → 2.65.0-preview.20260925
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 -25
- package/bin/ocx.mjs +16 -2
- package/gui/dist/assets/App-8NMiZxT0.css +1 -0
- package/gui/dist/assets/App-CsYvvpr3.js +50 -0
- package/gui/dist/assets/Tray-DUvc_Wul.js +1 -0
- package/gui/dist/assets/{index-DiBRuK-d.css → index--EWgGQvZ.css} +1 -1
- package/gui/dist/assets/{index-SggB6t3z.js → index-BB0iHG8-.js} +12 -12
- package/gui/dist/assets/tray-data-f0lTZ4sF.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -4
- package/src/adapters/anthropic.ts +30 -1
- package/src/adapters/claude-cli/adapter.ts +218 -0
- package/src/adapters/claude-cli/profiles.ts +38 -0
- package/src/adapters/codebuddy/profiles.ts +2 -0
- package/src/adapters/coding-agent/profile.ts +12 -4
- package/src/adapters/coding-agent/turn.ts +16 -6
- package/src/adapters/command-code-tool-text.ts +133 -23
- package/src/adapters/cursor/catalog.ts +4 -8
- package/src/adapters/cursor/discovery.ts +10 -5
- package/src/adapters/cursor/effort-map.ts +2 -6
- package/src/adapters/cursor/envelope-echo.ts +10 -5
- package/src/adapters/cursor/request-builder.ts +9 -4
- package/src/adapters/cursor/thread-continuity.ts +53 -0
- package/src/adapters/cursor.ts +29 -13
- package/src/adapters/devin/cloud-direct/chat.ts +3 -1
- package/src/adapters/google-tool-schema.ts +26 -3
- package/src/adapters/identity.ts +214 -2
- package/src/adapters/openai-chat/passthrough.ts +1 -0
- package/src/adapters/openai-chat/serialized-tool-call-content.ts +569 -0
- package/src/adapters/openai-chat.ts +43 -15
- package/src/adapters/openai-responses/canonical-forward.ts +17 -10
- package/src/adapters/openai-responses/passthrough.ts +19 -4
- package/src/adapters/openai-responses/request-strips.ts +25 -0
- package/src/adapters/openai-responses/web-search.ts +22 -4
- package/src/adapters/qoder/profiles.ts +2 -0
- package/src/adapters/registry.ts +8 -0
- package/src/adapters/run-turn-queue.ts +7 -0
- package/src/bridge/response-json.ts +6 -4
- package/src/bridge/sse.ts +7 -5
- package/src/claude/alias.ts +73 -26
- package/src/claude/context-windows.ts +15 -6
- package/src/claude/desktop-3p-library.ts +3 -2
- package/src/claude/desktop-3p.ts +1 -1
- package/src/claude/desktop-first-party.ts +63 -22
- package/src/claude/desktop-picker-profile.ts +302 -0
- package/src/claude/desktop-picker.ts +365 -0
- package/src/claude/desktop-risk.ts +20 -0
- package/src/claude/gateway-cache.ts +13 -4
- package/src/claude/inbound.ts +5 -2
- package/src/claude/intercept/connect-proxy.ts +106 -29
- package/src/claude/intercept/local-ca.ts +76 -18
- package/src/claude/intercept/model-bindings.ts +145 -0
- package/src/claude/intercept/picker-bootstrap.ts +141 -0
- package/src/claude/intercept/picker-ca.ts +126 -0
- package/src/claude/intercept/picker-listener.ts +213 -0
- package/src/claude/intercept/picker-models.ts +101 -0
- package/src/claude/intercept/picker-runtime.ts +330 -0
- package/src/claude/intercept/picker-trust.ts +125 -0
- package/src/claude/intercept/runtime.ts +149 -2
- package/src/claude/model-info.ts +18 -4
- package/src/cli/agent.ts +16 -2
- package/src/cli/capabilities.ts +71 -2
- package/src/cli/claude-desktop.ts +319 -33
- package/src/cli/claude.ts +15 -7
- package/src/cli/codex-shim-autorestore.ts +1 -0
- package/src/cli/dispatch.ts +29 -23
- package/src/cli/effort.ts +16 -6
- package/src/cli/ensure-desired-integrations.ts +29 -4
- package/src/cli/ready.ts +1 -1
- package/src/cli/registry.ts +8 -0
- package/src/cli/restart-scope.ts +9 -1
- package/src/cli/runtime-api.ts +17 -0
- package/src/cli/system-command.ts +11 -12
- package/src/client/connect.ts +12 -2
- package/src/client/state.ts +39 -1
- package/src/clients/config-export.ts +57 -9
- package/src/codex/auth-context.ts +51 -11
- package/src/codex/catalog/derive-entry.ts +5 -5
- package/src/codex/catalog/gather-capture.ts +25 -6
- package/src/codex/catalog/metadata.ts +3 -3
- package/src/codex/catalog/parsing.ts +39 -1
- package/src/codex/catalog/provider-models.ts +8 -6
- package/src/codex/catalog/retained-sync.ts +17 -7
- package/src/codex/catalog/sync.ts +2 -2
- package/src/codex/desktop-app-restart.ts +8 -0
- package/src/codex/desktop-switches.ts +9 -1
- package/src/codex/home.ts +43 -4
- package/src/codex/inject/config-toml.ts +137 -0
- package/src/codex/inject/plan.ts +23 -0
- package/src/codex/inject/remove.ts +21 -1
- package/src/codex/inject.ts +63 -8
- package/src/codex/journal.ts +36 -0
- package/src/codex/main-account-hard-lock.ts +14 -1
- package/src/codex/main-account-policy-wait.ts +109 -0
- package/src/codex/native-profile-startup.ts +24 -6
- package/src/codex/native-residue.ts +28 -10
- package/src/codex/quota-types.ts +8 -1
- package/src/codex/quota.ts +49 -7
- package/src/codex/runtime.ts +31 -10
- package/src/combos/failover.ts +7 -6
- package/src/combos/resolve.ts +91 -14
- package/src/combos/types.ts +18 -1
- package/src/config/load-degrade.ts +2 -0
- package/src/config/schema/config-schema.ts +21 -2
- package/src/config/schema/leaf-validators.ts +6 -3
- package/src/config/subagent-models.ts +3 -1
- package/src/generated/compatibility-version.json +275 -163
- package/src/images/artifacts.ts +14 -0
- package/src/integrations/owned-refresh.ts +1 -1
- package/src/integrations/ownership-policy.ts +32 -1
- package/src/integrations/state.ts +3 -1
- package/src/integrations/writer.ts +9 -0
- package/src/lib/config-ownership.ts +3 -0
- package/src/lib/errors.ts +87 -0
- package/src/lib/package-tree-integrity.ts +1 -1
- package/src/lib/request-execution-budget.ts +42 -14
- package/src/lib/request-resend-gate.ts +4 -3
- package/src/lib/retry-delay.ts +7 -1
- package/src/lib/tool-envelope-echo-filter.ts +236 -0
- package/src/lib/upstream-retry.ts +20 -12
- package/src/oauth/index.ts +1 -0
- package/src/oauth/kiro-credentials.ts +14 -1
- package/src/oauth/login-cli.ts +1 -0
- package/src/providers/derive.ts +4 -0
- package/src/providers/fast-opt-in.ts +31 -0
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/provider-id-rewrite.ts +2 -0
- package/src/providers/quota/vendor-probes-key.ts +8 -2
- package/src/providers/registry/entries-core.ts +45 -2
- package/src/providers/registry/entries-extended.ts +99 -12
- package/src/providers/registry/model-ids.ts +2 -0
- package/src/providers/registry/types.ts +7 -1
- package/src/providers/resolved-model-policy.ts +11 -4
- package/src/providers/service-tier.ts +10 -3
- package/src/responses/code-mode-helper-compat.ts +3 -0
- package/src/responses/parser.ts +45 -3
- package/src/router.ts +6 -0
- package/src/server/auth-cors.ts +8 -0
- package/src/server/chat-completions.ts +3 -1
- package/src/server/claude-messages.ts +69 -14
- package/src/server/grok-upstream-envelope-echo.ts +120 -0
- package/src/server/index/claude-intercept-lifecycle.ts +24 -0
- package/src/server/index/serve-options.ts +2 -2
- package/src/server/index.ts +7 -0
- package/src/server/management/agent-settings-routes.ts +204 -112
- package/src/server/management/claude-desktop-picker-routes.ts +128 -0
- package/src/server/management/combo-routes.ts +59 -2
- package/src/server/management/config-routes.ts +81 -39
- package/src/server/management/context.ts +3 -0
- package/src/server/management/native-integration-routes.ts +169 -93
- package/src/server/management/provider-routes.ts +46 -4
- package/src/server/management/route-registry.ts +10 -0
- package/src/server/management/routing-profile-routes.ts +9 -0
- package/src/server/management/sidebar-routes.ts +52 -0
- package/src/server/management-api.ts +2 -0
- package/src/server/proxy-liveness.ts +38 -3
- package/src/server/request-log-account-rotation.ts +53 -0
- package/src/server/request-log.ts +3 -16
- package/src/server/responses/adapter-dispatch.ts +17 -1
- package/src/server/responses/agent-task-recovery.ts +49 -26
- package/src/server/responses/codex-ws-exchange.ts +18 -7
- package/src/server/responses/codex-ws-wire.ts +27 -4
- package/src/server/responses/core-combo.ts +21 -3
- package/src/server/responses/core-normalize.ts +7 -0
- package/src/server/responses/core-opaque-recovery.ts +3 -2
- package/src/server/responses/encrypted-payload.ts +15 -14
- package/src/server/responses/fetch-helpers.ts +1 -1
- package/src/server/responses/input-admission.ts +10 -6
- package/src/server/responses/native-response-control.ts +2 -2
- package/src/server/responses/passthrough-delivery.ts +182 -12
- package/src/server/responses/passthrough-dispatch.ts +102 -44
- package/src/server/responses/policy-fallback.ts +3 -0
- package/src/server/responses/policy-refusal.ts +67 -0
- package/src/server/responses/request-send-budget.ts +4 -0
- package/src/server/responses/run-turn-execution.ts +42 -11
- package/src/server/responses/ws-upstream.ts +2 -1
- package/src/server/system-env-shell.ts +0 -1
- package/src/server/system-env.ts +25 -4
- package/src/service/cli.ts +34 -3
- package/src/tray/assets/opencodex-tray-offline-update.ico +0 -0
- package/src/tray/assets/opencodex-tray-online-update.ico +0 -0
- package/src/tray/assets/opencodex-tray-warning-update.ico +0 -0
- package/src/tray/windows-tray.ps1 +188 -7
- package/src/tray/windows.ts +23 -2
- package/src/types/config.ts +52 -12
- package/src/types/provider.ts +22 -5
- package/src/types/tools.ts +11 -5
- package/src/types.ts +1 -0
- package/src/update/async-check.ts +109 -0
- package/src/update/badge.ts +11 -17
- package/src/update/check-types.ts +9 -0
- package/src/update/desktop-badge.ts +102 -0
- package/src/update/index.ts +48 -8
- package/src/update/install-detection.d.mts +29 -2
- package/src/update/install-detection.mjs +213 -7
- package/src/update/job.ts +17 -12
- package/src/update/notify.ts +16 -11
- package/src/update/pnpm-owner-worker.ts +12 -0
- package/src/update/refresh-scheduler.ts +141 -0
- package/src/usage/cost.ts +31 -4
- package/src/usage/expected-prices.ts +57 -0
- package/assets/download-linux.svg +0 -10
- package/assets/download-macos.svg +0 -10
- package/assets/download-windows.svg +0 -10
- package/gui/dist/assets/App-CpuDF3ci.js +0 -50
- package/gui/dist/assets/App-I5AnaSLh.css +0 -1
- package/gui/dist/assets/Tray-B4uEIa1O.js +0 -1
- package/gui/dist/assets/usage-companion-chart-DLbKOJml.js +0 -1
|
@@ -440,6 +440,25 @@ function invitesResendAfterReplacement(status: number): boolean {
|
|
|
440
440
|
|| status === 307 || status === 308 || status === 413 || status >= 500;
|
|
441
441
|
}
|
|
442
442
|
|
|
443
|
+
/**
|
|
444
|
+
* The answer a request keeps once its one operator replacement has gone out.
|
|
445
|
+
*
|
|
446
|
+
* A status that invites another send settles as the refusal. Any other answer keeps its real
|
|
447
|
+
* status: no client retries it, and the caller needs the evidence (a 400 names the request
|
|
448
|
+
* defect). The marker still stops this process from using it as a recovery trigger, such as the
|
|
449
|
+
* opaque-blob rebuild of a 400 or a combo hop on a context overflow, because each of those checks
|
|
450
|
+
* it before sending again.
|
|
451
|
+
*/
|
|
452
|
+
export function settleOperatorReplacement(response: Response): Response {
|
|
453
|
+
if (response.ok) return response;
|
|
454
|
+
if (invitesResendAfterReplacement(response.status)) {
|
|
455
|
+
cancelResponseBodyBestEffort(response);
|
|
456
|
+
return replayRefusalResponse();
|
|
457
|
+
}
|
|
458
|
+
markResponseNonReplayable(response);
|
|
459
|
+
return response;
|
|
460
|
+
}
|
|
461
|
+
|
|
443
462
|
export async function fetchWithAttemptDeadline(
|
|
444
463
|
url: string,
|
|
445
464
|
init: RequestInit,
|
|
@@ -624,18 +643,7 @@ export async function fetchWithResetRetry(
|
|
|
624
643
|
opts.onSendsConsumed?.(1);
|
|
625
644
|
try {
|
|
626
645
|
const response = await doFetch(attempt === 0 ? firstRecovery : "connection-reset");
|
|
627
|
-
|
|
628
|
-
if (invitesResendAfterReplacement(response.status)) {
|
|
629
|
-
cancelResponseBodyBestEffort(response);
|
|
630
|
-
return replayRefusalResponse();
|
|
631
|
-
}
|
|
632
|
-
// Any other answer keeps its real status: no client retries it, and the caller needs the
|
|
633
|
-
// evidence (a 400 names the request defect). The marker still stops this process from
|
|
634
|
-
// using it as a recovery trigger, such as the opaque-blob rebuild of a 400 or a combo hop
|
|
635
|
-
// on a context overflow, because each of those checks it before sending again.
|
|
636
|
-
markResponseNonReplayable(response);
|
|
637
|
-
}
|
|
638
|
-
return response;
|
|
646
|
+
return spentOperatorReplacement ? settleOperatorReplacement(response) : response;
|
|
639
647
|
} catch (err) {
|
|
640
648
|
if (opts.abortSignal?.aborted) throw err;
|
|
641
649
|
if (!isConnectionResetError(err)) {
|
package/src/oauth/index.ts
CHANGED
|
@@ -1270,6 +1270,7 @@ const OAUTH_RECONCILE_FIELDS: (keyof OcxProviderConfig)[] = [
|
|
|
1270
1270
|
"modelReasoningEffortMap",
|
|
1271
1271
|
"noTemperatureModels",
|
|
1272
1272
|
"noTopPModels",
|
|
1273
|
+
"noStopModels",
|
|
1273
1274
|
"noPenaltyModels",
|
|
1274
1275
|
"autoToolChoiceOnlyModels",
|
|
1275
1276
|
"preserveReasoningContentModels",
|
|
@@ -179,6 +179,12 @@ export function resolveKiroCliNativeSessionEntries(
|
|
|
179
179
|
* Windows: official MSI installs to `C:\Program Files\Kiro-Cli\kiro-cli.exe`, while some local
|
|
180
180
|
* installs keep the binary next to `%LOCALAPPDATA%\Kiro-Cli\data.sqlite3`.
|
|
181
181
|
* macOS/Linux: prefer PATH, then the usual user-local bin directories.
|
|
182
|
+
*
|
|
183
|
+
* The canonical `kiro-cli` name is exhausted everywhere first. Only then, and only on Windows, does
|
|
184
|
+
* the short `kiro.exe` name count, and only inside the two dedicated `Kiro-Cli` install folders
|
|
185
|
+
* already trusted for `kiro-cli.exe`, resolved from an absolute base. A short name is never looked
|
|
186
|
+
* up on PATH or in shared POSIX bin directories (`~/.local/bin`, `/usr/local/bin`, `/opt/homebrew/bin`):
|
|
187
|
+
* an unrelated `kiro` there, such as the Kiro IDE launcher, must not be run for credential commands.
|
|
182
188
|
*/
|
|
183
189
|
export function resolveKiroCliExecutable(
|
|
184
190
|
inputs: KiroCliNativeInputs & {
|
|
@@ -212,6 +218,8 @@ export function resolveKiroCliExecutable(
|
|
|
212
218
|
: pathEntries.map(entry => posix.join(entry, "kiro-cli"));
|
|
213
219
|
|
|
214
220
|
const installCandidates: string[] = [];
|
|
221
|
+
// Windows only: kiro.exe inside the dedicated Kiro-Cli folders, tried after every canonical name.
|
|
222
|
+
const shortInstallCandidates: string[] = [];
|
|
215
223
|
if (inputs.platform === "win32") {
|
|
216
224
|
const localBase = inputs.env.LOCALAPPDATA?.trim()
|
|
217
225
|
|| (inputs.env.USERPROFILE?.trim() ? win32.join(inputs.env.USERPROFILE.trim(), "AppData", "Local") : "")
|
|
@@ -221,6 +229,11 @@ export function resolveKiroCliExecutable(
|
|
|
221
229
|
win32.join(localBase, "Kiro-Cli", "kiro-cli.exe"),
|
|
222
230
|
win32.join(programFiles, "Kiro-Cli", "kiro-cli.exe"),
|
|
223
231
|
);
|
|
232
|
+
// A relative or drive-relative base would make the short-name lookup depend on the process
|
|
233
|
+
// working directory or current drive, so only a fully qualified drive path qualifies.
|
|
234
|
+
for (const base of [localBase, programFiles]) {
|
|
235
|
+
if (/^[A-Za-z]:[\\/]/.test(base)) shortInstallCandidates.push(win32.join(base, "Kiro-Cli", "kiro.exe"));
|
|
236
|
+
}
|
|
224
237
|
} else if (inputs.platform === "darwin") {
|
|
225
238
|
installCandidates.push(
|
|
226
239
|
posix.join(inputs.home, ".local", "bin", "kiro-cli"),
|
|
@@ -234,7 +247,7 @@ export function resolveKiroCliExecutable(
|
|
|
234
247
|
);
|
|
235
248
|
}
|
|
236
249
|
|
|
237
|
-
for (const candidate of [...pathCandidates, ...installCandidates]) {
|
|
250
|
+
for (const candidate of [...pathCandidates, ...installCandidates, ...shortInstallCandidates]) {
|
|
238
251
|
if (exists(candidate) && isFile(candidate)) return candidate;
|
|
239
252
|
}
|
|
240
253
|
return inputs.platform === "win32" ? "kiro-cli.exe" : "kiro-cli";
|
package/src/oauth/login-cli.ts
CHANGED
|
@@ -189,6 +189,7 @@ export function providerConfigFromKeyLoginProvider(def: KeyLoginProvider, key: s
|
|
|
189
189
|
...(def.noReasoningModels ? { noReasoningModels: [...def.noReasoningModels] } : {}),
|
|
190
190
|
...(def.noTemperatureModels ? { noTemperatureModels: [...def.noTemperatureModels] } : {}),
|
|
191
191
|
...(def.noTopPModels ? { noTopPModels: [...def.noTopPModels] } : {}),
|
|
192
|
+
...(def.noStopModels ? { noStopModels: [...def.noStopModels] } : {}),
|
|
192
193
|
...(def.noPenaltyModels ? { noPenaltyModels: [...def.noPenaltyModels] } : {}),
|
|
193
194
|
...(def.autoToolChoiceOnlyModels ? { autoToolChoiceOnlyModels: [...def.autoToolChoiceOnlyModels] } : {}),
|
|
194
195
|
...(def.preserveReasoningContentModels ? { preserveReasoningContentModels: [...def.preserveReasoningContentModels] } : {}),
|
package/src/providers/derive.ts
CHANGED
|
@@ -41,6 +41,7 @@ export interface DerivedKeyLoginProvider {
|
|
|
41
41
|
noReasoningModels?: string[];
|
|
42
42
|
noTemperatureModels?: string[];
|
|
43
43
|
noTopPModels?: string[];
|
|
44
|
+
noStopModels?: string[];
|
|
44
45
|
noPenaltyModels?: string[];
|
|
45
46
|
autoToolChoiceOnlyModels?: string[];
|
|
46
47
|
preserveReasoningContentModels?: string[];
|
|
@@ -269,6 +270,7 @@ export function providerConfigSeed(entry: ProviderRegistryEntry): OcxProviderCon
|
|
|
269
270
|
...(entry.noReasoningModels ? { noReasoningModels: [...entry.noReasoningModels] } : {}),
|
|
270
271
|
...(entry.noTemperatureModels ? { noTemperatureModels: [...entry.noTemperatureModels] } : {}),
|
|
271
272
|
...(entry.noTopPModels ? { noTopPModels: [...entry.noTopPModels] } : {}),
|
|
273
|
+
...(entry.noStopModels ? { noStopModels: [...entry.noStopModels] } : {}),
|
|
272
274
|
...(entry.noPenaltyModels ? { noPenaltyModels: [...entry.noPenaltyModels] } : {}),
|
|
273
275
|
...(entry.parallelToolCalls !== undefined ? { parallelToolCalls: entry.parallelToolCalls } : {}),
|
|
274
276
|
...(entry.promptCacheKey !== undefined ? { promptCacheKey: entry.promptCacheKey } : {}),
|
|
@@ -336,6 +338,7 @@ export function deriveKeyLoginMap(): Record<string, DerivedKeyLoginProvider> {
|
|
|
336
338
|
...(entry.noReasoningModels ? { noReasoningModels: [...entry.noReasoningModels] } : {}),
|
|
337
339
|
...(entry.noTemperatureModels ? { noTemperatureModels: [...entry.noTemperatureModels] } : {}),
|
|
338
340
|
...(entry.noTopPModels ? { noTopPModels: [...entry.noTopPModels] } : {}),
|
|
341
|
+
...(entry.noStopModels ? { noStopModels: [...entry.noStopModels] } : {}),
|
|
339
342
|
...(entry.noPenaltyModels ? { noPenaltyModels: [...entry.noPenaltyModels] } : {}),
|
|
340
343
|
...(entry.autoToolChoiceOnlyModels ? { autoToolChoiceOnlyModels: [...entry.autoToolChoiceOnlyModels] } : {}),
|
|
341
344
|
...(entry.preserveReasoningContentModels ? { preserveReasoningContentModels: [...entry.preserveReasoningContentModels] } : {}),
|
|
@@ -552,6 +555,7 @@ export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig
|
|
|
552
555
|
if (!prov.noReasoningModels && seed.noReasoningModels) prov.noReasoningModels = [...seed.noReasoningModels];
|
|
553
556
|
if (!prov.noTemperatureModels && seed.noTemperatureModels) prov.noTemperatureModels = [...seed.noTemperatureModels];
|
|
554
557
|
if (!prov.noTopPModels && seed.noTopPModels) prov.noTopPModels = [...seed.noTopPModels];
|
|
558
|
+
if (!prov.noStopModels && seed.noStopModels) prov.noStopModels = [...seed.noStopModels];
|
|
555
559
|
if (!prov.noPenaltyModels && seed.noPenaltyModels) prov.noPenaltyModels = [...seed.noPenaltyModels];
|
|
556
560
|
if (prov.parallelToolCalls === undefined && seed.parallelToolCalls !== undefined) prov.parallelToolCalls = seed.parallelToolCalls;
|
|
557
561
|
if (prov.promptCacheKey === undefined && seed.promptCacheKey !== undefined) prov.promptCacheKey = seed.promptCacheKey;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { OcxProviderConfig } from "../types";
|
|
2
|
+
import { getProviderRegistryEntry } from "./registry";
|
|
3
|
+
import type { ProviderRegistryEntry } from "./registry/types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Whether a provider's Fast lane is switched off.
|
|
7
|
+
*
|
|
8
|
+
* `fastEnabled: false` turns Fast off on any provider. A registry entry marked `fastOptIn` bills its
|
|
9
|
+
* Fast lane beyond the plan (Anthropic fast mode draws usage credits at 2x price), so it stays off
|
|
10
|
+
* until the operator sets `fastEnabled: true`. Off is expressed as provider capability `false`,
|
|
11
|
+
* which every Fast consumer already treats as a global denial.
|
|
12
|
+
*
|
|
13
|
+
* The registry entry is matched by name without a transport check on purpose: this can only turn
|
|
14
|
+
* Fast off, so a custom endpoint that reuses the name loses nothing it could safely keep.
|
|
15
|
+
*/
|
|
16
|
+
export function fastSwitchOff(
|
|
17
|
+
provider: Pick<OcxProviderConfig, "fastEnabled">,
|
|
18
|
+
entry: Pick<ProviderRegistryEntry, "fastOptIn"> | undefined,
|
|
19
|
+
): boolean {
|
|
20
|
+
if (provider.fastEnabled === false) return true;
|
|
21
|
+
if (provider.fastEnabled === true) return false;
|
|
22
|
+
return entry?.fastOptIn === true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** `fastSwitchOff` with the registry entry looked up by provider name. */
|
|
26
|
+
export function providerFastSwitchOff(
|
|
27
|
+
providerName: string | undefined,
|
|
28
|
+
provider: Pick<OcxProviderConfig, "fastEnabled">,
|
|
29
|
+
): boolean {
|
|
30
|
+
return fastSwitchOff(provider, providerName ? getProviderRegistryEntry(providerName) : undefined);
|
|
31
|
+
}
|
|
@@ -25,6 +25,7 @@ export const PROVIDER_MODEL_RENAME_ROLES = {
|
|
|
25
25
|
requiresPairedResponsesToolResults: "none",
|
|
26
26
|
annotateEmptyToolOutputs: "none",
|
|
27
27
|
supportsServiceTier: "none",
|
|
28
|
+
fastEnabled: "none",
|
|
28
29
|
modelSupportsServiceTier: "record",
|
|
29
30
|
preserveResponsesReasoningContent: "none",
|
|
30
31
|
modelReasoningEffortsAuthoritative: "none",
|
|
@@ -101,6 +102,7 @@ export const PROVIDER_MODEL_RENAME_ROLES = {
|
|
|
101
102
|
noReasoningModels: "list",
|
|
102
103
|
noTemperatureModels: "list",
|
|
103
104
|
noTopPModels: "list",
|
|
105
|
+
noStopModels: "list",
|
|
104
106
|
noPenaltyModels: "list",
|
|
105
107
|
noStructuredOutputModels: "list",
|
|
106
108
|
noJsonSchemaModels: "list",
|
|
@@ -95,6 +95,8 @@ export function rewriteProviderReferences(config: OcxConfig, from: string, to: s
|
|
|
95
95
|
|
|
96
96
|
routeRecordValues(config.claudeCode?.tierModels as Record<string, string> | undefined);
|
|
97
97
|
routeRecordValues(config.claudeCode?.modelMap as Record<string, string> | undefined);
|
|
98
|
+
// First-party picker bindings hold routes too; their keys are Anthropic picker ids.
|
|
99
|
+
routeRecordValues(config.claudeCode?.intercept?.modelMap);
|
|
98
100
|
|
|
99
101
|
// Bare provider ids.
|
|
100
102
|
for (const model of config.customModels ?? []) {
|
|
@@ -371,9 +371,15 @@ async function fetchDeepSeekQuota(provider: string, config: OcxProviderConfig):
|
|
|
371
371
|
const toppedUp = toFiniteNumber(preferred.topped_up_balance);
|
|
372
372
|
const balance = totalBalance ?? grantedBalance ?? toppedUp;
|
|
373
373
|
if (balance === undefined || balance < 0) return null;
|
|
374
|
+
// The rows are currency-scoped, so the symbol has to follow the row that was
|
|
375
|
+
// picked: the two glyph currencies keep their sign, any other ISO code
|
|
376
|
+
// prefixes the amount, and a row without one keeps the legacy dollar.
|
|
377
|
+
const currency = String(preferred.currency ?? "").trim().toUpperCase();
|
|
378
|
+
const sign = currency === "CNY" ? "¥" : currency === "" || currency === "USD" ? "$" : `${currency} `;
|
|
379
|
+
const amount = (value: number) => `${sign}${value.toFixed(2)}`;
|
|
374
380
|
const label = grantedBalance !== undefined && grantedBalance > 0
|
|
375
|
-
? `API balance (
|
|
376
|
-
: `API balance (
|
|
381
|
+
? `API balance (${amount(balance)} total, ${amount(grantedBalance)} granted)`
|
|
382
|
+
: `API balance (${amount(balance)})`;
|
|
377
383
|
return report(provider, "deepseek:balance", {
|
|
378
384
|
customWindows: [{ label, percent: 0 }],
|
|
379
385
|
updatedAt: Date.now(),
|
|
@@ -267,6 +267,29 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
|
|
|
267
267
|
// 260813: grok-4.6 added per docs.x.ai/developers/grok-4-6. Context/vision still match
|
|
268
268
|
// grok-4.5; the reasoning ladder does not — 4.6 adds the documented xhigh rung.
|
|
269
269
|
models: XAI_MODELS,
|
|
270
|
+
// grok-4.7-build-fast arrives only through OAuth discovery. We read it as the Grok Build id of
|
|
271
|
+
// what xAI documents as Grok 4.7 Fast: "the same model served on faster infrastructure",
|
|
272
|
+
// offered in Cursor and Grok Build only, not on the public xAI API (docs.x.ai/developers/grok-4-7,
|
|
273
|
+
// fetched 2026-09-24). It therefore inherits grok-4.7's documented facts in the lists below.
|
|
274
|
+
// Its wire pin and service tier stay unclaimed until probed, which is why it is absent from
|
|
275
|
+
// XAI_MODELS, modelWireDefaults and modelSupportsServiceTier.
|
|
276
|
+
// Live 2026-09-20: Chat Completions rejects `stop` on grok-4.6
|
|
277
|
+
// (`400 invalid-argument "Model grok-4.6 does not support parameter stop."`).
|
|
278
|
+
// xAI documents `stop` as unsupported for reasoning models. Claude Code
|
|
279
|
+
// auto-mode always sends stop_sequences; forwarding that as `stop` makes
|
|
280
|
+
// the classifier treat Grok as temporarily unavailable while chat turns
|
|
281
|
+
// still work. Keep caller stop sequences on non-reasoning ids.
|
|
282
|
+
// Live 2026-09-23: grok-4.7 answers the same 400.
|
|
283
|
+
noStopModels: [
|
|
284
|
+
"grok-4.7",
|
|
285
|
+
"grok-4.7-build-fast",
|
|
286
|
+
"grok-4.6",
|
|
287
|
+
"grok-4.5",
|
|
288
|
+
"grok-4.3",
|
|
289
|
+
"grok-4.20-multi-agent-0309",
|
|
290
|
+
"grok-4.20-0309-reasoning",
|
|
291
|
+
"grok-build-0.1",
|
|
292
|
+
],
|
|
270
293
|
// Measured only on grok-4.6 against cli-chat-proxy.grok.com: even an invalid
|
|
271
294
|
// `text.verbosity` value is accepted and low/high/omitted output length is non-monotonic.
|
|
272
295
|
// Apply the resulting opt-out to the whole xAI lineup because `text.verbosity` is an OpenAI
|
|
@@ -278,6 +301,20 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
|
|
|
278
301
|
// absent from xAI's documented API, so a model discovered later has no more support for it
|
|
279
302
|
// than the seeded ones do.
|
|
280
303
|
supportsVerbosity: false,
|
|
304
|
+
// docs.x.ai/docs/guides/reasoning: presencePenalty and frequencyPenalty "cannot be used with
|
|
305
|
+
// reasoning models. Requests that include them return an error." Live 2026-09-23: grok-4.7
|
|
306
|
+
// answers 400 invalid-argument "Model grok-4.7 does not support parameter presencePenalty."
|
|
307
|
+
// Non-reasoning ids keep caller penalties.
|
|
308
|
+
noPenaltyModels: [
|
|
309
|
+
"grok-4.7",
|
|
310
|
+
"grok-4.7-build-fast",
|
|
311
|
+
"grok-4.6",
|
|
312
|
+
"grok-4.5",
|
|
313
|
+
"grok-4.3",
|
|
314
|
+
"grok-4.20-multi-agent-0309",
|
|
315
|
+
"grok-4.20-0309-reasoning",
|
|
316
|
+
"grok-build-0.1",
|
|
317
|
+
],
|
|
281
318
|
defaultModel: "grok-4.5",
|
|
282
319
|
// Grok 4.7/4.6/4.5 subscription Responses callers use the native wire with the existing
|
|
283
320
|
// namespace/web-search/replay normalization. Chat remains an explicit modelAdapters
|
|
@@ -335,6 +372,7 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
|
|
|
335
372
|
// (they are already listed in noVisionModels below).
|
|
336
373
|
modelInputModalities: {
|
|
337
374
|
"grok-4.7": ["text", "image"],
|
|
375
|
+
"grok-4.7-build-fast": ["text", "image"],
|
|
338
376
|
"grok-4.6": ["text", "image"],
|
|
339
377
|
"grok-4.5": ["text", "image"],
|
|
340
378
|
"grok-4.3": ["text", "image"],
|
|
@@ -347,7 +385,7 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
|
|
|
347
385
|
// reasoning_content as the top cause of prompt-cache misses on multi-turn conversations
|
|
348
386
|
// (docs.x.ai prompt-caching/multi-turn, verified 2026-07-13 — devlog/_plan/260713_grok_caching).
|
|
349
387
|
// Models that never emit reasoning simply have no thinking parts to replay (no-op).
|
|
350
|
-
preserveReasoningContentModels: ["grok-4.7", "grok-4.6", "grok-4.5", "grok-4.3", "grok-4.20-0309-reasoning"],
|
|
388
|
+
preserveReasoningContentModels: ["grok-4.7", "grok-4.7-build-fast", "grok-4.6", "grok-4.5", "grok-4.3", "grok-4.20-0309-reasoning"],
|
|
351
389
|
// grok-4.5 reasoning is always-on with low/medium/high (no off tier, no xhigh).
|
|
352
390
|
// grok-4.6 adds xhigh per docs.x.ai/developers/model-capabilities/text/reasoning;
|
|
353
391
|
// multi-agent accepts the same four wire values to select 4 or 16 collaborators. xAI
|
|
@@ -356,15 +394,17 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
|
|
|
356
394
|
// 2026-09-23 live probe accepted low..xhigh and rejected max on both wires;
|
|
357
395
|
// devlog/_plan/260923_grok47_parity/010_probe-evidence.md.
|
|
358
396
|
"grok-4.7": ["low", "medium", "high", "xhigh"],
|
|
397
|
+
"grok-4.7-build-fast": ["low", "medium", "high", "xhigh"],
|
|
359
398
|
"grok-4.6": ["low", "medium", "high", "xhigh"],
|
|
360
399
|
"grok-4.5": ["low", "medium", "high"],
|
|
361
400
|
"grok-4.20-multi-agent-0309": ["low", "medium", "high", "xhigh"],
|
|
362
401
|
},
|
|
363
|
-
modelDefaultReasoningEfforts: { "grok-4.7": "high", "grok-4.6": "high" },
|
|
402
|
+
modelDefaultReasoningEfforts: { "grok-4.7": "high", "grok-4.7-build-fast": "high", "grok-4.6": "high" },
|
|
364
403
|
modelContextWindows: {
|
|
365
404
|
// 500k confirmed by context_length_exceeded:
|
|
366
405
|
// devlog/_plan/260923_grok47_parity/010_probe-evidence.md.
|
|
367
406
|
"grok-4.7": 500_000,
|
|
407
|
+
"grok-4.7-build-fast": 500_000,
|
|
368
408
|
"grok-4.6": 500_000,
|
|
369
409
|
"grok-4.5": 500_000,
|
|
370
410
|
"grok-4.3": 1_000_000,
|
|
@@ -446,9 +486,11 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
|
|
|
446
486
|
// Claude fast mode on the subscription lane (Claude Code `/fast`): the OAuth route accepts
|
|
447
487
|
// `speed` and gates it on account entitlement (usage credits / org enablement), probed live
|
|
448
488
|
// 2026-09-23 (devlog/_plan/260923_anthropic_fast_speed/020_probe-evidence.md).
|
|
489
|
+
// Off until the operator opts in: fast mode draws usage credits at 2x price.
|
|
449
490
|
fastWire: ANTHROPIC_FAST_WIRE,
|
|
450
491
|
modelSupportsServiceTier: { ...ANTHROPIC_FAST_MODELS },
|
|
451
492
|
fastTierDescription: ANTHROPIC_FAST_TIER_DESCRIPTION,
|
|
493
|
+
fastOptIn: true,
|
|
452
494
|
},
|
|
453
495
|
{
|
|
454
496
|
id: "anthropic-apikey",
|
|
@@ -471,6 +513,7 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
|
|
|
471
513
|
fastWire: ANTHROPIC_FAST_WIRE,
|
|
472
514
|
modelSupportsServiceTier: { ...ANTHROPIC_FAST_MODELS },
|
|
473
515
|
fastTierDescription: ANTHROPIC_FAST_TIER_DESCRIPTION,
|
|
516
|
+
fastOptIn: true,
|
|
474
517
|
},
|
|
475
518
|
{
|
|
476
519
|
id: "kimi",
|
|
@@ -108,6 +108,12 @@ import {
|
|
|
108
108
|
STEPFUN_MODEL_INPUT_MODALITIES,
|
|
109
109
|
STEPFUN_NO_VISION_MODELS,
|
|
110
110
|
STEPFUN_REASONING_EFFORTS,
|
|
111
|
+
ANTHROPIC_MODELS,
|
|
112
|
+
ANTHROPIC_MODEL_CONTEXT_WINDOWS,
|
|
113
|
+
ANTHROPIC_MODEL_INPUT_MODALITIES,
|
|
114
|
+
ANTHROPIC_MODEL_REASONING_EFFORTS,
|
|
115
|
+
ANTHROPIC_REASONING_EFFORTS,
|
|
116
|
+
ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS,
|
|
111
117
|
} from "./model-seeds";
|
|
112
118
|
|
|
113
119
|
export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
|
|
@@ -824,15 +830,30 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
|
|
|
824
830
|
// glm-5.3 carry live end-to-end evidence there (custom tools, reasoning replay, streaming,
|
|
825
831
|
// multi-turn continuation).
|
|
826
832
|
//
|
|
827
|
-
// That is
|
|
828
|
-
//
|
|
829
|
-
//
|
|
830
|
-
//
|
|
831
|
-
//
|
|
832
|
-
//
|
|
833
|
-
//
|
|
834
|
-
//
|
|
835
|
-
//
|
|
833
|
+
// That evidence is now expressed as a modelWireDefaults pin scoped to Responses inbound
|
|
834
|
+
// only: Codex clients ride the native wire with zero translation hops, while chat and
|
|
835
|
+
// anthropic inbound keep the provider-wide chat wire and its measured prefix-cache
|
|
836
|
+
// behavior. The pin was held back until the one open delta was closed with its own live
|
|
837
|
+
// evidence: the Responses serializer replays reasoning content through the separate
|
|
838
|
+
// preserveResponsesReasoningContent flag, which the Chat-side preserveReasoningContentModels
|
|
839
|
+
// list does not cover. Measured 260922 on this gateway (#5188): a two-turn replay that
|
|
840
|
+
// round-trips a reasoning item WITH its plaintext content array is accepted (HTTP 200) and
|
|
841
|
+
// the model continues from it, so the flag is set beside the pins — the same pairing Z.AI
|
|
842
|
+
// and DeepSeek use. qwen3.7-plus is the one pinned model in thinkingBudgetModels, and its
|
|
843
|
+
// full low/medium/high/xhigh/max effort ladder is accepted as reasoning.effort strings on
|
|
844
|
+
// this wire (measured same day), so the Responses path does not need the numeric
|
|
845
|
+
// thinking_budget translation the Chat wire applies. The rest of the family stays a
|
|
846
|
+
// documented per-model modelAdapters opt-in; modelAdapters always wins over the pin in
|
|
847
|
+
// both directions.
|
|
848
|
+
// tests/providers/alibaba-token-plan-responses-optin.test.ts holds the opt-in half and the
|
|
849
|
+
// flag guard; tests/providers/alibaba-token-plan-wire-defaults.test.ts holds the pins.
|
|
850
|
+
// The intl sibling stays unpinned until the same four-axis verification runs against its
|
|
851
|
+
// gateway (its /responses route is registered, #5097).
|
|
852
|
+
modelWireDefaults: {
|
|
853
|
+
"qwen3.8-flash": { wire: "openai-responses", inbound: ["responses"] },
|
|
854
|
+
"qwen3.7-plus": { wire: "openai-responses", inbound: ["responses"] },
|
|
855
|
+
"glm-5.3": { wire: "openai-responses", inbound: ["responses"] },
|
|
856
|
+
},
|
|
836
857
|
note: "Token Plan Personal Edition · China (Beijing)",
|
|
837
858
|
modelInputModalities: ALIBABA_TOKEN_PLAN_INPUT_MODALITIES,
|
|
838
859
|
modelContextWindows: ALIBABA_TOKEN_PLAN_CONTEXT_WINDOWS,
|
|
@@ -862,6 +883,10 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
|
|
|
862
883
|
directReasoningEffortModels: QWEN38_FAMILY,
|
|
863
884
|
thinkingBudgetModels: ALIBABA_TOKEN_PLAN_QWEN_MODELS.filter(id => !QWEN38_FAMILY.includes(id)),
|
|
864
885
|
preserveReasoningContentModels: ALIBABA_TOKEN_PLAN_PRESERVE_REASONING,
|
|
886
|
+
// Responses replay uses this provider-level flag, not the Chat-path model list above;
|
|
887
|
+
// measured live on this gateway (see the pin comment). The model list still covers a
|
|
888
|
+
// caller who opts back into Chat.
|
|
889
|
+
preserveResponsesReasoningContent: true,
|
|
865
890
|
noVisionModels: ALIBABA_TOKEN_PLAN_NO_VISION,
|
|
866
891
|
// The gateway accepts prompt_cache_key on every Token Plan chat model (probed 260902).
|
|
867
892
|
promptCacheKey: true,
|
|
@@ -1199,11 +1224,23 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
|
|
|
1199
1224
|
adapter: "openai-chat",
|
|
1200
1225
|
authKind: "key",
|
|
1201
1226
|
dashboardUrl: "https://xiaomimimo.com",
|
|
1202
|
-
// Token-plan roster per Xiaomi's token-plan model list (V2.6 Pro and Flash).
|
|
1203
|
-
//
|
|
1204
|
-
//
|
|
1227
|
+
// Token-plan roster per Xiaomi's token-plan model list (V2.6 Pro and Flash). Model-level facts
|
|
1228
|
+
// come from Xiaomi's model pages (mimo.mi.com/models/en-US/<id>, fetched 2026-09-24): 1M context,
|
|
1229
|
+
// 128K max output; V2.6 Pro/Flash and V2.5 take text/image/video/audio, V2.5 Pro text only. The
|
|
1230
|
+
// catalog vocabulary has no video or audio, so only text/image are claimed. The token plan speaks
|
|
1231
|
+
// the same API format as pay-as-you-go, so these are model facts rather than plan facts. No
|
|
1232
|
+
// jawcodeBundle: pricing and entitlement stay unclaimed, and usage estimates still come from the
|
|
1233
|
+
// model-level vendor price fallback, exactly as they did for V2.5.
|
|
1205
1234
|
defaultModel: "mimo-v2.6-pro",
|
|
1206
1235
|
models: ["mimo-v2.6-pro", "mimo-v2.6-flash", "mimo-v2.5-pro", "mimo-v2.5"],
|
|
1236
|
+
modelContextWindows: { "mimo-v2.6-pro": 1_048_576, "mimo-v2.6-flash": 1_048_576, "mimo-v2.5-pro": 1_048_576, "mimo-v2.5": 1_048_576 },
|
|
1237
|
+
modelMaxOutputTokens: { "mimo-v2.6-pro": 131_072, "mimo-v2.6-flash": 131_072, "mimo-v2.5-pro": 131_072, "mimo-v2.5": 131_072 },
|
|
1238
|
+
modelInputModalities: {
|
|
1239
|
+
"mimo-v2.6-pro": ["text", "image"],
|
|
1240
|
+
"mimo-v2.6-flash": ["text", "image"],
|
|
1241
|
+
"mimo-v2.5": ["text", "image"],
|
|
1242
|
+
"mimo-v2.5-pro": ["text"],
|
|
1243
|
+
},
|
|
1207
1244
|
// The gateway validates the ladder strictly and rejects anything above `high`.
|
|
1208
1245
|
reasoningEfforts: ["low", "medium", "high"],
|
|
1209
1246
|
reasoningEffortMap: { xhigh: "high", max: "high", ultra: "high" },
|
|
@@ -1401,4 +1438,54 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
|
|
|
1401
1438
|
reasoningEfforts: STEPFUN_REASONING_EFFORTS,
|
|
1402
1439
|
note: "StepFun (阶跃星辰) official OpenAI-compatible API.",
|
|
1403
1440
|
},
|
|
1441
|
+
{
|
|
1442
|
+
// Official Claude Code CLI as the transport for a Claude subscription (§三十一). The CLI owns
|
|
1443
|
+
// the account: this row stores no token and the adapter reads and injects none, so the request
|
|
1444
|
+
// path is Anthropic's own harness rather than a replayed Claude Code identity against the
|
|
1445
|
+
// Messages API. `baseUrl` is the destination the subscription's traffic reaches; OpenCodex
|
|
1446
|
+
// never sends it. Fails closed if the row's base URL is overridden.
|
|
1447
|
+
// v1 runs tools-disabled (`--tools ""`, no `--mcp-config`) so the client keeps tool ownership:
|
|
1448
|
+
// text/reasoning only until the shared capture-only tool bridge lands. Requires the CLI:
|
|
1449
|
+
// `npm i -g @anthropic-ai/claude-code`, plus a signed-in session (`claude` -> /login).
|
|
1450
|
+
// GOVERNANCE: whether a subscription login may be driven through a proxy for a third-party
|
|
1451
|
+
// agent is Anthropic's call rather than OpenCodex's — flagged for maintainer review, as with
|
|
1452
|
+
// the CodeBuddy rows above.
|
|
1453
|
+
id: "claude-cli",
|
|
1454
|
+
label: "Claude Code CLI (subscription)",
|
|
1455
|
+
adapter: "claude-cli",
|
|
1456
|
+
baseUrl: "https://api.anthropic.com",
|
|
1457
|
+
// `key` + `keyOptional`, deliberately not `local`. "local" (Ollama, vLLM, LM Studio) means the
|
|
1458
|
+
// traffic never leaves the machine and there is no credential to classify; this row reaches
|
|
1459
|
+
// api.anthropic.com, so `local` misreported it wherever auth is classified — the account
|
|
1460
|
+
// surface answered "local provider ... has no credentials" (`classifyAccount`,
|
|
1461
|
+
// src/cli/account-api.ts) and the dashboard filed the row as a local runtime. What IS true is
|
|
1462
|
+
// keyless: the CLI reads the operator's own sign-in, so `keyOptional` is the existing flag that
|
|
1463
|
+
// exempts a row from key enforcement without claiming a key exists. Key rows are also what
|
|
1464
|
+
// `deriveProviderPresets` lists, so this entry needs no `dashboardPreset` flag to stay
|
|
1465
|
+
// reachable from the Providers page.
|
|
1466
|
+
authKind: "key",
|
|
1467
|
+
keyOptional: true,
|
|
1468
|
+
// There is no key console for a keyless row: the link that helps an operator is the one that
|
|
1469
|
+
// documents the install and sign-in this provider requires.
|
|
1470
|
+
dashboardUrl: "https://docs.claude.com/en/docs/claude-code/setup",
|
|
1471
|
+
defaultModel: "claude-sonnet-5",
|
|
1472
|
+
models: [...ANTHROPIC_MODELS],
|
|
1473
|
+
// Static roster, exactly like the CodeBuddy rows. Without this the catalog treats the row as a
|
|
1474
|
+
// live-discovery candidate and requests a model list the CLI route never serves: a real start
|
|
1475
|
+
// logged `Provider model discovery for "claude-cli" failed with HTTP 404` and then fell back to
|
|
1476
|
+
// these ids anyway. `liveModels: false` makes the configured roster authoritative and skips the
|
|
1477
|
+
// request entirely (src/codex/catalog/provider-models.ts).
|
|
1478
|
+
liveModels: false,
|
|
1479
|
+
modelContextWindows: { ...ANTHROPIC_MODEL_CONTEXT_WINDOWS },
|
|
1480
|
+
// Text-only for v1, not the image modality the Messages API rows publish. The CLI parses an
|
|
1481
|
+
// image frame (verified against 2.1.270), but a headless turn has no verified contract that the
|
|
1482
|
+
// harness hands those bytes to the model, and advertising a modality the route cannot honour
|
|
1483
|
+
// makes a route selection pick this row for a picture it then answers blind. The adapter refuses
|
|
1484
|
+
// direct image input for the same reason; the vision sidecar still captions images into text.
|
|
1485
|
+
noVisionModels: [...ANTHROPIC_MODELS],
|
|
1486
|
+
reasoningEfforts: ANTHROPIC_REASONING_EFFORTS,
|
|
1487
|
+
modelReasoningEfforts: { ...ANTHROPIC_MODEL_REASONING_EFFORTS },
|
|
1488
|
+
defaultMaxOutputTokens: ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS,
|
|
1489
|
+
note: "Runs Claude subscription traffic through Anthropic's own harness: the official Claude Code CLI headlessly (`claude -p`), one turn per request. OpenCodex stores no Claude token, reads none and injects none — the CLI signs in and bills the account itself, which is why this row is keyless and an API key saved here never reaches the harness (use `anthropic-apikey` for key billing). The sign-in is the one of the user this proxy runs as, so every request served through this row — by any client of this proxy — spends that same account; OpenCodex neither pools nor multiplexes Claude sign-ins. Requires the CLI (`npm i -g @anthropic-ai/claude-code`) and a signed-in session (`claude` -> /login). v1 disables CLI tools (--tools \"\", --strict-mcp-config) so the client retains tool ownership: text/reasoning only for now. Subscription routing authorization flagged for maintainer review.",
|
|
1490
|
+
},
|
|
1404
1491
|
];
|
|
@@ -83,6 +83,7 @@ export const REGISTRY_FIELD_MODEL_ID_ROLES = {
|
|
|
83
83
|
modelSupportsServiceTier: RECORD_KEYS,
|
|
84
84
|
keyAuthServiceTier: KEY_AUTH_SERVICE_TIER,
|
|
85
85
|
fastTierDescription: NONE,
|
|
86
|
+
fastOptIn: NONE,
|
|
86
87
|
modelServiceTierCapabilityBaseUrlGuard: NONE,
|
|
87
88
|
preserveResponsesReasoningContent: NONE,
|
|
88
89
|
dropResponsesReasoningItems: NONE,
|
|
@@ -107,6 +108,7 @@ export const REGISTRY_FIELD_MODEL_ID_ROLES = {
|
|
|
107
108
|
noReasoningModels: NONE,
|
|
108
109
|
noTemperatureModels: NONE,
|
|
109
110
|
noTopPModels: NONE,
|
|
111
|
+
noStopModels: NONE,
|
|
110
112
|
noPenaltyModels: NONE,
|
|
111
113
|
noJsonSchemaModels: NONE,
|
|
112
114
|
parallelToolCalls: NONE,
|
|
@@ -257,6 +257,11 @@ export interface ProviderRegistryEntry {
|
|
|
257
257
|
};
|
|
258
258
|
/** Provider-specific copy for the Codex catalog's Fast tier. */
|
|
259
259
|
fastTierDescription?: string;
|
|
260
|
+
/**
|
|
261
|
+
* The Fast lane is billed beyond the plan, so it stays off until the operator sets
|
|
262
|
+
* `providers.<name>.fastEnabled: true` (see `providerFastSwitchOff`).
|
|
263
|
+
*/
|
|
264
|
+
fastOptIn?: boolean;
|
|
260
265
|
/**
|
|
261
266
|
* Registry-only destination guard for `modelSupportsServiceTier`. This scopes vendor evidence
|
|
262
267
|
* without changing provider ownership, routing, authentication, or config validation.
|
|
@@ -308,6 +313,7 @@ export interface ProviderRegistryEntry {
|
|
|
308
313
|
noReasoningModels?: string[];
|
|
309
314
|
noTemperatureModels?: string[];
|
|
310
315
|
noTopPModels?: string[];
|
|
316
|
+
noStopModels?: string[];
|
|
311
317
|
noPenaltyModels?: string[];
|
|
312
318
|
/**
|
|
313
319
|
* Registry-only seed for `OcxProviderConfig.noJsonSchemaModels`. Merged into the
|
|
@@ -359,7 +365,7 @@ export type ProviderConfigSeed = Pick<
|
|
|
359
365
|
| "modelDisplayNames"
|
|
360
366
|
| "modelMaxInputTokens" | "defaultMaxOutputTokens" | "modelMaxOutputTokens"
|
|
361
367
|
| "reasoningEfforts" | "modelReasoningEfforts" | "modelDefaultReasoningEfforts" | "reasoningEffortMap" | "modelReasoningEffortMap" | "reasoningWireFormat"
|
|
362
|
-
| "noVisionModels" | "noReasoningModels" | "noTemperatureModels" | "noTopPModels" | "noPenaltyModels"
|
|
368
|
+
| "noVisionModels" | "noReasoningModels" | "noTemperatureModels" | "noTopPModels" | "noStopModels" | "noPenaltyModels"
|
|
363
369
|
| "autoToolChoiceOnlyModels" | "preserveReasoningContentModels" | "requiresReasoningPlaceholderModels" | "reasoningSplitModels" | "inlineThinkTagModels" | "reasoningDetailsModels" | "thinkingToggleModels" | "thinkingBudgetModels" | "escapeBuiltinToolNames" | "openaiChatEofTolerance" | "showThinkingSummary"
|
|
364
370
|
| "googleMode" | "project" | "location" | "headers"
|
|
365
371
|
>;
|
|
@@ -2,6 +2,7 @@ import type { ModelCapabilities, OcxProviderConfig } from "../types";
|
|
|
2
2
|
import { MODEL_ADAPTER_OVERRIDE_ALLOWED, pinnedWireAdapter } from "../types";
|
|
3
3
|
import { isCanonicalOpenAiForwardProvider } from "./openai-tiers";
|
|
4
4
|
import { resolveProviderAuthTransport } from "./fastwire";
|
|
5
|
+
import { fastSwitchOff } from "./fast-opt-in";
|
|
5
6
|
import { registryEntrySupportsLiveModelDiscovery } from "./static-model-discovery";
|
|
6
7
|
import type { InboundWire, ProviderRegistryEntry, ResponsesTerminalRepairPolicy } from "./registry/types";
|
|
7
8
|
import {
|
|
@@ -30,7 +31,7 @@ export type StaticProviderPolicyField =
|
|
|
30
31
|
| "modelMaxOutputTokens" | "reasoningEfforts" | "modelReasoningEfforts" | "modelReasoningEffortsAuthoritative"
|
|
31
32
|
| "modelDefaultReasoningEfforts" | "reasoningEffortMap" | "modelReasoningEffortMap"
|
|
32
33
|
| "reasoningWireFormat" | "noVisionModels" | "noReasoningModels" | "noTemperatureModels"
|
|
33
|
-
| "noTopPModels" | "noPenaltyModels" | "noJsonSchemaModels" | "parallelToolCalls"
|
|
34
|
+
| "noTopPModels" | "noStopModels" | "noPenaltyModels" | "noJsonSchemaModels" | "parallelToolCalls"
|
|
34
35
|
| "promptCacheKey" | "chatServiceTier" | "openaiChatEofTolerance" | "statelessResponses"
|
|
35
36
|
| "requiresAdjacentResponsesToolResults" | "requiresPairedResponsesToolResults" | "annotateEmptyToolOutputs"
|
|
36
37
|
| "fastWire" | "supportsServiceTier" | "modelSupportsServiceTier" | "supportsOpenAiWebSearchToolFields"
|
|
@@ -187,7 +188,10 @@ export function resolveModelPolicy(input: ResolveModelPolicyInput): ResolvedMode
|
|
|
187
188
|
legacyClinePassLadder || provider.reasoningEfforts === undefined ? entry?.reasoningEfforts ? "registry" : "unknown" : "operator");
|
|
188
189
|
put("chatServiceTier", provider.chatServiceTier ?? keyAuthDefaults?.chatServiceTier ?? entry?.chatServiceTier,
|
|
189
190
|
provider.chatServiceTier !== undefined ? "operator" : keyAuthDefaults?.chatServiceTier !== undefined ? "registry" : entry?.chatServiceTier !== undefined ? "registry" : "unknown");
|
|
190
|
-
|
|
191
|
+
// The Fast switch overrides every capability source; see providerFastSwitchOff.
|
|
192
|
+
const fastOff = fastSwitchOff(provider, input.registryEntry);
|
|
193
|
+
if (fastOff) put("supportsServiceTier", false, provider.fastEnabled === false ? "operator" : "registry");
|
|
194
|
+
else put("supportsServiceTier", provider.supportsServiceTier ?? keyAuthDefaults?.supportsServiceTier ?? entry?.supportsServiceTier,
|
|
191
195
|
provider.supportsServiceTier !== undefined ? "operator" : keyAuthDefaults?.supportsServiceTier !== undefined ? "registry" : entry?.supportsServiceTier !== undefined ? "registry" : "unknown");
|
|
192
196
|
if (entry && !registryEntrySupportsLiveModelDiscovery(entry)) put("liveModels", false, "registry");
|
|
193
197
|
putMergedMap("modelDisplayNames", entry?.modelDisplayNames, provider.modelDisplayNames);
|
|
@@ -226,6 +230,7 @@ export function resolveModelPolicy(input: ResolveModelPolicyInput): ResolvedMode
|
|
|
226
230
|
put("modelReasoningEffortMap", modelEffortMap, modelEffortMapSource);
|
|
227
231
|
for (const key of [
|
|
228
232
|
"noVisionModels", "noReasoningModels", "noTemperatureModels", "noTopPModels",
|
|
233
|
+
"noStopModels",
|
|
229
234
|
"noPenaltyModels", "noJsonSchemaModels", "autoToolChoiceOnlyModels",
|
|
230
235
|
"preserveReasoningContentModels", "requiresReasoningPlaceholderModels",
|
|
231
236
|
"reasoningSplitModels", "reasoningDetailsModels", "thinkingToggleModels", "thinkingBudgetModels",
|
|
@@ -324,7 +329,9 @@ export function resolveModelPolicy(input: ResolveModelPolicyInput): ResolvedMode
|
|
|
324
329
|
const modelSupportsReasoningSummaries = modelValue(providerPolicy.modelSupportsReasoningSummaries)
|
|
325
330
|
?? (modelValue(providerPolicy.modelReasoningSummaryDelivery) !== undefined ? true : undefined);
|
|
326
331
|
const modelSupportsVerbosity = modelValue(providerPolicy.modelSupportsVerbosity) ?? providerPolicy.supportsVerbosity;
|
|
327
|
-
const modelSupportsServiceTier =
|
|
332
|
+
const modelSupportsServiceTier = fastOff
|
|
333
|
+
? false
|
|
334
|
+
: modelValue(providerPolicy.modelSupportsServiceTier) ?? providerPolicy.supportsServiceTier;
|
|
328
335
|
const model: ResolvedPerModelStaticPolicy = {
|
|
329
336
|
adapter,
|
|
330
337
|
...(modelContextWindow !== undefined ? { contextWindow: modelContextWindow } : {}),
|
|
@@ -379,7 +386,7 @@ export function resolveModelPolicy(input: ResolveModelPolicyInput): ResolvedMode
|
|
|
379
386
|
}
|
|
380
387
|
modelProvenance.supportsVerbosity = modelOrProviderSource(provider.modelSupportsVerbosity, entry?.modelSupportsVerbosity, provider.supportsVerbosity, entry?.supportsVerbosity);
|
|
381
388
|
const exactServiceTierSource = modelSource(provider.modelSupportsServiceTier, registryServiceTierDefaults);
|
|
382
|
-
modelProvenance.supportsServiceTier = exactServiceTierSource !== "unknown" ? exactServiceTierSource
|
|
389
|
+
modelProvenance.supportsServiceTier = !fastOff && exactServiceTierSource !== "unknown" ? exactServiceTierSource
|
|
383
390
|
: providerProvenance.supportsServiceTier ?? "unknown";
|
|
384
391
|
modelProvenance.responsesUpstreamStreaming = model.responsesUpstreamStreaming === undefined ? "unknown" : "registry";
|
|
385
392
|
modelProvenance.responsesTerminalRepair = model.responsesTerminalRepair === undefined ? "unknown" : "registry";
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
type FastPolicyAuthority,
|
|
16
16
|
type ResolvedFastPolicy,
|
|
17
17
|
} from "./fastwire";
|
|
18
|
+
import { providerFastSwitchOff } from "./fast-opt-in";
|
|
18
19
|
|
|
19
20
|
/** OpenAI-compatible adapters that can carry the standard `service_tier` field. */
|
|
20
21
|
export const SERVICE_TIER_ADAPTERS = new Set(["openai-chat", "openai-responses"]);
|
|
@@ -35,6 +36,7 @@ type ServiceTierCapabilityProvider = Pick<
|
|
|
35
36
|
| "apiKeyTransport"
|
|
36
37
|
| "chatServiceTier"
|
|
37
38
|
| "fastWire"
|
|
39
|
+
| "fastEnabled"
|
|
38
40
|
>;
|
|
39
41
|
|
|
40
42
|
function cloneRegistryWireDefaults(
|
|
@@ -83,9 +85,14 @@ function buildFastPolicyAuthority(
|
|
|
83
85
|
&& registryModelServiceTierCapabilityApplies(registry, capabilityProvider)
|
|
84
86
|
? registry.modelSupportsServiceTier
|
|
85
87
|
: undefined;
|
|
86
|
-
const
|
|
87
|
-
??
|
|
88
|
-
|
|
88
|
+
const fastSwitchOff = providerFastSwitchOff(providerName, {
|
|
89
|
+
fastEnabled: capabilityProvider.fastEnabled ?? provider.fastEnabled,
|
|
90
|
+
});
|
|
91
|
+
const providerCapability = fastSwitchOff
|
|
92
|
+
? false
|
|
93
|
+
: capabilityProvider.supportsServiceTier
|
|
94
|
+
?? keyAuthDefaults?.supportsServiceTier
|
|
95
|
+
?? registry?.supportsServiceTier;
|
|
89
96
|
const authority: FastPolicyAuthority = Object.freeze({
|
|
90
97
|
providerAdapter: provider.adapter,
|
|
91
98
|
providerAuthMode: provider.authMode ?? registry?.authKind ?? "key",
|
|
@@ -92,6 +92,9 @@ export function compileCodeModeHelperInput(
|
|
|
92
92
|
}
|
|
93
93
|
return `const result = await tools.view_image(${JSON.stringify(viewArgs)});\nif (result && result.image_url) { image(result.image_url); } else { text(result); }`;
|
|
94
94
|
}
|
|
95
|
+
if (helperName === "create_goal" || helperName === "get_goal" || helperName === "update_goal") {
|
|
96
|
+
return `const result = await tools.${helperName}(${JSON.stringify(args)});\ntext(result);`;
|
|
97
|
+
}
|
|
95
98
|
return `const result = await tools.exec_command(${JSON.stringify(args)});\ntext(result);`;
|
|
96
99
|
}
|
|
97
100
|
|