feat: add xAI as official provider (#865)

* feat: add xAI as official provider

- Add xAI preset to ProviderManager (alphabetical order)
- Add xAI provider detection via XAI_API_KEY
- Add xAI startup screen heuristic (x.ai base URL or grok model)
- Add xAI status display properties
- Add grok-4 and grok-3 context windows
- Add xAI model fallbacks across all tiers
- Fix JSDoc priority order in providerAutoDetect

Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev>

* fix(xai): persist relaunch classification for xAI profiles

Addresses reviewer feedback on feat/xai-official-provider:
- isProcessEnvAlignedWithProfile now validates XAI_API_KEY for x.ai
  base URLs, mirroring the Bankr pattern. Without this, relaunch
  skips re-applying the profile, XAI_API_KEY stays unset, and
  getAPIProvider() falls back to 'openai'.
- buildOpenAICompatibleStartupEnv now sets XAI_API_KEY when syncing
  active xAI profile to the legacy fallback file.
- Adds 'xai' to VALID_PROVIDERS and --provider xai CLI flag support.
- Adds xAI detection to providerDiscovery label heuristics.
- Adds 'xai' to legacy ProviderProfile type/isProviderProfile guard.
- Adds targeted tests for relaunch alignment, flag application, and
  discovery labeling.

Co-Authored-By: OpenClaude <openclaude@gitlawb.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@openclaude.dev>
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
This commit is contained in:
Kevin Codex
2026-04-26 21:26:44 +08:00
committed by GitHub
parent d45628c413
commit 2586a9cddb
18 changed files with 248 additions and 7 deletions

View File

@@ -71,6 +71,10 @@ export function getSmallFastModel(): ModelName {
if (getAPIProvider() === 'minimax') {
return process.env.OPENAI_MODEL || 'MiniMax-M2.5-highspeed'
}
// xAI — OPENAI_MODEL carries the active Grok model; fall back to grok-3.
if (getAPIProvider() === 'xai') {
return process.env.OPENAI_MODEL || 'grok-3'
}
return getDefaultHaikuModel()
}
@@ -119,7 +123,8 @@ export function getUserSpecifiedModelSetting(): ModelSetting | undefined {
provider === 'codex' ||
provider === 'github' ||
provider === 'nvidia-nim' ||
provider === 'minimax'
provider === 'minimax' ||
provider === 'xai'
specifiedModel =
(provider === 'gemini' ? process.env.GEMINI_MODEL : undefined) ||
(provider === 'mistral' ? process.env.MISTRAL_MODEL : undefined) ||
@@ -194,6 +199,10 @@ export function getDefaultOpusModel(): ModelName {
if (getAPIProvider() === 'minimax') {
return process.env.OPENAI_MODEL || 'MiniMax-M2.7'
}
// xAI — flagship Grok model for "opus"-equivalent.
if (getAPIProvider() === 'xai') {
return process.env.OPENAI_MODEL || 'grok-4'
}
// 3P providers (Bedrock, Vertex, Foundry) — kept as a separate branch
// even when values match, since 3P availability lags firstParty and
// these will diverge again at the next model launch.
@@ -236,6 +245,10 @@ export function getDefaultSonnetModel(): ModelName {
if (getAPIProvider() === 'minimax') {
return process.env.OPENAI_MODEL || 'MiniMax-M2.5'
}
// xAI — flagship Grok model for "sonnet"-equivalent.
if (getAPIProvider() === 'xai') {
return process.env.OPENAI_MODEL || 'grok-4'
}
// Default to Sonnet 4.5 for 3P since they may not have 4.6 yet
if (getAPIProvider() !== 'firstParty') {
return getModelStrings().sonnet45
@@ -276,6 +289,10 @@ export function getDefaultHaikuModel(): ModelName {
if (getAPIProvider() === 'minimax') {
return process.env.OPENAI_MODEL || 'MiniMax-M2.5-highspeed'
}
// xAI — faster Grok model for "haiku"-equivalent.
if (getAPIProvider() === 'xai') {
return process.env.OPENAI_MODEL || 'grok-3'
}
// Haiku 4.5 is available on all platforms (first-party, Foundry, Bedrock, Vertex)
return getModelStrings().haiku45
@@ -344,6 +361,10 @@ export function getDefaultMainLoopModelSetting(): ModelName | ModelAlias {
if (getAPIProvider() === 'codex') {
return process.env.OPENAI_MODEL || 'gpt-5.5'
}
// xAI provider: always use the configured Grok model (default grok-4)
if (getAPIProvider() === 'xai') {
return process.env.OPENAI_MODEL || 'grok-4'
}
// Ants default to defaultModel from flag config, or Opus 1M if not configured
if (process.env.USER_TYPE === 'ant') {
@@ -524,7 +545,7 @@ export function renderModelSetting(setting: ModelName | ModelAlias): string {
*/
export function getPublicModelDisplayName(model: ModelName): string | null {
// For OpenAI/Gemini/Codex/GitHub providers, show the actual model name not a Claude alias
if (getAPIProvider() === 'openai' || getAPIProvider() === 'gemini' || getAPIProvider() === 'codex' || getAPIProvider() === 'github') {
if (getAPIProvider() === 'openai' || getAPIProvider() === 'gemini' || getAPIProvider() === 'codex' || getAPIProvider() === 'github' || getAPIProvider() === 'xai') {
// Return display names for known GitHub Copilot models
const copilotModelNames: Record<string, string> = {
'gpt-5.5': 'GPT-5.5',