@bitkyc08/opencodex 2.6.31-preview.20260707 → 2.7.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.ko.md +19 -3
- package/README.md +17 -2
- package/README.zh-CN.md +16 -3
- package/gui/dist/assets/index-BGdxwydf.js +34 -0
- package/gui/dist/assets/index-DANCQ2Jt.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic.ts +62 -1
- package/src/adapters/cursor/cursor-errors.ts +28 -1
- package/src/adapters/cursor/discovery.ts +56 -10
- package/src/adapters/cursor/effort-map.ts +35 -7
- package/src/adapters/cursor/live-models.ts +3 -0
- package/src/adapters/cursor/live-transport.ts +136 -7
- package/src/adapters/cursor/protobuf-request.ts +24 -1
- package/src/adapters/cursor/request-builder.ts +6 -5
- package/src/adapters/cursor/transport-retry.ts +22 -3
- package/src/adapters/cursor.ts +2 -1
- package/src/adapters/kiro.ts +1 -1
- package/src/adapters/openai-chat.ts +75 -26
- package/src/bridge.ts +50 -4
- package/src/cli/debug.ts +203 -0
- package/src/cli/doctor.ts +11 -0
- package/src/cli/help.ts +11 -0
- package/src/cli/index.ts +10 -0
- package/src/cli/v2.ts +131 -0
- package/src/codex/account-store.ts +42 -1
- package/src/codex/auth-api.ts +43 -0
- package/src/codex/catalog.ts +356 -29
- package/src/codex/data/upstream-models.json +830 -0
- package/src/codex/features.ts +178 -0
- package/src/codex/project-config-warnings.ts +388 -0
- package/src/codex/sync.ts +8 -0
- package/src/codex/warmup.ts +193 -0
- package/src/config.ts +7 -5
- package/src/lib/debug-log-buffer.ts +42 -0
- package/src/lib/debug-settings.ts +84 -0
- package/src/lib/debug.ts +18 -9
- package/src/lib/errors.ts +104 -1
- package/src/oauth/cursor.ts +35 -12
- package/src/oauth/store.ts +4 -3
- package/src/oauth/token-guardian.ts +32 -7
- package/src/providers/derive.ts +8 -0
- package/src/providers/kiro-models.ts +3 -3
- package/src/providers/registry.ts +77 -56
- package/src/reasoning-effort.ts +34 -12
- package/src/responses/parser.ts +7 -2
- package/src/router.ts +7 -3
- package/src/server/adapter-resolve.ts +1 -1
- package/src/server/index.ts +27 -3
- package/src/server/management-api.ts +168 -7
- package/src/server/relay.ts +2 -2
- package/src/server/request-log.ts +86 -2
- package/src/server/responses.ts +209 -0
- package/src/types.ts +38 -2
- package/src/usage/debug.ts +32 -5
- package/src/usage/summary.ts +6 -6
- package/src/web-search/index.ts +1 -1
- package/gui/dist/assets/index-ByGC8-Bm.css +0 -1
- package/gui/dist/assets/index-CWujz83O.js +0 -15
|
@@ -18,9 +18,12 @@ import { getValidAccessTokenForAccount, listOAuthProviders, OAuthLoginRequiredEr
|
|
|
18
18
|
import {
|
|
19
19
|
getValidCodexToken,
|
|
20
20
|
listCodexAccountIds,
|
|
21
|
+
markCodexAccountValidated,
|
|
22
|
+
markCodexAccountValidationFailed,
|
|
21
23
|
readCodexAccountRecord,
|
|
22
24
|
TokenRefreshError,
|
|
23
25
|
} from "../codex/account-store";
|
|
26
|
+
import { codexWarmupFailureReason, warmCodexAccount } from "../codex/warmup";
|
|
24
27
|
|
|
25
28
|
export interface TokenGuardianHandle {
|
|
26
29
|
stop(): void;
|
|
@@ -29,6 +32,7 @@ export interface TokenGuardianHandle {
|
|
|
29
32
|
export interface GuardianSweepResult {
|
|
30
33
|
enabled: boolean;
|
|
31
34
|
refreshed: string[];
|
|
35
|
+
warmed: string[];
|
|
32
36
|
failed: string[];
|
|
33
37
|
skippedBackoff: string[];
|
|
34
38
|
}
|
|
@@ -40,6 +44,8 @@ const DEFAULTS = {
|
|
|
40
44
|
leadSeconds: 900,
|
|
41
45
|
failureBackoffBaseSeconds: 300,
|
|
42
46
|
failureBackoffMaxSeconds: 3600,
|
|
47
|
+
codexWarmupMaxAgeSeconds: 691_200, // 8d — matches Codex managed-auth last_refresh cadence.
|
|
48
|
+
codexWarmupModel: "gpt-5.4-mini",
|
|
43
49
|
};
|
|
44
50
|
|
|
45
51
|
interface BackoffEntry {
|
|
@@ -67,6 +73,9 @@ function resolved(g: OcxTokenGuardianConfig | undefined) {
|
|
|
67
73
|
leadSeconds: num(g?.leadSeconds, DEFAULTS.leadSeconds, 0),
|
|
68
74
|
backoffBaseSeconds: num(g?.failureBackoffBaseSeconds, DEFAULTS.failureBackoffBaseSeconds, 0),
|
|
69
75
|
backoffMaxSeconds: num(g?.failureBackoffMaxSeconds, DEFAULTS.failureBackoffMaxSeconds, 0),
|
|
76
|
+
codexWarmupEnabled: g?.codexWarmupEnabled === true,
|
|
77
|
+
codexWarmupMaxAgeSeconds: num(g?.codexWarmupMaxAgeSeconds, DEFAULTS.codexWarmupMaxAgeSeconds, 60),
|
|
78
|
+
codexWarmupModel: g?.codexWarmupModel?.trim() || DEFAULTS.codexWarmupModel,
|
|
70
79
|
};
|
|
71
80
|
}
|
|
72
81
|
|
|
@@ -106,7 +115,7 @@ async function runWithConcurrency(tasks: Array<() => Promise<void>>, limit: numb
|
|
|
106
115
|
export async function guardianSweep(nowMs: number = Date.now()): Promise<GuardianSweepResult> {
|
|
107
116
|
const config: OcxConfig = loadConfig();
|
|
108
117
|
const g = config.tokenGuardian;
|
|
109
|
-
const result: GuardianSweepResult = { enabled: !!g?.enabled, refreshed: [], failed: [], skippedBackoff: [] };
|
|
118
|
+
const result: GuardianSweepResult = { enabled: !!g?.enabled, refreshed: [], warmed: [], failed: [], skippedBackoff: [] };
|
|
110
119
|
if (!g?.enabled) return result;
|
|
111
120
|
|
|
112
121
|
const opts = resolved(g);
|
|
@@ -142,18 +151,34 @@ export async function guardianSweep(nowMs: number = Date.now()): Promise<Guardia
|
|
|
142
151
|
if (resolveRefreshPolicy("chatgpt", config) === "proactive") {
|
|
143
152
|
for (const id of listCodexAccountIds()) {
|
|
144
153
|
const record = readCodexAccountRecord(id);
|
|
145
|
-
|
|
154
|
+
if (!record || record.deletedAt != null) continue;
|
|
155
|
+
const cred = record.credential;
|
|
146
156
|
if (!cred) continue;
|
|
147
|
-
|
|
157
|
+
const needsRefresh = cred.expiresAt <= nowMs + horizonMs;
|
|
158
|
+
const needsWarmup = opts.codexWarmupEnabled
|
|
159
|
+
&& (record.lastCodexValidatedAt === undefined || nowMs - record.lastCodexValidatedAt > opts.codexWarmupMaxAgeSeconds * 1000);
|
|
160
|
+
if (!needsRefresh && !needsWarmup) continue;
|
|
148
161
|
const key = `codex:${id}`;
|
|
149
162
|
if (inBackoff(key, nowMs)) { result.skippedBackoff.push(key); continue; }
|
|
150
163
|
tasks.push(async () => {
|
|
151
164
|
try {
|
|
152
|
-
await getValidCodexToken(id);
|
|
165
|
+
const token = await getValidCodexToken(id);
|
|
166
|
+
if (needsRefresh) result.refreshed.push(key);
|
|
167
|
+
if (needsWarmup) {
|
|
168
|
+
await warmCodexAccount({
|
|
169
|
+
accessToken: token.accessToken,
|
|
170
|
+
chatgptAccountId: token.chatgptAccountId,
|
|
171
|
+
model: opts.codexWarmupModel,
|
|
172
|
+
});
|
|
173
|
+
markCodexAccountValidated(id, Date.now());
|
|
174
|
+
result.warmed.push(key);
|
|
175
|
+
}
|
|
153
176
|
backoff.delete(key);
|
|
154
|
-
result.refreshed.push(key);
|
|
155
177
|
} catch (err) {
|
|
156
178
|
const permanent = err instanceof TokenRefreshError && (err.reason === "revoked" || err.reason === "expired");
|
|
179
|
+
if (needsWarmup && !(err instanceof TokenRefreshError)) {
|
|
180
|
+
markCodexAccountValidationFailed(id, codexWarmupFailureReason(err));
|
|
181
|
+
}
|
|
157
182
|
recordFailure(key, nowMs, opts.backoffBaseSeconds, opts.backoffMaxSeconds, permanent);
|
|
158
183
|
result.failed.push(key);
|
|
159
184
|
}
|
|
@@ -186,8 +211,8 @@ export function startTokenGuardian(): TokenGuardianHandle {
|
|
|
186
211
|
const runSweep = () => {
|
|
187
212
|
void guardianSweep()
|
|
188
213
|
.then(r => {
|
|
189
|
-
if (r.enabled && (r.refreshed.length || r.failed.length)) {
|
|
190
|
-
console.log(`🛡️ token-guardian: refreshed ${r.refreshed.length}, failed ${r.failed.length}`);
|
|
214
|
+
if (r.enabled && (r.refreshed.length || r.warmed.length || r.failed.length)) {
|
|
215
|
+
console.log(`🛡️ token-guardian: refreshed ${r.refreshed.length}, warmed ${r.warmed.length}, failed ${r.failed.length}`);
|
|
191
216
|
}
|
|
192
217
|
})
|
|
193
218
|
.catch(err => console.log(`token-guardian sweep error: ${err instanceof Error ? err.message : String(err)}`))
|
package/src/providers/derive.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface DerivedKeyLoginProvider {
|
|
|
23
23
|
noPenaltyModels?: string[];
|
|
24
24
|
autoToolChoiceOnlyModels?: string[];
|
|
25
25
|
preserveReasoningContentModels?: string[];
|
|
26
|
+
thinkingToggleModels?: string[];
|
|
27
|
+
thinkingBudgetModels?: string[];
|
|
26
28
|
escapeBuiltinToolNames?: boolean;
|
|
27
29
|
}
|
|
28
30
|
|
|
@@ -80,9 +82,11 @@ export function providerConfigSeed(entry: ProviderRegistryEntry): OcxProviderCon
|
|
|
80
82
|
...(entry.noTemperatureModels ? { noTemperatureModels: [...entry.noTemperatureModels] } : {}),
|
|
81
83
|
...(entry.noTopPModels ? { noTopPModels: [...entry.noTopPModels] } : {}),
|
|
82
84
|
...(entry.noPenaltyModels ? { noPenaltyModels: [...entry.noPenaltyModels] } : {}),
|
|
85
|
+
...(entry.parallelToolCalls !== undefined ? { parallelToolCalls: entry.parallelToolCalls } : {}),
|
|
83
86
|
...(entry.autoToolChoiceOnlyModels ? { autoToolChoiceOnlyModels: [...entry.autoToolChoiceOnlyModels] } : {}),
|
|
84
87
|
...(entry.preserveReasoningContentModels ? { preserveReasoningContentModels: [...entry.preserveReasoningContentModels] } : {}),
|
|
85
88
|
...(entry.thinkingToggleModels ? { thinkingToggleModels: [...entry.thinkingToggleModels] } : {}),
|
|
89
|
+
...(entry.thinkingBudgetModels ? { thinkingBudgetModels: [...entry.thinkingBudgetModels] } : {}),
|
|
86
90
|
...(entry.escapeBuiltinToolNames !== undefined ? { escapeBuiltinToolNames: entry.escapeBuiltinToolNames } : {}),
|
|
87
91
|
...(entry.googleMode ? { googleMode: entry.googleMode } : {}),
|
|
88
92
|
...(entry.project ? { project: entry.project } : {}),
|
|
@@ -117,6 +121,8 @@ export function deriveKeyLoginMap(): Record<string, DerivedKeyLoginProvider> {
|
|
|
117
121
|
...(entry.noPenaltyModels ? { noPenaltyModels: [...entry.noPenaltyModels] } : {}),
|
|
118
122
|
...(entry.autoToolChoiceOnlyModels ? { autoToolChoiceOnlyModels: [...entry.autoToolChoiceOnlyModels] } : {}),
|
|
119
123
|
...(entry.preserveReasoningContentModels ? { preserveReasoningContentModels: [...entry.preserveReasoningContentModels] } : {}),
|
|
124
|
+
...(entry.thinkingToggleModels ? { thinkingToggleModels: [...entry.thinkingToggleModels] } : {}),
|
|
125
|
+
...(entry.thinkingBudgetModels ? { thinkingBudgetModels: [...entry.thinkingBudgetModels] } : {}),
|
|
120
126
|
...(entry.escapeBuiltinToolNames !== undefined ? { escapeBuiltinToolNames: entry.escapeBuiltinToolNames } : {}),
|
|
121
127
|
...(entry.googleMode ? { googleMode: entry.googleMode } : {}),
|
|
122
128
|
...(entry.project ? { project: entry.project } : {}),
|
|
@@ -177,9 +183,11 @@ export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig
|
|
|
177
183
|
if (!prov.noTemperatureModels && seed.noTemperatureModels) prov.noTemperatureModels = [...seed.noTemperatureModels];
|
|
178
184
|
if (!prov.noTopPModels && seed.noTopPModels) prov.noTopPModels = [...seed.noTopPModels];
|
|
179
185
|
if (!prov.noPenaltyModels && seed.noPenaltyModels) prov.noPenaltyModels = [...seed.noPenaltyModels];
|
|
186
|
+
if (prov.parallelToolCalls === undefined && seed.parallelToolCalls !== undefined) prov.parallelToolCalls = seed.parallelToolCalls;
|
|
180
187
|
if (!prov.autoToolChoiceOnlyModels && seed.autoToolChoiceOnlyModels) prov.autoToolChoiceOnlyModels = [...seed.autoToolChoiceOnlyModels];
|
|
181
188
|
if (!prov.preserveReasoningContentModels && seed.preserveReasoningContentModels) prov.preserveReasoningContentModels = [...seed.preserveReasoningContentModels];
|
|
182
189
|
if (!prov.thinkingToggleModels && seed.thinkingToggleModels) prov.thinkingToggleModels = [...seed.thinkingToggleModels];
|
|
190
|
+
if (!prov.thinkingBudgetModels && seed.thinkingBudgetModels) prov.thinkingBudgetModels = [...seed.thinkingBudgetModels];
|
|
183
191
|
if (prov.escapeBuiltinToolNames === undefined && seed.escapeBuiltinToolNames !== undefined) prov.escapeBuiltinToolNames = seed.escapeBuiltinToolNames;
|
|
184
192
|
}
|
|
185
193
|
|
|
@@ -38,10 +38,10 @@ export const KIRO_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
|
38
38
|
"qwen3-coder-next": 256_000,
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
const KIRO_REASONING_EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
41
|
+
const KIRO_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
42
42
|
|
|
43
|
-
//
|
|
44
|
-
//
|
|
43
|
+
// Kiro has no upstream reasoning_effort enum; these labels map to fake-thinking budgets in
|
|
44
|
+
// src/adapters/kiro.ts.
|
|
45
45
|
export const KIRO_MODEL_REASONING_EFFORTS: Record<string, string[]> = Object.fromEntries(
|
|
46
46
|
KIRO_MODELS.map(id => [id, KIRO_REASONING_EFFORTS]),
|
|
47
47
|
);
|
|
@@ -37,9 +37,12 @@ export interface ProviderRegistryEntry {
|
|
|
37
37
|
noTemperatureModels?: string[];
|
|
38
38
|
noTopPModels?: string[];
|
|
39
39
|
noPenaltyModels?: string[];
|
|
40
|
+
/** Opt this provider into parallel tool calls (see OcxProviderConfig.parallelToolCalls). */
|
|
41
|
+
parallelToolCalls?: boolean;
|
|
40
42
|
autoToolChoiceOnlyModels?: string[];
|
|
41
43
|
preserveReasoningContentModels?: string[];
|
|
42
44
|
thinkingToggleModels?: string[];
|
|
45
|
+
thinkingBudgetModels?: string[];
|
|
43
46
|
escapeBuiltinToolNames?: boolean;
|
|
44
47
|
oauthId?: string;
|
|
45
48
|
jawcodeBundle?: string;
|
|
@@ -56,37 +59,40 @@ export type ProviderConfigSeed = Pick<
|
|
|
56
59
|
| "liveModels" | "contextWindow" | "modelContextWindows" | "modelInputModalities"
|
|
57
60
|
| "reasoningEfforts" | "modelReasoningEfforts" | "reasoningEffortMap" | "modelReasoningEffortMap"
|
|
58
61
|
| "noVisionModels" | "noReasoningModels" | "noTemperatureModels" | "noTopPModels" | "noPenaltyModels"
|
|
59
|
-
| "autoToolChoiceOnlyModels" | "preserveReasoningContentModels" | "thinkingToggleModels" | "escapeBuiltinToolNames"
|
|
62
|
+
| "autoToolChoiceOnlyModels" | "preserveReasoningContentModels" | "thinkingToggleModels" | "thinkingBudgetModels" | "escapeBuiltinToolNames"
|
|
60
63
|
| "googleMode" | "project" | "location"
|
|
61
64
|
>;
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
const OLLAMA_REASONING_MAP: Record<string, string> = { xhigh: "max" };
|
|
65
|
-
|
|
66
66
|
// Shared between the OAuth (Claude account) and API-key Anthropic entries so both expose the
|
|
67
67
|
// same static model seed.
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
// 260709 refresh: claude-fable-5 added (official models overview); evidence in
|
|
69
|
+
// devlog/model_update/260709_model_refresh/002_cursor_registry_drift.md.
|
|
70
|
+
const ANTHROPIC_MODELS = ["claude-fable-5", "claude-sonnet-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5"];
|
|
71
|
+
const ANTHROPIC_MODEL_CONTEXT_WINDOWS: Record<string, number> = { "claude-sonnet-5": 1_000_000, "claude-fable-5": 1_000_000 };
|
|
70
72
|
|
|
71
73
|
const ZAI_GLM_52_MODELS = ["glm-5.2", "glm-5.2[1m]"];
|
|
72
|
-
const ZAI_GLM_52_REASONING_EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
const ZAI_GLM_52_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
75
|
+
const OPENAI_GPT56_MODELS = ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"];
|
|
76
|
+
const OPENAI_GPT56_CONTEXT_WINDOW = 372_000;
|
|
77
|
+
const OPENAI_GPT56_CONTEXT_WINDOWS = {
|
|
78
|
+
"gpt-5.6-sol": OPENAI_GPT56_CONTEXT_WINDOW,
|
|
79
|
+
"gpt-5.6-terra": OPENAI_GPT56_CONTEXT_WINDOW,
|
|
80
|
+
"gpt-5.6-luna": OPENAI_GPT56_CONTEXT_WINDOW,
|
|
81
|
+
};
|
|
82
|
+
const OPENROUTER_GPT56_MODELS = OPENAI_GPT56_MODELS.map(id => `openai/${id}`);
|
|
83
|
+
const OPENROUTER_GPT56_CONTEXT_WINDOWS = {
|
|
84
|
+
"openai/gpt-5.6-sol": OPENAI_GPT56_CONTEXT_WINDOW,
|
|
85
|
+
"openai/gpt-5.6-terra": OPENAI_GPT56_CONTEXT_WINDOW,
|
|
86
|
+
"openai/gpt-5.6-luna": OPENAI_GPT56_CONTEXT_WINDOW,
|
|
81
87
|
};
|
|
82
88
|
|
|
83
89
|
/**
|
|
84
90
|
* Vendor thinking-toggle models (MiMo v2.x, GLM 5/5.1 on Zen Go): the wire knob is
|
|
85
|
-
* `thinking: {type: enabled|disabled}` — a binary. Advertise
|
|
86
|
-
*
|
|
91
|
+
* `thinking: {type: enabled|disabled}` — a binary. Advertise the full Codex picker ladder
|
|
92
|
+
* and map efforts onto the toggle. Zen Go
|
|
87
93
|
* pass-through probed live 2026-07-07 (glm-5.2 toggle verified; mimo/minimax accept shape).
|
|
88
94
|
*/
|
|
89
|
-
const THINKING_TOGGLE_EFFORTS = ["low", "high"];
|
|
95
|
+
const THINKING_TOGGLE_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
90
96
|
const THINKING_TOGGLE_MAP: Record<string, string> = {
|
|
91
97
|
none: "disabled",
|
|
92
98
|
minimal: "disabled",
|
|
@@ -99,8 +105,16 @@ const THINKING_TOGGLE_MAP: Record<string, string> = {
|
|
|
99
105
|
const OPENCODE_GO_THINKING_TOGGLE_MODELS = [
|
|
100
106
|
"mimo-v2.5", "mimo-v2.5-pro", "mimo-v2-omni", "mimo-v2-pro", "glm-5", "glm-5.1",
|
|
101
107
|
];
|
|
108
|
+
const THINKING_BUDGET_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
109
|
+
const THINKING_BUDGET_MODELS = [
|
|
110
|
+
"qwen3.5-397b", "qwen3.6-35b",
|
|
111
|
+
"qwen3.5-plus", "qwen3.6-plus", "qwen3.7-max", "qwen3.7-plus",
|
|
112
|
+
];
|
|
113
|
+
const OPENCODE_GO_THINKING_BUDGET_MODELS = ["qwen3.5-plus", "qwen3.6-plus", "qwen3.7-max", "qwen3.7-plus"];
|
|
102
114
|
const DEEPSEEK_THINKING_MODELS = ["deepseek-v4-pro", "deepseek-v4-flash"];
|
|
103
|
-
|
|
115
|
+
// "max" is advertised too: the wire map routes xhigh->max and max->max, so the picker
|
|
116
|
+
// should surface the max tier instead of hiding it behind xhigh.
|
|
117
|
+
const DEEPSEEK_THINKING_EFFORTS = ["high", "xhigh", "max"];
|
|
104
118
|
const DEEPSEEK_THINKING_REASONING_MAP: Record<string, string> = {
|
|
105
119
|
low: "high",
|
|
106
120
|
medium: "high",
|
|
@@ -108,7 +122,7 @@ const DEEPSEEK_THINKING_REASONING_MAP: Record<string, string> = {
|
|
|
108
122
|
xhigh: "max",
|
|
109
123
|
max: "max",
|
|
110
124
|
};
|
|
111
|
-
const KIMI_THINKING_MODELS = ["kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2.5"
|
|
125
|
+
const KIMI_THINKING_MODELS = ["kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2.5"];
|
|
112
126
|
const KIMI_LOCKED_PARAMETER_MODELS = ["kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2.5"];
|
|
113
127
|
const NEURALWATT_REASONING_HISTORY_MODELS = [
|
|
114
128
|
"glm-5.2",
|
|
@@ -118,28 +132,17 @@ const NEURALWATT_REASONING_HISTORY_MODELS = [
|
|
|
118
132
|
const UMANS_MODELS = [
|
|
119
133
|
"umans-coder",
|
|
120
134
|
"umans-kimi-k2.7",
|
|
121
|
-
"umans-kimi-k2.6",
|
|
122
135
|
"umans-flash",
|
|
123
136
|
"umans-glm-5.2",
|
|
124
137
|
"umans-glm-5.1",
|
|
125
138
|
"umans-qwen3.6-35b-a3b",
|
|
126
139
|
];
|
|
127
|
-
const UMANS_REASONING_EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
128
|
-
const UMANS_GLM_REASONING_EFFORTS = ["high", "xhigh"];
|
|
129
|
-
const UMANS_GLM_REASONING_MAP: Record<string, string> = {
|
|
130
|
-
none: "high",
|
|
131
|
-
minimal: "high",
|
|
132
|
-
low: "high",
|
|
133
|
-
medium: "high",
|
|
134
|
-
high: "high",
|
|
135
|
-
xhigh: "max",
|
|
136
|
-
max: "max",
|
|
137
|
-
};
|
|
140
|
+
const UMANS_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
141
|
+
const UMANS_GLM_REASONING_EFFORTS = ["high", "xhigh", "max"];
|
|
138
142
|
const UMANS_TEXT_ONLY_MODELS = ["umans-glm-5.2", "umans-glm-5.1"];
|
|
139
143
|
const UMANS_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
140
144
|
"umans-coder": 262_144,
|
|
141
145
|
"umans-kimi-k2.7": 262_144,
|
|
142
|
-
"umans-kimi-k2.6": 262_144,
|
|
143
146
|
"umans-flash": 262_144,
|
|
144
147
|
"umans-glm-5.2": 405_504,
|
|
145
148
|
"umans-glm-5.1": 202_752,
|
|
@@ -167,7 +170,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
167
170
|
authKind: "oauth",
|
|
168
171
|
featured: false,
|
|
169
172
|
dashboardPreset: true,
|
|
170
|
-
note: "Experimental Cursor bridge. Live transport and live model discovery are enabled after a standalone PKCE browser login via 'ocx login cursor'; native read/write/delete/shell/fetch execution stays disabled unless
|
|
173
|
+
note: "Experimental Cursor bridge. Live transport and live model discovery are enabled after a standalone PKCE browser login via 'ocx login cursor'; native read/write/delete/shell/fetch execution stays disabled unless you set \"unsafeAllowNativeLocalExec\": true on providers.cursor in ~/.opencodex/config.json (dashboard: Providers → Cursor → Edit JSON) for a trusted local experiment.",
|
|
171
174
|
models: cursorModelIds(CURSOR_STATIC_MODELS),
|
|
172
175
|
liveModels: true,
|
|
173
176
|
defaultModel: "auto",
|
|
@@ -190,9 +193,29 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
190
193
|
oauthId: "xai",
|
|
191
194
|
jawcodeBundle: "xai",
|
|
192
195
|
note: "Log in with your Grok account",
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
+
// Parallel tool calls: officially supported and default-on per docs.x.ai function-calling
|
|
197
|
+
// (verified 260709, devlog/_plan/260709_parallel_tool_calls). Streamed calls arrive whole
|
|
198
|
+
// per chunk, so the buffered parser assembles them losslessly.
|
|
199
|
+
parallelToolCalls: true,
|
|
200
|
+
// Live /v1/models discovery is the authoritative lineup (verified 260709: returns grok-4.5);
|
|
201
|
+
// the static list below is the logged-out fallback seed.
|
|
202
|
+
liveModels: true,
|
|
203
|
+
// 260709 refresh: lineup + metadata from official docs.x.ai (grok-4.5 announced 07-08);
|
|
204
|
+
// grok-composer-2.5-fast kept as account-verified (absent from public docs). Evidence:
|
|
205
|
+
// devlog/model_update/260709_model_refresh/001_xai_lineup.md.
|
|
206
|
+
models: ["grok-4.5", "grok-4.3", "grok-4.20-multi-agent-0309", "grok-4.20-0309-reasoning", "grok-4.20-0309-non-reasoning", "grok-build-0.1", "grok-composer-2.5-fast"],
|
|
207
|
+
defaultModel: "grok-4.5",
|
|
208
|
+
noReasoningModels: ["grok-4.20-0309-non-reasoning", "grok-build-0.1", "grok-composer-2.5-fast"],
|
|
209
|
+
// grok-4.5 reasoning is always-on with low/medium/high control (no off tier upstream).
|
|
210
|
+
modelReasoningEfforts: { "grok-4.5": ["low", "medium", "high"] },
|
|
211
|
+
modelContextWindows: {
|
|
212
|
+
"grok-4.5": 500_000,
|
|
213
|
+
"grok-4.3": 1_000_000,
|
|
214
|
+
"grok-4.20-multi-agent-0309": 1_000_000,
|
|
215
|
+
"grok-4.20-0309-reasoning": 1_000_000,
|
|
216
|
+
"grok-4.20-0309-non-reasoning": 1_000_000,
|
|
217
|
+
"grok-build-0.1": 256_000,
|
|
218
|
+
},
|
|
196
219
|
noVisionModels: ["grok-build-0.1", "grok-composer-2.5-fast"],
|
|
197
220
|
},
|
|
198
221
|
{
|
|
@@ -207,7 +230,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
207
230
|
note: "Log in with your Claude account",
|
|
208
231
|
models: [...ANTHROPIC_MODELS],
|
|
209
232
|
modelContextWindows: { ...ANTHROPIC_MODEL_CONTEXT_WINDOWS },
|
|
210
|
-
defaultModel: "claude-sonnet-
|
|
233
|
+
defaultModel: "claude-sonnet-5",
|
|
211
234
|
},
|
|
212
235
|
{
|
|
213
236
|
id: "anthropic-apikey",
|
|
@@ -223,7 +246,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
223
246
|
models: [...ANTHROPIC_MODELS],
|
|
224
247
|
liveModels: true,
|
|
225
248
|
modelContextWindows: { ...ANTHROPIC_MODEL_CONTEXT_WINDOWS },
|
|
226
|
-
defaultModel: "claude-sonnet-
|
|
249
|
+
defaultModel: "claude-sonnet-5",
|
|
227
250
|
},
|
|
228
251
|
{
|
|
229
252
|
id: "kimi",
|
|
@@ -260,7 +283,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
260
283
|
modelContextWindows: KIRO_MODEL_CONTEXT_WINDOWS,
|
|
261
284
|
modelReasoningEfforts: KIRO_MODEL_REASONING_EFFORTS,
|
|
262
285
|
},
|
|
263
|
-
{ id: "openai-apikey", label: "OpenAI (API key)", adapter: "openai-responses", baseUrl: "https://api.openai.com/v1", authKind: "key", featured: true, dashboardUrl: "https://platform.openai.com/api-keys", defaultModel: "gpt-5.5" },
|
|
286
|
+
{ id: "openai-apikey", label: "OpenAI (API key)", adapter: "openai-responses", baseUrl: "https://api.openai.com/v1", authKind: "key", featured: true, dashboardUrl: "https://platform.openai.com/api-keys", defaultModel: "gpt-5.5", models: ["gpt-5.5", ...OPENAI_GPT56_MODELS], liveModels: true, modelContextWindows: OPENAI_GPT56_CONTEXT_WINDOWS },
|
|
264
287
|
{
|
|
265
288
|
id: "umans",
|
|
266
289
|
label: "Umans AI Coding Plan",
|
|
@@ -277,15 +300,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
277
300
|
modelReasoningEfforts: {
|
|
278
301
|
"umans-coder": UMANS_REASONING_EFFORTS,
|
|
279
302
|
"umans-kimi-k2.7": UMANS_REASONING_EFFORTS,
|
|
280
|
-
"umans-
|
|
281
|
-
"umans-flash": ["low", "medium", "high"],
|
|
303
|
+
"umans-flash": UMANS_REASONING_EFFORTS,
|
|
282
304
|
"umans-glm-5.2": UMANS_GLM_REASONING_EFFORTS,
|
|
283
305
|
"umans-glm-5.1": UMANS_GLM_REASONING_EFFORTS,
|
|
284
|
-
"umans-qwen3.6-35b-a3b":
|
|
285
|
-
},
|
|
286
|
-
modelReasoningEffortMap: {
|
|
287
|
-
"umans-glm-5.2": UMANS_GLM_REASONING_MAP,
|
|
288
|
-
"umans-glm-5.1": UMANS_GLM_REASONING_MAP,
|
|
306
|
+
"umans-qwen3.6-35b-a3b": UMANS_REASONING_EFFORTS,
|
|
289
307
|
},
|
|
290
308
|
noVisionModels: UMANS_TEXT_ONLY_MODELS,
|
|
291
309
|
escapeBuiltinToolNames: true,
|
|
@@ -299,12 +317,15 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
299
317
|
"kimi-k2.7-code": [],
|
|
300
318
|
"kimi-k2.7-code-highspeed": [],
|
|
301
319
|
...Object.fromEntries(OPENCODE_GO_THINKING_TOGGLE_MODELS.map(id => [id, THINKING_TOGGLE_EFFORTS])),
|
|
320
|
+
...Object.fromEntries(OPENCODE_GO_THINKING_BUDGET_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])),
|
|
302
321
|
},
|
|
322
|
+
// glm-5.2 uses identity labels now that `max` is a native Codex level (no alias map);
|
|
323
|
+
// the thinking-toggle map is a REAL wire alias (effort -> enabled/disabled) and stays.
|
|
303
324
|
modelReasoningEffortMap: {
|
|
304
|
-
"glm-5.2": ZAI_GLM_52_REASONING_MAP,
|
|
305
325
|
...Object.fromEntries(OPENCODE_GO_THINKING_TOGGLE_MODELS.map(id => [id, THINKING_TOGGLE_MAP])),
|
|
306
326
|
},
|
|
307
327
|
thinkingToggleModels: OPENCODE_GO_THINKING_TOGGLE_MODELS,
|
|
328
|
+
thinkingBudgetModels: THINKING_BUDGET_MODELS,
|
|
308
329
|
noReasoningModels: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
|
|
309
330
|
// Text-only Zen Go models (jawcode metadata) — the vision sidecar describes images for
|
|
310
331
|
// every model listed here (and the catalog advertises image input on their behalf).
|
|
@@ -345,12 +366,14 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
345
366
|
"kimi-k2.6": [],
|
|
346
367
|
"kimi-k2.6-fast": [],
|
|
347
368
|
"kimi-k2.7-code": [],
|
|
348
|
-
|
|
369
|
+
// Qwen3.x uses thinking_budget, NOT graded reasoning_effort; the adapter maps the five
|
|
370
|
+
// Codex picker levels onto budget fractions.
|
|
371
|
+
"qwen3.5-397b": THINKING_BUDGET_EFFORTS,
|
|
349
372
|
"qwen3.5-397b-fast": [],
|
|
350
|
-
"qwen3.6-35b":
|
|
373
|
+
"qwen3.6-35b": THINKING_BUDGET_EFFORTS,
|
|
351
374
|
"qwen3.6-35b-fast": [],
|
|
352
375
|
},
|
|
353
|
-
|
|
376
|
+
thinkingBudgetModels: THINKING_BUDGET_MODELS,
|
|
354
377
|
noReasoningModels: ["glm-5.2-fast", "kimi-k2.5-fast", "kimi-k2.6-fast", "qwen3.5-397b-fast", "qwen3.6-35b-fast"],
|
|
355
378
|
noVisionModels: ["glm-5.2", "glm-5.2-fast", "qwen3.5-397b", "qwen3.5-397b-fast"],
|
|
356
379
|
noTemperatureModels: ["kimi-k2.7-code"],
|
|
@@ -359,13 +382,13 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
359
382
|
autoToolChoiceOnlyModels: ["kimi-k2.7-code"],
|
|
360
383
|
preserveReasoningContentModels: NEURALWATT_REASONING_HISTORY_MODELS,
|
|
361
384
|
},
|
|
362
|
-
{ id: "openrouter", label: "OpenRouter", adapter: "openai-chat", baseUrl: "https://openrouter.ai/api/v1", authKind: "key", featured: true, dashboardUrl: "https://openrouter.ai/keys", jawcodeBundle: "openrouter", models: ["anthropic/claude-sonnet-5"], modelContextWindows: { "anthropic/claude-sonnet-5": 1_000_000 } },
|
|
385
|
+
{ id: "openrouter", label: "OpenRouter", adapter: "openai-chat", baseUrl: "https://openrouter.ai/api/v1", authKind: "key", featured: true, dashboardUrl: "https://openrouter.ai/keys", jawcodeBundle: "openrouter", models: ["anthropic/claude-sonnet-5", ...OPENROUTER_GPT56_MODELS], modelContextWindows: { "anthropic/claude-sonnet-5": 1_000_000, ...OPENROUTER_GPT56_CONTEXT_WINDOWS } },
|
|
363
386
|
{ id: "groq", label: "Groq", adapter: "openai-chat", baseUrl: "https://api.groq.com/openai/v1", authKind: "key", featured: true, dashboardUrl: "https://console.groq.com/keys" },
|
|
364
387
|
{ id: "google", label: "Google Gemini", adapter: "google", baseUrl: "https://generativelanguage.googleapis.com", authKind: "key", featured: true, dashboardUrl: "https://aistudio.google.com/apikey", defaultModel: "gemini-3-pro", jawcodeBundle: "google", extraMetadataAliases: ["gemini"] },
|
|
365
388
|
{ id: "google-vertex", label: "Google Vertex AI", adapter: "google", baseUrl: "https://aiplatform.googleapis.com", authKind: "key", dashboardUrl: "https://console.cloud.google.com/vertex-ai", defaultModel: "gemini-3-pro", googleMode: "vertex", jawcodeBundle: "google", extraMetadataAliases: ["gemini-vertex"] },
|
|
366
389
|
{ id: "google-antigravity", label: "Google Antigravity", adapter: "google", baseUrl: "https://daily-cloudcode-pa.googleapis.com", authKind: "oauth", dashboardUrl: "https://antigravity.google", models: ANTIGRAVITY_MODELS, defaultModel: "gemini-3.5-flash-low", modelContextWindows: ANTIGRAVITY_MODEL_CONTEXT_WINDOWS, googleMode: "cloud-code-assist", jawcodeBundle: "google", extraMetadataAliases: ["antigravity", "gemini-antigravity"] },
|
|
367
390
|
{ id: "azure-openai", label: "Azure OpenAI", adapter: "azure-openai", baseUrl: "https://{resource}.openai.azure.com/openai", authKind: "key", featured: true, dashboardUrl: "https://portal.azure.com" },
|
|
368
|
-
{ id: "ollama", label: "Ollama (local)", adapter: "openai-chat", baseUrl: "http://localhost:11434/v1", authKind: "local", featured: true, note: "Local — key usually blank"
|
|
391
|
+
{ id: "ollama", label: "Ollama (local)", adapter: "openai-chat", baseUrl: "http://localhost:11434/v1", authKind: "local", featured: true, note: "Local — key usually blank" },
|
|
369
392
|
{ id: "vllm", label: "vLLM (local)", adapter: "openai-chat", baseUrl: "http://localhost:8000/v1", authKind: "local", featured: true, note: "Local — key usually blank" },
|
|
370
393
|
{ id: "lm-studio", label: "LM Studio (local)", adapter: "openai-chat", baseUrl: "http://localhost:1234/v1", authKind: "local", featured: true, note: "Local — no key needed" },
|
|
371
394
|
{
|
|
@@ -393,7 +416,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
393
416
|
{
|
|
394
417
|
id: "moonshot", label: "Moonshot (Kimi API)", baseUrl: "https://api.moonshot.ai/v1", adapter: "openai-chat", authKind: "key",
|
|
395
418
|
dashboardUrl: "https://platform.moonshot.ai/console/api-keys", defaultModel: "kimi-k2.7-code", jawcodeBundle: "moonshot",
|
|
396
|
-
models: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2.5"
|
|
419
|
+
models: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2.5"],
|
|
397
420
|
noReasoningModels: KIMI_THINKING_MODELS,
|
|
398
421
|
modelReasoningEfforts: Object.fromEntries(KIMI_THINKING_MODELS.map(id => [id, []])),
|
|
399
422
|
noTemperatureModels: KIMI_LOCKED_PARAMETER_MODELS,
|
|
@@ -412,7 +435,6 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
412
435
|
models: ["glm-5.2", "glm-5.2[1m]", "glm-5.1", "glm-5", "glm-4.6"],
|
|
413
436
|
noVisionModels: ZAI_GLM_52_MODELS,
|
|
414
437
|
modelReasoningEfforts: Object.fromEntries(ZAI_GLM_52_MODELS.map(id => [id, ZAI_GLM_52_REASONING_EFFORTS])),
|
|
415
|
-
modelReasoningEffortMap: Object.fromEntries(ZAI_GLM_52_MODELS.map(id => [id, ZAI_GLM_52_REASONING_MAP])),
|
|
416
438
|
preserveReasoningContentModels: ZAI_GLM_52_MODELS,
|
|
417
439
|
},
|
|
418
440
|
{ id: "nanogpt", label: "NanoGPT", baseUrl: "https://nano-gpt.com/api/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://nano-gpt.com/api" },
|
|
@@ -430,7 +452,6 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
430
452
|
adapter: "openai-chat",
|
|
431
453
|
authKind: "key",
|
|
432
454
|
dashboardUrl: "https://ollama.com/settings/keys",
|
|
433
|
-
reasoningEffortMap: OLLAMA_REASONING_MAP,
|
|
434
455
|
models: ["glm-5.2", "deepseek-v4-pro", "qwen3-coder", "gpt-oss:120b", "kimi-k2.6", "minimax-m3", "qwen3.5", "gemma4"],
|
|
435
456
|
defaultModel: "glm-5.2",
|
|
436
457
|
noVisionModels: [
|
package/src/reasoning-effort.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { OcxProviderConfig } from "./types";
|
|
2
2
|
import { modelInList } from "./types";
|
|
3
3
|
|
|
4
|
+
// Descriptions mirror the upstream bundled models.json canonical wording (openai/codex PR #31684).
|
|
4
5
|
export const CODEX_REASONING_LEVELS: { effort: string; description: string }[] = [
|
|
5
6
|
{ effort: "low", description: "Fast responses with lighter reasoning" },
|
|
6
|
-
{ effort: "medium", description: "Balances speed and reasoning depth" },
|
|
7
|
+
{ effort: "medium", description: "Balances speed and reasoning depth for everyday tasks" },
|
|
7
8
|
{ effort: "high", description: "Greater reasoning depth for complex problems" },
|
|
8
|
-
{ effort: "xhigh", description: "
|
|
9
|
+
{ effort: "xhigh", description: "Extra high reasoning depth for complex problems" },
|
|
10
|
+
{ effort: "max", description: "Maximum reasoning depth for the hardest problems" },
|
|
11
|
+
{ effort: "ultra", description: "Maximum reasoning with automatic task delegation" },
|
|
9
12
|
];
|
|
10
13
|
|
|
11
14
|
const CODEX_REASONING_ORDER = CODEX_REASONING_LEVELS.map(l => l.effort);
|
|
@@ -45,15 +48,28 @@ export function sanitizeCodexReasoningEfforts(efforts: readonly string[] | undef
|
|
|
45
48
|
export function configuredReasoningEfforts(provider: OcxProviderConfig, modelId: string): string[] | undefined {
|
|
46
49
|
if (modelInList(provider.noReasoningModels, modelId)) return [];
|
|
47
50
|
const modelEfforts = modelRecordValue(provider.modelReasoningEfforts, modelId);
|
|
48
|
-
if (modelEfforts !== undefined) return sanitizeCodexReasoningEfforts(modelEfforts) ?? [];
|
|
49
|
-
if (provider.reasoningEfforts !== undefined) return sanitizeCodexReasoningEfforts(provider.reasoningEfforts) ?? [];
|
|
51
|
+
if (modelEfforts !== undefined) return healMaxTier(provider, modelId, sanitizeCodexReasoningEfforts(modelEfforts) ?? []);
|
|
52
|
+
if (provider.reasoningEfforts !== undefined) return healMaxTier(provider, modelId, sanitizeCodexReasoningEfforts(provider.reasoningEfforts) ?? []);
|
|
50
53
|
return undefined;
|
|
51
54
|
}
|
|
52
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Stale-ladder self-heal: saved configs seeded before `max` became a native Codex level can
|
|
58
|
+
* advertise a ladder that stops at `xhigh` while the wire map already routes xhigh -> max
|
|
59
|
+
* (e.g. opencode-go glm-5.2, deepseek thinking models). When the map proves the provider
|
|
60
|
+
* accepts wire `max`, append `max` so the picker actually shows the top tier. Thinking-toggle
|
|
61
|
+
* maps (xhigh -> "enabled") never match, so binary-toggle models stay two-step.
|
|
62
|
+
*/
|
|
63
|
+
function healMaxTier(provider: OcxProviderConfig, modelId: string, efforts: string[]): string[] {
|
|
64
|
+
if (efforts.includes("max") || !efforts.includes("xhigh")) return efforts;
|
|
65
|
+
const wireMap = reasoningEffortMapFor(provider, modelId);
|
|
66
|
+
if (wireMap?.xhigh !== "max" && wireMap?.max !== "max") return efforts;
|
|
67
|
+
return sanitizeCodexReasoningEfforts([...efforts, "max"]) ?? efforts;
|
|
68
|
+
}
|
|
69
|
+
|
|
53
70
|
function requestToCodexEffort(requested: string): string | undefined {
|
|
54
71
|
if (requested === "none") return undefined;
|
|
55
72
|
if (requested === "minimal") return "low";
|
|
56
|
-
if (requested === "max") return "xhigh";
|
|
57
73
|
return CODEX_REASONING_SET.has(requested) ? requested : undefined;
|
|
58
74
|
}
|
|
59
75
|
|
|
@@ -82,21 +98,27 @@ export function reasoningEffortMapFor(provider: OcxProviderConfig, modelId: stri
|
|
|
82
98
|
}
|
|
83
99
|
|
|
84
100
|
/**
|
|
85
|
-
* Translate Codex's reasoning label into the provider's real wire value.
|
|
86
|
-
*
|
|
87
|
-
* different values (`max`) or a smaller subset (`low`/`medium`/`high`).
|
|
101
|
+
* Translate Codex's reasoning label into the provider's real wire value. Prefer identity labels
|
|
102
|
+
* (`xhigh` stays `xhigh`, `max` stays `max`); provider maps are only for real upstream aliases.
|
|
88
103
|
*/
|
|
89
104
|
export function mapReasoningEffort(provider: OcxProviderConfig, modelId: string, requested: string | undefined): string | undefined {
|
|
90
105
|
if (!requested) return undefined;
|
|
91
106
|
if (modelInList(provider.noReasoningModels, modelId)) return undefined;
|
|
92
107
|
|
|
108
|
+
// Upstream codex-rs converts ultra -> max before ANY provider request (core/src/client.rs
|
|
109
|
+
// `reasoning_effort_for_request`), so "ultra" must never influence the provider wire — not even
|
|
110
|
+
// through a raw alias. Apply the boundary before alias/clamp resolution.
|
|
111
|
+
const boundary = requested === "ultra" ? "max" : requested;
|
|
112
|
+
|
|
93
113
|
const wireMap = reasoningEffortMapFor(provider, modelId);
|
|
94
|
-
if (wireMap && Object.prototype.hasOwnProperty.call(wireMap,
|
|
114
|
+
if (wireMap && Object.prototype.hasOwnProperty.call(wireMap, boundary)) return wireMap[boundary];
|
|
95
115
|
|
|
96
116
|
const supported = configuredReasoningEfforts(provider, modelId);
|
|
97
|
-
const codexEffort = supported !== undefined ? clampToSupportedCodexEffort(
|
|
117
|
+
const codexEffort = supported !== undefined ? clampToSupportedCodexEffort(boundary, supported) : requestToCodexEffort(boundary);
|
|
98
118
|
if (!codexEffort) return undefined;
|
|
99
119
|
|
|
100
|
-
|
|
101
|
-
|
|
120
|
+
// Belt for the odd config where the supported ladder is ultra-only and the clamp lands on it.
|
|
121
|
+
const wire = codexEffort === "ultra" ? "max" : codexEffort;
|
|
122
|
+
if (wireMap && Object.prototype.hasOwnProperty.call(wireMap, wire)) return wireMap[wire];
|
|
123
|
+
return wire;
|
|
102
124
|
}
|
package/src/responses/parser.ts
CHANGED
|
@@ -464,8 +464,13 @@ export function parseRequest(body: unknown): OcxParsedRequest {
|
|
|
464
464
|
const tc = mapToolChoice(data.tool_choice);
|
|
465
465
|
if (tc !== undefined) options.toolChoice = tc;
|
|
466
466
|
if (data.parallel_tool_calls !== undefined) options.parallelToolCalls = data.parallel_tool_calls;
|
|
467
|
-
|
|
468
|
-
|
|
467
|
+
// Upstream codex-rs converts "ultra" to "max" at the inference boundary (core/src/client.rs
|
|
468
|
+
// `reasoning_effort_for_request`), so current clients never send it — but a catalog that
|
|
469
|
+
// advertises ultra plus an older/direct caller can. Degrade it to max like upstream instead of
|
|
470
|
+
// silently dropping reasoning altogether.
|
|
471
|
+
const requestedEffort = data.reasoning?.effort === "ultra" ? "max" : data.reasoning?.effort;
|
|
472
|
+
if (requestedEffort && REASONING_EFFORTS.has(requestedEffort)) {
|
|
473
|
+
options.reasoning = requestedEffort;
|
|
469
474
|
}
|
|
470
475
|
const summaryMode = data.reasoning?.summary;
|
|
471
476
|
if (!summaryMode || summaryMode === "none") options.hideThinkingSummary = true;
|
package/src/router.ts
CHANGED
|
@@ -29,9 +29,8 @@ const MODEL_PROVIDER_PATTERNS: Array<{ providerNames: string[]; prefixes: string
|
|
|
29
29
|
},
|
|
30
30
|
];
|
|
31
31
|
|
|
32
|
-
// Merge registry-default effort maps under user values so
|
|
33
|
-
//
|
|
34
|
-
// (e.g. ollama-cloud xhigh -> max) without a disk migration. User overrides win per-key.
|
|
32
|
+
// Merge registry-default effort maps under user values so built-in provider configs can
|
|
33
|
+
// carry real upstream aliases without a disk migration. User overrides win per-key.
|
|
35
34
|
function mergeRecord(
|
|
36
35
|
seed: Record<string, string> | undefined,
|
|
37
36
|
user: Record<string, string> | undefined,
|
|
@@ -97,6 +96,7 @@ function routedProviderConfig(providerName: string, provider: OcxProviderConfig)
|
|
|
97
96
|
const autoToolChoiceOnlyModels = mergeStringArray(registryEntry.autoToolChoiceOnlyModels, provider.autoToolChoiceOnlyModels);
|
|
98
97
|
const preserveReasoningContentModels = mergeStringArray(registryEntry.preserveReasoningContentModels, provider.preserveReasoningContentModels);
|
|
99
98
|
const thinkingToggleModels = mergeStringArray(registryEntry.thinkingToggleModels, provider.thinkingToggleModels);
|
|
99
|
+
const thinkingBudgetModels = mergeStringArray(registryEntry.thinkingBudgetModels, provider.thinkingBudgetModels);
|
|
100
100
|
|
|
101
101
|
return {
|
|
102
102
|
...provider,
|
|
@@ -113,6 +113,9 @@ function routedProviderConfig(providerName: string, provider: OcxProviderConfig)
|
|
|
113
113
|
...(provider.contextWindow === undefined && registryEntry.contextWindow !== undefined ? { contextWindow: registryEntry.contextWindow } : {}),
|
|
114
114
|
...(provider.reasoningEfforts === undefined && registryEntry.reasoningEfforts !== undefined ? { reasoningEfforts: registryEntry.reasoningEfforts } : {}),
|
|
115
115
|
...(provider.escapeBuiltinToolNames === undefined && registryEntry.escapeBuiltinToolNames !== undefined ? { escapeBuiltinToolNames: registryEntry.escapeBuiltinToolNames } : {}),
|
|
116
|
+
// Scalar backfill: a persisted config created before the flag shipped inherits the registry
|
|
117
|
+
// opt-in, while an explicit user `false` keeps overriding registry `true`.
|
|
118
|
+
...(provider.parallelToolCalls === undefined && registryEntry.parallelToolCalls !== undefined ? { parallelToolCalls: registryEntry.parallelToolCalls } : {}),
|
|
116
119
|
...(modelContextWindows ? { modelContextWindows } : {}),
|
|
117
120
|
...(modelInputModalities ? { modelInputModalities } : {}),
|
|
118
121
|
...(modelReasoningEfforts ? { modelReasoningEfforts } : {}),
|
|
@@ -126,6 +129,7 @@ function routedProviderConfig(providerName: string, provider: OcxProviderConfig)
|
|
|
126
129
|
...(autoToolChoiceOnlyModels ? { autoToolChoiceOnlyModels } : {}),
|
|
127
130
|
...(preserveReasoningContentModels ? { preserveReasoningContentModels } : {}),
|
|
128
131
|
...(thinkingToggleModels ? { thinkingToggleModels } : {}),
|
|
132
|
+
...(thinkingBudgetModels ? { thinkingBudgetModels } : {}),
|
|
129
133
|
};
|
|
130
134
|
}
|
|
131
135
|
|
|
@@ -10,7 +10,7 @@ import type { OcxProviderConfig } from "../types";
|
|
|
10
10
|
/** Providers whose listed model ids must be driven over the Anthropic wire even if the provider's
|
|
11
11
|
* configured adapter is something else (the upstream only speaks Anthropic for these models). */
|
|
12
12
|
const ANTHROPIC_WIRE_MODELS: Record<string, Set<string>> = {
|
|
13
|
-
"opencode-go": new Set(["minimax-m2.5", "minimax-m2.7", "minimax-m3"
|
|
13
|
+
"opencode-go": new Set(["minimax-m2.5", "minimax-m2.7", "minimax-m3"]),
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
/** Return a provider config whose adapter is forced to "anthropic" when the model id is wire-pinned. */
|