mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-23 16:36:23 +00:00
fix(desktop): prevent stale optimistic tails after compression (#69682)
This commit is contained in:
parent
6283a33a50
commit
93c97073d8
2 changed files with 54 additions and 3 deletions
|
|
@ -327,6 +327,42 @@ describe('preserveLocalPendingTurnMessages', () => {
|
|||
|
||||
expect(preserveLocalPendingTurnMessages(next, previous)).toBe(next)
|
||||
})
|
||||
|
||||
it('drops stale optimistic history after compression and keeps only the live tail', () => {
|
||||
const compressedAuthority = [
|
||||
msg('stored-user', 'user', 'first turn that survived compression'),
|
||||
msg('stored-assistant', 'assistant', 'latest authoritative reply')
|
||||
]
|
||||
|
||||
const pollutedWarmCache = [
|
||||
msg('user-old-1', 'user', 'compressed-away prompt one'),
|
||||
msg('assistant-old-1', 'assistant', 'compressed-away reply one'),
|
||||
msg('user-old-2', 'user', 'compressed-away prompt two'),
|
||||
msg('assistant-old-2', 'assistant', 'compressed-away reply two'),
|
||||
msg('user-inflight', 'user', 'the one genuinely in-flight prompt')
|
||||
]
|
||||
|
||||
expect(preserveLocalPendingTurnMessages(compressedAuthority, pollutedWarmCache).map(message => message.id)).toEqual([
|
||||
'stored-user',
|
||||
'stored-assistant',
|
||||
'user-inflight'
|
||||
])
|
||||
})
|
||||
|
||||
it('drops the live tail once the latest authoritative user has persisted it after compression', () => {
|
||||
const compressedAuthority = [
|
||||
msg('stored-user', 'user', 'the one genuinely in-flight prompt'),
|
||||
msg('stored-assistant', 'assistant', 'its authoritative reply')
|
||||
]
|
||||
|
||||
const pollutedWarmCache = [
|
||||
msg('user-old-1', 'user', 'compressed-away prompt one'),
|
||||
msg('assistant-old-1', 'assistant', 'compressed-away reply one'),
|
||||
msg('user-inflight', 'user', 'the one genuinely in-flight prompt')
|
||||
]
|
||||
|
||||
expect(preserveLocalPendingTurnMessages(compressedAuthority, pollutedWarmCache)).toBe(compressedAuthority)
|
||||
})
|
||||
})
|
||||
|
||||
describe('appendLiveSessionProjection', () => {
|
||||
|
|
|
|||
|
|
@ -234,9 +234,12 @@ export function reconcileResumeMessages(nextMessages: ChatMessage[], previousMes
|
|||
* dropping either makes an accepted turn appear to vanish during transport
|
||||
* churn.
|
||||
*
|
||||
* Authoritative rows use different ids, so match by role ordinal. A matching
|
||||
* user row is considered committed only when its visible text also matches;
|
||||
* any authoritative assistant at the same ordinal supersedes the local stream.
|
||||
* A lagging projection can be behind by one live turn, never a whole local
|
||||
* history window. Preserve only the newest optimistic user row: compression
|
||||
* rewrites past context, so older `user-*` rows in a warm cache are stale
|
||||
* history, not in-flight work. The latest authoritative user confirms whether
|
||||
* that tail has persisted; any authoritative assistant at the same ordinal
|
||||
* supersedes the local stream.
|
||||
*/
|
||||
export function preserveLocalPendingTurnMessages(
|
||||
nextMessages: ChatMessage[],
|
||||
|
|
@ -257,6 +260,10 @@ export function preserveLocalPendingTurnMessages(
|
|||
|
||||
const nextIds = new Set(nextMessages.map(message => message.id))
|
||||
const previousRoleCounts = new Map<ChatMessage['role'], number>()
|
||||
const newestOptimisticUser = [...previousMessages]
|
||||
.reverse()
|
||||
.find(message => message.role === 'user' && message.id.startsWith('user-'))
|
||||
const latestAuthoritativeUser = [...nextMessages].reverse().find(message => message.role === 'user')
|
||||
const preserved: ChatMessage[] = []
|
||||
|
||||
for (const message of previousMessages) {
|
||||
|
|
@ -272,6 +279,14 @@ export function preserveLocalPendingTurnMessages(
|
|||
continue
|
||||
}
|
||||
|
||||
if (isOptimisticUser && message !== newestOptimisticUser) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (isOptimisticUser && latestAuthoritativeUser && chatMessageText(latestAuthoritativeUser).trim() === chatMessageText(message).trim()) {
|
||||
continue
|
||||
}
|
||||
|
||||
const authoritative = nextByRoleOrdinal.get(`${message.role}:${ordinal}`)
|
||||
|
||||
if (authoritative) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue