diff --git a/csc-claude-anthropic-reference-report-ui-text-replacement-candidates.md b/csc-claude-anthropic-reference-report-ui-text-replacement-candidates.md new file mode 100644 index 000000000..9eeadf8b4 --- /dev/null +++ b/csc-claude-anthropic-reference-report-ui-text-replacement-candidates.md @@ -0,0 +1,28 @@ +# csc 项目 UI 文案可直接替换清单 + +- 目的:筛选出**只需要替换展示文案**、即可让 UI 页面显示 `CoStrict` 而不是 `Claude` / `Claude Code` / `Anthropic` 的内容。 +- 筛选原则: + 1. 仅保留用户可见文案,例如 `title`、`subtitle`、`message`、`placeholder`、`label`、`aria-label`、`` 文本。 + 2. 不纳入命令、包名、环境变量、URL、路径、主题 token、埋点名、注释说明。 + 3. 对账号体系、订阅体系、Desktop/Chrome 集成等文案,标记为“**可替换但需谨慎**”。 + +--- + +## 一、高置信:可直接文本替换的 UI 文案 + +这部分基本都属于纯展示文本,直接替换后主要影响界面显示,不会直接触发命令、配置或协议变更。 + +| 文件 | 行号 | 原文 | 建议替换为 | +| --- | ---: | --- | --- | +| `packages/remote-control-server/web/index.html` | L6 | `Remote Control — Claude Code` | `Remote Control — CoStrict` | +| `packages/remote-control-server/web/automation.js` | L280 | `Claude Code is in proactive mode and currently sleeping until the next wake-up or user message.` | `CoStrict is in proactive mode and currently sleeping until the next wake-up or user message.` | +| `packages/remote-control-server/web/automation.js` | L290 | `Claude Code is in proactive mode and waiting for the next scheduled check-in.` | `CoStrict is in proactive mode and waiting for the next scheduled check-in.` | +| `packages/remote-control-server/web/automation.js` | L299 / L309 | `Claude Code is in proactive mode and may continue working between user messages.` | `CoStrict is in proactive mode and may continue working between user messages.` | +| `packages/remote-control-server/web/automation.js` | L319 | `Claude Code is processing an automatic background trigger.` | `CoStrict is processing an automatic background trigger.` | +| `packages/remote-control-server/web/automation.js` | L361 | `aria-label="Claude Code status"` | `aria-label="CoStrict status"` | +| `packages/@ant/ink/src/theme/LoadingState.tsx` | L45 | `Fetching your Claude Code sessions...` | `Fetching your CoStrict sessions...` | +| `src/components/FeedbackSurvey/FeedbackSurveyView.tsx` | L26 | `How is Claude doing this session? (optional)` | `How is CoStrict doing this session? (optional)` | +| `src/components/FeedbackSurvey/TranscriptSharePrompt.tsx` | L43 | `Can Anthropic look at your session transcript to help us improve` | `Can CoStrict look at your session transcript to help us improve` | +| `src/components/CostThresholdDialog.tsx` | L12 | `You've spent $5 on the Anthropic API this session.` | `You've spent $5 on the CoStrict API this session.` | +| `src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx` | L47 | `Description (tell Claude when to use this agent)` | `Description (tell CoStrict when to use this agent)` | +| `src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx` | L68 | `When should Claude use this agent?` | `When should CoStrict use this agent?` | diff --git a/packages/@ant/ink/src/theme/LoadingState.tsx b/packages/@ant/ink/src/theme/LoadingState.tsx index ec1459cee..c209952f6 100644 --- a/packages/@ant/ink/src/theme/LoadingState.tsx +++ b/packages/@ant/ink/src/theme/LoadingState.tsx @@ -42,7 +42,7 @@ type LoadingStateProps = { * */ export function LoadingState({ diff --git a/packages/@ant/model-provider/src/shared/__tests__/openaiConvertMessages.test.ts b/packages/@ant/model-provider/src/shared/__tests__/openaiConvertMessages.test.ts index 6de81d8a4..5ece2fd93 100644 --- a/packages/@ant/model-provider/src/shared/__tests__/openaiConvertMessages.test.ts +++ b/packages/@ant/model-provider/src/shared/__tests__/openaiConvertMessages.test.ts @@ -428,7 +428,7 @@ describe('DeepSeek thinking mode (enableThinking)', () => { { enableThinking: true }, ) const assistant = result.filter(m => m.role === 'assistant')[0] as any - expect(assistant.reasoning_content).toBeUndefined() + expect(assistant.reasoning_content).toBe(' ') }) // ── fix: reorder tool and user messages for OpenAI API compatibility (#168) ── diff --git a/packages/@ant/model-provider/src/shared/openaiConvertMessages.ts b/packages/@ant/model-provider/src/shared/openaiConvertMessages.ts index 4d2553653..9535e3fa1 100644 --- a/packages/@ant/model-provider/src/shared/openaiConvertMessages.ts +++ b/packages/@ant/model-provider/src/shared/openaiConvertMessages.ts @@ -211,21 +211,29 @@ function convertInternalAssistantMessage( const content = msg.message.content if (typeof content === 'string') { - return [ - { - role: 'assistant', - content, - } satisfies ChatCompletionAssistantMessageParam, - ] + const result: ChatCompletionAssistantMessageParam & { reasoning_content?: string } = { + role: 'assistant', + content, + } + // 如果开启了thinking模式,必须提供reasoning_content字段 + if (preserveReasoning) { + // content是字符串,没有reasoning内容,设置为空字符串 + result.reasoning_content = ' ' + } + return [result] } if (!Array.isArray(content)) { - return [ - { - role: 'assistant', - content: '', - } satisfies ChatCompletionAssistantMessageParam, - ] + const result: ChatCompletionAssistantMessageParam & { reasoning_content?: string } = { + role: 'assistant', + content: '', + } + // 如果开启了thinking模式,必须提供reasoning_content字段 + if (preserveReasoning) { + // content不是数组,没有reasoning内容,设置为空字符串 + result.reasoning_content = ' ' + } + return [result] } const textParts: string[] = [] @@ -258,11 +266,16 @@ function convertInternalAssistantMessage( // Skip redacted_thinking, server_tool_use, etc. } - const result: ChatCompletionAssistantMessageParam = { + const result: ChatCompletionAssistantMessageParam & { reasoning_content?: string } = { role: 'assistant', content: textParts.length > 0 ? textParts.join('\n') : null, ...(toolCalls.length > 0 && { tool_calls: toolCalls }), - ...(reasoningParts.length > 0 && { reasoning_content: reasoningParts.join('\n') }), + } + + // 如果开启了thinking模式,必须提供reasoning_content字段 + if (preserveReasoning) { + // 如果有reasoning内容就用这些内容,没有才设置成空字符串 + result.reasoning_content = reasoningParts.length > 0 ? reasoningParts.join('\n') : ' ' } return [result] diff --git a/packages/remote-control-server/web/automation.js b/packages/remote-control-server/web/automation.js index 801864c17..dc50c4004 100644 --- a/packages/remote-control-server/web/automation.js +++ b/packages/remote-control-server/web/automation.js @@ -277,7 +277,7 @@ export function getAutomationIndicator(state) { visible: true, label: "Autopilot", tone: "sleeping", - title: "Claude Code is in proactive mode and currently sleeping until the next wake-up or user message.", + title: "CoStrict is in proactive mode and currently sleeping until the next wake-up or user message.", iconVariant: "sleeping", }; } @@ -287,7 +287,7 @@ export function getAutomationIndicator(state) { visible: true, label: "Autopilot", tone: "proactive", - title: "Claude Code is in proactive mode and waiting for the next scheduled check-in.", + title: "CoStrict is in proactive mode and waiting for the next scheduled check-in.", iconVariant: "standby", }; } @@ -296,7 +296,7 @@ export function getAutomationIndicator(state) { visible: true, label: "Autopilot", tone: "proactive", - title: "Claude Code is in proactive mode and may continue working between user messages.", + title: "CoStrict is in proactive mode and may continue working between user messages.", iconVariant: "active", }; } @@ -306,7 +306,7 @@ export function getAutomationIndicator(state) { visible: true, label: "Autopilot", tone: "proactive", - title: "Claude Code is in proactive mode and may continue working between user messages.", + title: "CoStrict is in proactive mode and may continue working between user messages.", iconVariant: "active", }; } @@ -316,7 +316,7 @@ export function getAutomationIndicator(state) { visible: true, label: "Auto Run", tone: "auto-run", - title: "Claude Code is processing an automatic background trigger.", + title: "CoStrict is processing an automatic background trigger.", iconVariant: "active", }; } @@ -358,7 +358,7 @@ export function getAutomationActivity(state) { export function renderAutomationIcon(variant = "active", { className = "", decorative = true } = {}) { const classes = ["clawd-icon", `clawd-icon-${variant}`, className].filter(Boolean).join(" "); - const ariaAttrs = decorative ? 'aria-hidden="true"' : 'role="img" aria-label="Claude Code status"'; + const ariaAttrs = decorative ? 'aria-hidden="true"' : 'role="img" aria-label="CoStrict status"'; return ` diff --git a/packages/remote-control-server/web/automation.test.js b/packages/remote-control-server/web/automation.test.js index 144bbe213..ec03a5b5c 100644 --- a/packages/remote-control-server/web/automation.test.js +++ b/packages/remote-control-server/web/automation.test.js @@ -96,7 +96,7 @@ describe("automation helpers", () => { visible: true, label: "Autopilot", tone: "proactive", - title: "Claude Code is in proactive mode and may continue working between user messages.", + title: "CoStrict is in proactive mode and may continue working between user messages.", iconVariant: "active", }); @@ -154,7 +154,7 @@ describe("automation helpers", () => { visible: true, label: "Autopilot", tone: "proactive", - title: "Claude Code is in proactive mode and waiting for the next scheduled check-in.", + title: "CoStrict is in proactive mode and waiting for the next scheduled check-in.", iconVariant: "standby", }); expect(getAutomationActivity(state)).toEqual({ diff --git a/packages/remote-control-server/web/index.html b/packages/remote-control-server/web/index.html index dc83250ae..137e6fab8 100644 --- a/packages/remote-control-server/web/index.html +++ b/packages/remote-control-server/web/index.html @@ -3,7 +3,7 @@ - Remote Control — Claude Code + Remote Control — CoStrict diff --git a/src/commands/model/model.tsx b/src/commands/model/model.tsx index 4a6114fde..f1d11fddc 100644 --- a/src/commands/model/model.tsx +++ b/src/commands/model/model.tsx @@ -37,6 +37,7 @@ import { generateMachineId, saveCoStrictCredentials } from '../../costrict/provi import { extractExpiryFromJWT } from '../../costrict/provider/token.js'; import { updateSettingsForSource } from '../../utils/settings/settings.js'; import { useSetAppState as useSetGlobalAppState } from '../../state/AppState.js'; +import { isNotLoggedIn } from '../../utils/logoV2Utils.js'; function ModelPickerWrapper({ onDone, @@ -339,6 +340,15 @@ export const call: LocalJSXCommandCall = async (onDone, _context, args) => { return ; } + // Check if user is logged in before showing model picker + if (isNotLoggedIn()) { + // User is not logged in, redirect to login + onDone('You need to login first. Use /login to authenticate.', { + display: 'system', + }); + return; + } + return ; }; diff --git a/src/components/ConsoleOAuthFlow.tsx b/src/components/ConsoleOAuthFlow.tsx index 0f6acd502..751fe12c1 100644 --- a/src/components/ConsoleOAuthFlow.tsx +++ b/src/components/ConsoleOAuthFlow.tsx @@ -288,6 +288,8 @@ export function ConsoleOAuthFlow({ updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any) // Clear costrict env var to prevent conflicts delete process.env.CLAUDE_CODE_USE_COSTRICT + // Reset AppState model so logo reflects the new provider default + setAppState(prev => ({ ...prev, mainLoopModel: null, mainLoopModelForSession: null })) setOAuthStatus({ state: 'success' }) void sendNotification( @@ -734,6 +736,8 @@ function OAuthStatusMessage({ delete process.env.CLAUDE_CODE_USE_COSTRICT // Set default model to sonnet for this provider updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any) + // Reset AppState model so logo reflects the new provider default + setAppState(prev => ({ ...prev, mainLoopModel: null, mainLoopModelForSession: null })) setOAuthStatus({ state: 'success' }) void onDone() } @@ -958,6 +962,8 @@ function OAuthStatusMessage({ delete process.env.CLAUDE_CODE_USE_COSTRICT // Set default model to sonnet for this provider updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any) + // Reset AppState model so logo reflects the new provider default + setAppState(prev => ({ ...prev, mainLoopModel: null, mainLoopModelForSession: null })) setOAuthStatus({ state: 'success' }) void onDone() } @@ -1195,6 +1201,8 @@ function OAuthStatusMessage({ delete process.env.CLAUDE_CODE_USE_COSTRICT // Set default model to sonnet for this provider updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any) + // Reset AppState model so logo reflects the new provider default + setAppState(prev => ({ ...prev, mainLoopModel: null, mainLoopModelForSession: null })) setOAuthStatus({ state: 'success' }) void onDone() } diff --git a/src/components/CostThresholdDialog.tsx b/src/components/CostThresholdDialog.tsx index cf968f653..e9d426bdf 100644 --- a/src/components/CostThresholdDialog.tsx +++ b/src/components/CostThresholdDialog.tsx @@ -9,7 +9,7 @@ type Props = { export function CostThresholdDialog({ onDone }: Props): React.ReactNode { return ( diff --git a/src/components/FeedbackSurvey/FeedbackSurveyView.tsx b/src/components/FeedbackSurvey/FeedbackSurveyView.tsx index 5a8721e12..25abe9937 100644 --- a/src/components/FeedbackSurvey/FeedbackSurveyView.tsx +++ b/src/components/FeedbackSurvey/FeedbackSurveyView.tsx @@ -23,7 +23,7 @@ const inputToResponse: Record = { export const isValidResponseInput = (input: string): input is ResponseInput => (RESPONSE_INPUTS as readonly string[]).includes(input) -const DEFAULT_MESSAGE = 'How is Claude doing this session? (optional)' +const DEFAULT_MESSAGE = 'How is CoStrict doing this session? (optional)' export function FeedbackSurveyView({ onSelect, diff --git a/src/components/FeedbackSurvey/TranscriptSharePrompt.tsx b/src/components/FeedbackSurvey/TranscriptSharePrompt.tsx index 35f623ce6..45c187448 100644 --- a/src/components/FeedbackSurvey/TranscriptSharePrompt.tsx +++ b/src/components/FeedbackSurvey/TranscriptSharePrompt.tsx @@ -40,8 +40,8 @@ export function TranscriptSharePrompt({ {BLACK_CIRCLE} - Can Anthropic look at your session transcript to help us improve - CoStrict? + Can the CoStrict team look at your session transcript to help us + improve CoStrict? diff --git a/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx index 50a0d0590..778dbeeb4 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx @@ -44,7 +44,7 @@ export function DescriptionStep(): ReactNode { return ( @@ -65,7 +65,7 @@ export function DescriptionStep(): ReactNode { } > - When should Claude use this agent? + When should CoStrict use this agent? [ ...prev, - createSystemMessage(`Previous session saved · resume with: claude --resume ${previousSessionId}`, 'suggestion'), + createSystemMessage(`Previous session saved · resume with: csc --resume ${previousSessionId}`, 'suggestion'), ]); } diff --git a/src/costrict/provider/index.ts b/src/costrict/provider/index.ts index fc2f916ee..1dcede320 100644 --- a/src/costrict/provider/index.ts +++ b/src/costrict/provider/index.ts @@ -31,6 +31,7 @@ import { createCoStrictFetch } from './fetch.js' import { resolveCoStrictModel } from './modelMapping.js' import { getCoStrictBaseURL } from './auth.js' import { loadCoStrictCredentials } from './credentials.js' +import { isOpenAIThinkingEnabled } from '../../services/api/openai/requestBody.js' /** * CoStrict 查询路径 @@ -80,9 +81,12 @@ export async function* queryModelCoStrict( ) // 5. 转换为 OpenAI 格式 + // 根据模型名称自动检测是否启用thinking模式 + const enableThinking = isOpenAIThinkingEnabled(costrictModel) const openaiMessages = anthropicMessagesToOpenAI( messagesForAPI, - systemPrompt + systemPrompt, + { enableThinking } ) const openaiTools = anthropicToolsToOpenAI(standardTools) const openaiToolChoice = anthropicToolChoiceToOpenAI(options.toolChoice) diff --git a/src/costrict/provider/modelMapping.ts b/src/costrict/provider/modelMapping.ts index 73a415f3d..ac8ecae7c 100644 --- a/src/costrict/provider/modelMapping.ts +++ b/src/costrict/provider/modelMapping.ts @@ -17,10 +17,9 @@ function getModelFamily(model: string): 'haiku' | 'sonnet' | 'opus' | null { * * 优先级: * 1. 传入的 model 本身就是已知的 CoStrict 模型 ID(用户通过 /model 明确选择) - * 2. COSTRICT_MODEL 环境变量(管理员全局覆盖 / 登录默认值) - * 3. COSTRICT_DEFAULT_{FAMILY}_MODEL 环境变量(按模型族) - * 4. 已缓存的 CoStrict 模型列表中的第一个 - * 5. 直接透传原始模型名(最后兜底) + * 2. COSTRICT_DEFAULT_{FAMILY}_MODEL 环境变量(按模型族) + * 3. 直接透传原始模型名(用户手动配置的模型名) + * 4. COSTRICT_MODEL 环境变量(仅作为最终兜底) */ export function resolveCoStrictModel(anthropicModel: string): string { const cleanModel = anthropicModel.replace(/\[1m\]$/, '') @@ -29,10 +28,7 @@ export function resolveCoStrictModel(anthropicModel: string): string { const cached = getCachedCoStrictModels() if (cached.some(m => m.id === cleanModel)) return cleanModel - // 优先级 2: COSTRICT_MODEL 环境变量 - if (process.env.COSTRICT_MODEL) return process.env.COSTRICT_MODEL - - // 优先级 3: COSTRICT_DEFAULT_{FAMILY}_MODEL 环境变量 + // 优先级 2: COSTRICT_DEFAULT_{FAMILY}_MODEL 环境变量 const family = getModelFamily(cleanModel) if (family) { const envVar = `COSTRICT_DEFAULT_${family.toUpperCase()}_MODEL` @@ -40,9 +36,6 @@ export function resolveCoStrictModel(anthropicModel: string): string { if (override) return override } - // 优先级 4: 缓存中的第一个模型 - if (cached.length > 0) return cached[0].id - - // 优先级 5: 透传原始模型名 + // 优先级 3: 直接透传原始模型名(用户手动配置的模型名优先于环境变量) return cleanModel } diff --git a/src/screens/REPL.tsx b/src/screens/REPL.tsx index f28eb9438..7d7d4b612 100644 --- a/src/screens/REPL.tsx +++ b/src/screens/REPL.tsx @@ -721,7 +721,7 @@ function TranscriptSearchBar({ } const TITLE_ANIMATION_FRAMES = ['⠂', '⠐']; -const TITLE_STATIC_PREFIX = '✳'; +const TITLE_STATIC_PREFIX = ''; const TITLE_ANIMATION_INTERVAL_MS = 960; /** diff --git a/src/services/api/openai/requestBody.ts b/src/services/api/openai/requestBody.ts index e8f93ecfa..d49fdc1e1 100644 --- a/src/services/api/openai/requestBody.ts +++ b/src/services/api/openai/requestBody.ts @@ -25,9 +25,9 @@ export function isOpenAIThinkingEnabled(model: string): boolean { if (isEnvDefinedFalsy(process.env.OPENAI_ENABLE_THINKING)) return false // Explicit enable if (isEnvTruthy(process.env.OPENAI_ENABLE_THINKING)) return true - // Auto-detect from model name (deepseek-reasoner and DeepSeek-V3.2 support thinking mode) + // Auto-detect from model name (deepseek-reasoner, DeepSeek-V3.2, kimi support thinking mode) const modelLower = model.toLowerCase() - return modelLower.includes('deepseek-reasoner') || modelLower.includes('deepseek-v3.2') + return modelLower.includes('deepseek-reasoner') || modelLower.includes('deepseek-v3.2') || modelLower.includes('kimi') } /** diff --git a/src/services/tips/tipRegistry.ts b/src/services/tips/tipRegistry.ts index 634ba782e..ab16cc4c4 100644 --- a/src/services/tips/tipRegistry.ts +++ b/src/services/tips/tipRegistry.ts @@ -376,7 +376,7 @@ const externalTips: Tip[] = [ { id: 'continue', content: async () => - 'Run claude --continue or claude --resume to resume a conversation', + 'Run csc --continue or csc --resume to resume a conversation', cooldownSessions: 10, isRelevant: async () => true, }, diff --git a/src/setup.ts b/src/setup.ts index d2bb9451d..eca42d030 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -433,9 +433,8 @@ export async function setup( // 预取模型列表,填充同步缓存 const baseUrl = getCoStrictBaseURL(activeCreds.base_url) const models = await fetchCoStrictModels(baseUrl, activeCreds.access_token) - if (models.length > 0 && !process.env.COSTRICT_MODEL) { - process.env.COSTRICT_MODEL = models[0].id - } + // 模型列表已预取并缓存,不再自动设置 COSTRICT_MODEL 环境变量 + // resolveCoStrictModel() 会直接透传用户配置的模型名 process.env.CLAUDE_CODE_USE_COSTRICT = '1' } catch { // 初始化失败不阻断启动 diff --git a/src/utils/crossProjectResume.ts b/src/utils/crossProjectResume.ts index 2a5f2f2a8..527a1b6db 100644 --- a/src/utils/crossProjectResume.ts +++ b/src/utils/crossProjectResume.ts @@ -41,7 +41,7 @@ export function checkCrossProjectResume( // Gate worktree detection to ants only for staged rollout if (process.env.USER_TYPE !== 'ant') { const sessionId = getSessionIdFromLog(log) - const command = `cd ${quote([log.projectPath])} && claude --resume ${sessionId}` + const command = `cd ${quote([log.projectPath])} && csc --resume ${sessionId}` return { isCrossProject: true, isSameRepoWorktree: false, @@ -65,7 +65,7 @@ export function checkCrossProjectResume( // Different repo - generate cd command const sessionId = getSessionIdFromLog(log) - const command = `cd ${quote([log.projectPath])} && claude --resume ${sessionId}` + const command = `cd ${quote([log.projectPath])} && csc --resume ${sessionId}` return { isCrossProject: true, isSameRepoWorktree: false, diff --git a/src/utils/gracefulShutdown.ts b/src/utils/gracefulShutdown.ts index a7feac188..754d82f07 100644 --- a/src/utils/gracefulShutdown.ts +++ b/src/utils/gracefulShutdown.ts @@ -156,7 +156,7 @@ function printResumeHint(): void { writeSync( 1, chalk.dim( - `\nResume this session with:\nclaude --resume ${resumeArg}\n`, + `\nResume this session with:\ncsc --resume ${resumeArg}\n`, ), ) resumeHintPrinted = true diff --git a/src/utils/model/model.ts b/src/utils/model/model.ts index 61cccbcf9..21a8f7c49 100644 --- a/src/utils/model/model.ts +++ b/src/utils/model/model.ts @@ -36,6 +36,8 @@ export type ModelSetting = ModelName | ModelAlias | null export function getSmallFastModel(): ModelName { const provider = getAPIProvider() + // CoStrict has no haiku alias — use the main loop model to stay on the same provider + if (provider === 'costrict') return getMainLoopModel() // Provider-specific small fast model if (provider === 'openai' && process.env.OPENAI_SMALL_FAST_MODEL) { return process.env.OPENAI_SMALL_FAST_MODEL @@ -149,6 +151,8 @@ export function getDefaultOpusModel(): ModelName { // @[MODEL LAUNCH]: Update the default Sonnet model (3P providers may lag so keep defaults unchanged). export function getDefaultSonnetModel(): ModelName { const provider = getAPIProvider() + // CoStrict has no sonnet alias — use the main loop model to stay on the same provider + if (provider === 'costrict') return getMainLoopModel() // For OpenAI provider, check OPENAI_DEFAULT_SONNET_MODEL first if (provider === 'openai' && process.env.OPENAI_DEFAULT_SONNET_MODEL) { return process.env.OPENAI_DEFAULT_SONNET_MODEL @@ -171,6 +175,8 @@ export function getDefaultSonnetModel(): ModelName { // @[MODEL LAUNCH]: Update the default Haiku model (3P providers may lag so keep defaults unchanged). export function getDefaultHaikuModel(): ModelName { const provider = getAPIProvider() + // CoStrict has no haiku alias — use the main loop model to stay on the same provider + if (provider === 'costrict') return getMainLoopModel() // For OpenAI provider, check OPENAI_DEFAULT_HAIKU_MODEL first if (provider === 'openai' && process.env.OPENAI_DEFAULT_HAIKU_MODEL) { return process.env.OPENAI_DEFAULT_HAIKU_MODEL diff --git a/src/utils/model/modelOptions.ts b/src/utils/model/modelOptions.ts index 55a5c2fde..e3b10576d 100644 --- a/src/utils/model/modelOptions.ts +++ b/src/utils/model/modelOptions.ts @@ -1,5 +1,4 @@ // biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered -import { getInitialMainLoopModel } from '../../bootstrap/state.js' import { isClaudeAISubscriber, isMaxSubscriber, @@ -25,7 +24,6 @@ import { getDefaultHaikuModel, getDefaultMainLoopModelSetting, getMarketingNameForModel, - getUserSpecifiedModelSetting, isOpus1mMergeEnabled, getOpus46PricingSuffix, renderDefaultModelSetting, @@ -542,45 +540,31 @@ export function getModelOptions(fastMode = false): ModelOption[] { } } - // Add custom model from either the current model value or the initial one - // if it is not already in the options. - let customModel: ModelSetting = null - const currentMainLoopModel = getUserSpecifiedModelSetting() - const initialMainLoopModel = getInitialMainLoopModel() - if (currentMainLoopModel !== undefined && currentMainLoopModel !== null) { - customModel = currentMainLoopModel - } else if (initialMainLoopModel !== null) { - customModel = initialMainLoopModel - } - if (customModel === null || options.some(opt => opt.value === customModel)) { - return filterModelOptionsByAllowlist(options) - } else if (customModel === 'opusplan') { - return filterModelOptionsByAllowlist([...options, getOpusPlanOption()]) - } else if (customModel === 'opus' && getAPIProvider() === 'firstParty') { - return filterModelOptionsByAllowlist([ - ...options, - getMaxOpusOption(fastMode), - ]) - } else if (customModel === 'opus[1m]' && getAPIProvider() === 'firstParty') { - return filterModelOptionsByAllowlist([ - ...options, - getMergedOpus1MOption(fastMode), - ]) - } else { - // Try to show a human-readable label for known Anthropic models, with an - // upgrade hint if the alias now resolves to a newer version. - const knownOption = getKnownModelOption(customModel) + // Add ANTHROPIC_MODEL env var model as a custom option, but only for + // anthropic-compatible providers. settings.model is NOT added here. + const provider = getAPIProvider() + const envModel = + provider === 'firstParty' || + provider === 'bedrock' || + provider === 'vertex' || + provider === 'foundry' + ? process.env.ANTHROPIC_MODEL + : undefined + + if (envModel && !options.some(opt => opt.value === envModel)) { + const knownOption = getKnownModelOption(envModel) if (knownOption) { options.push(knownOption) } else { options.push({ - value: customModel, - label: customModel, - description: 'Custom model', + value: envModel, + label: envModel, + description: 'Custom model (ANTHROPIC_MODEL)', }) } - return filterModelOptionsByAllowlist(options) } + + return filterModelOptionsByAllowlist(options) } /** diff --git a/src/utils/releaseNotes.ts b/src/utils/releaseNotes.ts index 3f0448e64..69f0c6630 100644 --- a/src/utils/releaseNotes.ts +++ b/src/utils/releaseNotes.ts @@ -26,9 +26,9 @@ const MAX_RELEASE_NOTES_SHOWN = 5 * 3. Next time the user starts Claude, the cached changelog is available immediately */ export const CHANGELOG_URL = - 'https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md' + 'https://github.com/y574444354/csc/blob/main/CHANGELOG.md' const RAW_CHANGELOG_URL = - 'https://raw.githubusercontent.com/anthropics/claude-code/refs/heads/main/CHANGELOG.md' + 'https://raw.githubusercontent.com/y574444354/csc/refs/heads/main/CHANGELOG.md' /** * Get the path for the cached changelog file.