feat(agent): optimize initial message handling to clear input and display user messages immediately

This commit is contained in:
HouYunFei
2026-07-30 13:11:03 +08:00
parent 45addead96
commit f2757baf20
3 changed files with 11 additions and 5 deletions
+1
View File
@@ -2,6 +2,7 @@
## Unreleased
+ [优化] Agent 首次发送消息时立即清空输入框并展示用户消息,避免等待线程创建期间出现输入内容滞留和后续草稿被清空。
+ [优化] Agent 动态工具卡片显示具体工具名称、准确执行状态和失败原因,避免统一显示为含糊的「工具操作」。
+ [修复] 画布 Agent 默认通过画布节点生成内容,仅在用户明确指定时使用 Codex 内置生图并将结果展示到对话和当前画布。
+ [优化] Agent 面板标题、功能标签与新对话操作合并为单行顶部栏,减少面板空间占用。
@@ -5,6 +5,7 @@ description: 当前版本已实现但仍需人工验证的变更项
# 待测试
- Agent 首次发送响应:在空白新对话中输入内容并按回车后,输入框应立即清空、用户消息应立即出现在对话中,再显示「正在思考」;线程创建或发送失败时,原输入和附件应恢复;任务运行期间输入的新草稿不应在请求成功后被清空。
- Agent 动态工具信息:执行内置生图、查看图片、命令、文件修改或其他动态工具时,卡片标题应显示具体工具名称;执行失败时正文应显示真实错误原因,刷新并恢复历史对话后仍应保持一致,不再统一显示「工具操作已完成」。
- Agent 画布生图:重启 Canvas Agent 后,其自动生成的工作区 `AGENTS.md` 应同步为最新版指令(用户自行编写的其他 `AGENTS.md` 不应被覆盖);输入“生成一张图片”时应调用 `canvas_generate_image`,通过当前画布节点创建并运行生成流程;只有明确输入“使用 Codex 内置生图/ImageGen 技能生成”时才允许调用内置能力,Codex App Server 返回的 `imageGeneration` 结果应显示在对话中并作为保持原比例的图片节点添加到当前画布;Agent 不应在没有生成结果时提前声称“已生成”。
- Agent 顶部栏:标题只显示垂直居中的「Agent」,连接、对话、历史、日志、新对话和收起操作应位于同一行;面板较窄时标签仍可横向滚动且操作不应错位。
@@ -298,18 +298,18 @@ export function LocalAgentPanel({ embedded, headless, autoConnect }: { embedded?
addMessage({ role: "error", title: "图片过大", text: "图片附件超过 30MB,请删减后再发送。" });
return;
}
setAgentState({ activity: "发送中", sending: true });
const messageId = createId();
const userText = text || `发送了 ${files.length} 张图片`;
setAgentState({ prompt: "", attachments: [], activity: "发送中", sending: true });
addMessage({ id: messageId, role: "user", text: userText, historyText: requestPrompt, attachments: files });
let threadId = useAgentStore.getState().activeThreadId;
try {
if (!threadId) {
const created = await fetchAgentJson<AgentThreadResponse>(endpoint, token, "/agent/codex/threads/new", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ permissionMode }) });
threadId = created.thread?.id || created.workspace?.activeThreadId || "";
if (!threadId) throw new Error("新建对话失败");
setAgentState({ activeThreadId: threadId, messages: [], tokenUsage: null });
setAgentState({ activeThreadId: threadId, tokenUsage: null });
}
addMessage({ id: messageId, role: "user", text: userText, historyText: requestPrompt, attachments: files });
if (files.length) void saveAgentUserMessage(threadId, { id: messageId, role: "user", text: userText, historyText: requestPrompt, attachments: files }).catch(() => undefined);
addEventLog("发送任务", `${compactText(text) || "仅附件"}${files.length ? ` · 附件 ${files.length}` : ""}`);
const data = await fetchAgentJson<{ threadId?: string }>(endpoint, token, "/agent/codex/turn", {
@@ -330,11 +330,15 @@ export function LocalAgentPanel({ embedded, headless, autoConnect }: { embedded?
URL.revokeObjectURL(item.url);
attachmentUrlsRef.current.delete(item.url);
});
setAgentState({ prompt: "", attachments: [], sending: false, waiting: true, activity: "Codex 正在运行" });
setAgentState({ sending: false, waiting: true, activity: "Codex 正在运行" });
} catch (error) {
const text = error instanceof Error ? error.message : "发送失败";
const busy = text.includes("Codex 正在运行");
setAgentState({ activity: busy ? "Codex 正在运行" : "发送失败" });
const state = useAgentStore.getState();
setAgentState({
activity: busy ? "Codex 正在运行" : "发送失败",
...(state.prompt || state.attachments.length ? {} : { prompt, attachments: files }),
});
addMessage({ role: "error", title: busy ? "任务仍在运行" : "发送失败", text });
addEventLog("发送失败", error);
} finally {