@bitkyc08/opencodex 2.11.0 → 2.11.1
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/gui/dist/assets/{index-Bk-PN-70.css → index-BA1dgo4q.css} +1 -1
- package/gui/dist/assets/index-DDZpgzKk.js +70 -0
- package/gui/dist/index.html +2 -2
- package/package.json +6 -4
- package/src/adapters/anthropic.ts +10 -2
- package/src/adapters/cursor/native-exec-common.ts +6 -2
- package/src/adapters/google-antigravity-replay.ts +3 -2
- package/src/adapters/google-antigravity-wire.ts +38 -6
- package/src/adapters/google.ts +57 -10
- package/src/adapters/openai-chat.ts +19 -2
- package/src/claude/model-info.ts +1 -1
- package/src/cli/account-auth.ts +3 -1
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +15 -1
- package/src/cli/codex-shim-readiness.ts +69 -0
- package/src/cli/combo.ts +5 -0
- package/src/cli/config-command.ts +33 -4
- package/src/cli/doctor.ts +21 -0
- package/src/cli/export-command.ts +6 -6
- package/src/cli/help.ts +6 -6
- package/src/cli/index.ts +22 -5
- package/src/cli/models-runtime.ts +13 -1
- package/src/cli/provider.ts +7 -0
- package/src/clients/config-export.ts +67 -5
- package/src/codex/account-lifecycle.ts +99 -10
- package/src/codex/auth-api.ts +222 -34
- package/src/codex/catalog/account-models.ts +9 -4
- package/src/codex/catalog/aggregation.ts +41 -8
- package/src/codex/catalog/bundled.ts +54 -22
- package/src/codex/catalog/effort.ts +47 -20
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +70 -18
- package/src/codex/catalog/native-models.ts +7 -0
- package/src/codex/catalog/parsing.ts +29 -11
- package/src/codex/catalog/provider-fetch.ts +335 -50
- package/src/codex/catalog/sync.ts +588 -126
- package/src/codex/catalog-refresh-status.ts +87 -0
- package/src/codex/catalog-write-serialization.ts +2 -1
- package/src/codex/catalog.ts +4 -3
- package/src/codex/convergence-types.ts +1 -1
- package/src/codex/convergence.ts +190 -52
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/features.ts +79 -1
- package/src/codex/history-job.ts +81 -1
- package/src/codex/history-lock.ts +2 -1
- package/src/codex/history-provider.ts +4 -3
- package/src/codex/inject.ts +56 -12
- package/src/codex/model-cache.ts +50 -10
- package/src/codex/transition-state.ts +10 -2
- package/src/codex/user-identity.ts +110 -2
- package/src/combos/index.ts +3 -0
- package/src/combos/types.ts +75 -9
- package/src/config.ts +26 -12
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +12 -10
- package/src/integrations/registry.ts +7 -0
- package/src/integrations/writer.ts +1 -1
- package/src/lib/pinned-http.ts +40 -9
- package/src/lib/process-control.ts +4 -1
- package/src/lib/provider-outbound.ts +42 -9
- package/src/oauth/cursor.ts +25 -5
- package/src/oauth/index.ts +59 -45
- package/src/providers/antigravity-models.ts +115 -3
- package/src/providers/context-cap.ts +13 -5
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +14 -15
- package/src/providers/provider-id-rewrite.ts +29 -0
- package/src/providers/quota.ts +93 -19
- package/src/providers/registry.ts +16 -5
- package/src/providers/slug-codec.ts +13 -6
- package/src/reasoning-effort.ts +22 -0
- package/src/router.ts +0 -20
- package/src/routing/history/indexer.ts +54 -39
- package/src/routing/quota.ts +77 -56
- package/src/server/index.ts +32 -7
- package/src/server/management/combo-routes.ts +43 -19
- package/src/server/management/config-routes.ts +115 -20
- package/src/server/management/model-routes.ts +10 -7
- package/src/server/management/model-rows.ts +6 -2
- package/src/server/management/oauth-account-routes.ts +12 -0
- package/src/server/management/provider-routes.ts +114 -40
- package/src/server/management/routing-profile-routes.ts +0 -22
- package/src/server/management-api.ts +7 -25
- package/src/server/responses/core.ts +3 -3
- package/src/server/responses/policy-fallback.ts +152 -0
- package/src/server/responses.ts +3 -2
- package/src/types.ts +15 -2
- package/src/usage/cost.ts +0 -0
- package/src/vision/describe.ts +3 -1
- package/src/vision/index.ts +17 -6
- package/src/vision/reasoning.ts +55 -0
- package/src/web-search/parse.ts +15 -3
- package/gui/dist/assets/index-BynIEIV-.js +0 -70
|
@@ -111,6 +111,8 @@ export async function handleComboRoutes(ctx: ManagementContext): Promise<Respons
|
|
|
111
111
|
clearComboSelectionState,
|
|
112
112
|
clearComboTargetCooldowns,
|
|
113
113
|
comboConfigError,
|
|
114
|
+
comboDisabledModelId,
|
|
115
|
+
comboDisabledModelSelectors,
|
|
114
116
|
comboModelId,
|
|
115
117
|
comboPublicModelId,
|
|
116
118
|
normalizeComboConfig,
|
|
@@ -122,13 +124,31 @@ export async function handleComboRoutes(ctx: ManagementContext): Promise<Respons
|
|
|
122
124
|
});
|
|
123
125
|
if (error) return jsonResponse({ error }, 400);
|
|
124
126
|
const normalized = normalizeComboConfig(body.combo as import("../../types").OcxComboConfig);
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
:
|
|
127
|
+
const {
|
|
128
|
+
alias: normalizedAlias,
|
|
129
|
+
nativeAlias: normalizedNativeAlias,
|
|
130
|
+
displayName: normalizedDisplayName,
|
|
131
|
+
...normalizedBase
|
|
132
|
+
} = normalized;
|
|
133
|
+
const stored: import("../../types").OcxComboConfig = {
|
|
134
|
+
...normalizedBase,
|
|
135
|
+
...(normalizedAlias ? { alias: normalizedAlias } : {}),
|
|
136
|
+
...(normalizedNativeAlias ? { nativeAlias: true } : {}),
|
|
137
|
+
...(normalizedDisplayName ? { displayName: normalizedDisplayName } : {}),
|
|
138
|
+
};
|
|
128
139
|
const sourceId = renameFrom ?? id;
|
|
129
140
|
const previous = config.combos?.[sourceId];
|
|
130
141
|
const oldPublicModel = previous ? comboPublicModelId(sourceId, previous) : null;
|
|
131
142
|
const newPublicModel = comboPublicModelId(id, normalized);
|
|
143
|
+
const disabledIdentityChanged = previous !== undefined && (
|
|
144
|
+
renameFrom !== undefined
|
|
145
|
+
|| oldPublicModel !== newPublicModel
|
|
146
|
+
|| (previous.nativeAlias === true) !== normalized.nativeAlias
|
|
147
|
+
);
|
|
148
|
+
const oldDisabledSelectors = disabledIdentityChanged
|
|
149
|
+
? new Set(comboDisabledModelSelectors(sourceId, previous))
|
|
150
|
+
: new Set<string>();
|
|
151
|
+
const newDisabledModel = comboDisabledModelId(id, normalized);
|
|
132
152
|
if (codexAccountNamespaceForModel(config.codexAccountNamespaces, newPublicModel)) {
|
|
133
153
|
return jsonResponse({ error: CODEX_ACCOUNT_NAMESPACE_COMBO_ALIAS_COLLISION_ERROR }, 409);
|
|
134
154
|
}
|
|
@@ -137,36 +157,35 @@ export async function handleComboRoutes(ctx: ManagementContext): Promise<Respons
|
|
|
137
157
|
nextCombos[id] = stored;
|
|
138
158
|
config.combos = nextCombos;
|
|
139
159
|
let shouldSyncClaudeAgentDefs = false;
|
|
140
|
-
const migratedModels = new
|
|
141
|
-
if (oldPublicModel && oldPublicModel !== newPublicModel) {
|
|
142
|
-
migratedModels.
|
|
160
|
+
const migratedModels = new Map<string, string>();
|
|
161
|
+
if (oldPublicModel && oldPublicModel !== newPublicModel && previous?.nativeAlias !== true) {
|
|
162
|
+
migratedModels.set(oldPublicModel, newPublicModel);
|
|
143
163
|
}
|
|
144
|
-
if (renameFrom)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
if (renameFrom) {
|
|
165
|
+
// A bare native id is ambiguous after the alias changes. Preserve it as a native route,
|
|
166
|
+
// while the unambiguous canonical combo reference follows the renamed combo.
|
|
167
|
+
migratedModels.set(
|
|
168
|
+
comboModelId(renameFrom),
|
|
169
|
+
previous?.nativeAlias === true ? comboModelId(id) : newPublicModel,
|
|
148
170
|
);
|
|
171
|
+
}
|
|
172
|
+
if (migratedModels.size > 0) {
|
|
173
|
+
const migrateReference = (model: string): string => migratedModels.get(model) ?? model;
|
|
149
174
|
const migrateAgentReference = (model: string): string => {
|
|
150
175
|
const migrated = migrateReference(model);
|
|
151
176
|
if (migrated !== model) shouldSyncClaudeAgentDefs = true;
|
|
152
177
|
return migrated;
|
|
153
178
|
};
|
|
154
|
-
const migrateReferences = (models: string[]): string[] => [
|
|
155
|
-
...new Set(models.map(migrateReference)),
|
|
156
|
-
];
|
|
157
|
-
if (config.disabledModels) {
|
|
158
|
-
config.disabledModels = migrateReferences(config.disabledModels);
|
|
159
|
-
}
|
|
160
179
|
if (config.subagentModels) {
|
|
161
180
|
config.subagentModels = [...new Set(config.subagentModels.map(migrateAgentReference))];
|
|
162
181
|
}
|
|
163
182
|
if (config.injectionModel && migratedModels.has(config.injectionModel)) {
|
|
164
|
-
config.injectionModel =
|
|
183
|
+
config.injectionModel = migrateReference(config.injectionModel);
|
|
165
184
|
}
|
|
166
185
|
if (config.shadowCallIntercept?.model && migratedModels.has(config.shadowCallIntercept.model)) {
|
|
167
186
|
config.shadowCallIntercept = {
|
|
168
187
|
...config.shadowCallIntercept,
|
|
169
|
-
model:
|
|
188
|
+
model: migrateReference(config.shadowCallIntercept.model),
|
|
170
189
|
};
|
|
171
190
|
}
|
|
172
191
|
if (config.claudeCode) {
|
|
@@ -187,6 +206,11 @@ export async function handleComboRoutes(ctx: ManagementContext): Promise<Respons
|
|
|
187
206
|
config.claudeCode = claudeCode;
|
|
188
207
|
}
|
|
189
208
|
}
|
|
209
|
+
if (oldDisabledSelectors.size > 0 && config.disabledModels) {
|
|
210
|
+
config.disabledModels = [...new Set(config.disabledModels.map(model => (
|
|
211
|
+
oldDisabledSelectors.has(model) ? newDisabledModel : model
|
|
212
|
+
)))];
|
|
213
|
+
}
|
|
190
214
|
saveConfigPreservingClaudeCode(config);
|
|
191
215
|
reconcileLiveStateStores();
|
|
192
216
|
clearComboSelectionState(id);
|
|
@@ -197,7 +221,7 @@ export async function handleComboRoutes(ctx: ManagementContext): Promise<Respons
|
|
|
197
221
|
}
|
|
198
222
|
const catalogRefresh = await convergeCodexCatalog();
|
|
199
223
|
if (shouldSyncClaudeAgentDefs) await syncClaudeAgentDefsBestEffort();
|
|
200
|
-
return jsonResponse({ success: true, id, model: newPublicModel, combo:
|
|
224
|
+
return jsonResponse({ success: true, id, model: newPublicModel, combo: stored, catalogRefresh });
|
|
201
225
|
}
|
|
202
226
|
|
|
203
227
|
if (url.pathname === "/api/combos" && req.method === "DELETE") {
|
|
@@ -40,6 +40,11 @@ import { clearProviderQuotaCache, fetchProviderQuotaReports } from "../../provid
|
|
|
40
40
|
import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
|
|
41
41
|
import { clearThreadAccountMap } from "../../codex/routing";
|
|
42
42
|
import { primeCodexPoolQuotas } from "../../codex/auth-api";
|
|
43
|
+
import {
|
|
44
|
+
codexAccountPickerEnabled,
|
|
45
|
+
initializeDefaultCodexAccountNamespaces,
|
|
46
|
+
} from "../../codex/account-namespaces";
|
|
47
|
+
import { catalogRefreshIsPending } from "../../codex/catalog-refresh-status";
|
|
43
48
|
import { DEFAULT_PROVIDER_CONTEXT_CAP, globalContextCapValue, providerContextCap, providerContextCaps, setAllProviderContextCaps, setGlobalContextCapValue, setProviderContextCap } from "../../providers/context-cap";
|
|
44
49
|
import { resolveCodexHomeDir } from "../../codex/home";
|
|
45
50
|
import { readUsageEntries } from "../../usage/log";
|
|
@@ -47,6 +52,8 @@ import { getUsageDebugLogEntries } from "../../usage/debug";
|
|
|
47
52
|
import { parseRange, parseUsageSurface, summarizeUsage } from "../../usage/summary";
|
|
48
53
|
import { stripCodexRuntimeProviderFields } from "../../codex/auth-context";
|
|
49
54
|
import { getProviderRegistryEntry } from "../../providers/registry";
|
|
55
|
+
import { VISION_REASONING_EFFORTS, isVisionReasoningEffort } from "../../reasoning-effort";
|
|
56
|
+
import { normalizeVisionReasoningForModel } from "../../vision/reasoning";
|
|
50
57
|
import { getDebugLogEntries } from "../../lib/debug-log-buffer";
|
|
51
58
|
import { getInjectionDebugLogEntries } from "../../lib/injection-debug-log";
|
|
52
59
|
import {
|
|
@@ -74,7 +81,7 @@ import type { ManagementContext } from "./context";
|
|
|
74
81
|
import { readManagementJsonBody, rethrowManagementBodyTooLarge } from "./body";
|
|
75
82
|
|
|
76
83
|
export async function handleConfigRoutes(ctx: ManagementContext): Promise<Response | null> {
|
|
77
|
-
const { req, url, config, deps, syncClaudeAgentDefsBestEffort } = ctx;
|
|
84
|
+
const { req, url, config, deps, convergeCodexCatalog, syncClaudeAgentDefsBestEffort } = ctx;
|
|
78
85
|
if (url.pathname === "/api/config" && req.method === "GET") {
|
|
79
86
|
return jsonResponse(safeConfigDTO(config));
|
|
80
87
|
}
|
|
@@ -129,6 +136,7 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
129
136
|
hostname: config.hostname ?? "127.0.0.1",
|
|
130
137
|
streamMode: config.streamMode ?? "auto",
|
|
131
138
|
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb ?? 256,
|
|
139
|
+
codexAccountPickerEnabled: codexAccountPickerEnabled(config),
|
|
132
140
|
startupHealth: await getCachedStartupHealth(config),
|
|
133
141
|
codexRuntime: {
|
|
134
142
|
path: displayCodexRuntimePath(resolved.runtime.command),
|
|
@@ -205,10 +213,20 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
205
213
|
// (a Windows service does not inherit shell env). A stream-shape
|
|
206
214
|
// change applies to NEW turns only — the config object is shared by
|
|
207
215
|
// reference with the request handlers, no restart needed.
|
|
208
|
-
let
|
|
209
|
-
try {
|
|
210
|
-
if (
|
|
211
|
-
|
|
216
|
+
let parsedBody: unknown;
|
|
217
|
+
try { parsedBody = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
218
|
+
if (!isPlainRecord(parsedBody)) return jsonResponse({ error: "settings body must be an object" }, 400);
|
|
219
|
+
const body = parsedBody as {
|
|
220
|
+
codexAutoStart?: unknown;
|
|
221
|
+
streamMode?: unknown;
|
|
222
|
+
appOwnedMemoryBudgetMb?: unknown;
|
|
223
|
+
codexAccountPickerEnabled?: unknown;
|
|
224
|
+
};
|
|
225
|
+
if (body.codexAutoStart === undefined
|
|
226
|
+
&& body.streamMode === undefined
|
|
227
|
+
&& body.appOwnedMemoryBudgetMb === undefined
|
|
228
|
+
&& body.codexAccountPickerEnabled === undefined) {
|
|
229
|
+
return jsonResponse({ error: "provide codexAutoStart, streamMode, appOwnedMemoryBudgetMb, or codexAccountPickerEnabled" }, 400);
|
|
212
230
|
}
|
|
213
231
|
if (body.codexAutoStart !== undefined && typeof body.codexAutoStart !== "boolean") {
|
|
214
232
|
return jsonResponse({ error: "codexAutoStart boolean is required" }, 400);
|
|
@@ -216,6 +234,10 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
216
234
|
if (body.streamMode !== undefined && !isStreamMode(body.streamMode)) {
|
|
217
235
|
return jsonResponse({ error: "streamMode must be auto, legacy-tee, or eager-relay" }, 400);
|
|
218
236
|
}
|
|
237
|
+
if (body.codexAccountPickerEnabled !== undefined
|
|
238
|
+
&& typeof body.codexAccountPickerEnabled !== "boolean") {
|
|
239
|
+
return jsonResponse({ error: "codexAccountPickerEnabled boolean is required" }, 400);
|
|
240
|
+
}
|
|
219
241
|
if (body.appOwnedMemoryBudgetMb !== undefined && (
|
|
220
242
|
typeof body.appOwnedMemoryBudgetMb !== "number"
|
|
221
243
|
|| !Number.isInteger(body.appOwnedMemoryBudgetMb)
|
|
@@ -224,30 +246,76 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
224
246
|
)) {
|
|
225
247
|
return jsonResponse({ error: `appOwnedMemoryBudgetMb must be an integer from ${MIN_APP_OWNED_MEMORY_BUDGET_MB} to ${MAX_APP_OWNED_MEMORY_BUDGET_MB}` }, 400);
|
|
226
248
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
249
|
+
const previousSettings = {
|
|
250
|
+
codexAutoStart: config.codexAutoStart,
|
|
251
|
+
hasCodexAutoStart: Object.hasOwn(config, "codexAutoStart"),
|
|
252
|
+
streamMode: config.streamMode,
|
|
253
|
+
hasStreamMode: Object.hasOwn(config, "streamMode"),
|
|
254
|
+
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb,
|
|
255
|
+
hasAppOwnedMemoryBudgetMb: Object.hasOwn(config, "appOwnedMemoryBudgetMb"),
|
|
256
|
+
codexAccountNamespaces: config.codexAccountNamespaces,
|
|
257
|
+
hasCodexAccountNamespaces: Object.hasOwn(config, "codexAccountNamespaces"),
|
|
258
|
+
codexAccountPickerEnabled: config.codexAccountPickerEnabled,
|
|
259
|
+
hasCodexAccountPickerEnabled: Object.hasOwn(config, "codexAccountPickerEnabled"),
|
|
260
|
+
};
|
|
261
|
+
const pickerWasEnabled = codexAccountPickerEnabled(config);
|
|
262
|
+
let pickerIsEnabled = pickerWasEnabled;
|
|
263
|
+
try {
|
|
264
|
+
if (typeof body.codexAutoStart === "boolean") {
|
|
265
|
+
config.codexAutoStart = body.codexAutoStart;
|
|
235
266
|
}
|
|
267
|
+
if (body.streamMode !== undefined) {
|
|
268
|
+
if (body.streamMode === "auto") {
|
|
269
|
+
delete config.streamMode;
|
|
270
|
+
} else {
|
|
271
|
+
config.streamMode = body.streamMode as "legacy-tee" | "eager-relay";
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (typeof body.appOwnedMemoryBudgetMb === "number") {
|
|
275
|
+
config.appOwnedMemoryBudgetMb = body.appOwnedMemoryBudgetMb;
|
|
276
|
+
}
|
|
277
|
+
if (body.codexAccountPickerEnabled === true) {
|
|
278
|
+
config.codexAccountPickerEnabled = true;
|
|
279
|
+
initializeDefaultCodexAccountNamespaces(config);
|
|
280
|
+
} else if (body.codexAccountPickerEnabled === false) {
|
|
281
|
+
config.codexAccountPickerEnabled = false;
|
|
282
|
+
}
|
|
283
|
+
pickerIsEnabled = codexAccountPickerEnabled(config);
|
|
284
|
+
(deps.saveConfigPreservingClaudeCode ?? saveConfigPreservingClaudeCode)(config);
|
|
285
|
+
} catch (error) {
|
|
286
|
+
if (previousSettings.hasCodexAutoStart) config.codexAutoStart = previousSettings.codexAutoStart;
|
|
287
|
+
else delete config.codexAutoStart;
|
|
288
|
+
if (previousSettings.hasStreamMode) config.streamMode = previousSettings.streamMode;
|
|
289
|
+
else delete config.streamMode;
|
|
290
|
+
if (previousSettings.hasAppOwnedMemoryBudgetMb) {
|
|
291
|
+
config.appOwnedMemoryBudgetMb = previousSettings.appOwnedMemoryBudgetMb;
|
|
292
|
+
} else delete config.appOwnedMemoryBudgetMb;
|
|
293
|
+
if (previousSettings.hasCodexAccountNamespaces) {
|
|
294
|
+
config.codexAccountNamespaces = previousSettings.codexAccountNamespaces;
|
|
295
|
+
} else delete config.codexAccountNamespaces;
|
|
296
|
+
if (previousSettings.hasCodexAccountPickerEnabled) {
|
|
297
|
+
config.codexAccountPickerEnabled = previousSettings.codexAccountPickerEnabled;
|
|
298
|
+
} else delete config.codexAccountPickerEnabled;
|
|
299
|
+
throw error;
|
|
236
300
|
}
|
|
237
|
-
if (typeof body.appOwnedMemoryBudgetMb === "number") {
|
|
238
|
-
config.appOwnedMemoryBudgetMb = body.appOwnedMemoryBudgetMb;
|
|
239
|
-
}
|
|
240
|
-
saveConfigPreservingClaudeCode(config);
|
|
241
301
|
if (typeof body.appOwnedMemoryBudgetMb === "number") {
|
|
242
302
|
configureAppOwnedMemoryBudget(resolveAppOwnedMemoryBudgetBytes(body.appOwnedMemoryBudgetMb));
|
|
243
303
|
enforceAppOwnedMemoryBudget();
|
|
244
304
|
}
|
|
305
|
+
const catalogRefresh = pickerWasEnabled !== pickerIsEnabled
|
|
306
|
+
? await convergeCodexCatalog()
|
|
307
|
+
: undefined;
|
|
308
|
+
const catalogRefreshPending = catalogRefresh
|
|
309
|
+
? catalogRefreshIsPending(catalogRefresh)
|
|
310
|
+
: false;
|
|
245
311
|
invalidateStartupHealthCache();
|
|
246
312
|
return jsonResponse({
|
|
247
313
|
ok: true,
|
|
248
314
|
codexAutoStart: codexAutoStartEnabled(config),
|
|
249
315
|
streamMode: config.streamMode ?? "auto",
|
|
250
316
|
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb ?? 256,
|
|
317
|
+
codexAccountPickerEnabled: pickerIsEnabled,
|
|
318
|
+
catalogRefreshPending,
|
|
251
319
|
startupHealth: await getCachedStartupHealth(config),
|
|
252
320
|
});
|
|
253
321
|
}
|
|
@@ -312,11 +380,14 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
312
380
|
if (url.pathname === "/api/sidecar-settings" && req.method === "GET") {
|
|
313
381
|
const ws = config.webSearchSidecar ?? {};
|
|
314
382
|
const vs = config.visionSidecar ?? {};
|
|
383
|
+
const visionModel = vs.model || "gpt-5.4-mini";
|
|
384
|
+
const visionReasoning = normalizeVisionReasoningForModel(visionModel, vs.reasoning) ?? "low";
|
|
315
385
|
return jsonResponse({
|
|
316
386
|
webSearch: { model: ws.model ?? "gpt-5.6-luna", backend: ws.backend },
|
|
317
387
|
vision: {
|
|
318
|
-
model:
|
|
388
|
+
model: visionModel,
|
|
319
389
|
backend: vs.backend,
|
|
390
|
+
reasoning: visionReasoning,
|
|
320
391
|
maxDescriptionsPerTurn: vs.maxDescriptionsPerTurn,
|
|
321
392
|
},
|
|
322
393
|
});
|
|
@@ -332,7 +403,7 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
332
403
|
if (raw.vision !== undefined && !isPlainRecord(raw.vision)) return jsonResponse({ error: "vision must be an object" }, 400);
|
|
333
404
|
const body = raw as {
|
|
334
405
|
webSearch?: { model?: unknown; backend?: unknown; reasoning?: unknown };
|
|
335
|
-
vision?: { model?: unknown; backend?: unknown; maxDescriptionsPerTurn?: unknown };
|
|
406
|
+
vision?: { model?: unknown; backend?: unknown; reasoning?: unknown; maxDescriptionsPerTurn?: unknown };
|
|
336
407
|
};
|
|
337
408
|
if (body.webSearch && body.webSearch.backend !== undefined && body.webSearch.backend !== null
|
|
338
409
|
&& body.webSearch.backend !== "openai" && body.webSearch.backend !== "anthropic") {
|
|
@@ -348,6 +419,23 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
348
419
|
|| body.vision.maxDescriptionsPerTurn <= 0)) {
|
|
349
420
|
return jsonResponse({ error: "vision.maxDescriptionsPerTurn must be a positive integer" }, 400);
|
|
350
421
|
}
|
|
422
|
+
if (body.vision?.reasoning !== undefined && !isVisionReasoningEffort(body.vision.reasoning)) {
|
|
423
|
+
return jsonResponse({ error: `vision.reasoning must be ${VISION_REASONING_EFFORTS.join(", ")}` }, 400);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
let normalizedVisionReasoning: ReturnType<typeof normalizeVisionReasoningForModel>;
|
|
427
|
+
let visionReasoningTouched = false;
|
|
428
|
+
if (body.vision && (body.vision.model !== undefined || body.vision.reasoning !== undefined)) {
|
|
429
|
+
visionReasoningTouched = true;
|
|
430
|
+
const model = typeof body.vision.model === "string"
|
|
431
|
+
? (body.vision.model === "" ? "gpt-5.4-mini" : body.vision.model)
|
|
432
|
+
: (config.visionSidecar?.model || "gpt-5.4-mini");
|
|
433
|
+
const sourceReasoning = body.vision.reasoning ?? config.visionSidecar?.reasoning;
|
|
434
|
+
normalizedVisionReasoning = sourceReasoning === undefined
|
|
435
|
+
? undefined
|
|
436
|
+
: normalizeVisionReasoningForModel(model, sourceReasoning);
|
|
437
|
+
}
|
|
438
|
+
|
|
351
439
|
if (body.webSearch) {
|
|
352
440
|
config.webSearchSidecar = { ...config.webSearchSidecar };
|
|
353
441
|
if (typeof body.webSearch.model === "string") {
|
|
@@ -373,16 +461,23 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
373
461
|
if (typeof body.vision.maxDescriptionsPerTurn === "number") {
|
|
374
462
|
config.visionSidecar.maxDescriptionsPerTurn = body.vision.maxDescriptionsPerTurn;
|
|
375
463
|
}
|
|
464
|
+
if (visionReasoningTouched) {
|
|
465
|
+
if (normalizedVisionReasoning === undefined) delete config.visionSidecar.reasoning;
|
|
466
|
+
else config.visionSidecar.reasoning = normalizedVisionReasoning;
|
|
467
|
+
}
|
|
376
468
|
}
|
|
377
469
|
saveConfigPreservingClaudeCode(config);
|
|
378
470
|
const ws = config.webSearchSidecar ?? {};
|
|
379
471
|
const vs = config.visionSidecar ?? {};
|
|
472
|
+
const visionModel = vs.model || "gpt-5.4-mini";
|
|
473
|
+
const visionReasoning = normalizeVisionReasoningForModel(visionModel, vs.reasoning) ?? "low";
|
|
380
474
|
return jsonResponse({
|
|
381
475
|
ok: true,
|
|
382
476
|
webSearch: { model: ws.model ?? "gpt-5.6-luna", backend: ws.backend },
|
|
383
477
|
vision: {
|
|
384
|
-
model:
|
|
478
|
+
model: visionModel,
|
|
385
479
|
backend: vs.backend,
|
|
480
|
+
reasoning: visionReasoning,
|
|
386
481
|
maxDescriptionsPerTurn: vs.maxDescriptionsPerTurn,
|
|
387
482
|
},
|
|
388
483
|
});
|
|
@@ -30,7 +30,7 @@ function readInputModalities(raw: unknown): { values?: string[]; error?: string
|
|
|
30
30
|
return { values: raw as string[] };
|
|
31
31
|
}
|
|
32
32
|
import type { CatalogModel } from "../../codex/catalog";
|
|
33
|
-
import { catalogModelSlug, disabledNativeSlugs, invalidateCodexModelsCache, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
|
|
33
|
+
import { catalogModelSlug, configuredNativeAliasSlugs, disabledNativeSlugs, invalidateCodexModelsCache, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
|
|
34
34
|
import { CatalogGatherBusyError } from "../../codex/catalog/provider-fetch";
|
|
35
35
|
import { getProviderLiveModelCount } from "../../codex/model-cache";
|
|
36
36
|
import {
|
|
@@ -58,7 +58,7 @@ import { enrichProviderFromCatalog, listKeyLoginProviders } from "../../oauth/ke
|
|
|
58
58
|
import { deriveProviderPresets } from "../../providers/derive";
|
|
59
59
|
import { providerCodexAccountMode } from "../../providers/registry";
|
|
60
60
|
import { routedSlug, slugEquals } from "../../providers/slug-codec";
|
|
61
|
-
import { COMBO_NAMESPACE,
|
|
61
|
+
import { COMBO_NAMESPACE, comboDisabledModelSelectors, comboModelId, preservesPhysicalComboProvider } from "../../combos";
|
|
62
62
|
import { clearProviderQuotaCache, fetchProviderQuotaReports } from "../../providers/quota";
|
|
63
63
|
import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
|
|
64
64
|
import { clearThreadAccountMap } from "../../codex/routing";
|
|
@@ -255,17 +255,16 @@ export async function handleModelRoutes(ctx: ManagementContext): Promise<Respons
|
|
|
255
255
|
if (targets.length === 0) return jsonResponse({ error: "model visibility targets required" }, 400);
|
|
256
256
|
|
|
257
257
|
const knownComboSelectors = new Set(
|
|
258
|
-
Object.entries(config.combos ?? {}).flatMap(([id, combo]) =>
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
]),
|
|
258
|
+
Object.entries(config.combos ?? {}).flatMap(([id, combo]) => (
|
|
259
|
+
comboDisabledModelSelectors(id, combo)
|
|
260
|
+
)),
|
|
262
261
|
);
|
|
263
262
|
const targetComboSelectors = new Map<string, Set<string>>();
|
|
264
263
|
if (isVirtualComboNamespace) {
|
|
265
264
|
for (const target of targets) {
|
|
266
265
|
const combo = config.combos && Object.hasOwn(config.combos, target.id) ? config.combos[target.id] : undefined;
|
|
267
266
|
if (!combo) return jsonResponse({ error: "invalid model visibility target" }, 400);
|
|
268
|
-
targetComboSelectors.set(target.id, new Set(
|
|
267
|
+
targetComboSelectors.set(target.id, new Set(comboDisabledModelSelectors(target.id, combo)));
|
|
269
268
|
}
|
|
270
269
|
}
|
|
271
270
|
const matchesTarget = (stored: string, target: { id: string; native: boolean }) => target.native
|
|
@@ -284,8 +283,12 @@ export async function handleModelRoutes(ctx: ManagementContext): Promise<Respons
|
|
|
284
283
|
const nativeIds = provider === "openai"
|
|
285
284
|
? disabledNativeSlugs({ disabledModels: disabled })
|
|
286
285
|
: new Set<string>();
|
|
286
|
+
const nativeAliasSlugs = provider === "openai"
|
|
287
|
+
? configuredNativeAliasSlugs(config)
|
|
288
|
+
: new Set<string>();
|
|
287
289
|
disabled = disabled.filter(stored => (
|
|
288
290
|
knownComboSelectors.has(stored)
|
|
291
|
+
|| nativeAliasSlugs.has(stored)
|
|
289
292
|
|| (!stored.startsWith(`${provider}/`) && !nativeIds.has(stored))
|
|
290
293
|
));
|
|
291
294
|
}
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
* Bodies are unchanged from their previous home; only `export` was added.
|
|
10
10
|
*/
|
|
11
11
|
import type { CatalogModel } from "../../codex/catalog";
|
|
12
|
-
import { catalogModelSlug, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
|
|
12
|
+
import { catalogModelSlug, nativeModelRows, nativeReasoningEfforts, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
|
|
13
13
|
import type { ExportModel } from "../../clients/config-export";
|
|
14
14
|
import { providerContextCap } from "../../providers/context-cap";
|
|
15
|
+
import { isVisionReasoningEffort } from "../../reasoning-effort";
|
|
15
16
|
import { routedSlug, slugEquals } from "../../providers/slug-codec";
|
|
16
17
|
import type { OcxConfig } from "../../types";
|
|
17
18
|
import { fetchAllModels } from "./shared";
|
|
@@ -47,6 +48,8 @@ export async function listManagementModelRows(config: OcxConfig): Promise<Manage
|
|
|
47
48
|
namespaced: row.slug,
|
|
48
49
|
disabled: row.disabled,
|
|
49
50
|
native: true,
|
|
51
|
+
// The Codex catalog may advertise `ultra`, but the image describer accepts only low..max.
|
|
52
|
+
reasoningEfforts: nativeReasoningEfforts(row.slug).filter(isVisionReasoningEffort),
|
|
50
53
|
...(row.contextWindow !== undefined ? { contextWindow: row.contextWindow } : {}),
|
|
51
54
|
}));
|
|
52
55
|
const customModels: ManagementModelRow[] = (config.customModels ?? []).map(cm => {
|
|
@@ -76,11 +79,12 @@ export async function listManagementModelRows(config: OcxConfig): Promise<Manage
|
|
|
76
79
|
const namespaced = catalogModelSlug(m);
|
|
77
80
|
if (m.provider !== "combo" && customNamespaced.has(namespaced)) return null;
|
|
78
81
|
const contextCap = providerContextCap(config, m.provider);
|
|
82
|
+
const nativeAlias = m.provider === "combo" && m.nativeAlias === true;
|
|
79
83
|
return {
|
|
80
84
|
...m,
|
|
81
85
|
namespaced,
|
|
82
86
|
disabled: [...disabled].some(stored => (
|
|
83
|
-
stored === namespaced || slugEquals(stored, m.provider, m.id)
|
|
87
|
+
(!nativeAlias && stored === namespaced) || slugEquals(stored, m.provider, m.id)
|
|
84
88
|
)),
|
|
85
89
|
...(contextCap !== undefined ? { contextCap, contextCapped: m.contextCapped === true } : {}),
|
|
86
90
|
};
|
|
@@ -210,6 +210,10 @@ export async function handleOauthAccountRoutes(ctx: ManagementContext): Promise<
|
|
|
210
210
|
await removeCredential(provider);
|
|
211
211
|
reconcileLiveStateStores();
|
|
212
212
|
clearLoginState(provider);
|
|
213
|
+
const { clearModelCache } = await import("../../codex/model-cache");
|
|
214
|
+
const { clearGatherRoutedModelsInflight } = await import("../../codex/catalog");
|
|
215
|
+
clearModelCache(provider);
|
|
216
|
+
clearGatherRoutedModelsInflight();
|
|
213
217
|
// Drop cached/last-good quota rows tied to the removed credential.
|
|
214
218
|
const { clearProviderQuotaCache, clearAccountQuotaCache } = await import("../../providers/quota");
|
|
215
219
|
clearProviderQuotaCache();
|
|
@@ -281,6 +285,10 @@ export async function handleOauthAccountRoutes(ctx: ManagementContext): Promise<
|
|
|
281
285
|
const { resetAnthropicRoutingForManualSelection } = await import("../../oauth/anthropic-routing");
|
|
282
286
|
resetAnthropicRoutingForManualSelection(body.accountId);
|
|
283
287
|
}
|
|
288
|
+
const { clearModelCache } = await import("../../codex/model-cache");
|
|
289
|
+
const { clearGatherRoutedModelsInflight } = await import("../../codex/catalog");
|
|
290
|
+
clearModelCache(provider);
|
|
291
|
+
clearGatherRoutedModelsInflight();
|
|
284
292
|
const { clearProviderQuotaCache } = await import("../../providers/quota");
|
|
285
293
|
clearProviderQuotaCache();
|
|
286
294
|
return jsonResponse({ ok: true, provider, activeAccountId: body.accountId });
|
|
@@ -404,6 +412,10 @@ export async function handleOauthAccountRoutes(ctx: ManagementContext): Promise<
|
|
|
404
412
|
clearAnthropicSessionAffinityForAccount(id);
|
|
405
413
|
}
|
|
406
414
|
if (!getAccountSet(provider)) clearLoginState(provider);
|
|
415
|
+
const { clearModelCache } = await import("../../codex/model-cache");
|
|
416
|
+
const { clearGatherRoutedModelsInflight } = await import("../../codex/catalog");
|
|
417
|
+
clearModelCache(provider);
|
|
418
|
+
clearGatherRoutedModelsInflight();
|
|
407
419
|
const { clearProviderQuotaCache, clearAccountQuotaCache } = await import("../../providers/quota");
|
|
408
420
|
clearProviderQuotaCache();
|
|
409
421
|
clearAccountQuotaCache(provider);
|