跳到主要内容

oh-my-pi v15.10.11 发布 —— 渲染引擎重写,13 个 bug 簇一次埋平

· 阅读需 6 分钟

别怀疑是自己的终端在抽风。这些现象有一个共同的根因——oh-my-pi 的旧渲染引擎一直在做一个它不擅长的事:猜测终端滚动缓冲区的位置,并据此重建屏幕内容。猜对了风平浪静,猜错了就是闪烁、重复行、不可见到重新调整大小后的各种视觉灾难。

v15.10.11 做了一件事,一口气把这些 bug 全部埋了。

oh-my-pi v15.10.11 封面

核心痛点:那个"可破坏的滚动缓冲区"到底有多坑

旧渲染引擎的设计思路是:把终端屏幕当成一块可以随时擦除重画的画布。引擎需要知道"现在终端滚动到了哪一行",然后判断"我能从这里往上重绘多少行而不破坏已有内容"。听起来似乎可行,但问题是——终端 emulator 根本没有可靠的 API 告诉你当前滚动位置,引擎只能靠蛛丝马迹去猜。

为了应对这种不确定性,开发团队先后引入了 ED3-risk(擦除滚动缓冲区时的风险判定)、defer(延迟渲染)、checkpoint(检查点)等一整套复杂机制。代码量越堆越多,bug 却越修越多。

光看这些关联的 issue 编号就够吓人的:#1610、#1635、#1651、#1682、#1719、#1746、#1799、#1823、#1962、#1974、#2000、#2011、#2154——整整 13 个 bug 簇,涉及滚动偏移、闪烁、重复行、窗口 resize、overlay 关闭后的残留内容等等。每一个都是同一个底层问题在不同场景下的变种。

更糟糕的是性能。旧引擎每帧渲染的代价是 O(total transcript)——也就是说,对话越长,每帧越慢。哪怕终端只需要刷新很小的改动,引擎也要把整段历史重新过一遍。

新功能怎么解决:放弃猜,只追加

v15.10.11 的新渲染引擎换了一个截然不同的思路——只追加(append-only)

核心逻辑变得很简单:每一行一旦提交,就成为不可变的历史,永远按顺序在一处存放。引擎不再探查滚动位置,不再判断能否破坏性重绘。所有关于 ED3 的复杂逻辑(ED3-risk、defer、checkpoint)全部删掉——ED3 (CSI 3 J,清空滚动缓冲区) 只在用户明确发出手势时才执行。

这个转变本质上是对终端模拟器的一种"认输"——承认上层应用永远无法可靠地推断终端内部的滚动状态,与其在灰色地带里反复试探,不如放弃所有权,让终端自己管理历史,应用只管追加。

Overlay 的处理也改动了。旧模式中,overlay 覆盖在主内容上,关闭时会触发破坏性历史重建(因为需要把被覆盖的原始行恢复回去)。新引擎里,overlay 只合成到可见窗口切片中,关掉就直接摘掉切片,不动历史。这解决了关闭 overlay 后出现空白行或重复行的老毛病。

另一个巧妙的设计是帧管线的虚拟化(stable-prefix 契约)。现在 Component.render 返回一个只读的字符串数组,如果某个组件的内容没有变化,它返回的数组引用和上一帧完全相同。渲染引擎通过引用比较就能跳过不动的组件,不再需要逐个字符比较输出。这意味着——帧渲染不再是 O(total transcript),而是 O(visible changes)。

为了验证这套新机制的可靠性,开发团队还加了一个"影子记账本"式的压力测试:在渲染压力测试中,一个影子跟踪器记录了每一行应该出现的位置和内容,然后断言实际生成的 scrollback 与预期严格逐行匹配。不匹配就报错。这种级别的自动化验证在 TUI 渲染领域相当少见。

连带改善:Codex 和 Anthropic 的修修补补

渲染引擎重写之外,v15.10.11 还修了两个可能让你吃哑巴亏的 bug。

Codex WebSocket 无限重连:如果你用过 Codex 模式,可能经历过这样的情况——任务跑完了,oh-my-pi 还在疯狂尝试重连 WebSocket,没有任何退避延迟和预算检查,陷入死循环。这不仅仅是烦人,它意味着你的 API 请求在后台无声地消耗配额。v15.10.11 修复了重连逻辑,加入了预算和延迟检查,同时确保 WebSocket 传输错误不会丢弃已经排好队的 response.completed 帧。

Anthropic 输出被砍半:这是个更隐蔽的问题。如果你用 API key 使用 Claude Opus 4(原生支持 128K 输出),oh-my-pi 之前会无条件用 CLAUDE_CODE_MAX_OUTPUT_TOKENS 把输出夹住——无论你设了多少,op code 运行的版本都会砍掉一半输出。这意味着你在对话框里看到的内容只有模型实际生成的一半。修复后,不同 tier 的模型配置被正确区分对待。

其他更新精选

  • Exa API key 泄露修复 — Exa 搜索的 API key 之前会被写入日志文件。如果你在调试环境里共享了日志,key 就白给了。
  • Web 搜索年份替换web-search 工具会把查询中的 "202x" 替换为当前年份,导致 CVE 标识符(如 CVE-2023-xxx)和历史年份搜索全坏。这个修得很及时,安全研究员应该是最大的受益者。
  • Windows CP437 乱码修复 — PHP CLI 会切换 console 的 codepage,之后 oh-my-pi 的边框字符从 ASCII 框变成了一堆乱码。现在会在渲染前正确还原 codepage。
  • Bracketed paste 吞输入修复 — 如果粘贴操作丢失了 ESC[201~ 结束标记,所有后续键盘输入会被静默吞掉,你打字但屏幕上没反应。这个修复对远程开发场景很关键。
  • 并发 git stash/pop 竞态 — 多个并发操作写同一个 git stash 时可能互相覆盖。现在加了锁保护。
  • 新版启动流式日志 — 设置 PI_DEBUG_STARTUP=1 可以看到启动过程的逐行日志,对调试配置加载问题非常有用。
  • grep maxCountPerFile — 新增选项限制单个文件的匹配数量,在大代码库中搜索时防止爆内存。

如果你正在用 oh-my-pi,升级非常简单:

# 一键安装
curl -fsSL https://omp.sh/install | sh

# 或者通过 Homebrew
brew install can1357/tap/omp

# 或者用 Bun
bun install -g @oh-my-pi/pi-coding-agent

安装后运行 omp --version 确认版本号是 v15.10.11。如果你用的是 bun 包,注意 npm 同步可能比 GitHub Release 晚几小时。

完整更新日志:GitHub Release

📢 关注我,获取最新开源更新日志和开发者工具动态。

#ohmyPi #AI编程 #渲染引擎 #代码修复