fix(webui): keep multi-file apply_patch edits
This commit is contained in:
@@ -272,10 +272,16 @@ function absorbCompleteAssistantMessage(
|
||||
}
|
||||
|
||||
function fileEditKey(edit: Pick<UIFileEdit, "call_id" | "tool" | "path">): string {
|
||||
if (edit.call_id && edit.path) return `${edit.call_id}|${edit.tool}|${edit.path}`;
|
||||
if (edit.call_id) return `${edit.call_id}|${edit.tool}`;
|
||||
return `${edit.tool}|${edit.path}`;
|
||||
}
|
||||
|
||||
function fileEditToolEventKey(edit: Pick<UIFileEdit, "call_id" | "tool" | "path">): string {
|
||||
if (edit.call_id) return `${edit.call_id}|${edit.tool}`;
|
||||
return fileEditKey(edit);
|
||||
}
|
||||
|
||||
function toolEventFileEditKey(event: ToolProgressEvent): string | null {
|
||||
const fn = (event as { function?: { name?: unknown } }).function;
|
||||
const name = typeof event.name === "string"
|
||||
@@ -292,7 +298,7 @@ function hasFileEditForToolEvent(messages: UIMessage[], event: ToolProgressEvent
|
||||
const key = toolEventFileEditKey(event);
|
||||
if (!key) return false;
|
||||
return messages.some((message) =>
|
||||
message.fileEdits?.some((edit) => fileEditKey(edit) === key),
|
||||
message.fileEdits?.some((edit) => fileEditToolEventKey(edit) === key),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -305,7 +311,7 @@ function filterCoveredFileEditToolEvents(
|
||||
}
|
||||
|
||||
function stripCoveredFileEditToolHints(message: UIMessage, edits: UIFileEdit[]): UIMessage {
|
||||
const incomingKeys = new Set(edits.map(fileEditKey));
|
||||
const incomingKeys = new Set(edits.map(fileEditToolEventKey));
|
||||
const events = message.toolEvents ?? [];
|
||||
if (!events.length || incomingKeys.size === 0) return message;
|
||||
|
||||
@@ -367,7 +373,14 @@ function mergeFileEdits(existing: UIFileEdit[] | undefined, incoming: UIFileEdit
|
||||
const edit = normalizeFileEdit(raw);
|
||||
if (!edit) continue;
|
||||
const key = fileEditKey(edit);
|
||||
const existingIndex = indexByKey.get(key);
|
||||
let existingIndex = indexByKey.get(key);
|
||||
if (existingIndex === undefined && edit.path) {
|
||||
const eventKey = fileEditToolEventKey(edit);
|
||||
const pendingIndex = next.findIndex((existing) =>
|
||||
!existing.path && existing.pending && fileEditToolEventKey(existing) === eventKey,
|
||||
);
|
||||
if (pendingIndex >= 0) existingIndex = pendingIndex;
|
||||
}
|
||||
if (existingIndex === undefined) {
|
||||
indexByKey.set(key, next.length);
|
||||
next.push(edit);
|
||||
@@ -376,6 +389,7 @@ function mergeFileEdits(existing: UIFileEdit[] | undefined, incoming: UIFileEdit
|
||||
const merged = { ...next[existingIndex], ...edit };
|
||||
if (edit.path && !edit.pending) delete merged.pending;
|
||||
next[existingIndex] = merged;
|
||||
indexByKey.set(key, existingIndex);
|
||||
}
|
||||
return next;
|
||||
}
|
||||
@@ -386,17 +400,25 @@ function findFileEditTraceIndex(
|
||||
incoming: UIFileEdit[],
|
||||
): number | null {
|
||||
const incomingKeys = new Set(incoming.map(fileEditKey));
|
||||
const incomingToolEventKeys = new Set(incoming.map(fileEditToolEventKey));
|
||||
for (let i = prev.length - 1; i >= 0; i -= 1) {
|
||||
const candidate = prev[i];
|
||||
if (candidate.role === "user") break;
|
||||
if (candidate.kind !== "trace") continue;
|
||||
if (segmentId && candidate.activitySegmentId === segmentId) return i;
|
||||
for (const existing of candidate.fileEdits ?? []) {
|
||||
if (incomingKeys.has(fileEditKey(existing))) return i;
|
||||
if (
|
||||
incomingKeys.has(fileEditKey(existing))
|
||||
|| (
|
||||
!existing.path
|
||||
&& existing.pending
|
||||
&& incomingToolEventKeys.has(fileEditToolEventKey(existing))
|
||||
)
|
||||
) return i;
|
||||
}
|
||||
for (const event of candidate.toolEvents ?? []) {
|
||||
const key = toolEventFileEditKey(event);
|
||||
if (key && incomingKeys.has(key)) return i;
|
||||
if (key && incomingToolEventKeys.has(key)) return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user