Skip to content

feat: surface Grok's reasoning effort in the composer #2538

Description

@Lite-G

Prior search

  • I searched existing issues and discussions, and this isn't already proposed.

What's the problem

Grok 4.5 is my daily driver in Paseo. Grok Build supports three reasoning efforts (High / Medium / Low) and reports them over ACP, but when I pick Grok the composer shows only the model chip — no reasoning-effort control. Claude, Codex and Cursor all get one. To change effort I have to leave Paseo and run the Grok CLI in a terminal, which defeats the point of driving the agent from my phone.

The cause is a shape mismatch, not a missing capability. Paseo derives thinking options from the standard ACP config option with category: "thought_level" (deriveSelectorOptions in packages/server/src/server/agent/providers/acp-agent.ts). Grok reaches Paseo through the generic ACP drop-in (acp-provider-catalog.tsextends: "acp"GenericACPAgentClient), which by design applies no vendor-specific transforms — so nothing reads Grok's data.

Probing grok 0.2.112 directly (grok agent stdio, initialize + session/new):

  • ✅ standard models → this is why the model picker works
  • ❌ no modes field at all
  • ❌ no top-level configOptions field at all
  • the reasoning efforts live in the model's _meta:
"availableModels": [{
  "modelId": "grok-4.5",
  "name": "Grok 4.5",
  "_meta": {
    "supportsReasoningEffort": true,
    "reasoningEffort": "high",
    "reasoningEfforts": [
      { "id": "high",   "label": "High Effort",   "description": "Highest implementation quality with extensive reasoning", "default": true  },
      { "id": "medium", "label": "Medium Effort", "description": "Balanced effort with standard implementation and testing", "default": false },
      { "id": "low",    "label": "Low Effort",    "description": "Quick, fast implementations",                             "default": false }
    ]
  }
}]

For contrast, cursor-agent 2026.07.23 returns both standard modes.availableModes and standard configOptions at the top level, which is exactly why Cursor needs no special-casing today.

What would solve it

UI: nothing new. The existing Brain control already renders whenever the selected model carries thinkingOptionsAgentControls gates on thinkingOptions.length > 1, DraftAgentControls on > 0. Feed it data and the control appears on every platform for free.

Server: one small transform for provider id grok. Map the model's _meta.reasoningEffortsAgentModelDefinition.thinkingOptions, and _meta.reasoningEffortdefaultThinkingOptionId, via a modelDefinitionTransformer. Selecting an option then needs to reach the CLI as --reasoning-effort <id>.

Scope is server-only, plus a unit test on the transform (pure function, ACP _meta fixture in → thinkingOptions out).

One gotcha for whoever implements it. Grok also ships a second copy of the same data under _meta["x.ai/sessionConfig"].options, but tagged "category": "mode":

{ "id": "high", "category": "mode", "label": "High Effort", "selected": true }

Mapping that blob straight onto the standard configOptions shape would make Paseo render High/Medium/Low Effort as the mode control (deriveModesFromACP reads category: "mode"), which is semantically wrong — these are efforts, not permission modes. Read _meta.reasoningEfforts, or retag to thought_level.

Alternatives you considered

  • Wait for the generic ACP path to pick it up. It can't — the standard fields Paseo reads are empty in Grok's response. This needs a vendor transform by definition.
  • Add a thought_level option to a custom provider config by hand. Custom provider config has no way to declare thinking options for an ACP-discovered model (that limitation is feat: allow defining thinking options for models added via the "Add model" UI #2251), and even if it did, the values would be static rather than read from the agent.
  • Encode effort into separate model entries (grok-4.5-high, grok-4.5-low, …) the way Cursor bakes [effort=high] into model IDs. Rejected: it triples the model list, doesn't match what Grok actually reports, and loses the labels/descriptions Grok already provides.
  • Just use the terminal. Works, but it's the thing Paseo exists to avoid.

Relationship to existing issues

Flagging this so it doesn't read as a duplicate:

So: the behavior is currently tracked nowhere on its own. Opening this as a narrow issue so it can land as one focused change on its own merits, independent of how #2385 is resolved. Happy to close it as a duplicate if you'd rather keep it under #2402 — and happy to do the implementation if the shape above looks right.

Why this doesn't bloat the product

It adds no new UI surface, no new concept, and no new settings — it makes an already-shipped control (the one Claude/Codex/Cursor use) light up for one more provider by reading data the agent already sends. Net effect on the maintenance surface is one pure mapping function and its test.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions