mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-10 08:32:09 +00:00
The in-flight user bubble seeded image attachment refs as `@image:<localpath>`.
In remote-gateway mode that path lives on the desktop, not the gateway, so the
inline thumbnail fetch hit /api/media and 403'd ("Path outside media roots"),
flashing a fallback chip until submit uploaded the bytes.
Seed (and keep) image refs as the raw base64 preview data URL instead. It
renders inline via extractEmbeddedImages with zero network, and survives the
post-sync rewrite (the agent gets the bytes through the attached-image pipeline,
not this display ref) so the thumbnail no longer remounts/flashes. Non-image
refs are unchanged.
Adds optimisticAttachmentRef + unit coverage.
54 lines
2.2 KiB
TypeScript
54 lines
2.2 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import type { ComposerAttachment } from '@/store/composer'
|
|
|
|
import { coerceThinkingText, optimisticAttachmentRef } from './chat-runtime'
|
|
|
|
const DATA_URL = 'data:image/png;base64,iVBORw0KGgoAAAANS'
|
|
|
|
function attachment(overrides: Partial<ComposerAttachment> & Pick<ComposerAttachment, 'kind'>): ComposerAttachment {
|
|
return { id: 'a', label: 'file.png', ...overrides }
|
|
}
|
|
|
|
describe('optimisticAttachmentRef', () => {
|
|
it('renders an image from its in-hand base64 preview (no @image: path ref)', () => {
|
|
const ref = optimisticAttachmentRef(attachment({ kind: 'image', detail: '/tmp/shot.png', previewUrl: DATA_URL }))
|
|
|
|
// The raw data URL flows through extractEmbeddedImages → inline thumbnail,
|
|
// dodging the remote /api/media 403 an @image:<localpath> ref would hit.
|
|
expect(ref).toBe(DATA_URL)
|
|
})
|
|
|
|
it('falls back to an @image: path ref when no preview is available', () => {
|
|
expect(optimisticAttachmentRef(attachment({ kind: 'image', detail: '/tmp/shot.png' }))).toBe('@image:/tmp/shot.png')
|
|
})
|
|
|
|
it('ignores a non-data preview url and uses the path ref', () => {
|
|
const ref = optimisticAttachmentRef(
|
|
attachment({ kind: 'image', detail: '/tmp/shot.png', previewUrl: 'https://example.com/x.png' })
|
|
)
|
|
|
|
expect(ref).toBe('@image:/tmp/shot.png')
|
|
})
|
|
|
|
it('passes non-image attachments straight through to attachmentDisplayText', () => {
|
|
expect(optimisticAttachmentRef(attachment({ kind: 'file', refText: '@file:src/a.ts', previewUrl: DATA_URL }))).toBe(
|
|
'@file:src/a.ts'
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('coerceThinkingText', () => {
|
|
it('strips streaming status prefixes from thinking deltas', () => {
|
|
expect(coerceThinkingText("◉_◉ processing... checking the user's request")).toBe("checking the user's request")
|
|
expect(coerceThinkingText('(¬‿¬) analyzing... reading the file')).toBe('reading the file')
|
|
})
|
|
|
|
it('drops empty thinking rewrite placeholder text', () => {
|
|
expect(
|
|
coerceThinkingText(
|
|
"◉_◉ processing... I don't see any current rewritten thinking or next thinking to process. Could you provide the thinking content you'd like me to rewrite?"
|
|
)
|
|
).toBe('')
|
|
})
|
|
})
|