fix(status): pick latest assistant message in single pass (#1297)
* fix(status): pick latest assistant message in single pass Sync reconciliation can splice messages out of array order, so the previous "filter + sort + last" pipeline could pick the wrong record as the latest assistant message and surface a stale status. Replace it with a single-pass scan that compares created-time (tiebreak by id) — same selection, but resilient to non-sorted message arrays. * refactor: use comparator + running max for latest assistant pick Address Greptile review: collapse the dual-exit-point single-pass max finder (separate continue + isNewer branches, repeated null checks) into a single isLater(a, b) comparator with one running-max comparison per iteration. Same selection rule, easier to verify. --------- Co-authored-by: vhqtvn <8930337+vhqtvn@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
co-authored by
vhqtvn
parent
cbe6335ed9
commit
4f25c3009d
@@ -198,28 +198,29 @@ export function useAssistantStatus(): AssistantStatusSnapshot {
|
|||||||
return { activePartType: undefined, activeToolName: undefined, statusText: 'working', isGenericStatus: true };
|
return { activePartType: undefined, activeToolName: undefined, statusText: 'working', isGenericStatus: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
const assistantMessages = sessionMessages
|
// Pick the latest assistant message by created-time (tiebreak by id) in a
|
||||||
.filter(
|
// single pass — sync reconciliation can splice messages out of array order.
|
||||||
(msg): msg is AssistantSessionMessageRecord =>
|
const isLater = (a: AssistantSessionMessageRecord, b: AssistantSessionMessageRecord) => {
|
||||||
isAssistantMessage(msg.info) && !isFullySyntheticMessage(msg.parts)
|
const aTime = typeof a.info.time?.created === 'number' ? a.info.time.created : null;
|
||||||
);
|
const bTime = typeof b.info.time?.created === 'number' ? b.info.time.created : null;
|
||||||
|
if (aTime !== null && bTime !== null && aTime !== bTime) return aTime > bTime;
|
||||||
|
return a.info.id.localeCompare(b.info.id) > 0;
|
||||||
|
};
|
||||||
|
|
||||||
if (assistantMessages.length === 0) {
|
let lastAssistant: AssistantSessionMessageRecord | null = null;
|
||||||
return { activePartType: undefined, activeToolName: undefined, statusText: 'working', isGenericStatus: true };
|
for (const candidate of sessionMessages) {
|
||||||
|
if (!isAssistantMessage(candidate.info) || isFullySyntheticMessage(candidate.parts)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const typed = candidate as AssistantSessionMessageRecord;
|
||||||
|
if (lastAssistant === null || isLater(typed, lastAssistant)) {
|
||||||
|
lastAssistant = typed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedAssistantMessages = [...assistantMessages].sort((a, b) => {
|
if (!lastAssistant) {
|
||||||
const aCreated = typeof a.info.time?.created === 'number' ? a.info.time.created : null;
|
return { activePartType: undefined, activeToolName: undefined, statusText: 'working', isGenericStatus: true };
|
||||||
const bCreated = typeof b.info.time?.created === 'number' ? b.info.time.created : null;
|
}
|
||||||
|
|
||||||
if (aCreated !== null && bCreated !== null && aCreated !== bCreated) {
|
|
||||||
return aCreated - bCreated;
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.info.id.localeCompare(b.info.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
const lastAssistant = sortedAssistantMessages[sortedAssistantMessages.length - 1];
|
|
||||||
|
|
||||||
let activePartType: 'text' | 'tool' | 'reasoning' | 'editing' | undefined = undefined;
|
let activePartType: 'text' | 'tool' | 'reasoning' | 'editing' | undefined = undefined;
|
||||||
let activeToolName: string | undefined = undefined;
|
let activeToolName: string | undefined = undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user