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 };
|
||||
}
|
||||
|
||||
const assistantMessages = sessionMessages
|
||||
.filter(
|
||||
(msg): msg is AssistantSessionMessageRecord =>
|
||||
isAssistantMessage(msg.info) && !isFullySyntheticMessage(msg.parts)
|
||||
);
|
||||
// Pick the latest assistant message by created-time (tiebreak by id) in a
|
||||
// single pass — sync reconciliation can splice messages out of array order.
|
||||
const isLater = (a: AssistantSessionMessageRecord, b: AssistantSessionMessageRecord) => {
|
||||
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) {
|
||||
return { activePartType: undefined, activeToolName: undefined, statusText: 'working', isGenericStatus: true };
|
||||
let lastAssistant: AssistantSessionMessageRecord | null = null;
|
||||
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) => {
|
||||
const aCreated = typeof a.info.time?.created === 'number' ? a.info.time.created : null;
|
||||
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];
|
||||
if (!lastAssistant) {
|
||||
return { activePartType: undefined, activeToolName: undefined, statusText: 'working', isGenericStatus: true };
|
||||
}
|
||||
|
||||
let activePartType: 'text' | 'tool' | 'reasoning' | 'editing' | undefined = undefined;
|
||||
let activeToolName: string | undefined = undefined;
|
||||
|
||||
Reference in New Issue
Block a user