Skip to content

feat: egress image downscale + read_original_image (#3400) - #3425

Merged
ranxianglei merged 13 commits into
masterfrom
2026-10-16_image-rescale-v2
Oct 17, 2026
Merged

ranxianglei merged 13 commits into
masterfrom
2026-10-16_image-rescale-v2

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Closes #3400

一句话

#3295 删掉图片降采样后重做的简化版:发模型前把大图压小(收益 <20% 的不动),原图在会话存储里逐字节原样保留,模型在图片旁被告知两个尺寸后可用 read_original_image 自读原图。kernel 零改动、零原生依赖、无状态。

Behavior change(旧值→新值)

Config surface(新增配置面声明)

  • 新增顶层配置键 imageRescale(boolean,默认 true)——现有面无法表达:feat!: remove image pre-compression and the image_full tool (#3266) #3295 删除的旧键属于「到达时压缩 + restore 状态机」语义,本次是无状态出站 pass,语义不同不复用旧键。
  • 新增 env BILI_IMAGE_RESCALE("0"/"false" 关闭,其余开启,实时读取)与 BILI_IMAGE_RESCALE_MAX_DIM(默认 1600,仅环境变量、无文件镜像,与 spec §6 一致)。
  • 迁移:无需迁移;默认 ON 本身就是上面的行为变化。CONFIGURATION.md en+zh 已同步文档化。

实现要点

  • 单点共享 pass(src/image-rescale.ts):kernel 折叠重建之后、协议编码之前,在解码后的通用 BiliMessage[] 上做形状无关检测,四协议共用同一实现,不做逐协议分叉;responses steady-path 因 patchOriginalItem 从原始 item 重建,另加同模块 item 级 walker。
  • 幂等缓存:sha256(base64) → 缩小后字节,进程内 Map(cap 512),每图只编码一次;同一输入字节必产出同一输出字节与同一注释文本 → 同一历史两轮出站 body 逐字节稳定(测试钉死,image_full 无法被正确调用:同一张图每轮重复降采样(38 次)、restore 提示注入 38 次,模型只能猜 ref → 每轮一次失败+重发,缓存命中从 75% 掉到 28% #3266 缓存命中 75%→28% 的教训)。
  • 注释跟图走:被压图片旁附稳定文本(当前尺寸/字节 vs 原图尺寸/字节);支持注入工具的车道(pi/omp/proxy 注入 + DSH/opencode 插件车道经 handlePluginManifest 注册,owner spec 修正)额外给出 read_original_image("mNNNNN") 调用入口;codex 车道不注册、注释退化为只报尺寸。
  • 防循环(零状态):read_original_image 结果消息按 toolName 结构性豁免不再压缩;插件车道(string 协议)回执文本携带本地 spill 绝对路径,宿主 agent 自行读文件。
  • 纯 JS 编码器 jimp@1.6.1(devDependency EXACT,tsup noExternal 内联,dist 自包含;sharp 不回来)。顺带清理 tsup.config.ts 里 feat!: remove image pre-compression and the image_full tool (#3266) #3295 遗留的死 external:["sharp"](已并入 feat commit 以便每个 commit 独立可构建)。

kernel 边界

kernel/** 零 diff(无版本 bump、无 golden ledger/wire-contract 变更、无 Kernel Boundary 流程)。

验收对照(spec 清单 + owner 补充第 8 条)

  • 大图(>1600 或 >100KB)被压小;小图、低收益图(<20%)原样过(keep-smaller,BMP 线性字节 fixture 钉死)
  • 同一图片只编码一次(hash 缓存);两轮出站 body 逐字节一致(byte-stability 测试)
  • read_original_image 返回字节与会话存储原始字节完全一致(exact-bytes 测试)
  • read_original_image 结果不被二次压缩(结构性豁免测试:后续轮次不带注释、不被替换)
  • codex 车道:manifest 无该工具,注释存在且只报尺寸(gate + note 变体测试)
  • 注释逐轮稳定(buildRescaleNote 纯函数 + 精确模板断言),系统层无新增提示
  • typecheck + 全量套件绿:typecheck 本地干净,全量以 CI test lanes 为准(§7.2);kernel 套件零接触
  • DSH/opencode 车道经 handlePluginManifest 可见(read_original_image 加入 manifest 三协议数组,rescaleOn 门控);pi/omp/代理走注入;codex 不出现且注释不提及该工具

本地证据(定向)

tests/issue3400-image-rescale.test.ts 17/17;npm run typecheck 干净;npm run build 后 dist/index.js 5.4MB 且零 require("jimp")(jimp 全内联);tests/e2e/e2e-image-billing.test.ts 5/5 冒烟。全量回归以 push 后 CI 为准。

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-10-16_image-rescale-v2 (b396861)

Option A — Install this exact build (recommended)

npm install -g billion-context@0.0.4121-pr.3425.387ebef

This exact version is immutable and reproducible. The rolling pr tag tracks the newest test build across all PRs.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr3425.tgz
npm install -g package

Install instructions are refreshed on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

查重:不重复。 按失败签名/主题检索 open+closed issue 与 PR(image rescale / downscale / read_original_image / image compression):本 PR 是该主题唯一在途实现。相关条目均为已终结的旧链——#3266(38 轮猜→拒→重发病灶,closed)、#3270/#3295(删除链,closed)、#1095/#1211(旧版起源,closed)——以及触发重做的 #3398;无并行重复工作,也不存在同根因的在途修复 PR。

开始审核本 PR(feat: egress image downscale + read_original_image)。已核对:分支 2026-10-16_image-rescale-v2(@1bf309e21)与 master 同步(0 behind)、2 commit、22 文件(+2105/−123)、kernel/** 零 diff。按 PR Review 规范执行:①原 issue(#3400)方向核对 → ②回归审查(四协议出站点、工具注入面、loop 重建路径的既有行为清点)→ ③代码审查 → ④测试有效性 → ⑤diff 整洁度,结论用结构化八问回复。本地只跑定向验证(typecheck + 定向测试 + build 产物检查),全量以 CI test lanes 为准(§7.2)。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

Review 结论:方向正确、实现扎实。初头 4 个红门禁已全部修掉并推送(现头 9e885f6,11/11 命名门禁绿),建议 CI test lanes 全绿后合并。

查重(首条已答):不重复——本 PR 是 #3400 的唯一在途实现;相关条目为已关闭的删除链(#3266→#3270→#3295)、旧起源(#1095/#1211)或触发 issue(#3398)。

结构化八问

  1. 是什么问题:feat!: remove image pre-compression and the image_full tool (#3266) #3295 删掉图片预压缩后,大图逐字节透传无任何防线(Image errors accumulating in the conversation | 对话中图片错误堆积 #3398:58 张图原样堆进上下文、账单上涨)。本 PR 是简化版重做:发模型前把大图(最长边 >1600 或 base64 >100KB)压小,会话存储里原图逐字节不动,模型在图片旁被告知两个尺寸,需要时用 read_original_image 自读原图。
  2. 什么原因导致:原病的根因是「到达时压缩 + restore 状态机」(image_full 无法被正确调用:同一张图每轮重复降采样(38 次)、restore 提示注入 38 次,模型只能猜 ref → 每轮一次失败+重发,缓存命中从 75% 掉到 28% #3266 的 38 轮猜测-拒绝循环 → fix: image_full 死循环(#3266)——codex lane 上 restore 通道结构性不可用:按宿主门控 + 提示/回执列出可恢复 ref #3270 → feat!: remove image pre-compression and the image_full tool (#3266) #3295 整删)。本次换成无状态出站 pass——只在出站副本上换小图,零状态、零 restore 路径,从结构上消灭了循环成因。设计打到了根因,不是治标。
  3. 影响范围与严重程度:一次有意的行为变更(已披露):默认 ON,波及所有车道上带图片的会话;超阈值图片从「逐字节透传」变为「降采样后发送」。严重度=预期功能变化而非缺陷;想保持旧行为的用户可 BILI_IMAGE_RESCALE=0 或 imageRescale:false 关闭。
  4. 怎么修复的:单点共享 pass(src/image-rescale.ts)插在 kernel 折叠重建之后、协议编码之前,形状无关检测,四协议同一实现;sha256 缓存保证同输入必出同字节(护上游前缀缓存);注释跟图走、逐轮稳定文本;read_original_image 读本地 spill 返回精确原图字节,其结果按 toolName 结构性豁免再压缩(零状态防循环);codex 车道不注册工具,注释退化为只报尺寸。
  5. 是否有回归问题:逐项审过既有行为——
    • coreToAnthropic 相邻同角色消息合并已核实(kernel/src/wire/anthropic.ts:217-234):独立 note 消息会正确并入同一条 wire 消息,不破交替约束;
    • thinking 家族块不受影响(pass 只动 image part/文本,KDD#10 安全);tool-call arguments 保持逐字节透传(KDD#1039 安全);
    • 两种压缩模式都推过:plugin 车道经 handlePluginManifest 注册(rescaleOn 门控),proxy 车道走注入(armed stamp 门控),codex 不出现且幻觉调用被 isProxyToolFor 服务端拦截;
    • buildLoopRequestBody/buildThreadBody 改为 async(loop 重建路径),全部调用点已更新;carrier 消息用 ephemeral acp_loop_*,不进持久化;
    • 发现并修复了一个真实 bug(并行会话提交 9dd4052):prepare-* 把本地 processedMessages 重新赋值为降采样数组并经 Prepared 返回 → rememberPluginMessages 把缩小后的字节和合成 acp_imgnote_* 消息存进 session.transient.pluginMessages(tool-API 压缩锚定视图),违反 spec 目标 2「wire 收进来什么样就存什么样」。修复正确且最小(responses 项级车道与 loop 重建本就无泄漏);我补了 pin 测试(9e885f6):真实请求过 startServer,断言上游收到的是小图、存储视图保留原始字节逐字节一致且无合成 id;
    • storage-engine-proofs 钉死 BILI_IMAGE_RESCALE=0(6edfc2f):存储重启等价证明对committed参考摘要有意解耦编码器字节,隔离合理;
    • 字节稳定性有测试钉死(清缓存后两轮出站 body 一致),前缀缓存受保护。
  6. 是否破坏缓存:未破坏。上游前缀缓存反被确定性重编码保护(有测试);bili 侧无缓存/重试/增量游标/去重键失效;进程级编码缓存纯新增。
  7. 是否改了设计方向:无转向。新增单点模块 src/image-rescale.ts;kernel/** 零 diff、零原生依赖(jimp 1.6.1 EXACT devDependency,tsup 内联——实测 dist/index.js 5.4MB、零 require("jimp"),dist 自包含成立)。
  8. 是否引入新配置:三项——imageRescale(顶层 boolean,默认 true,env>file>默认)、BILI_IMAGE_RESCALE("0"/"false" 关,实时读取,有文件镜像)、BILI_IMAGE_RESCALE_MAX_DIM(仅 env,默认 1600,floor 128)。CONFIGURATION.md en+zh 均已文档化(yaml seed → 生成器产出,gator-clean);无需迁移。

测试有效性

18 个单测有牙:精确模板正则、对象身份钉死(不变消息同引用)、跨缓存清理的字节相等、Jimp 重解码验尺寸、计数器/日志断言。wire-contract 台账 BASE_EXPECTED 已纳入 read_original_image(初头此处过期,D 车道 5/5 红,已修)。新增持久化 pin 见第 5 问。全量以 CI 为准。

diff 整洁度

22 个文件全部与本 PR 目的相关;lockfile churn = jimp 依赖树(预期);tsup 死代码 external:["sharp"] 清理已标注且相关(#3295 遗留)。

CI 状态

初头(1bf309e)4 个门禁红(unused-exports / docs-coverage / config-surface-guard / docs-sync),直接卡住全量 test lanes——根因:死导出、yaml seed 缺失、手改生成块、wire-contract 期望过期。现已全部修复:并行会话 3 个 commit(含上述持久化 bug 修复 + 存储隔离)+ 我 3 个 commit(orphan 注释清理 + 1600 常量单源化 bdd1bca、wire-contract 台账 02dfa83、持久化 pin 9e885f6)。新头 9e885f6:11/11 命名门禁绿(version/kernel/docs-coverage/pi-subagents/import-graph/plan/config-surface/lock-sync/lane-guard/unused-exports/docs-sync),全量 test lanes 正在跑,判定以远端 CI 为准(§7.2)。

合并判定

建议 CI test lanes 全绿后合并。 auto-merge 不适用(feat + 配置面 + wire 工具面变更);按仓库规则合并由你执行:#3425

另:按标题国际化惯例已将 #3400 标题改为「英文 | 原标题」格式(正文未动)。

@ranxianglei

Copy link
Copy Markdown
Owner Author

ci失败

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

收到,CI 红已在处理中。当前 head(9e885f6)上 4 个 test shard 失败,共 6 处,已逐一定位根因并全部本地复现:

  1. codex 车道工具面泄漏(真问题):isCodexClient 按 user-agent 识别 codex([Question] Previously, Codex displayed the context window size after compression, but it now appears to show an unexpected/incorrect value. #645),而 tests/codex-official.test.ts 的"官方传输"模拟没带 UA → bili 把它当普通 responses 客户端,把 read_original_image 注进了 codex 出站请求。生产真实 codex 带 UA、不受影响;但 spec 要求「codex 车道不出现该工具」目前没有任何测试钉住——正在补一个带真实 codex UA 的集成测试(断言 tools 无此工具 + 大图注释只报尺寸)。
  2. mcp stdio shim 未按车道过滤(spec 与实现不符):src/plugin.ts manifest 只按 rescaleOn 门控,codex 启动的 shim(BILI_CONVERSATION_ID,仅 codex launcher 会设)仍向宿主列出该工具——spec 明确「codex 车道 manifest 无该工具」。PR 里标 [x] 的 gate 测试其实只钉了注释变体,没钉 manifest 面。已在 src/mcp.ts 加 codex-style 启动过滤,launcher 两个 stdio 测试不改期望即转绿。
  3. cache-proof / storage-render golden ×4:注入的 +734B 工具 schema 让确定性 fold/nudge 锚点漂移(A/B dump 证明消息体逐字节一致、唯一差异就是工具 schema)。这两个文件是机制钉死型 proof,按 6edfc2f 先例在文件头固定 BILI_IMAGE_RESCALE=0,不重新生成 golden(避免把无关的 nudge 锚点漂移焊进状态快照)。
  4. wire-contract manifest golden ×2:manifest 端点确实新增了该工具(默认 ON,属预期披露的行为变化),走正规流程 scripts/update-wire-contract-goldens.ts 重生成,diff 只含工具新增行。
  5. plugin-fork ×3:非 codex HTTP 插件车道本就该出现该工具,属过期期望,更新断言。

修完本地定向全绿后 push,CI 转绿再回复闭环。

Redoes the image pre-compression removed in #3295 in simplified form:
on every outbound request (after kernel folding, before protocol
encoding, one shared pass over the generic message structure) an image
whose longest edge exceeds 1600px (BILI_IMAGE_RESCALE_MAX_DIM) or whose
base64 body exceeds 100KB is re-encoded fit-inside (never upscaled,
original format kept) on an outbound COPY only - session storage keeps
the original bytes byte-exact. A re-encode saving less than 20% of the
bytes is discarded (keep-smaller), so small and low-benefit images pass
through untouched. Only PNG/JPEG/BMP are handled (pure-JS jimp encoder,
zero native dependencies - sharp does not return).

Each shrunken image carries a stable annotation beside it reporting
current vs original size. Lanes that register bili tools name
read_original_image("mNNNNN") in the annotation: proxy-injection lanes
get it via the per-protocol manifest lists, plugin lanes (DSH/opencode)
via handlePluginManifest; tool-rejecting lanes (codex) get size-only
notes and no tool. read_original_image returns the original bytes from
local session storage (incomingImageIndex spill files); its results are
structurally exempt from re-shrinking - the zero-state loop guard
against the #3266 guess-reject-resend disease.

Output is deterministic: content-hash encode cache (one encode per
unique image), stable note text derived only from dims/bytes/ref - the
same history produces byte-identical outbound bodies, so upstream
prefix caching is undisturbed (#3266 lesson). Observability: one log
line per unique image and one per original-image read (ref, sizes,
session cumulative count+bytes); no hard cap.

Knobs (config surface): top-level config key imageRescale (boolean,
default true), env BILI_IMAGE_RESCALE ("0"/"false" off, live-read),
env BILI_IMAGE_RESCALE_MAX_DIM (default 1600, env-only). Documented in
CONFIGURATION.md en+zh. Legacy compress.imageCompression.* INERT
warnings remain owned by #3344/#3355.

Also removes the dead external:["sharp"] leftover from #3295 in
tsup.config.ts and adds jimp to the inlined devDependencies list.
kernel/** untouched.
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

收到。当前红的那轮(11 个门禁绿、4 个 test shard 红)跑的是修复前的 head——修复还没推上去,所以它必然还是红的。

状态:6 个失败面已全部在本地定位并修完验证中,还剩最后一组断言更新(plugin-fork 的 manifest 工具列表)和一个同族集成缺口(#1897 all-bili demotion 的名字集没登记新工具,会导致插件车道的 title/侧请求把 ~734B×N 的工具 schema 泄漏到上游——这正是该机制要防的 #1897 类问题)。全部本地绿后我会一次性推送并盯 CI 到全绿,到时再回一条完整结论。预计很快。

awork added 12 commits October 17, 2026 12:43
CONFIGURATION.md en+zh: top-level imageRescale key row, detailed
section, env quick-table rows and long-form env entries for
BILI_IMAGE_RESCALE / BILI_IMAGE_RESCALE_MAX_DIM. CHANGELOG [Unreleased]
entry stating the behavior change (since #3295 images passed through
byte-identical at original resolution; now large outbound images are
sent shrunken by default on an outbound copy only).
IMAGE_RESCALE_DEFAULT_MAX_DIM was dead (knobs.ts carries its own
default); rescaleImage and RescaleCtx are module-internal.
The rescale pass reassigned processedMessages in-place, so Prepared
(and thus rememberPluginMessages) persisted the rescaled copies plus
synthetic note messages instead of the wire originals. Encode from a
separate egressMessages local; persistence keeps byte-exact originals
(spec: storage stores what the wire received).
The egress image rescale changes outbound digests by design (disclosed
behavior change); the storage-parity proof keeps its committed reference
shape and stays decoupled from encoder bytes.
…0 default in knobs (#3400)

Review fix (follows 31ea189): removing the dead export left its doc comment
orphaned above RescaledMime — delete it. The 1600 default now lives next to
the knob it defaults (imageRescaleMaxDim) as a file-local const, so the magic
number has one source of truth instead of one literal plus a stale comment.
… surface (#3400)

Review fix (wire-contract D lanes red on CI): the egress rescale pass
defaults ON, so proxy-injection lanes forward read_original_image on every
wire. BASE_EXPECTED is derived from the BILI_ACP_TOOLS_* arrays only, which
predate the tool — same treatment as acp_rule's #1399 base-surface note.
…e bytes (egress-only shrink)

Regression pin for the aliasing leak fixed in 9dd4052: prepare-* once
reassigned its local processedMessages to the rescaled array and Prepared
returned that local, so rememberPluginMessages() stored shrunken bytes plus
synthetic acp_imgnote_* messages in session.transient.pluginMessages — the
session storage no longer kept what the wire received (#3400 spec goal 2).

Drives one real request through startServer (anthropic lane): asserts the
upstream saw the shrunk image while the remembered view keeps the original
base64 byte-exact and carries no synthetic note ids.
…acy wire shape in render/cache fidelity suites (#3400)
…nism (#3400)

The fixtures carry no images; the wire-economics shift comes from the
default-on read_original_image tool schema (~200 tokens/request) moving nudge
timing, not from rescaled image bytes.
…e-request.ts addition (#3400, #2170)

The #3400 egress rescale adds READ_ORIGINAL_IMAGE_TOOL_NAME to
BILI_TOOL_NAMES in src/server/side-request.ts so a session-sidecar
request leaking the armed tool set is still demoted by #1897. Per the
#2170 lane-guard discipline (lane logic moves with its dedicated
suite), test (J) pins the membership next to the post-#3293 precedent.
read_original_image was added to the plugin manifest but the committed
snapshots were not regenerated, so wire-contract-golden drifted on
CI. Regenerated with scripts/update-wire-contract-goldens.ts; the diff
is purely additive: the tool name plus its schema entry on the three
wires the manifest carries (anthropic/openai/responses).
@ranxianglei
ranxianglei force-pushed the 2026-10-16_image-rescale-v2 branch from c159399 to b396861 Compare October 17, 2026 04:52
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

查重: 不适用——本条是 PR #3425 自身的上游冲突处理(系统转发 merge_conflict 通知),未新建任何 issue/PR。

冲突已解决并推送:2026-10-16_image-rescale-v2 已 rebase 到最新 master 并 force-push(c15939960 → b39686166,rebase 特性分支,非 master)。

八问:

  1. 是什么问题:PR 分支与 master 分叉后 GitHub merge state 变 dirty(PR 无法合并)。
  2. 什么原因导致的:PR 建立后 master 前进了 71 个提交(根本原因立项:按 lane 名的大面积特判拷贝在 4 张 wire 适配器里各自漂移(#3293/#3401 同形 bug 家族),需要抽象重构 #3417 lane-decisions、config epoch、Vertex managed-block wrap 修复、Claude Code on Vertex 路由等)。其中 CHANGELOG.md 的版式变化(master 新增 ### Changes 子节)与本 PR 的条目插入位置发生文本级冲突;其余双方都改的文件(knobs/config/plugin/handle/prepare×4/loop/core.ts/tsup/website/tests/lockfile 等)被 git 自动合并,无文本冲突。
  3. 影响范围与严重程度:仅影响本 PR 的可合并性,不影响任何用户或运行时行为。严重度:阻塞项(不解决则 PR 无法 merge)。
  4. 怎么修复的:13 个提交整体 rebase 到 origin/master。唯一文本冲突在 CHANGELOG.md,按 union 解决——保留 master 新增的 ### Changes 子节及其 4 条新条目,把本 PR 的条目置于该列表顶部(保留原提交"置顶"意图)。对每个双方都改的自动合并文件逐一 diff 核对 master,确认两侧意图完整保留、无语义冲突(如 prepare-openai 里 master 的 resolveLaneDecisions 导入与本 PR 的 rescale 调用共存正确;tsup 的 jimp noExternal 与 master 变更无交叉)。
  5. 是否有回归问题:审过既有行为——master 侧新增行为(lane-decisions、config epoch 等)在 rebase 后代码中全部保留;本 PR 自身行为(egress downscale 默认 ON、read_original_image 注册门控)与 rebase 前完全一致。本地 typecheck + build 干净;定向测试 105/105 全绿(issue3400-image-rescale、wire-contract、plugin-protocol、launcher-plugin-mode、side-lane-invariants、cache-proof、golden-storage-render、storage-engine-proofs、codex-official)。全量回归按纪律交给远程 CI。
  6. 是否破坏缓存:不涉及。rebase 未触碰任何缓存逻辑;rescale 的内容哈希确定性缓存(prefix cache 字节稳定)是 PR 自带并被其测试钉住的性质,不受本次操作影响。
  7. 是否改了设计方向:不涉及。rebase 不改变 PR 的架构与数据流。
  8. 是否引入了新的配置:与 PR 原文一致(未变):config key imageRescale(默认 true)+ env BILI_IMAGE_RESCALE(0/false 关闭)/ BILI_IMAGE_RESCALE_MAX_DIM(默认 1600,下限 128),已入 CONFIGURATION en/zh。

行为变更披露(Deliverables 第 4 条):本 PR 改变既有行为——出站大图(长边 >1600px 或 base64 >100KB)默认降采样后发送(旧行为:#3295 起的字节级透传);原图在会话存储中保持字节级原样,BILI_IMAGE_RESCALE=0 可恢复旧行为。该披露已在 PR 描述与 CHANGELOG 条目中。

判定:冲突已清除,分支干净、本地定向验证通过,待 CI 绿后可由人工合并。

@ranxianglei
ranxianglei merged commit 5107447 into master Oct 17, 2026
47 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

📦 Merged Build Published

This merge to master was published as 0.0.520-master.5107447 (rolling master tag).

npm install -g billion-context@0.0.520-master.5107447  # this exact build
npm install -g billion-context@master  # always the newest merged build

npm view billion-context@master prints the current version at any time.

ranxianglei added a commit that referenced this pull request Oct 17, 2026
…lout)

Bare cpSync of the billion-context package left zod unresolved once
#3425's jimp made dist/index.js import it at module load: the lane's
self-spawned proxy exited code 1 on every attempt and traffic silently
went direct (uncompressed), failing 8 assertions. npm install --no-save
of the tarball installs the candidate and its deps while keeping the
dsh profile manifest untouched.
ranxianglei added a commit that referenced this pull request Oct 17, 2026
…ist-level P0s

8a772ac made the dsh setup npm-install the candidate tarball so its runtime deps (zod, pulled into dist by #3425's jimp) resolve. That is not a gate fix, it is burying the defect: the bare cpSync deliberately reproduces the real thin-install shape (no dependency tree beside the package), which is exactly how production dsh plugin installs ship. If dist/index.js requires an external package at module load, the lane must go red (self-spawned proxy dies, fold/nudge assertions fail) — that is the gate doing its job, not a lane bug to be absorbed. Reverting to the bare copy until the product-side fix (runtime dynamic import of jimp + dist "no static zod" build assertion) lands; after that, this lane doubles as the regression proof.
ranxianglei added a commit that referenced this pull request Oct 17, 2026
Gives the two guard helpers real consumers (unused-exports gate) and locks the regex: the four #3425 leak forms are violations, the sanctioned opaque lazy import and zod vendor strings are not.
aolshevskiy pushed a commit to aolshevskiy/billion-context that referenced this pull request Oct 17, 2026
…xianglei#3511 P0)

ranxianglei#3425 inlined jimp (devDependency, noExternal), and jimp imports zod at module scope: 16 STATIC from"zod" imports landed in dist/index.js and dist/agent/pi-native.js. Static imports have no degrade path, so installs shipping the package without its node_modules (dsh profile bare copies, thin pi plugin copies) died with ERR_MODULE_NOT_FOUND and traffic silently went direct, uncompressed. Bundle zod like every other transitive of an inlined dep (~1.4 MB on the two affected entries). The one sanctioned external consumer — opencode V1 host interop, which wants the HOST zod instance and already degrades when unresolvable — keeps its runtime resolution via an opaque specifier, unchanged behavior. New build gate scripts/check-dist-external-zod.mjs fails the build if a static zod import ever reappears; verified a bare dist copy with zero node_modules now runs --version clean.
aolshevskiy pushed a commit to aolshevskiy/billion-context that referenced this pull request Oct 17, 2026
…d + optional dep + dist-no-zod build gate

ranxianglei#3516 P0 (from ranxianglei#3511 final pre-release review): ranxianglei#3425's jimp swap was
inlined via noExternal, carrying jimp's transitive zod import verbatim into
dist/index.js & dist/agent/pi-native.js (16 static imports each). Installs
without a node_modules tree (dsh profile bundles) then die at startup with
ERR_MODULE_NOT_FOUND while model traffic silently bypasses compression.

- jimp removed from tsup noExternal and added to explicit external (tsup
  only auto-externalizes dependencies/peerDeps, not optionalDependencies)
- src/image-rescale.ts resolves jimp once at first use; a failed resolution
  is permanent per process and degrades rescaling to pass-through
- jimp moved devDependencies -> optionalDependencies (sharp precedent:
  installed where possible, silently skipped where not)
- new build gate scripts/check-dist-no-zod.mjs fails the build on any static
  zod import in dist (the original offender class, forever)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant