From c28cd05d890919fbf355ce633017f4eed9011c82 Mon Sep 17 00:00:00 2001 From: Askhz <1361267452@qq.com> Date: Tue, 21 Apr 2026 15:48:22 +0800 Subject: [PATCH 01/10] fix: handle reasoning_content for thinking mode in OpenAI compatibility layer - Add reasoning_content field when thinking mode is enabled - Set reasoning_content to empty string when no thinking content available - Enable model auto-detection for kimi provider - Add thinking mode support to costrict provider --- .../src/shared/openaiConvertMessages.ts | 41 ++++++++++++------- src/costrict/provider/index.ts | 6 ++- src/services/api/openai/requestBody.ts | 4 +- 3 files changed, 34 insertions(+), 17 deletions(-) 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/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/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') } /** From 3afc49eb82fdab37420de1862646fe93b3281d2b Mon Sep 17 00:00:00 2001 From: Askhz <1361267452@qq.com> Date: Tue, 21 Apr 2026 17:39:53 +0800 Subject: [PATCH 02/10] chore: change terminalTitle --- src/screens/REPL.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; /** From 32c6f687343571b88e557dccadab81a9f530aaaf Mon Sep 17 00:00:00 2001 From: Askhz <1361267452@qq.com> Date: Tue, 21 Apr 2026 19:28:08 +0800 Subject: [PATCH 03/10] test: fix empty thinking block test expectation The code sets reasoning_content to ' ' for empty thinking blocks when enableThinking is true, but the test expected undefined. Updated the test to match the actual behavior. Co-Authored-By: Claude Opus 4.6 --- .../src/shared/__tests__/openaiConvertMessages.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) ── From 7ad764b5a4b14ecef61126ef82a3e5cac756b8e7 Mon Sep 17 00:00:00 2001 From: Askhz <1361267452@qq.com> Date: Tue, 21 Apr 2026 19:38:55 +0800 Subject: [PATCH 04/10] chore: update changelog URL to y574444354/csc repository - Changed CHANGELOG_URL and RAW_CHANGELOG_URL to point to y574444354/csc - Updated both GitHub page and raw content URLs Co-Authored-By: Claude Opus 4.6 --- src/utils/releaseNotes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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. From bd150468b0eed197cd85beba0c5b5f38913d8315 Mon Sep 17 00:00:00 2001 From: yhangf Date: Tue, 21 Apr 2026 20:13:14 +0800 Subject: [PATCH 05/10] chore(branding): replace Claude/Anthropic references with CoStrict in UI strings --- ...e-report-ui-text-replacement-candidates.md | 28 +++++++++++++++++++ packages/@ant/ink/src/theme/LoadingState.tsx | 2 +- .../remote-control-server/web/automation.js | 12 ++++---- .../web/automation.test.js | 4 +-- packages/remote-control-server/web/index.html | 2 +- src/components/CostThresholdDialog.tsx | 2 +- .../FeedbackSurvey/FeedbackSurveyView.tsx | 2 +- .../FeedbackSurvey/TranscriptSharePrompt.tsx | 4 +-- .../wizard-steps/DescriptionStep.tsx | 4 +-- 9 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 csc-claude-anthropic-reference-report-ui-text-replacement-candidates.md 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/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/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? Date: Tue, 21 Apr 2026 21:08:44 +0800 Subject: [PATCH 06/10] fix: not login can not show model --- src/commands/model/model.tsx | 10 ++++++++++ src/components/ConsoleOAuthFlow.tsx | 8 ++++++++ 2 files changed, 18 insertions(+) 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() } From 5197e2d7cb5cc4dd6826d51c5b6421e7e042fea1 Mon Sep 17 00:00:00 2001 From: yhangf Date: Wed, 22 Apr 2026 09:34:58 +0800 Subject: [PATCH 07/10] refactor(cli): rename command references from 'claude --resume' to 'csc --resume' --- src/components/ultraplan/UltraplanChoiceDialog.tsx | 2 +- src/services/tips/tipRegistry.ts | 2 +- src/utils/crossProjectResume.ts | 4 ++-- src/utils/gracefulShutdown.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/ultraplan/UltraplanChoiceDialog.tsx b/src/components/ultraplan/UltraplanChoiceDialog.tsx index a49925e30..6b5a83c19 100644 --- a/src/components/ultraplan/UltraplanChoiceDialog.tsx +++ b/src/components/ultraplan/UltraplanChoiceDialog.tsx @@ -135,7 +135,7 @@ export function UltraplanChoiceDialog({ if (transcriptSaved) { setMessages(prev => [ ...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/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/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 From c1b7bbc2d315eed6b639280d6f696db33debf57f Mon Sep 17 00:00:00 2001 From: Askhz <1361267452@qq.com> Date: Wed, 22 Apr 2026 11:17:28 +0800 Subject: [PATCH 08/10] fix: restrict ANTHROPIC_MODEL to anthropic-compatible providers in model picker - Only show ANTHROPIC_MODEL env var model for firstParty/bedrock/vertex/foundry - Do not leak it into OpenAI/Gemini/Grok/CoStrict pickers - Remove settings.model from custom model options Co-Authored-By: Claude Opus 4.6 --- src/utils/model/modelOptions.ts | 52 ++++++++++++--------------------- 1 file changed, 18 insertions(+), 34 deletions(-) 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) } /** From 98c9f219f7530d21c80e1095c537f61888706168 Mon Sep 17 00:00:00 2001 From: Askhz <1361267452@qq.com> Date: Wed, 22 Apr 2026 15:48:15 +0800 Subject: [PATCH 09/10] fix: pass through user-configured model name instead of overriding with first API model CoStrict provider now directly passes the user's configured model name to the API, instead of replacing it with the first model from the /ai-gateway/api/v1/models list. Co-Authored-By: Claude Opus 4.6 --- src/costrict/provider/modelMapping.ts | 17 +++++------------ src/setup.ts | 5 ++--- 2 files changed, 7 insertions(+), 15 deletions(-) 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/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 { // 初始化失败不阻断启动 From 772739c6c9417557bf1588f23fd4dccfbef39a84 Mon Sep 17 00:00:00 2001 From: Askhz <1361267452@qq.com> Date: Wed, 22 Apr 2026 16:09:58 +0800 Subject: [PATCH 10/10] =?UTF-8?q?fix:=20=E5=9C=A8=20CoStrict=20provider=20?= =?UTF-8?q?=E4=B8=8B=E4=BE=A7=E6=9F=A5=E8=AF=A2=E7=BB=9F=E4=B8=80=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E4=B8=BB=E5=BE=AA=E7=8E=AF=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- src/utils/model/model.ts | 6 ++++++ 1 file changed, 6 insertions(+) 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