Files
orca/src/shared/agent-session-option-catalog-grok.test.ts
T
Jinjing d3cb02f6a9 fix(grok): carry reasoning effort for models outside the seed catalog (#13365)
Launch resolves options from the static seed, so a discovered model id had no
options and silently dropped --reasoning-effort. unknownModelOptions keeps the
effort menu for those ids. Leave the multi-host launch gate unchanged.
2026-08-09 12:31:47 -07:00

312 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest'
import {
getAgentSessionOptionCatalog,
mergeCatalogModels,
mergeDiscoveredAuthoritativeModels,
type CatalogModel,
type CatalogOption
} from './agent-session-option-catalog'
import { GROK_SESSION_OPTION_CATALOG } from './agent-session-option-catalog-grok'
import { resolveAgentSessionOptionLaunch } from './agent-session-option-launch'
import { parseBuiltSessionOptionCommand } from './native-chat-session-option-commands'
function grokEffortOption(): CatalogOption {
return GROK_SESSION_OPTION_CATALOG.models[0].options.find((option) => option.id === 'effort')!
}
describe('grok session option catalog', () => {
it('is registered for the grok agent', () => {
expect(getAgentSessionOptionCatalog('grok')).toBe(GROK_SESSION_OPTION_CATALOG)
})
it('seeds only the one verified model, with an effort menu', () => {
expect(GROK_SESSION_OPTION_CATALOG.models.map(({ id, label }) => ({ id, label }))).toEqual([
{ id: 'grok-4.5', label: 'Grok 4.5' }
])
expect(GROK_SESSION_OPTION_CATALOG.models[0].isDefault).toBe(true)
const effort = grokEffortOption()
// The id must stay `effort`: `LaunchPreferences` is a strict zod object, so a
// novel id is dropped client-side and rejected on the wire.
expect(effort.id).toBe('effort')
expect(effort.category).toBe('thought_level')
expect(effort.kind).toMatchObject({ type: 'select', defaultValue: 'high' })
expect(effort.kind.type === 'select' ? effort.kind.choices.map((c) => c.value) : []).toEqual([
'low',
'medium',
'high'
])
})
it('offers only effort values the shared option labels localize', () => {
const localized = ['minimal', 'low', 'medium', 'high', 'xhigh', 'max']
const effort = grokEffortOption()
for (const choice of effort.kind.type === 'select' ? effort.kind.choices : []) {
expect(localized).toContain(choice.value)
}
})
it('gives unknown model ids the same effort menu launch reads from the seed', () => {
expect(GROK_SESSION_OPTION_CATALOG.unknownModelOptions?.map(({ id }) => id)).toEqual(['effort'])
})
it('treats a successful discovery as authoritative, unlike the other agents', () => {
expect(GROK_SESSION_OPTION_CATALOG.discoveredModelsAreAuthoritative).toBe(true)
for (const agent of ['claude', 'codex', 'gemini', 'cursor'] as const) {
expect(getAgentSessionOptionCatalog(agent)?.discoveredModelsAreAuthoritative).toBeUndefined()
}
})
})
describe('grok launch args', () => {
it('emits the model flag first, then effort', () => {
expect(resolveAgentSessionOptionLaunch('grok', { model: 'grok-4.5', effort: 'low' })).toEqual({
args: ['-m', 'grok-4.5', '--reasoning-effort', 'low'],
appliedValues: { model: 'grok-4.5', effort: 'low' }
})
})
it('emits exactly the two model tokens', () => {
expect(GROK_SESSION_OPTION_CATALOG.modelApply.launchArgs!('grok-4.5')).toEqual([
'-m',
'grok-4.5'
])
})
it('falls back to the seeded effort default when none is stored', () => {
expect(resolveAgentSessionOptionLaunch('grok', { model: 'grok-4.5' }).args).toEqual([
'-m',
'grok-4.5',
'--reasoning-effort',
'high'
])
})
it('emits only -m for a persisted model the seed does not carry', () => {
// The flag still goes out for a model this host may no longer have, and grok
// exits fatally on an unknown id. Launch resolves against the static seed, so
// the guard lives upstream: an authoritative probe retires the persisted id
// (clearNativeChatSessionOptionModel) before it can reach this call.
expect(resolveAgentSessionOptionLaunch('grok', { model: 'grok-build' })).toEqual({
args: ['-m', 'grok-build'],
appliedValues: { model: 'grok-build' }
})
})
it('carries a picked effort onto a discovered model the seed never listed', () => {
// Regression: launch reads options off the static seed, so a discovered id
// resolved to no options and dropped `--reasoning-effort` from the argv.
expect(resolveAgentSessionOptionLaunch('grok', { model: 'grok-build', effort: 'low' })).toEqual(
{
args: ['-m', 'grok-build', '--reasoning-effort', 'low'],
appliedValues: { model: 'grok-build', effort: 'low' }
}
)
})
it('drops an effort value the menu does not offer on an unseeded model', () => {
expect(
resolveAgentSessionOptionLaunch('grok', { model: 'grok-build', effort: 'none' })
).toEqual({ args: ['-m', 'grok-build'], appliedValues: { model: 'grok-build' } })
})
it('honors a user effort flag over the picker on an unseeded model too', () => {
expect(
resolveAgentSessionOptionLaunch('grok', { model: 'grok-build', effort: 'low' }, [
'--reasoning-effort=high'
]).appliedValues
).toEqual({ model: 'grok-build' })
})
it('still adds no effort default for a model the seed does not carry', () => {
// An unseeded id has no verified menu, so only an explicit pick may reach argv.
expect(resolveAgentSessionOptionLaunch('grok', { model: 'grok-build' }).args).toEqual([
'-m',
'grok-build'
])
})
it('spawns vanilla when no model was ever picked', () => {
expect(resolveAgentSessionOptionLaunch('grok', undefined)).toEqual({
args: [],
appliedValues: {}
})
})
})
describe('grok agentArgsOverride', () => {
const modelOverride = GROK_SESSION_OPTION_CATALOG.modelApply.agentArgsOverride!
const effortOverride = grokEffortOption().apply.agentArgsOverride!
it('detects a user-supplied model flag in every spelling', () => {
for (const tokens of [
['-m', 'grok-build'],
['-mgrok-build'],
['--model', 'grok-build'],
['--model=grok-build']
]) {
expect(modelOverride(tokens)).toBe(true)
}
})
it('does not fire on a different flag or a positional that contains -m', () => {
expect(modelOverride(['--model-context', '8000'])).toBe(false)
expect(modelOverride(['summarize-my-diff'])).toBe(false)
expect(modelOverride(['--reasoning-effort', 'low'])).toBe(false)
expect(modelOverride([])).toBe(false)
})
it('detects both effort spellings', () => {
expect(effortOverride(['--effort', 'low'])).toBe(true)
expect(effortOverride(['--reasoning-effort=low'])).toBe(true)
expect(effortOverride(['--effortless'])).toBe(false)
})
it('drops only the overridden key from the launch record', () => {
expect(
resolveAgentSessionOptionLaunch('grok', { model: 'grok-4.5', effort: 'high' }, [
'--reasoning-effort=low'
])
).toEqual({
args: ['-m', 'grok-4.5', '--reasoning-effort', 'high'],
appliedValues: { model: 'grok-4.5' }
})
})
it('cascades a model override onto the effort entry', () => {
// A user-supplied `-m` can select a model whose effort menu differs, so the
// picker's effort is no longer evidence about what actually launched.
expect(
resolveAgentSessionOptionLaunch('grok', { model: 'grok-4.5', effort: 'high' }, [
'-m',
'grok-build'
]).appliedValues
).toEqual({})
})
it('keeps the record intact when a lookalike flag is present', () => {
expect(
resolveAgentSessionOptionLaunch('grok', { model: 'grok-4.5', effort: 'low' }, [
'--model-context',
'8000'
]).appliedValues
).toEqual({ model: 'grok-4.5', effort: 'low' })
})
})
describe('grok mid-session commands', () => {
it('round-trips the model command through the built-command parser', () => {
const midSession = GROK_SESSION_OPTION_CATALOG.modelApply.midSession!
if (midSession.kind !== 'command') {
throw new Error('grok model changes must be a typed command, not an agent picker')
}
expect(midSession.build('grok-4.5')).toBe('/model grok-4.5')
expect(parseBuiltSessionOptionCommand(midSession.build, '/model grok-4.5')).toBe('grok-4.5')
expect(parseBuiltSessionOptionCommand(midSession.build, '/effort low')).toBeNull()
})
it('round-trips the effort command', () => {
const midSession = grokEffortOption().apply.midSession!
if (midSession.kind !== 'command') {
throw new Error('grok effort changes must be a typed command')
}
expect(midSession.build('low')).toBe('/effort low')
expect(parseBuiltSessionOptionCommand(midSession.build, '/effort low')).toBe('low')
expect(parseBuiltSessionOptionCommand(midSession.build, '/effort ')).toBeNull()
})
})
describe('mergeDiscoveredAuthoritativeModels', () => {
const seed = GROK_SESSION_OPTION_CATALOG.models
const discovered = (...ids: string[]): CatalogModel[] =>
ids.map((id) => ({ id, label: id, options: [] }))
it('keeps a matched seed model’s option menu, which discovery never carries', () => {
const merged = mergeDiscoveredAuthoritativeModels(seed, [
{ id: 'grok-4.5', label: 'Grok 4.5 (live)', options: [] }
])
expect(merged).toHaveLength(1)
expect(merged[0]).toMatchObject({ id: 'grok-4.5', label: 'Grok 4.5 (live)' })
expect(merged[0].options.map(({ id }) => id)).toEqual(['effort'])
})
it('takes the default flag from the probe and drops the seed’s stale one', () => {
// The seed marks grok-4.5; once the account's listing marks another row, the
// picker must follow it or it names a model an unflagged launch will not run.
const merged = mergeDiscoveredAuthoritativeModels(seed, [
{ id: 'grok-4.5', label: 'Grok 4.5', options: [] },
{ id: 'grok-5', label: 'Grok 5', isDefault: true, options: [] }
])
expect(merged.map(({ id, isDefault }) => [id, isDefault])).toEqual([
['grok-4.5', undefined],
['grok-5', true]
])
})
it('names no default when the probe marks none, rather than reviving the seed’s', () => {
const merged = mergeDiscoveredAuthoritativeModels(seed, discovered('grok-4.5'))
expect(merged[0].isDefault).toBeUndefined()
// The seed's menu still survives — only the default claim is discovery's to make.
expect(merged[0].options.map(({ id }) => id)).toEqual(['effort'])
})
it('gives a matched seed row its own menu, not the default row’s', () => {
// Only a seed carrying two distinct menus can tell inheritance apart from a
// real match; grok's one-model seed makes the two branches look identical.
const multiSeed: CatalogModel[] = [
{ id: 'grok-4.5', label: 'Grok 4.5', isDefault: true, options: seed[0].options },
{ id: 'grok-lite', label: 'Grok Lite', options: [] }
]
const merged = mergeDiscoveredAuthoritativeModels(multiSeed, discovered('grok-lite'))
expect(merged.map(({ id }) => id)).toEqual(['grok-lite'])
expect(merged[0].options).toEqual([])
})
it('drops a seed model the account no longer lists', () => {
const merged = mergeDiscoveredAuthoritativeModels(seed, discovered('grok-build'))
expect(merged.map(({ id }) => id)).toEqual(['grok-build'])
})
it('adds discovered models absent from the seed, lending them the default’s options', () => {
// Effort is a global grok flag rather than a per-model capability, so an
// unseeded model still gets the menu instead of rendering an option-less pill.
const merged = mergeDiscoveredAuthoritativeModels(seed, discovered('grok-4.5', 'grok-build'))
expect(merged.map(({ id }) => id)).toEqual(['grok-4.5', 'grok-build'])
expect(merged[1].options.map(({ id }) => id)).toEqual(['effort'])
})
it('takes the inherited options from the default seed row, not the first one', () => {
const multiSeed: CatalogModel[] = [
{ id: 'legacy', label: 'Legacy', options: [] },
{ id: 'grok-4.5', label: 'Grok 4.5', isDefault: true, options: seed[0].options }
]
const merged = mergeDiscoveredAuthoritativeModels(multiSeed, discovered('grok-build'))
expect(merged[0].options.map(({ id }) => id)).toEqual(['effort'])
})
it('lends no options when the seed is empty', () => {
expect(mergeDiscoveredAuthoritativeModels([], discovered('grok-build'))).toEqual([
{ id: 'grok-build', label: 'grok-build', options: [] }
])
})
it('preserves discovery order rather than seed order', () => {
const merged = mergeDiscoveredAuthoritativeModels(seed, discovered('grok-build', 'grok-4.5'))
expect(merged.map(({ id }) => id)).toEqual(['grok-build', 'grok-4.5'])
})
it('publishes an empty list for an empty discovery, so callers must gate on it', () => {
// Only enrichment's non-empty guard keeps a failed probe from blanking the picker.
expect(mergeDiscoveredAuthoritativeModels(seed, [])).toEqual([])
})
it('drops the unmatched seed row the additive merge would have kept', () => {
expect(mergeCatalogModels(seed, discovered('grok-build')).map(({ id }) => id)).toEqual([
'grok-4.5',
'grok-build'
])
expect(
mergeDiscoveredAuthoritativeModels(seed, discovered('grok-build')).map(({ id }) => id)
).toEqual(['grok-build'])
})
})