From 42b352c57eb8b891637930a709de00c8594c4d49 Mon Sep 17 00:00:00 2001 From: Ayaan Zaidi Date: Wed, 29 Apr 2026 16:30:44 +0530 Subject: [PATCH] fix(github-copilot): publish model catalog --- extensions/github-copilot/models-defaults.ts | 23 ++- extensions/github-copilot/models.test.ts | 30 +-- .../github-copilot/openclaw.plugin.json | 195 ++++++++++++++++++ 3 files changed, 228 insertions(+), 20 deletions(-) diff --git a/extensions/github-copilot/models-defaults.ts b/extensions/github-copilot/models-defaults.ts index 600d894276b..1b5f9ef6905 100644 --- a/extensions/github-copilot/models-defaults.ts +++ b/extensions/github-copilot/models-defaults.ts @@ -8,16 +8,27 @@ const DEFAULT_MAX_TOKENS = 8192; // We keep this list intentionally broad; if a model isn't available Copilot will // return an error and users can remove it from their config. const DEFAULT_MODEL_IDS = [ + "claude-haiku-4.5", + "claude-opus-4.5", + "claude-opus-4.6", "claude-opus-4.7", + "claude-sonnet-4", "claude-sonnet-4.6", "claude-sonnet-4.5", - "gpt-4o", + "gemini-2.5-pro", + "gemini-3-flash", + "gemini-3.1-pro", "gpt-4.1", - "gpt-4.1-mini", - "gpt-4.1-nano", - "o1", - "o1-mini", - "o3-mini", + "gpt-5-mini", + "gpt-5.2", + "gpt-5.2-codex", + "gpt-5.3-codex", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "grok-code-fast-1", + "raptor-mini", + "goldeneye", ] as const; export function getDefaultCopilotModelIds(): string[] { diff --git a/extensions/github-copilot/models.test.ts b/extensions/github-copilot/models.test.ts index cdb5277986c..e503bd84910 100644 --- a/extensions/github-copilot/models.test.ts +++ b/extensions/github-copilot/models.test.ts @@ -23,12 +23,14 @@ vi.mock("openclaw/plugin-sdk/provider-model-shared", () => ({ }), })); -const loadJsonFile = vi.fn(); -const saveJsonFile = vi.fn(); +const jsonStoreMocks = vi.hoisted(() => ({ + loadJsonFile: vi.fn(), + saveJsonFile: vi.fn(), +})); vi.mock("openclaw/plugin-sdk/json-store", () => ({ - loadJsonFile, - saveJsonFile, + loadJsonFile: jsonStoreMocks.loadJsonFile, + saveJsonFile: jsonStoreMocks.saveJsonFile, })); vi.mock("openclaw/plugin-sdk/state-paths", () => ({ @@ -67,7 +69,7 @@ describe("github-copilot model defaults", () => { describe("getDefaultCopilotModelIds", () => { it("includes claude-opus-4.7", () => { expect(getDefaultCopilotModelIds()).toContain("claude-opus-4.7"); - expect(getDefaultCopilotModelIds()).not.toContain("claude-opus-4.6"); + expect(getDefaultCopilotModelIds()).toContain("claude-opus-4.6"); }); it("includes claude-sonnet-4.6", () => { @@ -303,8 +305,8 @@ describe("github-copilot token", () => { beforeEach(async () => { vi.resetModules(); - loadJsonFile.mockClear(); - saveJsonFile.mockClear(); + jsonStoreMocks.loadJsonFile.mockClear(); + jsonStoreMocks.saveJsonFile.mockClear(); ({ deriveCopilotApiBaseUrlFromToken, resolveCopilotApiToken } = await import("./token.js")); }); @@ -319,7 +321,7 @@ describe("github-copilot token", () => { it("uses cache when token is still valid", async () => { const now = Date.now(); - loadJsonFile.mockReturnValue({ + jsonStoreMocks.loadJsonFile.mockReturnValue({ token: "cached;proxy-ep=proxy.example.com;", expiresAt: now + 60 * 60 * 1000, updatedAt: now, @@ -329,8 +331,8 @@ describe("github-copilot token", () => { const res = await resolveCopilotApiToken({ githubToken: "gh", cachePath, - loadJsonFileImpl: loadJsonFile, - saveJsonFileImpl: saveJsonFile, + loadJsonFileImpl: jsonStoreMocks.loadJsonFile, + saveJsonFileImpl: jsonStoreMocks.saveJsonFile, fetchImpl: fetchImpl as unknown as typeof fetch, }); @@ -341,7 +343,7 @@ describe("github-copilot token", () => { }); it("fetches and stores token when cache is missing", async () => { - loadJsonFile.mockReturnValue(undefined); + jsonStoreMocks.loadJsonFile.mockReturnValue(undefined); const fetchImpl = vi.fn().mockResolvedValue({ ok: true, @@ -355,13 +357,13 @@ describe("github-copilot token", () => { const res = await resolveCopilotApiToken({ githubToken: "gh", cachePath, - loadJsonFileImpl: loadJsonFile, - saveJsonFileImpl: saveJsonFile, + loadJsonFileImpl: jsonStoreMocks.loadJsonFile, + saveJsonFileImpl: jsonStoreMocks.saveJsonFile, fetchImpl: fetchImpl as unknown as typeof fetch, }); expect(res.token).toBe("fresh;proxy-ep=https://proxy.contoso.test;"); expect(res.baseUrl).toBe("https://api.contoso.test"); - expect(saveJsonFile).toHaveBeenCalledTimes(1); + expect(jsonStoreMocks.saveJsonFile).toHaveBeenCalledTimes(1); }); }); diff --git a/extensions/github-copilot/openclaw.plugin.json b/extensions/github-copilot/openclaw.plugin.json index baa6d4baa6f..6b836c3552a 100644 --- a/extensions/github-copilot/openclaw.plugin.json +++ b/extensions/github-copilot/openclaw.plugin.json @@ -18,6 +18,201 @@ } } }, + "modelCatalog": { + "providers": { + "github-copilot": { + "baseUrl": "https://api.individual.githubcopilot.com", + "api": "openai-responses", + "models": [ + { + "id": "claude-haiku-4.5", + "name": "Claude Haiku 4.5", + "api": "anthropic-messages", + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "claude-opus-4.5", + "name": "Claude Opus 4.5", + "api": "anthropic-messages", + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "claude-opus-4.6", + "name": "Claude Opus 4.6", + "api": "anthropic-messages", + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "claude-opus-4.7", + "name": "Claude Opus 4.7", + "api": "anthropic-messages", + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "claude-sonnet-4", + "name": "Claude Sonnet 4", + "api": "anthropic-messages", + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "claude-sonnet-4.5", + "name": "Claude Sonnet 4.5", + "api": "anthropic-messages", + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "claude-sonnet-4.6", + "name": "Claude Sonnet 4.6", + "api": "anthropic-messages", + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gemini-3-flash", + "name": "Gemini 3 Flash", + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gemini-3.1-pro", + "name": "Gemini 3.1 Pro", + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gpt-4.1", + "name": "GPT-4.1", + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gpt-5-mini", + "name": "GPT-5 mini", + "reasoning": true, + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gpt-5.2", + "name": "GPT-5.2", + "reasoning": true, + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gpt-5.2-codex", + "name": "GPT-5.2-Codex", + "reasoning": true, + "input": ["text"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gpt-5.3-codex", + "name": "GPT-5.3-Codex", + "reasoning": true, + "input": ["text"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gpt-5.4", + "name": "GPT-5.4", + "reasoning": true, + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gpt-5.4-mini", + "name": "GPT-5.4 mini", + "reasoning": true, + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gpt-5.4-nano", + "name": "GPT-5.4 nano", + "reasoning": true, + "input": ["text", "image"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "grok-code-fast-1", + "name": "Grok Code Fast 1", + "input": ["text"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "raptor-mini", + "name": "Raptor mini", + "input": ["text"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "goldeneye", + "name": "Goldeneye", + "input": ["text"], + "contextWindow": 128000, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + } + ] + } + }, + "discovery": { + "github-copilot": "static" + } + }, "contracts": { "memoryEmbeddingProviders": ["github-copilot"] },