fix(workbench): seal terminal timing without projection (#2032)

This commit is contained in:
Lyon
2026-06-24 09:04:12 +08:00
committed by GitHub
parent df8249eb9b
commit 6e3d6f8aa6
+9 -8
View File
@@ -1838,18 +1838,19 @@ function messageTimingPatchForMerge(message: ChatMessage, value: unknown): Parti
// projection when trace rows arrive out of order.
const current = messageTimingPatch(message);
const currentTiming = current.timing ?? null;
const incoming = normalizeTimingProjection(value);
if (!incoming || isTerminalMessageStatus(message.status) || currentTiming?.finishedAt || currentTiming?.durationMs != null) return current;
const record = recordValue(value);
const incomingStatus = normalizedStatusText(firstNonEmptyString(record?.status));
const incomingTerminal = isTerminalMessageStatus(incomingStatus) || traceResultHasTerminalEvidence(value);
if (!incomingTerminal) return current;
const startedAt = firstNonEmptyString(currentTiming?.startedAt, incoming.startedAt) ?? null;
const durationMs = firstFiniteNumber(incoming.durationMs, record?.durationMs) ?? inferredDurationMs(startedAt, incoming.finishedAt) ?? currentTiming?.durationMs ?? null;
if (!incomingTerminal || isTerminalMessageStatus(message.status) || currentTiming?.finishedAt || currentTiming?.durationMs != null) return current;
const incoming = normalizeTimingProjection(value);
const terminalTiming = incoming ?? ({} as Partial<WorkbenchTurnTimingProjection>);
const startedAt = firstNonEmptyString(currentTiming?.startedAt, terminalTiming.startedAt) ?? null;
const terminalFinishedAt = firstNonEmptyString(terminalTiming.finishedAt, record?.finishedAt, record?.completedAt, record?.updatedAt, terminalTiming.lastEventAt, currentTiming?.finishedAt, currentTiming?.lastEventAt) ?? null;
const durationMs = firstFiniteNumber(terminalTiming.durationMs, record?.durationMs) ?? inferredDurationMs(startedAt, terminalFinishedAt) ?? currentTiming?.durationMs ?? null;
const inferredFinishedAt = inferFinishedAtFromDuration(startedAt, durationMs);
const finishedAt = firstNonEmptyString(incoming.finishedAt, inferredFinishedAt, incoming.lastEventAt, currentTiming?.finishedAt) ?? null;
const lastEventAt = firstNonEmptyString(incoming.lastEventAt, finishedAt, currentTiming?.lastEventAt, startedAt) ?? null;
const timing = { ...currentTiming, ...incoming, startedAt, lastEventAt, finishedAt, durationMs, valuesRedacted: incoming.valuesRedacted !== false && currentTiming?.valuesRedacted !== false } as WorkbenchTurnTimingProjection;
const finishedAt = firstNonEmptyString(terminalTiming.finishedAt, terminalFinishedAt, inferredFinishedAt, terminalTiming.lastEventAt, currentTiming?.finishedAt) ?? null;
const lastEventAt = firstNonEmptyString(terminalTiming.lastEventAt, finishedAt, currentTiming?.lastEventAt, startedAt) ?? null;
const timing = { ...currentTiming, ...terminalTiming, startedAt, lastEventAt, finishedAt, durationMs, valuesRedacted: terminalTiming.valuesRedacted !== false && currentTiming?.valuesRedacted !== false } as WorkbenchTurnTimingProjection;
return { ...current, timing, startedAt, lastEventAt, finishedAt, durationMs };
}