diff --git a/docs/concepts/model-providers.md b/docs/concepts/model-providers.md index 21f871914321..a7a00746aaf3 100644 --- a/docs/concepts/model-providers.md +++ b/docs/concepts/model-providers.md @@ -298,7 +298,7 @@ messages and normalizes `stats.cached` into `cacheRead`; legacy | Venice | `venice` | `VENICE_API_KEY` | - | | Vercel AI Gateway | `vercel-ai-gateway` | `AI_GATEWAY_API_KEY` | `vercel-ai-gateway/anthropic/claude-opus-4.6` | | Volcano Engine (Doubao) | `volcengine` / `volcengine-plan` | `VOLCANO_ENGINE_API_KEY` | `volcengine-plan/ark-code-latest` | -| xAI | `xai` | SuperGrok/X Premium OAuth or `XAI_API_KEY` | OAuth: `xai/auto`; API key: `xai/grok-4.5` | +| xAI | `xai` | SuperGrok/X Premium OAuth or `XAI_API_KEY` | OAuth: `xai/auto`; API key: `xai/grok-4.6` | | Xiaomi | `xiaomi` / `xiaomi-token-plan` | `XIAOMI_API_KEY` / `XIAOMI_TOKEN_PLAN_API_KEY` | `xiaomi/mimo-v2.5` / `xiaomi-token-plan/mimo-v2.5-pro` | #### Quirks worth knowing @@ -317,7 +317,7 @@ messages and normalizes `stats.cached` into `cacheRead`; legacy Model ids use a `nvidia//` namespace (for example `nvidia/nvidia/nemotron-...`); pickers preserve the literal `/` composition while the canonical key sent to the API stays single-prefixed. - Uses the xAI Responses path. The recommended path is SuperGrok/X Premium OAuth; fresh setup selects `xai/auto`, which follows xAI's authenticated default model without an OpenClaw update. Existing concrete model ids stay pinned. API keys still work via `XAI_API_KEY` or plugin config and use `grok-4.5` as the setup default. Grok `web_search` reuses the same auth profile before API-key fallback. Older `/fast` and `params.fastMode: true` configurations still resolve through xAI's Grok 4.3 compatibility redirects, but new configurations should select a current model directly. `tool_stream` defaults on; disable via `agents.defaults.models["xai/"].params.tool_stream=false`. + Uses the xAI Responses path. The recommended path is SuperGrok/X Premium OAuth; fresh setup selects `xai/auto`, which follows xAI's authenticated default model without an OpenClaw update. Existing concrete model ids stay pinned. API keys still work via `XAI_API_KEY` or plugin config and use `grok-4.6` as the setup default. Grok `web_search` reuses the same auth profile before API-key fallback. Older `/fast` and `params.fastMode: true` configurations still resolve through xAI's Grok 4.3 compatibility redirects, but new configurations should select a current model directly. `tool_stream` defaults on; disable via `agents.defaults.models["xai/"].params.tool_stream=false`. diff --git a/docs/gateway/configuration-reference.md b/docs/gateway/configuration-reference.md index d34e4abd3f6e..d2c8f5709ddf 100644 --- a/docs/gateway/configuration-reference.md +++ b/docs/gateway/configuration-reference.md @@ -396,7 +396,7 @@ cannot be read, account-wide exposure fails closed. - `timeoutSeconds`: scrape request timeout in seconds (default: `60`). - `plugins.entries.xai.config.xSearch`: xAI X Search (Grok web search) settings. - `enabled`: enable the X Search provider. - - `model`: Grok model to use for search (e.g. `"grok-4.5"`). + - `model`: Grok model to use for search (e.g. `"grok-4.6"`). - `plugins.entries.memory-core.config.dreaming`: memory dreaming settings. See [Dreaming](/concepts/dreaming) for phases and thresholds. - `enabled`: master dreaming switch (default `false`). - `frequency`: cron cadence for each full dreaming sweep (`"0 3 * * *"` by default). diff --git a/docs/help/testing-live.md b/docs/help/testing-live.md index 56c650d3ea8c..c888f145c428 100644 --- a/docs/help/testing-live.md +++ b/docs/help/testing-live.md @@ -603,7 +603,7 @@ Live is opt-in, so there is no fixed "CI model list." `OPENCLAW_LIVE_MODELS=mode | `openrouter/minimax/minimax-m2.7` | | | `opencode-go/glm-5` | | | `openrouter/ai21/jamba-large-1.7` | | -| `xai/grok-4.5` | | +| `xai/grok-4.6` | | | `xai/grok-4.20-0309-reasoning` | | | `zai/glm-5.1` | | | `fireworks/accounts/fireworks/models/glm-5p1` | | diff --git a/docs/providers/xai.md b/docs/providers/xai.md index 6badec874c18..9612eb337437 100644 --- a/docs/providers/xai.md +++ b/docs/providers/xai.md @@ -56,7 +56,7 @@ OAuth client. API-key setup still works for xAI Console keys and for media surfaces - that need key-backed provider config. It uses Grok 4.5 as the setup + that need key-backed provider config. It uses Grok 4.6 as the setup default: ```bash @@ -115,6 +115,7 @@ see [legacy compatibility and moving aliases](#legacy-compatibility-and-moving-a | Family | Model ids | | -------------- | ------------------------------------------------------------ | +| Grok 4.6 | `grok-4.6` | | Grok 4.5 | `grok-4.5` (aliases: `grok-4.5-latest`, `grok-build-latest`) | | Grok Build 0.1 | `grok-build-0.1` | | Grok 4.3 | `grok-4.3` (aliases: `grok-4.3-latest`, `grok-latest`) | @@ -122,9 +123,9 @@ see [legacy compatibility and moving aliases](#legacy-compatibility-and-moving-a Use `xai/auto` to follow xAI's authenticated OAuth default, or select a concrete -id such as `xai/grok-4.5` to remain pinned. API-key setup uses Grok 4.5 by -default; `grok-build-0.1`, Grok 4.3, and both dated Grok 4.20 variants remain -selectable. +id such as `xai/grok-4.6` to remain pinned. API-key setup uses Grok 4.6 by +default; Grok 4.5, `grok-build-0.1`, Grok 4.3, and both dated Grok 4.20 variants +remain selectable. Catalog context and token-cost metadata follows xAI's live @@ -537,7 +538,7 @@ stale context metadata on active 4.20 rows. It does not pin active 4.20 | Key | Type | Default | Description | | ----------------- | ------- | ------------------------- | ------------------------------------------------ | | `enabled` | boolean | Automatic for xAI models | Disable, or opt in for a known non-xAI provider | - | `model` | string | `grok-4.5` | Model used for x_search requests | + | `model` | string | `grok-4.6` | Model used for x_search requests | | `baseUrl` | string | - | xAI Responses base URL override | | `inlineCitations` | boolean | - | Include inline citations in results | | `maxTurns` | number | - | Maximum conversation turns | @@ -552,7 +553,7 @@ stale context metadata on active 4.20 rows. It does not pin active 4.20 config: { xSearch: { enabled: true, - model: "grok-4.5", + model: "grok-4.6", baseUrl: "https://api.x.ai/v1", inlineCitations: true, }, @@ -574,7 +575,7 @@ stale context metadata on active 4.20 rows. It does not pin active 4.20 | Key | Type | Default | Description | | ---------------- | ------- | ------------------------ | ------------------------------------------------ | | `enabled` | boolean | Automatic for xAI models | Disable, or opt in for a known non-xAI provider | - | `model` | string | `grok-4.5` | Model used for code execution requests | + | `model` | string | `grok-4.6` | Model used for code execution requests | | `maxTurns` | number | - | Maximum conversation turns | | `timeoutSeconds` | number | `30` | Request timeout in seconds | @@ -590,7 +591,7 @@ stale context metadata on active 4.20 rows. It does not pin active 4.20 config: { codeExecution: { enabled: true, - model: "grok-4.5", + model: "grok-4.6", }, }, }, @@ -628,8 +629,8 @@ stale context metadata on active 4.20 rows. It does not pin active 4.20 to disable it. - The bundled xAI wrapper strips unsupported contains-count schema bounds and unsupported reasoning *effort* payload keys before sending native - xAI requests. Grok 4.5 supports low, medium, and - high effort (default high). Grok 4.3 supports none, low, medium, and high + xAI requests. Grok 4.6 and Grok 4.5 support low, medium, and high effort + (default high). Grok 4.3 supports none, low, medium, and high effort (default low). Other reasoning-capable xAI models do not expose a configurable effort control, but still request `include: ["reasoning.encrypted_content"]` so prior encrypted reasoning @@ -659,7 +660,7 @@ OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_TEST_QUIET=1 pnpm test:live -- extensions/xai OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_XAI_VIDEO=1 pnpm test:live -- extensions/xai/xai.live.test.ts -t "classic Grok Imagine" OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_XAI_VIDEO=1 pnpm test:live -- extensions/xai/xai.live.test.ts -t "Grok Imagine Video 1.5" OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_TEST_QUIET=1 pnpm test:live -- extensions/xai/x-search.live.test.ts -OPENCLAW_LIVE_GATEWAY_MODELS="xai/grok-4.5,xai/grok-build-0.1,xai/grok-4.3,xai/grok-4.20-0309-reasoning,xai/grok-4.20-0309-non-reasoning" OPENCLAW_LIVE_GATEWAY_MAX_MODELS=0 OPENCLAW_LIVE_GATEWAY_SMOKE=0 pnpm test:live -- src/gateway/gateway-models.profiles.live.test.ts +OPENCLAW_LIVE_GATEWAY_MODELS="xai/grok-4.6,xai/grok-4.5,xai/grok-build-0.1,xai/grok-4.3,xai/grok-4.20-0309-reasoning,xai/grok-4.20-0309-non-reasoning" OPENCLAW_LIVE_GATEWAY_MAX_MODELS=0 OPENCLAW_LIVE_GATEWAY_SMOKE=0 pnpm test:live -- src/gateway/gateway-models.profiles.live.test.ts OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_TEST_QUIET=1 OPENCLAW_LIVE_IMAGE_GENERATION_PROVIDERS=xai pnpm test:live -- test/image-generation.runtime.live.test.ts ``` diff --git a/docs/tools/code-execution.md b/docs/tools/code-execution.md index 942f3e8ec65f..8cc8d191c5ee 100644 --- a/docs/tools/code-execution.md +++ b/docs/tools/code-execution.md @@ -21,7 +21,7 @@ registered by the bundled `xai` plugin under the `tools` contract. | Tool name | `code_execution` | | Provider plugin | `xai` (bundled, `enabledByDefault: true`) | | Auth | xAI auth profile, `XAI_API_KEY`, or `plugins.entries.xai.config.webSearch.apiKey` | -| Default model | `grok-4.5` | +| Default model | `grok-4.6` | | Default timeout | 30 seconds | | Default `maxTurns` | unset (xAI applies its own internal limit) | @@ -100,7 +100,7 @@ For local execution, use [`exec`](/tools/exec) instead. config: { codeExecution: { enabled: true, // required for a known non-xAI model provider - model: "grok-4.5", // optional explicit model pin + model: "grok-4.6", // optional explicit model pin maxTurns: 2, // optional cap on internal tool turns timeoutSeconds: 30, // request timeout (default: 30) }, diff --git a/docs/tools/web.md b/docs/tools/web.md index 80fd433de04f..0240d32ba811 100644 --- a/docs/tools/web.md +++ b/docs/tools/web.md @@ -532,7 +532,7 @@ every provider. xAI credentials are always required. config: { xSearch: { enabled: true, // required for a known non-xAI model provider - model: "grok-4.5", + model: "grok-4.6", baseUrl: "https://api.x.ai/v1", // optional, overrides webSearch.baseUrl inlineCitations: false, maxTurns: 2, diff --git a/extensions/xai/api.test.ts b/extensions/xai/api.test.ts index 0479273bc4d2..e4e6360d7c1e 100644 --- a/extensions/xai/api.test.ts +++ b/extensions/xai/api.test.ts @@ -58,7 +58,7 @@ describe("xai api helpers", () => { providerId: "xai", ctx: { provider, - modelId: "grok-4.5", + modelId: "grok-4.6", modelRegistry: { find: () => null } as never, providerConfig: { baseUrl: "", models: [] }, }, diff --git a/extensions/xai/code-execution.test.ts b/extensions/xai/code-execution.test.ts index 38436871bddd..15f9035f7e3e 100644 --- a/extensions/xai/code-execution.test.ts +++ b/extensions/xai/code-execution.test.ts @@ -155,7 +155,7 @@ describe("xai code_execution tool", () => { expect(mockFetch).toHaveBeenCalled(); expect(firstFetchUrl(mockFetch)).toContain("api.x.ai/v1/responses"); const body = parseFirstRequestBody(mockFetch); - expect(body.model).toBe("grok-4.5"); + expect(body.model).toBe("grok-4.6"); expect(body.store).toBe(false); expect(body.reasoning).toEqual({ effort: "low" }); expect(body.max_turns).toBe(2); diff --git a/extensions/xai/doctor-contract-api.test.ts b/extensions/xai/doctor-contract-api.test.ts index 50291958f4ef..2d1e29e45373 100644 --- a/extensions/xai/doctor-contract-api.test.ts +++ b/extensions/xai/doctor-contract-api.test.ts @@ -64,8 +64,8 @@ describe("xAI doctor contract", () => { expect(result.changes).toHaveLength(4); expect(result.config).not.toBe(config); expect(result.config.plugins?.entries?.xai?.config).toEqual({ - webSearch: { model: "grok-4.5", timeoutSeconds: 60 }, - xSearch: { model: "grok-4.5", inlineCitations: true }, + webSearch: { model: "grok-4.6", timeoutSeconds: 60 }, + xSearch: { model: "grok-4.6", inlineCitations: true }, codeExecution: { model: "grok-build-0.1", maxTurns: 2 }, }); expect(result.config.tools?.web).toEqual(config.tools?.web); diff --git a/extensions/xai/index.test.ts b/extensions/xai/index.test.ts index 5b8f1433182a..fa8ac711ed21 100644 --- a/extensions/xai/index.test.ts +++ b/extensions/xai/index.test.ts @@ -206,6 +206,7 @@ describe("xai provider plugin", () => { const fetchGuard: LiveModelCatalogFetchGuard = vi.fn(async () => ({ response: Response.json({ data: [ + { id: "grok-4.6", object: "model" }, { id: "grok-4.5", object: "model" }, { id: "grok-4.20-0309-reasoning", object: "model" }, { id: "grok-4.20-0309-non-reasoning", object: "model" }, @@ -222,6 +223,7 @@ describe("xai provider plugin", () => { }); expect(provider.apiKey).toBe("xai-key"); + expect(provider.models.map((model) => model.id)).toContain("grok-4.6"); expect(provider.models.map((model) => model.id)).toContain("grok-4.5"); expect(provider.models.map((model) => model.id)).toContain("grok-4.20-0309-reasoning"); expect(provider.models.map((model) => model.id)).toContain("grok-4.20-0309-non-reasoning"); @@ -802,7 +804,7 @@ describe("xai provider plugin", () => { expect( provider.isModernModelRef?.({ provider: "xai", - modelId: "grok-4.3", + modelId: "grok-4.6", } as never), ).toBe(true); expect( diff --git a/extensions/xai/model-definitions.ts b/extensions/xai/model-definitions.ts index 1a8f4b4ac228..ae7bd2538edc 100644 --- a/extensions/xai/model-definitions.ts +++ b/extensions/xai/model-definitions.ts @@ -4,16 +4,16 @@ import { asOptionalRecord, normalizeOptionalLowercaseString, } from "openclaw/plugin-sdk/string-coerce-runtime"; -import { normalizeXaiModelId } from "./model-id.js"; +import { isXaiFrontierModelId, normalizeXaiModelId } from "./model-id.js"; export const XAI_BASE_URL = "https://api.x.ai/v1"; export const XAI_DEFAULT_IMAGE_MODEL = "grok-imagine-image"; export const XAI_IMAGE_MODELS = ["grok-imagine-image", "grok-imagine-image-quality"] as const; export const XAI_DEFAULT_CONTEXT_WINDOW = 1_000_000; -const XAI_GROK_45_CONTEXT_WINDOW = 500_000; +const XAI_FRONTIER_CONTEXT_WINDOW = 500_000; const XAI_CODE_CONTEXT_WINDOW = 256_000; export const XAI_DEFAULT_MAX_TOKENS = 64_000; -export const XAI_DEFAULT_MODEL_ID = "grok-4.5"; +export const XAI_DEFAULT_MODEL_ID = "grok-4.6"; type XaiCost = ModelDefinitionConfig["cost"]; @@ -54,7 +54,7 @@ const XAI_GROK_43_COST = { cacheWrite: 0, } satisfies XaiCost; -const XAI_GROK_45_COST = { +const XAI_FRONTIER_COST = { input: 2, output: 6, cacheRead: 0.5, @@ -69,12 +69,19 @@ const XAI_GROK_BUILD_COST = { } satisfies XaiCost; const XAI_MODEL_CATALOG_ROWS: readonly XaiCatalogModelRow[] = [ + [ + "grok-4.6", + "Grok 4.6", + true, + ["text", "image"], + { contextWindow: XAI_FRONTIER_CONTEXT_WINDOW, cost: XAI_FRONTIER_COST }, + ], [ "grok-4.5", "Grok 4.5", true, ["text", "image"], - { contextWindow: XAI_GROK_45_CONTEXT_WINDOW, cost: XAI_GROK_45_COST }, + { contextWindow: XAI_FRONTIER_CONTEXT_WINDOW, cost: XAI_FRONTIER_COST }, ], [ "grok-build-0.1", @@ -128,6 +135,7 @@ const XAI_MODEL_CATALOG: readonly XaiCatalogEntry[] = XAI_MODEL_CATALOG_ROWS.map ); const XAI_SELECTABLE_MODEL_IDS = new Set([ + "grok-4.6", "grok-4.5", "grok-build-0.1", "grok-4.3", @@ -254,12 +262,12 @@ export function buildXaiModelDefinition(): ModelDefinitionConfig { return toModelDefinition( XAI_MODEL_CATALOG.find((entry) => entry.id === XAI_DEFAULT_MODEL_ID) ?? { id: XAI_DEFAULT_MODEL_ID, - name: "Grok 4.5", + name: "Grok 4.6", reasoning: true, input: ["text", "image"], - contextWindow: XAI_GROK_45_CONTEXT_WINDOW, + contextWindow: XAI_FRONTIER_CONTEXT_WINDOW, maxTokens: XAI_DEFAULT_MAX_TOKENS, - cost: XAI_GROK_45_COST, + cost: XAI_FRONTIER_COST, }, ); } @@ -310,7 +318,7 @@ export function resolveXaiCatalogEntry(modelId: string) { }); } if ( - lower.startsWith("grok-4.5") || + isXaiFrontierModelId(lower) || lower.startsWith("grok-4.3") || lower.startsWith("grok-4.20") || lower.startsWith("grok-4-1") || @@ -321,17 +329,17 @@ export function resolveXaiCatalogEntry(modelId: string) { name: trimmed, reasoning: !lower.includes("non-reasoning"), input: ["text", "image"], - contextWindow: lower.startsWith("grok-4.5") - ? XAI_GROK_45_CONTEXT_WINDOW + contextWindow: isXaiFrontierModelId(lower) + ? XAI_FRONTIER_CONTEXT_WINDOW : XAI_DEFAULT_CONTEXT_WINDOW, maxTokens: - lower.startsWith("grok-4.5") || lower.startsWith("grok-4.3") + isXaiFrontierModelId(lower) || lower.startsWith("grok-4.3") ? XAI_DEFAULT_MAX_TOKENS : lower.startsWith("grok-4.20") ? 30_000 : XAI_DEFAULT_MAX_TOKENS, - cost: lower.startsWith("grok-4.5") - ? XAI_GROK_45_COST + cost: isXaiFrontierModelId(lower) + ? XAI_FRONTIER_COST : lower.startsWith("grok-4.3") ? XAI_GROK_43_COST : lower.startsWith("grok-4.20") diff --git a/extensions/xai/model-id.test.ts b/extensions/xai/model-id.test.ts index a6eb07ab38ec..ea7f8510c319 100644 --- a/extensions/xai/model-id.test.ts +++ b/extensions/xai/model-id.test.ts @@ -1,6 +1,7 @@ // Xai tests cover model id plugin behavior. import { describe, expect, it } from "vitest"; import { normalizeXaiModelId } from "./api.js"; +import { isXaiFrontierModelId } from "./model-id.js"; describe("normalizeXaiModelId", () => { it("normalizes family-specific aliases but preserves the global alias", () => { @@ -33,10 +34,18 @@ describe("normalizeXaiModelId", () => { }); it("leaves current xai model ids unchanged", () => { + expect(normalizeXaiModelId("grok-4.6")).toBe("grok-4.6"); expect(normalizeXaiModelId("grok-4.20-beta-latest-reasoning")).toBe( "grok-4.20-beta-latest-reasoning", ); expect(normalizeXaiModelId("grok-4.20-0309-reasoning")).toBe("grok-4.20-0309-reasoning"); expect(normalizeXaiModelId("grok-4")).toBe("grok-4"); }); + + it("identifies current frontier model ids and aliases", () => { + expect(isXaiFrontierModelId("grok-4.6")).toBe(true); + expect(isXaiFrontierModelId("grok-4.5-latest")).toBe(true); + expect(isXaiFrontierModelId("grok-build-latest")).toBe(true); + expect(isXaiFrontierModelId("grok-4.3")).toBe(false); + }); }); diff --git a/extensions/xai/model-id.ts b/extensions/xai/model-id.ts index b6c1813de1d7..74aa2d9c40e7 100644 --- a/extensions/xai/model-id.ts +++ b/extensions/xai/model-id.ts @@ -1,6 +1,13 @@ // Xai plugin module implements model id behavior. export const XAI_OAUTH_AUTO_MODEL_ID = "auto"; +export function isXaiFrontierModelId(id: string): boolean { + const normalized = normalizeXaiModelId(id.trim().toLowerCase()); + return ["grok-4.6", "grok-4.5"].some( + (prefix) => normalized === prefix || normalized.startsWith(`${prefix}-`), + ); +} + export function normalizeXaiModelId(id: string): string { if (id === "grok-4.3-latest") { return "grok-4.3"; diff --git a/extensions/xai/onboard.test.ts b/extensions/xai/onboard.test.ts index 083bdeefd023..6e8780b69577 100644 --- a/extensions/xai/onboard.test.ts +++ b/extensions/xai/onboard.test.ts @@ -22,7 +22,7 @@ describe("xai onboard", () => { const cfg = applyXaiConfig({}); expect(cfg.models?.providers?.xai?.baseUrl).toBe("https://api.x.ai/v1"); expect(cfg.models?.providers?.xai?.api).toBe("openai-responses"); - expect(XAI_DEFAULT_MODEL_REF).toBe("xai/grok-4.5"); + expect(XAI_DEFAULT_MODEL_REF).toBe("xai/grok-4.6"); expect(resolveAgentModelPrimaryValue(cfg.agents?.defaults?.model)).toBe(XAI_DEFAULT_MODEL_REF); }); @@ -77,6 +77,7 @@ describe("xai onboard", () => { "grok-3", "grok-code-fast-1", "grok-4.20-beta-latest-reasoning", + "grok-4.6", "grok-4.5", "grok-build-0.1", "grok-4.3", @@ -96,6 +97,7 @@ describe("xai onboard", () => { expect(cfg.models?.providers?.xai?.baseUrl).toBe("https://api.x.ai/v1"); expect(cfg.models?.providers?.xai?.api).toBe("openai-responses"); expect(cfg.models?.providers?.xai?.models.map((m) => m.id)).toEqual([ + "grok-4.6", "grok-4.5", "grok-build-0.1", "grok-4.3", diff --git a/extensions/xai/provider-models.ts b/extensions/xai/provider-models.ts index a2f87b9bc57b..22874778d474 100644 --- a/extensions/xai/provider-models.ts +++ b/extensions/xai/provider-models.ts @@ -12,7 +12,13 @@ import { resolveXaiCatalogEntry, XAI_BASE_URL } from "./model-definitions.js"; import { normalizeXaiModelId, XAI_OAUTH_AUTO_MODEL_ID } from "./model-id.js"; import { applyXaiRuntimeModelCompat } from "./runtime-model-compat.js"; -const XAI_MODERN_MODEL_PREFIXES = ["grok-4.5", "grok-build-0.1", "grok-4.3", "grok-4.20"] as const; +const XAI_MODERN_MODEL_PREFIXES = [ + "grok-4.6", + "grok-4.5", + "grok-build-0.1", + "grok-4.3", + "grok-4.20", +] as const; export function isModernXaiModel(modelId: string): boolean { const normalized = normalizeXaiModelId(modelId.trim()); diff --git a/extensions/xai/provider-policy-api.test.ts b/extensions/xai/provider-policy-api.test.ts index c1921f68e765..aeede9d15fc5 100644 --- a/extensions/xai/provider-policy-api.test.ts +++ b/extensions/xai/provider-policy-api.test.ts @@ -27,9 +27,11 @@ describe("xai provider thinking policy", () => { }); it.each([ + ["xai", "grok-4.6"], ["xai", "grok-4.5"], ["xai", "grok-4.5-latest"], ["xai", "grok-build-latest"], + ["x-ai", "grok-4.6"], ["x-ai", "grok-4.5"], ["x-ai", "grok-4.5-latest"], ["x-ai", "grok-build-latest"], diff --git a/extensions/xai/provider-policy-api.ts b/extensions/xai/provider-policy-api.ts index f81104eba1a5..50721fa93956 100644 --- a/extensions/xai/provider-policy-api.ts +++ b/extensions/xai/provider-policy-api.ts @@ -4,7 +4,7 @@ import type { ProviderThinkingProfile, } from "openclaw/plugin-sdk/plugin-entry"; import { resolveXaiCatalogEntry } from "./model-definitions.js"; -import { normalizeXaiModelId } from "./model-id.js"; +import { isXaiFrontierModelId, normalizeXaiModelId } from "./model-id.js"; import { isXaiProviderId } from "./provider-id.js"; export function resolveThinkingProfile( @@ -15,8 +15,7 @@ export function resolveThinkingProfile( return { levels: [{ id: "off" }], defaultLevel: "off" }; } const modelId = normalizeXaiModelId(ctx.modelId.trim().toLowerCase()); - const isGrok45 = modelId === "grok-4.5" || modelId.startsWith("grok-4.5-"); - if (isGrok45) { + if (isXaiFrontierModelId(modelId)) { return { levels: [{ id: "low" }, { id: "medium" }, { id: "high" }], defaultLevel: "high", diff --git a/extensions/xai/runtime-model-compat.test.ts b/extensions/xai/runtime-model-compat.test.ts index 5b02026e12c0..dffbcc432296 100644 --- a/extensions/xai/runtime-model-compat.test.ts +++ b/extensions/xai/runtime-model-compat.test.ts @@ -24,26 +24,29 @@ describe("xai runtime model compat", () => { }); }); - it("maps Grok 4.5 thinking levels to its supported reasoning efforts", () => { - const model = applyXaiRuntimeModelCompat({ - id: "grok-4.5", - provider: "xai", - reasoning: true, - }); + it.each(["grok-4.6", "grok-4.5"])( + "maps %s thinking levels to its supported reasoning efforts", + (modelId) => { + const model = applyXaiRuntimeModelCompat({ + id: modelId, + provider: "xai", + reasoning: true, + }); - expect(model.compat).toMatchObject({ - supportsReasoningEffort: true, - supportedReasoningEfforts: ["low", "medium", "high"], - }); - expect(model.thinkingLevelMap).toEqual({ - off: null, - minimal: "low", - low: "low", - medium: "medium", - high: "high", - xhigh: "high", - }); - }); + expect(model.compat).toMatchObject({ + supportsReasoningEffort: true, + supportedReasoningEfforts: ["low", "medium", "high"], + }); + expect(model.thinkingLevelMap).toEqual({ + off: null, + minimal: "low", + low: "low", + medium: "medium", + high: "high", + xhigh: "high", + }); + }, + ); it("suppresses reasoning efforts for non-reasoning models", () => { const model = applyXaiRuntimeModelCompat({ diff --git a/extensions/xai/runtime-model-compat.ts b/extensions/xai/runtime-model-compat.ts index e3c8abd2a65c..0600dd85ffb1 100644 --- a/extensions/xai/runtime-model-compat.ts +++ b/extensions/xai/runtime-model-compat.ts @@ -2,6 +2,7 @@ // Reasoning effort is configurable only for current flagship Grok models; encrypted reasoning // include/replay is handled separately in stream.ts for every reasoning-capable xAI model. import { applyXaiModelCompat } from "./model-compat.js"; +import { isXaiFrontierModelId } from "./model-id.js"; type XaiRuntimeModelCompat = { compat?: unknown; @@ -43,7 +44,7 @@ function normalizeXaiCompatModelId(id: unknown): string { function supportsConfigurableXaiReasoningEffort(model: XaiRuntimeModelCompat): boolean { const id = normalizeXaiCompatModelId(model.id); - const isConfigurableModel = isGrok43Model(id) || id === "grok-4.5" || id.startsWith("grok-4.5-"); + const isConfigurableModel = isGrok43Model(id) || isXaiFrontierModelId(id); return model.reasoning === true && isConfigurableModel; } diff --git a/extensions/xai/stream.test.ts b/extensions/xai/stream.test.ts index 2439cd3e5729..4769a29627e8 100644 --- a/extensions/xai/stream.test.ts +++ b/extensions/xai/stream.test.ts @@ -108,7 +108,7 @@ function runXaiToolPayloadWrapper(params: { async function captureXaiResponsesPayloadWithThinking( reasoning: ModelThinkingLevel = "low", - modelId = "grok-4.5", + modelId = "grok-4.6", ): Promise> { const model = applyXaiRuntimeModelCompat({ api: "openai-responses", @@ -545,7 +545,7 @@ describe("xai stream wrappers", () => { expect(payload.include).toEqual(["reasoning.encrypted_content"]); }, 10_000); - it("clamps unsupported Grok 4.5 off reasoning to low", async () => { + it("clamps unsupported Grok 4.6 off reasoning to low", async () => { const payload = await captureXaiResponsesPayloadWithThinking("off"); expect(payload.reasoning).toEqual({ effort: "low", summary: "auto" }); diff --git a/extensions/xai/web-search.test.ts b/extensions/xai/web-search.test.ts index 9eab88e938e7..80a867aaa0ca 100644 --- a/extensions/xai/web-search.test.ts +++ b/extensions/xai/web-search.test.ts @@ -244,7 +244,7 @@ describe("xai web search config resolution", () => { it("prefers configured api keys and resolves grok scoped defaults", () => { expect(resolveXaiWebSearchCredential({ grok: { apiKey: "xai-secret" } })).toBe("xai-secret"); - expect(resolveXaiWebSearchModel()).toBe("grok-4.5"); + expect(resolveXaiWebSearchModel()).toBe("grok-4.6"); expect(resolveXaiInlineCitations()).toBe(false); }); @@ -644,8 +644,8 @@ describe("xai web search config resolution", () => { }); it("uses default model when not specified", () => { - expect(resolveXaiWebSearchModel({})).toBe("grok-4.5"); - expect(resolveXaiWebSearchModel(undefined)).toBe("grok-4.5"); + expect(resolveXaiWebSearchModel({})).toBe("grok-4.6"); + expect(resolveXaiWebSearchModel(undefined)).toBe("grok-4.6"); }); it("uses a Grok-specific 60s default timeout while preserving overrides", () => { @@ -676,7 +676,7 @@ describe("xai web search config resolution", () => { expect(firstFetchUrl(mockFetch)).toBe("https://api.x.ai/proxy/v1/responses"); expect(firstFetchBody(mockFetch)).toMatchObject({ - model: "grok-4.5", + model: "grok-4.6", store: false, reasoning: { effort: "low" }, tools: [{ type: "web_search" }], @@ -924,6 +924,7 @@ describe("xai web search response parsing", () => { describe("xai provider models", () => { it("publishes only current selectable chat models newest first", () => { expect(buildXaiCatalogModels().map((model) => model.id)).toEqual([ + "grok-4.6", "grok-4.5", "grok-build-0.1", "grok-4.3", @@ -932,6 +933,17 @@ describe("xai provider models", () => { ]); }); + it("publishes Grok 4.6 with its current metadata", () => { + expectCatalogEntry("grok-4.6", { + id: "grok-4.6", + reasoning: true, + input: ["text", "image"], + contextWindow: 500_000, + maxTokens: 64_000, + cost: { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 }, + }); + }); + it("publishes Grok 4.5 with its current metadata", () => { expectCatalogEntry("grok-4.5", { id: "grok-4.5", @@ -1051,6 +1063,7 @@ describe("xai provider models", () => { }); it("marks current Grok families as modern while excluding multi-agent ids", () => { + expect(isModernXaiModel("grok-4.6")).toBe(true); expect(isModernXaiModel("grok-4.5")).toBe(true); expect(isModernXaiModel("grok-4.3")).toBe(true); expect(isModernXaiModel("grok-build-0.1")).toBe(true); diff --git a/extensions/xai/x-search.live.test.ts b/extensions/xai/x-search.live.test.ts index 3bbd7a399353..bce78dc0581b 100644 --- a/extensions/xai/x-search.live.test.ts +++ b/extensions/xai/x-search.live.test.ts @@ -67,7 +67,7 @@ describeLive("xai x_search live", () => { expect(details.error, details.message).toBeUndefined(); expect(details.provider).toBe("xai"); - expect(details.model).toBe("grok-4.5"); + expect(details.model).toBe("grok-4.6"); expect(details.content?.trim().length ?? 0).toBeGreaterThan(0); const citationCount = diff --git a/extensions/xai/x-search.test.ts b/extensions/xai/x-search.test.ts index a54b44efbaa0..e6450d6cf2ea 100644 --- a/extensions/xai/x-search.test.ts +++ b/extensions/xai/x-search.test.ts @@ -291,7 +291,7 @@ describe("xai x_search tool", () => { expect(mockFetch).toHaveBeenCalled(); expect(firstFetchUrl(mockFetch)).toContain("api.x.ai/v1/responses"); const body = parseFirstRequestBody(mockFetch); - expect(body.model).toBe("grok-4.5"); + expect(body.model).toBe("grok-4.6"); expect(body.store).toBe(false); expect(body.reasoning).toEqual({ effort: "low" }); expect(body.max_turns).toBe(2); diff --git a/extensions/xai/xai.live.test.ts b/extensions/xai/xai.live.test.ts index 90f6da06f656..934a8576dc80 100644 --- a/extensions/xai/xai.live.test.ts +++ b/extensions/xai/xai.live.test.ts @@ -222,7 +222,7 @@ describeLive("xai plugin live", () => { usedCodeExecution?: boolean; }; - expect(details.model).toBe("grok-4.5"); + expect(details.model).toBe("grok-4.6"); expect(details.usedCodeExecution).toBe(true); expect(details.content).toContain("42"); }); @@ -257,7 +257,7 @@ describeLive("xai plugin live", () => { usedCodeExecution?: boolean; }; - expect(details.model).toBe("grok-4.5"); + expect(details.model).toBe("grok-4.6"); expect(details.usedCodeExecution).toBe(true); expect(details.content).toContain("5050"); }); diff --git a/src/agents/live-model-filter.ts b/src/agents/live-model-filter.ts index 9b1a03c54456..1769b1816f50 100644 --- a/src/agents/live-model-filter.ts +++ b/src/agents/live-model-filter.ts @@ -34,7 +34,7 @@ const HIGH_SIGNAL_LIVE_MODEL_PRIORITY = [ "openrouter/minimax/minimax-m2.7", "opencode-go/glm-5", "openrouter/ai21/jamba-large-1.7", - "xai/grok-4.5", + "xai/grok-4.6", "xai/grok-4.20-0309-reasoning", "zai/glm-5.1", "fireworks/accounts/fireworks/models/glm-5p1", diff --git a/src/agents/model-compat.test.ts b/src/agents/model-compat.test.ts index d195074f9358..b90cab1b6ec0 100644 --- a/src/agents/model-compat.test.ts +++ b/src/agents/model-compat.test.ts @@ -646,7 +646,8 @@ describe("isHighSignalLiveModelRef", () => { expect(isHighSignalLiveModelRef({ provider: "xai", id: "grok-4.20-0309-reasoning" })).toBe( true, ); - expect(isHighSignalLiveModelRef({ provider: "xai", id: "grok-4.5" })).toBe(true); + expect(isHighSignalLiveModelRef({ provider: "xai", id: "grok-4.6" })).toBe(true); + expect(isHighSignalLiveModelRef({ provider: "xai", id: "grok-4.5" })).toBe(false); expect(isHighSignalLiveModelRef({ provider: "xai", id: "grok-4.3" })).toBe(false); expect(isHighSignalLiveModelRef({ provider: "xai", id: "grok-3" })).toBe(false); expect(isHighSignalLiveModelRef({ provider: "xai", id: "grok-4-1-fast-non-reasoning" })).toBe( @@ -707,7 +708,7 @@ describe("isPrioritizedHighSignalLiveModelRef", () => { { provider: "openrouter", id: "minimax/minimax-m2.7" }, { provider: "opencode-go", id: "glm-5" }, { provider: "openrouter", id: "ai21/jamba-large-1.7" }, - { provider: "xai", id: "grok-4.5" }, + { provider: "xai", id: "grok-4.6" }, { provider: "xai", id: "grok-4.20-0309-reasoning" }, { provider: "zai", id: "glm-5.1" }, { provider: "fireworks", id: "accounts/fireworks/models/glm-5p1" }, diff --git a/src/agents/xai.live.test.ts b/src/agents/xai.live.test.ts index 4f78f56eeb58..735a7ef2901e 100644 --- a/src/agents/xai.live.test.ts +++ b/src/agents/xai.live.test.ts @@ -43,23 +43,23 @@ function getToolFunction(tool: Record): Record return undefined; } -function resolveLiveXaiModel(modelId: "grok-4.3" | "grok-4.5") { - const isGrok45 = modelId === "grok-4.5"; +function resolveLiveXaiModel(modelId: "grok-4.3" | "grok-4.5" | "grok-4.6") { + const isFrontier = modelId === "grok-4.5" || modelId === "grok-4.6"; return { id: modelId, - name: isGrok45 ? "Grok 4.5" : "Grok 4.3", + name: isFrontier ? `Grok ${modelId.slice("grok-".length)}` : "Grok 4.3", api: "openai-responses", provider: "xai", baseUrl: "https://api.x.ai/v1", reasoning: true, input: ["text", "image"], - cost: isGrok45 + cost: isFrontier ? { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 } : { input: 1.25, output: 2.5, cacheRead: 0.2, cacheWrite: 0 }, - contextWindow: isGrok45 ? 500_000 : 1_000_000, + contextWindow: isFrontier ? 500_000 : 1_000_000, maxTokens: 64_000, thinkingLevelMap: { - off: isGrok45 ? null : "none", + off: isFrontier ? null : "none", minimal: "low", low: "low", medium: "medium", @@ -123,7 +123,7 @@ async function collectDoneMessage( } describeLive("xai live", () => { - for (const modelId of ["grok-4.3", "grok-4.5"] as const) { + for (const modelId of ["grok-4.3", "grok-4.5", "grok-4.6"] as const) { it( `returns assistant text for ${modelId}`, async () => { @@ -150,7 +150,7 @@ describeLive("xai live", () => { ); } - for (const modelId of ["grok-4.3", "grok-4.5"] as const) { + for (const modelId of ["grok-4.3", "grok-4.5", "grok-4.6"] as const) { it(`sends wrapped ${modelId} tool payloads live`, async () => { await runXaiLiveCase("tool-call", async () => { const model = requireLiveValue(resolveLiveXaiModel(modelId), "xAI model");