Repository navigation
fix: keep prose between paired render tags in tag-echo filter (#1720) - #1721
Conversation
The PAIRED regex's inner group ([^<>]{0,64}) was meant to hold a tag ref
but matched any up-to-64 non-angle-bracket characters, including newlines
and ordinary assistant prose; stripAcpTags then deleted the whole match,
silently eating answer text that happened to sit between a leaked open and
close tag. The streaming filter had the same hole via its LONE_OPEN ->
swallow-until-close path for spans within the swallow budget.
Narrow PAIRED to a real ref shape (m + >=4 digits, optional surrounding
whitespace) and make the streaming swallow release a non-ref body at close
time, dropping only the tag fragments. Ref echoes are still stripped
completely on both paths; imitation swallowing is unchanged.
📦 Built Package ArtifactBranch: Option A — Install this exact build (recommended)npm install -g billion-context@0.0.1674-pr.1721.3e83bfbThis exact version is immutable and reproducible. The rolling Option B — Download artifact
tar xzf billion-context-pr1721.tgz
npm install -g packageInstall instructions are refreshed on each push. |
[bot] 🏷 Review 完成:可以合并(merge 仍由人执行)。按 PR Review 规范逐项如下。 ① 原 issue 审核(方向关)#1720 属实且方向正确: ② 回归审查(既有行为清点)diff 触碰
严格单调性验证:new PAIRED 的匹配集 ⊆ old PAIRED 的匹配集(引用形 body 必然是 ≤64 个非尖括号字符),流式侧改动只把「整段丢弃」转为「释放正文 + 只删闭标签」且仅限非引用形 body 且 ③ 代码审查(主关)
④ 测试有效性审查
⑤ diff 整洁度4 文件 +70/−16,单 commit,base 即当前 master(已验证祖先关系,无需 rebase);每一行都与本 PR 目的相关,无锁文件/生成物/空行噪音。干净。 证据汇总
一句话中文摘要:修复了 tag-echo filter 中 PAIRED 正则(及流式吞咽状态机)把配对标签间最多 64 字符助手正文当标签引用一并删除的数据损坏问题——收窄为真实引用形状、流式按内容形状判定,既有行为逐项核对无回归、测试有牙且两路 parity 钉住,本地+CI 全绿,可以合并。 |
Fixes #1720
What & why
PAIRED's inner group was([^<>]{0,64})— meant to hold a tag ref (m00123) — but[^<>]also matches newlines, so any up-to-64 non-angle-bracket characters (ordinary assistant prose included) between a leaked open tag and its close were deleted along with the tags bystripAcpTags(). Real-world symptom from the issue: a user pasted a leaked tag into the input box and the following answer text vanished.The streaming filter (
createTagEchoFilter) had the same hole on a different path: an attrs-bearing LONE_OPEN entered swallow-until-close mode and dropped everything up to the close within the swallow budget — prose included.Changes (src/loop/tag-echo-filter.ts only)
PAIRED's inner group to the real ref shape:\s*m\d{4,}\s*. The kernel emits exactly one form between tags —<acp tokens="…" type="…">mNNNNN</acp>with refs ofm+ ≥5 zero-padded digits (acp-kernelREF_WIDTH = 5) — so\d{4,}is a safe lower bound. Per the issue's caveat,PAIREDkeeps nogflag: the filter drives it withexec()on a sliding buffer, and a g flag would accumulatelastIndexacross chunks.releases=false, odd-quote / wrapped-turn) still discards whatever the body is.looseCloseEndsplit intolooseCloseSpan(s)(+ thin wrapper) so the filter can see where the close tag starts.Net effect on both paths: paired tags around prose keep the prose; paired tags around a ref are still stripped completely.
Behavior change (old → new)
PAIRED, streaming path via LONE_OPEN→swallow for spans ≤ ~80 chars.m+ ≥4 digits, optional surrounding whitespace) is deleted with the tags; any other content between paired tags survives with just the two tag fragments stripped. Strictly less deletion than before — no input that previously survived is now deleted. Genuine kernel echoes (≥5-digit refs, incl. whitespace-padded) are unaffected. Tool-call arguments remain byte-exact by design (invariant block atop the file).m1/m001/refbetween paired tags now survives as plain text instead of being eaten — intended, since the kernel never issues such refs.Config surface
None — no config fields, env vars, or CLI flags added or changed.
Tests
stripAcpTags).m1,m2,ref,m001,innerafter a ref) between paired tags — those were only ever stripped because of the over-broad group; updated them to real ≥5-digit refs so they keep testing what they claim to test (plugin 模式 openai SSE:混合文本字段 chunk 因单字段纯标签 echo 被整体丢弃(丢失同 chunk 真实输出) #463 / [bug] #644 follow-up (post-#646, v0.1.97): typo'd render-tag echo (<acip …) bypasses the strip regex and passes through as a 43-char degenerate end_turn turn — agent stalls mid-orchestration until manual nudge #673 / underscore-trigger semantics unchanged).Pre-flight
npm run typecheck— passnpm test— 3283 pass / 0 fail / 17 skipped (e2e-gated skips)npm run build— success