From e7c384bca5337042ecc098840ec82af64b048b08 Mon Sep 17 00:00:00 2001 From: Askhz <1361267452@qq.com> Date: Tue, 12 May 2026 17:50:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(server):=20=E4=BF=AE=E5=A4=8D=E5=86=85?= =?UTF-8?q?=E7=BD=AE=20agent=20=E5=88=87=E6=8D=A2=E6=97=B6=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E6=8F=90=E7=A4=BA=E6=9C=AA=E6=B3=A8=E5=85=A5=E5=8F=8A?= =?UTF-8?q?=20@agent=20=E5=87=BA=E7=8E=B0=E5=9C=A8=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E4=B8=AD=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - set_agent 控制消息处理中,查找对应 AgentDefinition 并将其 getSystemPrompt() 写入 options.systemPrompt,使 QueryEngine.ask() 能正确使用 agent 系统提示 - 将 "build"(前端默认模式的显示名称)处理为 undefined,切换回默认模式时 同步清空 options.systemPrompt 和 mainThreadAgentType - /prompt 和 /prompt_async 接口不再将 agentMentions (@agent-*) 拼入 content, 该语法仅用于 REPL 命令模式,出现在 server 模式的 prompt 中会被模型误解 Co-Authored-By: Sonnet-4.6-CoStrict --- src/cli/print.ts | 25 ++++++++++++++++++++++--- src/server/routes/session.ts | 12 ++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/cli/print.ts b/src/cli/print.ts index 3e6ae3e8d..ef93ec596 100644 --- a/src/cli/print.ts +++ b/src/cli/print.ts @@ -3100,9 +3100,28 @@ function runHeadlessStreaming( sendControlResponseSuccess(msg) } else if (msg.request.subtype === 'set_agent') { const agent = typeof msg.request.agent === 'string' ? msg.request.agent : undefined - if (agent) { - setMainThreadAgentType(agent) - saveAgentSetting(agent) + // 'build' is the frontend display name for the default (no-agent) mode. + // Treat it as clearing the agent, same as undefined. + const resolvedAgent = agent === 'build' ? undefined : agent + setMainThreadAgentType(resolvedAgent) + saveAgentSetting(resolvedAgent ?? '') + if (resolvedAgent) { + // Apply the agent's system prompt so QueryEngine picks it up on the next turn. + // QueryEngine.ask() uses options.systemPrompt (customSystemPrompt) directly, + // bypassing buildEffectiveSystemPrompt/mainThreadAgentDefinition. For built-in + // agents the getSystemPrompt params are ignored; pass a minimal stub. + const agentDef = currentAgents.find(a => a.agentType === resolvedAgent) + if (agentDef) { + const agentSystemPrompt = isBuiltInAgent(agentDef) + ? agentDef.getSystemPrompt({ toolUseContext: { options: {} as never } }) + : agentDef.getSystemPrompt() + if (agentSystemPrompt) { + options.systemPrompt = agentSystemPrompt + } + } + } else { + // Switching back to default mode: clear any previously applied agent system prompt. + options.systemPrompt = undefined } sendControlResponseSuccess(msg) } else if (msg.request.subtype === 'set_max_thinking_tokens') { diff --git a/src/server/routes/session.ts b/src/server/routes/session.ts index ff7a231fa..1afdd303f 100644 --- a/src/server/routes/session.ts +++ b/src/server/routes/session.ts @@ -382,8 +382,10 @@ export function createSessionRoutes( .join('\n') ?? '' const agentParts = body.parts?.filter((p) => p.type === 'agent' && p.name) ?? [] - const agentMentions = agentParts.map((p) => `@agent-${p.name}`).join(' ') - const content = [textContent, agentMentions].filter(Boolean).join('\n') + // Agent switching is handled via setAgent() control message below. + // Do not append @agent-* mentions to content — they are REPL-only syntax + // and would be sent verbatim to the model in server mode. + const content = textContent if (!content) throw badRequest('content is required') if (handle.prompting) throw conflict('session is already processing a prompt') @@ -435,8 +437,10 @@ export function createSessionRoutes( .join('\n') ?? '' const agentParts = body.parts?.filter((p) => p.type === 'agent' && p.name) ?? [] - const agentMentions = agentParts.map((p) => `@agent-${p.name}`).join(' ') - const content = [textContent, agentMentions].filter(Boolean).join('\n') + // Agent switching is handled via setAgent() control message below. + // Do not append @agent-* mentions to content — they are REPL-only syntax + // and would be sent verbatim to the model in server mode. + const content = textContent if (!content) { throw badRequest('content is required')