Allow longer local stream silence

ober

89a1f6ac863750847fc1bd6b3787186ab3fbb393

diff --git a/src/jcode/provider/provider.ss b/src/jcode/provider/provider.ss
index 1221373..222a1f5 100644
--- a/src/jcode/provider/provider.ss
+++ b/src/jcode/provider/provider.ss
@@ -540,11 +540,25 @@
 
 ;; Streaming read timeout: if no bytes arrive within this many seconds a
 ;; watchdog thread closes the connection so the read loop unblocks and we
-;; raise a clear error instead of hanging indefinitely. Local model servers
-;; usually emit keepalive/progress bytes during long prefill; silence for this
-;; long is treated as a dead stream. Override at runtime via
-;; (parameterize ((*stream-read-timeout-secs* N)) ...).
+;; raise a clear error instead of hanging indefinitely. Cloud providers usually
+;; emit keepalive/progress bytes during long prefill; silence for this long is
+;; treated as a dead stream. Local model servers can spend minutes in prefill
+;; before producing the first byte, especially for sub-agent prompts with large
+;; tool results, so loopback hosts get a wider floor below. Override at runtime
+;; via (parameterize ((*stream-read-timeout-secs* N)) ...).
 (def *stream-read-timeout-secs* (make-parameter 45))
+(def *local-stream-read-timeout-floor-secs* 300)
+
+(def (loopback-host? host)
+  (or (equal? host "127.0.0.1")
+      (equal? host "localhost")
+      (equal? host "::1")))
+
+(def (stream-read-timeout-for-host host)
+  (let ((base (*stream-read-timeout-secs*)))
+    (if (loopback-host? host)
+      (max base *local-stream-read-timeout-floor-secs*)
+      base)))
 
 ;; Streaming HTTP POST: calls line-cb with each line of the response body.
 ;; Used for SSE (Server-Sent Events) streaming from LLM APIs.
@@ -572,7 +586,7 @@
 (def (jcode-http-post-stream url headers body-json line-cb)
   (let-values (((scheme host port path) (parse-url-parts url)))
     (let ((req (build-http-request "POST" path host headers body-json))
-          (timeout-secs (*stream-read-timeout-secs*)))
+          (timeout-secs (stream-read-timeout-for-host host)))
       (if (equal? scheme "https")
         ;; HTTPS via rustls -- guarded by a watchdog thread that closes the
         ;; connection if no bytes arrive for timeout-secs seconds.