Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/codex/catalog/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,11 @@ export function shouldUpgradeToUpstreamEntry(entry: RawEntry): boolean {
}

export function nativeOpenAiSlugs(): string[] {
const live = listCatalogNativeSlugs();
const live = catalogNativeSlugs();
return live.length > 0 ? unique([...live, ...DOCUMENTED_NATIVE_OPENAI_ADDITIONS]) : NATIVE_OPENAI_MODELS;
}

export function listCatalogNativeSlugs(): string[] {
function catalogNativeSlugs(): string[] {
const cat = readCurrentCatalogOrCache();
const models = cat?.models ?? [];
const live = models.flatMap(entry => {
Expand All @@ -271,7 +271,11 @@ export function listCatalogNativeSlugs(): string[] {
// Deliberately ignore `visibility`: it is a rendered projection of disabledModels and account
// selectors, so treating it as fresh availability would shrink the supported set between syncs.
// visibleNativeSlugs applies the current disabledModels source of truth for public consumers.
return unique([...live, ...accountBound]);
}

export function listCatalogNativeSlugs(): string[] {
// Ensure documented additions (e.g. gpt-5.3-codex-spark) appear even when the bundled catalog
// predates the slug — mirrors nativeOpenAiSlugs() which already merges them for /v1/models.
return unique([...live, ...accountBound, ...DOCUMENTED_NATIVE_OPENAI_ADDITIONS]);
return unique([...catalogNativeSlugs(), ...DOCUMENTED_NATIVE_OPENAI_ADDITIONS]);
}
17 changes: 17 additions & 0 deletions tests/codex-catalog-sync-hardening.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,23 @@ describe("Codex catalog sync hardening", () => {
expect(rows.some(row => row.slug.startsWith("team/"))).toBe(false);
});

test("native model fallback remains reachable without a live catalog", () => {
writeFileSync(
join(codexHome, "config.toml"),
'model_catalog_json = "missing-catalog.json"\n',
"utf8",
);
const r = runScript(codexHome, opencodexHome, `
const { listCatalogNativeSlugs, nativeOpenAiSlugs, NATIVE_OPENAI_MODELS } = await import("./src/codex/catalog");
console.log(JSON.stringify({ picker: listCatalogNativeSlugs(), native: nativeOpenAiSlugs(), fallback: NATIVE_OPENAI_MODELS }));
`);

expect(r.status).toBe(0);
const result = JSON.parse(r.stdout) as { picker: string[]; native: string[]; fallback: string[] };
expect(result.picker).toContain("gpt-5.3-codex-spark");
expect(result.native).toEqual(result.fallback);
});

test("account sync recovers supported natives that were hidden before selectors existed", () => {
const catalogPath = join(codexHome, "catalog.json");
writeFileSync(join(codexHome, "config.toml"), 'model_catalog_json = "catalog.json"\n', "utf8");
Expand Down
Loading