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.
This commit is contained in:
Mohamed Boudra
2026-02-06 12:14:52 +07:00
parent 8dcb7233c0
commit 88ec20a511
2 changed files with 48 additions and 39 deletions

View File

@@ -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<string, ModelListItem[]> = {
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<ModelListItem> = {
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<ProviderModelsResult> {
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()
}
}

View File

@@ -113,6 +113,7 @@ type ClaudeAgentClientOptions = {
type ClaudeAgentSessionOptions = {
defaults?: { agents?: Record<string, AgentDefinition> };
claudePath: string | null;
handle?: AgentPersistenceHandle;
logger: Logger;
};
@@ -315,16 +316,23 @@ export class ClaudeAgentClient implements AgentClient {
private readonly defaults?: { agents?: Record<string, AgentDefinition> };
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<AgentSession> {
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<boolean> {
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<string, AgentDefinition> };
private readonly claudePath: string | null;
private readonly logger: Logger;
private query: Query | null = null;
private input: Pushable<SDKUserMessage> | 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: {