diff --git a/netlify/edge-functions/agent-proxy.ts b/netlify/edge-functions/agent-proxy.ts index 2974ba6..7572f32 100644 --- a/netlify/edge-functions/agent-proxy.ts +++ b/netlify/edge-functions/agent-proxy.ts @@ -59,13 +59,19 @@ const ANTHROPIC_VERSION = '2023-06-01' const ANTHROPIC_API_BASE = 'https://api.anthropic.com' // Downstream keep-alive cadence. The browser's fetch reader appears -// stuck if no bytes flow for too long. -const HEARTBEAT_MS = 10_000 +// stuck if no bytes flow for too long. We keep this tighter than the +// segment budget so the connection stays warm even during long +// model-thinking gaps with no agent events. +const HEARTBEAT_MS = 5_000 -// Close the current response just before Netlify's empirical ~60 s -// streaming cap. Leaves a few seconds for a final `segment_end` write -// + `controller.close()` to flush cleanly. -const SEGMENT_BUDGET_MS = 54_000 +// Close the current response well before Netlify's empirical streaming +// cap. We've observed responses cut anywhere between ~49 s and ~60 s, +// so a 54 s budget is too aggressive — sometimes the platform pulls +// the rug out before our final `segment_end` write + `controller.close()` +// can flush. 40 s gives ~10 s of headroom on the low end of the +// observed range while still amortising session setup (~1 segment per +// ~40 s of brief) across a typical 3-5 min brief. +const SEGMENT_BUDGET_MS = 40_000 // Total wall-clock allowance across ALL segments for a single user // brief. Past this we emit a final `error` line and stop reopening. @@ -660,13 +666,12 @@ Please follow your instructions to produce the funding outlook brief.` } if (done || segmenting) { - // Drain the live-stream body we opened above so we don't - // leak the connection. - try { - await upstream.cancel() - } catch { - /* ignore */ - } + // The upstream fetch is already cleaned up by + // `segmentAbort.abort()` (or will be by the runtime once + // we drop our reference). Don't `await upstream.cancel()` + // here — on an already-aborted body that call can hang + // on Deno Edge, which would prevent us from writing the + // final `segment_end` line. break }