{"repo": "bitterbot-ai/bitterbot-desktop", "files": {"docs/providers/xiaomi.md": {"bb1339160fff88bb6e83d6f95cd6001639f64a5f": {"t": 1785595131.6294305, "n": 1804, "h": "ffcbebb696cd418f", "c": "---\nsummary: \"Use Xiaomi MiMo (mimo-v2-flash) with Bitterbot\"\nread_when:\n - You want Xiaomi MiMo models in Bitterbot\n - You need XIAOMI_API_KEY setup\ntitle: \"Xiaomi MiMo\"\n---\n\n# Xiaomi MiMo\n\nXiaomi MiMo is the API platform for **MiMo** models. It provides REST APIs compatible with\nOpenAI and Anthropic formats and uses API keys for authentication. Create your API key in\nthe [Xiaomi MiMo console](https://platform.xiaomimimo.com/#/console/api-keys). Bitterbot uses\nthe `xiaomi` provider with a Xiaomi MiMo API key.\n\n## Model overview\n\n- **mimo-v2-flash**: 262144-token context window, Anthropic Messages API compatible.\n- Base URL: `https://api.xiaomimimo.com/anthropic`\n- Authorization: `Bearer $XIAOMI_API_KEY`\n\n## CLI setup\n\n```bash\nbitterbot onboard --auth-choice xiaomi-api-key\n# or non-interactive\nbitterbot onboard --auth-choice xiaomi-api-key --xiaomi-api-key \"$XIAOMI_API_KEY\"\n```\n\n## Config snippet\n\n```json5\n{\n env: { XIAOMI_API_KEY: \"your-key\" },\n agents: { defaults: { model: { primary: \"xiaomi/mimo-v2-flash\" } } },\n models: {\n mode: \"merge\",\n providers: {\n xiaomi: {\n baseUrl: \"https://api.xiaomimimo.com/anthropic\",\n api: \"anthropic-messages\",\n apiKey: \"XIAOMI_API_KEY\",\n models: [\n {\n id: \"mimo-v2-flash\",\n name: \"Xiaomi MiMo V2 Flash\",\n reasoning: false,\n input: [\"text\"],\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n contextWindow: 262144,\n maxTokens: 8192,\n },\n ],\n },\n },\n },\n}\n```\n\n## Notes\n\n- Model ref: `xiaomi/mimo-v2-flash`.\n- The provider is injected automatically when `XIAOMI_API_KEY` is set (or an auth profile exists).\n- See [/concepts/model-providers](/concepts/model-providers) for provider rules.\n"}}, "src/agents/auth-probe.ts": {"bb1339160fff88bb6e83d6f95cd6001639f64a5f": {"t": 1785746045.3843193, "n": 5146, "h": "723f2d9c35511bf0", "c": "import type { BitterbotConfig } from \"../config/config.js\";\nimport { fetchWithTimeout } from \"../utils/fetch-timeout.js\";\nimport { normalizeProviderId } from \"./model-selection.js\";\n\nconst PROBE_TIMEOUT_MS = 10_000;\n\n/**\n * Live credential probes shared by the onboarding wizard and the\n * models.auth.* gateway RPCs. A probe hits the provider's cheapest\n * authenticated endpoint (the model listing) so keys can be verified\n * before they are saved, without consuming tokens.\n *\n * The probe deliberately never logs or returns the key it was given.\n */\nexport type AuthProbeResult = {\n ok: boolean;\n /** HTTP status when the endpoint answered. */\n status?: number;\n /** Human-readable failure detail (never contains the key). */\n error?: string;\n /** True when this provider has no probe recipe; saving is still allowed. */\n unsupported?: boolean;\n};\n\ntype ProbeStyle = \"openai\" | \"anthropic\";\n\n/**\n * Built-in base URLs for providers whose keys can be probed directly.\n * OpenAI-compatible providers all expose GET {base}/models with a Bearer\n * header; Anthropic exposes GET /v1/models with x-api-key.\n */\nconst PROVIDER_PROBE_RECIPES: Record = {\n anthropic: { baseUrl: \"https://api.anthropic.com/v1\", style: \"anthropic\" },\n openai: { baseUrl: \"https://api.openai.com/v1\", style: \"openai\" },\n groq: { baseUrl: \"https://api.groq.com/openai/v1\", style: \"openai\" },\n openrouter: { baseUrl: \"https://openrouter.ai/api/v1\", style: \"openai\" },\n mistral: { baseUrl: \"https://api.mistral.ai/v1\", style: \"openai\" },\n xai: { baseUrl: \"https://api.x.ai/v1\", style: \"openai\" },\n together: { baseUrl: \"https://api.together.xyz/v1\", style: \"openai\" },\n cerebras: { baseUrl: \"https://api.cerebras.ai/v1\", style: \"openai\" },\n deepseek: { baseUrl: \"https://api.deepseek.com/v1\", style: \"openai\" },\n moonshot: { baseUrl: \"https://api.moonshot.ai/v1\", style: \"openai\" },\n google: {\n baseUrl: \"https://generativelanguage.googleapis.com/v1beta/openai\",\n style: \"openai\",\n },\n};\n\nfunction buildProbeHeaders(style: ProbeStyle, apiKey: string): Record {\n if (style === \"anthropic\") {\n return { \"x-api-key\": apiKey, \"anthropic-version\": \"2023-06-01\" };\n }\n return { Authorization: `Bearer ${apiKey}` };\n}\n\nfunction joinModelsEndpoint(baseUrl: string): string {\n return new URL(\"models\", baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`).href;\n}\n\nexport function resolveProbeRecipe(params: {\n provider: string;\n cfg?: BitterbotConfig;\n baseUrl?: string;\n}): { baseUrl: string; style: ProbeStyle } | null {\n const provider = normalizeProviderId(params.provider);\n const configured = params.cfg?.models?.providers?.[provider];\n const explicitBase = params.baseUrl?.trim() || configured?.baseUrl?.trim();\n if (explicitBase) {\n // Style precedence: configured api mode, then the provider's built-in\n // recipe (an anthropic key tested against a custom proxy still needs\n // x-api-key), then openai-compatible as the default.\n const style: ProbeStyle =\n configured?.api === \"anthropic-messages\"\n ? \"anthropic\"\n : configured?.api\n ? \"openai\"\n : (PROVIDER_PROBE_RECIPES[provider]?.style ?? \"openai\");\n return { baseUrl: explicitBase, style };\n }\n return PROVIDER_PROBE_RECIPES[provider] ?? null;\n}\n\nfunction describeProbeFailure(status: number): string {\n if (status === 401 || status === 403) {\n return \"The provider rejected the key (unauthorized).\";\n }\n if (status === 429) {\n return \"The key authenticated but the provider is rate-limiting it.\";\n }\n return `The provider answered with HTTP ${status}.`;\n}\n\nexport async function probeProviderKey(params: {\n provider: string;\n apiKey: string;\n cfg?: BitterbotConfig;\n baseUrl?: string;\n timeoutMs?: number;\n}): Promise {\n const recipe = resolveProbeRecipe(params);\n if (!recipe) {\n return {\n ok: false,\n unsupported: true,\n error: `No live probe available for provider \"${normalizeProviderId(params.provider)}\". The key can still be saved.`,\n };\n }\n const apiKey = params.apiKey.trim();\n if (!apiKey) {\n return { ok: false, error: \"Empty key.\" };\n }\n try {\n const res = await fetchWithTimeout(\n joinModelsEndpoint(recipe.baseUrl),\n { method: \"GET\", headers: buildProbeHeaders(recipe.style, apiKey) },\n params.timeoutMs ?? PROBE_TIMEOUT_MS,\n );\n // Rate-limited means the credential authenticated; treat as success so a\n // busy account can still save a valid key with an honest status attached.\n if (res.status === 429) {\n return { ok: true, status: res.status, error: describeProbeFailure(res.status) };\n }\n if (res.ok) {\n return { ok: true, status: res.status };\n }\n return { ok: false, status: res.status, error: describeProbeFailure(res.status) };\n } catch (error) {\n const message =\n error instanceof Error && error.name === \"AbortError\"\n ? \"The provider did not answer within the probe timeout.\"\n : error instanceof Error\n ? error.message\n : String(error);\n return { ok: false, error: message };\n }\n}\n"}, "c7fc31d9fc8dcf08afe54fc50ef65d2fbd98f024": {"t": 1785759158.492305, "n": 5146, "h": "723f2d9c35511bf0", "c": "import type { BitterbotConfig } from \"../config/config.js\";\nimport { fetchWithTimeout } from \"../utils/fetch-timeout.js\";\nimport { normalizeProviderId } from \"./model-selection.js\";\n\nconst PROBE_TIMEOUT_MS = 10_000;\n\n/**\n * Live credential probes shared by the onboarding wizard and the\n * models.auth.* gateway RPCs. A probe hits the provider's cheapest\n * authenticated endpoint (the model listing) so keys can be verified\n * before they are saved, without consuming tokens.\n *\n * The probe deliberately never logs or returns the key it was given.\n */\nexport type AuthProbeResult = {\n ok: boolean;\n /** HTTP status when the endpoint answered. */\n status?: number;\n /** Human-readable failure detail (never contains the key). */\n error?: string;\n /** True when this provider has no probe recipe; saving is still allowed. */\n unsupported?: boolean;\n};\n\ntype ProbeStyle = \"openai\" | \"anthropic\";\n\n/**\n * Built-in base URLs for providers whose keys can be probed directly.\n * OpenAI-compatible providers all expose GET {base}/models with a Bearer\n * header; Anthropic exposes GET /v1/models with x-api-key.\n */\nconst PROVIDER_PROBE_RECIPES: Record = {\n anthropic: { baseUrl: \"https://api.anthropic.com/v1\", style: \"anthropic\" },\n openai: { baseUrl: \"https://api.openai.com/v1\", style: \"openai\" },\n groq: { baseUrl: \"https://api.groq.com/openai/v1\", style: \"openai\" },\n openrouter: { baseUrl: \"https://openrouter.ai/api/v1\", style: \"openai\" },\n mistral: { baseUrl: \"https://api.mistral.ai/v1\", style: \"openai\" },\n xai: { baseUrl: \"https://api.x.ai/v1\", style: \"openai\" },\n together: { baseUrl: \"https://api.together.xyz/v1\", style: \"openai\" },\n cerebras: { baseUrl: \"https://api.cerebras.ai/v1\", style: \"openai\" },\n deepseek: { baseUrl: \"https://api.deepseek.com/v1\", style: \"openai\" },\n moonshot: { baseUrl: \"https://api.moonshot.ai/v1\", style: \"openai\" },\n google: {\n baseUrl: \"https://generativelanguage.googleapis.com/v1beta/openai\",\n style: \"openai\",\n },\n};\n\nfunction buildProbeHeaders(style: ProbeStyle, apiKey: string): Record {\n if (style === \"anthropic\") {\n return { \"x-api-key\": apiKey, \"anthropic-version\": \"2023-06-01\" };\n }\n return { Authorization: `Bearer ${apiKey}` };\n}\n\nfunction joinModelsEndpoint(baseUrl: string): string {\n return new URL(\"models\", baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`).href;\n}\n\nexport function resolveProbeRecipe(params: {\n provider: string;\n cfg?: BitterbotConfig;\n baseUrl?: string;\n}): { baseUrl: string; style: ProbeStyle } | null {\n const provider = normalizeProviderId(params.provider);\n const configured = params.cfg?.models?.providers?.[provider];\n const explicitBase = params.baseUrl?.trim() || configured?.baseUrl?.trim();\n if (explicitBase) {\n // Style precedence: configured api mode, then the provider's built-in\n // recipe (an anthropic key tested against a custom proxy still needs\n // x-api-key), then openai-compatible as the default.\n const style: ProbeStyle =\n configured?.api === \"anthropic-messages\"\n ? \"anthropic\"\n : configured?.api\n ? \"openai\"\n : (PROVIDER_PROBE_RECIPES[provider]?.style ?? \"openai\");\n return { baseUrl: explicitBase, style };\n }\n return PROVIDER_PROBE_RECIPES[provider] ?? null;\n}\n\nfunction describeProbeFailure(status: number): string {\n if (status === 401 || status === 403) {\n return \"The provider rejected the key (unauthorized).\";\n }\n if (status === 429) {\n return \"The key authenticated but the provider is rate-limiting it.\";\n }\n return `The provider answered with HTTP ${status}.`;\n}\n\nexport async function probeProviderKey(params: {\n provider: string;\n apiKey: string;\n cfg?: BitterbotConfig;\n baseUrl?: string;\n timeoutMs?: number;\n}): Promise {\n const recipe = resolveProbeRecipe(params);\n if (!recipe) {\n return {\n ok: false,\n unsupported: true,\n error: `No live probe available for provider \"${normalizeProviderId(params.provider)}\". The key can still be saved.`,\n };\n }\n const apiKey = params.apiKey.trim();\n if (!apiKey) {\n return { ok: false, error: \"Empty key.\" };\n }\n try {\n const res = await fetchWithTimeout(\n joinModelsEndpoint(recipe.baseUrl),\n { method: \"GET\", headers: buildProbeHeaders(recipe.style, apiKey) },\n params.timeoutMs ?? PROBE_TIMEOUT_MS,\n );\n // Rate-limited means the credential authenticated; treat as success so a\n // busy account can still save a valid key with an honest status attached.\n if (res.status === 429) {\n return { ok: true, status: res.status, error: describeProbeFailure(res.status) };\n }\n if (res.ok) {\n return { ok: true, status: res.status };\n }\n return { ok: false, status: res.status, error: describeProbeFailure(res.status) };\n } catch (error) {\n const message =\n error instanceof Error && error.name === \"AbortError\"\n ? \"The provider did not answer within the probe timeout.\"\n : error instanceof Error\n ? error.message\n : String(error);\n return { ok: false, error: message };\n }\n}\n"}, "dd57ae8fe47803af810015019ef8d3699b07d7fa": {"t": 1785759159.9824827, "n": 4823, "h": "46074acdf231010d", "c": "import type { BitterbotConfig } from \"../config/config.js\";\nimport { fetchWithTimeout } from \"../utils/fetch-timeout.js\";\nimport { normalizeProviderId } from \"./model-selection.js\";\n\nconst PROBE_TIMEOUT_MS = 10_000;\n\n/**\n * Live credential probes shared by the onboarding wizard and the\n * models.auth.* gateway RPCs. A probe hits the provider's cheapest\n * authenticated endpoint (the model listing) so keys can be verified\n * before they are saved, without consuming tokens.\n *\n * The probe deliberately never logs or returns the key it was given.\n */\nexport type AuthProbeResult = {\n ok: boolean;\n /** HTTP status when the endpoint answered. */\n status?: number;\n /** Human-readable failure detail (never contains the key). */\n error?: string;\n /** True when this provider has no probe recipe; saving is still allowed. */\n unsupported?: boolean;\n};\n\ntype ProbeStyle = \"openai\" | \"anthropic\";\n\n/**\n * Built-in base URLs for providers whose keys can be probed directly.\n * OpenAI-compatible providers all expose GET {base}/models with a Bearer\n * header; Anthropic exposes GET /v1/models with x-api-key.\n */\nconst PROVIDER_PROBE_RECIPES: Record = {\n anthropic: { baseUrl: \"https://api.anthropic.com/v1\", style: \"anthropic\" },\n openai: { baseUrl: \"https://api.openai.com/v1\", style: \"openai\" },\n groq: { baseUrl: \"https://api.groq.com/openai/v1\", style: \"openai\" },\n openrouter: { baseUrl: \"https://openrouter.ai/api/v1\", style: \"openai\" },\n mistral: { baseUrl: \"https://api.mistral.ai/v1\", style: \"openai\" },\n xai: { baseUrl: \"https://api.x.ai/v1\", style: \"openai\" },\n together: { baseUrl: \"https://api.together.xyz/v1\", style: \"openai\" },\n cerebras: { baseUrl: \"https://api.cerebras.ai/v1\", style: \"openai\" },\n deepseek: { baseUrl: \"https://api.deepseek.com/v1\", style: \"openai\" },\n moonshot: { baseUrl: \"https://api.moonshot.ai/v1\", style: \"openai\" },\n google: {\n baseUrl: \"https://generativelanguage.googleapis.com/v1beta/openai\",\n style: \"openai\",\n },\n};\n\nfunction buildProbeHeaders(style: ProbeStyle, apiKey: string): Record {\n if (style === \"anthropic\") {\n return { \"x-api-key\": apiKey, \"anthropic-version\": \"2023-06-01\" };\n }\n return { Authorization: `Bearer ${apiKey}` };\n}\n\nfunction joinModelsEndpoint(baseUrl: string): string {\n return new URL(\"models\", baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`).href;\n}\n\nexport function resolveProbeRecipe(params: {\n provider: string;\n cfg?: BitterbotConfig;\n baseUrl?: string;\n}): { baseUrl: string; style: ProbeStyle } | null {\n const provider = normalizeProviderId(params.provider);\n const configured = params.cfg?.models?.providers?.[provider];\n const explicitBase = params.baseUrl?.trim() || configured?.baseUrl?.trim();\n if (explicitBase) {\n const style: ProbeStyle = configured?.api === \"anthropic-messages\" ? \"anthropic\" : \"openai\";\n return { baseUrl: explicitBase, style };\n }\n return PROVIDER_PROBE_RECIPES[provider] ?? null;\n}\n\nfunction describeProbeFailure(status: number): string {\n if (status === 401 || status === 403) {\n return \"The provider rejected the key (unauthorized).\";\n }\n if (status === 429) {\n return \"The key authenticated but the provider is rate-limiting it.\";\n }\n return `The provider answered with HTTP ${status}.`;\n}\n\nexport async function probeProviderKey(params: {\n provider: string;\n apiKey: string;\n cfg?: BitterbotConfig;\n baseUrl?: string;\n timeoutMs?: number;\n}): Promise {\n const recipe = resolveProbeRecipe(params);\n if (!recipe) {\n return {\n ok: false,\n unsupported: true,\n error: `No live probe available for provider \"${normalizeProviderId(params.provider)}\". The key can still be saved.`,\n };\n }\n const apiKey = params.apiKey.trim();\n if (!apiKey) {\n return { ok: false, error: \"Empty key.\" };\n }\n try {\n const res = await fetchWithTimeout(\n joinModelsEndpoint(recipe.baseUrl),\n { method: \"GET\", headers: buildProbeHeaders(recipe.style, apiKey) },\n params.timeoutMs ?? PROBE_TIMEOUT_MS,\n );\n // Rate-limited means the credential authenticated; treat as success so a\n // busy account can still save a valid key with an honest status attached.\n if (res.status === 429) {\n return { ok: true, status: res.status, error: describeProbeFailure(res.status) };\n }\n if (res.ok) {\n return { ok: true, status: res.status };\n }\n return { ok: false, status: res.status, error: describeProbeFailure(res.status) };\n } catch (error) {\n const message =\n error instanceof Error && error.name === \"AbortError\"\n ? \"The provider did not answer within the probe timeout.\"\n : error instanceof Error\n ? error.message\n : String(error);\n return { ok: false, error: message };\n }\n}\n"}}, "docs/providers/minimax.md": {"a6cea7896521667b7079217e521c3cdaad3decba": {"t": 1785846813.201012, "n": 6736, "h": "3bd72ea4acfe5fe5", "c": "---\nsummary: \"Use MiniMax M2.1 in Bitterbot\"\nread_when:\n - You want MiniMax models in Bitterbot\n - You need MiniMax setup guidance\ntitle: \"MiniMax\"\n---\n\n# MiniMax\n\nMiniMax is an AI company that builds the **M2/M2.1** model family. The current\ncoding-focused release is **MiniMax M2.1** (December 23, 2025), built for\nreal-world complex tasks.\n\nSource: [MiniMax M2.1 release note](https://www.minimax.io/news/minimax-m21)\n\n## Model overview (M2.1)\n\nMiniMax highlights these improvements in M2.1:\n\n- Stronger **multi-language coding** (Rust, Java, Go, C++, Kotlin, Objective-C, TS/JS).\n- Better **web/app development** and aesthetic output quality (including native mobile).\n- Improved **composite instruction** handling for office-style workflows, building on\n interleaved thinking and integrated constraint execution.\n- **More concise responses** with lower token usage and faster iteration loops.\n- Stronger **tool/agent framework** compatibility and context management (Claude Code,\n Droid/Factory AI, Cline, Kilo Code, Roo Code, BlackBox).\n- Higher-quality **dialogue and technical writing** outputs.\n\n## MiniMax M2.1 vs MiniMax M2.1 Lightning\n\n- **Speed:** Lightning is the “fast” variant in MiniMax’s pricing docs.\n- **Cost:** Pricing shows the same input cost, but Lightning has higher output cost.\n- **Coding plan routing:** The Lightning back-end isn’t directly available on the MiniMax\n coding plan. MiniMax auto-routes most requests to Lightning, but falls back to the\n regular M2.1 back-end during traffic spikes.\n\n## Choose a setup\n\n### MiniMax OAuth (Coding Plan) — recommended\n\n**Best for:** quick setup with MiniMax Coding Plan via OAuth, no API key required.\n\nEnable the bundled OAuth plugin and authenticate:\n\n```bash\nbitterbot plugins enable minimax-portal-auth # skip if already loaded.\nbitterbot gateway restart # restart if gateway is already running\nbitterbot onboard --auth-choice minimax-portal\n```\n\nYou will be prompted to select an endpoint:\n\n- **Global** - International users (`api.minimax.io`)\n- **CN** - Users in China (`api.minimaxi.com`)\n\nSee [MiniMax OAuth plugin README](https://github.com/bitterbot/bitterbot/tree/main/extensions/minimax-portal-auth) for details.\n\n### MiniMax M2.1 (API key)\n\n**Best for:** hosted MiniMax with Anthropic-compatible API.\n\nConfigure via CLI:\n\n- Run `bitterbot configure`\n- Select **Model/auth**\n- Choose **MiniMax M2.1**\n\n```json5\n{\n env: { MINIMAX_API_KEY: \"sk-...\" },\n agents: { defaults: { model: { primary: \"minimax/MiniMax-M2.1\" } } },\n models: {\n mode: \"merge\",\n providers: {\n minimax: {\n baseUrl: \"https://api.minimax.io/anthropic\",\n apiKey: \"${MINIMAX_API_KEY}\",\n api: \"anthropic-messages\",\n models: [\n {\n id: \"MiniMax-M2.1\",\n name: \"MiniMax M2.1\",\n reasoning: false,\n input: [\"text\"],\n cost: { input: 15, output: 60, cacheRead: 2, cacheWrite: 10 },\n contextWindow: 200000,\n maxTokens: 8192,\n },\n ],\n },\n },\n },\n}\n```\n\n### MiniMax M2.1 as fallback (Opus primary)\n\n**Best for:** keep Opus 4.6 as primary, fail over to MiniMax M2.1.\n\n```json5\n{\n env: { MINIMAX_API_KEY: \"sk-...\" },\n agents: {\n defaults: {\n models: {\n \"anthropic/claude-opus-4-6\": { alias: \"opus\" },\n \"minimax/MiniMax-M2.1\": { alias: \"minimax\" },\n },\n model: {\n primary: \"anthropic/claude-opus-4-6\",\n fallbacks: [\"minimax/MiniMax-M2.1\"],\n },\n },\n },\n}\n```\n\n### Optional: Local via LM Studio (manual)\n\n**Best for:** local inference with LM Studio.\nWe have seen strong results with MiniMax M2.1 on powerful hardware (e.g. a\ndesktop/server) using LM Studio's local server.\n\nConfigure manually via `bitterbot.json`:\n\n```json5\n{\n agents: {\n defaults: {\n model: { primary: \"lmstudio/minimax-m2.1-gs32\" },\n models: { \"lmstudio/minimax-m2.1-gs32\": { alias: \"Minimax\" } },\n },\n },\n models: {\n mode: \"merge\",\n providers: {\n lmstudio: {\n baseUrl: \"http://127.0.0.1:1234/v1\",\n apiKey: \"lmstudio\",\n api: \"openai-responses\",\n models: [\n {\n id: \"minimax-m2.1-gs32\",\n name: \"MiniMax M2.1 GS32\",\n reasoning: false,\n input: [\"text\"],\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n contextWindow: 196608,\n maxTokens: 8192,\n },\n ],\n },\n },\n },\n}\n```\n\n## Configure via `bitterbot configure`\n\nUse the interactive config wizard to set MiniMax without editing JSON:\n\n1. Run `bitterbot configure`.\n2. Select **Model/auth**.\n3. Choose **MiniMax M2.1**.\n4. Pick your default model when prompted.\n\n## Configuration options\n\n- `models.providers.minimax.baseUrl`: prefer `https://api.minimax.io/anthropic` (Anthropic-compatible); `https://api.minimax.io/v1` is optional for OpenAI-compatible payloads.\n- `models.providers.minimax.api`: prefer `anthropic-messages`; `openai-completions` is optional for OpenAI-compatible payloads.\n- `models.providers.minimax.apiKey`: MiniMax API key (`MINIMAX_API_KEY`).\n- `models.providers.minimax.models`: define `id`, `name`, `reasoning`, `contextWindow`, `maxTokens`, `cost`.\n- `agents.defaults.models`: alias models you want in the allowlist.\n- `models.mode`: keep `merge` if you want to add MiniMax alongside built-ins.\n\n## Notes\n\n- Model refs are `minimax/`.\n- Coding Plan usage API: `https://api.minimaxi.com/v1/api/openplatform/coding_plan/remains` (requires a coding plan key).\n- Update pricing values in `models.json` if you need exact cost tracking.\n- Referral link for MiniMax Coding Plan (10% off): [https://platform.minimax.io/subscribe/coding-plan?code=DbXJTRClnb&source=link](https://platform.minimax.io/subscribe/coding-plan?code=DbXJTRClnb&source=link)\n- See [/concepts/model-providers](/concepts/model-providers) for provider rules.\n- Use `bitterbot models list` and `bitterbot models set minimax/MiniMax-M2.1` to switch.\n\n## Troubleshooting\n\n### “Unknown model: minimax/MiniMax-M2.1”\n\nThis usually means the **MiniMax provider isn’t configured** (no provider entry\nand no MiniMax auth profile/env key found). A fix for this detection is in\n**2026.1.12** (unreleased at the time of writing). Fix by:\n\n- Upgrading to **2026.1.12** (or run from source `main`), then restarting the gateway.\n- Running `bitterbot configure` and selecting **MiniMax M2.1**, or\n- Adding the `models.providers.minimax` block manually, or\n- Setting `MINIMAX_API_KEY` (or a MiniMax auth profile) so the provider can be injected.\n\nMake sure the model id is **case‑sensitive**:\n\n- `minimax/MiniMax-M2.1`\n- `minimax/MiniMax-M2.1-lightning`\n\nThen recheck with:\n\n```bash\nbitterbot models list\n```\n"}}}}