From 88ec20a5117e9a3d5391f75cc5d361634be67e17 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 6 Feb 2026 12:14:52 +0700 Subject: [PATCH] Use system Claude binary for model listing The bundled SDK binary had stale model definitions. Resolve the system `claude` path once and pass it via pathToClaudeCodeExecutable so both agent sessions and supportedModels() use the up-to-date binary. Replace the CLI's hardcoded static model list with a live call to the daemon's listProviderModels endpoint. --- packages/cli/src/commands/provider/models.ts | 67 ++++++++++--------- .../server/agent/providers/claude-agent.ts | 20 ++++-- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/packages/cli/src/commands/provider/models.ts b/packages/cli/src/commands/provider/models.ts index f1f7ef852..62637b881 100644 --- a/packages/cli/src/commands/provider/models.ts +++ b/packages/cli/src/commands/provider/models.ts @@ -1,39 +1,23 @@ import type { Command } from 'commander' -import type { CommandOptions, ListResult, OutputSchema, CommandError } from '../../output/index.js' +import { connectToDaemon } from '../../utils/client.js' +import type { CommandOptions, ListResult, OutputSchema } from '../../output/index.js' /** Model list item for display */ export interface ModelListItem { model: string id: string + description: string } -/** Static model data by provider */ -const MODELS_BY_PROVIDER: Record = { - claude: [ - { model: 'Claude Sonnet 4', id: 'claude-sonnet-4-20250514' }, - { model: 'Claude Opus 4', id: 'claude-opus-4-20250514' }, - { model: 'Claude Haiku 3.5', id: 'claude-3-5-haiku-20241022' }, - ], - codex: [ - { model: 'o3-mini', id: 'o3-mini' }, - { model: 'o4-mini', id: 'o4-mini' }, - ], - opencode: [ - // opencode uses claude or codex under the hood - { model: 'Claude Sonnet 4', id: 'claude-sonnet-4-20250514' }, - { model: 'Claude Opus 4', id: 'claude-opus-4-20250514' }, - { model: 'Claude Haiku 3.5', id: 'claude-3-5-haiku-20241022' }, - { model: 'o3-mini', id: 'o3-mini' }, - { model: 'o4-mini', id: 'o4-mini' }, - ], -} +const VALID_PROVIDERS = ['claude', 'codex', 'opencode'] /** Schema for provider models output */ export const providerModelsSchema: OutputSchema = { idField: 'id', columns: [ - { header: 'MODEL', field: 'model', width: 30 }, { header: 'ID', field: 'id', width: 30 }, + { header: 'MODEL', field: 'model', width: 30 }, + { header: 'DESCRIPTION', field: 'description', width: 40 }, ], } @@ -45,25 +29,42 @@ export interface ProviderModelsOptions extends CommandOptions { export async function runModelsCommand( provider: string, - _options: ProviderModelsOptions, + options: ProviderModelsOptions, _command: Command ): Promise { const normalizedProvider = provider.toLowerCase() - const models = MODELS_BY_PROVIDER[normalizedProvider] - if (!models) { - const validProviders = Object.keys(MODELS_BY_PROVIDER).join(', ') - const error: CommandError = { + if (!VALID_PROVIDERS.includes(normalizedProvider)) { + throw { code: 'UNKNOWN_PROVIDER', message: `Unknown provider: ${provider}`, - details: `Valid providers: ${validProviders}`, + details: `Valid providers: ${VALID_PROVIDERS.join(', ')}`, } - throw error } - return { - type: 'list', - data: models, - schema: providerModelsSchema, + const client = await connectToDaemon({ host: options.host }) + try { + const result = await client.listProviderModels(normalizedProvider) + + if (result.error) { + throw { + code: 'PROVIDER_ERROR', + message: `Failed to fetch models for ${provider}: ${result.error}`, + } + } + + const models: ModelListItem[] = (result.models ?? []).map((m) => ({ + model: m.label, + id: m.id, + description: m.description ?? '', + })) + + return { + type: 'list', + data: models, + schema: providerModelsSchema, + } + } finally { + await client.close() } } diff --git a/packages/server/src/server/agent/providers/claude-agent.ts b/packages/server/src/server/agent/providers/claude-agent.ts index 47dbed1da..d23ff8c17 100644 --- a/packages/server/src/server/agent/providers/claude-agent.ts +++ b/packages/server/src/server/agent/providers/claude-agent.ts @@ -113,6 +113,7 @@ type ClaudeAgentClientOptions = { type ClaudeAgentSessionOptions = { defaults?: { agents?: Record }; + claudePath: string | null; handle?: AgentPersistenceHandle; logger: Logger; }; @@ -315,16 +316,23 @@ export class ClaudeAgentClient implements AgentClient { private readonly defaults?: { agents?: Record }; private readonly logger: Logger; + private readonly claudePath: string | null; constructor(options: ClaudeAgentClientOptions) { this.defaults = options.defaults; this.logger = options.logger.child({ module: "agent", provider: "claude" }); + try { + this.claudePath = execSync("which claude", { encoding: "utf8" }).trim() || null; + } catch { + this.claudePath = null; + } } async createSession(config: AgentSessionConfig): Promise { const claudeConfig = this.assertConfig(config); return new ClaudeAgentSession(claudeConfig, { defaults: this.defaults, + claudePath: this.claudePath, logger: this.logger, }); } @@ -342,6 +350,7 @@ export class ClaudeAgentClient implements AgentClient { const claudeConfig = this.assertConfig(mergedConfig); return new ClaudeAgentSession(claudeConfig, { defaults: this.defaults, + claudePath: this.claudePath, handle, logger: this.logger, }); @@ -353,6 +362,7 @@ export class ClaudeAgentClient implements AgentClient { cwd: options?.cwd ?? process.cwd(), permissionMode: "plan", includePartialMessages: false, + ...(this.claudePath ? { pathToClaudeCodeExecutable: this.claudePath } : {}), }; const claudeQuery = query({ prompt, options: claudeOptions }); @@ -407,12 +417,7 @@ export class ClaudeAgentClient implements AgentClient { } async isAvailable(): Promise { - try { - const claudePath = execSync("which claude", { encoding: "utf8" }).trim(); - return Boolean(claudePath); - } catch { - return false; - } + return this.claudePath !== null; } private assertConfig(config: AgentSessionConfig): ClaudeAgentConfig { @@ -429,6 +434,7 @@ class ClaudeAgentSession implements AgentSession { private readonly config: ClaudeAgentConfig; private readonly defaults?: { agents?: Record }; + private readonly claudePath: string | null; private readonly logger: Logger; private query: Query | null = null; private input: Pushable | null = null; @@ -464,6 +470,7 @@ class ClaudeAgentSession implements AgentSession { ) { this.config = config; this.defaults = options.defaults; + this.claudePath = options.claudePath; this.logger = options.logger; const handle = options.handle; @@ -880,6 +887,7 @@ class ClaudeAgentSession implements AgentSession { permissionMode: this.currentMode, agents: this.defaults?.agents, canUseTool: this.handlePermissionRequest, + ...(this.claudePath ? { pathToClaudeCodeExecutable: this.claudePath } : {}), // Use Claude Code preset system prompt and load CLAUDE.md files // Append orchestrator mode instructions for agents systemPrompt: {