Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Open-ClaudeCode/
- **API Key** (choose one):
- 🔵 **Anthropic Official API** — Register at [console.anthropic.com](https://console.anthropic.com/) to get API Key
- 🟢 **Third-party Proxy** — Recommended for users in certain regions, get proxy URL and API Key
> ⚠️ **OpenAI format proxies** (e.g., MiniMax, OpenRouter) require [Universal-AI-Protocol-Bridge](https://github.com/LING71671/Universal-AI-Protocol-Bridge) for protocol conversion
> ⚠️ MiniMax's Anthropic-compatible endpoint is supported directly. OpenAI-format-only endpoints require a protocol bridge.
- 🔴 **Claude Subscription** — Login via OAuth after running (requires network access)

### Step 1: Clone and Run
Expand Down Expand Up @@ -213,6 +213,36 @@ node package/cli.js -r <session-id>

## ⚙️ Common Configurations

### Configure MiniMax

MiniMax can be selected directly through the built-in provider registry. The default region is `global_en`; use `cn_zh` for the China endpoint.

```json
{
"env": {
"CLAUDE_CODE_USE_MINIMAX": "1",
"MINIMAX_API_REGION": "global_en",
"ANTHROPIC_AUTH_TOKEN": "your-minimax-api-key"
}
}
```

Run either registered model explicitly when needed:

```bash
node package/cli.js --settings settings.json --model MiniMax-M3
node package/cli.js --settings settings.json --model MiniMax-M2.7
```

| Region | Anthropic-compatible base URL | OpenAI-compatible base URL | Documentation |
| --- | --- | --- | --- |
| `global_en` | `https://api.minimax.io/anthropic` | `https://api.minimax.io/v1` | `https://platform.minimax.io/docs` |
| `cn_zh` | `https://api.minimaxi.com/anthropic` | `https://api.minimaxi.com/v1` | `https://platform.minimaxi.com/docs` |

`ANTHROPIC_BASE_URL` can still override the selected Anthropic-compatible endpoint for a custom deployment.

This client sends Anthropic Messages requests directly. To use one of the OpenAI-compatible endpoints above, configure [Universal-AI-Protocol-Bridge](https://github.com/LING71671/Universal-AI-Protocol-Bridge) with that OpenAI base URL, then point `ANTHROPIC_BASE_URL` at the bridge's Anthropic-compatible listener. Do not pass an OpenAI `/v1` URL directly as `ANTHROPIC_BASE_URL`.

### 🔑 Configure Your Own API (Third-party Proxy / Custom Endpoint)

If you use third-party API proxy services or have custom endpoints:
Expand Down
1 change: 1 addition & 0 deletions src/commands/feedback/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const feedback = {
isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_MINIMAX) ||
isEnvTruthy(process.env.DISABLE_FEEDBACK_COMMAND) ||
isEnvTruthy(process.env.DISABLE_BUG_COMMAND) ||
isEssentialTrafficOnly() ||
Expand Down
1 change: 1 addition & 0 deletions src/services/analytics/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export function isAnalyticsDisabled(): boolean {
isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_MINIMAX) ||
isTelemetryDisabled()
)
}
Expand Down
13 changes: 10 additions & 3 deletions src/services/api/claude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ import { isMcpInstructionsDeltaEnabled } from 'src/utils/mcpInstructionsDelta.js
import { calculateUSDCost } from 'src/utils/modelCost.js'
import { endQueryProfile, queryCheckpoint } from 'src/utils/queryProfiler.js'
import {
modelAlwaysUsesThinking,
modelSupportsAdaptiveThinking,
modelSupportsThinking,
type ThinkingConfig,
Expand Down Expand Up @@ -1593,15 +1594,21 @@ async function* queryModel(
options.maxOutputTokensOverride ||
getMaxOutputTokensForModel(options.model)

const alwaysUsesThinking = modelAlwaysUsesThinking(options.model)
const hasThinking =
thinkingConfig.type !== 'disabled' &&
!isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_THINKING)
alwaysUsesThinking ||
(thinkingConfig.type !== 'disabled' &&
!isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_THINKING))
let thinking: BetaMessageStreamParams['thinking'] | undefined = undefined

// IMPORTANT: Do not change the adaptive-vs-budget thinking selection below
// without notifying the model launch DRI and research. This is a sensitive
// setting that can greatly affect model quality and bashing.
if (hasThinking && modelSupportsThinking(options.model)) {
if (
hasThinking &&
!alwaysUsesThinking &&
modelSupportsThinking(options.model)
) {
if (
!isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING) &&
modelSupportsAdaptiveThinking(options.model)
Expand Down
17 changes: 12 additions & 5 deletions src/services/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { getUserAgent } from 'src/utils/http.js'
import { getSmallFastModel } from 'src/utils/model/model.js'
import {
getAPIProvider,
getMiniMaxEndpoint,
isFirstPartyAnthropicBaseUrl,
} from 'src/utils/model/providers.js'
import { getProxyFetchOptions } from 'src/utils/proxy.js'
Expand Down Expand Up @@ -303,11 +304,17 @@ export async function getAnthropicClient({
authToken: isClaudeAISubscriber()
? getClaudeAIOAuthTokens()?.accessToken
: undefined,
// Set baseURL from OAuth config when using staging OAuth
...(process.env.USER_TYPE === 'ant' &&
isEnvTruthy(process.env.USE_STAGING_OAUTH)
? { baseURL: getOauthConfig().BASE_API_URL }
: {}),
// Use the selected compatible endpoint unless the user supplied an override.
...(getAPIProvider() === 'minimax'
? {
baseURL:
process.env.ANTHROPIC_BASE_URL ||
getMiniMaxEndpoint().anthropicBaseUrl,
}
: process.env.USER_TYPE === 'ant' &&
isEnvTruthy(process.env.USE_STAGING_OAUTH)
? { baseURL: getOauthConfig().BASE_API_URL }
: {}),
...ARGS,
...(isDebugToStdErr() && { logger: createStderrLogger() }),
}
Expand Down
3 changes: 2 additions & 1 deletion src/utils/apiPreconnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export function preconnectAnthropicApi(): void {
if (
isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY)
isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_MINIMAX)
) {
return
}
Expand Down
9 changes: 6 additions & 3 deletions src/utils/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ export function isAnthropicAuthEnabled(): boolean {
const is3P =
isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY)
isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_MINIMAX)

// Check if user has configured an external API key source
// This allows externally-provided API keys to work (without requiring proxy configuration)
Expand Down Expand Up @@ -1594,7 +1595,8 @@ export function is1PApiCustomer(): boolean {
if (
isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY)
isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_MINIMAX)
) {
return false
}
Expand Down Expand Up @@ -1733,7 +1735,8 @@ export function isUsing3PServices(): boolean {
return !!(
isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY)
isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_MINIMAX)
)
}

Expand Down
6 changes: 6 additions & 0 deletions src/utils/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getGlobalConfig } from './config.js'
import { isEnvTruthy } from './envUtils.js'
import { getCanonicalName } from './model/model.js'
import { getModelCapability } from './model/modelCapabilities.js'
import { getMiniMaxModel } from './model/providers.js'

// Model context window size (200k tokens for all models right now)
export const MODEL_CONTEXT_WINDOW_DEFAULT = 200_000
Expand Down Expand Up @@ -71,6 +72,11 @@ export function getContextWindowForModel(
return 1_000_000
}

const miniMaxModel = getMiniMaxModel(model)
if (miniMaxModel) {
return miniMaxModel.contextWindow
}

const cap = getModelCapability(model)
if (cap?.max_input_tokens && cap.max_input_tokens >= 100_000) {
if (
Expand Down
1 change: 1 addition & 0 deletions src/utils/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export function logError(error: unknown): void {
isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY) ||
isEnvTruthy(process.env.CLAUDE_CODE_USE_MINIMAX) ||
process.env.DISABLE_ERROR_REPORTING ||
isEssentialTrafficOnly()
) {
Expand Down
4 changes: 4 additions & 0 deletions src/utils/managedEnvConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const PROVIDER_MANAGED_ENV_VARS = new Set([
'CLAUDE_CODE_USE_BEDROCK',
'CLAUDE_CODE_USE_VERTEX',
'CLAUDE_CODE_USE_FOUNDRY',
'CLAUDE_CODE_USE_MINIMAX',
// Endpoint config (base URLs, project/resource identifiers)
'ANTHROPIC_BASE_URL',
'ANTHROPIC_BEDROCK_BASE_URL',
Expand All @@ -27,6 +28,7 @@ const PROVIDER_MANAGED_ENV_VARS = new Set([
'ANTHROPIC_VERTEX_PROJECT_ID',
// Region routing (per-model VERTEX_REGION_CLAUDE_* handled by prefix below)
'CLOUD_ML_REGION',
'MINIMAX_API_REGION',
// Auth
'ANTHROPIC_API_KEY',
'ANTHROPIC_AUTH_TOKEN',
Expand Down Expand Up @@ -147,6 +149,7 @@ export const SAFE_ENV_VARS = new Set([
'CLAUDE_CODE_SUBAGENT_MODEL',
'CLAUDE_CODE_USE_BEDROCK',
'CLAUDE_CODE_USE_FOUNDRY',
'CLAUDE_CODE_USE_MINIMAX',
'CLAUDE_CODE_USE_VERTEX',
'DISABLE_AUTOUPDATER',
'DISABLE_BUG_COMMAND',
Expand All @@ -157,6 +160,7 @@ export const SAFE_ENV_VARS = new Set([
'ENABLE_TOOL_SEARCH',
'MAX_MCP_OUTPUT_TOKENS',
'MAX_THINKING_TOKENS',
'MINIMAX_API_REGION',
'MCP_TIMEOUT',
'MCP_TOOL_TIMEOUT',
'OTEL_EXPORTER_OTLP_HEADERS',
Expand Down
11 changes: 11 additions & 0 deletions src/utils/model/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,76 +11,87 @@ export const CLAUDE_3_7_SONNET_CONFIG = {
bedrock: 'us.anthropic.claude-3-7-sonnet-20250219-v1:0',
vertex: 'claude-3-7-sonnet@20250219',
foundry: 'claude-3-7-sonnet',
minimax: 'MiniMax-M3',
} as const satisfies ModelConfig

export const CLAUDE_3_5_V2_SONNET_CONFIG = {
firstParty: 'claude-3-5-sonnet-20241022',
bedrock: 'anthropic.claude-3-5-sonnet-20241022-v2:0',
vertex: 'claude-3-5-sonnet-v2@20241022',
foundry: 'claude-3-5-sonnet',
minimax: 'MiniMax-M3',
} as const satisfies ModelConfig

export const CLAUDE_3_5_HAIKU_CONFIG = {
firstParty: 'claude-3-5-haiku-20241022',
bedrock: 'us.anthropic.claude-3-5-haiku-20241022-v1:0',
vertex: 'claude-3-5-haiku@20241022',
foundry: 'claude-3-5-haiku',
minimax: 'MiniMax-M2.7',
} as const satisfies ModelConfig

export const CLAUDE_HAIKU_4_5_CONFIG = {
firstParty: 'claude-haiku-4-5-20251001',
bedrock: 'us.anthropic.claude-haiku-4-5-20251001-v1:0',
vertex: 'claude-haiku-4-5@20251001',
foundry: 'claude-haiku-4-5',
minimax: 'MiniMax-M2.7',
} as const satisfies ModelConfig

export const CLAUDE_SONNET_4_CONFIG = {
firstParty: 'claude-sonnet-4-20250514',
bedrock: 'us.anthropic.claude-sonnet-4-20250514-v1:0',
vertex: 'claude-sonnet-4@20250514',
foundry: 'claude-sonnet-4',
minimax: 'MiniMax-M3',
} as const satisfies ModelConfig

export const CLAUDE_SONNET_4_5_CONFIG = {
firstParty: 'claude-sonnet-4-5-20250929',
bedrock: 'us.anthropic.claude-sonnet-4-5-20250929-v1:0',
vertex: 'claude-sonnet-4-5@20250929',
foundry: 'claude-sonnet-4-5',
minimax: 'MiniMax-M3',
} as const satisfies ModelConfig

export const CLAUDE_OPUS_4_CONFIG = {
firstParty: 'claude-opus-4-20250514',
bedrock: 'us.anthropic.claude-opus-4-20250514-v1:0',
vertex: 'claude-opus-4@20250514',
foundry: 'claude-opus-4',
minimax: 'MiniMax-M3',
} as const satisfies ModelConfig

export const CLAUDE_OPUS_4_1_CONFIG = {
firstParty: 'claude-opus-4-1-20250805',
bedrock: 'us.anthropic.claude-opus-4-1-20250805-v1:0',
vertex: 'claude-opus-4-1@20250805',
foundry: 'claude-opus-4-1',
minimax: 'MiniMax-M3',
} as const satisfies ModelConfig

export const CLAUDE_OPUS_4_5_CONFIG = {
firstParty: 'claude-opus-4-5-20251101',
bedrock: 'us.anthropic.claude-opus-4-5-20251101-v1:0',
vertex: 'claude-opus-4-5@20251101',
foundry: 'claude-opus-4-5',
minimax: 'MiniMax-M3',
} as const satisfies ModelConfig

export const CLAUDE_OPUS_4_6_CONFIG = {
firstParty: 'claude-opus-4-6',
bedrock: 'us.anthropic.claude-opus-4-6-v1',
vertex: 'claude-opus-4-6',
foundry: 'claude-opus-4-6',
minimax: 'MiniMax-M3',
} as const satisfies ModelConfig

export const CLAUDE_SONNET_4_6_CONFIG = {
firstParty: 'claude-sonnet-4-6',
bedrock: 'us.anthropic.claude-sonnet-4-6',
vertex: 'claude-sonnet-4-6',
foundry: 'claude-sonnet-4-6',
minimax: 'MiniMax-M3',
} as const satisfies ModelConfig

// @[MODEL LAUNCH]: Register the new config here.
Expand Down
3 changes: 3 additions & 0 deletions src/utils/model/deprecation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const DEPRECATED_MODELS: Record<string, DeprecationEntry> = {
bedrock: 'January 15, 2026',
vertex: 'January 5, 2026',
foundry: 'January 5, 2026',
minimax: null,
},
},
'claude-3-7-sonnet': {
Expand All @@ -47,6 +48,7 @@ const DEPRECATED_MODELS: Record<string, DeprecationEntry> = {
bedrock: 'April 28, 2026',
vertex: 'May 11, 2026',
foundry: 'February 19, 2026',
minimax: null,
},
},
'claude-3-5-haiku': {
Expand All @@ -56,6 +58,7 @@ const DEPRECATED_MODELS: Record<string, DeprecationEntry> = {
bedrock: null,
vertex: null,
foundry: null,
minimax: null,
},
},
}
Expand Down
17 changes: 16 additions & 1 deletion src/utils/model/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { getModelStrings, resolveOverriddenModel } from './modelStrings.js'
import { formatModelPricing, getOpus46CostTier } from '../modelCost.js'
import { getSettings_DEPRECATED } from '../settings/settings.js'
import type { PermissionMode } from '../permissions/PermissionMode.js'
import { getAPIProvider } from './providers.js'
import { getAPIProvider, getMiniMaxModel } from './providers.js'
import { LIGHTNING_BOLT } from '../../constants/figures.js'
import { isModelAllowed } from './modelAllowlist.js'
import { type ModelAlias, isModelAlias } from './aliases.js'
Expand All @@ -38,6 +38,9 @@ export function getSmallFastModel(): ModelName {
}

export function isNonCustomOpusModel(model: ModelName): boolean {
if (getAPIProvider() === 'minimax') {
return false
}
return (
model === getModelStrings().opus40 ||
model === getModelStrings().opus41 ||
Expand Down Expand Up @@ -347,6 +350,10 @@ export function renderModelSetting(setting: ModelName | ModelAlias): string {
* if the model is not recognized as a public model.
*/
export function getPublicModelDisplayName(model: ModelName): string | null {
const miniMaxModel = getMiniMaxModel(model)
if (miniMaxModel) {
return miniMaxModel.modelId
}
switch (model) {
case getModelStrings().opus46:
return 'Opus 4.6'
Expand Down Expand Up @@ -423,6 +430,10 @@ export function renderModelName(model: ModelName): string {
* @returns "Claude {ModelName}" for public models, or "Claude ({model})" for non-public models
*/
export function getPublicModelName(model: ModelName): string {
const miniMaxModel = getMiniMaxModel(model)
if (miniMaxModel) {
return miniMaxModel.modelId
}
const publicName = getPublicModelDisplayName(model)
if (publicName) {
return `Claude ${publicName}`
Expand Down Expand Up @@ -568,6 +579,10 @@ export function modelDisplayString(model: ModelSetting): string {

// @[MODEL LAUNCH]: Add a marketing name mapping for the new model below.
export function getMarketingNameForModel(modelId: string): string | undefined {
const miniMaxModel = getMiniMaxModel(modelId)
if (miniMaxModel) {
return miniMaxModel.modelId
}
if (getAPIProvider() === 'foundry') {
// deployment ID is user-defined in Foundry, so it may have no relation to the actual model
return undefined
Expand Down
Loading