Repository navigation
fix: hold the client through long preflight compression (#568) - #571
Conversation
📦 Built Package ArtifactBranch: Option A — Install from npm PR tag (recommended)npm install -g billion-context@pr-571Each push to this PR publishes a new version under the Option B — Download artifact
tar xzf billion-context-pr571.tgz
npm install -g packageThis comment is automatically updated on each push. |
[bot] 🏷 Reviewing PR #571 now. Plan: (1) re-run pre-flight on head |
[bot] 🏷 Review complete — LGTM with minor (non-blocking) notes. Full pre-flight re-run on head Pre-flight re-verification (all claims confirmed):
Code review — key correctness points I verified:
Minor findings (report-only, none block merge):
Bottom line: root cause (zero-byte silence racing undici's 300s |
a9b98a7 to
5783e03
Compare
[bot] 🏷 Got it — re-reviewing the linearized head |
[bot] 🏷 Re-review of linearized head Linearity & mergeability (confirmed):
Semantic-merge fidelity (checked hunk-by-hunk vs the round-1 head
Pre-flight re-run on
Earlier minor notes — status: all four still stand (expected, since the PR content was preserved verbatim): Bottom line: the semantic merge is faithful (hold over master's newer preflight, master's measured fit decisions kept, payloadEstimate gate dropped, in-band delivery intact), the tree is green, and it merges cleanly onto today's master. Gate remaining: full E2E run, then human merge — #571 (I can't merge per AGENTS.md). |
5783e03 to
7bdcc73
Compare
|
已解决与 master(d0c941c,含 #593/#550/#558)的冲突,force-push 到本分支(5783e03 → 7bdcc73,保持单提交)。 冲突点(#558 同改 preflight 区):
语义交互已核:#571 的早提交(200 + keep-alive)与 #558 的 unknownBaseline fail-fast 共存 — grace 内失败仍走结构化 5xx(#301 全过),grace 外失败走 in-band 错误事件;fits 判定沿用 #558 的 Pre-flight:typecheck ✓;定向 preflight-hold 5 + preflight-fork-estimate 2 + preflight-fail-fast 4 = 11/11 ✓;全量 1155/1157(2 个失败为 plugin-agent.test.ts 已知环境失败,与改动无关);build ✓。 |
[bot] 🏷 Got it — re-reviewing |
7bdcc73 to
dce7502
Compare
[bot] 🏷 Got it — verifying |
Semantic merge onto current master: the hold wrapper (grace timer + beginPreflightHold early-commit + try/finally stop) now wraps the master-side preflight call including wireOverhead, and the forward/fail gate keeps master's rebuilt-payload check (#330/#488/#496) instead of the older payloadEstimate gate. In-band error delivery after an early commit (#568) unchanged.
[bot] 🏷 Re-review of Linearity: single commit whose parent is current master tip Conflict resolutions (hunk-by-hunk):
Pre-flight re-run on Content fidelity: Bottom line: conflict resolution is correct, the semantic interaction with #558 is sound, tree is green, merge is a clean fast-forward onto today's master. Remaining gate: full |
dce7502 to
19f0fbb
Compare
|
(更正上条:反引号被 shell 吃了,补一句)补进 CONFIGURATION.md / CONFIGURATION.zh-CN.md 环境变量参考表的是 BILI_PREFLIGHT_HOLD_MS(预压缩挂起宽限期,默认 30000)。 |
Removed section on preflight hold and its implications for response handling.
[bot] 🏷 Amend The amend (dce7502 → 19f0fbb) touches exactly 3 doc files:
Master moved past the PR parent again:
So the semantic interaction with #583's role-rejection retry ladder is safe: with an early-committed 200, the PR's State: single commit, parent |
What
Preflight compression sent zero bytes to the client until it finished. On big sessions it takes minutes (summarization round-trips to the same upstream — the issue logs show 48s–407s per run, with one finished run at 265,882ms), so any client whose header deadline expires mid-compression aborts. With undici's default
headersTimeoutof 300s the proxy then detects the disconnect, drops the in-flight compression, never forwards, and the client retries into the same wall — the repeating ~5-minute death loop from #568.Fix
When preflight outlives a grace period (default 30s, override
BILI_PREFLIGHT_HOLD_MS), the proxy commits the response early and holds the client with keep-alive bytes:200+text/event-stream+ SSE comment keep-alives (: bili-preflight, every 15s — spec-mandated no-op for every SSE consumer, same pattern as OpenAI's pings)200+application/json+ whitespace padding (valid JSON padding)x-bili-preflight: compressingheader for observabilityEach byte resets undici's inactivity-based
bodyTimeout, so the client is held for the whole compression regardless of duration.Trade-off (documented): once the status is committed, late failures can no longer change it. They are delivered in-band instead — a protocol-native error event (
preflight_compress_failed) for streams (per-protocol shapes: OpenAI data-frame error +[DONE], Anthropic/Responsesevent: error), and the identical structured error body under the already-sent 200 for JSON. Upstream non-2xx after an early commit is likewise delivered in-band (protocol error event / verbatim error body). Fast paths (preflight finishes within the grace) are unchanged and keep full status-code fidelity — all existing #301 fail-fast tests pass untouched.Changes
src/server.ts:beginPreflightHold()+ grace timer aroundpreflightCompress; in-band fail-fast branch inhandle();headersSentguards inforward()(codex forge response, non-2xx upstream branch, 2xx commit)src/stream-error.ts:emitPreflightError()— per-protocol in-band error emission, never throwstests/preflight-hold.test.ts: 4 e2e cases (stream success, stream 429 fail-fast, Responses 429 fail-fast, non-stream success) with timing assertions proving headers arrive before the slow summarization completes and the response still waits for itREADME.md: "Preflight hold" sectionBoth compression modes (AGENTS.md §6)
The hold is wire-level: plugin-mode responses pass through verbatim, where SSE comment lines are inert no-ops for ACP clients and a leading whitespace byte is inert JSON padding; the in-band error events use the same protocol shapes plugin mode already emits on stream errors. No mode-specific state is touched.
Pre-flight
npm run typecheck— cleannpm test— 1051/1052 pass; the single failure (launcher.test.ts: resolveClientCommand codex/claude resolve to themselves) also fails on the unmodified tree in this environment (it assertscommand === 'codex'but this sandbox has acodexbinary on PATH that resolves to an absolute path) — pre-existing, unrelatednpm run build— successE2E_CHECK=1 node --import tsx --test tests/e2e/e2e-codex.test.tspasses (codex CLI + dist verified); the fullACP_TEST_E2E=1run needs a reachable Responses-compatible upstream (http://127.0.0.1:8199/v1default), which is unreachable from this environment — please run it (or the CI dispatch) before merging