diff --git a/packages/fold-agent/src/Config/ModelSelections.ts b/packages/fold-agent/src/Config/ModelSelections.ts index f3067c0..3df12c8 100644 --- a/packages/fold-agent/src/Config/ModelSelections.ts +++ b/packages/fold-agent/src/Config/ModelSelections.ts @@ -1,8 +1,8 @@ import { DEFAULT_CODEX_MODEL_ID } from '@humanlayer/fold-codex' import { DEFAULT_ANTHROPIC_MODEL_ID, type ModelCatalogEntry, type FoldModel } from '@humanlayer/fold-core' import { DEFAULT_OPENCODE_MODEL_ID, GROK_BUILD_MODEL_ID } from '@humanlayer/fold-opencode' -import { DEFAULT_XAI_MODEL_ID } from '@humanlayer/fold-xai' -import { Predicate, Effect, Match } from 'effect' +import { DEFAULT_XAI_MODEL_ID, XAI_FRONTIER_MODELS } from '@humanlayer/fold-xai' +import { Effect, Match, Predicate } from 'effect' import { agentModelsFromConfig, type AgentModelsOptions, RoleResolutionError } from './AgentModels' import type { ConfigRole, ProfileConfig, ProfileModeName, RoleBinding, FoldConfig } from './ConfigSchema' @@ -77,7 +77,7 @@ export const describeModelConfiguration = ( const defaultModels = Match.value(provider.kind).pipe( Match.when('codex', () => [DEFAULT_OPENCODE_MODEL_ID]), Match.when('opencode', () => [DEFAULT_OPENCODE_MODEL_ID, GROK_BUILD_MODEL_ID]), - Match.when('xai', () => ['grok-4.5', DEFAULT_XAI_MODEL_ID]), + Match.when('xai', () => XAI_FRONTIER_MODELS.map(({ modelId }) => modelId)), Match.orElse((): ReadonlyArray => []), ) const models = [ diff --git a/packages/fold-xai/src/XaiModel.ts b/packages/fold-xai/src/XaiModel.ts index 3465ac5..931a223 100644 --- a/packages/fold-xai/src/XaiModel.ts +++ b/packages/fold-xai/src/XaiModel.ts @@ -10,9 +10,9 @@ import { FetchHttpClient, HttpClient } from 'effect/unstable/http' import type { XaiAuthStore } from './AuthStore' import { makeXaiAuth, withXaiAuth } from './XaiAuth' +import { DEFAULT_XAI_MODEL_ID } from './XaiModelCatalog' export const XAI_API_URL = 'https://api.x.ai/v1' -export const DEFAULT_XAI_MODEL_ID = 'grok-4.6' export type XaiModelOptions = { readonly model?: string diff --git a/packages/fold-xai/src/XaiModelCatalog.ts b/packages/fold-xai/src/XaiModelCatalog.ts new file mode 100644 index 0000000..fe017b8 --- /dev/null +++ b/packages/fold-xai/src/XaiModelCatalog.ts @@ -0,0 +1,21 @@ +import { Schema } from 'effect' + +/** Model IDs verified for xAI subscription-backed OAuth sessions. */ +export const XaiFrontierModelId = Schema.Literals(['grok-4.5', 'grok-4.6']) +export type XaiFrontierModelId = typeof XaiFrontierModelId.Type + +/** One model exposed to embedding hosts for validation and selection controls. */ +export const XaiFrontierModel = Schema.Struct({ + modelId: XaiFrontierModelId, + label: Schema.NonEmptyString, +}) +export type XaiFrontierModel = typeof XaiFrontierModel.Type + +/** Frontier Grok models supported by Fold's xAI OAuth transport. */ +export const XAI_FRONTIER_MODELS = [ + { modelId: 'grok-4.5', label: 'Grok 4.5' }, + { modelId: 'grok-4.6', label: 'Grok 4.6' }, +] as const satisfies ReadonlyArray + +/** The model selected when an embedding host does not provide one. */ +export const DEFAULT_XAI_MODEL_ID: XaiFrontierModelId = 'grok-4.6' diff --git a/packages/fold-xai/src/index.ts b/packages/fold-xai/src/index.ts index 413bc30..45ecb09 100644 --- a/packages/fold-xai/src/index.ts +++ b/packages/fold-xai/src/index.ts @@ -2,3 +2,4 @@ export * from './AuthStore' export * from './OAuthFlows' export * from './XaiAuth' export * from './XaiModel' +export * from './XaiModelCatalog' diff --git a/packages/fold-xai/test/Xai.vi.test.ts b/packages/fold-xai/test/Xai.vi.test.ts index d26c954..f86359b 100644 --- a/packages/fold-xai/test/Xai.vi.test.ts +++ b/packages/fold-xai/test/Xai.vi.test.ts @@ -2,15 +2,17 @@ import { readdir, rm } from 'node:fs/promises' import * as NodeFileSystem from '@effect/platform-node/NodeFileSystem' import { describe, expect, it } from '@effect/vitest' -import { Effect, Option } from 'effect' +import { Effect, Option, Schema } from 'effect' import { buildXaiAuthorizeUrl, DEFAULT_XAI_MODEL_ID, makeXaiAuthStore, + XAI_FRONTIER_MODELS, XAI_BROWSER_REDIRECT_URI, XAI_CLIENT_ID, xaiModel, + XaiFrontierModelId, XaiTokenData, } from '../src/index' @@ -48,6 +50,17 @@ describe('xAI OAuth', () => { }) describe('xaiModel', () => { + it('exports the supported frontier catalog and defaults to its newest model', () => { + expect(XAI_FRONTIER_MODELS).toEqual([ + { modelId: 'grok-4.5', label: 'Grok 4.5' }, + { modelId: 'grok-4.6', label: 'Grok 4.6' }, + ]) + expect(XAI_FRONTIER_MODELS.at(-1)?.modelId).toBe(DEFAULT_XAI_MODEL_ID) + expect(Schema.is(XaiFrontierModelId)('grok-4.5')).toBe(true) + expect(Schema.is(XaiFrontierModelId)('grok-4.6')).toBe(true) + expect(Schema.is(XaiFrontierModelId)('grok-unverified')).toBe(false) + }) + it('returns a FoldModel-compatible OpenAI snapshot', () => { const model = xaiModel() expect(model.activeModel).toMatchObject({