mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-05 17:04:27 +08:00
fix(agent): correct image attachment positioning and prevent disappearance after task completion
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
+ [优化] Agent 历史记录支持多选批量删除,点击记录可直接进入对话。
|
+ [优化] Agent 历史记录支持多选批量删除,点击记录可直接进入对话。
|
||||||
+ [调整] 进入画布并连接 Agent 时默认开启空白新对话,不再自动加载上一次会话,历史对话改为由用户主动选择恢复。
|
+ [调整] 进入画布并连接 Agent 时默认开启空白新对话,不再自动加载上一次会话,历史对话改为由用户主动选择恢复。
|
||||||
+ [优化] Agent 在画布内接收任务时优先直接操作当前画布,不再无故查询画布列表或重复进入画布。
|
+ [优化] Agent 在画布内接收任务时优先直接操作当前画布,不再无故查询画布列表或重复进入画布。
|
||||||
|
+ [修复] 修复 Agent 用户图片附件位置偏移,以及任务完成同步历史后图片消失并回显内部附件说明的问题。
|
||||||
+ [优化] Canvas Agent 改为从独立指令文件初始化当前工作目录的 AGENTS.md,不再为每轮消息重复拼接前置提示词。
|
+ [优化] Canvas Agent 改为从独立指令文件初始化当前工作目录的 AGENTS.md,不再为每轮消息重复拼接前置提示词。
|
||||||
+ [优化] 重构 Canvas Agent 源码目录,按 Agent、画布、服务与通用工具拆分模块职责,提升代码可维护性。
|
+ [优化] 重构 Canvas Agent 源码目录,按 Agent、画布、服务与通用工具拆分模块职责,提升代码可维护性。
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,8 @@ function displayUserText(text: string) {
|
|||||||
const value = text.trim();
|
const value = text.trim();
|
||||||
const marker = "用户请求:";
|
const marker = "用户请求:";
|
||||||
const index = value.lastIndexOf(marker);
|
const index = value.lastIndexOf(marker);
|
||||||
return (index >= 0 ? value.slice(index + marker.length) : value).trim();
|
const prompt = index >= 0 ? value.slice(index + marker.length) : value;
|
||||||
|
return prompt.split("\n\n本轮可用图片附件(顺序与图片输入一致):", 1)[0].trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 将未知值转换为数组。 */
|
/** 将未知值转换为数组。 */
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ description: 当前版本已实现但仍需人工验证的变更项
|
|||||||
- Agent 历史记录:点击记录卡片应直接进入对应对话,不再显示「进入」按钮;可勾选单条或全选多条记录并批量删除,删除当前对话后聊天内容应清空。
|
- Agent 历史记录:点击记录卡片应直接进入对应对话,不再显示「进入」按钮;可勾选单条或全选多条记录并批量删除,删除当前对话后聊天内容应清空。
|
||||||
- Agent 默认新对话:每次进入任一画布并连接 Agent 后,对话区应保持空白且不自动恢复上一次会话;第一次发送消息时才创建新线程,不应产生未发送消息的空历史记录;需要继续旧对话时可在「历史」中主动选择恢复。
|
- Agent 默认新对话:每次进入任一画布并连接 Agent 后,对话区应保持空白且不自动恢复上一次会话;第一次发送消息时才创建新线程,不应产生未发送消息的空历史记录;需要继续旧对话时可在「历史」中主动选择恢复。
|
||||||
- Agent 当前画布优先:在已打开某个画布时要求 Agent 创建、修改、整理或生成内容,Agent 应直接读取并操作当前画布,不应先调用 `canvas_list_projects` 或使用 `site_navigate` 重复进入画布;只有明确要求查看或切换其他画布时才允许查询画布列表并导航。
|
- Agent 当前画布优先:在已打开某个画布时要求 Agent 创建、修改、整理或生成内容,Agent 应直接读取并操作当前画布,不应先调用 `canvas_list_projects` 或使用 `site_navigate` 重复进入画布;只有明确要求查看或切换其他画布时才允许查询画布列表并导航。
|
||||||
|
- Agent 图片消息:发送一张或多张图片附件后,图片应紧跟用户文字并在消息右侧按原比例展示;任务运行中和完成同步历史后图片位置及内容应保持不变,不应消失或把 attachmentId、附件使用说明等内部上下文回显到用户消息中。
|
||||||
- Agent 工作目录指令:`canvas-agent/agent-instructions.md` 应作为独立维护源;重启 Canvas Agent 后,当前工作目录应自动生成 `AGENTS.md`;新建对话发送消息时,Codex 日志中的用户消息只包含本轮请求和必要的附件上下文,不再重复整段 Infinite Canvas 前置提示词,画布及工作台工具仍可正常调用。
|
- Agent 工作目录指令:`canvas-agent/agent-instructions.md` 应作为独立维护源;重启 Canvas Agent 后,当前工作目录应自动生成 `AGENTS.md`;新建对话发送消息时,Codex 日志中的用户消息只包含本轮请求和必要的附件上下文,不再重复整段 Infinite Canvas 前置提示词,画布及工作台工具仍可正常调用。
|
||||||
- 画布文本设置:文本节点和生成配置节点切换到文本模式后应显示推理强度设置,可选择自动、低、中、高、极高;选择自动时默认 OpenAI Responses 请求不应携带 `reasoning`,选择其他档位时应携带所选强度,刷新画布后节点设置应保留;文本模型自定义调用脚本应能读取 `reasoningEffort`,OpenAI 模板应按自动或指定档位正确组装请求。
|
- 画布文本设置:文本节点和生成配置节点切换到文本模式后应显示推理强度设置,可选择自动、低、中、高、极高;选择自动时默认 OpenAI Responses 请求不应携带 `reasoning`,选择其他档位时应携带所选强度,刷新画布后节点设置应保留;文本模型自定义调用脚本应能读取 `reasoningEffort`,OpenAI 模板应按自动或指定档位正确组装请求。
|
||||||
- 生图工作台参考图:将一张或多张图片拖入参考图区域后应直接上传并显示缩略图;拖入非图片文件应忽略,拖动过程中区域应显示高亮提示,放下文件不应导致浏览器打开或替换当前页面。
|
- 生图工作台参考图:将一张或多张图片拖入参考图区域后应直接上传并显示缩略图;拖入非图片文件应忽略,拖动过程中区域应显示高亮提示,放下文件不应导致浏览器打开或替换当前页面。
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export function AgentChatMessage({ item, theme, user, onRejectTool, onApproveToo
|
|||||||
) : (
|
) : (
|
||||||
<Streamdown animated isAnimating={!!item.streamId}>{item.text}</Streamdown>
|
<Streamdown animated isAnimating={!!item.streamId}>{item.text}</Streamdown>
|
||||||
)}
|
)}
|
||||||
{item.attachments?.length ? <AgentMessageAttachments attachments={item.attachments} /> : null}
|
{item.attachments?.length ? <AgentMessageAttachments attachments={item.attachments} alignRight={isUser} /> : null}
|
||||||
{item.meta ? <div className={`mt-1 text-[11px] tabular-nums opacity-55 ${isUser ? "text-right" : ""}`}>{item.meta}</div> : null}
|
{item.meta ? <div className={`mt-1 text-[11px] tabular-nums opacity-55 ${isUser ? "text-right" : ""}`}>{item.meta}</div> : null}
|
||||||
</div>
|
</div>
|
||||||
{isUser ? <AgentUserAvatar user={user} theme={theme} /> : null}
|
{isUser ? <AgentUserAvatar user={user} theme={theme} /> : null}
|
||||||
@@ -239,11 +239,11 @@ function AgentUserAvatar({ user, theme }: { user: LocalUser | null; theme: (type
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AgentMessageAttachments({ attachments }: { attachments: AgentChatAttachment[] }) {
|
function AgentMessageAttachments({ attachments, alignRight }: { attachments: AgentChatAttachment[]; alignRight?: boolean }) {
|
||||||
return (
|
return (
|
||||||
<div className="mt-2 grid grid-cols-3 gap-1.5">
|
<div className={`mt-2 flex flex-wrap gap-2 ${alignRight ? "justify-end" : "justify-start"}`}>
|
||||||
{attachments.map((item) => (
|
{attachments.map((item) => (
|
||||||
<img key={item.id} src={item.url} alt={item.name} className="aspect-square w-full rounded-lg object-cover" />
|
<img key={item.id} src={item.url} alt={item.name} className="max-h-72 max-w-[280px] rounded-xl object-contain" />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -473,3 +473,16 @@ export function normalizeHistoryMessages(messages: AgentChatItem[]) {
|
|||||||
}))
|
}))
|
||||||
.filter((item) => item.text);
|
.filter((item) => item.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mergeHistoryAttachments(messages: AgentChatItem[], currentMessages: AgentChatItem[]) {
|
||||||
|
const currentUsers = currentMessages.filter((item) => item.role === "user").reverse();
|
||||||
|
let userIndex = 0;
|
||||||
|
return [...messages]
|
||||||
|
.reverse()
|
||||||
|
.map((item) => {
|
||||||
|
if (item.role !== "user") return item;
|
||||||
|
const current = currentUsers[userIndex++];
|
||||||
|
return current?.attachments?.length ? { ...item, id: current.id, text: current.text, attachments: current.attachments } : item;
|
||||||
|
})
|
||||||
|
.reverse();
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import {
|
|||||||
isCanvasWriteTool,
|
isCanvasWriteTool,
|
||||||
isConnectionErrorMessage,
|
isConnectionErrorMessage,
|
||||||
isCurrentThreadEvent,
|
isCurrentThreadEvent,
|
||||||
|
mergeHistoryAttachments,
|
||||||
mergeAgentText,
|
mergeAgentText,
|
||||||
normalizeHistoryMessages,
|
normalizeHistoryMessages,
|
||||||
normalizeText,
|
normalizeText,
|
||||||
@@ -127,7 +128,7 @@ export function LocalAgentPanel({ embedded, headless, autoConnect }: { embedded?
|
|||||||
if (currentThreadId && !skipHistory) {
|
if (currentThreadId && !skipHistory) {
|
||||||
let thread = await currentThreadRequest;
|
let thread = await currentThreadRequest;
|
||||||
thread ||= await fetchAgentJson<AgentThreadResponse>(endpoint, token, `/agent/codex/threads/${encodeURIComponent(currentThreadId)}`);
|
thread ||= await fetchAgentJson<AgentThreadResponse>(endpoint, token, `/agent/codex/threads/${encodeURIComponent(currentThreadId)}`);
|
||||||
nextMessages = normalizeHistoryMessages(thread.messages || []);
|
nextMessages = mergeHistoryAttachments(normalizeHistoryMessages(thread.messages || []), useAgentStore.getState().messages);
|
||||||
}
|
}
|
||||||
if (sequence !== loadThreadsSequenceRef.current) return;
|
if (sequence !== loadThreadsSequenceRef.current) return;
|
||||||
setAgentState({ threads: data.data || [], workspacePath: data.workspace?.workspacePath || "", ...(skipHistory ? {} : { messages: nextMessages }) });
|
setAgentState({ threads: data.data || [], workspacePath: data.workspace?.workspacePath || "", ...(skipHistory ? {} : { messages: nextMessages }) });
|
||||||
|
|||||||
Reference in New Issue
Block a user