diff --git a/src/commands/models/list.list-command.forward-compat.test.ts b/src/commands/models/list.list-command.forward-compat.test.ts index 73cb279c225..3bf14e26fc6 100644 --- a/src/commands/models/list.list-command.forward-compat.test.ts +++ b/src/commands/models/list.list-command.forward-compat.test.ts @@ -62,6 +62,7 @@ const mocks = vi.hoisted(() => { loadModelRegistry: vi.fn(), loadModelCatalog: vi.fn(), loadProviderCatalogModelsForList: vi.fn(), + hasProviderStaticCatalogForFilter: vi.fn(), resolveConfiguredEntries: vi.fn(), printModelTable: vi.fn(), listProfilesForProvider: vi.fn(), @@ -88,6 +89,7 @@ function resetMocks() { }); mocks.loadModelCatalog.mockResolvedValue([]); mocks.loadProviderCatalogModelsForList.mockResolvedValue([]); + mocks.hasProviderStaticCatalogForFilter.mockResolvedValue(false); mocks.resolveConfiguredEntries.mockReturnValue({ entries: [ { @@ -141,6 +143,10 @@ function installModelsListCommandForwardCompatMocks() { printModelTable: mocks.printModelTable, })); + vi.doMock("./list.provider-catalog.js", () => ({ + hasProviderStaticCatalogForFilter: mocks.hasProviderStaticCatalogForFilter, + })); + vi.doMock("./list.registry-load.js", () => ({ loadListModelRegistry: async ( cfg: unknown, @@ -427,6 +433,7 @@ describe("modelsListCommand forward-compat", () => { describe("--all catalog supplementation", () => { it("uses the provider catalog fast path for Codex provider lists", async () => { mocks.resolveConfiguredEntries.mockReturnValueOnce({ entries: [] }); + mocks.hasProviderStaticCatalogForFilter.mockResolvedValueOnce(true); mocks.loadProviderCatalogModelsForList.mockResolvedValueOnce([ { provider: "codex", @@ -451,6 +458,7 @@ describe("modelsListCommand forward-compat", () => { cfg: mocks.resolvedConfig, agentDir: "/tmp/openclaw-agent", providerFilter: "codex", + staticOnly: true, }); expect(lastPrintedRows<{ key: string; available: boolean }>()).toEqual([ expect.objectContaining({ @@ -460,6 +468,21 @@ describe("modelsListCommand forward-compat", () => { ]); }); + it("keeps the registry path for provider filters without static catalog coverage", async () => { + mocks.resolveConfiguredEntries.mockReturnValueOnce({ entries: [] }); + mocks.hasProviderStaticCatalogForFilter.mockResolvedValueOnce(false); + const runtime = createRuntime(); + + await modelsListCommand({ all: true, provider: "openrouter", json: true }, runtime as never); + + expect(mocks.loadModelRegistry).toHaveBeenCalledWith( + mocks.resolvedConfig, + expect.objectContaining({ + providerFilter: "openrouter", + }), + ); + }); + it("includes synthetic codex gpt-5.4 in --all output when catalog supports it", async () => { mocks.resolveConfiguredEntries.mockReturnValueOnce({ entries: [] }); mocks.loadModelRegistry.mockResolvedValueOnce({ diff --git a/src/commands/models/list.provider-catalog.test.ts b/src/commands/models/list.provider-catalog.test.ts index 876efa71337..889a3be0686 100644 --- a/src/commands/models/list.provider-catalog.test.ts +++ b/src/commands/models/list.provider-catalog.test.ts @@ -135,6 +135,21 @@ describe("loadProviderCatalogModelsForList", () => { ); }); + it("requires complete discovery-entry coverage for static-only loads", async () => { + await loadProviderCatalogModelsForList({ + ...baseParams, + providerFilter: "moonshot", + staticOnly: true, + }); + + expect(providerDiscoveryMocks.resolvePluginDiscoveryProviders).toHaveBeenCalledWith( + expect.objectContaining({ + onlyPluginIds: ["moonshot"], + requireCompleteDiscoveryEntryCoverage: true, + }), + ); + }); + it("recognizes bundled provider hook aliases before the unknown-provider short-circuit", async () => { await expect( resolveProviderCatalogPluginIdsForFilter({