diff --git a/docs/providers/github-copilot.md b/docs/providers/github-copilot.md
index ccb091282035..c03c5f0b4c01 100644
--- a/docs/providers/github-copilot.md
+++ b/docs/providers/github-copilot.md
@@ -289,8 +289,9 @@ configured default model is never replaced.
Claude model IDs use the Anthropic Messages transport automatically.
Gemini models use the OpenAI Chat Completions transport; GPT and o-series
- models keep the OpenAI Responses transport. OpenClaw selects the correct
- transport based on the model ref.
+ models keep the OpenAI Responses transport. The bundled static catalog
+ includes these transports and request compatibility settings, so Gemini
+ keeps using Chat Completions when live discovery is disabled or unavailable.
diff --git a/extensions/github-copilot/model-metadata.ts b/extensions/github-copilot/model-metadata.ts
index 67d21f4dfafd..ec33d24d44de 100644
--- a/extensions/github-copilot/model-metadata.ts
+++ b/extensions/github-copilot/model-metadata.ts
@@ -26,10 +26,6 @@ const manifestModels = buildManifestModelProviderConfig({
providerId: "github-copilot",
catalog: manifestCatalog,
}).models;
-for (const model of manifestModels) {
- model.api = resolveCopilotTransportApi(model.id);
- model.compat = { ...resolveCopilotModelCompat(model.id), ...model.compat };
-}
const STATIC_MODEL_OVERRIDES = new Map>([
...manifestModels.map((model) => [model.id, model] as const),
diff --git a/extensions/github-copilot/openclaw.plugin.json b/extensions/github-copilot/openclaw.plugin.json
index dc4e60aed25f..0f194d387434 100644
--- a/extensions/github-copilot/openclaw.plugin.json
+++ b/extensions/github-copilot/openclaw.plugin.json
@@ -97,6 +97,13 @@
{
"id": "gemini-3.6-flash",
"name": "Gemini 3.6 Flash",
+ "api": "openai-completions",
+ "compat": {
+ "supportsStore": false,
+ "supportsDeveloperRole": false,
+ "supportsUsageInStreaming": false,
+ "maxTokensField": "max_tokens"
+ },
"input": ["text", "image"],
"contextWindow": 1048576,
"maxTokens": 65536,
@@ -105,6 +112,13 @@
{
"id": "gemini-3.1-pro-preview",
"name": "Gemini 3.1 Pro Preview",
+ "api": "openai-completions",
+ "compat": {
+ "supportsStore": false,
+ "supportsDeveloperRole": false,
+ "supportsUsageInStreaming": false,
+ "maxTokensField": "max_tokens"
+ },
"input": ["text", "image"],
"contextWindow": 1048576,
"maxTokens": 65536,
@@ -113,6 +127,13 @@
{
"id": "gemini-3.5-flash",
"name": "Gemini 3.5 Flash",
+ "api": "openai-completions",
+ "compat": {
+ "supportsStore": false,
+ "supportsDeveloperRole": false,
+ "supportsUsageInStreaming": false,
+ "maxTokensField": "max_tokens"
+ },
"status": "deprecated",
"replacedBy": "gemini-3.6-flash",
"input": ["text", "image"],
@@ -123,6 +144,13 @@
{
"id": "gemini-2.5-pro",
"name": "Gemini 2.5 Pro",
+ "api": "openai-completions",
+ "compat": {
+ "supportsStore": false,
+ "supportsDeveloperRole": false,
+ "supportsUsageInStreaming": false,
+ "maxTokensField": "max_tokens"
+ },
"status": "deprecated",
"replacedBy": "gemini-3.1-pro-preview",
"input": ["text", "image"],
diff --git a/src/plugin-sdk/test-helpers/provider-runtime-contract.ts b/src/plugin-sdk/test-helpers/provider-runtime-contract.ts
index 988055342219..ae6dbaeeeaac 100644
--- a/src/plugin-sdk/test-helpers/provider-runtime-contract.ts
+++ b/src/plugin-sdk/test-helpers/provider-runtime-contract.ts
@@ -1,5 +1,7 @@
// Provider runtime contract helpers define reusable runtime tests for provider plugins.
+import { normalizeModelCatalog } from "@openclaw/model-catalog-core/model-catalog-normalize";
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
+import { createPluginMetadataSnapshot } from "../../config/plugin-auto-enable.test-helpers.js";
import type { ProviderRuntimeModel } from "../plugin-entry.js";
import { registerProviderPlugin, requireRegisteredProvider } from "../plugin-test-runtime.js";
import { buildManifestModelProviderConfig } from "../provider-catalog-shared.js";
@@ -283,6 +285,95 @@ export function describeGithubCopilotProviderRuntimeContract(
]);
const createManifestModel = createManifestModelFactory("github-copilot", manifestCatalog);
+ it.each([
+ ["gemini-3.6-flash", "openai-completions", false],
+ ["gemini-3.1-pro-preview", "openai-completions", false],
+ ["gemini-3.5-flash", "openai-completions", false],
+ ["gemini-2.5-pro", "openai-completions", false],
+ ["gpt-5.6-sol", "openai-responses", false],
+ ["claude-sonnet-5", "anthropic-messages", false],
+ ["gemini-3.6-flash", "openai-completions", true],
+ ] as const)(
+ "routes static %s through %s with discovery enabled=%s",
+ async (modelId, api, discoveryEnabled) => {
+ const { createBundledStaticCatalogModelResolver } =
+ await import("../../agents/embedded-agent-runner/model.static-catalog.js");
+ const config = {
+ models: { catalogRefresh: { enabled: false } },
+ plugins: {
+ entries: {
+ "github-copilot": { config: { discovery: { enabled: discoveryEnabled } } },
+ },
+ },
+ };
+ const metadataSnapshot = createPluginMetadataSnapshot({
+ config,
+ manifestRegistry: {
+ diagnostics: [],
+ plugins: [
+ {
+ id: "github-copilot",
+ origin: "bundled",
+ providers: ["github-copilot"],
+ channels: [],
+ cliBackends: [],
+ skills: [],
+ hooks: [],
+ rootDir: "/fixtures/github-copilot",
+ source: "/fixtures/github-copilot/index.js",
+ manifestPath: "/fixtures/github-copilot/openclaw.plugin.json",
+ modelCatalog: normalizeModelCatalog(
+ {
+ providers: { "github-copilot": manifestCatalog },
+ discovery: { "github-copilot": "runtime" },
+ },
+ { ownedProviders: new Set(["github-copilot"]) },
+ ),
+ },
+ ],
+ },
+ });
+ const resolveModel = createBundledStaticCatalogModelResolver({
+ cfg: config,
+ env: {},
+ metadataSnapshot,
+ includeRuntimeDiscovery: true,
+ });
+ const model = resolveModel({ provider: "github-copilot", modelId });
+ expect(model?.api).toBe(api);
+ if (api === "openai-completions") {
+ expect(model?.compat).toMatchObject({
+ supportsStore: false,
+ supportsDeveloperRole: false,
+ supportsUsageInStreaming: false,
+ maxTokensField: "max_tokens",
+ });
+ }
+ const provider = requireProviderContractProvider("github-copilot");
+ expect(
+ provider.preferRuntimeResolvedModel?.({
+ config,
+ provider: "github-copilot",
+ modelId,
+ }),
+ ).toBe(discoveryEnabled);
+ // A missing prepared live row also occurs when enabled discovery is unavailable.
+ expect(
+ provider.resolveDynamicModel?.({
+ config,
+ provider: "github-copilot",
+ modelId,
+ modelRegistry: {
+ find: () => model,
+ getAll: () => (model ? [model] : []),
+ getAvailable: () => (model ? [model] : []),
+ hasConfiguredAuth: () => false,
+ },
+ }),
+ ).toBeUndefined();
+ },
+ );
+
it("owns Copilot-specific forward-compat fallbacks", () => {
const provider = requireProviderContractProvider("github-copilot");
const expected = createManifestModel("gpt-5.4");