revert: harden Hermes stream recovery around tool-call boundaries (#189) (#192)

Reverts #189 due to reported bugs.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
ekko
2026-04-24 22:18:32 +08:00
committed by GitHub
parent bff6f844e6
commit 70ed0e0dc2
6 changed files with 114 additions and 496 deletions
-85
View File
@@ -414,89 +414,4 @@ describe('SSE stream interception — run.completed', () => {
expect(mockUpdateUsage).toHaveBeenCalledWith('session-split', 200, 50)
})
it('forwards colon-containing SSE deltas around tool events unchanged and disables buffering', async () => {
const runId = 'run-colon-tool'
const sseData = [
`data: ${JSON.stringify({ event: 'message.delta', run_id: runId, delta: '让我直接读文件:A: B' })}\n\n`,
`data: ${JSON.stringify({ event: 'tool.started', run_id: runId, tool: 'read_file', preview: 'file:a.md' })}\n\n`,
`data: ${JSON.stringify({ event: 'tool.completed', run_id: runId })}\n\n`,
`data: ${JSON.stringify({ event: 'message.delta', run_id: runId, delta: '继续:done' })}\n\n`,
]
mockFetch.mockResolvedValue({
status: 200,
headers: new Headers({ 'content-type': 'text/event-stream' }),
body: createSSEBody(sseData),
})
const ctx = createMockCtx({
path: `/api/hermes/v1/runs/${runId}/events`,
search: '',
})
await proxy(ctx)
const forwarded = ctx.res.write.mock.calls
.map(([chunk]: [Uint8Array]) => new TextDecoder().decode(chunk))
.join('')
expect(forwarded).toBe(sseData.join(''))
expect(ctx.set).toHaveBeenCalledWith('Content-Type', 'text/event-stream')
expect(ctx.set).toHaveBeenCalledWith('Cache-Control', 'no-cache, no-transform')
expect(ctx.set).toHaveBeenCalledWith('X-Accel-Buffering', 'no')
})
it('intercepts run.completed with CRLF delimiters and data without a space', async () => {
const runId = 'run-crlf'
setRunSession(runId, 'session-crlf')
const completedJson = JSON.stringify({ event: 'run.completed', run_id: runId, usage: { input_tokens: 321, output_tokens: 45, total_tokens: 366 } })
const sseData = [`data:${completedJson}\r\n\r\n`]
mockFetch.mockResolvedValue({
status: 200,
headers: new Headers({ 'content-type': 'text/event-stream' }),
body: createSSEBody(sseData),
})
const ctx = createMockCtx({
path: `/api/hermes/v1/runs/${runId}/events`,
search: '',
})
await proxy(ctx)
expect(mockUpdateUsage).toHaveBeenCalledWith('session-crlf', 321, 45)
})
it('does not let usage accounting failures abort the SSE stream', async () => {
const runId = 'run-usage-fails'
setRunSession(runId, 'session-usage-fails')
mockUpdateUsage.mockImplementationOnce(() => {
throw new Error('usage db unavailable')
})
const sseData = [
`data: ${JSON.stringify({ event: 'message.delta', run_id: runId, delta: 'before' })}\n\n`,
`data: ${JSON.stringify({ event: 'run.completed', run_id: runId, usage: { input_tokens: 1, output_tokens: 2, total_tokens: 3 } })}\n\n`,
]
mockFetch.mockResolvedValue({
status: 200,
headers: new Headers({ 'content-type': 'text/event-stream' }),
body: createSSEBody(sseData),
})
const ctx = createMockCtx({
path: `/api/hermes/v1/runs/${runId}/events`,
search: '',
})
await proxy(ctx)
const forwarded = ctx.res.write.mock.calls
.map(([chunk]: [Uint8Array]) => new TextDecoder().decode(chunk))
.join('')
expect(ctx.status).toBe(200)
expect(forwarded).toBe(sseData.join(''))
expect(ctx.res.end).toHaveBeenCalled()
})
})