From 4dcb3a21072dd0a57b4439e83954218dea7f9529 Mon Sep 17 00:00:00 2001 From: Askhz <1361267452@qq.com> Date: Mon, 11 May 2026 11:17:02 +0800 Subject: [PATCH] feat: add agent-type request header to CoStrict provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 CoStrict provider 的所有 API 请求中注入 agent-type header, 反映当前会话所使用的 agent 或 skill,默认值为 "build"。 - fetch.ts: createCoStrictFetch 接受可选 agentType 参数,PascalCase 自动转 kebab-case(StrictSpec → strict-spec),注入 agent-type header - index.ts: queryModelCoStrict 读取 getMainThreadAgentType()(--agent 启动) 或 getActiveSkillName()(slash skill 触发)作为 agentType - models.ts: /v1/models 请求添加 User-Agent: csc/{VERSION} header - state.ts: 新增 activeSkillName 状态及 getter/setter - processSlashCommand.tsx: inline skill 和 fork skill(context:fork) 触发时均调用 setActiveSkillName 记录当前 skill/agent 名 - caches.ts: /clear 时调用 setActiveSkillName(undefined) 重置状态, 避免新会话继承上一会话的 agent-type Co-Authored-By: Claude Opus 4.7 --- src/bootstrap/state.ts | 12 ++++++++++++ src/commands/clear/caches.ts | 3 +++ src/costrict/provider/fetch.ts | 15 ++++++++++++++- src/costrict/provider/index.ts | 5 ++++- src/costrict/provider/models.ts | 1 + .../processUserInput/processSlashCommand.tsx | 5 ++++- 6 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/state.ts b/src/bootstrap/state.ts index 90d613b61..623d99f92 100644 --- a/src/bootstrap/state.ts +++ b/src/bootstrap/state.ts @@ -195,6 +195,8 @@ type State = { sdkBetas: string[] | undefined // Main thread agent type (from --agent flag or settings) mainThreadAgentType: string | undefined + // Currently active skill name (set when a /skill-name is invoked) + activeSkillName: string | undefined // Remote mode (--remote flag) isRemoteMode: boolean // Direct connect server URL (for display in header) @@ -381,6 +383,8 @@ function getInitialState(): State { sdkBetas: undefined, // Main thread agent type mainThreadAgentType: undefined, + // Currently active skill name + activeSkillName: undefined, // Remote mode isRemoteMode: false, ...(process.env.USER_TYPE === 'ant' @@ -1622,6 +1626,14 @@ export function setMainThreadAgentType(agentType: string | undefined): void { STATE.mainThreadAgentType = agentType } +export function getActiveSkillName(): string | undefined { + return STATE.activeSkillName +} + +export function setActiveSkillName(skillName: string | undefined): void { + STATE.activeSkillName = skillName +} + export function getIsRemoteMode(): boolean { return STATE.isRemoteMode } diff --git a/src/commands/clear/caches.ts b/src/commands/clear/caches.ts index 8eacc0d6f..479763b67 100644 --- a/src/commands/clear/caches.ts +++ b/src/commands/clear/caches.ts @@ -5,6 +5,7 @@ import { feature } from 'bun:bundle' import { clearInvokedSkills, + setActiveSkillName, setLastEmittedDate, } from '../../bootstrap/state.js' import { clearCommandsCache } from '../../commands.js' @@ -115,6 +116,8 @@ export function clearSessionCaches( if (!hasPreserved) clearAllDumpState() // Clear invoked skills cache (each entry holds full skill file content) clearInvokedSkills(preservedAgentIds) + // Reset active skill name so new session doesn't inherit previous session's agent-type + setActiveSkillName(undefined) // Clear git dir resolution cache clearResolveGitDirCache() // Clear dynamic skills (loaded from skill directories) diff --git a/src/costrict/provider/fetch.ts b/src/costrict/provider/fetch.ts index c974749e5..94f6881c7 100644 --- a/src/costrict/provider/fetch.ts +++ b/src/costrict/provider/fetch.ts @@ -38,6 +38,15 @@ type CoStrictFetch = typeof fetch & { preconnect?: (url: string | URL) => void } +// PascalCase / camelCase → kebab-case: "StrictSpec" → "strict-spec", "TDD" → "tdd" +function toKebabCase(s: string | undefined): string | undefined { + if (!s) return undefined + return s + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2') + .replace(/([a-z\d])([A-Z])/g, '$1-$2') + .toLowerCase() +} + /** * 创建自定义 fetch 函数,用于 CoStrict API 请求 * @@ -47,7 +56,10 @@ type CoStrictFetch = typeof fetch & { * 3. 注入 Authorization 和 CoStrict 特有 headers * 4. 反应性 401 错误恢复(自动重试一次) */ -export function createCoStrictFetch(): CoStrictFetch { +export function createCoStrictFetch(options?: { + agentType?: string +}): CoStrictFetch { + const agentType = toKebabCase(options?.agentType) || 'build' const costrictFetch = async ( input: RequestInfo | URL, init?: RequestInit, @@ -94,6 +106,7 @@ export function createCoStrictFetch(): CoStrictFetch { headers.set('X-Title', 'CoStrict-CLI') headers.set('X-Costrict-Version', `costrict-cli-${VERSION}`) headers.set('X-Request-ID', randomUUID()) + headers.set('agent-type', agentType) headers.set('zgsm-client-id', creds.machine_id) headers.set('zgsm-client-ide', 'cli') diff --git a/src/costrict/provider/index.ts b/src/costrict/provider/index.ts index 0db3ebe0a..53f474a7a 100644 --- a/src/costrict/provider/index.ts +++ b/src/costrict/provider/index.ts @@ -33,6 +33,7 @@ import { resolveCoStrictModel } from './modelMapping.js' import { getCoStrictBaseURL } from './auth.js' import { loadCoStrictCredentials } from './credentials.js' import { isOpenAIThinkingEnabled } from '../../services/api/openai/requestBody.js' +import { getMainThreadAgentType, getActiveSkillName } from '../../bootstrap/state.js' /** * CoStrict 查询路径 @@ -93,7 +94,9 @@ export async function* queryModelCoStrict( const openaiToolChoice = anthropicToolChoiceToOpenAI(options.toolChoice) // 6. 创建专用的 CoStrict OpenAI 客户端(不缓存,每次使用新的 fetch) - const costrictFetch = createCoStrictFetch() + const costrictFetch = createCoStrictFetch({ + agentType: getMainThreadAgentType() ?? getActiveSkillName(), + }) const client = new OpenAI({ apiKey: 'costrict-managed', // 实际 token 由 createCoStrictFetch 注入 baseURL: chatBaseURL, diff --git a/src/costrict/provider/models.ts b/src/costrict/provider/models.ts index 16743ba5d..77c73ed8f 100644 --- a/src/costrict/provider/models.ts +++ b/src/costrict/provider/models.ts @@ -42,6 +42,7 @@ export async function fetchCoStrictModels( headers: { Authorization: `Bearer ${accessToken}`, Accept: 'application/json', + 'User-Agent': `csc/${MACRO.VERSION}`, }, }) diff --git a/src/utils/processUserInput/processSlashCommand.tsx b/src/utils/processUserInput/processSlashCommand.tsx index b2bf58965..59e4ceecf 100644 --- a/src/utils/processUserInput/processSlashCommand.tsx +++ b/src/utils/processUserInput/processSlashCommand.tsx @@ -25,7 +25,7 @@ import type { ProgressMessage, UserMessage, } from 'src/types/message.js' -import { addInvokedSkill, getSessionId } from '../../bootstrap/state.js' +import { addInvokedSkill, getSessionId, setActiveSkillName } from '../../bootstrap/state.js' import { COMMAND_MESSAGE_TAG, COMMAND_NAME_TAG } from '../../constants/xml.js' import type { CanUseToolFn } from '../../hooks/useCanUseTool.js' import { @@ -162,6 +162,8 @@ async function executeForkedSlashCommand( ? { ...baseAgent, effort: command.effort } : baseAgent + setActiveSkillName(agentDefinition.agentType ?? command.name) + logForDebugging( `Executing forked slash command /${command.name} with agent ${agentDefinition.agentType}`, ) @@ -1230,6 +1232,7 @@ async function getMessagesForPromptSlashCommand( skillContent, getAgentContext()?.agentId ?? null, ) + setActiveSkillName(command.name) const metadata = formatCommandLoadingMetadata(command, args)