diff --git a/packages/ai/test/xai-oauth-effort-strip.test.ts b/packages/ai/test/xai-oauth-effort-strip.test.ts index 6b7f99d89..d6d00d367 100644 --- a/packages/ai/test/xai-oauth-effort-strip.test.ts +++ b/packages/ai/test/xai-oauth-effort-strip.test.ts @@ -31,6 +31,19 @@ describe("effort-dial-less reasoner encoding (regression)", () => { expect(getSupportedEfforts(grok43).length).toBeGreaterThan(0); }); + test("xai-oauth/grok-4.6 keeps its effort dial including xhigh", () => { + const grok46 = getBundledModel("xai-oauth", "grok-4.6"); + if (!grok46) throw new Error("xai-oauth/grok-4.6 must be in bundled models.json"); + expect(grok46.thinking).toBeDefined(); + expect(getSupportedEfforts(grok46)).toEqual([ + Effort.Minimal, + Effort.Low, + Effort.Medium, + Effort.High, + Effort.XHigh, + ]); + }); + test("xai-oauth/grok-4.20-0309-reasoning reasons but carries no thinking config", () => { const grokR = getBundledModel("xai-oauth", "grok-4.20-0309-reasoning"); if (!grokR) throw new Error("xai-oauth/grok-4.20-0309-reasoning must be in bundled models.json"); @@ -175,6 +188,16 @@ describe("xAI OAuth Responses reasoning payload (regression)", () => { encrypted_content: "enc_next_turn", }); }); + + test("xai-oauth/grok-4.6 sends reasoning.effort xhigh and omits max", () => { + const grok46 = getBundledModel<"openai-responses">("xai-oauth", "grok-4.6"); + if (!grok46) throw new Error("xai-oauth/grok-4.6 must be in bundled models.json"); + + const { params } = buildParams(grok46, singleUserContext, { reasoning: Effort.XHigh }, undefined); + + expect(params.reasoning).toEqual({ effort: "xhigh" }); + expect(getSupportedEfforts(grok46)).not.toContain(Effort.Max); + }); }); function followUpContextWithEncryptedReasoning(model: Model<"openai-responses">): Context { diff --git a/packages/catalog/CHANGELOG.md b/packages/catalog/CHANGELOG.md index 21ffab449..d2a7a51c6 100644 --- a/packages/catalog/CHANGELOG.md +++ b/packages/catalog/CHANGELOG.md @@ -15,6 +15,7 @@ ### Fixed - Raised the GPT-5.6 Sol/Terra/Luna context window on the Codex transport (openai-codex) from 372K to 1M tokens: OpenAI enabled the 1M window for subscription Codex on 2026-08-16, but the Codex model registry still reports the stale 272,000, so discovery now floors these SKUs at 1,000,000 instead of trusting the reported value ([openai/codex#38917](https://github.com/openai/codex/issues/38917)). +- Fixed SuperGrok (`xai-oauth`) Grok 4.6 hiding the thinking-level picker: the Responses effort-capable allowlist now includes `grok-4.6`, so `/model` can select the documented `low`/`medium`/`high`/`xhigh` ladder (`max` is rejected by api.x.ai). ## [17.3.5] - 2026-08-16 @@ -46,6 +47,9 @@ - Fixed raw `COPILOT_GITHUB_TOKEN` credentials skipping plan-specific endpoint discovery, which routed GitHub Copilot Business model requests to the personal endpoint and returned HTTP 403. The GitHub Copilot model cache is now scoped per credential, so switching the token no longer serves another account's stale endpoint for the cache TTL ([#8507](https://github.com/can1357/oh-my-pi/issues/8507)). - Fixed the OpenRouter `deepseek/deepseek-v4-pro-0813` route silently clamping the reasoning effort to `high`: the dated SKU advertises (and accepts) the wire-exact `low`/`high`/`max` ladder, so its effort override no longer collapses to `high`-only. The undated `deepseek/deepseek-v4-pro` OpenRouter route stays `high`-only. ([#8517](https://github.com/can1357/oh-my-pi/issues/8517)) +- Fixed SuperGrok (`xai-oauth`) Grok 4.6 hiding the thinking-level picker: the Responses effort-capable allowlist now includes `grok-4.6`, so `/model` can select the documented `low`/`medium`/`high`/`xhigh` ladder (`max` is rejected by api.x.ai). + +- Fixed `streamMarkupHealingPattern` gating DeepSeek DSML healing on a provider-id allowlist, which left DeepSeek models behind user-configured proxies (LiteLLM, private gateways) with no tool-call grammar. Whether the envelope leaks is decided by the serving stack behind the host, not the provider id, so any DeepSeek model on a non-official-OpenAI endpoint now selects `"dsml"`. ## [17.3.2] - 2026-08-13 ### Added diff --git a/packages/catalog/src/compat/openai.ts b/packages/catalog/src/compat/openai.ts index 15f45f9eb..9e8e9bda8 100644 --- a/packages/catalog/src/compat/openai.ts +++ b/packages/catalog/src/compat/openai.ts @@ -821,6 +821,18 @@ export function buildOpenAIResponsesCompat(spec: OpenAIResponsesSpecLike): Resol if (spec.compat?.omitReasoningEffort === undefined && !compat.supportsReasoningEffort) { compat.omitReasoningEffort = true; } + // xai-oauth cache/discovery rows written before a SKU joined the + // effort-capable allowlist still carry omitReasoningEffort: true. The + // allowlist is the live wire contract; do not let that stale flag hide + // the picker or strip reasoning.effort. + if ( + spec.provider === "xai-oauth" && + isGrokReasoningEffortCapable(id) && + spec.compat?.supportsReasoningEffort !== false + ) { + compat.supportsReasoningEffort = true; + compat.omitReasoningEffort = false; + } return compat; } diff --git a/packages/catalog/src/identity/family.ts b/packages/catalog/src/identity/family.ts index 6bb563ca5..729ed1bc6 100644 --- a/packages/catalog/src/identity/family.ts +++ b/packages/catalog/src/identity/family.ts @@ -140,7 +140,8 @@ const GROK_EFFORT_CAPABLE_PREFIXES = [ /** * Grok SKUs that expose the wire `reasoning.effort` dial. Other Grok reasoners * (e.g. `grok-build`, `grok-4.20-0309-reasoning`) think natively but reject the - * param, so callers must omit reasoning effort for them. + * param, so callers must omit reasoning effort for them. `grok-4.6` accepts + * `low`/`medium`/`high`/`xhigh` and 400s on `max`. */ export const isGrokReasoningEffortCapable = memo((modelId: string): boolean => { const bare = bareModelId(modelId).trim().toLowerCase(); diff --git a/packages/catalog/test/build.test.ts b/packages/catalog/test/build.test.ts index db22929aa..d91a31269 100644 --- a/packages/catalog/test/build.test.ts +++ b/packages/catalog/test/build.test.ts @@ -292,6 +292,25 @@ describe("xAI Responses reasoning-effort suppression", () => { expect(buildModel(grokResponsesSpec("grok-code-fast-1", "xai")).thinking).toBeUndefined(); }); + it("exposes the grok-4.6 low..xhigh ladder and rejects max", () => { + const model = buildModel(grokResponsesSpec("grok-4.6")); + expect(model.compat.supportsReasoningEffort).toBe(true); + expect(model.compat.omitReasoningEffort).toBe(false); + expect(model.thinking?.efforts).toEqual([Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh]); + expect(model.thinking?.efforts).not.toContain(Effort.Max); + }); + + it("lets the grok-4.6 allowlist beat a stale cached omitReasoningEffort flag", () => { + const model = buildModel({ + ...grokResponsesSpec("grok-4.6"), + compat: { omitReasoningEffort: true }, + }); + expect(model.compat.supportsReasoningEffort).toBe(true); + expect(model.compat.omitReasoningEffort).toBe(false); + expect(model.thinking?.efforts).toContain(Effort.XHigh); + expect(model.thinking?.efforts).not.toContain(Effort.Max); + }); + it("lets an explicit compat.supportsReasoningEffort override the allowlist default", () => { const compat = buildOpenAIResponsesCompat({ ...grokResponsesSpec("grok-build"), diff --git a/packages/catalog/test/identity-family.test.ts b/packages/catalog/test/identity-family.test.ts index 101ffc77f..4de00f1c5 100644 --- a/packages/catalog/test/identity-family.test.ts +++ b/packages/catalog/test/identity-family.test.ts @@ -364,6 +364,7 @@ describe("isGrokReasoningEffortCapable", () => { expect(isGrokReasoningEffortCapable("xai-oauth/grok-4.3")).toBe(true); expect(isGrokReasoningEffortCapable("xai-oauth/grok-4.5")).toBe(true); expect(isGrokReasoningEffortCapable("xai-oauth/grok-4.6")).toBe(true); + expect(isGrokReasoningEffortCapable("grok-4.6")).toBe(true); expect(isGrokReasoningEffortCapable("openrouter/xai/grok-3-mini")).toBe(true); });