fix(server): SSE keep-alive on /v1/agent/stream to survive proxy idle timeouts (T-125) #116
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/T-125-agent-stream-keepalive"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Tâche
T-xxx — (titre dans docs/STATUS.md)
Changements
Contrat modifié ?
packages/contract/supabase/migrations—docs/API.md/docs/DATA.mdmis à jour, labelcontract, approbation humaine requiseTests
scripts/check.shvertImpacts cross-domaine
Fichiers hors périmètre de l'agent (et pourquoi) :
Suivi
docs/STATUS.mdmis à jour (ligne de la tâche)docs/CHANGELOG.md: entrée ajoutée sous Unreleaseddocs/DECISIONS.md/docs/ECARTS.mdmis à jour si décision ou écartReal bug confirmed against the deployed server: the SSE connection was cut after ~50s systematically while waiting for Hermes to produce its first token (typically mid tool-call), even though Hermes finished responding on its own dashboard. Distinct from the buffering fix in T-104 (X-Accel-Buffering): an intermediary reverse proxy idle timeout cuts a connection with zero bytes in flight, which disabling buffering does not address. pipeline/agent.py::_stream_with_keepalive wraps agent.run_stream() with a persistent asyncio task observed via asyncio.wait(timeout=...) and emits an SSE comment line (": keep-alive\n\n") every agent_stream_keepalive_s seconds (15s default) of inactivity. Using asyncio.wait_for directly on __anext__() was tried first and is documented as a real pitfall: it cancels the underlying task on timeout, closing the async generator for good. Transport-level only, no new SSE event type / no contract change; confirmed by an explicit Rust test that the desktop SSE parser already ignores comment-only frames silently. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011MSWGSF3VJrGALNM8JsE2u