Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions components/frontend/src/hooks/agui/event-handlers.ts
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -847,14 +847,21 @@ function handleMessagesSnapshot(

// Sort by timestamp so messages from interleaved runs appear in
// chronological order. Messages without timestamps keep their
// relative position (stable sort).
// relative position. Use original index as tiebreaker so that
// thinking blocks with identical timestamps stay interleaved with
// their corresponding agent messages.
const originalOrder = new Map(filtered.map((msg, idx) => [msg.id, idx]))
filtered.sort((a, b) => {
const ta = a.timestamp ? new Date(a.timestamp).getTime() : null
const tb = b.timestamp ? new Date(b.timestamp).getTime() : null
if (ta == null && tb == null) return 0
if (ta == null) return 0 // keep relative position
if (tb == null) return 0
return ta - tb

if (ta != null && tb != null) {
const diff = ta - tb
if (diff !== 0) return diff
}

// Equal or missing timestamps: preserve original snapshot order
return (originalOrder.get(a.id) ?? 0) - (originalOrder.get(b.id) ?? 0)
Comment on lines +853 to +864
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use snapshot order, not merged state order, as the tie-breaker.

originalOrder is derived from filtered, which already inherits state.messages ordering for existing IDs. If the local order is wrong before reconnect, equal-timestamp messages stay wrong after the snapshot too, because this sort never reorders them to match normalizedMessages. That means the reconnect path can still leave thinking blocks stacked instead of interleaved.

Proposed fix
-  const originalOrder = new Map(filtered.map((msg, idx) => [msg.id, idx]))
+  const snapshotOrder = new Map(normalizedMessages.map((msg, idx) => [msg.id, idx]))
+  const mergedOrder = new Map(filtered.map((msg, idx) => [msg.id, idx]))
   filtered.sort((a, b) => {
     const ta = a.timestamp ? new Date(a.timestamp).getTime() : null
     const tb = b.timestamp ? new Date(b.timestamp).getTime() : null

     if (ta != null && tb != null) {
       const diff = ta - tb
       if (diff !== 0) return diff
     }

-    // Equal or missing timestamps: preserve original snapshot order
-    return (originalOrder.get(a.id) ?? 0) - (originalOrder.get(b.id) ?? 0)
+    // Equal or missing timestamps: prefer authoritative snapshot order.
+    // Fall back to current merged order for messages not present in the snapshot.
+    return (snapshotOrder.get(a.id) ?? mergedOrder.get(a.id) ?? 0)
+      - (snapshotOrder.get(b.id) ?? mergedOrder.get(b.id) ?? 0)
   })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const originalOrder = new Map(filtered.map((msg, idx) => [msg.id, idx]))
filtered.sort((a, b) => {
const ta = a.timestamp ? new Date(a.timestamp).getTime() : null
const tb = b.timestamp ? new Date(b.timestamp).getTime() : null
if (ta == null && tb == null) return 0
if (ta == null) return 0 // keep relative position
if (tb == null) return 0
return ta - tb
if (ta != null && tb != null) {
const diff = ta - tb
if (diff !== 0) return diff
}
// Equal or missing timestamps: preserve original snapshot order
return (originalOrder.get(a.id) ?? 0) - (originalOrder.get(b.id) ?? 0)
const snapshotOrder = new Map(normalizedMessages.map((msg, idx) => [msg.id, idx]))
const mergedOrder = new Map(filtered.map((msg, idx) => [msg.id, idx]))
filtered.sort((a, b) => {
const ta = a.timestamp ? new Date(a.timestamp).getTime() : null
const tb = b.timestamp ? new Date(b.timestamp).getTime() : null
if (ta != null && tb != null) {
const diff = ta - tb
if (diff !== 0) return diff
}
// Equal or missing timestamps: prefer authoritative snapshot order.
// Fall back to current merged order for messages not present in the snapshot.
return (snapshotOrder.get(a.id) ?? mergedOrder.get(a.id) ?? 0)
- (snapshotOrder.get(b.id) ?? mergedOrder.get(b.id) ?? 0)
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/frontend/src/hooks/agui/event-handlers.ts` around lines 853 - 864,
The tie-breaker currently builds originalOrder from filtered (variable filtered)
so equal-timestamp messages keep the pre-reconnect local ordering; change the
comparator to use the snapshot/normalized order instead: derive originalOrder
(or a separate snapshotOrder map) from normalizedMessages and use that map in
the sort comparator (inside filtered.sort) as the fallback when timestamps are
equal or missing so messages are reordered to match the snapshot's
normalizedMessages order rather than the existing filtered order.

})
state.messages = filtered

Expand Down
Loading