Problem
Grok Build CLI accepts a reasoning-effort flag, but PortOS never emits one for it. A user can save an effort level against a grok provider (a CoS task pin, a pipeline stage, a scheduled task) and it is silently dropped at spawn time — and the effort select is hidden in the picker, so there is no UI that reveals the level is inert.
The cause is effortLevelsForProvider (server/lib/providerModels.js:356), which has ladder entries for opencode-local, codex, antigravity, cursor and claude and returns null for everything else. resolveCliEffort short-circuits on a null ladder, so buildEffortArgs returns [] and no flag is injected. The client mirror (client/src/utils/providers.js:566) has the same gap, which is why the select disappears.
Surfaced while making the pr-reviewer pipeline stages provider-agnostic (#5830): a grok-only install can now configure every stage's provider and model, but its thinking effort is inert.
Decision (already made — do not re-litigate)
Add a grok ladder. The values are not a guess — the CLI itself enumerates them:
$ grok --reasoning-effort bogus -p hi
--effort/--reasoning-effort: unknown effort level 'bogus'; use one of: xhigh, high, medium, low
So: GROK_EFFORT_LEVELS = ['low', 'medium', 'high', 'xhigh'].
No argv change is needed. grok --help documents --reasoning-effort <EFFORT> [aliases: --effort], and buildEffortArgs already emits ['--effort', level] for every non-codex/non-cursor vendor. Adding the ladder is what makes that path fire.
Detect grok inline, do not import grok.js. server/lib/grok.js already imports providerModels.js (line 28), so importing back would create a cycle. Mirror the existing isCodexProvider shape (providerModels.js:216) instead:
export function isGrokProvider(provider) {
const id = String(provider?.id || '').toLowerCase();
return id === 'grok-cli' || id === 'grok-tui' || commandBasename(provider?.command) === 'grok';
}
(GROK_CLI_ID / GROK_TUI_ID in grok.js are grok-cli / grok-tui; grok alone is the API provider id, which has no CLI effort flag — leave it out.)
Scope
-
server/lib/providerModels.js
- Add
GROK_EFFORT_LEVELS = Object.freeze(['low', 'medium', 'high', 'xhigh']) beside the other ladders (~line 90-106).
- Add
isGrokProvider as above.
- Add the
isGrokProvider(provider) arm to effortLevelsForProvider (~line 356).
ALL_EFFORT_LEVELS (~line 111) already contains every one of these values via the claude ladder — confirm, and add the spread only if that stops being true.
-
server/lib/providerModels.js — hasEffortFlag (~line 417)
Teach it grok's long form. It currently recognizes --effort / --effort= / model_reasoning_effort=, so a user who bakes --reasoning-effort high into provider.args would get a second injected --effort <level> appended. Grok's clap parser accepts the duplicate without error and last-wins, so the user's explicit pin is silently overridden — the opposite of the documented "a baked pin wins and the runner-injected effort is suppressed" contract in that function's own doc comment. Match --reasoning-effort and --reasoning-effort= alongside the existing forms.
-
client/src/utils/providers.js — mirror both the ladder constant and the effortLevelsForProvider arm (~line 355-367 and ~566). Keep the existing "sanitized inventories omit command" fallback in mind: gate on the provider id as well as the command basename, exactly as the server predicate above does.
-
Tests
server/lib/providerModels.test.js: effortLevelsForProvider returns the grok ladder for a grok-cli provider, for a path-configured command (/Users/x/.grok/bin/grok), and for grok-tui; returns null for the grok API provider. resolveCliEffort clamps an out-of-range level down the ladder (max and ultra -> xhigh, minimal -> low), matching the clamp contract already tested for agy.
buildEffortArgs emits ['--effort', <level>] for a grok provider, and emits nothing when the argv already carries --reasoning-effort.
client/src/utils/providers.test.js: the same ladder assertions on the mirror. There is an existing server/client mirror-parity expectation for these ladders — keep both sides in lockstep or that test fails.
Acceptance criteria
- Saving
xhigh on a grok provider (task pin, pipeline stage, or scheduled task) renders in the effort select and reaches the spawned CLI as --effort xhigh.
- A provider whose saved
args already contain --reasoning-effort <level> gets no second injected flag.
- The
grok API-type provider still has no effort ladder.
- Server and client ladders stay identical; the existing mirror-parity test passes.
Problem
Grok Build CLI accepts a reasoning-effort flag, but PortOS never emits one for it. A user can save an effort level against a grok provider (a CoS task pin, a pipeline stage, a scheduled task) and it is silently dropped at spawn time — and the effort select is hidden in the picker, so there is no UI that reveals the level is inert.
The cause is
effortLevelsForProvider(server/lib/providerModels.js:356), which has ladder entries for opencode-local, codex, antigravity, cursor and claude and returnsnullfor everything else.resolveCliEffortshort-circuits on anullladder, sobuildEffortArgsreturns[]and no flag is injected. The client mirror (client/src/utils/providers.js:566) has the same gap, which is why the select disappears.Surfaced while making the pr-reviewer pipeline stages provider-agnostic (#5830): a grok-only install can now configure every stage's provider and model, but its thinking effort is inert.
Decision (already made — do not re-litigate)
Add a grok ladder. The values are not a guess — the CLI itself enumerates them:
So:
GROK_EFFORT_LEVELS = ['low', 'medium', 'high', 'xhigh'].No argv change is needed.
grok --helpdocuments--reasoning-effort <EFFORT> [aliases: --effort], andbuildEffortArgsalready emits['--effort', level]for every non-codex/non-cursor vendor. Adding the ladder is what makes that path fire.Detect grok inline, do not import
grok.js.server/lib/grok.jsalready importsproviderModels.js(line 28), so importing back would create a cycle. Mirror the existingisCodexProvidershape (providerModels.js:216) instead:(
GROK_CLI_ID/GROK_TUI_IDingrok.jsaregrok-cli/grok-tui;grokalone is the API provider id, which has no CLI effort flag — leave it out.)Scope
server/lib/providerModels.jsGROK_EFFORT_LEVELS = Object.freeze(['low', 'medium', 'high', 'xhigh'])beside the other ladders (~line 90-106).isGrokProvideras above.isGrokProvider(provider)arm toeffortLevelsForProvider(~line 356).ALL_EFFORT_LEVELS(~line 111) already contains every one of these values via the claude ladder — confirm, and add the spread only if that stops being true.server/lib/providerModels.js—hasEffortFlag(~line 417)Teach it grok's long form. It currently recognizes
--effort/--effort=/model_reasoning_effort=, so a user who bakes--reasoning-effort highintoprovider.argswould get a second injected--effort <level>appended. Grok's clap parser accepts the duplicate without error and last-wins, so the user's explicit pin is silently overridden — the opposite of the documented "a baked pin wins and the runner-injected effort is suppressed" contract in that function's own doc comment. Match--reasoning-effortand--reasoning-effort=alongside the existing forms.client/src/utils/providers.js— mirror both the ladder constant and theeffortLevelsForProviderarm (~line 355-367 and ~566). Keep the existing "sanitized inventories omit command" fallback in mind: gate on the provider id as well as the command basename, exactly as the server predicate above does.Tests
server/lib/providerModels.test.js:effortLevelsForProviderreturns the grok ladder for agrok-cliprovider, for a path-configured command (/Users/x/.grok/bin/grok), and forgrok-tui; returnsnullfor thegrokAPI provider.resolveCliEffortclamps an out-of-range level down the ladder (maxandultra->xhigh,minimal->low), matching the clamp contract already tested for agy.buildEffortArgsemits['--effort', <level>]for a grok provider, and emits nothing when the argv already carries--reasoning-effort.client/src/utils/providers.test.js: the same ladder assertions on the mirror. There is an existing server/client mirror-parity expectation for these ladders — keep both sides in lockstep or that test fails.Acceptance criteria
xhighon a grok provider (task pin, pipeline stage, or scheduled task) renders in the effort select and reaches the spawned CLI as--effort xhigh.argsalready contain--reasoning-effort <level>gets no second injected flag.grokAPI-type provider still has no effort ladder.