Repository navigation
[PR] feat: add compress.preserveRecentTurns and protectUserMessages (#4593) - #4620
ranxianglei wants to merge 21 commits into
Conversation
(ai: vllm-qwen/qwen3.8-27b) 查重: 不重复。 独立检索 open+closed issues/PRs(preserveRecentTurns / protectUserMessages / turn retention / user message protection / 「按轮保留」多组关键词):唯一相关条目是功能请求本身(GitHub #4592 / ework #4593)与本 PR,无其他实现尝试或同机制在途工作。 标签: scope/kernel, dsh(主改动面在 kernel 保护层;DSH 是驱动场景,机制本身通用)。功能类 PR 不打 bug、不评缺陷严重度。 说明:这是本 daemon 另一会话(ework #4593 工作区)完成的实现 PR,正文无 先报一个已确认的硬伤:PR 正文写 review 进行中:正在拉分支核对代码、跑定向测试、查 CI。 |
d791f53 to
22f6e21
Compare
📦 Built Package ArtifactBranch: Latest build — install this exact versionnpm install -g [email protected]This exact version is immutable and reproducible. The rolling Recent builds (commit → version)
Download artifact
tar xzf billion-context-pr4620.tgz
npm install -g packageInstall instructions are refreshed on each push. |
(ai: vllm-qwen/qwen3.8-27b) 冲突已解决。 PR #4620 分支落后 master 约 497 个提交,rebase 到当前 master( 结构化八问
判定:冲突清零、分支已 rebase 到当前 master 并 force-push,本地定向验证绿;待 CI 全量 + 人工 review(kernel 变更保持人工把关)。 |
kernel-change: per-turn retention window + hard protection for user messages requested in #4593 — the host side cannot express either: turn boundaries are positional (slide per user message, incompatible with permanent BLOCKED-ref identity decisions) and user-message hard exclusion needs to ride the shared isMessageProtected predicate so every consumer (refs, projection, report, absorb/ccr guards) stays in sync automatically. - turn-retention.ts: computeTurnRetention — visible user-message boundaries (consecutive users = one, summary carriers excluded), strict window starting AT the (N+1)-th-from-last boundary - recommend.ts: Rule 4 unions the retention suffix into the soft zone (partial overlap = existing #3953 carve + warning, full containment = zone fatal) - protected.ts: protectUserMessages hard-excludes user text messages (BLOCKED refs); compress.ts gate extended so the predicate actually runs - unit-projection.ts: warnings/fatal attribute causes (last-N-turns retention vs classic recent zone vs protectUserMessages) - config.ts: validateConfig checks; types.ts optional fields (defaultConfig untouched -> golden wire-contract stays stable) - version 0.0.151 -> 0.0.152
Config surface (owner-gated mapping, per-tier like siblings): - compress.preserveRecentTurns?: number (0/unset = off): last N turns (turn = user message -> next user message) join the soft-protected zone; submitted ranges crossing the boundary are carved with an auditable warning, fully-contained ranges are rejected - compress.protectUserMessages?: boolean (unset = false): every user text message hard-excluded (BLOCKED refs, never advertised), same semantics as protectedTools - parseCompressSettings: finite >= 0 number (explicit 0 = off) and strict boolean validation; COMPRESS_SETTING_FIELDS updated - mergeCompress/applyCompressSettings: per-field global -> provider -> model cascade with ?? (an explicit off at any tier wins) - preflight overflow relaxation deliberately does NOT touch preserveRecentTurns: folding user instructions defeats the feature, so unresolvable overflow fails loudly instead - CONFIGURATION.md en+zh: table rows + deterministic behavior spec (reject / filter+receipt / priority order)
(ai: vllm-qwen/qwen3.8-27b) 查重:不重复 —— 本条为 CI 自动修复车道(attempt 1/2、2/2 合并交付),四条红已逐条分账并对照 #4599 总控核实,无新立项。 CI 四条红全部已在分支修复并推送,新 head 修复分账
八问
预期残留红:重触发后 11 个 test 分片仍会命中继承的 LIVE_FILES tripwire(#4599 盘点第 2 条: 结论:点名的四条门禁本地验证全绿、已推送,等 CI 重触发;剩余红均属 #4606 工作流,非本 PR 代码问题。本车道收工。 |
…ior; floor fractional preserveRecentTurns Review fixes for the turn-retention feature (review of this PR): - types.ts / turn-retention.ts / recommend.ts: three comments still described the abandoned design (ANY overlap rejected). The implemented behavior is two-tier: partial overlap carves at the boundary with an auditable receipt, full containment rejects. Also corrected the window-start wording in types.ts (the boundary itself is included). - turn-retention.ts: floor fractional N. The host floors at parse time, but acp-kernel ships standalone — a direct-API fractional value would index boundaries[] fractionally and silently degrade to off. - unit-projection.ts: zone error wording uses the effective (floored) N. - tests: fractional-N case (1.9 -> window of 1 turn).
…carrier-exclusion wording and kernel version refs Review fixes for the turn-retention feature (review of this PR): - The two new compress keys were added to COMPRESS_SETTING_FIELDS and to the generated index tables by hand, but their seeds were missing from website/config-reference/*.yaml — both the config-surface-guard and the docs-sync CI jobs fail on that. Add the seeds (cw.prturns / cw.pum) and regenerate all derived artifacts through tools/gen-config-docs.mjs. Regeneration also corrects the hand-edited table rows: the Env column is for BILI_* env vars (these keys have none), so it renders '—'. - Detail sections claimed summary carriers are role system; the actual exclusion is by SUMMARY_HEADER text prefix on user-role messages (tool results never match on role). Fix en+zh wording to match the code (kernel/src/protected.ts isUserTextMessage). - Kernel version refs 0.0.152 -> 0.0.164: the branch was rebased onto master, where acp-kernel is at 0.0.163, so this change ships as 0.0.164 (CI-enforced version discipline).
e204c60 to
9df2264
Compare
(ai: vllm-qwen/qwen3.8-27b) Review 结论:修复 + rebase 后可合并(等新 head 先说我在 PR 分支上直接做的四类事(均已 push,独立标注 commit):
结构化八问
测试有效性(④):kernel 13 个测试有牙(精确块构成 deepEqual、精确 ref 值如 BLOCKED/m00001..m00005、warning/error 正则、defaults-off 负守护),能抓住 filter↔reject 回归与审计归因丢失;host 侧 4 个钉住 cascade/显式 false 胜出/parse 边界(0=off、3.7→3 floor、strict boolean 拒 "yes"/1)。本地定向全绿:kernel 保护族 76 + recommend 族 41 + turn-retention 13、bili compress-settings 47、verif-subset-guard(compress.ts 在 Tier-A 守护清单内)2/2、config-surface-guard ✓、docs-sync 幂等 ✓、typecheck ✓、build ✓。全量套件按纪律交远程 CI——新 head 的 CI 正在跑,转绿后我会补一行确认。 判定:可合并(CI 绿后)。合并请 owner 执行:#4620 |
…ync gate) The rebase bumped kernel/package.json to 0.0.164 but the kernel's own lockfile (standalone acp-kernel identity, #2092) still recorded 0.0.163 — the lock-sync CI job diffs the regenerated kernel/package-lock.json and fails on that drift. (The original branch had the same latent drift from its 0.0.151 -> 0.0.152 bump.)
….164 (lock-sync gate) Follow-up to the kernel/package-lock.json sync: the root lockfile also carries the file:-link's recorded version under packages."kernel", which the rebase bump to 0.0.164 left at 0.0.163 — the lock-sync job's root regen diffs exactly that line.
…ment #4316 PR3/3 (restampRefsToLedger) re-homes citation numbers once on every full-mode cold load; goldens pinning exact ref numbers are born-red. Re-pin the 7 storage-lane tests (engine-proofs x6 + orphan-recovery x1) modulo that renumbering — zero production code touched. - golden-storage-drive.ts: per-turn citation-normalized digest (shaCiteNorm) and state digest modulo stripOneTimeAlignment (stateDigestAligned); the deep-sort helper is hoisted into golden-canonical.ts for sharing. - corrupt-index-child.ts: new stripOneTimeAlignment normalizer — strips the refLedgerAligned stamp and re-homes refs onto canonical raw-id order so a clean full-mode read and the legacy fallback load compare equal. - storage-engine-proofs.test.ts: wire legs assert shaCiteNorm equality vs the legacy restart leg and the committed goldens; state legs assert stateDigestAligned; rollback leg now spells BILI_STORAGE_UNIFIED=legacy explicitly (#3818 flip: absent resolves to full, deleting the env would vacate the legacy path). - storage-orphan-recovery.test.ts: pins the content-addressable byRaw->byRef round-trip instead of a counter-era m00003 literal. - golden/storage-render/*.json regenerated with shaCiteNorm/stateDigestAligned. Converges the master-red storage-lane family (#4599 plan A, owner directive).
…dger parity masks The #4316 one-time renumbering restamped messageRefs but left state.tokenSnapshot keyed by the OLD refs, so post-restart bodies carried stale per-message token counts (1.3K vs 1.4K) and the full-mode-restart wire drifted off the legacy baseline (#4599). persist.ts: two-pass remap (stage moves, delete old keys, then write new keys) so entries whose new ref collides with another move's old ref survive; fires only when superseded refs exist. Aligned normalizer (corrupt-index-child.ts): the cacheLedger's segment bookkeeping (be/bl/bs, unfinalized tail line), the derived agg block, and per-line miss-attribution (prov/seam) are restart experience, not content — no-restart and restart legs legitimately differ there. Masked in the aligned view only; raw digests keep full accounting. Goldens regenerated (stateDigest + aligned). storage-engine-proofs 12/12, golden-storage-render 5/5 locally.
…persisted state (#4599) The fork-survival test asserted immediately after the compress tool response, but the fold lands in the live session state asynchronously (#3573 replay semantics). Slow CI runners persisted the pre-fold state ('0 block(s) (compressions=0)' cold load) and the deepEqual diverged. Bounded 5s wait on the in-memory fold; fast machines exit on the first iteration. Product semantics unchanged.
The #4316 cold-load alignment reads the unified sidecar; its ingest of just-flushed records completes asynchronously. Slow runners compared the pre-ingest state and saw refLedgerAligned missing while the fold itself had landed. Poll (bounded 5s) until the loaded state carries the flag; fast machines exit on the first probe.
Git-for-Windows autocrlf checks the bundled .md posts out with CRLF; the exact-match '---' fences then failed to parse and every post lost its title/excerpt/date frontmatter (Windows CI: 'bundled meet-bili post found' assertion). Normalize CRLF to LF inside parseFrontmatter when the fence is CRLF-shaped; LF content is byte-identical as before. Regression test with a CRLF fixture.
…help-merge sweep)
… storage mode (#4599) The #4316 cold-load alignment sets refLedgerAligned only when the unified store is available; config-clean machines (CI runners) run legacy mode where it is an honest skip. The test asserted the flag unconditionally, so every shard carrying this file was born-red on CI while unified-config machines passed. Expectation now mirrors store.replicasAvailable(); verified 49/49 in BOTH modes locally.
…help-merge sweep)
…mspec wrap on win32 (#4599)
The ci-check lane's expectation change (c496cfb) exposed a real Windows bug: stripInheritedProxy builds inlineEnv as a spread copy of process.env, which keeps the original key casing (ComSpec) — Windows env vars are case-insensitive but plain objects are not. planClientSpawn's exact env.COMSPEC probe then missed it and silently degraded every wrapped spawn to the bare-name 'cmd.exe' fallback (verified by repro on a real win32 box: childEnv.COMSPEC=undefined while process.env.COMSPEC was set). Fix: probe COMSPEC/ComSpec/comspec in planClientSpawn and planDshSpawn. Verified on real Windows (Node 22): launcher-opencode-inline + fix-679 + dsh-refresh 37/37; host tsc clean, 35 pass / 2 win32-skips.
…OMSPEC case-probe c496cfb re-derived the win32 expectation from ambient process.env, which still mismatches production: Node's process.env member access is case-insensitive on Windows while the child env handed to the planner is a plain object whose key keeps the OS casing (ComSpec), so the exact env.COMSPEC probe fell through to the bare-name fallback. The assertion now derives from the child's OWN captured env/options and probes COMSPEC/ComSpec/comspec exactly like planClientSpawn does post-dfeea8a11 — self-contained under any concurrent-test env churn in single-process CI shards — and additionally pins windowsVerbatimArguments=true on the wrap.
…help-merge sweep)
kernel-change: #4593 requests two protection mechanisms the host side cannot express: (1) per-turn retention is POSITIONAL — the window slides forward with every new user message, so it cannot ride permanent BLOCKED-ref identity decisions (aged-out messages would stay blocked forever, or force per-turn BLOCKED→numeric migration = prefix-cache churn); it belongs in the kernel's per-turn-recomputed soft zone; (2) user-message hard exclusion must ride the shared isMessageProtected predicate so every consumer (ref assignment, unit projection, report, absorb/ccr guards) stays in sync automatically.
Closes #4592
What
Two opt-in keys under
compress, both off by default:preserveRecentTurns?: number— the last N turns (turn = user message → next user message) join the soft-protected zone alongside preserveRecentMessages/Tokens/latest-user pin. Strict window: starts AT a user-message boundary, never mid-turn. Consecutive user messages count as ONE boundary; summary carriers are not boundaries.protectUserMessages?: boolean— every user text message hard-excluded (BLOCKED refs, never advertised as compressible), same semantics asprotectedTools. Host-injected runtime-context user messages are covered too (deliberate conservative default: over-protection costs ratio, under-protection loses instructions).Submitted-range behavior (per the author's in-thread refinement — filter, don't reject):
Range is entirely within the protected zone (…), cause named)Config surface
compress.preserveRecentTurns(number, default off/0),compress.protectUserMessages(boolean, default off/unset)??merge)Behavior decisions (flagged per owner policy)
Tests
查重: 不重复(open+closed issues/PRs searched, EN+CN keywords, no match)
Kernel version: acp-kernel 0.0.163 → 0.0.164 (branch rebased onto master, where acp-kernel is at 0.0.163; single bump for this change set, CI-enforced)