@bitkyc08/opencodex 2.7.41 → 2.7.42
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 +4 -0
- package/gui/dist/assets/index-Bl_VBGoI.js +65 -0
- package/gui/dist/assets/index-DfVGuN88.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/base.ts +6 -0
- package/src/adapters/kiro-constants.ts +6 -2
- package/src/adapters/kiro-retry.ts +175 -10
- package/src/adapters/kiro.ts +172 -85
- package/src/adapters/mimo-free.ts +1 -0
- package/src/adapters/openai-chat.ts +30 -4
- package/src/adapters/openai-responses.ts +90 -12
- package/src/bridge.ts +91 -43
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +29 -2
- package/src/cli/access.ts +108 -0
- package/src/cli/account-auth.ts +223 -0
- package/src/cli/account.ts +9 -1
- package/src/cli/agent.ts +184 -0
- package/src/cli/combo.ts +119 -0
- package/src/cli/config-command.ts +145 -0
- package/src/cli/debug.ts +20 -8
- package/src/cli/doctor.ts +45 -8
- package/src/cli/help.ts +65 -13
- package/src/cli/index.ts +108 -7
- package/src/cli/integrations.ts +142 -0
- package/src/cli/models-runtime.ts +212 -0
- package/src/cli/models.ts +9 -10
- package/src/cli/observe.ts +117 -0
- package/src/cli/provider-runtime.ts +152 -0
- package/src/cli/provider.ts +23 -1
- package/src/cli/runtime-api.ts +325 -0
- package/src/cli/star-prompt.ts +3 -3
- package/src/cli/status.ts +17 -0
- package/src/cli/system-command.ts +112 -0
- package/src/codex/auth-api.ts +3 -2
- package/src/codex/catalog/aggregation.ts +113 -18
- package/src/codex/catalog/provider-fetch.ts +24 -13
- package/src/codex/catalog/sync.ts +20 -8
- package/src/codex/catalog.ts +2 -1
- package/src/codex/refresh.ts +10 -3
- package/src/codex/routing.ts +21 -32
- package/src/codex/sync.ts +17 -0
- package/src/config.ts +48 -0
- package/src/generated/jawcode-model-metadata.ts +2 -1
- package/src/grok/inject.ts +184 -4
- package/src/grok/status.ts +33 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/windows-elevation.ts +627 -0
- package/src/providers/openai-sidecar.ts +46 -2
- package/src/providers/registry.ts +52 -0
- package/src/server/auth-cors.ts +6 -0
- package/src/server/chat-completions.ts +6 -1
- package/src/server/claude-messages.ts +20 -1
- package/src/server/images.ts +14 -7
- package/src/server/management/agent-settings-routes.ts +10 -4
- package/src/server/management/combo-routes.ts +0 -1
- package/src/server/management/config-routes.ts +0 -1
- package/src/server/management/logs-usage-routes.ts +94 -0
- package/src/server/management/model-routes.ts +0 -1
- package/src/server/management/oauth-account-routes.ts +0 -1
- package/src/server/management/provider-routes.ts +0 -1
- package/src/server/management/shared.ts +0 -1
- package/src/server/management/system-routes.ts +27 -15
- package/src/server/management-api.ts +0 -1
- package/src/server/memory-watchdog.ts +54 -10
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +122 -2
- package/src/server/responses/core.ts +76 -13
- package/src/server/responses/passthrough-error.ts +38 -13
- package/src/server/startup-action-control.ts +266 -15
- package/src/service.ts +512 -3
- package/src/storage/cleanup.ts +1538 -0
- package/src/storage/scanner.ts +4 -1
- package/src/types.ts +16 -0
- package/src/update/job.ts +229 -25
- package/src/usage/log.ts +39 -0
- package/src/web-search/loop.ts +8 -1
- package/gui/dist/assets/index-B2J4t3te.css +0 -1
- package/gui/dist/assets/index-BmvM6wRb.js +0 -65
|
@@ -38,6 +38,37 @@ export const openAiApiCollisionWarnings = new Set<string>();
|
|
|
38
38
|
|
|
39
39
|
export const comboCatalogWarningSignatures = new Map<string, string>();
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Why `deriveComboCatalogModel` rejected a configured combo (#484 / #516).
|
|
43
|
+
* Distinguishes unresolved/incomplete member metadata from a complete but empty
|
|
44
|
+
* modality intersection so diagnostics do not send operators to the wrong fix.
|
|
45
|
+
*/
|
|
46
|
+
export type ComboCatalogOmissionReason =
|
|
47
|
+
| "incomplete_metadata"
|
|
48
|
+
| "incompatible_modalities";
|
|
49
|
+
|
|
50
|
+
/** Combos omitted from the catalog during the most recent `gatherRoutedModels` call (#484). */
|
|
51
|
+
export interface ComboCatalogOmission {
|
|
52
|
+
id: string;
|
|
53
|
+
targets: string[];
|
|
54
|
+
reason: ComboCatalogOmissionReason;
|
|
55
|
+
message: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let lastComboCatalogOmissions: ComboCatalogOmission[] = [];
|
|
59
|
+
|
|
60
|
+
export function clearLastComboCatalogOmissions(): void {
|
|
61
|
+
lastComboCatalogOmissions = [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function getLastComboCatalogOmissions(): readonly ComboCatalogOmission[] {
|
|
65
|
+
return lastComboCatalogOmissions;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function replaceLastComboCatalogOmissions(items: readonly ComboCatalogOmission[]): void {
|
|
69
|
+
lastComboCatalogOmissions = [...items];
|
|
70
|
+
}
|
|
71
|
+
|
|
41
72
|
export function intersectStrings(values: readonly string[][]): string[] {
|
|
42
73
|
if (values.length === 0) return [];
|
|
43
74
|
const rest = values.slice(1).map(value => new Set(value));
|
|
@@ -60,28 +91,50 @@ export function effectiveComboDefault(
|
|
|
60
91
|
return atOrBelow.at(-1)?.effort ?? ranked[0]!.effort;
|
|
61
92
|
}
|
|
62
93
|
|
|
63
|
-
|
|
64
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Classify a combo derivation failure. Returns `null` when the combo would catalog.
|
|
96
|
+
* Callers that already know derivation failed can map the reason into diagnostics.
|
|
97
|
+
*/
|
|
98
|
+
export function comboCatalogOmissionReason(
|
|
65
99
|
combo: NormalizedComboConfig,
|
|
66
100
|
members: readonly CatalogModel[],
|
|
67
|
-
):
|
|
68
|
-
if (combo.targets.length === 0) return
|
|
69
|
-
if (new Set(combo.targets.map(targetKey)).size !== combo.targets.length)
|
|
70
|
-
|
|
101
|
+
): ComboCatalogOmissionReason | null {
|
|
102
|
+
if (combo.targets.length === 0) return "incomplete_metadata";
|
|
103
|
+
if (new Set(combo.targets.map(targetKey)).size !== combo.targets.length) {
|
|
104
|
+
return "incomplete_metadata";
|
|
105
|
+
}
|
|
106
|
+
if (members.length !== combo.targets.length) return "incomplete_metadata";
|
|
71
107
|
if (!members.every((member, index) => (
|
|
72
108
|
`${member.provider}/${member.id}` === targetKey(combo.targets[index]!)
|
|
73
|
-
)))
|
|
109
|
+
))) {
|
|
110
|
+
return "incomplete_metadata";
|
|
111
|
+
}
|
|
74
112
|
const contexts = members.map(member => member.contextWindow);
|
|
75
|
-
if (contexts.some(value => typeof value !== "number" || value <= 0))
|
|
113
|
+
if (contexts.some(value => typeof value !== "number" || value <= 0)) {
|
|
114
|
+
return "incomplete_metadata";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const inputModalities = intersectStrings(
|
|
118
|
+
members.map(member => member.inputModalities ?? ["text"]),
|
|
119
|
+
);
|
|
120
|
+
if (inputModalities.length === 0) return "incompatible_modalities";
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function deriveComboCatalogModel(
|
|
125
|
+
id: string,
|
|
126
|
+
combo: NormalizedComboConfig,
|
|
127
|
+
members: readonly CatalogModel[],
|
|
128
|
+
): CatalogModel | null {
|
|
129
|
+
if (comboCatalogOmissionReason(combo, members) !== null) return null;
|
|
76
130
|
|
|
77
131
|
const inputModalities = intersectStrings(
|
|
78
132
|
members.map(member => member.inputModalities ?? ["text"]),
|
|
79
133
|
);
|
|
80
|
-
if (inputModalities.length === 0) return null;
|
|
81
134
|
const reasoningEfforts = intersectStrings(
|
|
82
135
|
members.map(member => member.reasoningEfforts ?? []),
|
|
83
136
|
);
|
|
84
|
-
const contextWindow = Math.min(...
|
|
137
|
+
const contextWindow = Math.min(...members.map(member => member.contextWindow!));
|
|
85
138
|
const maxInputTokens = Math.min(
|
|
86
139
|
...members.map(member => member.maxInputTokens ?? member.contextWindow!),
|
|
87
140
|
);
|
|
@@ -136,20 +189,62 @@ export function comboCatalogWarningSignature(
|
|
|
136
189
|
}).sort((a, b) => a.key.localeCompare(b.key)));
|
|
137
190
|
}
|
|
138
191
|
|
|
139
|
-
export function
|
|
192
|
+
export function comboCatalogOmissionDetail(reason: ComboCatalogOmissionReason): string {
|
|
193
|
+
return reason === "incompatible_modalities"
|
|
194
|
+
? "members have no common input modalities"
|
|
195
|
+
: "member capabilities are incomplete";
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** One-line sync/CLI summary that respects the actual omission reason(s). */
|
|
199
|
+
export function summarizeComboCatalogOmissions(
|
|
200
|
+
omissions: readonly Pick<ComboCatalogOmission, "reason">[],
|
|
201
|
+
): string {
|
|
202
|
+
const n = omissions.length;
|
|
203
|
+
const prefix = `${n} combo${n === 1 ? "" : "s"} omitted from the catalog`;
|
|
204
|
+
if (n === 0) return prefix + ".";
|
|
205
|
+
const reasons = new Set(omissions.map(item => item.reason));
|
|
206
|
+
if (reasons.size === 1) {
|
|
207
|
+
return `${prefix} because ${comboCatalogOmissionDetail([...reasons][0]!)}.`;
|
|
208
|
+
}
|
|
209
|
+
return `${prefix}.`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function buildComboCatalogOmission(
|
|
140
213
|
id: string,
|
|
141
214
|
combo: NormalizedComboConfig,
|
|
142
215
|
members: readonly CatalogModel[],
|
|
143
|
-
):
|
|
144
|
-
const signature = comboCatalogWarningSignature(combo, members);
|
|
145
|
-
if (comboCatalogWarningSignatures.get(id) === signature) return;
|
|
146
|
-
comboCatalogWarningSignatures.set(id, signature);
|
|
216
|
+
): ComboCatalogOmission {
|
|
147
217
|
const targets = combo.targets
|
|
148
218
|
.map(target => safeCatalogWarningLabel(targetKey(target)))
|
|
149
219
|
.sort((a, b) => a.localeCompare(b));
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
220
|
+
const reason = comboCatalogOmissionReason(combo, members) ?? "incomplete_metadata";
|
|
221
|
+
return {
|
|
222
|
+
id,
|
|
223
|
+
targets,
|
|
224
|
+
reason,
|
|
225
|
+
message: `[opencodex] Combo "${safeCatalogWarningLabel(id)}" is omitted from the catalog because ${comboCatalogOmissionDetail(reason)}: ${targets.join(", ")}.`,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Record a combo omitted from `/v1/models` + on-disk catalog and warn once per signature.
|
|
231
|
+
* Callers that need a race-free list (catalog sync) pass a local `sink` from the same
|
|
232
|
+
* gather invocation instead of reading process-global state afterward (#484 review).
|
|
233
|
+
*/
|
|
234
|
+
export function warnUncataloguedComboOnce(
|
|
235
|
+
id: string,
|
|
236
|
+
combo: NormalizedComboConfig,
|
|
237
|
+
members: readonly CatalogModel[],
|
|
238
|
+
sink: ComboCatalogOmission[] = lastComboCatalogOmissions,
|
|
239
|
+
): ComboCatalogOmission {
|
|
240
|
+
const omission = buildComboCatalogOmission(id, combo, members);
|
|
241
|
+
sink.push(omission);
|
|
242
|
+
const signature = comboCatalogWarningSignature(combo, members);
|
|
243
|
+
if (comboCatalogWarningSignatures.get(id) !== signature) {
|
|
244
|
+
comboCatalogWarningSignatures.set(id, signature);
|
|
245
|
+
console.warn(omission.message);
|
|
246
|
+
}
|
|
247
|
+
return omission;
|
|
153
248
|
}
|
|
154
249
|
|
|
155
250
|
export function exactComboCatalogSlugs(
|
|
@@ -47,11 +47,8 @@ import upstreamModelsSnapshot from "../data/upstream-models.json";
|
|
|
47
47
|
import { JAWCODE_CATALOG_AUGMENT_PROVIDERS, catalogModelSlug, shouldExposeRoutedModel } from "./parsing";
|
|
48
48
|
import type { CatalogModel } from "./parsing";
|
|
49
49
|
import { disabledNativeSlugs, hasComboTargets, nativeInputModalities, nativeOpenAiContextWindow, nativeOpenAiSlugs, nativeParallelToolCalls, nativeReasoningEfforts } from "./metadata";
|
|
50
|
-
import { deriveComboCatalogModel, normalizedOpenAiApiSignature, openAiApiCollisionWarnings, warnUncataloguedComboOnce } from "./aggregation";
|
|
51
|
-
|
|
52
|
-
type OcxProviderConfigWithReasoningSummaries = OcxProviderConfig & {
|
|
53
|
-
modelSupportsReasoningSummaries?: Record<string, boolean>;
|
|
54
|
-
};
|
|
50
|
+
import { deriveComboCatalogModel, normalizedOpenAiApiSignature, openAiApiCollisionWarnings, replaceLastComboCatalogOmissions, warnUncataloguedComboOnce } from "./aggregation";
|
|
51
|
+
import type { ComboCatalogOmission } from "./aggregation";
|
|
55
52
|
|
|
56
53
|
export type ProviderModelsApiItem = {
|
|
57
54
|
id: string;
|
|
@@ -89,6 +86,13 @@ export function configuredMaxInputTokens(prov: OcxProviderConfig, id: string): n
|
|
|
89
86
|
return typeof configured === "number" && configured > 0 ? configured : undefined;
|
|
90
87
|
}
|
|
91
88
|
|
|
89
|
+
function configuredReasoningSummarySupport(prov: OcxProviderConfig | undefined, id: string): boolean | undefined {
|
|
90
|
+
if (!prov) return undefined;
|
|
91
|
+
const explicit = modelRecordValue(prov.modelSupportsReasoningSummaries, id);
|
|
92
|
+
if (explicit !== undefined) return explicit;
|
|
93
|
+
return modelRecordValue(prov.modelReasoningSummaryDelivery, id) !== undefined ? true : undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
92
96
|
export function applyProviderConfigHints(name: string, prov: OcxProviderConfig, model: CatalogModel, providerCap?: number): CatalogModel {
|
|
93
97
|
void name;
|
|
94
98
|
const configuredCap = configuredContextWindow(prov, model.id);
|
|
@@ -104,10 +108,7 @@ export function applyProviderConfigHints(name: string, prov: OcxProviderConfig,
|
|
|
104
108
|
}
|
|
105
109
|
const reasoningEfforts = configuredReasoningEfforts(prov, model.id);
|
|
106
110
|
const defaultReasoningEffort = modelRecordValue(prov.modelDefaultReasoningEfforts, model.id) ?? model.defaultReasoningEffort;
|
|
107
|
-
const supportsReasoningSummaries =
|
|
108
|
-
(prov as OcxProviderConfigWithReasoningSummaries).modelSupportsReasoningSummaries,
|
|
109
|
-
model.id,
|
|
110
|
-
);
|
|
111
|
+
const supportsReasoningSummaries = configuredReasoningSummarySupport(prov, model.id);
|
|
111
112
|
const hinted = {
|
|
112
113
|
...model,
|
|
113
114
|
...(configuredCap !== undefined
|
|
@@ -464,7 +465,12 @@ export function filterCatalogVisibleModels(
|
|
|
464
465
|
});
|
|
465
466
|
}
|
|
466
467
|
|
|
467
|
-
export async function gatherRoutedModels(
|
|
468
|
+
export async function gatherRoutedModels(
|
|
469
|
+
config: OcxConfig,
|
|
470
|
+
options?: { comboOmissions?: ComboCatalogOmission[] },
|
|
471
|
+
): Promise<CatalogModel[]> {
|
|
472
|
+
// Per-invocation list: sync passes `comboOmissions` so overlapping gathers cannot race.
|
|
473
|
+
const localOmissions: ComboCatalogOmission[] = [];
|
|
468
474
|
const ttlMs = config.modelCacheTtlMs ?? DEFAULT_MODEL_CACHE_TTL_MS;
|
|
469
475
|
// Persisted provider entries can predate newer registry fields (noVisionModels,
|
|
470
476
|
// modelInputModalities, ...). The ROUTER merges registry seeds at request time
|
|
@@ -542,15 +548,20 @@ export async function gatherRoutedModels(config: OcxConfig): Promise<CatalogMode
|
|
|
542
548
|
.filter((member): member is CatalogModel => member !== undefined);
|
|
543
549
|
const derived = deriveComboCatalogModel(id, combo, members);
|
|
544
550
|
if (derived) all.push(derived);
|
|
545
|
-
else warnUncataloguedComboOnce(id, combo, members);
|
|
551
|
+
else warnUncataloguedComboOnce(id, combo, members, localOmissions);
|
|
552
|
+
}
|
|
553
|
+
replaceLastComboCatalogOmissions(localOmissions);
|
|
554
|
+
if (options?.comboOmissions) {
|
|
555
|
+
options.comboOmissions.length = 0;
|
|
556
|
+
options.comboOmissions.push(...localOmissions);
|
|
546
557
|
}
|
|
547
558
|
all.sort((a, b) => (a.provider === b.provider ? a.id.localeCompare(b.id) : a.provider.localeCompare(b.provider)));
|
|
548
559
|
// Enriched (registry-hydrated) provider clones, keyed by name — the same view used above so
|
|
549
560
|
// custom rows get the same noVisionModels / inputModalities treatment as discovered rows.
|
|
550
561
|
const enrichedByName = new Map(activeProviders);
|
|
551
562
|
const customModels = (config.customModels ?? []).map(cm => {
|
|
552
|
-
const rawProvider = config.providers[cm.provider]
|
|
553
|
-
const supportsReasoningSummaries =
|
|
563
|
+
const rawProvider = config.providers[cm.provider];
|
|
564
|
+
const supportsReasoningSummaries = configuredReasoningSummarySupport(rawProvider, cm.modelId);
|
|
554
565
|
const base: CatalogModel = {
|
|
555
566
|
id: cm.modelId,
|
|
556
567
|
provider: cm.provider,
|
|
@@ -37,7 +37,8 @@ import { applyNativeVisibility, disabledNativeSlugs, isUnsupportedOpenAiNativeSl
|
|
|
37
37
|
import { loadCatalogForSync, resetBundledCatalogCacheForTests } from "./bundled";
|
|
38
38
|
import { applyCatalogModelMetadata, applyReasoningLevels, catalogEntryEfforts, clampCatalogModelsToCodexSupport, ensureGpt56ReasoningLevels, ensureUltraReasoningLevel, isGpt56NativeSlug } from "./effort";
|
|
39
39
|
import { filterCatalogVisibleModels, gatherRoutedModels, lastDropWarnSignature } from "./provider-fetch";
|
|
40
|
-
import { comboCatalogWarningSignatures, comboMasqueradeCollisionWarnings, exactComboCatalogSlugs, openAiApiCollisionWarnings, resolveSlugAliasCollisions, slugAliasCollisionWarnings, warnComboMasqueradeCollisionOnce } from "./aggregation";
|
|
40
|
+
import { clearLastComboCatalogOmissions, comboCatalogWarningSignatures, comboMasqueradeCollisionWarnings, exactComboCatalogSlugs, openAiApiCollisionWarnings, resolveSlugAliasCollisions, slugAliasCollisionWarnings, warnComboMasqueradeCollisionOnce } from "./aggregation";
|
|
41
|
+
import type { ComboCatalogOmission } from "./aggregation";
|
|
41
42
|
|
|
42
43
|
export const MAX_SPAWN_AGENT_MODEL_OVERRIDES = 5;
|
|
43
44
|
|
|
@@ -293,6 +294,7 @@ export function resetCatalogRuntimeStateForTests(): void {
|
|
|
293
294
|
comboCatalogWarningSignatures.clear();
|
|
294
295
|
slugAliasCollisionWarnings.clear();
|
|
295
296
|
comboMasqueradeCollisionWarnings.clear();
|
|
297
|
+
clearLastComboCatalogOmissions();
|
|
296
298
|
clearModelCache();
|
|
297
299
|
}
|
|
298
300
|
|
|
@@ -452,14 +454,20 @@ export function mergeCatalogEntriesForSync(
|
|
|
452
454
|
return applyMultiAgentMode(applyNativeVisibility(mergedEntries, disabledNative), multiAgentMode);
|
|
453
455
|
}
|
|
454
456
|
|
|
455
|
-
export async function syncCatalogModels(config: OcxConfig): Promise<{
|
|
457
|
+
export async function syncCatalogModels(config: OcxConfig): Promise<{
|
|
458
|
+
added: number;
|
|
459
|
+
path: string;
|
|
460
|
+
catalogWritten: boolean;
|
|
461
|
+
comboOmissions: ComboCatalogOmission[];
|
|
462
|
+
}> {
|
|
456
463
|
const catalogPath = readCodexCatalogPath();
|
|
457
464
|
const catalog = loadCatalogForSync(catalogPath);
|
|
458
|
-
if (!catalog) return { added: 0, path: catalogPath };
|
|
465
|
+
if (!catalog) return { added: 0, path: catalogPath, catalogWritten: false, comboOmissions: [] };
|
|
459
466
|
|
|
460
467
|
const template = findNativeTemplate(catalog);
|
|
461
468
|
|
|
462
|
-
const
|
|
469
|
+
const comboOmissions: ComboCatalogOmission[] = [];
|
|
470
|
+
const goModels = await gatherRoutedModels(config, { comboOmissions });
|
|
463
471
|
try {
|
|
464
472
|
// Once-only: preserve the PRISTINE pre-opencodex catalog as the native-priority baseline
|
|
465
473
|
// (later syncs would otherwise overwrite it with featured-modified priorities).
|
|
@@ -493,7 +501,7 @@ export async function syncCatalogModels(config: OcxConfig): Promise<{ added: num
|
|
|
493
501
|
clampCatalogModelsToCodexSupport(catalog.models);
|
|
494
502
|
|
|
495
503
|
atomicWriteFile(catalogPath, JSON.stringify(catalog, null, 2) + "\n");
|
|
496
|
-
return { added: goEntries.length, path: catalogPath };
|
|
504
|
+
return { added: goEntries.length, path: catalogPath, catalogWritten: true, comboOmissions };
|
|
497
505
|
}
|
|
498
506
|
|
|
499
507
|
export function restoreCodexCatalog(): { removed: number; kept: number; path: string } {
|
|
@@ -524,10 +532,11 @@ export function restoreCodexCatalog(): { removed: number; kept: number; path: st
|
|
|
524
532
|
return { removed, kept: native.length, path: catalogPath };
|
|
525
533
|
}
|
|
526
534
|
|
|
527
|
-
|
|
535
|
+
/** Force Codex's models_cache stale from the on-disk catalog. Returns whether a cache write occurred. */
|
|
536
|
+
export function invalidateCodexModelsCache(): boolean {
|
|
528
537
|
try {
|
|
529
538
|
const catalogPath = readCodexCatalogPath();
|
|
530
|
-
if (!existsSync(catalogPath)) return;
|
|
539
|
+
if (!existsSync(catalogPath)) return false;
|
|
531
540
|
const catalog = JSON.parse(readFileSync(catalogPath, "utf8"));
|
|
532
541
|
const models = catalog.models ?? catalog;
|
|
533
542
|
const wrapper = {
|
|
@@ -536,5 +545,8 @@ export function invalidateCodexModelsCache(): void {
|
|
|
536
545
|
models,
|
|
537
546
|
};
|
|
538
547
|
atomicWriteFile(activeCodexModelsCachePath(), JSON.stringify(wrapper, null, 2) + "\n");
|
|
539
|
-
|
|
548
|
+
return true;
|
|
549
|
+
} catch {
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
540
552
|
}
|
package/src/codex/catalog.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { NATIVE_OPENAI_MODELS, nativeOpenAiContextWindow, disabledNativeSlugs, v
|
|
|
6
6
|
export { isSpawnableCodexCandidate, codexExecInvocation, loadBundledCodexCatalog, materializeBundledCodexCatalog, loadCatalogTemplate } from "./catalog/bundled";
|
|
7
7
|
export { nativeEffortClamp, shouldApplyNativeEffortClamp, catalogModelEfforts, codexSupportedReasoningEfforts, clampedDefaultEffort, clampEntryToCodexSupportedEfforts, clampCatalogModelsToCodexSupport } from "./catalog/effort";
|
|
8
8
|
export { applyProviderConfigHints, isDatedVariantId, filterCatalogVisibleModels, gatherRoutedModels, augmentRoutedModelsWithRegistryOpenAiApiRows, augmentRoutedModelsWithJawcodeMetadata } from "./catalog/provider-fetch";
|
|
9
|
-
export { deriveComboCatalogModel, exactComboCatalogSlugs, resetOpenAiApiCatalogWarningStateForTests, uniqueCatalogModelsForPublicList, uniqueCatalogModelsForRawPublicList } from "./catalog/aggregation";
|
|
9
|
+
export { deriveComboCatalogModel, exactComboCatalogSlugs, getLastComboCatalogOmissions, resetOpenAiApiCatalogWarningStateForTests, uniqueCatalogModelsForPublicList, uniqueCatalogModelsForRawPublicList, buildComboCatalogOmission, comboCatalogOmissionReason, summarizeComboCatalogOmissions } from "./catalog/aggregation";
|
|
10
|
+
export type { ComboCatalogOmission, ComboCatalogOmissionReason } from "./catalog/aggregation";
|
|
10
11
|
export { MAX_SPAWN_AGENT_MODEL_OVERRIDES, effectiveSubagentRoster, buildCatalogEntries, resetCatalogRuntimeStateForTests, orderForSubagents, mergeCatalogEntriesForSync, syncCatalogModels, restoreCodexCatalog, invalidateCodexModelsCache } from "./catalog/sync";
|
|
11
12
|
export type { SpawnAgentSurface, SubagentRosterExclusionReason, EffectiveSubagentModel, SubagentRosterExclusion, EffectiveSubagentRoster } from "./catalog/sync";
|
package/src/codex/refresh.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { invalidateCodexModelsCache, syncCatalogModels } from "./catalog";
|
|
3
|
+
import type { ComboCatalogOmission } from "./catalog/aggregation";
|
|
3
4
|
import { CODEX_MODELS_CACHE_PATH } from "./paths";
|
|
4
5
|
import { atomicWriteFile } from "../config";
|
|
5
6
|
import type { OcxConfig } from "../types";
|
|
@@ -8,7 +9,9 @@ export interface CodexCatalogRefreshResult {
|
|
|
8
9
|
added: number;
|
|
9
10
|
path: string;
|
|
10
11
|
catalogExists: boolean;
|
|
12
|
+
catalogWritten: boolean;
|
|
11
13
|
cacheSynced: boolean;
|
|
14
|
+
comboOmissions: ComboCatalogOmission[];
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
interface RefreshDeps {
|
|
@@ -40,7 +43,11 @@ export async function refreshCodexModelCatalog(
|
|
|
40
43
|
): Promise<CodexCatalogRefreshResult> {
|
|
41
44
|
const result = await deps.syncCatalogModels(config);
|
|
42
45
|
const catalogExists = deps.existsSync(result.path);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
const catalogWritten = result.catalogWritten === true;
|
|
47
|
+
const comboOmissions = result.comboOmissions ?? [];
|
|
48
|
+
if (!catalogExists) {
|
|
49
|
+
return { ...result, catalogExists, catalogWritten: false, cacheSynced: false, comboOmissions };
|
|
50
|
+
}
|
|
51
|
+
const cacheSynced = deps.invalidateCodexModelsCache();
|
|
52
|
+
return { ...result, catalogExists, catalogWritten, cacheSynced, comboOmissions };
|
|
46
53
|
}
|
package/src/codex/routing.ts
CHANGED
|
@@ -305,6 +305,16 @@ function preservedCooldownFields(health: CodexUpstreamHealth | undefined): Parti
|
|
|
305
305
|
return cooldownFields;
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
+
/** Manual selection resets transient routing evidence without bypassing a real 429 cooldown. */
|
|
309
|
+
export function resetCodexRoutingForManualSelection(accountId: string): void {
|
|
310
|
+
clearThreadAccountMap();
|
|
311
|
+
const current = upstreamHealth.get(accountId);
|
|
312
|
+
if (!current) return;
|
|
313
|
+
const preserved = preservedCooldownFields(current);
|
|
314
|
+
if (Object.keys(preserved).length === 0) upstreamHealth.delete(accountId);
|
|
315
|
+
else upstreamHealth.set(accountId, { consecutiveFailures: 0, ...preserved });
|
|
316
|
+
}
|
|
317
|
+
|
|
308
318
|
export function getCodexAccountCooldownUntil(accountId: string, now = Date.now()): number | null {
|
|
309
319
|
const cooldownUntil = upstreamHealth.get(accountId)?.cooldownUntil;
|
|
310
320
|
return typeof cooldownUntil === "number" && Number.isFinite(cooldownUntil) && cooldownUntil > now ? cooldownUntil : null;
|
|
@@ -491,21 +501,14 @@ function isUnknownUsage(usage: number): boolean {
|
|
|
491
501
|
return usage >= CODEX_UNKNOWN_USAGE_SCORE;
|
|
492
502
|
}
|
|
493
503
|
|
|
494
|
-
// Round-robin among eligible unknown-quota candidates. `getEligiblePoolAccounts`
|
|
495
|
-
// already returns a deterministic order (config order, main unshifted first) and
|
|
496
|
-
// excludes the active id, so taking the first eligible unknown is a stable rotation
|
|
497
|
-
// without any new per-account state.
|
|
498
|
-
function pickNextUnknownAccount(config: OcxConfig, active: string, now: number): string | null {
|
|
499
|
-
const eligible = getEligiblePoolAccounts(config, active, now)
|
|
500
|
-
.filter(id => isUnknownUsage(computeCodexUsageScore(getAccountQuota(id), getPoolAccountPlan(config, id))));
|
|
501
|
-
return eligible.length > 0 ? eligible[0]! : null;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
504
|
function applyQuotaAutoSwitch(config: OcxConfig, active: string, now: number): string {
|
|
505
505
|
const threshold = config.autoSwitchThreshold ?? 80;
|
|
506
506
|
if (threshold <= 0) return active;
|
|
507
507
|
const quota = getAccountQuota(active);
|
|
508
508
|
const activeUsage = computeCodexUsageScore(quota, getPoolAccountPlan(config, active));
|
|
509
|
+
// Unknown usage is not evidence that a user's explicit selection crossed the
|
|
510
|
+
// threshold. Wait for quota priming instead of rotating among guesses.
|
|
511
|
+
if (isUnknownUsage(activeUsage)) return active;
|
|
509
512
|
if (activeUsage < threshold) return active;
|
|
510
513
|
const best = pickLowerUsageAccount(config, active, activeUsage, now);
|
|
511
514
|
if (best !== active) {
|
|
@@ -513,21 +516,6 @@ function applyQuotaAutoSwitch(config: OcxConfig, active: string, now: number): s
|
|
|
513
516
|
return best;
|
|
514
517
|
}
|
|
515
518
|
|
|
516
|
-
// Deadlock guard: active is over threshold but no candidate scored strictly
|
|
517
|
-
// lower. When the active itself is unknown, every candidate is likely unknown
|
|
518
|
-
// too (100 < 100 never fires), which pins the pool to one account whose real
|
|
519
|
-
// usage we cannot see (e.g. quota never primed on WSL). Rotate to the next
|
|
520
|
-
// eligible unknown so rotation is not stuck; known-but-saturated accounts are
|
|
521
|
-
// intentionally left alone so a genuinely hot pool stays visible.
|
|
522
|
-
if (isUnknownUsage(activeUsage)) {
|
|
523
|
-
const next = pickNextUnknownAccount(config, active, now);
|
|
524
|
-
if (next) {
|
|
525
|
-
console.warn(`[codex-routing] quota unknown for active "${active}"; rotating to "${next}" (all candidates unknown, threshold=${threshold})`);
|
|
526
|
-
setActiveCodexAccount(config, next);
|
|
527
|
-
return next;
|
|
528
|
-
}
|
|
529
|
-
console.warn(`[codex-routing] quota unknown for active "${active}" and no eligible rotation target; staying put`);
|
|
530
|
-
}
|
|
531
519
|
return active;
|
|
532
520
|
}
|
|
533
521
|
|
|
@@ -585,7 +573,7 @@ export function previewCodexAccountForRequest(
|
|
|
585
573
|
getAccountQuota(entry.accountId),
|
|
586
574
|
getPoolAccountPlan(config, entry.accountId),
|
|
587
575
|
);
|
|
588
|
-
if (usage >= threshold) {
|
|
576
|
+
if (!isUnknownUsage(usage) && usage >= threshold) {
|
|
589
577
|
const best = pickLowerUsageAccount(config, entry.accountId, usage, now);
|
|
590
578
|
if (best !== entry.accountId) return best;
|
|
591
579
|
}
|
|
@@ -609,7 +597,7 @@ export function previewCodexAccountForRequest(
|
|
|
609
597
|
const threshold = config.autoSwitchThreshold ?? 80;
|
|
610
598
|
if (threshold > 0) {
|
|
611
599
|
const usage = computeCodexUsageScore(getAccountQuota(active), getPoolAccountPlan(config, active));
|
|
612
|
-
if (usage >= threshold) {
|
|
600
|
+
if (!isUnknownUsage(usage) && usage >= threshold) {
|
|
613
601
|
active = pickLowerUsageAccount(config, active, usage, now);
|
|
614
602
|
}
|
|
615
603
|
}
|
|
@@ -657,7 +645,7 @@ export function resolveCodexAccountForThreadDetailed(
|
|
|
657
645
|
getAccountQuota(entry.accountId),
|
|
658
646
|
getPoolAccountPlan(config, entry.accountId),
|
|
659
647
|
);
|
|
660
|
-
if (usage >= threshold) {
|
|
648
|
+
if (!isUnknownUsage(usage) && usage >= threshold) {
|
|
661
649
|
const best = pickLowerUsageAccount(config, entry.accountId, usage, now);
|
|
662
650
|
if (best !== entry.accountId) {
|
|
663
651
|
setActiveCodexAccount(config, best);
|
|
@@ -807,12 +795,13 @@ export function recordCodexUpstreamOutcome(
|
|
|
807
795
|
const hardCooldownUntil = getCodexAccountCooldownUntil(accountId, now) ?? undefined;
|
|
808
796
|
// Soft avoid + affinity clears are part of failover. When threshold is 0, leave
|
|
809
797
|
// sticky sessions alone (same as shouldFailover / applyFailureFailover no-ops).
|
|
810
|
-
const
|
|
798
|
+
const failoverThreshold = config.upstreamFailoverThreshold ?? 3;
|
|
811
799
|
const consecutiveFailures = stale ? 1 : (current?.consecutiveFailures ?? 0) + 1;
|
|
800
|
+
const failoverReady = failoverThreshold > 0 && consecutiveFailures >= failoverThreshold;
|
|
812
801
|
const escalationMs = CODEX_TRANSIENT_SOFT_AVOID_ESCALATION_MS[
|
|
813
|
-
Math.min(consecutiveFailures, CODEX_TRANSIENT_SOFT_AVOID_ESCALATION_MS.length
|
|
802
|
+
Math.min(Math.max(consecutiveFailures - failoverThreshold, 0), CODEX_TRANSIENT_SOFT_AVOID_ESCALATION_MS.length - 1)
|
|
814
803
|
]!;
|
|
815
|
-
const softAvoidUntil =
|
|
804
|
+
const softAvoidUntil = failoverReady
|
|
816
805
|
? Math.max(
|
|
817
806
|
getCodexAccountSoftAvoidUntil(accountId, now) ?? 0,
|
|
818
807
|
now + escalationMs,
|
|
@@ -831,7 +820,7 @@ export function recordCodexUpstreamOutcome(
|
|
|
831
820
|
// thread is still pinned to the FAILING account — a late failure from account A
|
|
832
821
|
// must not delete a newer healthy binding to account B (race: T→A, A fails,
|
|
833
822
|
// T→B, late A failure must not delete B's mapping).
|
|
834
|
-
if (
|
|
823
|
+
if (failoverReady && meta.threadId) {
|
|
835
824
|
const bound = threadAccountMap.get(meta.threadId);
|
|
836
825
|
if (bound?.accountId === accountId) threadAccountMap.delete(meta.threadId);
|
|
837
826
|
}
|
package/src/codex/sync.ts
CHANGED
|
@@ -4,15 +4,18 @@ import { refreshCodexModelCatalog } from "./refresh";
|
|
|
4
4
|
import { applyProxyEnv, loadConfig } from "../config";
|
|
5
5
|
import type { OcxConfig } from "../types";
|
|
6
6
|
import { collectOrcaCodexHomeDiagnostic } from "./home";
|
|
7
|
+
import { summarizeComboCatalogOmissions, type ComboCatalogOmission } from "./catalog/aggregation";
|
|
7
8
|
|
|
8
9
|
export interface CodexSyncResult {
|
|
9
10
|
ok: boolean;
|
|
10
11
|
added: number;
|
|
11
12
|
catalogPath: string | null;
|
|
12
13
|
catalogExists: boolean;
|
|
14
|
+
catalogWritten: boolean;
|
|
13
15
|
cacheSynced: boolean;
|
|
14
16
|
message: string;
|
|
15
17
|
warning?: string;
|
|
18
|
+
comboOmissions?: ComboCatalogOmission[];
|
|
16
19
|
projectConfigWarnings?: ProjectCodexConfigWarning[];
|
|
17
20
|
projectConfigGrouped?: { path: string; issues: string[]; bypass: string }[];
|
|
18
21
|
}
|
|
@@ -59,6 +62,7 @@ export async function syncModelsToCodex(
|
|
|
59
62
|
added: 0,
|
|
60
63
|
catalogPath: null,
|
|
61
64
|
catalogExists: false,
|
|
65
|
+
catalogWritten: false,
|
|
62
66
|
cacheSynced: false,
|
|
63
67
|
message: result.message,
|
|
64
68
|
};
|
|
@@ -69,22 +73,33 @@ export async function syncModelsToCodex(
|
|
|
69
73
|
let catalogPath: string | null = null;
|
|
70
74
|
let catalogPathForInjection: string | null | undefined;
|
|
71
75
|
let catalogExists = false;
|
|
76
|
+
let catalogWritten = false;
|
|
72
77
|
let cacheSynced = false;
|
|
73
78
|
let warning: string | undefined;
|
|
79
|
+
let comboOmissions: ComboCatalogOmission[] = [];
|
|
74
80
|
|
|
75
81
|
try {
|
|
76
82
|
const cat = await deps.refreshCodexModelCatalog(config);
|
|
77
83
|
added = cat.added;
|
|
78
84
|
catalogExists = cat.catalogExists;
|
|
85
|
+
catalogWritten = cat.catalogWritten;
|
|
79
86
|
cacheSynced = cat.cacheSynced;
|
|
80
87
|
catalogPathForInjection = cat.catalogExists ? cat.path : null;
|
|
81
88
|
catalogPath = catalogPathForInjection;
|
|
89
|
+
comboOmissions = cat.comboOmissions ?? [];
|
|
82
90
|
if (cat.added > 0) {
|
|
83
91
|
log?.log(` + ${cat.added} models appended to Codex catalog (${cat.path})`);
|
|
84
92
|
} else if (!cat.catalogExists) {
|
|
85
93
|
warning = "catalog sync skipped: no Codex catalog source found; keeping Codex's native catalog.";
|
|
86
94
|
log?.error(warning);
|
|
87
95
|
}
|
|
96
|
+
if (comboOmissions.length > 0) {
|
|
97
|
+
// Individual omission lines already went through console.warn during gather;
|
|
98
|
+
// keep a single summary on the sync logger to avoid duplicate stderr noise.
|
|
99
|
+
const summary = summarizeComboCatalogOmissions(comboOmissions);
|
|
100
|
+
log?.error(summary);
|
|
101
|
+
warning = warning ? `${warning} ${summary}` : summary;
|
|
102
|
+
}
|
|
88
103
|
} catch (e) {
|
|
89
104
|
warning = `catalog sync skipped: ${e instanceof Error ? e.message : String(e)}`;
|
|
90
105
|
log?.error(warning);
|
|
@@ -99,9 +114,11 @@ export async function syncModelsToCodex(
|
|
|
99
114
|
added,
|
|
100
115
|
catalogPath,
|
|
101
116
|
catalogExists,
|
|
117
|
+
catalogWritten,
|
|
102
118
|
cacheSynced,
|
|
103
119
|
message: result.message,
|
|
104
120
|
...(warning ? { warning } : {}),
|
|
121
|
+
...(comboOmissions.length > 0 ? { comboOmissions } : {}),
|
|
105
122
|
...(projectConfigWarnings.length > 0 ? {
|
|
106
123
|
projectConfigWarnings,
|
|
107
124
|
projectConfigGrouped: groupProjectCodexConfigWarningsByPath(projectConfigWarnings),
|
package/src/config.ts
CHANGED
|
@@ -11,11 +11,13 @@ import {
|
|
|
11
11
|
isWirePinnedModel,
|
|
12
12
|
MODEL_ADAPTER_OVERRIDE_ALLOWED,
|
|
13
13
|
OPENAI_PROVIDER_TIER_VERSION,
|
|
14
|
+
REASONING_SUMMARY_DELIVERY_VALUES,
|
|
14
15
|
type OcxConfig,
|
|
15
16
|
type OcxProviderConfig,
|
|
16
17
|
} from "./types";
|
|
17
18
|
import { isCanonicalOpenAiForwardProvider } from "./providers/openai-tiers";
|
|
18
19
|
import { parseDesktopProfile } from "./claude/desktop-profile";
|
|
20
|
+
import { modelRecordValue } from "./reasoning-effort";
|
|
19
21
|
|
|
20
22
|
let _atomicSeq = 0;
|
|
21
23
|
|
|
@@ -442,6 +444,34 @@ export function booleanRecordConfigError(value: unknown, field: string): string
|
|
|
442
444
|
return null;
|
|
443
445
|
}
|
|
444
446
|
|
|
447
|
+
const REASONING_SUMMARY_DELIVERY_SET = new Set<string>(REASONING_SUMMARY_DELIVERY_VALUES);
|
|
448
|
+
|
|
449
|
+
export function reasoningSummaryDeliveryRecordConfigError(
|
|
450
|
+
value: unknown,
|
|
451
|
+
supportsReasoningSummaries: unknown,
|
|
452
|
+
field = "modelReasoningSummaryDelivery",
|
|
453
|
+
): string | null {
|
|
454
|
+
if (value === undefined) return null;
|
|
455
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return `${field} must be a plain object`;
|
|
456
|
+
const prototype = Object.getPrototypeOf(value);
|
|
457
|
+
if (prototype !== Object.prototype && prototype !== null) return `${field} must be a plain object with own properties`;
|
|
458
|
+
|
|
459
|
+
const supports = booleanRecordConfigError(supportsReasoningSummaries, "modelSupportsReasoningSummaries") === null
|
|
460
|
+
&& supportsReasoningSummaries && typeof supportsReasoningSummaries === "object"
|
|
461
|
+
? supportsReasoningSummaries as Record<string, boolean>
|
|
462
|
+
: undefined;
|
|
463
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
464
|
+
if (!key.trim()) return `${field} keys must be nonblank model ids`;
|
|
465
|
+
if (typeof entry !== "string" || !REASONING_SUMMARY_DELIVERY_SET.has(entry)) {
|
|
466
|
+
return `${field}.${key} must be one of: ${REASONING_SUMMARY_DELIVERY_VALUES.join(", ")}`;
|
|
467
|
+
}
|
|
468
|
+
if (modelRecordValue(supports, key) === false) {
|
|
469
|
+
return `${field}.${key} conflicts with modelSupportsReasoningSummaries=false`;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
|
|
445
475
|
/**
|
|
446
476
|
* Validate a provider's per-model wire override map (#404).
|
|
447
477
|
*
|
|
@@ -605,6 +635,17 @@ const configSchema = z.object({
|
|
|
605
635
|
message: reasoningSummariesError,
|
|
606
636
|
});
|
|
607
637
|
}
|
|
638
|
+
const reasoningSummaryDeliveryError = reasoningSummaryDeliveryRecordConfigError(
|
|
639
|
+
(provider as { modelReasoningSummaryDelivery?: unknown }).modelReasoningSummaryDelivery,
|
|
640
|
+
(provider as { modelSupportsReasoningSummaries?: unknown }).modelSupportsReasoningSummaries,
|
|
641
|
+
);
|
|
642
|
+
if (reasoningSummaryDeliveryError) {
|
|
643
|
+
ctx.addIssue({
|
|
644
|
+
code: "custom",
|
|
645
|
+
path: ["providers", name, "modelReasoningSummaryDelivery"],
|
|
646
|
+
message: reasoningSummaryDeliveryError,
|
|
647
|
+
});
|
|
648
|
+
}
|
|
608
649
|
const defaultMaxOutputError = positiveIntegerConfigError(
|
|
609
650
|
(provider as { defaultMaxOutputTokens?: unknown }).defaultMaxOutputTokens,
|
|
610
651
|
"defaultMaxOutputTokens",
|
|
@@ -820,6 +861,13 @@ function schemaDiagnosticsError(error: z.ZodError): string {
|
|
|
820
861
|
return details.length > 0 ? `schema_invalid: ${details.join("; ")}` : "schema_invalid";
|
|
821
862
|
}
|
|
822
863
|
|
|
864
|
+
/** Validate an in-memory config candidate without touching disk. Used by headless CLI import/set. */
|
|
865
|
+
export function validateConfigCandidate(value: unknown): { ok: true; config: OcxConfig } | { ok: false; error: string } {
|
|
866
|
+
const result = configSchema.safeParse(value);
|
|
867
|
+
if (result.success) return { ok: true, config: result.data as OcxConfig };
|
|
868
|
+
return { ok: false, error: schemaDiagnosticsError(result.error) };
|
|
869
|
+
}
|
|
870
|
+
|
|
823
871
|
export function readConfigDiagnostics(): ConfigDiagnostics {
|
|
824
872
|
const configPath = getConfigPath();
|
|
825
873
|
if (!existsSync(configPath)) {
|
|
@@ -29,7 +29,8 @@ const PROVIDER_ALIASES: Record<string, string> = {
|
|
|
29
29
|
"gemini-antigravity": "google",
|
|
30
30
|
"moonshot": "moonshot",
|
|
31
31
|
"minimax": "minimax",
|
|
32
|
-
"minimax-cn": "minimax"
|
|
32
|
+
"minimax-cn": "minimax",
|
|
33
|
+
"zhipu-bigmodel": "zai"
|
|
33
34
|
} as const;
|
|
34
35
|
|
|
35
36
|
type Row = readonly [id: string, contextWindow?: number | null, maxTokens?: number | null, input?: string | null, reasoning?: 0 | 1 | null, wireModelId?: string | null, costInput?: number | null, costOutput?: number | null, costCacheRead?: number | null, costCacheWrite?: number | null];
|