<img width="801" height="95" alt="Image" src="https://github.com/user-attachments/assets/c27dfc5c-5fe5-430f-b383-d7f078bd14af" /> 流式输出十分钟后对话被截断,以上为报错截图。
Activity
ranxianglei commentedon Sep 1, 2026
ework 接单
ranxianglei commentedon Sep 1, 2026
已定位并修复。
根因:
fetchWithTimeout(src/fetch-util.ts)用的是一个总时长定时器(UPSTREAM_TIMEOUT_MS = 10 分钟),只有当响应体完全读完后才会清除。所以一个健康的、正常跑了超过 10 分钟的流式输出(比如 DeepSeek 的长生成),会在第 10 分钟被强制 abort ——for await循环抛错,emitStreamError写入❌ [ACP] stream error+ finish 事件,客户端就看到对话被截断了。修复:把超时改成**空闲(idle)**语义 —— 每收到一个响应体 chunk 就重置一次定时器。效果:
timeoutMs内没有任何 chunk)依旧会被 abort;clearTimer/ 客户端断开(externalSignal)语义保持不变,调用方看到的Response形状(status/headers/body)一致。改动集中在
fetchWithTimeout,因此覆盖所有上游路径(主forward()、preflight、compress-loop 重放请求、registry)。测试:
tests/fix-437-idle-timeout.test.ts:健康长流完整跑完(已验证在旧的"总时长"实现下该用例会失败);卡死流仍会被 abort;包装后的 body 保留 status + headers。npm run typecheck通过;npm test899/899 通过;npm run build通过。PR:#439
(E2E 需要真实的 codex 客户端 + 本地 Responses 上游,本环境不具备;此改动是
fetchWithTimeout里孤立的超时语义变更,未触碰任何消息重建/改写逻辑,已由完整单测覆盖。合并前如有上游环境,建议跑一下 E2E 核心。)Merge pull request #439 from ranxianglei/2026-09-02_fix-437-idle-timeout
ranxianglei commentedon Sep 2, 2026
已经合并 等待发新版本
fix(ranxianglei#437): make upstream timeout idle-based so long health…
refactor: drop the dead SSE rewriters that kept causing mis-triage