Repository navigation
[PR] fix(pi): inline mode now intercepts codex Responses WebSocket traffic (#4526) - #4559
ranxianglei wants to merge 15 commits into
Conversation
9b99c17 to
e44c054
Compare
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b) 查重:不重复——但这是同一 issue 的第二次 PR。 前一次尝试是 PR #4527(branch 标签:bug, pi, codex, scope/wiring, p2(pi inline 车道 codex WS 绕过压缩;inline+codex 用户丢压缩但有绕行,非数据丢失/缓存泄露类,维持 #4527 审定的 p2) 开始按 PR 规范独立审核(不复读 #4527 的结论):①原 issue 方向 → ②回归审查 → ③代码 → ④测试有效性 → ⑤diff 整洁度。另注意到 GitHub 实时状态 |
📦 Built Package ArtifactBranch: Latest build — install this exact versionnpm install -g billion-context@0.0.5639-pr.4559.02db234This exact version is immutable and reproducible. The rolling Recent builds (commit → version)
Download artifact
tar xzf billion-context-pr4559.tgz
npm install -g packageInstall instructions are refreshed on each push. |
e44c054 to
b9e666a
Compare
small fix) #4526 widened the explicit-off tokens from {0,false} to {0,false,no/off}; the pi.inline doc comment in FileConfig still listed only 0/false. Comment-only, matches planNativePi (src/agent/pi-native.ts).
…4559 small fix) Check-run id list left behind while debugging the CI failures; not part of the change.
f90e209 to
af6786f
Compare
small fix) #4526 widened the explicit-off tokens from {0,false} to {0,false,no/off}; the pi.inline doc comment in FileConfig still listed only 0/false. Comment-only, matches planNativePi (src/agent/pi-native.ts).
…4559 small fix) Check-run id list left behind while debugging the CI failures; not part of the change.
(ai: vllm-qwen/qwen3.8-27b) Review 完成,结论:方向正确、代码质量达标,可合(CI 绿后由人合并)。 我独立审了全部 diff(未照抄 issue 结论),ci-worker 修的两个门禁红已逐项验证,另发现并直接修掉一个错误路径缺陷,已 rebase 到最新 master 并推送——新 head af6786f(4 commits:主修复 + 两个小修 + 我的 parity 小修)。 结构化八问:
验证状态:docs-sync 门禁本地实测通过( 收工判定:代码与行为审查通过,无拦下项;待 af6786f 的 CI 全绿即可合并(合并本人来)。标签: bug / pi / codex / scope/wiring / p2(首条回复已打,一句话理由:pi inline 车道 WS 流量漏压缩,功能性缺陷有绕行)。 |
…est (born-red positional drift) (#4559 small fix)
…small fix) DecompositionSource (fold-decomposition-lookup.ts) and WatcherTickDecision (server.ts) are referenced only inside their own files - zero external usages across src/tests/kernel/pi-subagents. The unused-exports gate flags both INTERNAL_ONLY once the #4572 train landed on master. Dropping the export modifier is behavior-neutral (typecheck clean); StoredRow stays exported (has an external consumer).
…from #4572 train (#4559 small fix) The train added the external-summary config surface but the committed generated docs predate it, so docs-sync regenerated one new table row (compress.externalSummary) in each of the four derived files. Mechanical regeneration via tools/gen-config-docs.mjs - no hand edits.
…#4526) BILI_PI_INLINE left the Codex Responses WebSocket lane unpatched on the false premise that "the pi lane does not use codex WS" (pi rides the codex provider over that exact lane, #2073/#2111), so codex WS dialed the model directly — uncompressed, no ACP session record. - inline engine binds a loopback-only WS-upgrade listener (ephemeral 127.0.0.1 port, unref'd) serving the same per-request pipeline through installWebSocketBridge verbatim (loopback-source admission, tunnel destination SSRF guard, 426 fallthrough parity with server.ts); a bind failure degrades ONLY the WS channel direct (warned) — HTTP compression and the engine stay healthy - native-ws-intercept gains the inline lane: rewrites through state.resolveInlineWs/wsOrigin instead of the undialable pseudo-origin; defers connections until the engine decision settles; bootstrap failure degrades direct. Spawn/attach lanes byte-identical (branch keyed on the inline-only resolver being present) - BILI_PI_INLINE value table: any non-empty value other than 0/false/no/off now enables (on/yes/etc. used to fall through to spawn silently, reading as "inline does nothing"); off tokens expanded from 0/false to 0/false/no/off — they now disable over the config-file pi.inline value too; arm-time info log makes the switch observable - docs: CONFIGURATION.md/.zh-CN.md, LIFECYCLE.md - tests: planNativePi truth table, WS-intercept inline seams (fast-path rewrite / deferred hold / degrade-direct / bootstrap rejection), new issue4526-pi-inline-ws integration suite (real engine boot + real ws client round trip through the bridge: probe + turn, ACP tools injected, session recorded; 404 plain HTTP; 426 wrong-shape upgrades)
small fix) #4526 widened the explicit-off tokens from {0,false} to {0,false,no/off}; the pi.inline doc comment in FileConfig still listed only 0/false. Comment-only, matches planNativePi (src/agent/pi-native.ts).
…4559 small fix) Check-run id list left behind while debugging the CI failures; not part of the change.
Review finding on the new inline WS bridge: it passed the bare pipeline to installWebSocketBridge, so if handle() rejected (last-resort internal error), the session's own catch swallowed it (warn + bare end) and Codex saw a truncated stream instead of an error frame. Spawn mode renders the failure into the same envelope via its dispatch catch (informative error, 503+Retry-After on a circuit-open fail-fast, #4254) before resolving. Wrap the bridge's dispatch with the identical egress so both lanes report the same failure shape.
…est (born-red positional drift) (#4559 small fix)
(ai: vllm-qwen/qwen3.8-27b) 状态更新(接上条八问结论):af6786fba 上又红了两个门禁(config-surface-guard、lock-sync)。查了日志与 master 历史,根因都不是本 PR 的代码问题——是我 rebase 之后 #4572 train 才落上 master(kernel 0.0.162→0.0.163 的 lockfile 同步、config-surface-guard 脚本自身的定位 bug、以及三个 internal-only export 的去导出),master 侧已在 当前处置:整支 rebase 到最新 master |
6056114 to
e82ccd7
Compare
…ectSystem test (master born-red, ranxianglei#4599 sentinel) The tier-5 train added generationBoundaryActive as injectSystem's 9th parameter and updated every caller except this test's 'active' case: its trailing true landed in mcpDialectNote, so the note never appeared and the assertion must fail. Identical one-line fix already committed on PR ranxianglei#4559's branch (e82ccd7); drop that commit when rebasing whichever way merges first.
(ai: vllm-qwen/qwen3.8-27b) CI 自动修复通道结论(attempt 2/2):本轮无需代码改动——点名的两个红门禁已在最新 head 变绿,剩余红为 master 继承性失败,等 #4606 合入后 rebase 即全绿。 查重: 不重复 —— 本帖是 PR #4559 自身 CI 的自动修复通道;CI 问题按 owner 政策统一归口 #4599(已评论一行状态),不另立 issue。 事实核对(head
八问:
后续动作(唯一遗留):#4606 合入 master 后,把本分支 rebase 过去并丢弃与 #4606 重复的两个小修 commit( 收工判定:本通道无更多可做(第 2 次尝试,剩余红属等待外部合入,非本分支可修);PR 本身维持「可合并、待 CI 全绿」状态,合并由人工执行。 |
(ai: vllm-qwen/qwen3.8-27b) CI 状态更新(head 8042523,run 40117138820):
代码审核结论不变(floor 7 八问):方向正确、回归面干净、可合——待 CI 绿后由人合并。 |
…used-exports (ranxianglei#4599 inherited red) Rebased head onto master 146b34f resolves config-surface-guard / lock-sync / docs-sync (all stale-base drift fixed there by 4d34abf). The one remaining gate red is the master-born-red ClientBuildCheck (ranxianglei#4599 floor 8: the ranxianglei#4545 train added the type used only inside client-build-note.ts; 4d34abf's de-export sweep missed it). Same one-word fix as ranxianglei#45598042523, duplicated here per the few-line CI-fix discipline so this PR is green without waiting for ranxianglei#4559 to merge. Behavior-neutral: type-only, zero external usages (grep-verified).
…ndary flag reaches injectSystem Born-red inherited from base cd92b0b (ranxianglei#4305 landed after ranxianglei#4344 added the 8th param but its test only passed 8 positional args, so the 'on' case's trailing true bound to mcpDialectNote and generationBoundaryActive stayed false). Byte-identical to ranxianglei#4606 commit 389b815 / ranxianglei#4559 e82ccd7; drop on the post-ranxianglei#4606 rebase.
…est (master born-red, #4599 sentinel) The tier-5 train added generationBoundaryActive as injectSystem's 9th parameter and updated every caller except this test's 'active' case: its trailing true landed in mcpDialectNote, so the note never appeared and the assertion must fail. Identical one-line fix already committed on PR #4559's branch (e82ccd7); drop that commit when rebasing whichever way merges first.
…s site (#4598, #4599 sentinel) pages.yml has failed 75/75 runs since landing: actions/configure-pages@v5 gets 'Get Pages site failed ... Not Found' because GitHub Pages was never enabled on this repo (the earlier enablement:true attempt was reverted without a recorded reason — not re-applied here). Stop-the-bleed per #4598 option 1: drop the push trigger, keep manual workflow_dispatch so the deploy path stays intact and no website push red-lines master again. Final direction (re-enable Pages vs retire the workflow vs in-install serving) awaits owner decision.
…ne-rewrite (#4599 sentinel) The #3719 Updates tab stayed routed (PAGES/route/loadUpdates in client.ts) but its topbar anchor was dropped when #3536 rewrote the whole topbar line, making nav.updates a dead catalog key (web-i18n born-red on every PR head). Restored at the original position (after logs); identical to the sibling fix on the panel-blog branch (900c09c), adapted to master (no blog anchor yet).
…4427 Windows red, #4599 sentinel) maskPathsForPublish keyed off os.homedir() alone. On Windows that reads %USERPROFILE% while git-bash-style tool output carries $HOME paths, so the publish-boundary identity leak masked one spelling and leaked the other (#4427 suite red on windows shards only). Now masks the deduped set of os.homedir() + $HOME + %USERPROFILE%; POSIX behavior unchanged (values dedupe to one root), anchored-flank semantics preserved.
… restamp flag (#4599 sentinel) Deliberate persisted-state change: 380ef4d (#4316 layer 3 PR3/3) added the one-time refLedgerAligned boolean to session state. All four wire digests are UNCHANGED; only the metadata section digest moved (+refLedgerAligned state key). Regen per the suite's own protocol (justification recorded here and in the ci-fixes PR body).
…wed-load interface (#4599 sentinel) #D5-5 changed buildView to call store.unifiedMessageIdRows() + store.unifiedUncoveredRows(id, intervals) first; the pre-existing fake stores (only unifiedTailSeqs/unifiedTailViewRows) threw TypeError, caught by the by-design degrade net as transcript-error, turning every render-expecting assertion red (chain 6/6, verif replay). Extended both real-JS fakes and the symbolic harness portImpls (__method_unifiedMessageIdRows / __method_unifiedUncoveredRows) to mirror SessionStore semantics.
(ai: vllm-qwen/qwen3.8-27b) duplicate of #4669 —— 同一症状 + 同一机制:inline 模式下 codex Responses WS 流量绕过压缩,解法都是回环 WS 桥拦截。#4669(10-25)是 #4526/#4527/#4571 悬空工作的全套 rebase 重提,其第 1 个提交完整包含本 PR 的 WS 桥工作(另加断流根因修复、TUI 日志、 说明:严格按先来后到本 PR(10-24)更早,但 #4669 是其超集且被明确定位为合并载体,故在此打 |
Closes #4526
What & why
With
BILI_PI_INLINEon, pi's HTTP model traffic correctly ran through the in-process engine — but the Codex Responses WebSocket lane was left unpatched on the false premise that "the pi lane does not use codex WS". It does: pi rides the codex provider over exactly that lane (#2073/#2111). So codex WS dialed the model directly — uncompressed, with no ACP session record. The old comment claiming otherwise is removed.Changes
src/inline-engine.ts). The engine binds one real listener: a loopback-only HTTP server on an ephemeral127.0.0.1port that serves WebSocket upgrades only, reusinginstallWebSocketBridgeverbatim from the spawn-mode path (loopback-source admission, tunnel-destination SSRF guard, session lifecycle) with the codex codec and the same per-request pipeline (pipelineFor, shared with the HTTP dispatcher). Unclaimed upgrades keep the 426 contract so Codex's fast-fallback can switch to HTTP POST (which still compresses in-process). Plain HTTP on that port gets a 404. The listener isunref()'d — it never holds the host process open.src/agent/native-ws-intercept.ts,src/agent/pi-native.ts). Inline mode now installs the WebSocket wrapper too. In inline mode the wrapper resolves its target throughstate.resolveInlineWs/state.wsOrigin(the bridge origin, decided when the engine settles) instead ofstate.origin— which is frozen to the pseudo-origin that never listens and would short-circuitreadyOriginto an undialable URL. Connections made before the decision settle are held in the existing deferred shell. Spawn/attach lanes are byte-identical: the new branch keys on the inline-only resolver being present.planNativePi). See Behavior changes below; arm-time info log makes the switch visible in the host log.Behavior changes (old → new)
BILI_PI_INLINEenabled inline only for1/true(case-insensitive);on/yes/ON/… silently fell through to spawn mode (the reported "inline doesn't work, still starts a server" confusion)0/false/no/offenables inline0/false;no/offwere unrecognized, so the config-filepi.inline: truestill WON over them0/false/no/off; they now disable over the file valueinstallNativeWebSocketInterceptentirely[bili-native(pi)] inline mode active — compression runs in-process, no proxy spawned; engine log gains the bridge addressBlank/whitespace values keep today's semantics (neutral — the config file decides). Spawn/attach lanes: no change.
Config surface
No new field/env/flag.
BILI_PI_INLINE(existing, documented in CONFIGURATION.md en+zh) gets a relaxed value table — rows updated in this PR.Tests
tests/pi-native.test.ts: planNativePi truth table (on/yes/ON/" True " → inline; no/off/OFF/false → spawn; no/off beatfileInline=true; blank neutral).tests/native-ws-intercept.test.ts: inline seams — settled fast-path rewrite to the bridge origin (androutedOrigindeliberately NOT recorded, avoiding a The same sessionId is served by two bili processes simultaneously: interleaved logs from global + pi-native dual instanc | 同一 sessionId 被两个 bili 进程同时服务:全局 + pi-native 双实例日志交织、重复 cold-load(split-brain) #3923 split-brain false alarm against the pseudo-origin), deferred hold while booting, degrade-direct on unavailable bridge, bootstrap rejection degrades without unhandled rejection.tests/issue4526-pi-inline-ws.test.ts(hermetic XDG, mock codex WS upstream): real engine boot exposes a dialablewsOrigin; realwsclient round trip through the bridge — probe forwarded verbatim, turn compressed (ACP tools injected), ACP session recorded (the issue's missing-record symptom); plain HTTP on the bridge port → 404; wrong-shape upgrades → 426; end-to-end client wiring withBILI_PI_INLINE=onthrough the patched global WebSocket (deferred shell → attach → exchange).No
kernel/, no version bump, no lockfile churn.