mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-17 09:41:58 +00:00
Two races in the drop-time eager upload: - Resurrected chip: the success path used addComposerAttachment, which re-appends when the id is gone, so a file removed mid-upload reappeared once the upload resolved. Add updateComposerAttachment (update-only; no-op when the chip was removed) and use it on both the eager success path and submit-time sync. - Duplicate upload: submit-time sync didn't join an eager upload still in flight, so drop-then-Enter could fire file.attach twice and leave a duplicate under .hermes/desktop-attachments/. Track in-flight eager uploads by id and await the pending one before deciding to re-upload, reusing its gateway ref. Tests: composer-store no-resurrect unit tests + a join-on-submit integration test asserting a single file.attach. Addresses @helix4u review on #43109.
43 lines
1.5 KiB
TypeScript
43 lines
1.5 KiB
TypeScript
import { afterEach, describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
$composerAttachments,
|
|
addComposerAttachment,
|
|
type ComposerAttachment,
|
|
removeComposerAttachment,
|
|
updateComposerAttachment
|
|
} from './composer'
|
|
|
|
function attachment(overrides: Partial<ComposerAttachment> & Pick<ComposerAttachment, 'id'>): ComposerAttachment {
|
|
return { kind: 'file', label: 'doc.pdf', ...overrides }
|
|
}
|
|
|
|
describe('updateComposerAttachment', () => {
|
|
afterEach(() => {
|
|
$composerAttachments.set([])
|
|
})
|
|
|
|
it('replaces an existing attachment in place', () => {
|
|
addComposerAttachment(attachment({ id: 'file:a', uploadState: 'uploading' }))
|
|
|
|
const updated = updateComposerAttachment(attachment({ id: 'file:a', attachedSessionId: 'sess-1' }))
|
|
|
|
expect(updated).toBe(true)
|
|
const current = $composerAttachments.get()
|
|
expect(current).toHaveLength(1)
|
|
expect(current[0]?.attachedSessionId).toBe('sess-1')
|
|
expect(current[0]?.uploadState).toBeUndefined()
|
|
})
|
|
|
|
it('does NOT resurrect an attachment the user removed mid-upload', () => {
|
|
// Drop → eager upload starts → user removes the chip → upload resolves.
|
|
// The late success must not re-add the removed attachment.
|
|
addComposerAttachment(attachment({ id: 'file:a', uploadState: 'uploading' }))
|
|
removeComposerAttachment('file:a')
|
|
|
|
const updated = updateComposerAttachment(attachment({ id: 'file:a', attachedSessionId: 'sess-1' }))
|
|
|
|
expect(updated).toBe(false)
|
|
expect($composerAttachments.get()).toHaveLength(0)
|
|
})
|
|
})
|