Skip to content

fix: tolerate SSE-buffering reverse proxies in the composer - #631

Open
c-with wants to merge 1 commit into
agegr:mainfrom
c-with:fix/sse-buffering-proxy-resilience
Open

fix: tolerate SSE-buffering reverse proxies in the composer#631
c-with wants to merge 1 commit into
agegr:mainfrom
c-with:fix/sse-buffering-proxy-resilience

Conversation

@c-with

@c-with c-with commented Aug 27, 2026

Copy link
Copy Markdown

What does this PR do?

Deployments behind a reverse proxy / WAF that buffers text/event-stream responses break two client paths, because SSE frames never reach the browser:

  1. ensureEventsConnected() before sendAgentCommand waits for a connected event that never arrives — the composer silently swallows the message (input clears, no POST, no error). This PR races it against a 3s timeout (SSE_CONNECT_RACE_TIMEOUT_MS), so the prompt always goes out; a missed stream only costs event-based niceties, not the prompt itself.
  2. Prompt settlement then relies on the reconcile poll (15s default). Kept the default, added opt-in NEXT_PUBLIC_AGENT_STATE_RECONCILE_MS so buffering-proxy deployments can lower it (e.g. 2000 → UI settles ~2s after completion).

Design note

Defaults are unchanged — direct-connection users see zero behavior difference. The env override is opt-in for non-ideal proxies where the correct server-side fix (disabling SSE buffering) isn't available.

Related Issue

No existing issue found for the silent-swallow symptom.

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

hooks/useAgentSession.ts:

  • two await ensureEventsConnected(...) call sites → raced against SSE_CONNECT_RACE_TIMEOUT_MS (3s)
  • AGENT_STATE_RECONCILE_MS now overridable via NEXT_PUBLIC_AGENT_STATE_RECONCILE_MS (default unchanged at 15s)

Verification

Running behind Nginx with SSE buffering on (cannot change the upstream WAF config): without the fix, prompts never POST and the UI hangs; with it, prompts send, complete, and render within ~2s using the env override. Full vitest suite passes (844 tests), tsc --noEmit clean.

中文摘要:反代/WAF 缓冲 SSE 时,ensureEventsConnected 永远等不到 connected 事件,输入框会静默吞掉消息(不 POST 不报错)。改为 3s 竞速保底必发;reconcile 轮询保持 15s 默认、新增 NEXT_PUBLIC_AGENT_STATE_RECONCILE_MS 供缓冲代理部署降到 2s。默认行为零变化。

Deployments behind a reverse proxy or WAF that buffers text/event-stream
responses break two client paths, since SSE frames may never reach the
browser:

1. ensureEventsConnected() before sendAgentCommand waits for a connected
   event that never arrives — the composer silently swallows the message
   (input cleared, no POST, no error). Race it against a 3s timeout so the
   prompt is sent regardless; a missed stream then only costs event-based
   niceties, not the prompt itself.
2. Prompt settlement relies on SSE events; with the stream buffered the UI
   settles only on the next reconcile poll (15s default). Keep the default
   but allow NEXT_PUBLIC_AGENT_STATE_RECONCILE_MS to lower it (e.g. 2000)
   so a finished prompt settles the UI within ~2s on such deployments.

Verified behind an Nginx reverse proxy with SSE buffering enabled: prompts
send, complete, and render within ~2s with the env override; without the
fix the message never POSTs and the UI hangs until a manual refresh.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant