mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-04 16:14:22 +08:00
fix(agent): ensure image attachments retain thumbnails after history sync and prevent internal context from displaying in user messages
This commit is contained in:
@@ -54,7 +54,7 @@ export function AgentChatTimeline({
|
||||
}, [messages, pendingTool, scrollToBottom, updateScrollState, waiting]);
|
||||
return (
|
||||
<div className="relative min-h-0 flex-1">
|
||||
<div ref={listRef} className="thin-scrollbar h-full space-y-4 overflow-y-auto px-4 pb-12 pt-4" onScroll={updateScrollState}>
|
||||
<div ref={listRef} className="thin-scrollbar h-full select-text space-y-4 overflow-y-auto px-4 pb-12 pt-4" onScroll={updateScrollState}>
|
||||
{messages.map((item) => (
|
||||
isPlanMessage(item) ? null : <AgentChatMessageRow key={item.id} item={item} theme={theme} user={user} />
|
||||
))}
|
||||
@@ -128,4 +128,3 @@ function UsageNumber({ label, value, color }: { label: string; value: number; co
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -475,14 +475,15 @@ export function normalizeHistoryMessages(messages: AgentChatItem[]) {
|
||||
}
|
||||
|
||||
export function mergeHistoryAttachments(messages: AgentChatItem[], currentMessages: AgentChatItem[]) {
|
||||
const currentUsers = currentMessages.filter((item) => item.role === "user").reverse();
|
||||
let userIndex = 0;
|
||||
const currentUsers = currentMessages.filter((item) => item.role === "user" && item.attachments?.length).reverse();
|
||||
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;
|
||||
const index = currentUsers.findIndex((current) => current.text === item.text || current.historyText === item.text);
|
||||
if (index < 0) return item;
|
||||
const current = currentUsers.splice(index, 1)[0];
|
||||
return { ...item, id: current.id, text: current.text, historyText: current.historyText, attachments: current.attachments };
|
||||
})
|
||||
.reverse();
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ export function AgentPanel() {
|
||||
>
|
||||
<motion.aside
|
||||
className="relative flex h-full shrink-0 flex-col border-l"
|
||||
data-canvas-shortcuts-ignore
|
||||
initial={{ x: 48 }}
|
||||
animate={{ x: panelClosing ? 28 : 0 }}
|
||||
transition={{ duration: resizing ? 0 : PANEL_MOTION_SECONDS, ease: [0.22, 1, 0.36, 1] }}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { fitNodeSize } from "@/lib/canvas/canvas-node-size";
|
||||
import { readImageMeta } from "@/lib/image-utils";
|
||||
import { randomId } from "@/lib/utils";
|
||||
import { uploadImage } from "@/services/image-storage";
|
||||
import { deleteAgentThreadMessages, readAgentUserMessages, saveAgentUserMessage } from "@/services/agent-chat-storage";
|
||||
import { useThemeStore } from "@/stores/use-theme-store";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import { useAgentStore, type AgentCanvasContext, type AgentChatItem, type AgentPendingToolCall, type AgentThreadSummary } from "@/stores/use-agent-store";
|
||||
@@ -123,12 +124,14 @@ export function LocalAgentPanel({ embedded, headless, autoConnect }: { embedded?
|
||||
try {
|
||||
const currentThreadId = useAgentStore.getState().activeThreadId;
|
||||
const currentThreadRequest = currentThreadId && !skipHistory ? fetchAgentJson<AgentThreadResponse>(endpoint, token, `/agent/codex/threads/${encodeURIComponent(currentThreadId)}`).catch(() => null) : null;
|
||||
const storedMessagesRequest = currentThreadId && !skipHistory ? readAgentUserMessages(currentThreadId) : null;
|
||||
const data = await fetchAgentJson<AgentThreadsResponse>(endpoint, token, `/agent/codex/threads`);
|
||||
let nextMessages: AgentChatItem[] = [];
|
||||
if (currentThreadId && !skipHistory) {
|
||||
let thread = await currentThreadRequest;
|
||||
thread ||= await fetchAgentJson<AgentThreadResponse>(endpoint, token, `/agent/codex/threads/${encodeURIComponent(currentThreadId)}`);
|
||||
nextMessages = mergeHistoryAttachments(normalizeHistoryMessages(thread.messages || []), useAgentStore.getState().messages);
|
||||
const storedMessages = (await storedMessagesRequest) || [];
|
||||
nextMessages = mergeHistoryAttachments(normalizeHistoryMessages(thread.messages || []), [...storedMessages, ...useAgentStore.getState().messages]);
|
||||
}
|
||||
if (sequence !== loadThreadsSequenceRef.current) return;
|
||||
setAgentState({ threads: data.data || [], workspacePath: data.workspace?.workspacePath || "", ...(skipHistory ? {} : { messages: nextMessages }) });
|
||||
@@ -282,6 +285,7 @@ export function LocalAgentPanel({ embedded, headless, autoConnect }: { embedded?
|
||||
}
|
||||
setAgentState({ activity: "发送中", sending: true });
|
||||
const messageId = createId();
|
||||
const userText = text || `发送了 ${files.length} 张图片`;
|
||||
let threadId = useAgentStore.getState().activeThreadId;
|
||||
try {
|
||||
if (!threadId) {
|
||||
@@ -290,14 +294,15 @@ export function LocalAgentPanel({ embedded, headless, autoConnect }: { embedded?
|
||||
if (!threadId) throw new Error("新建对话失败");
|
||||
setAgentState({ activeThreadId: threadId, messages: [], tokenUsage: null });
|
||||
}
|
||||
addMessage({ id: messageId, role: "user", text: text || "发送了图片", attachments: files });
|
||||
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", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
prompt: requestPrompt,
|
||||
messageText: text || `发送了 ${files.length} 张图片`,
|
||||
messageText: userText,
|
||||
messageId,
|
||||
clientId: clientIdRef.current,
|
||||
threadId,
|
||||
@@ -541,8 +546,13 @@ export function LocalAgentPanel({ embedded, headless, autoConnect }: { embedded?
|
||||
if (!connected || !threadId || sending || waiting) return;
|
||||
setAgentState({ loadingThreads: true });
|
||||
try {
|
||||
const data = await fetchAgentJson<AgentThreadResponse>(endpoint, token, `/agent/codex/threads/${encodeURIComponent(threadId)}/resume`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({}) });
|
||||
setAgentState({ activeThreadId: data.thread?.id || threadId, messages: normalizeHistoryMessages(data.messages || []), tokenUsage: null, activeTab: "chat", activity: "已恢复会话" });
|
||||
const current = useAgentStore.getState();
|
||||
const [data, storedMessages] = await Promise.all([
|
||||
fetchAgentJson<AgentThreadResponse>(endpoint, token, `/agent/codex/threads/${encodeURIComponent(threadId)}/resume`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({}) }),
|
||||
readAgentUserMessages(threadId),
|
||||
]);
|
||||
const localMessages = current.activeThreadId === threadId ? current.messages : [];
|
||||
setAgentState({ activeThreadId: data.thread?.id || threadId, messages: mergeHistoryAttachments(normalizeHistoryMessages(data.messages || []), [...storedMessages, ...localMessages]), tokenUsage: null, activeTab: "chat", activity: "已恢复会话" });
|
||||
} catch (error) {
|
||||
addEventLog("恢复对话失败", error);
|
||||
message.error(error instanceof Error ? error.message : "恢复对话失败");
|
||||
@@ -556,6 +566,7 @@ export function LocalAgentPanel({ embedded, headless, autoConnect }: { embedded?
|
||||
setAgentState({ loadingThreads: true });
|
||||
try {
|
||||
await Promise.all(threadIds.map((threadId) => fetchAgentJson(endpoint, token, `/agent/codex/threads/${encodeURIComponent(threadId)}/delete`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({}) })));
|
||||
void deleteAgentThreadMessages(threadIds).catch(() => undefined);
|
||||
const current = useAgentStore.getState();
|
||||
const deleted = new Set(threadIds);
|
||||
setAgentState({
|
||||
|
||||
Reference in New Issue
Block a user