fix(chat): group run output and hide empty messages
Build / build (push) Canceled after 0s
Playwright / e2e (push) Canceled after 0s

This commit is contained in:
2026-11-05 11:06:36 +08:00
parent 586776998b
commit 276e2389d6
8 changed files with 155 additions and 3 deletions
+56
View File
@@ -82,6 +82,62 @@ test('streams a run and renders markdown, code, math and mermaid without leaking
expect(api.unexpectedRequests).toEqual([])
})
test('does not render empty assistant bubbles between tool calls', async ({ page }) => {
await authenticate(page, TEST_ACCESS_KEY, 'research')
await mockHermesApi(page)
await mockChatSocket(page)
await page.goto('/#/hermes/chat')
await sendChatMessage(page, 'run tools')
const { sessionId } = await waitForRunSessionId(page)
await page.evaluate((sid) => {
const socket = (window as any).__PW_CHAT_SOCKET__.latest
const common = { session_id: sid, run_id: 'blank-bubbles' }
socket.__trigger('run.started', { ...common, event: 'run.started' })
for (let i = 0; i < 3; i++) {
socket.__trigger('message.delta', { ...common, event: 'message.delta', delta: '\n ' })
socket.__trigger('tool.started', { ...common, event: 'tool.started', tool_call_id: `tool-${i}`, tool: 'terminal' })
socket.__trigger('tool.completed', { ...common, event: 'tool.completed', tool_call_id: `tool-${i}`, output: 'done' })
}
}, sessionId)
await expect(page.locator('.message.assistant')).toHaveCount(0)
await page.evaluate((sid) => {
const socket = (window as any).__PW_CHAT_SOCKET__.latest
socket.__trigger('message.delta', { event: 'message.delta', session_id: sid, run_id: 'blank-bubbles', delta: '\n' })
socket.__trigger('message.delta', { event: 'message.delta', session_id: sid, run_id: 'blank-bubbles', delta: 'Visible answer' })
}, sessionId)
await expect(page.locator('.message.assistant')).toHaveCount(1)
await expect(page.locator('.message.assistant')).toContainText('Visible answer')
})
test('groups assistant output across tool calls until the run ends', async ({ page }) => {
await authenticate(page, TEST_ACCESS_KEY, 'research')
await mockHermesApi(page)
await mockChatSocket(page)
await page.goto('/#/hermes/chat')
await sendChatMessage(page, 'inspect')
const { sessionId } = await waitForRunSessionId(page)
await page.evaluate((sid) => {
const socket = (window as any).__PW_CHAT_SOCKET__.latest
const common = { session_id: sid, run_id: 'grouped-run' }
socket.__trigger('run.started', { ...common, event: 'run.started' })
socket.__trigger('message.delta', { ...common, event: 'message.delta', delta: 'First update' })
socket.__trigger('tool.started', { ...common, event: 'tool.started', tool_call_id: 't1', tool: 'terminal' })
socket.__trigger('tool.completed', { ...common, event: 'tool.completed', tool_call_id: 't1', output: 'done' })
socket.__trigger('message.delta', { ...common, event: 'message.delta', delta: 'Second update' })
}, sessionId)
const assistant = page.locator('.message.assistant')
await expect(assistant).toHaveCount(1)
await expect(assistant).toContainText('First update')
await expect(assistant).toContainText('Second update')
await expect(page.locator('.run-end')).toHaveCount(0)
await page.evaluate((sid) => {
(window as any).__PW_CHAT_SOCKET__.latest.__trigger('run.completed', { event: 'run.completed', session_id: sid, run_id: 'grouped-run' })
}, sessionId)
await expect(page.locator('.run-end')).toHaveText('Run output ended')
})
test('opens the session model dialog and the session search from the chat header', async ({ page }) => {
await authenticate(page, TEST_ACCESS_KEY, 'research')
await mockHermesApi(page, { sessions: [sampleSession] })