refactor(providers): extract provider management to dedicated module

- Create src/agent/providers/ with registry.ts and resolver.ts
- registry.ts: Provider metadata, status checking, login instructions
- resolver.ts: API key resolution, model resolution
- oauth/providers.ts now re-exports from providers/ (deprecated)
- tools.ts: Remove PROVIDER_ALIAS and DEFAULT_MODELS (moved to providers/)
- Update imports in runner.ts and chat.ts

This separates concerns:
- oauth/ only handles OAuth credential reading
- providers/ manages all provider metadata and resolution
This commit is contained in:
Jiang Bohan 2026-02-02 17:20:02 +08:00
parent 5952f22ca2
commit 6723aa8561
7 changed files with 503 additions and 382 deletions

View file

@ -17,7 +17,7 @@ import {
getCurrentProvider,
getLoginInstructions,
type ProviderInfo,
} from "../../oauth/providers.js";
} from "../../providers/index.js";
type ChatOptions = {
profile?: string;

View file

@ -1,292 +1,20 @@
/**
* Provider Management
* @deprecated This file is deprecated. Import from '../providers/index.js' instead.
*
* Manage LLM providers with support for:
* - API Key authentication (traditional)
* - OAuth authentication (Claude Code, Codex)
* This file re-exports from the new providers/ module for backwards compatibility.
* Will be removed in a future version.
*/
import { credentialManager } from "../credentials.js";
import {
readClaudeCliCredentials,
readCodexCliCredentials,
hasValidClaudeCliCredentials,
hasValidCodexCliCredentials,
type ClaudeCliCredential,
type CodexCliCredential,
} from "./cli-credentials.js";
// ============================================================
// Types
// ============================================================
export type AuthMethod = "api-key" | "oauth";
export interface ProviderInfo {
id: string;
name: string;
authMethod: AuthMethod;
available: boolean;
configured: boolean;
current: boolean;
models: string[];
loginUrl?: string;
loginCommand?: string;
}
export interface ProviderConfig {
provider: string;
model?: string | undefined;
apiKey?: string | undefined;
baseUrl?: string | undefined;
// OAuth specific
accessToken?: string | undefined;
refreshToken?: string | undefined;
expires?: number | undefined;
}
// ============================================================
// Provider Registry
// ============================================================
const PROVIDER_INFO: Record<string, Omit<ProviderInfo, "available" | "configured" | "current">> = {
"anthropic": {
id: "anthropic",
name: "Anthropic (API Key)",
authMethod: "api-key",
models: ["claude-sonnet-4-20250514", "claude-opus-4-20250514", "claude-haiku-3-5-20241022"],
loginUrl: "https://console.anthropic.com/",
},
"claude-code": {
id: "claude-code",
name: "Claude Code (OAuth)",
authMethod: "oauth",
models: ["claude-sonnet-4-20250514", "claude-opus-4-20250514"],
loginCommand: "claude login",
},
"openai": {
id: "openai",
name: "OpenAI",
authMethod: "api-key",
models: ["gpt-4o", "gpt-4o-mini", "o1", "o1-mini"],
loginUrl: "https://platform.openai.com/api-keys",
},
"openai-codex": {
id: "openai-codex",
name: "Codex (OAuth)",
authMethod: "oauth",
models: ["gpt-5.1", "gpt-5.1-codex-max"],
loginCommand: "codex login",
},
"kimi-coding": {
id: "kimi-coding",
name: "Kimi Code",
authMethod: "api-key",
models: ["kimi-k2-thinking", "k2p5"],
loginUrl: "https://kimi.moonshot.cn/",
},
"google": {
id: "google",
name: "Google AI",
authMethod: "api-key",
models: ["gemini-2.0-flash", "gemini-1.5-pro"],
loginUrl: "https://aistudio.google.com/apikey",
},
"groq": {
id: "groq",
name: "Groq",
authMethod: "api-key",
models: ["llama-3.3-70b-versatile", "mixtral-8x7b-32768"],
loginUrl: "https://console.groq.com/keys",
},
"mistral": {
id: "mistral",
name: "Mistral",
authMethod: "api-key",
models: ["mistral-large-latest", "codestral-latest"],
loginUrl: "https://console.mistral.ai/api-keys",
},
"xai": {
id: "xai",
name: "xAI (Grok)",
authMethod: "api-key",
models: ["grok-beta", "grok-vision-beta"],
loginUrl: "https://console.x.ai/",
},
"openrouter": {
id: "openrouter",
name: "OpenRouter",
authMethod: "api-key",
models: ["anthropic/claude-3.5-sonnet", "openai/gpt-4o"],
loginUrl: "https://openrouter.ai/keys",
},
};
// ============================================================
// Provider Status
// ============================================================
/**
* Check if a provider is configured with API key in credentials.json5
*/
function isApiKeyConfigured(providerId: string): boolean {
const config = credentialManager.getLlmProviderConfig(providerId);
return !!config?.apiKey;
}
/**
* Check if OAuth provider has valid credentials
*/
function isOAuthAvailable(providerId: string): boolean {
if (providerId === "claude-code") {
return hasValidClaudeCliCredentials();
}
if (providerId === "openai-codex") {
return hasValidCodexCliCredentials();
}
return false;
}
/**
* Get current provider from credentials
*/
export function getCurrentProvider(): string {
return credentialManager.getLlmProvider() ?? "kimi-coding";
}
/**
* Get list of all providers with their status
*/
export function getProviderList(): ProviderInfo[] {
const currentProvider = getCurrentProvider();
return Object.values(PROVIDER_INFO).map((info) => {
const isOAuth = info.authMethod === "oauth";
const available = isOAuth ? isOAuthAvailable(info.id) : isApiKeyConfigured(info.id);
const configured = isOAuth ? isOAuthAvailable(info.id) : isApiKeyConfigured(info.id);
// Check if this is the current provider
// For claude-code, check if current is "anthropic" and OAuth is available
let isCurrent = currentProvider === info.id;
if (info.id === "claude-code" && currentProvider === "anthropic") {
// If anthropic is current and claude-code OAuth is available, mark both
isCurrent = hasValidClaudeCliCredentials();
}
return {
...info,
available,
configured,
current: isCurrent,
};
});
}
/**
* Get available providers only
*/
export function getAvailableProviders(): ProviderInfo[] {
return getProviderList().filter((p) => p.available);
}
// ============================================================
// Provider Resolution
// ============================================================
/**
* Get provider config for making API calls
*/
export function resolveProviderConfig(providerId: string): ProviderConfig | null {
const info = PROVIDER_INFO[providerId];
if (!info) return null;
if (info.authMethod === "oauth") {
if (providerId === "claude-code") {
const creds = readClaudeCliCredentials();
if (!creds) return null;
const accessToken = creds.type === "oauth" ? creds.access : creds.token;
return {
provider: "anthropic", // Use anthropic API
apiKey: accessToken,
accessToken,
refreshToken: creds.type === "oauth" ? creds.refresh : undefined,
expires: creds.expires,
};
}
if (providerId === "openai-codex") {
const creds = readCodexCliCredentials();
if (!creds) return null;
return {
provider: "openai-codex",
accessToken: creds.access,
refreshToken: creds.refresh,
expires: creds.expires,
};
}
}
// API Key based
const config = credentialManager.getLlmProviderConfig(providerId);
if (!config?.apiKey) return null;
return {
provider: providerId,
model: config.model,
apiKey: config.apiKey,
baseUrl: config.baseUrl,
};
}
/**
* Format provider for display
*/
export function formatProviderStatus(provider: ProviderInfo): string {
const status = provider.available ? "✓" : "✗";
const current = provider.current ? " (current)" : "";
const auth = provider.authMethod === "oauth" ? " [OAuth]" : "";
return `${status} ${provider.name}${auth}${current}`;
}
/**
* Get login instructions for a provider
*/
export function getLoginInstructions(providerId: string): string {
const info = PROVIDER_INFO[providerId];
if (!info) return `Unknown provider: ${providerId}`;
if (info.authMethod === "oauth") {
if (info.loginCommand) {
return `Run: ${info.loginCommand}\nThen restart Super Multica to use the credentials.`;
}
}
if (info.loginUrl) {
return `Get your API key at: ${info.loginUrl}\nThen add it to ~/.super-multica/credentials.json5`;
}
return "No login instructions available.";
}
/**
* Check if a provider uses OAuth authentication
*/
export function isOAuthProvider(providerId: string): boolean {
const info = PROVIDER_INFO[providerId];
return info?.authMethod === "oauth";
}
/**
* Check if provider is available (has valid credentials)
*/
export function isProviderAvailable(providerId: string): boolean {
const info = PROVIDER_INFO[providerId];
if (!info) return false;
if (info.authMethod === "oauth") {
return isOAuthAvailable(providerId);
}
return isApiKeyConfigured(providerId);
}
export {
type AuthMethod,
type ProviderInfo,
type ProviderConfig,
isOAuthProvider,
isProviderAvailable,
getCurrentProvider,
getProviderList,
getAvailableProviders,
formatProviderStatus,
getLoginInstructions,
resolveProviderConfig,
} from "../providers/index.js";

View file

@ -0,0 +1,34 @@
/**
* Provider Management
*
* Unified exports for LLM provider management:
* - Registry: Provider metadata, status checking, listing
* - Resolver: API key resolution, model resolution
*/
// Registry exports
export {
type AuthMethod,
type ProviderInfo,
type ProviderMeta,
PROVIDER_ALIAS,
isOAuthProvider,
isProviderAvailable,
getCurrentProvider,
getProviderMeta,
getDefaultModel,
getProviderList,
getAvailableProviders,
formatProviderStatus,
getLoginInstructions,
} from "./registry.js";
// Resolver exports
export {
type ProviderConfig,
resolveProviderConfig,
resolveApiKey,
resolveBaseUrl,
resolveModelId,
resolveModel,
} from "./resolver.js";

View file

@ -0,0 +1,275 @@
/**
* Provider Registry
*
* Central registry for all LLM providers with metadata,
* status checking, and display formatting.
*/
import { credentialManager } from "../credentials.js";
import {
hasValidClaudeCliCredentials,
hasValidCodexCliCredentials,
} from "../oauth/cli-credentials.js";
// ============================================================
// Types
// ============================================================
export type AuthMethod = "api-key" | "oauth";
export interface ProviderInfo {
id: string;
name: string;
authMethod: AuthMethod;
available: boolean;
configured: boolean;
current: boolean;
defaultModel: string;
models: string[];
loginUrl?: string | undefined;
loginCommand?: string | undefined;
}
/** Static provider metadata (without runtime status) */
export interface ProviderMeta {
id: string;
name: string;
authMethod: AuthMethod;
defaultModel: string;
models: string[];
loginUrl?: string | undefined;
loginCommand?: string | undefined;
}
// ============================================================
// Provider Registry
// ============================================================
const PROVIDER_REGISTRY: Record<string, ProviderMeta> = {
"claude-code": {
id: "claude-code",
name: "Claude Code (OAuth)",
authMethod: "oauth",
defaultModel: "claude-sonnet-4-20250514",
models: ["claude-sonnet-4-20250514", "claude-opus-4-20250514"],
loginCommand: "claude login",
},
"openai-codex": {
id: "openai-codex",
name: "Codex (OAuth)",
authMethod: "oauth",
defaultModel: "gpt-5.1",
models: ["gpt-5.1", "gpt-5.1-codex-max"],
loginCommand: "codex login",
},
"anthropic": {
id: "anthropic",
name: "Anthropic (API Key)",
authMethod: "api-key",
defaultModel: "claude-sonnet-4-20250514",
models: ["claude-sonnet-4-20250514", "claude-opus-4-20250514", "claude-haiku-3-5-20241022"],
loginUrl: "https://console.anthropic.com/",
},
"openai": {
id: "openai",
name: "OpenAI",
authMethod: "api-key",
defaultModel: "gpt-4o",
models: ["gpt-4o", "gpt-4o-mini", "o1", "o1-mini"],
loginUrl: "https://platform.openai.com/api-keys",
},
"kimi-coding": {
id: "kimi-coding",
name: "Kimi Code",
authMethod: "api-key",
defaultModel: "kimi-k2-thinking",
models: ["kimi-k2-thinking", "k2p5"],
loginUrl: "https://kimi.moonshot.cn/",
},
"google": {
id: "google",
name: "Google AI",
authMethod: "api-key",
defaultModel: "gemini-2.0-flash",
models: ["gemini-2.0-flash", "gemini-1.5-pro"],
loginUrl: "https://aistudio.google.com/apikey",
},
"groq": {
id: "groq",
name: "Groq",
authMethod: "api-key",
defaultModel: "llama-3.3-70b-versatile",
models: ["llama-3.3-70b-versatile", "mixtral-8x7b-32768"],
loginUrl: "https://console.groq.com/keys",
},
"mistral": {
id: "mistral",
name: "Mistral",
authMethod: "api-key",
defaultModel: "mistral-large-latest",
models: ["mistral-large-latest", "codestral-latest"],
loginUrl: "https://console.mistral.ai/api-keys",
},
"xai": {
id: "xai",
name: "xAI (Grok)",
authMethod: "api-key",
defaultModel: "grok-beta",
models: ["grok-beta", "grok-vision-beta"],
loginUrl: "https://console.x.ai/",
},
"openrouter": {
id: "openrouter",
name: "OpenRouter",
authMethod: "api-key",
defaultModel: "anthropic/claude-3.5-sonnet",
models: ["anthropic/claude-3.5-sonnet", "openai/gpt-4o"],
loginUrl: "https://openrouter.ai/keys",
},
};
/**
* Provider alias mapping for OAuth providers.
* Maps friendly names to actual pi-ai provider names.
*/
export const PROVIDER_ALIAS: Record<string, string> = {
"claude-code": "anthropic", // Claude Code OAuth uses anthropic API
};
// ============================================================
// Status Checking
// ============================================================
/**
* Check if a provider is configured with API key in credentials.json5
*/
function isApiKeyConfigured(providerId: string): boolean {
const config = credentialManager.getLlmProviderConfig(providerId);
return !!config?.apiKey;
}
/**
* Check if OAuth provider has valid credentials
*/
function isOAuthAvailable(providerId: string): boolean {
if (providerId === "claude-code") {
return hasValidClaudeCliCredentials();
}
if (providerId === "openai-codex") {
return hasValidCodexCliCredentials();
}
return false;
}
/**
* Check if a provider uses OAuth authentication
*/
export function isOAuthProvider(providerId: string): boolean {
const info = PROVIDER_REGISTRY[providerId];
return info?.authMethod === "oauth";
}
/**
* Check if provider is available (has valid credentials)
*/
export function isProviderAvailable(providerId: string): boolean {
const info = PROVIDER_REGISTRY[providerId];
if (!info) return false;
if (info.authMethod === "oauth") {
return isOAuthAvailable(providerId);
}
return isApiKeyConfigured(providerId);
}
/**
* Get current provider from credentials
*/
export function getCurrentProvider(): string {
return credentialManager.getLlmProvider() ?? "kimi-coding";
}
// ============================================================
// Provider Listing
// ============================================================
/**
* Get static provider metadata
*/
export function getProviderMeta(providerId: string): ProviderMeta | undefined {
return PROVIDER_REGISTRY[providerId];
}
/**
* Get default model for a provider
*/
export function getDefaultModel(providerId: string): string | undefined {
return PROVIDER_REGISTRY[providerId]?.defaultModel;
}
/**
* Get list of all providers with their runtime status
*/
export function getProviderList(): ProviderInfo[] {
const currentProvider = getCurrentProvider();
return Object.values(PROVIDER_REGISTRY).map((meta) => {
const isOAuth = meta.authMethod === "oauth";
const available = isOAuth ? isOAuthAvailable(meta.id) : isApiKeyConfigured(meta.id);
// Check if this is the current provider
// For claude-code, check if current is "anthropic" and OAuth is available
let isCurrent = currentProvider === meta.id;
if (meta.id === "claude-code" && currentProvider === "anthropic") {
isCurrent = hasValidClaudeCliCredentials();
}
return {
...meta,
available,
configured: available,
current: isCurrent,
};
});
}
/**
* Get available providers only
*/
export function getAvailableProviders(): ProviderInfo[] {
return getProviderList().filter((p) => p.available);
}
// ============================================================
// Display Helpers
// ============================================================
/**
* Format provider for display
*/
export function formatProviderStatus(provider: ProviderInfo): string {
const status = provider.available ? "✓" : "✗";
const current = provider.current ? " (current)" : "";
const auth = provider.authMethod === "oauth" ? " [OAuth]" : "";
return `${status} ${provider.name}${auth}${current}`;
}
/**
* Get login instructions for a provider
*/
export function getLoginInstructions(providerId: string): string {
const info = PROVIDER_REGISTRY[providerId];
if (!info) return `Unknown provider: ${providerId}`;
if (info.authMethod === "oauth") {
if (info.loginCommand) {
return `Run: ${info.loginCommand}\nThen restart Super Multica to use the credentials.`;
}
}
if (info.loginUrl) {
return `Get your API key at: ${info.loginUrl}\nThen add it to ~/.super-multica/credentials.json5`;
}
return "No login instructions available.";
}

View file

@ -0,0 +1,166 @@
/**
* Provider Resolver
*
* Resolves provider configuration for making API calls,
* including API keys, OAuth tokens, and model selection.
*/
import { getModel } from "@mariozechner/pi-ai";
import { credentialManager } from "../credentials.js";
import {
readClaudeCliCredentials,
readCodexCliCredentials,
} from "../oauth/cli-credentials.js";
import {
PROVIDER_ALIAS,
getProviderMeta,
getDefaultModel,
isOAuthProvider,
} from "./registry.js";
import type { AgentOptions } from "../types.js";
// ============================================================
// Types
// ============================================================
export interface ProviderConfig {
provider: string;
model?: string | undefined;
apiKey?: string | undefined;
baseUrl?: string | undefined;
// OAuth specific
accessToken?: string | undefined;
refreshToken?: string | undefined;
expires?: number | undefined;
}
// ============================================================
// Provider Config Resolution
// ============================================================
/**
* Get provider config for making API calls.
* Handles both OAuth and API Key authentication.
*/
export function resolveProviderConfig(providerId: string): ProviderConfig | null {
const meta = getProviderMeta(providerId);
if (!meta) return null;
if (meta.authMethod === "oauth") {
if (providerId === "claude-code") {
const creds = readClaudeCliCredentials();
if (!creds) return null;
const accessToken = creds.type === "oauth" ? creds.access : creds.token;
return {
provider: "anthropic", // Use anthropic API
apiKey: accessToken,
accessToken,
refreshToken: creds.type === "oauth" ? creds.refresh : undefined,
expires: creds.expires,
};
}
if (providerId === "openai-codex") {
const creds = readCodexCliCredentials();
if (!creds) return null;
return {
provider: "openai-codex",
accessToken: creds.access,
refreshToken: creds.refresh,
expires: creds.expires,
};
}
}
// API Key based
const config = credentialManager.getLlmProviderConfig(providerId);
if (!config?.apiKey) return null;
return {
provider: providerId,
model: config.model,
apiKey: config.apiKey,
baseUrl: config.baseUrl,
};
}
// ============================================================
// API Key Resolution
// ============================================================
/**
* Get API Key based on provider.
* Priority: explicit key > OAuth credentials > credentials.json5 config.
*/
export function resolveApiKey(provider: string, explicitKey?: string): string | undefined {
if (explicitKey) return explicitKey;
// Try OAuth providers first (claude-code, openai-codex)
const providerConfig = resolveProviderConfig(provider);
if (providerConfig?.apiKey) {
return providerConfig.apiKey;
}
if (providerConfig?.accessToken) {
return providerConfig.accessToken;
}
// Fall back to credentials.json5
return credentialManager.getLlmProviderConfig(provider)?.apiKey;
}
/**
* Get Base URL based on provider.
* Priority: explicit URL > credentials.json5 config.
*/
export function resolveBaseUrl(provider: string, explicitUrl?: string): string | undefined {
if (explicitUrl) return explicitUrl;
return credentialManager.getLlmProviderConfig(provider)?.baseUrl;
}
/**
* Get Model ID based on provider.
* Priority: explicit model > credentials.json5 config > default.
*/
export function resolveModelId(provider: string, explicitModel?: string): string | undefined {
if (explicitModel) return explicitModel;
return credentialManager.getLlmProviderConfig(provider)?.model ?? getDefaultModel(provider);
}
// ============================================================
// Model Resolution
// ============================================================
/**
* Resolve model for pi-ai based on provider and options.
*/
export function resolveModel(options: AgentOptions) {
if (options.provider && options.model) {
// Map provider alias (e.g., claude-code -> anthropic)
const actualProvider = PROVIDER_ALIAS[options.provider] ?? options.provider;
// Type assertion needed because provider/model come from dynamic user config
return (getModel as (p: string, m: string) => ReturnType<typeof getModel>)(
actualProvider,
options.model,
);
}
// If only provider specified, use default model for that provider
if (options.provider) {
const actualProvider = PROVIDER_ALIAS[options.provider] ?? options.provider;
const defaultModel = getDefaultModel(options.provider) ?? getDefaultModel(actualProvider);
if (defaultModel) {
return (getModel as (p: string, m: string) => ReturnType<typeof getModel>)(
actualProvider,
defaultModel,
);
}
}
return getModel("kimi-coding", "kimi-k2-thinking");
}
// Re-export for convenience
export { isOAuthProvider };

View file

@ -7,7 +7,13 @@ import { SessionManager } from "./session/session-manager.js";
import { ProfileManager } from "./profile/index.js";
import { SkillManager } from "./skills/index.js";
import { credentialManager, getCredentialsPath } from "./credentials.js";
import { resolveProviderConfig, isOAuthProvider, getLoginInstructions } from "./oauth/providers.js";
import {
resolveApiKey,
resolveBaseUrl,
resolveModelId,
isOAuthProvider,
getLoginInstructions,
} from "./providers/index.js";
import {
checkContextWindow,
DEFAULT_CONTEXT_TOKENS,
@ -15,47 +21,6 @@ import {
} from "./context-window/index.js";
import { mergeToolsConfig, type ToolsConfig } from "./tools/policy.js";
/**
* Get API Key based on provider.
* Priority: explicit key > OAuth credentials > credentials.json5 config.
*
* Supports OAuth providers like "claude-code" and "openai-codex" by
* reading credentials from their respective CLI tools.
*/
function resolveApiKey(provider: string, explicitKey?: string): string | undefined {
if (explicitKey) return explicitKey;
// Try OAuth providers first (claude-code, openai-codex)
const providerConfig = resolveProviderConfig(provider);
if (providerConfig?.apiKey) {
return providerConfig.apiKey;
}
if (providerConfig?.accessToken) {
return providerConfig.accessToken;
}
// Fall back to credentials.json5
return credentialManager.getLlmProviderConfig(provider)?.apiKey;
}
/**
* Get Base URL based on provider.
* Priority: explicit URL > provider-specific env var > generic env var format.
*/
function resolveBaseUrl(provider: string, explicitUrl?: string): string | undefined {
if (explicitUrl) return explicitUrl;
return credentialManager.getLlmProviderConfig(provider)?.baseUrl;
}
/**
* Get Model ID based on provider.
* Priority: explicit model > provider-specific env var > generic env var format.
*/
function resolveModelId(provider: string, explicitModel?: string): string | undefined {
if (explicitModel) return explicitModel;
return credentialManager.getLlmProviderConfig(provider)?.model;
}
export class Agent {
private readonly agent: PiAgentCore;
private readonly output;

View file

@ -1,5 +1,4 @@
import type { AgentOptions } from "./types.js";
import { getModel } from "@mariozechner/pi-ai";
import { createCodingTools } from "@mariozechner/pi-coding-agent";
import type { AgentTool } from "@mariozechner/pi-agent-core";
import { createExecTool } from "./tools/exec.js";
@ -9,62 +8,16 @@ import { createWebFetchTool, createWebSearchTool } from "./tools/web/index.js";
import { createMemoryTools } from "./tools/memory/index.js";
import { filterTools } from "./tools/policy.js";
/**
* Provider alias mapping for OAuth providers.
* Maps friendly names to actual pi-ai provider names.
*/
const PROVIDER_ALIAS: Record<string, string> = {
"claude-code": "anthropic", // Claude Code OAuth uses anthropic API
};
/**
* Default models for each provider.
*/
const DEFAULT_MODELS: Record<string, string> = {
"anthropic": "claude-sonnet-4-20250514",
"claude-code": "claude-sonnet-4-20250514",
"openai": "gpt-4o",
"openai-codex": "gpt-5.1",
"kimi-coding": "kimi-k2-thinking",
"google": "gemini-2.0-flash",
"groq": "llama-3.3-70b-versatile",
"mistral": "mistral-large-latest",
};
export function resolveModel(options: AgentOptions) {
if (options.provider && options.model) {
// Map provider alias (e.g., claude-code -> anthropic)
const actualProvider = PROVIDER_ALIAS[options.provider] ?? options.provider;
// Type assertion needed because provider/model come from dynamic user config
return (getModel as (p: string, m: string) => ReturnType<typeof getModel>)(
actualProvider,
options.model,
);
}
// If only provider specified, use default model for that provider
if (options.provider) {
const actualProvider = PROVIDER_ALIAS[options.provider] ?? options.provider;
const defaultModel = DEFAULT_MODELS[options.provider] ?? DEFAULT_MODELS[actualProvider];
if (defaultModel) {
return (getModel as (p: string, m: string) => ReturnType<typeof getModel>)(
actualProvider,
defaultModel,
);
}
}
return getModel("kimi-coding", "kimi-k2-thinking");
}
// Re-export resolveModel from providers for backwards compatibility
export { resolveModel } from "./providers/index.js";
/** Options for creating tools */
export interface CreateToolsOptions {
cwd: string;
/** Profile ID for memory tools (optional) */
profileId?: string;
profileId?: string | undefined;
/** Base directory for profiles (optional) */
profileBaseDir?: string;
profileBaseDir?: string | undefined;
}
/**