Repository navigation
[PR] merge: tier-2 consolidation train — 15 PRs + repair of #4525's sidScope compile break - #4551
Merged
Merged
Conversation
(cherry picked from commit 9d000df)
(cherry picked from commit 970d2b9)
…lane stops re-overflowing (#3900) An anthropic-wire upstream can still bill max_tokens inside the context wall: DeepSeek's /anthropic endpoint answers a 1,048,576-token request with "requested 1370540 (1114540 in the messages, 256000 in the completion)", while shouldReserveOutputHeadroom() exempts the anthropic protocol, so the preflight judged those messages against the full window. #3450 already learns the stated completion share from that rejection, but it books it on the one-shot overflow arm, and the next real usage report retires the arm and the share together (#1110). The recovered session then judges on the declared window again, over-fills, and re-earns the same 400 — each recovery paying a fresh multi-range emergency fold during which the client sees only a timer. Keep the proof on its own lifetime: a session-level provider fact scoped to the upstream origin and model that stated it. armOverflowShrink() stamps it; the output-headroom step reserves it on wires that do not reserve output budget themselves, so the nudge/preflight denominator becomes window - proven share. A route or model change, or an absent/zero proof, falls back to the untouched protocol rule; the one-shot arm and its #1110 retirement are unchanged. Tests: tests/e2e-proven-completion-wall.test.ts proves the share survives the usage report, that the next turn judges on window - completion, and that a foreign origin cannot claim it; context-overflow.test.ts covers scoping and corrupt walls. (cherry picked from commit 62289ef)
After an overflow proves a completion share against the stated wall, handle.ts rewrites effectiveContextLimit to window - share, so the #3738 arm clamp now lands at min(stated messages, proven window). Pin the new number and keep the stated ceiling assertion so the rule, not the width, is what the case checks. (cherry picked from commit e6bf8c0)
…change) (cherry picked from commit db7c389)
#4205 split the display window (effectiveContextLimit, the declared wall) from the judged one (reservedContextLimit). The proven-share clamp acts on the judged window, so the e2e assertions must read reservedContextLimit; effectiveContextLimit stays the declared 40000 and is asserted as such. (cherry picked from commit da1c3ac)
… fold-reconcile fully re-anchors (#4131) The #1001 unannounced-rewrite detector judged history rewrites on raw-id hash identity alone, outside the order-centric reconcile ladder. A bili upgrade across v0.1.201 re-mints non-text message ids by design (#3642 D1 accepted this one-time churn and promised the reconcile ladder would absorb it), so on the first turn after a cross-version restart the detector saw the collapse, marked a compaction boundary anyway, and applyCompactionArchive pruned the ref map + tombstoned hundreds of still-cited refs — destroying fold coverage for a pure transform (incident: 212 covered refs re-anchored with 0 unmatched, then pruned to 392 live ids). detectUnannouncedHistoryRewrite now takes this pass's fold-reconcile verdict: when kind is reanchored/resend with unmatched === 0, the collapse is explained churn — the destructive boundary is suppressed and an id-churn-adoption conflict event + info log are recorded instead. Off/noop verdicts carry no evidence and keep legacy behavior; partial claims (unmatched > 0) keep the legacy escalation path unchanged. Second facet (issue expectation #4): applyCompactionArchive now names in its log line how many refs the prune drops and which block coverages they sit under, so any remaining destructive prune is auditable instead of silent. Tests: tests/issue4131-cross-version-churn-gate.test.ts pins both directions (explained churn suppressed; genuine rewrite still escalates; off/noop verdicts do not suppress; legacy callers unchanged) plus the log facet. (cherry picked from commit 4cb446b)
The client renders the `error` string, but bili answered every proxy failure
with the constant "acp-proxy failure" while the real cause already sat in
detail / upstreamFailure / alerts. Changes:
- buildProxyFailureResponse() shared by both egress sites (startServer dispatch
catch + inline engine): `error` is now informative ("acp-proxy failure: <kind>
[<code>] [host[:port]]", legacy prefix kept, stays a string); a round-level
circuit-breaker fail-fast answers 503 + Retry-After (next-probe interval)
instead of a generic 502. Initial transport failures stay 502; overflow 413.
- structured upstreamFailure gains host/circuitOpen/retryAfterSeconds (additive,
byte-identical when absent); the inline lane now emits it too (was omitted).
- circuit-open fail-fast logs throttled to one [warn]/window/host; the per-request
[error]+stack is suppressed via an expected-fail-fast marker (stops the storm).
- free-text circuit reason masks the host through the same rule as the embedded
last-failure detail (non-public -> <private-host>), ending the raw-next-to-masked
contradiction; the actionable endpoint rides structured upstreamFailure.host
(loopback-exempt). Free-text masking otherwise unchanged (#255 intact).
- circuit last-failure detail truncates at a whitespace boundary, not mid-token.
Addresses #4254 (defects 1,3,4,5,6; defect 2 pending
a design decision tracked in-thread). Regression: tests/issue4254-*.test.ts.
(cherry picked from commit 670f4a0)
Defect 2 render half of #4254, pairing with the GET /__bili/upstreams read surface shipped in #4257. bili's DSH client registers a frame-level shell.overlay slot that polls /__bili/upstreams every 10s and renders one row per unreachable upstream (host:port, kind, errno code, consecutive-failure tally, and a retry ETA while the circuit is open); it returns null while every upstream is reachable, so the banner auto-dismisses when circuits close + alerts clear. - Display identity is the forwarded host:port (bili is a transparent pipe); provider/model reverse lookup stays host-side (DSH reads its own baseURL). - Defensive parse of the report never throws: a broken health report cannot break the DSH client. - Purely additive UI, no request-path changes; deferred injection is fail-open on hosts whose build lacks the shell.overlay slot. - i18n zh/en in sync; deterministic render test (populated / healthy / multi-host). (cherry picked from commit dce1538)
…ute model traffic at fetch time (#4276) The V2 opencode entries (native + launcher) rewrote event.request.url in place inside their unscoped http.request hook. opencode hands that one mutable event to every matching hook — provider-ID-scoped plugins included — so URL-validating provider plugins saw the proxied <origin>/bili/<upstream> URL instead of the real upstream: Kiro's AWS bridge silently never engaged (empty assistant replies), Antigravity's request hook threw on the non-canonical origin. Root fix: the routes keep only discovery/degrade (resolveLive -> proxyBase, oversize local refusal); both setup()s now install the shared native fetch intercept chain which performs the rewrite at fetch time, leaving every hook-visible payload untouched. The WS handshake hook builds its tunnel itself (byte-identical output for formerly-working shapes). Also honors providers.<url>.direct in these lanes (previously ZCode-only): the hook marks exempt requests (no stamping/incremental/ws-tunnel) and the chain sends them direct before any proxy resolution. No new config surface — completes the documented field in the opencode lanes. Behavior changes: provider plugins now see original upstream URLs (the fix); signed-body model requests in the V2 lane get the local #1884 refusal instead of unsigned-through-bili -> upstream 401; #3304 prepareDirectSend + #3248 onResponse acks activate in V2 processes (were dead code); launcher dead-origin handling moves from preemptive unwrap to chain recover/retry/give-up-direct. Closes #4276 (cherry picked from commit 5602c07)
…ef residue After a mid-conversation model/upstream switch the ref ledger restarts at m00001 while the client-persisted history keeps citing previous-generation refs, which look exactly like live ones — the model keeps aiming compress at dead ranges until the circuit breaker fires. The reactive receipts (#1029, flails through them, so the fix is a STANDING part of the context. One-shot evidence-based detection (>=2 distinct above-frontier citations in visible history), verdict persisted in session metadata, static number-free note appended to the ACP standing system block on every wire plus the round-2 fold rebuild in exact steady-path order. Display-only: no storage, ref-space, gate or breaker changes. (cherry picked from commit ae1f0f0)
(cherry picked from commit d839b38)
The 'proxy respawn gave up' dsh client event was a single bare sentence with no branch information, so users hitting hours-long direct-send episodes could not self-diagnose which failure mode they were in. - native-intercept: the two send-failure recovery paths now record lastUnavailableReason='send-failed' immediately before firing onGiveUp (the resolver paths already recorded dead/busy-exhausted/bootstrap- failed); UnavailableReason gains the member. Consumers compare only against 'busy-exhausted', so the new member is inert elsewhere. - dsh-native: the three duplicated onGiveUp closures collapse into one fireGiveUp seam that appends ': <reason>' to the persisted line when the interceptor recorded one, keeping the original bare wording when none is present. _resetRegisterForTest clears the reason between scenarios; _nativeStateForTest exposes the shared state to suites. - launcher: killAbandonedChild no longer swallows win32 taskkill failures or a failing final SIGKILL — both now log a warn naming the pid, since a surviving listener is exactly the confusion #4125's netstat showed. Behavior change (intended): the give-up line gains a ': <reason>' suffix (dead | busy-exhausted | bootstrap-failed | send-failed) whenever the interceptor recorded why nothing was routable; hosts parsing the exact old string will see the longer form. No wire, config, persistence or kernel changes. (cherry picked from commit b3d8ee7)
… checkpoint-framing quotes from triggering rebases Incident (v0.1.204, dsh web profile + Cherry Studio gateway): an UNSTAMPED dsh auto-review request (fixed REVIEW_POLICY system + one freshly-flattened history blob, no tools) arrived on a session already sticky-bound to dsh by earlier main turns. Every isolation gate keyed on the LIVE x-bili-plugin header missed it, so it rode the raw main session key through the full pipeline: its blob quoted an old compaction checkpoint mid-text, which false-positived the #2432 native-compaction rebase (0/112 ids retained -> total refs/blocks/fold-state wipe), and its tiny usage sample clobbered the usage-grade baseline the #453 output clamp anchors on (676541 -> 127497) — max_tokens stayed 384000 against real inputs past the window edge, producing permanent upstream 400s. - side-request.ts: new isDshAutoReviewRequest() shape detector (fixed REVIEW_POLICY system prefix + exactly one user entry + no tools), version-pinned like DSH_TITLE_SYSTEM_PREFIX (#2503). - handle.ts: review shape joins sideRequestLike and rides the #388 stateless side passthrough on every wire — no refs/blocks, no rebase detection, no usage settle (relay useRewriter gate keeps side responses off the ledger). Gate is asymmetric by design: identity side requires the LIVE dsh stamp (unstamped reviews already key verbatim there); lane side accepts dsh evidence = live stamp OR sticky session binding — the sticky leg is what catches the incident's unstamped extension traffic. - dsh-compaction-guard.ts: carriesDshLocalCompactionSummary now requires the standalone-checkpoint shape (message STARTS with the preamble or the open tag); framing QUOTED mid-message no longer counts — per the file's version-pin doctrine, template drift degrades to a miss, never a false positive that wipes a live session. Behavior change (disclosed): dsh auto-review requests that used to fork onto persisted |sub:<fp> sessions (#1916/#2203) now ride the stateless passthrough regardless of stamping. The forked lane had no functional value (each review re-serializes the whole blob, so ref ids churn and nothing ever folds) and #3681 showed it overflowing its own window. Tests: new tests/issue4438-dsh-review-lane-isolation.test.ts (unit both directions + e2e incident repro: unstamped review on a sticky-bound folded session leaves refs/boundary/baseline intact and forwards byte-exact); tests/issue2432-dsh-local-compaction.test.ts carrier pins flipped to the standalone contract; tests/dsh-persona-fingerprint.test.ts rewritten from the superseded fork contract to the stateless-passthrough contract on all four wires; side-lane-invariants allowlist extended with justification. Fixes #4438 (cherry picked from commit bfe25bf)
…passthrough tests/dsh-persona-continuity.test.ts still pinned the superseded fork behavior (review request mints a |sub: session) that this PR replaces: the openai-lane e2e now asserts no fork minted, main session requests/refs untouched, 200 verbatim forward. The gated real-dsh twin (e2e-native-dsh-persona scenario B) flips its acceptance from forks>=1 to zero forks for the same reason. Resolver-level unit blocks are unchanged by design: the resolver itself still forks a discontinuous shape — only the wiring keeps auto-review blobs off it. (cherry picked from commit b306aa6)
…cit per-session marker (#4458) Add a HOST-NATIVE COMPACTION section to acp_status, shown per-session when a host-native compaction landing is recorded (session.metadata.nativeCompactionBoundary), in BOTH compression modes. It distinguishes "the host compacted this conversation" from "bili folded it", shows pending-rebase vs rebased state, and names the resolved client/lane. Proactive visibility ahead of the reactive DEAD REFS / breaker / conflict-watch surfaces, so an operator or agent can see a landing BEFORE a follow-up call burns a ref minted before the boundary. Additive display-only output: absent when no landing was recorded; no wire, config, cache, or identity impact. Regression test covers both gate directions (#3617). (cherry picked from commit 3676ee6)
…e result as possibly-undefined
…#4452) An intermediate gateway that rewrites every reject into one generic body (Cherry Studio et al.) strips the token counts / window from each overflow 400, so inspectContextOverflow returns isOverflow:false and bili's whole overflow self-heal silently never engages — the route starves of calibration and re-sends the same oversized payload forever. Decision-free observability slice of #4452 (behavioral recovery awaits owner sign-off on thresholds): - add isMarkerlessOverflowReject() (util.ts): a 4xx overflow-shaped reject whose body matches no CONTEXT_OVERFLOW_PATTERNS (excludes 5xx, which already self-heal via the local estimate, and any body that carries a marker). - the relay logs a loud warn naming the starvation + the last meter reading (settled input vs effective window) instead of only the opaque `<- upstream 400` line. Purely additive logging — no behavior change when isOverflow is true, no new config surface. (cherry picked from commit 6906dfa)
…eadout Closes #3693 A failing embed-route probe or Context-tab status poll was swallowed silently: the settings panel and the Context tab spun on the connecting hint forever with zero evidence of what actually failed. Remote-access users (page served from a LAN IP while the panel binds to server loopback) could neither see nor copy the underlying network error. Every failure state now renders the raw engine message plus the failed URL in a selectable block with an explicit copy action (navigator.clipboard.writeText with an execCommand fallback, since LAN-http pages are not secure contexts). Probe errors are transient by design: never cached, retried on every poll tick until a verdict lands. (cherry picked from commit a8acce2)
…4482) Opt-in body dumps (ACP_DUMP_BODY / diagnostics.dumpBody, REREQUEST dumps, BILI_DUMP_4XX err/summary-err dumps) write the full plaintext conversation body to disk and may carry real credentials (tool arguments, host-injected content), yet nothing warned the user. #4397's reporter saw live credentials in such dumps; sharing them into an issue leaks them publicly. No automatic redaction — byte-exactness is the point of these dumps. Instead every plaintext-body dump file now starts with one standing warning line (PLAINTEXT DUMP ... scrub before sharing): - raw/*-REQ.txt, raw/*-INCOMING.txt, dumps/req-*.json, req-*-REREQUEST.json and err-*.json / summary-err-*.json all get the banner (DUMP_PLAINTEXT_BANNER in src/dump-banner.ts). - bili acp-cache diff strips the known leading banner line explicitly, so pre-banner (old-format) dumps keep parsing byte-identically — old and new files also mix fine in one dir (tested). The banner text must stay free of ": " (head lines parse as headers); pinned by test. - tools/analyze-dumps.mjs strips the banner before JSON.parse; its local copy of the literal is checked against src/dump-banner.ts by test. - CONFIGURATION.md en+zh warnings for dumpBody/dumpReq/dump4xx go through the gen-config-docs seed (website/config-reference/diagnostics.yaml), keeping the docs-sync drift guard green. Behavior change (intended, disclosed per owner policy): dump file FORMAT changes for all six writers — a one-line banner is prepended. Consumers that strictly parsed dumps/req-*.json or err-*.json as pure JSON from outside this repo will see a non-JSON first line; in-repo consumers are updated. Body bytes after line one are untouched (byte-diff forensics preserved). Tests: new tests/issue4482-dump-banner.test.ts (banner pinned + tool sync, stripDumpBanner unit, real-writer harness asserting INCOMING/REQ/JSON banners with intact bodies, runDiff old/new/mixed-dir equivalence) plus banner assertions added to error-dump / issue1993 / reasoning-echo suites. (cherry picked from commit e354285)
…d images — #4124 Upstreams with a hard per-prompt image-count wall (e.g. vLLM --limit-mm-per-prompt) reject the whole request ("Too many images in request: 165 > 30"); bili forwarded that 400 verbatim every round and the session dead-ended. The reject vocabulary only knew token/context shapes, so the error was not even classified as a capacity rejection. - inspectContextOverflow detects image-count rejections on an independent channel (imageCountCap/imageCountSeen); isOverflow keeps its token-only meaning so no token-space arm can be minted from a count wall (parseOverflowWindow/toTokenNumber would reject <1000). - shedWireImagesToCount: count-space twin of shedWireImages (rung A only — downscaling cannot reduce count; tail yields to the hard cap; no MAX_SHED_IMAGES slice) converging the outbound copy to cap-1. Originals stay in session storage behind stable placeholder refs (decompress({imageRef}) recovers them). - Relay: same-request rescue (converge + re-send once; the client sees success), the learned cap persists per-origin in stats.imageCountCaps, later requests to the same route converge pre-send (no rejected round-trip), and the pck A/B probe skips content-wall 400s instead of white-running a key-less ablation that fails identically. (cherry picked from commit 632b28a)
Contributor
📦 Built Package ArtifactBranch: Latest build — install this exact versionnpm install -g billion-context@0.0.5499-pr.4551.e44a70dThis exact version is immutable and reproducible. The rolling Recent builds (commit → version)
Download artifact
tar xzf billion-context-pr4551.tgz
npm install -g packageInstall instructions are refreshed on each push. |
Contributor
📦 Merged Build PublishedThis merge to master was published as npm install -g billion-context@0.0.819-master.b5a4f60 # this exact build
npm install -g billion-context@master # always the newest merged build
|
This was referenced Oct 24, 2026
Closed
This was referenced Oct 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
这是什么
第 2 级合并列车(#4525 的续集):15 个绿票一次落位。合并方式请选 Merge commit。
#4525 遗留了一个编译错误:
src/agent/dsh-native.ts引用的sidScope只存在于 #4424 分支(#4223 与它堆叠)。本列车的 #4424 修复了这一点——建议尽快合并以恢复 master 可编译状态。(列车1 的教训:typecheck 只看 tail 输出没验 exit code,已在本列车修正流程。)车上清单(15 个 PR)
出车与延后(诚实记录,5 个)
另:#4534、#4211 目标 issue 已关,建议直接关单。
验证
kernel/碰撞关单
Closes #4131, Closes #4254, Closes #4305, Closes #4438, Closes #4452, Closes #4458, Closes #3693, Closes #4482, Closes #4488, Closes #4124
Refs #4125, Refs #3900
外部贡献者署名(merge commit 保留作者)
Co-authored-by: WSXYT ws675170761@gmail.com
Co-authored-by: Zhishu 963336185@qq.com