Merge pull request #54 from Askhz/feat/costrict-agent-type-header

feat: add agent-type request header to CoStrict provider
This commit is contained in:
geroge 2026-05-12 10:10:48 +08:00 committed by GitHub
commit d1197c2efc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 38 additions and 3 deletions

View File

@ -195,6 +195,8 @@ type State = {
sdkBetas: string[] | undefined sdkBetas: string[] | undefined
// Main thread agent type (from --agent flag or settings) // Main thread agent type (from --agent flag or settings)
mainThreadAgentType: string | undefined mainThreadAgentType: string | undefined
// Currently active skill name (set when a /skill-name is invoked)
activeSkillName: string | undefined
// Remote mode (--remote flag) // Remote mode (--remote flag)
isRemoteMode: boolean isRemoteMode: boolean
// Direct connect server URL (for display in header) // Direct connect server URL (for display in header)
@ -381,6 +383,8 @@ function getInitialState(): State {
sdkBetas: undefined, sdkBetas: undefined,
// Main thread agent type // Main thread agent type
mainThreadAgentType: undefined, mainThreadAgentType: undefined,
// Currently active skill name
activeSkillName: undefined,
// Remote mode // Remote mode
isRemoteMode: false, isRemoteMode: false,
...(process.env.USER_TYPE === 'ant' ...(process.env.USER_TYPE === 'ant'
@ -1622,6 +1626,14 @@ export function setMainThreadAgentType(agentType: string | undefined): void {
STATE.mainThreadAgentType = agentType 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 { export function getIsRemoteMode(): boolean {
return STATE.isRemoteMode return STATE.isRemoteMode
} }

View File

@ -5,6 +5,7 @@
import { feature } from 'bun:bundle' import { feature } from 'bun:bundle'
import { import {
clearInvokedSkills, clearInvokedSkills,
setActiveSkillName,
setLastEmittedDate, setLastEmittedDate,
} from '../../bootstrap/state.js' } from '../../bootstrap/state.js'
import { clearCommandsCache } from '../../commands.js' import { clearCommandsCache } from '../../commands.js'
@ -115,6 +116,8 @@ export function clearSessionCaches(
if (!hasPreserved) clearAllDumpState() if (!hasPreserved) clearAllDumpState()
// Clear invoked skills cache (each entry holds full skill file content) // Clear invoked skills cache (each entry holds full skill file content)
clearInvokedSkills(preservedAgentIds) clearInvokedSkills(preservedAgentIds)
// Reset active skill name so new session doesn't inherit previous session's agent-type
setActiveSkillName(undefined)
// Clear git dir resolution cache // Clear git dir resolution cache
clearResolveGitDirCache() clearResolveGitDirCache()
// Clear dynamic skills (loaded from skill directories) // Clear dynamic skills (loaded from skill directories)

View File

@ -38,6 +38,15 @@ type CoStrictFetch = typeof fetch & {
preconnect?: (url: string | URL) => void 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 * fetch CoStrict API
* *
@ -47,7 +56,10 @@ type CoStrictFetch = typeof fetch & {
* 3. Authorization CoStrict headers * 3. Authorization CoStrict headers
* 4. 401 * 4. 401
*/ */
export function createCoStrictFetch(): CoStrictFetch { export function createCoStrictFetch(options?: {
agentType?: string
}): CoStrictFetch {
const agentType = toKebabCase(options?.agentType) || 'build'
const costrictFetch = async ( const costrictFetch = async (
input: RequestInfo | URL, input: RequestInfo | URL,
init?: RequestInit, init?: RequestInit,
@ -94,6 +106,7 @@ export function createCoStrictFetch(): CoStrictFetch {
headers.set('X-Title', 'CoStrict-CLI') headers.set('X-Title', 'CoStrict-CLI')
headers.set('X-Costrict-Version', `costrict-cli-${VERSION}`) headers.set('X-Costrict-Version', `costrict-cli-${VERSION}`)
headers.set('X-Request-ID', randomUUID()) headers.set('X-Request-ID', randomUUID())
headers.set('agent-type', agentType)
headers.set('zgsm-client-id', creds.machine_id) headers.set('zgsm-client-id', creds.machine_id)
headers.set('zgsm-client-ide', 'cli') headers.set('zgsm-client-ide', 'cli')

View File

@ -33,6 +33,7 @@ import { resolveCoStrictModel } from './modelMapping.js'
import { getCoStrictBaseURL } from './auth.js' import { getCoStrictBaseURL } from './auth.js'
import { loadCoStrictCredentials } from './credentials.js' import { loadCoStrictCredentials } from './credentials.js'
import { isOpenAIThinkingEnabled } from '../../services/api/openai/requestBody.js' import { isOpenAIThinkingEnabled } from '../../services/api/openai/requestBody.js'
import { getMainThreadAgentType, getActiveSkillName } from '../../bootstrap/state.js'
/** /**
* CoStrict * CoStrict
@ -93,7 +94,9 @@ export async function* queryModelCoStrict(
const openaiToolChoice = anthropicToolChoiceToOpenAI(options.toolChoice) const openaiToolChoice = anthropicToolChoiceToOpenAI(options.toolChoice)
// 6. 创建专用的 CoStrict OpenAI 客户端(不缓存,每次使用新的 fetch // 6. 创建专用的 CoStrict OpenAI 客户端(不缓存,每次使用新的 fetch
const costrictFetch = createCoStrictFetch() const costrictFetch = createCoStrictFetch({
agentType: getMainThreadAgentType() ?? getActiveSkillName(),
})
const client = new OpenAI({ const client = new OpenAI({
apiKey: 'costrict-managed', // 实际 token 由 createCoStrictFetch 注入 apiKey: 'costrict-managed', // 实际 token 由 createCoStrictFetch 注入
baseURL: chatBaseURL, baseURL: chatBaseURL,

View File

@ -42,6 +42,7 @@ export async function fetchCoStrictModels(
headers: { headers: {
Authorization: `Bearer ${accessToken}`, Authorization: `Bearer ${accessToken}`,
Accept: 'application/json', Accept: 'application/json',
'User-Agent': `csc/${MACRO.VERSION}`,
}, },
}) })

View File

@ -25,7 +25,7 @@ import type {
ProgressMessage, ProgressMessage,
UserMessage, UserMessage,
} from 'src/types/message.js' } 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 { COMMAND_MESSAGE_TAG, COMMAND_NAME_TAG } from '../../constants/xml.js'
import type { CanUseToolFn } from '../../hooks/useCanUseTool.js' import type { CanUseToolFn } from '../../hooks/useCanUseTool.js'
import { import {
@ -162,6 +162,8 @@ async function executeForkedSlashCommand(
? { ...baseAgent, effort: command.effort } ? { ...baseAgent, effort: command.effort }
: baseAgent : baseAgent
setActiveSkillName(agentDefinition.agentType ?? command.name)
logForDebugging( logForDebugging(
`Executing forked slash command /${command.name} with agent ${agentDefinition.agentType}`, `Executing forked slash command /${command.name} with agent ${agentDefinition.agentType}`,
) )
@ -1230,6 +1232,7 @@ async function getMessagesForPromptSlashCommand(
skillContent, skillContent,
getAgentContext()?.agentId ?? null, getAgentContext()?.agentId ?? null,
) )
setActiveSkillName(command.name)
const metadata = formatCommandLoadingMetadata(command, args) const metadata = formatCommandLoadingMetadata(command, args)