@golba98/codexa 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -10
- package/package.json +1 -1
- package/src/app.tsx +188 -121
- package/src/commands/handler.ts +11 -20
- package/src/config/appVersion.ts +65 -0
- package/src/config/buildInfo.ts +2 -2
- package/src/config/settings.ts +5 -1
- package/src/config/updateCheckCache.ts +18 -8
- package/src/core/models/codexModelCapabilities.ts +57 -4
- package/src/core/models/codexModelsCacheSeed.ts +153 -0
- package/src/core/models/providerModelCache.ts +90 -0
- package/src/core/process/CommandRunner.ts +12 -1
- package/src/core/providerLauncher/registry.ts +37 -7
- package/src/core/providerLauncher/types.ts +2 -1
- package/src/core/providerRuntime/anthropic.ts +3 -5
- package/src/core/providerRuntime/antigravity.ts +257 -90
- package/src/core/providerRuntime/claudeCodeDiscovery.ts +724 -692
- package/src/core/providerRuntime/mistralVibe.ts +663 -0
- package/src/core/providerRuntime/models.ts +18 -4
- package/src/core/providerRuntime/reasoning.ts +9 -6
- package/src/core/providerRuntime/registry.ts +93 -44
- package/src/core/providerRuntime/types.ts +1 -0
- package/src/core/version/packageManager.ts +119 -0
- package/src/core/version/updateCheck.ts +15 -9
- package/src/index.tsx +23 -9
- package/src/ui/{ActivityBars.tsx → chrome/ActivityBars.tsx} +1 -1
- package/src/ui/{ActivityIndicator.tsx → chrome/ActivityIndicator.tsx} +3 -3
- package/src/ui/{AnimatedStatusText.tsx → chrome/AnimatedStatusText.tsx} +3 -3
- package/src/ui/{AppShell.tsx → chrome/AppShell.tsx} +11 -11
- package/src/ui/{BottomComposer.tsx → chrome/BottomComposer.tsx} +15 -15
- package/src/ui/{DashCard.tsx → chrome/DashCard.tsx} +2 -2
- package/src/ui/{RunFooter.tsx → chrome/RunFooter.tsx} +3 -3
- package/src/ui/{RuntimeStatusBar.tsx → chrome/RuntimeStatusBar.tsx} +3 -3
- package/src/ui/{Spinner.tsx → chrome/Spinner.tsx} +1 -1
- package/src/ui/{TopHeader.tsx → chrome/TopHeader.tsx} +15 -13
- package/src/ui/{UpdateAvailableCard.tsx → chrome/UpdateAvailableCard.tsx} +6 -5
- package/src/ui/{commandNormalize.ts → input/commandNormalize.ts} +1 -1
- package/src/ui/{focus.ts → input/focus.ts} +1 -1
- package/src/ui/{inputBuffer.ts → input/inputBuffer.ts} +3 -3
- package/src/ui/{slashCommands.ts → input/slashCommands.ts} +1 -0
- package/src/ui/{AttachmentImportPanel.tsx → panels/AttachmentImportPanel.tsx} +1 -1
- package/src/ui/{AuthPanel.tsx → panels/AuthPanel.tsx} +5 -5
- package/src/ui/{BackendPicker.tsx → panels/BackendPicker.tsx} +2 -2
- package/src/ui/{ModePicker.tsx → panels/ModePicker.tsx} +2 -2
- package/src/ui/{ModelPicker.tsx → panels/ModelPicker.tsx} +2 -2
- package/src/ui/{ModelPickerScreen.tsx → panels/ModelPickerScreen.tsx} +11 -9
- package/src/ui/{ModelReasoningPicker.tsx → panels/ModelReasoningPicker.tsx} +5 -5
- package/src/ui/{Panel.tsx → panels/Panel.tsx} +1 -1
- package/src/ui/{PermissionsPanel.tsx → panels/PermissionsPanel.tsx} +3 -3
- package/src/ui/{PlanActionPicker.tsx → panels/PlanActionPicker.tsx} +2 -2
- package/src/ui/{PlanReviewPanel.tsx → panels/PlanReviewPanel.tsx} +5 -5
- package/src/ui/{ProviderPicker.tsx → panels/ProviderPicker.tsx} +9 -7
- package/src/ui/{ReasoningPicker.tsx → panels/ReasoningPicker.tsx} +3 -3
- package/src/ui/{SelectionPanel.tsx → panels/SelectionPanel.tsx} +1 -1
- package/src/ui/{SettingsPanel.tsx → panels/SettingsPanel.tsx} +3 -3
- package/src/ui/{TextEntryPanel.tsx → panels/TextEntryPanel.tsx} +3 -3
- package/src/ui/{ThemePicker.tsx → panels/ThemePicker.tsx} +2 -2
- package/src/ui/{UpdatePromptPanel.tsx → panels/UpdatePromptPanel.tsx} +54 -44
- package/src/ui/{Markdown.tsx → render/Markdown.tsx} +2 -2
- package/src/ui/{diffRenderer.ts → render/diffRenderer.ts} +1 -1
- package/src/ui/{modeDisplay.ts → render/modeDisplay.ts} +2 -2
- package/src/ui/{outputPipeline.ts → render/outputPipeline.ts} +2 -2
- package/src/ui/{runtimeDisplay.ts → render/runtimeDisplay.ts} +9 -8
- package/src/ui/{ActionRequiredBlock.tsx → timeline/ActionRequiredBlock.tsx} +3 -3
- package/src/ui/{AgentBlock.tsx → timeline/AgentBlock.tsx} +10 -10
- package/src/ui/{StaticIntroItem.tsx → timeline/StaticIntroItem.tsx} +2 -2
- package/src/ui/{ThinkingBlock.tsx → timeline/ThinkingBlock.tsx} +4 -4
- package/src/ui/{Timeline.tsx → timeline/Timeline.tsx} +8 -8
- package/src/ui/{TranscriptShell.tsx → timeline/TranscriptShell.tsx} +6 -6
- package/src/ui/{TurnGroup.tsx → timeline/TurnGroup.tsx} +15 -15
- package/src/ui/{progressEntries.ts → timeline/progressEntries.ts} +3 -3
- package/src/ui/{runActivityView.ts → timeline/runActivityView.ts} +1 -1
- package/src/ui/{timelineMeasure.ts → timeline/timelineMeasure.ts} +14 -14
- /package/src/ui/{busyStatusAnimation.ts → chrome/busyStatusAnimation.ts} +0 -0
- /package/src/ui/{logoVariants.ts → render/logoVariants.ts} +0 -0
- /package/src/ui/{terminalAnswerFormat.ts → render/terminalAnswerFormat.ts} +0 -0
- /package/src/ui/{textLayout.ts → render/textLayout.ts} +0 -0
- /package/src/ui/{layoutListWindow.ts → timeline/layoutListWindow.ts} +0 -0
|
@@ -16,14 +16,15 @@ import {
|
|
|
16
16
|
} from "../providerRuntime/registry.js";
|
|
17
17
|
import { normalizeGeminiModelId } from "../providerRuntime/models.js";
|
|
18
18
|
import { ANTIGRAVITY_DEFAULT_MODEL_ID } from "../providerRuntime/antigravity.js";
|
|
19
|
+
import { discoverMistralVibeModels } from "../providerRuntime/mistralVibe.js";
|
|
19
20
|
import { setLocalProviderConfig } from "../providerRuntime/local.js";
|
|
20
21
|
import { formatContextLength, resolveModelContextLengthCached } from "../providerRuntime/contextMetadata.js";
|
|
21
22
|
import { resolveModelCapabilityProfileCached } from "../providerRuntime/capabilityProfile.js";
|
|
22
23
|
|
|
23
24
|
// Google/Gemini remains a recognized legacy config value so existing workspace
|
|
24
25
|
// files can be migrated, but it is no longer a selectable Codexa provider.
|
|
25
|
-
const PROVIDER_ORDER: readonly ProviderId[] = ["openai", "anthropic", "local", "antigravity"];
|
|
26
|
-
const KNOWN_PROVIDER_IDS: readonly ProviderId[] = ["openai", "anthropic", "google", "local", "antigravity"];
|
|
26
|
+
const PROVIDER_ORDER: readonly ProviderId[] = ["openai", "anthropic", "mistral", "local", "antigravity"];
|
|
27
|
+
const KNOWN_PROVIDER_IDS: readonly ProviderId[] = ["openai", "anthropic", "google", "mistral", "local", "antigravity"];
|
|
27
28
|
|
|
28
29
|
const DEFAULT_PROVIDER_ID: ProviderId = "openai";
|
|
29
30
|
|
|
@@ -78,6 +79,17 @@ const DEFAULT_PROVIDERS: Record<ProviderId, ProviderDefault> = {
|
|
|
78
79
|
isActiveRoute: false,
|
|
79
80
|
routeUnavailableReason: "Local provider unavailable. Start LM Studio, load a model, and enable the local server.",
|
|
80
81
|
},
|
|
82
|
+
mistral: {
|
|
83
|
+
id: "mistral",
|
|
84
|
+
displayName: "Mistral Vibe CLI",
|
|
85
|
+
currentModel: () => "Vibe default",
|
|
86
|
+
backendType: "mistral-vibe-cli-auth",
|
|
87
|
+
routeMode: "in-codexa",
|
|
88
|
+
enabled: true,
|
|
89
|
+
launchCommand: { executable: "vibe", args: [] },
|
|
90
|
+
isActiveRoute: false,
|
|
91
|
+
routeUnavailableReason: null,
|
|
92
|
+
},
|
|
81
93
|
antigravity: {
|
|
82
94
|
id: "antigravity",
|
|
83
95
|
displayName: "Antigravity",
|
|
@@ -137,9 +149,11 @@ function applyOverride(
|
|
|
137
149
|
: provider.currentModel,
|
|
138
150
|
enabled: nextEnabled,
|
|
139
151
|
launchCommand: nextCommand,
|
|
140
|
-
statusLabel: nextEnabled
|
|
141
|
-
?
|
|
142
|
-
: "
|
|
152
|
+
statusLabel: !nextEnabled
|
|
153
|
+
? "Disabled"
|
|
154
|
+
: provider.routeMode === "launch-only"
|
|
155
|
+
? provider.statusLabel
|
|
156
|
+
: (provider.routeUnavailableReason ? "Needs config" : "Enabled"),
|
|
143
157
|
};
|
|
144
158
|
}
|
|
145
159
|
|
|
@@ -157,6 +171,7 @@ export function getActiveRouteProviderId(config: ProviderWorkspaceConfig | null
|
|
|
157
171
|
|
|
158
172
|
export function buildProviderRegistry(options: {
|
|
159
173
|
activeModel: string;
|
|
174
|
+
workspaceRoot?: string;
|
|
160
175
|
workspaceConfig?: ProviderWorkspaceConfig | null;
|
|
161
176
|
diagnostics?: Record<string, Record<string, string | number | boolean | null>>;
|
|
162
177
|
routeErrors?: Record<string, string>;
|
|
@@ -170,7 +185,9 @@ export function buildProviderRegistry(options: {
|
|
|
170
185
|
}
|
|
171
186
|
const defaults = DEFAULT_PROVIDERS[id];
|
|
172
187
|
const runtime = getProviderRuntime(id);
|
|
173
|
-
const discovery =
|
|
188
|
+
const discovery = id === "mistral"
|
|
189
|
+
? discoverMistralVibeModels(options.workspaceRoot ?? process.cwd())
|
|
190
|
+
: runtime.discoverModels();
|
|
174
191
|
|
|
175
192
|
const activeRoute = options.workspaceConfig?.activeRoute;
|
|
176
193
|
const isThisActive = activeRoute?.providerId === id;
|
|
@@ -205,6 +222,10 @@ export function buildProviderRegistry(options: {
|
|
|
205
222
|
}
|
|
206
223
|
}
|
|
207
224
|
|
|
225
|
+
if (id === "mistral") {
|
|
226
|
+
currentModelLabel = discovery.models[0]?.modelId ?? "Vibe default";
|
|
227
|
+
}
|
|
228
|
+
|
|
208
229
|
const rawMetadataForModel = discovery.models.find((model) => model.modelId === currentModelLabel)?.raw;
|
|
209
230
|
const contextMetadata = resolveModelContextLengthCached({
|
|
210
231
|
providerId: id,
|
|
@@ -230,7 +251,16 @@ export function buildProviderRegistry(options: {
|
|
|
230
251
|
|
|
231
252
|
const enabled = id === "local" ? discovery.status === "ready" : defaults.enabled;
|
|
232
253
|
|
|
233
|
-
const
|
|
254
|
+
const availabilityStatus = options.diagnostics?.[id]?.availabilityStatus;
|
|
255
|
+
const statusLabel = id === "mistral"
|
|
256
|
+
? availabilityStatus === "checking"
|
|
257
|
+
? "Checking"
|
|
258
|
+
: availabilityStatus === "unavailable"
|
|
259
|
+
? "Missing"
|
|
260
|
+
: routeUnavailableReason
|
|
261
|
+
? "Needs config"
|
|
262
|
+
: "Enabled"
|
|
263
|
+
: id === "local"
|
|
234
264
|
? (discovery.status === "ready" ? "Enabled" : "Disabled")
|
|
235
265
|
: !defaults.enabled
|
|
236
266
|
? "Disabled"
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export type ProviderId = "openai" | "anthropic" | "google" | "local" | "antigravity";
|
|
1
|
+
export type ProviderId = "openai" | "anthropic" | "google" | "mistral" | "local" | "antigravity";
|
|
2
2
|
|
|
3
3
|
export type ProviderBackendType =
|
|
4
4
|
| "codex-cli-auth"
|
|
5
5
|
| "gemini-cli-auth"
|
|
6
6
|
| "claude-code-auth"
|
|
7
|
+
| "mistral-vibe-cli-auth"
|
|
7
8
|
| "antigravity-cli-auth"
|
|
8
9
|
| "openai-api-key"
|
|
9
10
|
| "gemini-api-key"
|
|
@@ -4,7 +4,6 @@ import type { BackendRunHandlers } from "../providers/types.js";
|
|
|
4
4
|
import { ANTHROPIC_FALLBACK_MODELS } from "./models.js";
|
|
5
5
|
import type { ProviderBackendKind, ProviderChatRequest, ProviderModel, ProviderModelDiscoveryResult, ProviderRouteValidationResult, ProviderRuntime } from "./types.js";
|
|
6
6
|
import { buildClaudeSpawnSpec, resetClaudeExecutableCacheForTests } from "../executables/claudeExecutable.js";
|
|
7
|
-
import { CLAUDE_CODE_EFFORT_IDS } from "./reasoning.js";
|
|
8
7
|
import {
|
|
9
8
|
claudeCodeModelsToProviderModels,
|
|
10
9
|
discoverClaudeCodeCapabilities,
|
|
@@ -56,10 +55,9 @@ export function mapModelIdToClaudeArg(modelId: string): string {
|
|
|
56
55
|
return modelId;
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
export function mapReasoningToEffort(reasoning: string | null | undefined): string | null {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
58
|
+
export function mapReasoningToEffort(reasoning: string | null | undefined): string | null {
|
|
59
|
+
return reasoning?.trim() || null;
|
|
60
|
+
}
|
|
63
61
|
|
|
64
62
|
function buildClaudeCodeBaseArgs(request: ProviderChatRequest): string[] {
|
|
65
63
|
const effort = mapReasoningToEffort(request.route.reasoning ?? null);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { runCommand } from "../process/CommandRunner.js";
|
|
2
2
|
import { sanitizeTerminalOutput } from "../terminal/terminalSanitize.js";
|
|
3
3
|
import type { ReasoningEffortCapability } from "../models/codexModelCapabilities.js";
|
|
4
|
+
import { loadCachedProviderModels } from "../models/providerModelCache.js";
|
|
4
5
|
import type { BackendRunHandlers } from "../providers/types.js";
|
|
5
6
|
import type {
|
|
6
7
|
ProviderBackendKind,
|
|
@@ -27,97 +28,155 @@ export const ANTIGRAVITY_DEFAULT_MODEL_ID = "gemini-3.5-flash";
|
|
|
27
28
|
export const ANTIGRAVITY_DEFAULT_REASONING = "high";
|
|
28
29
|
|
|
29
30
|
// ---------------------------------------------------------------------------
|
|
30
|
-
//
|
|
31
|
+
// Model definitions
|
|
31
32
|
// ---------------------------------------------------------------------------
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
interface AgySelectorMetadata {
|
|
35
|
+
provider: "antigravity";
|
|
36
|
+
selectors: Record<string, string>;
|
|
37
|
+
}
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
function normalizeAgyId(value: string): string {
|
|
40
|
+
return value
|
|
41
|
+
.trim()
|
|
42
|
+
.toLowerCase()
|
|
43
|
+
.replace(/[^a-z0-9.]+/g, "-")
|
|
44
|
+
.replace(/^-+|-+$/g, "");
|
|
45
|
+
}
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
supportedReasoningLevels: [AGY_LOW, AGY_MEDIUM, AGY_HIGH],
|
|
49
|
-
source: "fallback",
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
id: "gemini-3.1-pro",
|
|
53
|
-
modelId: "gemini-3.1-pro",
|
|
54
|
-
label: "Gemini 3.1 Pro",
|
|
55
|
-
description: "Antigravity-routed Gemini 3.1 Pro. Select effort with ←/→.",
|
|
56
|
-
defaultReasoningLevel: "high",
|
|
57
|
-
supportedReasoningLevels: [AGY_LOW, AGY_HIGH],
|
|
58
|
-
source: "fallback",
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
id: "claude-sonnet-4-6-think",
|
|
62
|
-
modelId: "claude-sonnet-4-6-think",
|
|
63
|
-
label: "Claude Sonnet 4.6 (Thinking)",
|
|
64
|
-
description: "Antigravity-routed Claude Sonnet 4.6 with extended thinking.",
|
|
65
|
-
defaultReasoningLevel: null,
|
|
66
|
-
supportedReasoningLevels: null,
|
|
67
|
-
source: "fallback",
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
id: "claude-opus-4-6-think",
|
|
71
|
-
modelId: "claude-opus-4-6-think",
|
|
72
|
-
label: "Claude Opus 4.6 (Thinking)",
|
|
73
|
-
description: "Antigravity-routed Claude Opus 4.6 with extended thinking.",
|
|
74
|
-
defaultReasoningLevel: null,
|
|
75
|
-
supportedReasoningLevels: null,
|
|
76
|
-
source: "fallback",
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
id: "gpt-oss-120b",
|
|
80
|
-
modelId: "gpt-oss-120b",
|
|
81
|
-
label: "GPT-OSS 120B",
|
|
82
|
-
description: "Antigravity-routed GPT-OSS 120B.",
|
|
83
|
-
defaultReasoningLevel: null,
|
|
84
|
-
supportedReasoningLevels: null,
|
|
85
|
-
source: "fallback",
|
|
86
|
-
},
|
|
87
|
-
];
|
|
47
|
+
function formatAgyVariantLabel(value: string): string {
|
|
48
|
+
return value
|
|
49
|
+
.split(/[-_\s]+/)
|
|
50
|
+
.filter(Boolean)
|
|
51
|
+
.map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1).toLowerCase()}`)
|
|
52
|
+
.join(" ") || value;
|
|
53
|
+
}
|
|
88
54
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
55
|
+
function readAgySelectorMetadata(model: ProviderModel): AgySelectorMetadata | null {
|
|
56
|
+
if (!model.raw || typeof model.raw !== "object" || Array.isArray(model.raw)) return null;
|
|
57
|
+
const raw = model.raw as Partial<AgySelectorMetadata>;
|
|
58
|
+
if (raw.provider !== "antigravity" || !raw.selectors || typeof raw.selectors !== "object") return null;
|
|
59
|
+
return { provider: "antigravity", selectors: raw.selectors };
|
|
60
|
+
}
|
|
92
61
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
62
|
+
function preferredAgyDefault(modelId: string, efforts: readonly string[]): string {
|
|
63
|
+
if ((modelId === "gemini-3.5-flash" || modelId === "gemini-3.1-pro") && efforts.includes(ANTIGRAVITY_DEFAULT_REASONING)) {
|
|
64
|
+
return ANTIGRAVITY_DEFAULT_REASONING;
|
|
65
|
+
}
|
|
66
|
+
return efforts[0] ?? ANTIGRAVITY_DEFAULT_REASONING;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const AGY_REASONING_DISPLAY_ORDER = ["low", "medium", "high", "xhigh", "max"] as const;
|
|
70
|
+
|
|
71
|
+
function sortAgyReasoningLevels(levels: readonly ReasoningEffortCapability[]): ReasoningEffortCapability[] {
|
|
72
|
+
const rank = new Map<string, number>(AGY_REASONING_DISPLAY_ORDER.map((id, index) => [id, index]));
|
|
73
|
+
return levels
|
|
74
|
+
.map((level, index) => ({ level, index }))
|
|
75
|
+
.sort((left, right) => (rank.get(left.level.id) ?? AGY_REASONING_DISPLAY_ORDER.length + left.index)
|
|
76
|
+
- (rank.get(right.level.id) ?? AGY_REASONING_DISPLAY_ORDER.length + right.index))
|
|
77
|
+
.map(({ level }) => level);
|
|
78
|
+
}
|
|
106
79
|
|
|
107
|
-
export function
|
|
108
|
-
|
|
80
|
+
export function parseAgyModelsOutput(stdout: string): ProviderModel[] {
|
|
81
|
+
const lines = stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
82
|
+
const parsed = lines.map((selector) => {
|
|
83
|
+
const match = selector.match(/^(.*?)\s+\(([^()]+)\)$/);
|
|
84
|
+
return { selector, base: match?.[1]?.trim() ?? selector, variant: match?.[2]?.trim() ?? null };
|
|
85
|
+
});
|
|
86
|
+
const baseCounts = new Map<string, number>();
|
|
87
|
+
for (const item of parsed) baseCounts.set(item.base, (baseCounts.get(item.base) ?? 0) + 1);
|
|
88
|
+
|
|
89
|
+
const models: ProviderModel[] = [];
|
|
90
|
+
const grouped = new Map<string, ProviderModel>();
|
|
91
|
+
for (const item of parsed) {
|
|
92
|
+
const isVariantGroup = item.variant !== null && (baseCounts.get(item.base) ?? 0) > 1;
|
|
93
|
+
if (!isVariantGroup) {
|
|
94
|
+
const modelId = normalizeAgyId(item.selector);
|
|
95
|
+
models.push({
|
|
96
|
+
id: modelId,
|
|
97
|
+
modelId,
|
|
98
|
+
label: item.selector,
|
|
99
|
+
description: `Discovered from agy models: ${item.selector}`,
|
|
100
|
+
defaultReasoningLevel: null,
|
|
101
|
+
supportedReasoningLevels: null,
|
|
102
|
+
source: "discovered",
|
|
103
|
+
raw: { provider: "antigravity", selectors: { "": item.selector } } satisfies AgySelectorMetadata,
|
|
104
|
+
});
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const modelId = normalizeAgyId(item.base);
|
|
109
|
+
const effortId = normalizeAgyId(item.variant ?? "");
|
|
110
|
+
const existing = grouped.get(item.base);
|
|
111
|
+
if (existing) {
|
|
112
|
+
const metadata = readAgySelectorMetadata(existing);
|
|
113
|
+
const levels = sortAgyReasoningLevels([...(existing.supportedReasoningLevels ?? []), {
|
|
114
|
+
id: effortId,
|
|
115
|
+
label: formatAgyVariantLabel(item.variant ?? effortId),
|
|
116
|
+
description: null,
|
|
117
|
+
}]);
|
|
118
|
+
const selectors = { ...(metadata?.selectors ?? {}), [effortId]: item.selector };
|
|
119
|
+
const updated = {
|
|
120
|
+
...existing,
|
|
121
|
+
defaultReasoningLevel: preferredAgyDefault(modelId, levels.map((level) => level.id)),
|
|
122
|
+
supportedReasoningLevels: levels,
|
|
123
|
+
raw: { provider: "antigravity", selectors } satisfies AgySelectorMetadata,
|
|
124
|
+
};
|
|
125
|
+
grouped.set(item.base, updated);
|
|
126
|
+
models[models.indexOf(existing)] = updated;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const level: ReasoningEffortCapability = {
|
|
131
|
+
id: effortId,
|
|
132
|
+
label: formatAgyVariantLabel(item.variant ?? effortId),
|
|
133
|
+
description: null,
|
|
134
|
+
};
|
|
135
|
+
const model: ProviderModel = {
|
|
136
|
+
id: modelId,
|
|
137
|
+
modelId,
|
|
138
|
+
label: item.base,
|
|
139
|
+
description: `Discovered from agy models. Select an advertised variant with ←/→.`,
|
|
140
|
+
defaultReasoningLevel: preferredAgyDefault(modelId, [effortId]),
|
|
141
|
+
supportedReasoningLevels: [level],
|
|
142
|
+
source: "discovered",
|
|
143
|
+
raw: { provider: "antigravity", selectors: { [effortId]: item.selector } } satisfies AgySelectorMetadata,
|
|
144
|
+
};
|
|
145
|
+
grouped.set(item.base, model);
|
|
146
|
+
models.push(model);
|
|
147
|
+
}
|
|
148
|
+
return models;
|
|
109
149
|
}
|
|
110
150
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
151
|
+
// Resolve persisted model/reasoning state to the exact selector advertised by `agy models`.
|
|
152
|
+
export function getAgyModelSelector(
|
|
153
|
+
modelId: string,
|
|
154
|
+
reasoning: string | null | undefined,
|
|
155
|
+
models: readonly ProviderModel[] = getActiveAgyModels(),
|
|
156
|
+
): string | null {
|
|
157
|
+
const model = models.find((item) => item.modelId === modelId || item.id === modelId);
|
|
158
|
+
if (!model) return null;
|
|
159
|
+
const metadata = readAgySelectorMetadata(model);
|
|
160
|
+
if (!metadata) return null;
|
|
161
|
+
if (!model.supportedReasoningLevels?.length) return metadata.selectors[""] ?? null;
|
|
162
|
+
if (reasoning) return metadata.selectors[reasoning] ?? null;
|
|
163
|
+
const effort = model.defaultReasoningLevel;
|
|
164
|
+
return effort ? metadata.selectors[effort] ?? null : null;
|
|
117
165
|
}
|
|
118
166
|
|
|
119
167
|
export function getAntigravityModelLabel(modelId: string): string {
|
|
120
|
-
|
|
168
|
+
const discovered = getActiveAgyModels().find((m) => m.id === modelId || m.modelId === modelId);
|
|
169
|
+
if (discovered) return discovered.label;
|
|
170
|
+
|
|
171
|
+
const normalizedId = migrateAntigravityLegacyModelId(modelId).modelId;
|
|
172
|
+
const knownLabels: Record<string, string> = {
|
|
173
|
+
"gemini-3.5-flash": "Gemini 3.5 Flash",
|
|
174
|
+
"gemini-3.1-pro": "Gemini 3.1 Pro",
|
|
175
|
+
"claude-sonnet-4.6-thinking": "Claude Sonnet 4.6 (Thinking)",
|
|
176
|
+
"claude-opus-4.6-thinking": "Claude Opus 4.6 (Thinking)",
|
|
177
|
+
"gpt-oss-120b-medium": "GPT-OSS 120B",
|
|
178
|
+
};
|
|
179
|
+
return knownLabels[normalizedId] ?? modelId;
|
|
121
180
|
}
|
|
122
181
|
|
|
123
182
|
// ---------------------------------------------------------------------------
|
|
@@ -138,7 +197,9 @@ export function migrateAntigravityLegacyModelId(modelId: string): { modelId: str
|
|
|
138
197
|
"gemini-3.5-flash-low": { modelId: "gemini-3.5-flash", reasoning: "low" },
|
|
139
198
|
"gemini-3.1-pro-high": { modelId: "gemini-3.1-pro", reasoning: "high" },
|
|
140
199
|
"gemini-3.1-pro-low": { modelId: "gemini-3.1-pro", reasoning: "low" },
|
|
141
|
-
"
|
|
200
|
+
"claude-sonnet-4-6-think": { modelId: "claude-sonnet-4.6-thinking" },
|
|
201
|
+
"claude-opus-4-6-think": { modelId: "claude-opus-4.6-thinking" },
|
|
202
|
+
"gpt-oss-120b": { modelId: "gpt-oss-120b-medium" },
|
|
142
203
|
};
|
|
143
204
|
return legacy[modelId] ?? { modelId };
|
|
144
205
|
}
|
|
@@ -149,6 +210,52 @@ export function migrateAntigravityLegacyModelId(modelId: string): { modelId: str
|
|
|
149
210
|
|
|
150
211
|
let agyRouteValidated = false;
|
|
151
212
|
let resolvedAgyExecutable: string = "agy";
|
|
213
|
+
let discoveredAgyModels: readonly ProviderModel[] | null = null;
|
|
214
|
+
|
|
215
|
+
function getActiveAgyModels(): readonly ProviderModel[] {
|
|
216
|
+
if (discoveredAgyModels?.length) return discoveredAgyModels;
|
|
217
|
+
return loadCachedProviderModels("antigravity")?.models ?? [];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export async function discoverAgyModels(options: {
|
|
221
|
+
executable: string;
|
|
222
|
+
cwd: string;
|
|
223
|
+
runCommandImpl: typeof runCommand;
|
|
224
|
+
platform: NodeJS.Platform;
|
|
225
|
+
}): Promise<ProviderModelDiscoveryResult> {
|
|
226
|
+
const spawnSpec = buildSpawnSpec(options.executable, ["models"], options.platform);
|
|
227
|
+
const result = await options.runCommandImpl({
|
|
228
|
+
executable: spawnSpec.executable,
|
|
229
|
+
args: spawnSpec.args,
|
|
230
|
+
cwd: options.cwd,
|
|
231
|
+
timeoutMs: ANTIGRAVITY_VALIDATION_TIMEOUT_MS,
|
|
232
|
+
}).result;
|
|
233
|
+
const models = result.status === "completed" && result.exitCode === 0
|
|
234
|
+
? parseAgyModelsOutput(result.stdout)
|
|
235
|
+
: [];
|
|
236
|
+
if (models.length > 0) {
|
|
237
|
+
discoveredAgyModels = models;
|
|
238
|
+
return {
|
|
239
|
+
status: "ready",
|
|
240
|
+
providerId: "antigravity",
|
|
241
|
+
backendKind: "antigravity-cli-auth",
|
|
242
|
+
models,
|
|
243
|
+
message: `Loaded ${models.length} models from agy models.`,
|
|
244
|
+
diagnostics: { modelSource: "agy-models-command", modelsExitCode: result.exitCode, modelsStatus: result.status },
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
const cached = loadCachedProviderModels("antigravity")?.models ?? [];
|
|
248
|
+
return {
|
|
249
|
+
status: cached.length > 0 ? "ready" : "not-configured",
|
|
250
|
+
providerId: "antigravity",
|
|
251
|
+
backendKind: cached.length > 0 ? "antigravity-cli-auth" : "unavailable",
|
|
252
|
+
models: cached,
|
|
253
|
+
message: cached.length > 0
|
|
254
|
+
? "Live agy model metadata is unavailable; using the last successful discovery."
|
|
255
|
+
: "Antigravity model metadata is unavailable. Run Refresh models after checking `agy models`.",
|
|
256
|
+
diagnostics: { modelSource: cached.length > 0 ? "cache" : "unavailable", modelsExitCode: result.exitCode, modelsStatus: result.status },
|
|
257
|
+
};
|
|
258
|
+
}
|
|
152
259
|
|
|
153
260
|
export function isAntigravityRouteConfigured(): boolean {
|
|
154
261
|
return agyRouteValidated;
|
|
@@ -157,6 +264,7 @@ export function isAntigravityRouteConfigured(): boolean {
|
|
|
157
264
|
export function resetAntigravityRouteValidationCacheForTests(): void {
|
|
158
265
|
agyRouteValidated = false;
|
|
159
266
|
resolvedAgyExecutable = "agy";
|
|
267
|
+
discoveredAgyModels = null;
|
|
160
268
|
resetAgyExecutableCacheForTests();
|
|
161
269
|
}
|
|
162
270
|
|
|
@@ -170,6 +278,24 @@ export async function validateAntigravityRoute(options: {
|
|
|
170
278
|
runCommandImpl?: typeof runCommand;
|
|
171
279
|
platform?: NodeJS.Platform;
|
|
172
280
|
}): Promise<ProviderRouteValidationResult> {
|
|
281
|
+
// Already validated this session: skip the executable probe and model
|
|
282
|
+
// re-discovery so re-activating antigravity is instant. "Refresh models"
|
|
283
|
+
// bypasses this via runtime.refreshModels, so a stale catalog stays
|
|
284
|
+
// user-recoverable.
|
|
285
|
+
if (agyRouteValidated && discoveredAgyModels?.length) {
|
|
286
|
+
return {
|
|
287
|
+
status: "ready",
|
|
288
|
+
providerId: "antigravity",
|
|
289
|
+
backendKind: "antigravity-cli-auth",
|
|
290
|
+
message: `Antigravity CLI found at: ${resolvedAgyExecutable}`,
|
|
291
|
+
diagnostics: {
|
|
292
|
+
resolvedCommand: resolvedAgyExecutable,
|
|
293
|
+
modelSource: "session-cache",
|
|
294
|
+
discoveredModelCount: discoveredAgyModels.length,
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
173
299
|
let resolved: string;
|
|
174
300
|
try {
|
|
175
301
|
resolved = await resolveAgyExecutable({
|
|
@@ -213,13 +339,23 @@ export async function validateAntigravityRoute(options: {
|
|
|
213
339
|
|
|
214
340
|
resolvedAgyExecutable = resolved;
|
|
215
341
|
agyRouteValidated = true;
|
|
342
|
+
const modelDiscovery = await discoverAgyModels({
|
|
343
|
+
executable: resolved,
|
|
344
|
+
cwd: options.cwd ?? process.cwd(),
|
|
345
|
+
runCommandImpl,
|
|
346
|
+
platform: options.platform ?? process.platform,
|
|
347
|
+
});
|
|
216
348
|
|
|
217
349
|
return {
|
|
218
350
|
status: "ready",
|
|
219
351
|
providerId: "antigravity",
|
|
220
352
|
backendKind: "antigravity-cli-auth",
|
|
221
353
|
message: `Antigravity CLI found at: ${resolved}`,
|
|
222
|
-
diagnostics: {
|
|
354
|
+
diagnostics: {
|
|
355
|
+
resolvedCommand: resolved,
|
|
356
|
+
modelSource: modelDiscovery.diagnostics?.modelSource ?? "unavailable",
|
|
357
|
+
discoveredModelCount: modelDiscovery.models.length,
|
|
358
|
+
},
|
|
223
359
|
};
|
|
224
360
|
}
|
|
225
361
|
|
|
@@ -233,16 +369,23 @@ export function runAntigravityWithRunner(
|
|
|
233
369
|
runCommandImpl: typeof runCommand = runCommand,
|
|
234
370
|
executable: string = resolvedAgyExecutable,
|
|
235
371
|
platform: NodeJS.Platform = process.platform,
|
|
372
|
+
models: readonly ProviderModel[] = getActiveAgyModels(),
|
|
236
373
|
): () => void {
|
|
237
|
-
const
|
|
238
|
-
|
|
374
|
+
const selector = getAgyModelSelector(request.route.modelId, request.route.reasoning, models);
|
|
375
|
+
if (!selector) {
|
|
376
|
+
handlers.onError(
|
|
377
|
+
`Antigravity has no verified selector for ${request.route.modelId}${request.route.reasoning ? ` / ${request.route.reasoning}` : ""}. Refresh models and try again.`,
|
|
378
|
+
);
|
|
379
|
+
return () => undefined;
|
|
380
|
+
}
|
|
381
|
+
const spawnSpec = buildSpawnSpec(executable, ["--model", selector, "-p", request.prompt], platform);
|
|
239
382
|
|
|
240
383
|
const runner = runCommandImpl(
|
|
241
384
|
{
|
|
242
385
|
executable: spawnSpec.executable,
|
|
243
386
|
args: spawnSpec.args,
|
|
244
387
|
cwd: request.workspaceRoot,
|
|
245
|
-
env,
|
|
388
|
+
env: { ...process.env },
|
|
246
389
|
timeoutMs: ANTIGRAVITY_TIMEOUT_MS,
|
|
247
390
|
},
|
|
248
391
|
);
|
|
@@ -288,12 +431,36 @@ export const antigravityRuntime: ProviderRuntime = {
|
|
|
288
431
|
cwd: workspaceRoot,
|
|
289
432
|
configuredPath: antigravityCommandPath ?? null,
|
|
290
433
|
}),
|
|
291
|
-
discoverModels: (): ProviderModelDiscoveryResult =>
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
434
|
+
discoverModels: (): ProviderModelDiscoveryResult => {
|
|
435
|
+
const models = getActiveAgyModels();
|
|
436
|
+
return {
|
|
437
|
+
status: models.length > 0 ? "ready" : "not-configured",
|
|
438
|
+
providerId: "antigravity",
|
|
439
|
+
backendKind: models.length > 0 ? "antigravity-cli-auth" : "unavailable",
|
|
440
|
+
models,
|
|
441
|
+
...(models.length === 0 ? { message: "Antigravity model metadata is unavailable. Run Refresh models." } : {}),
|
|
442
|
+
};
|
|
443
|
+
},
|
|
444
|
+
refreshModels: async ({ cwd }): Promise<ProviderModelDiscoveryResult> => {
|
|
445
|
+
let executable = resolvedAgyExecutable;
|
|
446
|
+
try {
|
|
447
|
+
executable = await resolveAgyExecutable({ cwd });
|
|
448
|
+
resolvedAgyExecutable = executable;
|
|
449
|
+
} catch {
|
|
450
|
+
const cached = loadCachedProviderModels("antigravity")?.models ?? [];
|
|
451
|
+
return {
|
|
452
|
+
status: cached.length > 0 ? "ready" : "not-configured",
|
|
453
|
+
providerId: "antigravity",
|
|
454
|
+
backendKind: cached.length > 0 ? "antigravity-cli-auth" : "unavailable",
|
|
455
|
+
models: cached,
|
|
456
|
+
message: cached.length > 0
|
|
457
|
+
? "Antigravity CLI is unavailable; using the last successful model discovery."
|
|
458
|
+
: ANTIGRAVITY_ROUTE_SETUP_MESSAGE,
|
|
459
|
+
diagnostics: { modelSource: cached.length > 0 ? "cache" : "unavailable", resolvedCommand: null },
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
return discoverAgyModels({ executable, cwd, runCommandImpl: runCommand, platform: process.platform });
|
|
463
|
+
},
|
|
297
464
|
run: (request: ProviderChatRequest, handlers: BackendRunHandlers) => {
|
|
298
465
|
handlers.onProgress?.({
|
|
299
466
|
id: "antigravity-route",
|