{"repo": "chaoyue0307/cycode", "files": {"examples/config.example.json": {"8b09d0a8cbca739a8d5598127b00edd3e5955fb5": {"t": 1785674676.7579262, "n": 888, "h": "7c2d9a6c721a5d1d", "c": "{\n \"model\": \"anthropic/claude-sonnet-4-6\",\n \"smallModel\": \"anthropic/claude-haiku-4-5-20251001\",\n \"permissions\": {\n \"allow\": [\"bash(git status)\", \"bash(git diff*)\", \"bash(npm run *)\", \"latex_build\"],\n \"deny\": [\"bash(rm -rf *)\"]\n },\n \"diagnostics\": { \"command\": \"npm run typecheck\", \"timeoutMs\": 60000 },\n \"mcpServers\": {},\n \"providers\": {\n \"anthropic\": { \"apiKey\": \"sk-ant-...\" },\n \"openai\": { \"apiKey\": \"sk-...\" },\n \"google\": { \"apiKey\": \"...\" },\n \"openrouter\": { \"apiKey\": \"sk-or-...\" },\n \"deepseek\": { \"apiKey\": \"sk-...\" },\n \"qwen\": { \"apiKey\": \"sk-...\" },\n \"zhipu\": { \"apiKey\": \"...\" },\n \"moonshot\": { \"apiKey\": \"sk-...\" },\n \"doubao\": { \"apiKey\": \"...\" },\n \"ernie\": { \"apiKey\": \"bce-v3/...\" },\n \"ollama\": { \"baseURL\": \"http://localhost:11434/v1\" },\n \"vllm\": { \"baseURL\": \"http://localhost:8000/v1\", \"apiKeyEnv\": \"VLLM_API_KEY\" }\n }\n}\n"}}, "src/provider/registry.ts": {"8b09d0a8cbca739a8d5598127b00edd3e5955fb5": {"t": 1785745603.9715075, "n": 7523, "h": "2456d9d236fc66c4", "c": "import { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\nimport { createGoogleGenerativeAI } from \"@ai-sdk/google\";\nimport { createOpenAICompatible } from \"@ai-sdk/openai-compatible\";\nimport type { LanguageModel } from \"ai\";\nimport type { CycodeConfig, ProviderConfig } from \"../config.js\";\n\n/** Providers with a native AI SDK package (key read from the default env var or config). */\nconst NATIVE = new Set([\"anthropic\", \"openai\", \"google\"]);\n\ninterface CompatibleProvider {\n baseURL: string;\n /** Default environment variable for the key (\"\" = no key required, e.g. local Ollama). */\n keyEnv: string;\n}\n\n/**\n * OpenAI-compatible built-in providers — their endpoint and default key env are baked\n * in, so users only set a key (or `--model /`). Chinese providers\n * default to their mainland endpoints; override `baseURL` in config for other regions\n * (e.g. DashScope Singapore, Moonshot/Zhipu international).\n */\nexport const COMPATIBLE: Record = {\n openrouter: { baseURL: \"https://openrouter.ai/api/v1\", keyEnv: \"OPENROUTER_API_KEY\" },\n ollama: { baseURL: \"http://localhost:11434/v1\", keyEnv: \"\" },\n // Chinese providers\n deepseek: { baseURL: \"https://api.deepseek.com/v1\", keyEnv: \"DEEPSEEK_API_KEY\" },\n qwen: { baseURL: \"https://dashscope.aliyuncs.com/compatible-mode/v1\", keyEnv: \"DASHSCOPE_API_KEY\" },\n zhipu: { baseURL: \"https://open.bigmodel.cn/api/paas/v4\", keyEnv: \"ZHIPUAI_API_KEY\" },\n moonshot: { baseURL: \"https://api.moonshot.cn/v1\", keyEnv: \"MOONSHOT_API_KEY\" },\n minimax: { baseURL: \"https://api.minimaxi.com/v1\", keyEnv: \"MINIMAX_API_KEY\" },\n hunyuan: { baseURL: \"https://api.hunyuan.cloud.tencent.com/v1\", keyEnv: \"HUNYUAN_API_KEY\" },\n doubao: { baseURL: \"https://ark.cn-beijing.volces.com/api/v3\", keyEnv: \"ARK_API_KEY\" },\n ernie: { baseURL: \"https://qianfan.baidubce.com/v2\", keyEnv: \"QIANFAN_API_KEY\" },\n siliconflow: { baseURL: \"https://api.siliconflow.cn/v1\", keyEnv: \"SILICONFLOW_API_KEY\" },\n stepfun: { baseURL: \"https://api.stepfun.com/v1\", keyEnv: \"STEPFUN_API_KEY\" },\n baichuan: { baseURL: \"https://api.baichuan-ai.com/v1\", keyEnv: \"BAICHUAN_API_KEY\" },\n yi: { baseURL: \"https://api.lingyiwanwu.com/v1\", keyEnv: \"YI_API_KEY\" },\n};\n\n/** Friendly aliases → canonical provider name. */\nexport const ALIASES: Record = {\n dashscope: \"qwen\",\n tongyi: \"qwen\",\n glm: \"zhipu\",\n zhipuai: \"zhipu\",\n kimi: \"moonshot\",\n ark: \"doubao\",\n volcengine: \"doubao\",\n qianfan: \"ernie\",\n wenxin: \"ernie\",\n step: \"stepfun\",\n \"01ai\": \"yi\",\n lingyiwanwu: \"yi\",\n};\n\nexport function canonicalProvider(name: string): string {\n return ALIASES[name] ?? name;\n}\n\n/** Default environment variable each built-in provider reads its key from. */\nexport const DEFAULT_KEY_ENV: Record = {\n anthropic: \"ANTHROPIC_API_KEY\",\n openai: \"OPENAI_API_KEY\",\n google: \"GOOGLE_GENERATIVE_AI_API_KEY\",\n ...Object.fromEntries(\n Object.entries(COMPATIBLE)\n .filter(([, p]) => p.keyEnv)\n .map(([name, p]) => [name, p.keyEnv]),\n ),\n};\n\n/** Providers that get a sensible default model when none is configured, in priority order. */\nconst DEFAULT_MODELS: [provider: string, modelId: string][] = [\n [\"anthropic\", \"claude-sonnet-4-6\"],\n [\"openai\", \"gpt-5.1\"],\n [\"google\", \"gemini-2.5-pro\"],\n [\"deepseek\", \"deepseek-chat\"],\n [\"qwen\", \"qwen-max\"],\n [\"zhipu\", \"glm-4.6\"],\n [\"moonshot\", \"kimi-k2\"],\n [\"openrouter\", \"anthropic/claude-sonnet-4-6\"],\n];\n\n/**\n * Resolve an API key for a provider. Precedence:\n * config providers..apiKeyEnv (env var) > providers..apiKey (literal)\n * > the provider's default env var.\n */\nexport function resolveApiKey(\n provider: string,\n custom: ProviderConfig | undefined,\n): string | undefined {\n if (custom?.apiKeyEnv && process.env[custom.apiKeyEnv]) {\n return process.env[custom.apiKeyEnv];\n }\n if (custom?.apiKey) return custom.apiKey;\n const defaultEnv = DEFAULT_KEY_ENV[provider];\n if (defaultEnv && process.env[defaultEnv]) return process.env[defaultEnv];\n return undefined;\n}\n\n/** Whether a usable key (or no key needed, e.g. ollama) is available for a provider. */\nexport function hasKey(provider: string, config: CycodeConfig): boolean {\n const name = canonicalProvider(provider);\n if (COMPATIBLE[name]?.keyEnv === \"\") return true;\n return resolveApiKey(name, config.providers?.[name]) !== undefined;\n}\n\n/**\n * Model specs are \"provider/model-id\", e.g. anthropic/claude-sonnet-4-6,\n * openai/gpt-5.1, deepseek/deepseek-chat, qwen/qwen-max, zhipu/glm-4.6,\n * moonshot/kimi-k2, ollama/llama3.3. Any provider's key and baseURL can be\n * overridden under config.providers..\n */\nexport function resolveModel(spec: string, config: CycodeConfig): LanguageModel {\n const slash = spec.indexOf(\"/\");\n if (slash === -1) {\n throw new Error(\n `Invalid model spec \"${spec}\" — expected \"provider/model-id\" (e.g. anthropic/claude-sonnet-4-6)`,\n );\n }\n const provider = canonicalProvider(spec.slice(0, slash));\n const modelId = spec.slice(slash + 1);\n const custom = config.providers?.[provider] ?? config.providers?.[spec.slice(0, slash)];\n const apiKey = resolveApiKey(provider, custom);\n\n if (NATIVE.has(provider)) {\n switch (provider) {\n case \"anthropic\":\n return createAnthropic({ apiKey, baseURL: custom?.baseURL })(modelId);\n case \"openai\":\n return createOpenAI({ apiKey, baseURL: custom?.baseURL })(modelId);\n case \"google\":\n return createGoogleGenerativeAI({ apiKey, baseURL: custom?.baseURL })(modelId);\n }\n }\n\n const builtin = COMPATIBLE[provider];\n if (builtin) {\n return createOpenAICompatible({\n name: provider,\n apiKey: apiKey ?? (builtin.keyEnv === \"\" ? \"none\" : undefined),\n baseURL: custom?.baseURL ?? builtin.baseURL,\n })(modelId);\n }\n\n if (custom?.baseURL) {\n return createOpenAICompatible({ name: provider, apiKey, baseURL: custom.baseURL })(modelId);\n }\n\n throw new Error(\n `Unknown provider \"${provider}\". Built-ins: ${[\"anthropic\", \"openai\", \"google\", ...Object.keys(COMPATIBLE)].join(\", \")}. ` +\n `Define others under \"providers\" in your config with a baseURL.`,\n );\n}\n\n/** Pick a default model from config, or the first provider that has a usable key. */\nexport function defaultModelSpec(config: CycodeConfig): string {\n if (config.model) return config.model;\n for (const [provider, modelId] of DEFAULT_MODELS) {\n if (hasKey(provider, config)) return `${provider}/${modelId}`;\n }\n // a key-bearing provider without a built-in default model still works via --model\n for (const provider of Object.keys(COMPATIBLE)) {\n if (hasKey(provider, config) && COMPATIBLE[provider]!.keyEnv !== \"\") {\n throw new Error(\n `A key for \"${provider}\" is set but it has no default model. ` +\n `Pass --model ${provider}/ or set \"model\" in your config.`,\n );\n }\n }\n throw new Error(\n \"No model configured. Set a provider API key (e.g. ANTHROPIC_API_KEY, OPENAI_API_KEY, \" +\n \"DEEPSEEK_API_KEY, DASHSCOPE_API_KEY, …), add the key under \\\"providers\\\" in \" +\n '~/.cycode/config.json, or set \"model\" to a local one (e.g. \"ollama/llama3.3\").',\n );\n}\n\n/** Model used for compaction summaries and subagents. */\nexport function smallModelSpec(config: CycodeConfig, mainSpec: string): string {\n if (config.smallModel) return config.smallModel;\n if (mainSpec.startsWith(\"anthropic/\")) return \"anthropic/claude-haiku-4-5-20251001\";\n return mainSpec;\n}\n"}, "7ef1285a9e366096a5aff32b51077250394cadf5": {"t": 1785754701.2563927, "n": 7523, "h": "2456d9d236fc66c4", "c": "import { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\nimport { createGoogleGenerativeAI } from \"@ai-sdk/google\";\nimport { createOpenAICompatible } from \"@ai-sdk/openai-compatible\";\nimport type { LanguageModel } from \"ai\";\nimport type { CycodeConfig, ProviderConfig } from \"../config.js\";\n\n/** Providers with a native AI SDK package (key read from the default env var or config). */\nconst NATIVE = new Set([\"anthropic\", \"openai\", \"google\"]);\n\ninterface CompatibleProvider {\n baseURL: string;\n /** Default environment variable for the key (\"\" = no key required, e.g. local Ollama). */\n keyEnv: string;\n}\n\n/**\n * OpenAI-compatible built-in providers — their endpoint and default key env are baked\n * in, so users only set a key (or `--model /`). Chinese providers\n * default to their mainland endpoints; override `baseURL` in config for other regions\n * (e.g. DashScope Singapore, Moonshot/Zhipu international).\n */\nexport const COMPATIBLE: Record = {\n openrouter: { baseURL: \"https://openrouter.ai/api/v1\", keyEnv: \"OPENROUTER_API_KEY\" },\n ollama: { baseURL: \"http://localhost:11434/v1\", keyEnv: \"\" },\n // Chinese providers\n deepseek: { baseURL: \"https://api.deepseek.com/v1\", keyEnv: \"DEEPSEEK_API_KEY\" },\n qwen: { baseURL: \"https://dashscope.aliyuncs.com/compatible-mode/v1\", keyEnv: \"DASHSCOPE_API_KEY\" },\n zhipu: { baseURL: \"https://open.bigmodel.cn/api/paas/v4\", keyEnv: \"ZHIPUAI_API_KEY\" },\n moonshot: { baseURL: \"https://api.moonshot.cn/v1\", keyEnv: \"MOONSHOT_API_KEY\" },\n minimax: { baseURL: \"https://api.minimaxi.com/v1\", keyEnv: \"MINIMAX_API_KEY\" },\n hunyuan: { baseURL: \"https://api.hunyuan.cloud.tencent.com/v1\", keyEnv: \"HUNYUAN_API_KEY\" },\n doubao: { baseURL: \"https://ark.cn-beijing.volces.com/api/v3\", keyEnv: \"ARK_API_KEY\" },\n ernie: { baseURL: \"https://qianfan.baidubce.com/v2\", keyEnv: \"QIANFAN_API_KEY\" },\n siliconflow: { baseURL: \"https://api.siliconflow.cn/v1\", keyEnv: \"SILICONFLOW_API_KEY\" },\n stepfun: { baseURL: \"https://api.stepfun.com/v1\", keyEnv: \"STEPFUN_API_KEY\" },\n baichuan: { baseURL: \"https://api.baichuan-ai.com/v1\", keyEnv: \"BAICHUAN_API_KEY\" },\n yi: { baseURL: \"https://api.lingyiwanwu.com/v1\", keyEnv: \"YI_API_KEY\" },\n};\n\n/** Friendly aliases → canonical provider name. */\nexport const ALIASES: Record = {\n dashscope: \"qwen\",\n tongyi: \"qwen\",\n glm: \"zhipu\",\n zhipuai: \"zhipu\",\n kimi: \"moonshot\",\n ark: \"doubao\",\n volcengine: \"doubao\",\n qianfan: \"ernie\",\n wenxin: \"ernie\",\n step: \"stepfun\",\n \"01ai\": \"yi\",\n lingyiwanwu: \"yi\",\n};\n\nexport function canonicalProvider(name: string): string {\n return ALIASES[name] ?? name;\n}\n\n/** Default environment variable each built-in provider reads its key from. */\nexport const DEFAULT_KEY_ENV: Record = {\n anthropic: \"ANTHROPIC_API_KEY\",\n openai: \"OPENAI_API_KEY\",\n google: \"GOOGLE_GENERATIVE_AI_API_KEY\",\n ...Object.fromEntries(\n Object.entries(COMPATIBLE)\n .filter(([, p]) => p.keyEnv)\n .map(([name, p]) => [name, p.keyEnv]),\n ),\n};\n\n/** Providers that get a sensible default model when none is configured, in priority order. */\nconst DEFAULT_MODELS: [provider: string, modelId: string][] = [\n [\"anthropic\", \"claude-sonnet-4-6\"],\n [\"openai\", \"gpt-5.1\"],\n [\"google\", \"gemini-2.5-pro\"],\n [\"deepseek\", \"deepseek-chat\"],\n [\"qwen\", \"qwen-max\"],\n [\"zhipu\", \"glm-4.6\"],\n [\"moonshot\", \"kimi-k2\"],\n [\"openrouter\", \"anthropic/claude-sonnet-4-6\"],\n];\n\n/**\n * Resolve an API key for a provider. Precedence:\n * config providers..apiKeyEnv (env var) > providers..apiKey (literal)\n * > the provider's default env var.\n */\nexport function resolveApiKey(\n provider: string,\n custom: ProviderConfig | undefined,\n): string | undefined {\n if (custom?.apiKeyEnv && process.env[custom.apiKeyEnv]) {\n return process.env[custom.apiKeyEnv];\n }\n if (custom?.apiKey) return custom.apiKey;\n const defaultEnv = DEFAULT_KEY_ENV[provider];\n if (defaultEnv && process.env[defaultEnv]) return process.env[defaultEnv];\n return undefined;\n}\n\n/** Whether a usable key (or no key needed, e.g. ollama) is available for a provider. */\nexport function hasKey(provider: string, config: CycodeConfig): boolean {\n const name = canonicalProvider(provider);\n if (COMPATIBLE[name]?.keyEnv === \"\") return true;\n return resolveApiKey(name, config.providers?.[name]) !== undefined;\n}\n\n/**\n * Model specs are \"provider/model-id\", e.g. anthropic/claude-sonnet-4-6,\n * openai/gpt-5.1, deepseek/deepseek-chat, qwen/qwen-max, zhipu/glm-4.6,\n * moonshot/kimi-k2, ollama/llama3.3. Any provider's key and baseURL can be\n * overridden under config.providers..\n */\nexport function resolveModel(spec: string, config: CycodeConfig): LanguageModel {\n const slash = spec.indexOf(\"/\");\n if (slash === -1) {\n throw new Error(\n `Invalid model spec \"${spec}\" — expected \"provider/model-id\" (e.g. anthropic/claude-sonnet-4-6)`,\n );\n }\n const provider = canonicalProvider(spec.slice(0, slash));\n const modelId = spec.slice(slash + 1);\n const custom = config.providers?.[provider] ?? config.providers?.[spec.slice(0, slash)];\n const apiKey = resolveApiKey(provider, custom);\n\n if (NATIVE.has(provider)) {\n switch (provider) {\n case \"anthropic\":\n return createAnthropic({ apiKey, baseURL: custom?.baseURL })(modelId);\n case \"openai\":\n return createOpenAI({ apiKey, baseURL: custom?.baseURL })(modelId);\n case \"google\":\n return createGoogleGenerativeAI({ apiKey, baseURL: custom?.baseURL })(modelId);\n }\n }\n\n const builtin = COMPATIBLE[provider];\n if (builtin) {\n return createOpenAICompatible({\n name: provider,\n apiKey: apiKey ?? (builtin.keyEnv === \"\" ? \"none\" : undefined),\n baseURL: custom?.baseURL ?? builtin.baseURL,\n })(modelId);\n }\n\n if (custom?.baseURL) {\n return createOpenAICompatible({ name: provider, apiKey, baseURL: custom.baseURL })(modelId);\n }\n\n throw new Error(\n `Unknown provider \"${provider}\". Built-ins: ${[\"anthropic\", \"openai\", \"google\", ...Object.keys(COMPATIBLE)].join(\", \")}. ` +\n `Define others under \"providers\" in your config with a baseURL.`,\n );\n}\n\n/** Pick a default model from config, or the first provider that has a usable key. */\nexport function defaultModelSpec(config: CycodeConfig): string {\n if (config.model) return config.model;\n for (const [provider, modelId] of DEFAULT_MODELS) {\n if (hasKey(provider, config)) return `${provider}/${modelId}`;\n }\n // a key-bearing provider without a built-in default model still works via --model\n for (const provider of Object.keys(COMPATIBLE)) {\n if (hasKey(provider, config) && COMPATIBLE[provider]!.keyEnv !== \"\") {\n throw new Error(\n `A key for \"${provider}\" is set but it has no default model. ` +\n `Pass --model ${provider}/ or set \"model\" in your config.`,\n );\n }\n }\n throw new Error(\n \"No model configured. Set a provider API key (e.g. ANTHROPIC_API_KEY, OPENAI_API_KEY, \" +\n \"DEEPSEEK_API_KEY, DASHSCOPE_API_KEY, …), add the key under \\\"providers\\\" in \" +\n '~/.cycode/config.json, or set \"model\" to a local one (e.g. \"ollama/llama3.3\").',\n );\n}\n\n/** Model used for compaction summaries and subagents. */\nexport function smallModelSpec(config: CycodeConfig, mainSpec: string): string {\n if (config.smallModel) return config.smallModel;\n if (mainSpec.startsWith(\"anthropic/\")) return \"anthropic/claude-haiku-4-5-20251001\";\n return mainSpec;\n}\n"}, "71e92026bbe80f153484ec750b2cdaf34ac33a24": {"t": 1785754702.940498, "n": 4662, "h": "9cf425f571a6477d", "c": "import { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\nimport { createGoogleGenerativeAI } from \"@ai-sdk/google\";\nimport { createOpenAICompatible } from \"@ai-sdk/openai-compatible\";\nimport type { LanguageModel } from \"ai\";\nimport type { CycodeConfig, ProviderConfig } from \"../config.js\";\n\n/** Default environment variable each built-in provider reads its key from. */\nexport const DEFAULT_KEY_ENV: Record = {\n anthropic: \"ANTHROPIC_API_KEY\",\n openai: \"OPENAI_API_KEY\",\n google: \"GOOGLE_GENERATIVE_AI_API_KEY\",\n openrouter: \"OPENROUTER_API_KEY\",\n};\n\n/** A sensible default model id per built-in provider, used when no model is configured. */\nconst DEFAULT_MODEL: Record = {\n anthropic: \"anthropic/claude-sonnet-4-6\",\n openai: \"openai/gpt-5.1\",\n google: \"google/gemini-2.5-pro\",\n openrouter: \"openrouter/anthropic/claude-sonnet-4-6\",\n};\n\n/**\n * Resolve an API key for a provider. Precedence:\n * config providers..apiKeyEnv (env var) > providers..apiKey (literal)\n * > the provider's default env var.\n * So every major provider — not just Anthropic — has a config slot and an env slot.\n */\nexport function resolveApiKey(\n provider: string,\n custom: ProviderConfig | undefined,\n): string | undefined {\n if (custom?.apiKeyEnv && process.env[custom.apiKeyEnv]) {\n return process.env[custom.apiKeyEnv];\n }\n if (custom?.apiKey) return custom.apiKey;\n const defaultEnv = DEFAULT_KEY_ENV[provider];\n if (defaultEnv && process.env[defaultEnv]) return process.env[defaultEnv];\n return undefined;\n}\n\n/** Whether a usable key (or no key needed, e.g. ollama) is available for a provider. */\nexport function hasKey(provider: string, config: CycodeConfig): boolean {\n if (provider === \"ollama\") return true;\n return resolveApiKey(provider, config.providers?.[provider]) !== undefined;\n}\n\n/**\n * Model specs are \"provider/model-id\", e.g.:\n * anthropic/claude-sonnet-4-6\n * openai/gpt-5.1\n * google/gemini-2.5-pro\n * ollama/llama3.3\n * openrouter/anthropic/claude-sonnet-4-6\n * Any provider's key and baseURL can be set under config.providers..\n */\nexport function resolveModel(spec: string, config: CycodeConfig): LanguageModel {\n const slash = spec.indexOf(\"/\");\n if (slash === -1) {\n throw new Error(\n `Invalid model spec \"${spec}\" — expected \"provider/model-id\" (e.g. anthropic/claude-sonnet-4-6)`,\n );\n }\n const provider = spec.slice(0, slash);\n const modelId = spec.slice(slash + 1);\n const custom = config.providers?.[provider];\n const apiKey = resolveApiKey(provider, custom);\n\n switch (provider) {\n case \"anthropic\":\n return createAnthropic({ apiKey, baseURL: custom?.baseURL })(modelId);\n case \"openai\":\n return createOpenAI({ apiKey, baseURL: custom?.baseURL })(modelId);\n case \"google\":\n return createGoogleGenerativeAI({ apiKey, baseURL: custom?.baseURL })(modelId);\n case \"ollama\":\n return createOpenAICompatible({\n name: \"ollama\",\n apiKey: apiKey ?? \"ollama\",\n baseURL: custom?.baseURL ?? \"http://localhost:11434/v1\",\n })(modelId);\n case \"openrouter\":\n return createOpenAICompatible({\n name: \"openrouter\",\n apiKey,\n baseURL: custom?.baseURL ?? \"https://openrouter.ai/api/v1\",\n })(modelId);\n default: {\n if (custom?.baseURL) {\n return createOpenAICompatible({\n name: provider,\n apiKey,\n baseURL: custom.baseURL,\n })(modelId);\n }\n throw new Error(\n `Unknown provider \"${provider}\". Built-ins: anthropic, openai, google, ollama, openrouter. ` +\n `Define others under \"providers\" in your config with a baseURL.`,\n );\n }\n }\n}\n\n/** Pick a default model from config, or the first provider that has a usable key. */\nexport function defaultModelSpec(config: CycodeConfig): string {\n if (config.model) return config.model;\n for (const provider of [\"anthropic\", \"openai\", \"google\", \"openrouter\"]) {\n if (hasKey(provider, config)) return DEFAULT_MODEL[provider]!;\n }\n throw new Error(\n \"No model configured. Set ANTHROPIC_API_KEY / OPENAI_API_KEY / \" +\n \"GOOGLE_GENERATIVE_AI_API_KEY / OPENROUTER_API_KEY, add the key under \" +\n '\"providers\" in ~/.cycode/config.json, or set \"model\" to a local one (e.g. \"ollama/llama3.3\").',\n );\n}\n\n/** Model used for compaction summaries and subagents. */\nexport function smallModelSpec(config: CycodeConfig, mainSpec: string): string {\n if (config.smallModel) return config.smallModel;\n if (mainSpec.startsWith(\"anthropic/\")) return \"anthropic/claude-haiku-4-5-20251001\";\n return mainSpec;\n}\n"}, "5d4175f1bad10293064d9b02e8737f98a1ea89b8": {"t": 1785754704.2141168, "n": 3184, "h": "091ba9a3a82367cf", "c": "import { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\nimport { createGoogleGenerativeAI } from \"@ai-sdk/google\";\nimport { createOpenAICompatible } from \"@ai-sdk/openai-compatible\";\nimport type { LanguageModel } from \"ai\";\nimport type { CycodeConfig } from \"../config.js\";\n\n/**\n * Model specs are \"provider/model-id\", e.g.:\n * anthropic/claude-sonnet-4-6\n * openai/gpt-5.1\n * google/gemini-2.5-pro\n * ollama/llama3.3\n * openrouter/anthropic/claude-sonnet-4-6\n * Extra OpenAI-compatible providers can be defined in config.providers.\n */\nexport function resolveModel(spec: string, config: CycodeConfig): LanguageModel {\n const slash = spec.indexOf(\"/\");\n if (slash === -1) {\n throw new Error(\n `Invalid model spec \"${spec}\" — expected \"provider/model-id\" (e.g. anthropic/claude-sonnet-4-6)`,\n );\n }\n const provider = spec.slice(0, slash);\n const modelId = spec.slice(slash + 1);\n const custom = config.providers?.[provider];\n\n switch (provider) {\n case \"anthropic\":\n return createAnthropic({ baseURL: custom?.baseURL })(modelId);\n case \"openai\":\n return createOpenAI({ baseURL: custom?.baseURL })(modelId);\n case \"google\":\n return createGoogleGenerativeAI({ baseURL: custom?.baseURL })(modelId);\n case \"ollama\":\n return createOpenAICompatible({\n name: \"ollama\",\n baseURL: custom?.baseURL ?? \"http://localhost:11434/v1\",\n })(modelId);\n case \"openrouter\":\n return createOpenAICompatible({\n name: \"openrouter\",\n baseURL: custom?.baseURL ?? \"https://openrouter.ai/api/v1\",\n apiKey: process.env[custom?.apiKeyEnv ?? \"OPENROUTER_API_KEY\"],\n })(modelId);\n default: {\n if (custom?.baseURL) {\n return createOpenAICompatible({\n name: provider,\n baseURL: custom.baseURL,\n apiKey: custom.apiKeyEnv ? process.env[custom.apiKeyEnv] : undefined,\n })(modelId);\n }\n throw new Error(\n `Unknown provider \"${provider}\". Built-ins: anthropic, openai, google, ollama, openrouter. ` +\n `Define others under \"providers\" in your config with a baseURL.`,\n );\n }\n }\n}\n\n/** Pick a default model from config or whichever API key is present. */\nexport function defaultModelSpec(config: CycodeConfig): string {\n if (config.model) return config.model;\n if (process.env.ANTHROPIC_API_KEY) return \"anthropic/claude-sonnet-4-6\";\n if (process.env.OPENAI_API_KEY) return \"openai/gpt-5.1\";\n if (process.env.GOOGLE_GENERATIVE_AI_API_KEY) return \"google/gemini-2.5-pro\";\n if (process.env.OPENROUTER_API_KEY) return \"openrouter/anthropic/claude-sonnet-4-6\";\n throw new Error(\n \"No model configured. Set ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_GENERATIVE_AI_API_KEY / \" +\n 'OPENROUTER_API_KEY, or set \"model\" in ~/.cycode/config.json (e.g. \"ollama/llama3.3\").',\n );\n}\n\n/** Model used for compaction summaries and subagents. */\nexport function smallModelSpec(config: CycodeConfig, mainSpec: string): string {\n if (config.smallModel) return config.smallModel;\n if (mainSpec.startsWith(\"anthropic/\")) return \"anthropic/claude-haiku-4-5-20251001\";\n return mainSpec;\n}\n"}}}}