fix(antigravity): discover current source-control models and use CLI defaults

This commit is contained in:
Neil
2026-09-19 00:16:57 -07:00
parent 7063c2cbdd
commit a94b345313
5 changed files with 122 additions and 12 deletions
@@ -59,6 +59,7 @@ export const getActionDescriptions = createLocalizedCatalog(
const FALLBACK_AGENT_ARGS_PLACEHOLDER = '--model sonnet'
const AGENT_ARGS_PLACEHOLDER_OVERRIDES: Partial<Record<TuiAgent, string>> = {
antigravity: '--effort low',
// Why: Source Control AI action prompts are short, reviewable tasks; the
// mini Codex model is a better default hint than the frontier model.
codex: '--model gpt-5.4-mini',
@@ -0,0 +1,65 @@
import { describe, expect, it } from 'vitest'
import { getDefaultSettings } from './constants'
import { resolveSourceControlAiForOperation } from './source-control-ai'
import { planCommitMessageGeneration } from './commit-message-plan'
describe('Antigravity source-control model selection', () => {
for (const host of ['local', 'ssh:verification-host']) {
for (const operation of ['commitMessage', 'branchName', 'pullRequest'] as const) {
it(`${operation} on ${host} falls back from a retired saved model to the CLI configuration`, () => {
const settings = getDefaultSettings('/tmp')
settings.sourceControlAi = {
...settings.sourceControlAi!,
enabled: true,
agentId: 'antigravity',
selectedModelByAgent: { antigravity: 'Gemini 3.5 Flash (Medium)' },
selectedModelByAgentByHost: {
[host]: { antigravity: 'Gemini 3.5 Flash (Medium)' }
},
discoveredModelsByAgent: {},
discoveredModelsByAgentByHost: {}
}
const result = resolveSourceControlAiForOperation({
settings,
repo: null,
operation,
discoveryHostKey: host
})
expect(result.ok).toBe(true)
if (!result.ok) {
throw new Error(result.error)
}
expect(result.value.params.model).toBe('default')
const plan = planCommitMessageGeneration(result.value.params, 'Write Git text')
expect(plan.ok).toBe(true)
if (!plan.ok) {
throw new Error(plan.error)
}
expect(plan.plan.args).toEqual(['--print=Write Git text', '--sandbox'])
expect(plan.plan.stdinPayload).toBeNull()
})
}
}
it('lets explicit recipe arguments override the discovered model and requested effort once', () => {
const result = planCommitMessageGeneration(
{
agentId: 'antigravity',
model: 'gemini-3.8-flash-medium',
thinkingLevel: 'medium',
agentArgs: '--model gemini-3.7-flash-low --effort low'
},
'Write Git text'
)
expect(result.ok).toBe(true)
if (!result.ok) {
throw new Error(result.error)
}
expect(result.plan.args.filter((arg) => arg === '--model')).toHaveLength(1)
expect(result.plan.args.filter((arg) => arg === '--effort')).toHaveLength(1)
expect(result.plan.args).not.toContain('gemini-3.8-flash-medium')
expect(result.plan.args).not.toContain('medium')
expect(result.plan.args).toContain('gemini-3.7-flash-low')
expect(result.plan.args).toContain('low')
})
})
+35 -2
View File
@@ -607,7 +607,40 @@ describe('buildArgs (Antigravity)', () => {
expect(spec.modelDiscovery?.args).toEqual(['models'])
})
it('uses Gemini 3.5 Flash (Medium) as default model', () => {
expect(COMMIT_MESSAGE_AGENT_SPECS.antigravity?.defaultModelId).toBe('Gemini 3.5 Flash (Medium)')
it('uses the configured CLI model instead of a bundled model that can retire', () => {
expect(spec.defaultModelId).toBe('default')
expect(
spec.buildArgs({ prompt: 'Generate a commit message', model: spec.defaultModelId })
).toEqual(['--print=Generate a commit message', '--sandbox'])
})
it('passes only a nonempty requested effort', () => {
expect(spec.buildArgs({ prompt: 'P', model: 'default', thinkingLevel: '' })).not.toContain(
'--effort'
)
expect(spec.buildArgs({ prompt: 'P', model: 'default', thinkingLevel: 'high' })).toEqual([
'--print=P',
'--sandbox',
'--effort',
'high'
])
})
it('parses current tab-separated IDs without treating progress text as a model', () => {
expect(
parseAntigravityModels(
[
'Fetching available models...',
'id\tLabel',
'gemini-3.8-flash-medium\tGemini 3.8 Flash (Medium)',
'claude-sonnet-4-6\tClaude Sonnet 4.6 (Thinking)',
'gemini-3.8-flash-medium\tGemini 3.8 Flash (Medium)',
''
].join('\r\n')
)
).toEqual([
{ id: 'gemini-3.8-flash-medium', label: 'Gemini 3.8 Flash (Medium)' },
{ id: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6 (Thinking)' }
])
})
})
@@ -216,15 +216,17 @@ export function buildSecondaryCommitMessageAgentSpecs({
// using `--print=<value>` so a leading-dash prompt binds to the flag instead of
// being parsed as its own option, and --sandbox/--model stay separate options.
promptDelivery: 'argv',
buildArgs: ({ prompt, model }) => [`--print=${prompt}`, '--sandbox', '--model', model],
buildArgs: ({ prompt, model, thinkingLevel }) => [
`--print=${prompt}`,
'--sandbox',
...(model && model !== 'default' ? ['--model', model] : []),
...(thinkingLevel ? ['--effort', thinkingLevel] : [])
],
singletonOptions: [['--model'], ['--effort']],
modelSource: 'dynamic',
modelDiscovery: { binary: 'agy', args: ['models'], parse: parseAntigravityModels },
models: [
{ id: 'Gemini 3.5 Flash (Medium)', label: 'Gemini 3.5 Flash (Medium)' },
{ id: 'Gemini 3.5 Flash (High)', label: 'Gemini 3.5 Flash (High)' },
{ id: 'Gemini 3.5 Flash (Low)', label: 'Gemini 3.5 Flash (Low)' }
],
defaultModelId: 'Gemini 3.5 Flash (Medium)'
models: [{ id: 'default', label: 'Config default' }],
defaultModelId: 'default'
}
}
}
+12 -3
View File
@@ -235,13 +235,22 @@ export function parseCursorModels(stdout: string): CommitMessageModel[] {
export function parseAntigravityModels(stdout: string): CommitMessageModel[] {
const models: CommitMessageModel[] = []
for (const rawLine of iterateModelOutputLines(stdout)) {
const id = rawLine.trim()
if (id.length === 0) {
const line = rawLine.trim()
const separator = line.indexOf('\t')
const id = (separator === -1 ? line : line.slice(0, separator)).trim()
const label = separator === -1 ? id : line.slice(separator + 1).trim()
// Older agy versions list display names; current versions emit id<TAB>label.
if (
!id ||
!label ||
(separator === -1 && !/^.+ \((?:low|medium|high|thinking)\)$/i.test(id)) ||
(separator !== -1 && (/\s/.test(id) || /^(?:id|model)$/i.test(id)))
) {
continue
}
models.push({
id,
label: id
label
})
}
return uniqueModels(models)