import type { ModelInfo } from './types'; import { OPENCODE_SERVER_URL } from './opencode/server-manager'; // The Claude harness runs the `claude` CLI, so its tiers are a fixed set. const CLAUDE_CODE_MODELS: ModelInfo[] = [ { id: 'claude-code/opus', name: 'opus', provider: 'claude-code', contextWindow: 200000, maxTokens: 16000, reasoning: true, images: true }, { id: 'claude-code/sonnet', name: 'sonnet', provider: 'claude-code', contextWindow: 200000, maxTokens: 16000, reasoning: true, images: true }, { id: 'claude-code/haiku', name: 'haiku', provider: 'claude-code', contextWindow: 200000, maxTokens: 8192, reasoning: false, images: true }, ]; // Cache the OpenCode catalog; it's stable for a session. let openCodeCache: ModelInfo[] | null = null; export function invalidateModelCache(): void { openCodeCache = null; } type ProvidersResponse = { providers?: Array<{ id?: string; models?: Record }>; }; // Enumerate OpenCode models from the fixed server's GET /config/providers (reliable — no subprocess). // Model ids are `providerID/modelID` (e.g. `opencode/claude-haiku-4-5`); metadata is left at neutral // defaults for now. async function listOpenCodeModels(): Promise { if (openCodeCache) return openCodeCache; try { const res = await fetch(`${OPENCODE_SERVER_URL}/config/providers`, { signal: AbortSignal.timeout(5000) }); if (!res.ok) return []; const data = (await res.json()) as ProvidersResponse; const models: ModelInfo[] = []; for (const provider of data.providers ?? []) { const providerId = provider.id ?? ''; for (const modelId of Object.keys(provider.models ?? {})) { models.push({ id: `${providerId}/${modelId}`, name: modelId, provider: providerId, contextWindow: 200000, maxTokens: 8192, reasoning: false, images: true, }); } } openCodeCache = models; return models; } catch { return []; } } export async function listChatModels(): Promise { const openCode = await listOpenCodeModels(); return [...CLAUDE_CODE_MODELS, ...openCode]; }