跳到主要内容

53 篇博文 含有标签「Oh My Pi」

查看所有标签

oh-my-pi v15.11.2 发布 —— 12.5次/秒、串行、默认关闭:三个数字背后的进化

· 阅读需 9 分钟

串行。这是之前 bash 工具在多步骤请求里的执行方式——同一个消息里的 5 个 bash 调用,必须等前一个跑完才轮到下一个。如果你的第一步是 sleep 5,剩下 4 个就干等着。

默认关闭。这是之前 async.enabled 的状态。异步任务不是一个选项,而是一个你需要在配置里主动打开的功能——很多人甚至不知道它的存在。

这三个数字,是 v15.11.1 和 v15.11.2 联合发布的隐藏注脚。它们不是 Release Notes 里最显眼的那几行,却最精确地描述了 oh-my-pi 在两个小版本中完成了什么样的进化。

oh-my-pi v15.11.2 封面

渲染进化:从 12.5 次/秒到组件级隔离

如果你在 TUI 里跑过超过 30 分钟的会话,你应该经历过那种奇妙的卡顿——消息越来越多,终端越来越慢,但你分不清到底是模型在思考还是 UI 在拖后腿。你倾向于相信是后者。

以往的情况确实如此。TUI 的 loader tick 以 12.5 次/秒的频率触发全量 compose。每条新增消息、每个工具调用返回、甚至每帧加载动画,都会迫使整棵组件树从头 re-render 一遍。对话越长,开销越重。你不需要一个 profiler 就能感觉到——光标变得迟滞,键盘输入有微妙的延迟。[#2309] 的修复思路简单到只差一行 API:TUI.requestComponentRender。不是每次状态变化都重绘全局,而是只渲染状态变化的那个组件。受影响子树之外的所有节点,碰都不碰。

这个改动带来的效果非常直观——长对话再也不会有那种「帧率骤降」的瞬间了。渲染不再随对话长度衰减。

配合 v15.11.1 引入的 display.smoothStreaming——它让流式 tool-call 参数也能渐进式地出现在你眼前,而不是一次性刷出一大段 JSON——TUI 的实时感上升了一个台阶。模型在构建工具的输入参数时,你就能看到它是怎么一步步搭建这个参数的。

执行进化:bash 从串行到并行

这是 v15.11.2 中我相当欣赏的一个改动。它不是大幅降低了什么延迟,而是在某种意义上压缩了你对工具执行速度的「感知」。

之前,如果你在 assistant message 里提交 3 个 bash 调用,oh-my-pi 会把它们串行化——先跑第一个,等它返回,再跑第二个,再跑第三个。这在逻辑上没问题,但你在终端前看着光标转圈的时候,会下意识问:为什么不能一起跑?

现在可以了。[#2310] 的核心逻辑简单到可以用一句话概括:同一个 assistant message 里的 bash 工具调用,如果 shell 是 non-pty 且共享的,就并行执行。两个例外:

  • pty:true 的 shell 保持独占(终端交互不能乱)
  • 重叠的 shell key 调用退化为隔离的一次性 shell(不会互相污染状态)

这带来了一个务实的改变:当你让模型做一组无依赖的批量操作——比如同时查看 3 个不同的文件、同时启动 2 个本地服务——你不必再被串行化的顺序阻塞。结果的到达时间线被压缩了。

AgentConcurrency 的粒度进化

如果说 bash 并行是执行层面的改进,那 AgentTool.concurrency 的升级就是声明层面的。

以前,你的工具要么独占总线(exclusive),要么共享总线(shared),一个工具定义决定了它的调度策略,没办法按单次调用的参数动态选择。

v15.11.2 让 concurrency 支持一个 per-call 的 resolver 函数——(args) => shared | exclusive。你可以在工具定义里写一段逻辑:如果这次调用的 args 里带了一个特定 flag,就共享;没有,就独占。这个改动不大,但它打开了工具调度策略的「可编程性」。未来多 agent 共享同一个工具池的场景里,这种 per-call 调度会变得不可或缺。

魔力关键词:不再悄悄替你决定

oh-my-pi 有几种隐藏魔法关键词——ultrathinkorchestrateworkflow。你在对话里提到它们,框架会触发对应的特殊行为。这对熟客来说很有用,但对新用户来说可能有点「偷偷摸摸」——你说了一句话,框架替你做了某个决定,但你不知道自己触发了什么。

v15.11.2 增加了 magicKeywords.enabled 主开关和每个关键词的独立开关。你可以在配置里选择完全不启用这个机制(magicKeywords.enabled: false),或保留整体启用但关掉某个特定关键词的提示通知。这对于不需要 workflow 模式、但想保留 ultrathink 的用户来说,是实打实的选择权。

Expert Elixir:语言服务器自动探测

Elixir 社区的用户会在 v15.11.2 里发现一个让人会心一笑的改动:expert --stdio 形式的 Expert 语言服务器会被 Mix 项目自动检测到。

这意味着你不需要手动配置 [languages.elixir] 的专家模式。如果工作目录是个 Elixir/Mix 项目,框架会自动启用 Expert Elixir LSP。对于每天在 Phoenix 或 Nx 项目里工作的人来说,少配一行配置可能省不了几秒钟,但它消除了判断「这个项目该不该配」的认知成本。

v15.11.1 的基础演进

回过头看,v15.11.1 本身也做了相当多前期工作。

最值得说的是 async.enabled 现在默认是 true([#2301])。这背后是一系列任务编排组件成熟到了不需要用户主动理解的程度——后台任务执行、异步 bash 都开箱即用。「默认开启」的意思是,团队的信心到了。

display.smoothStreaming 前面已经提过,但 statusLine.transparent 这个外观设置值得单独说一下([#2306]):状态栏现在可以继承终端的默认背景色,而不是强制画出自己的底色。这会让 oh-my-pi 的使用体验融入到你的终端配色方案里,而不是像一个悬浮的 UI 块。

缩短的背景任务指示器——齿轮图标加数字——取代了冗长的文字提示。这跟透明状态栏的审美逻辑是一致的:不在你的屏幕上多占一个像素的不必要空间。

Bugfix 主题:数据路径的完整性

两个版本的 bugfix 整理来看,有一个清晰的脉络:数据路径的完整性。

Anthropic 方面,stop_details 在 assistant 消息上的保留([#2290])、metadata.user_id 在 web search 请求中的正确传递([#2295])、以及错误 tool results 中的纯空白内容不再导致 HTTP 400 错误(v15.11.2),全部指向同一个方向:模型和工具之间的数据交换不能因为「看起来空的」内容而断裂。

OpenAI 方面,Responses/Completions 流在 120 秒超时前卡死的问题被修复([#2290])。我之前遇到过类似的情况——一个看起来已经生成完毕的流卡在 final 帧上,终端没有任何错误提示,只是沉默。这种死锁比显式报错更让人困惑。

pi-catalog 方面,NVIDIA NIM Qwen 的 enable_thinking 参数([#2299])和 Moonshot/Kimi 的请求元数据([#2289])也修好了。

还有一组修复值得注意:CDP attach 目标选择(Chromium/Edge)和本地内存整理在 Responses 类模型上的修复。这两个都是「不太常见但一旦遇到就完全抓狂」的场景——前者的症状是开浏览器 debug 时连不上正确的 target,后者的症状是记忆文件越积越多但无人回收。

其他有印象的修复

Plain-RPC 斜杠命令发现了命令源元数据;编辑工具 hashline prompt 指引被修好([#2292]);MCP OAuth 授权在目标服务器的流程跑通了;IRC 发送的 await:true 不再困住发送者——消息发出后就释放发送者的上下文,不再等接收方读完。

流式代码块避免同步语法高亮直到最终确定——之前的版本会等整块代码到达后才解析,现在提前释放了帧率。job 工具 poll 头部不再重复显示 running count。Windows 风格反斜杠路径也能被正确解析了。修复了扩展/插件加载器注入 pi 自导入导致的栈溢出。

以及我特别想提的一个 Windows 修复:Ctrl+C/退出不再破坏父 shell。emergencyTerminalRestore 现在只在追踪到 alt-screen 状态后才触发([#2311])。之前如果你在 Windows 的 cmd 里按下 Ctrl+C 退出 oh-my-pi 的 TUI,回到命令行后终端显示会处于一个半 corrupted 的状态。

两个小版本合在一起,展示的是 oh-my-pi 这个工具性格的侧面特写——从「什么都能做」到「做什么都更稳」。


oh-my-pi v15.11.0 发布 —— 当 Agent 的"遗忘"变成像素级记忆:Snapcompact 来了

· 阅读需 6 分钟

这个版本最核心的改动只有三个,但每一个都在改变 Agent 框架的设计范式。

oh-my-pi v15.11.0 封面

亮点一:Snapcompact——把压缩历史变成像素帧

这是本版本最反直觉、也最值得关注的功能。

传统上,当 Agent 的对话历史撑满上下文窗口时,有两种处理方式:截断(丢掉最旧的内容)或压缩(用 LLM 把历史重写一份摘要)。后者效果好一些,但代价是每次压缩都要烧一趟 LLM 调用,而且 LLM 做摘要会丢失代码行号、错误栈、文件路径这些细节——它认为"重点"的和你关心的,天然有偏差。

Snapcompact 走了一条完全不同的路:不要 LLM 插手。它把即将被丢弃的历史文本直接渲染到密集的像素字体位图中,作为图片贴回对话上下文。Vision Model 读这些位图的速度和读纯文本相当,但渲染位图比跑 LLM 压缩便宜得多。

底层实现做了很细致的差异化适配。不同的 API provider 有不同的图片计费方式,Snapcompact 会根据当前模型自动选择最优的帧形状:

  • Anthropic 和未知 API8x8r-bw(unscii-8 方框字体,黑墨,每行双轨打印)——阅读速度接近纯文本,成本约低 2 倍,拒绝率最低。
  • Google8x8r-sent(句子色调渲染)——成本约低 2.9 倍。
  • OpenAI6x6u-sent(unscii Lanczos 拉伸到 6×6 格,detail: "original")——OpenAI 固定按 ~2.9k tokens/张计费,帧数才是成本杠杆。

Snapcompact 的帧数据存储在 preserveData.snapcompact 下,采用 8 帧中段弹出(middle-out)的 eviction 预算。帧会跨 compaction 操作持久存在——这意味着你的历史不会因为多次压缩而逐层退化。

snapcompact 已被提取为独立的 @oh-my-pi/snapcompact 包,包含完整的 SQuAD 实验套件、provider 探测工具和激活分析。如果你在构建自己的 Agent 框架,可以直接复用。

亮点二:Task 工具的并行化重写

Task 工具是本版本中破坏性最大的改动,也是最能改变你使用习惯的改动。

以前,task 工具在子目录里启动一个子 agent,等它跑完才返回结果。task.simple 选项允许简化 schema,但本质上还是串行执行。子 agent 的 session 在任务完成后就被回收。

现在,task 工具被彻底重写:

  • Batch 模式(默认开启):wire schema 变成 { agent, context, tasks[] }——每个 tasks 项启动一个独立的持久化子 agent,全部在后台并行运行。
  • 异步投递:结果以 job 交付形式返回,只在真正需要时才 job poll 阻塞等待。
  • 持久化生命周期:子 agent 完成后进入 idle 状态,7 分钟无活动后自动序列化到磁盘。被取消或 abort 的 child 可以 salvage 回收资源。
  • Soft request budget:子 agent 达到配额阈值后注入 wrap-up 指令,超额 1.5 倍时优雅中断。

同时,resume 选项被移除(通过新的 irc 发消息即可继续和子 agent 对话),task.simple 和 per-call schema 参数被移除(task.batch 覆盖了所有用例)。

如果你之前只在偶尔需要隔离测试时才用 task,建议在新版本里重新试试它——当你建了 5 个并行子 agent、各自处理不同上下文的时候,你会意识到以前串行 task 的方式已经回不去了。

亮点三:Agent Hub + IRC 总线

伴随 task 重写,session 观察器被替换为 Agent Hub。按下 ctrl+salt+a、或在空编辑器中双击左箭头,弹出一张实时表格:所有已注册的子 agent,存活状态、当前任务、消耗资源一目了然。每个子 agent 都可以进入聊天视图——Enter 打开 transcript + 输入行,可以给正在运行的 agent 发指令、唤醒空闲 agent、或唤醒已序列化的 agent。

IRC(agent 间通信)被从配置项升级为协议。irc.enabled 设置被移除,现在 IRC 的可用性自动推导。API 重写为 send/wait/inbox/list 四个操作,基于 per-agent 邮箱总线(mailbox bus),每个 agent 有绑定的收件箱容量。history:// 协议的加入让 agent 历史记录的引用方式标准化了——history:// 列出所有 agent,history://<agentId> 渲染一段折叠工具调用后的精简 transcript。

更多值得关注的细节

这个版本还有不少不容易归类的改动,但都很实用:

  • <files> 标签统一:compaction 和 branch-summary 中的文件列表从 <read-files><modified-files> 合并为单一的 <files>,按 prefix-folded 目录树渲染,每个文件标注 (Read)/(Write)/(RW)。旧标签会在下次 compaction 时自动愈合。
  • Supersede 裁剪:当同一个文件被重新读取后,之前的 tool 结果被标记为过时并在溢出裁剪时优先剔除。这是为 long-running agent 设计的——同一个文件的旧结果会累积占用大量上下文窗口。
  • 内联字节上限:bash 和 browser 工具结果现在有约 50KB 硬上限,尾部截断并附 artifact:// 链接。这关闭了此前工具结果可以塞进 100KB+ 内容的路径。
  • 思考量计费修复([#2275]):estimateTokens() 之前低估了 thinking-heavy 消息——thinkingSignature payloads 和 redactedThinking.data 没有被计入,导致本地估算远低于 provider 实际用量。现在二者一并计入。
  • OpenRouter DeepSeek V4 工具 schema 修复([#2270]):strict tool schemas 在嵌套 anyOf 时产生不含 type 的分支,OpenRouter 返回 400。修复使 enforceStrictSchema 自动展平纯 union 到父级 anyOf
  • 跨模型 reasoning chain 保留([#2257], [#2265]):anthropic-messages → anthropic-messages 续接时,之前的 thinking/redactedThinking block 会完整保留,不会断裂。

Breaking Changes 的决策逻辑

v15.11.0 的 breaking changes 集中在几个明确的删改上,每个删除都对应"有一个更好的路径存在":

  1. resume 从 task 移除 → irc.send 可以实现同样效果
  2. irc.enabled 移除 → 自动推导
  3. task.simple 和 per-call schema 移除 → task.batch 覆盖所有用例
  4. convertToLlm 别名删除 → 与 defaultConvertToLlm 重复
  5. compaction barrel 中 snapcompact helpers re-export 删除 → 强制从 @oh-my-pi/snapcompact 导入

对于没有自定义过这些 API 的用户,升级理论上不会有感知。


oh-my-pi v15.10.10 发布 —— Codex 无限重连、Anthropic thinking 签名、输出上限,三个大坑一次填完

· 阅读需 3 分钟

oh-my-pi v15.10.10 封面

Codex WebSocket 无限重连:一个零延迟的死循环

这是整个版本里最隐蔽也最严重的 bug。当 Codex 返回 websocket_connection_limit_reached 时,oh-my-pi 的 no-content reconnect 路径从不检查重试预算,也从不等待——它只是不停地连、不停地断、再连,无限循环。如果这个限制是 account-scoped 的,重试永远不会成功,客户端会浪费大量时间在死循环上。

修了:重连现在有预算和延迟,跟其他 WS 重试路径一致。预算耗尽后降级到单次 SSE 重放。

除此之外还修了 Codex WebSocket 的一串相关问题:

  • 两个并发 getOrCreateCodexWebSocketConnection 调用(prewarm 和第一次请求竞速)会互相关掉对方的 handshake——一个 CONNECTING 状态的 socket 被关掉后,另一个 caller 收到 websocket closed before open 的致命错误,整个 session 的 WebSocket 被禁用。现在并发的调用会加入 pending handshake。
  • WS 传输错误到达时,response.completed 事件如果排在 websocket closed 之前就会被丢弃——一个已经完成的响应被重放了一次。
  • whitespace-loop breaker 之前只处理正常帧,如果模型发送了不在正确窗口内的变形帧,watchdog 不会触发,turn 永远挂起。

Anthropic 修复:thinking 签名、输出上限、重组装

输出上限被悄悄砍了一半CLAUDE_CODE_MAX_OUTPUT_TOKENS 这个 clamp 原本只应为 OAuth 请求保留,但 buildParams 无条件应用了它。API-key 用户用 Opus 4.8(原生 128K 输出)时,实际只有 64K 可用。修了,OAuth 请求继续 clamp,API-key 不限。

thinking 签名修复:之前 Opus 4.0(claude-opus-4-20250514)的版本号被 supportsAdaptiveThinkingDisplay 错误解析成 4.20250514 ≥ 4.7,interleaved-thinking-2025-05-14 beta 被静默丢弃。

多个流处理修复processResponsesStream 中 reasoning items 的 finalization 修复——没有 ID 的 reasoning items(本地 host),每次 output_item.done 都匹配到第一个 thinking block,第二个 block 的文字覆盖了它。现在按 routed entry 匹配。

Foundry mTLS 证书缓存问题:当环境变量指向文件时,证书被缓存了进程生命周期——证书轮换不生效。现在缓存 key 加了文件 mtime。

其他值得注意的

Copilot premiumRequests 计数修复:失败/取消的 response 中用量计数被丢弃——populateResponsesUsageFromResponse 直接替换 usage,错误路径在 success-path 重新应用之前就抛出了异常。现在正确保留。

Anthropic stop_reason 未知值修复:当模型返回未知的 stop_reason(如 Sonnet 4.5+ 默认的 model_context_window_exceeded),mapStopReason 会抛异常。由于这个原因出现在 trailing message_delta 上,错误不可重试——整个 turn 挂了。现在未知值映射到 length

100+ 图片 resize 并发修复:图片 resize 的 fan-out 限制到 4 个并发 decode,之前 20+ 张图片时会一次性解码所有图片——两个 encode pipeline 同时跑,内存冲击很大。

升级:

curl -fsSL https://omp.sh/install | sh
# 或用 Bun
bun install -g @oh-my-pi/pi-coding-agent

完整更新日志:GitHub Release

50+ 条修复,Codex 和 Anthropic 各分一半。不吸引眼球,但如果你踩过 Codex 无限重连或者 Opus 4.8 输出被砍半,这个版本就是为你修的。

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

#oh-my-pi #AI编程 #Codex #Anthropic #版本更新

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 v15.10.9 发布 —— 20 条修复,没有一条新功能,但每一条你都能用上

· 阅读需 4 分钟

oh-my-pi v15.10.9

账号轮换与 Provider 修复:API 成本上去了,不能复用旧逻辑

最关键的修复来自 Google Antigravity 的多账号管理。oh-my-pi 支持为同一个 Provider 配多个 OAuth 账号做负载均衡,但如果其中一个账号额到了——Cloud Code Assist 返回 429 You have exhausted your capacity on this model——之前的逻辑只识别了 capacity 关键词,将其降级为 MODEL_CAPACITY_EXHAUSTED(45-75 秒 backoff),而不是真正需要触发的账号级 QUOTA_EXHAUSTED 冻结。结果呢?agent 对着同一个限流的账号反复重试,直到 retry-after 窗口耗尽,而另一个空闲账号就在旁边闲着(#2187)。

v15.10.9 引入了 antigravityRankingStrategy,在选号之前先查每个账号的 Antigravity 额度剩余比例,排过序再选,额度耗尽的账号被冻结到 reset 窗口期结束。

另一个 Provider 层面的大坑——DeepSeek V4 在私有 CoT 模式下的第一个事件超时。DeepSeek 的官方 API 在 model 做 chain-of-thought 推理时不发任何 SSE 字节,等推理结束才吐第一个 token。传统 120 秒的超时窗口不足以覆盖长思考场景,结果就是每次接近 DeepSeek V4 的复杂推理请求都以"timed out while waiting for the first event"告终并静默重试。第一事件 watchdow 提升到了 300 秒(#2177),基本上覆盖了 V4 的完整推理周期。

此外,OpenRouter 上 Anthropic 模型的 cache_control 也修了。之前工具调用的 assistant 空 content 也会带上 cache marker,API 返回 {type:"text", text:"", cache_control:...} 直接导致 HTTP 400。现在 cache marker 只挂到最近的非空文本段上。

子智能体的大问题:反复扫描父智能体已经扫过的所有内容

如果你的 workflow 经常用到子智能体分派子任务,v15.10.9 这条修复会让你的 token 消耗明显下降。这是因为 task 命令生成的子 agent 每次启动都要重新跑三件事:扫描加载 rules、扫描发现 extension、扫描 .omp/tools/ 目录——但所有这三项父 agent 在启动时已经做完了。

之前的代码不仅没有缓存扫描结果,而且 runSubprocess() 调用的 createAgentSession() 没有 forwarding 规则路径和扫描结果。结果就是一个含有 10 个子任务的工作流要重复扫描 10 次相同的目录树。修复的核心改动是:toolsession 缓存 session.rulessession.extensionPathssession.customToolPaths,子 agent 只需要接收到路径列表,而不需要重建扫描结果(#2190)。注意:只转发路径列表,不转发实例——每个 session 仍重建自己的 Extension 和 LoadedCustomTool 对象,这样监控器、事件总线、目录都能正确绑定到子 session 上。

TUI 场景的几个隐藏痛点

流式思考内容在滚动出视口后消失——这是 Terminal UI 里最让人沮丧的 bug。你看到一个 long CoT 输出滚动过视口顶部,然后想向上翻找某个中间推理步骤,它不见了。原因是 append-only commit 检测器在比较原始 row 字节,而 style 控制序列导致同一行的实际字节不同,commit 边界冻结了。v15.10.9 改成按可见内容比较([描述同上]),同时保留了 spinner/折叠工具预览等周期性重写块的 deferred 处理。

另一个场景:粘贴多个图片路径时,之前的实现把你粘贴的整段内容当作一个不可读的路径字符串处理。现在多个图片路径被正确分割并按序逐个 attach。

MCP OAuth 的回退提示也修了——非浏览器环境下,"Click here to authorize"现在会输出 auth-safe 的终端超链接(#2196),即使终端不支持自动超链接检测也能用。

升级

# macOS / Linux 首次安装
curl -fsSL https://omp.sh/install | sh

# Homebrew
brew install can1357/tap/omp

# Bun(推荐)
bun install -g @oh-my-pi/pi-coding-agent

完整更新日志:GitHub Release

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

#ohmyPi #AI编码工具 #版本更新 #开源工具 #终端Agent

oh-my-pi v15.10.4–v15.10.7 发布 —— agent loop 大修、thinking 签名修复、MCP 启动加速

· 阅读需 4 分钟

oh-my-pi v15.10.4–v15.10.7 封面

agent loop 大修(v15.10.5)

v15.10.5 是三个版本里改动最深的——它不是修某个 bug,是整条 agent loop 的执行路径重检了一遍。

最大的变化:去掉了 maxToolCallsPerTurn 上限。 之前每轮 assistant turn 有一个内置的工具调用数量上限,到了就截断。去掉之后,agent 在一轮里能调用的工具数不再被硬限制——对于复杂任务(比如 subagent 派发 + 结果的批量处理),之前可能一轮做不完要拆两三轮,现在一轮搞定。

剩下的全是修边缘情况的正确性:

  • 中断 assistant 响应后,run 不会再卡在 provider 的 iterator cleanup 上
  • afterToolCall 现在即使在 run 被 abort 后也会执行——工具的后处理逻辑不会因为中断而被跳过
  • agentLoopDetailed().detailed() 修复:run telemetry 和 coverage 在 stream.result() 解析前就捕获好
  • GPT-5 Harmony 泄露处理:有效的 tool call 会被恢复,泄露的 partial assistant output 在重试前丢弃
  • Abort 时只保留已完成的 tool-call 块,未完成的 tool-call 块如果不是故意中断则丢弃
  • 故意中断(TTSR 规则匹配、用户中断标签)会保留 mid-stream 的 tool-call 块,配一个占位 result

Anthropic thinking 签名修复(v15.10.7)

这个 bug 藏得很深:你在 agent 可见输出过程中 interrupt 了模型,transformMessages 会把被中断的 turn 里所有 thinking block 的签名都清空——包括那些已经完成流式输出的 block。Anthropic 在 content_block_stop 时就已经发了签名,所以已经完成的 thinking block 是有签名的。结果被 rerun 时带了一个空签名过去,Anthropic 拒收(HTTP 400)。

修法很简单也很精准:只有中断点正在流式输出的那个 thinking block 被标记为不可信,已经完成流式的 block 保留它们的可重放签名。

如果你用过 Opus 4.7+ 的 adaptive reasoning,又在运行中经常中断 agent 修改提示——这个 bug 你大概率碰到过(表现为 random 400 错误)。

MCP 启动加速(v15.10.7)

之前 omp 启动时如果有一个 MCP 服务器不响应,整个 UI 会被卡住 25–30 秒。原因是 MCPManager.connectServers 在等所有 pending 服务器的工具列表。现在没有缓存的服务器在 Promise.allSettled 中放行,它们的工具通过后台 #onToolsChangedrefreshMCPTools 路径在连接完成时自然出现,不阻塞启动。

装了较多 MCP 服务器的用户,启动速度会有明显改善。

其他修复

v15.10.5:tool result 解析修复(不支持的内容块标记为错误 + diagnostic text)、stall 时正确返回 aborted message、run telemetry 在正确的 finalization 路径上触发、length stop reason 后继续执行等。

v15.10.6:未列出具体变更(可能为内部基础设施)。

v15.10.7:Antigravity 用量计数器分离——Google/Gemini 配额耗尽显示 0% free,独立的 Anthropic/OpenAI Antigravity 计数器不受影响。MCP OAuth 回退渲染修复(终端显示短超链接,授权 URL 不换行)。

升级:

curl -fsSL https://omp.sh/install | sh
# 或用 Bun
bun install -g @oh-my-pi/pi-coding-agent

完整更新日志:GitHub Release

连续三个小版本,agent loop 的执行路径修了一遍、thinking 签名不再丢失、MCP 启动不再卡 30 秒。体量不大但每一刀都砍在点上。

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

#oh-my-pi #AI编程 #agentloop #Anthropic #MCP

oh-my-pi v15.10.8 发布 —— 以前偷改全局 fetch,现在每层都能注入

· 阅读需 4 分钟

v15.10.8 不是加新功能——它干了一件事:把 fetch 这个被隐藏的输入口,从每一层的代码里翻了出来,变成可注入的参数。

oh-my-pi v15.10.8 封面

以前:全局 hook 的暴力美学

omp 早期设计里,网络请求的入口就一个 globalThis.fetch。你要拦截、修改、mock 请求?就得自己 hook 全局 fetch。omp 自己甚至提供了一个 hookFetch API,在 @oh-my-pi/pi-utils 里导出——用中介者模式拦截全局 fetch

这套方案的局限很明显:

  • 只能拦截全局,不能精准控制某一层的 HTTP 行为
  • 多个 hook 同时工作时,谁先谁后不可控
  • 测试时想 mock 一个特定模块的请求,hook 全局会影响所有单元

现在:每层都可注入

这个版本在所有关键路径上加了一个 fetch?: FetchImpl 可选参数。不是单个包的单个入口——是跨 5 个包、几十个模块的统一改动:

pi-agent-core:SummaryOptions、compact/generateSummary、ProxyStreamOptions、requestRemoteCompaction——agent loop 里每个会产生 HTTP 请求的节点,全加了 fetch 注入点。

pi-ai:Google、Ollama、OpenAI-compatible 的 model manager 各自接收可选 fetch。OAuth 控制器、API key 校验、设备/PKCE 登录——整个认证链路也覆盖了。

pi-coding-agent:CustomToolContext、ModelRegistry 构造器、MCP 调用、web search、tool 网络调用——agent 暴露给模型的所有工具链。

pi-mnemopi:ExtractionClient、extractFacts——记忆系统的 LLM 请求入口。

组合起来的画面是:以后你想 mock 测试、加自定义代理、调整超时、或者给特定请求加签名,不再需要碰全局状态。传一个 fetch 进去就行,不影响其他路径。

与这次对应的还有 hookFetch 的移除——它从 @oh-my-pi/pi-utils 的导出中被删除,不再是公共 API。这不是功能退化,而是架构信号:以前需要 hook 全局完成的,现在每层都给了明确的注入点。

几个不容易注意但很重要的修复

Codex 的空白符号死循环(#2155):OpenAI Codex 流式输出会在 todo tool call 之后,不断发射纯空白字符的 function_call_arguments.delta 帧——没有新内容,但也不会断。之前的处理方式是 abort 共享的 AbortController,结果导致整个 requestSetup 无法 reopen。修复加了重试机制:放弃 abort,清理半成品 tool call,从 zero 重发请求,上限 2 次。这背后有一个更底层的教训——跨请求共享一个 AbortController 的短路效应,出 bug 后关联到另一个完全不相关的功能。

Output token 超上限 400 错误:GLM-4.7 之类的模型,catalog 里存的 maxTokens 其实是 context window(131072),不是 output 上限。请求 max_tokens=131072 会空手而归 400。修复加了硬上线 64k(OPENAI_MAX_OUTPUT_TOKENS),min(requested, model.maxTokens, 64000)——mirror 了 Anthropic 端 CLAUDE_CODE_MAX_OUTPUT_TOKENS 的设计。

OpenRouter max_tokens 过滤上游(#2155):更隐蔽的一个——OpenRouter 会过滤掉 advertised output cap < max_tokens 的上游。所以当你想要 Cerebras 的快速推理,但传了从 catalog 拿到的 131072 上限,Cerebras 被过滤了,fallback 到 DeepInfra。修复很简单:OpenRouter 请求去掉 max_tokens,让每个上游自己 cap。Kimi 继续保留——它的 TPM 限流依赖这个参数。

@upstream 模型选择器:给 aggregator 模型的 provider 路由加了一个 @ 后缀语法。--model openrouter/z-ai/glm-4.7@cerebras 的意思是「这个模型固定用 Cerebras 跑」,不再走自动路由。和后面的 thinking level 可以组合:@cerebras:high。设计上考虑了 id 中本身就含 @ 的模型(如 workers-ai/@cf/...),不会误解析。

TUI scrollback 修复(#2154):终端内容填满 viewport 后,TUI 每帧都走 historyRebuild 销毁 scrollback。修复让新增的行只追加在尾部,历史行等到安全 checkpoint 才清理——看起来像小问题,实际是 TUI 最影响日常使用体验的 bug 之一。

升级方式

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

# Homebrew
brew install can1357/tap/omp

# Bun(推荐)
bun install -g @oh-my-pi/pi-coding-agent

# Windows (PowerShell)
irm https://omp.sh/install.ps1 | iex

完整更新日志:GitHub Release

📢 关注我,获取最新开源更新日志和开发者工具动态。如果觉得这篇文章有用,欢迎转发给身边的开发者朋友。

#oh-my-pi #AI编程 #开源 #fetch注入 #agent框架

oh-my-pi v15.10.7 发布 —— 三行 bugfix,四个故事

· 阅读需 5 分钟

omp v15.10.7 的三个 PR 加一个伴随修复,就是从这种「不可能复现」的 bug 开始的。

故事一:被中断的 thinking block,签名去哪了?

用 Anthropic 的推理模型(Claude Opus 4、Sonnet 4 等)时,模型在「思考」阶段会产出 thinking block,每个 block 在结束时由 Anthropic 服务器签名——这个签名在 content_block_stop 事件里下发。如果你在模型产出 thinking block 后的「可见文本」阶段按了 Ctrl+C(即 interrupt),transformMessages 会把该 turn 里所有 thinking block 的签名清空,包括那些已经完成推送的。

问题是:一个 thinking block 完成后紧跟的 text/tool_use block 已经开始了,中间的 content_block_stop 事件下发的签名是有效的。强行清空后重新发送时,signature: "" 被 Anthropic 检测到并拒绝——签名是完整性校验的一部分,空字符串意味着篡改。

修法只有一行判断:只清除中断时刻那个「当前未完成」的 mid-stream thinking block 的签名,已完成的所有 block 保留原签名。(#2144)

这个 bug 影响的人群很特定:使用 Anthropic 原生 API 或经由 LLM 网关转发、且频繁中断模型输出的人。也就是 omp 的日常用户——因为你每天都在中断它。

故事二:Opus 4.6+ 的 clear_thinking 被误杀

这次还有一个更隐蔽的问题,编号 #2123。Claude Opus 4.6+ 的「自适应思考」(adaptive thinking)模式会发送一个特殊的 clear_thinking_20251015 内容块,它和 thinking 字段是配对出现的。当你设置了 tool_choice: "tool"tool: "any"(强制工具调用)时,omp 调用 disableThinkingIfToolChoiceForced 方法——它会清掉 params.thinking

但它忘了配套的 context_management 字段。结果就是一个没有 thinking 却有 clear_thinking_20251015 的畸形请求发到了 Anthropic,返回 400:clear_thinking_20251015 strategy requires thinking to be enabled or adaptive

修复很干净:现在 context_managementthinking 字段一起被移除,不留孤儿。

两个 Anthropic 修复讲的是同一个道理:你改了 A 之后,别忘了 B 和 C 是绑在一起的。在 Anthropic 日益复杂的请求结构中(thinking、tool_choice、context_management、beta headers),这种耦合越来越多。

故事三:omp 启动卡了 30 秒,是谁在拖后腿?

这是 #2100。现象:你执行 omp,等待,命令行空白了 25-30 秒,然后突然正常了。你查日志,发现一个 MCP server 超时了,但其他的都好好的。问题是——为什么一个服务器的超时要拖累整个 UI 的 ready 信号?

MCPManager.connectServers 的实现逻辑是:对每个 pending 状态的服务器执行连接,然后用 Promise.allSettled 等所有连接结束。问题是,allSettled 不区分「这服务器有没有缓存工具」——你的 SQLite MCP server 可能需要外部数据库启动 5 秒,而这 5 秒内 omp 不会通知前端「我准备好了」。

更糟的是,如果你配置了一个不被缓存的 MCP server(比如一个只在启动时才注册工具的 server),Promise.allSettled 会把所有待连接服务器捆在一起:一个卡 30 秒,整个 omp UI 就等你 30 秒。

修法:pending 且无缓存的服务器不再阻塞 ready 信号。它们被留在后台,tools 通过 #onToolsChangedrefreshMCPTools 路径异步上浮,连接失败由后台 catch handler 记录。

如果你配了多个 MCP server,升级后最直接的感觉就是——omp 启动速度不再等于最慢那个 MCP 的连接时间。

故事四:字数不多但值得说的

MCP OAuth fallback 的渲染修复。 v15.10.5/v15.10.6 加入了 OAuth fallback,但 terminal 里显示的授权 URL 被折行、覆盖、无法点击复制。现在展示简短超链,原始 URL 保持在一行内可复制。(#2121)

Antigravity 用量计数器独立显示。 如果你同时用 Google/Gemini 和 Anthropic/OpenAI 的 Antigravity 通道,Google 配额耗尽时,之前会把所有计数器的「空闲百分比」都渲染成 0%。现在各通道独立显示:Google 显示 0% free,Anthropic 那边该多少还是多少。


oh-my-pi v15.10.2 发布 —— 40 多条修复,暴露了终端里那些你忍了很久的问题

· 阅读需 7 分钟

这些都不是段子——它们是 oh-my-pi v15.10.2 里一一被修掉的 bug。

oh-my-pi v15.10.2 封面

「断连」是最隐蔽的坑

先讲一个最容易被忽视的问题:代理流(proxy stream)断了会怎样?

过去的行为是——服务器断开且没发 doneerror 事件,oh-my-pi 默默返回一个零 token 的成功响应(stopReason: "stop"),看起来一切正常,但内容全空。调用 stream.result() 的代码挂着不返回,没人知道发生了什么。

v15.10.2 把它改成了抛 error 事件,带着 stopReason: "error",然后 resolve finalResultPromise。断连终于不再沉默,而是明明白白告诉你「出问题了」。

这跟你在 REST API 调用里遇到了网络错误但被告知 200 OK 是一个级别的麻烦。能发现这个 bug 本身,说明 oh-my-pi 在用户活跃场景下跑了足够久。

另一个同类问题:Anthropic provider 遇到账号级别 429("This request would exceed your account's rate limit"usage_limit_reached),旧代码把它当临时错误重试——2s、4s、8s 回退,重试循环跑完一遍还不够,streamSimple 的 a/b/c 策略再换一个 credential 来一遍……结果服务器早已告诉你「歇上几分钟再说」。你看到的就是那只转啊转的 spinner 从不停下来。修法很简单:加了一个 isUsageLimitError 识别,命中的时候直接 surface 到 credential-rotation 层——omp dry-balance --bench 面对限速账号秒出结果,不用挂着干等。

按键失灵感是怎么来的

v15.10.2 修了好几个跟「输入响应」相关的 bug,这些 bug 单看都很小,合起来就是一个个「电脑卡了」的错觉。

最典型的是 Ctrl+Z(挂起进程):oh-my-pi 注册了一个 SIGTSTP 信号处理来支持终端挂起/恢复。问题是,Windows 上没有 SIGTSTP——Bun 和 Node 在 JS 侧直接抛 TypeError: Unknown signal: SIGTSTP。这个异常从 TUI 输入调度器冒出来,成了未捕获的异常。修法:Windows 上直接 no-op 并报「此平台不支持挂起」,POSIX 上用 try/catch 兜住异常,失败时自动解注册 SIGCONT 钩子并重启 TUI。

Esc 取消操作也有毛病:你摁了 Esc,agent 同步发起了 abort,但 loader 还在那里显示 "Working…",看起来像是键盘没响应。原因是 agent 循环要等所有正在执行的工具都 settle 了(await Promise.allSettled(...))才会走到 agent_end 切换 loader——而 process/subagent 等工具有宽限期(SIGTERM → 2-3s → SIGKILL)。v15.10.2 在 Esc 触发的时候立即切换 loader 显示 "Interrupting…"(通过 EventController.notifyInterrupting),让你知道系统已经收到了你的指令,只是在优雅地收尾。

Windows ConPTY 还有一个「滚动位置幻觉」:长 session 重绘后终端的 viewport 停在顶部,不滚动到底部。原因是 ConPTY 在 write 超过约 32 KiB 后丢失了光标追踪——数据全写到了终端,但宿主 UI 不知道光标在哪。修法是 ProcessTerminal#safeWrite 把超大写入拆成 ≤ 8 KiB 的片段,这样每段 WriteFile 都保持在 ConPTY 能追踪的范围内。

内存里的那些悄悄话

oh-my-pi 有一个用 Memory 管理聊天历史的系统。当你对话历史增长、它自动做 compaction(压缩)的时候,发生过一件微妙的事:压缩后 session 还在,但历史不完整。

原因出在 Anthropic 的 empty toolUse stops without tool calls——模型输出了一串空的 tool 调用请求(没有真正的 function call),压缩时把相关的 turn 判定为冗余删除了,但模型依赖的上下文断了。v15.10.2 的修复会识别这种空调用,在重试之前就把这些孤立的 turn 处理掉。

还有一个更隐蔽的:OpenAI Responses 家族(包括 CodeX、Azure Responses)在处理 session 树分支时,上下文停在一个 tool call 上,后续请求报 400 No tool output found for function call——tool-result 在重建历史时丢了。修法是在未配对的 function_call / custom_tool_call 之后立刻合成一个占位 function_call_output / custom_tool_call_output,让模型能 recover。

MiniMax 的 tool call 也有问题——function.arguments 拆分到多个 delta 里传输时,每个 chunk 的 partialArgs 被覆盖(partialArgs = rawArgs 只保留了最后一个 chunk)。结果是一个 edit 调用,实际应用的 patch 只包含最后几个字符。浅合并修复了这个问题:对共享的 string key,用 startsWith 区分累积重述(取最新的)和分片 delta(拼接)。

平台兼容不是一句口号

v15.10.2 里有大量针对特定终端/系统的「微调」。不值得大惊小怪,但哪天你换了终端模拟器,这些改动就会变得非常关键。

Kitty 键盘协议有一个时序问题:终端的 DA1(主设备属性)响应先到达,触发了 modifyOtherKeys fallback,等 Kitty 的 CSI u 回复到了却被丢弃了。结果 Superset 和 Electron 上的 xterm 一直走 fallback,Shift+Enter 发的是裸 \r。修复后 DA1 sentinel 不再覆盖 Kitty 回复,Kitty 协议优先。

还有一个:Kitty 的 OSC 5522 粘贴模式的 DATA payload 格式是 mime="."(空格分隔的 MIME 列表),不是规范文档里写的按类型分包的 DATA 结构。原来只有后者的解析逻辑,现在两者都覆盖了。

Linux 上的剪贴板问题更有意思:arboard 在 Clipboard 实例销毁时终止了后台服务线程。每次 copyToClipboard 新建一个 Clipboardset_text 写完就析构——X11 选择器失去了所有者,关掉进程后粘贴返回空字符串。修法是 Linux 路径上为进程生命周期持有一个全局 Clipboard(macOS 和 Windows 继续用瞬态写入)。

日常操作的隐身修复

以下这些修了,但你可能永远不需要知道——除非你踩到过。

eval 的 JS worker 冷启动在 --isolate + 高并发下偶尔被终止:默认 5s 超时覆盖了 worker init 时间,而终止一个尚在初始化中的 Bun worker 是文档记录的 SIGILL/SIGTRAP 触发条件。现在 worker init 固定 15s 基础设施预算,eval test suites 设置 20s 文件级超时。

searchfind 以前不认带括号的路径——Next.js 风格的 apps/[id]/page.tsx 被解释为 glob pattern,触发 glob 展开后匹配不到。现在首选字面路径解释,文件存在就直接用,不存在才回退到 glob。

read 做了两件事:local:// 和绝对路径的双向可互换(让 plan mode 下用 read 返回的标签路径编辑不会再报「tag not found」),以及文件快照通过 realpath 键归一化来避免 macOS 上 /tmp vs /private/tmp 的符号链接分歧。

还有一个你大概率试过:用 omp --cwd repo 启动时,repo 是相对路径(比如在 /tmp 下跑)。旧代码正确 resolved 并通过 setProjectDir 做了 chdir,但 parsed.cwd 还存着原始字符串 "repo"。后续 buildSessionOptions 优先用 parsed.cwd,结果路径变成了 /tmp/repo/repo。修完以后 parsed.cwd 也在 chdir 后同步为绝对路径。

还有几十条类似级别的修复——MiniMax think tags 跨 provider 解析、IRC 子 agent 的 findings schema 注入抑制、read 的 PDF/DOCX 行范围选择器被忽略、hashline 行号前缀注入污染、browser 的 credentials 泄漏、lsp 引用搜索重试机制、lsp 重命名文件过滤——每一个都是有人在真实环境中碰到了才被发现。

升级

curl -fsSL https://omp.sh/install | sh
# 或用 Bun
bun install -g @oh-my-pi/pi-coding-agent
# 已安装用户
omp update

⚠️ npm 包与 GitHub Release 可能有短暂延迟,安装后运行 omp --version 确认版本号。

完整更新日志:GitHub Release

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

#oh-my-pi #AI编程 #修复日志 #开源 #终端工具 #开发者体验

oh-my-pi v15.10.4 发布 —— 二进制终于有了签名,Homebrew 也找上门了

· 阅读需 5 分钟

v15.10.4 把这个问题彻底解决了——二进制签了名、走了公证,Homebrew 也正式上车了。

oh-my-pi v15.10.4 封面

以前:每次安装都要过三道关

没签名的 macOS 二进制面对的是这样的流程:下载 → 系统提示「无法验证开发者」→ 去设置里点「仍要打开」→ 输入密码 → 终于能用。不是不能用,但这个流程在每台新电脑、每次重装、每次给同事推荐时都会出现一次。

如果你用过 curl -fsSL https://omp.sh/install | sh,你可能已经习惯了这一步。但对比一下系统自带的工具、VS Code、甚至是 brew 装的软件——它们从来不弹这个框。因为它们的开发者付了 99 美元的年费走了 Apple Developer Program,给二进制打了签名、走了公证流程。不是 omp 不愿意,是之前这套自动化的 CI 还没搭好。

现在:签名 + 公证 + Homebrew,CI 一条龙

v15.10.4 的改动藏在构建流程的最底层。macOS release 二进制现在用 Developer ID Application identity 签名,启用了 hardened runtime + secure timestamp + JIT/library-validation entitlements,并且在 CI 中自动走 Apple notarization 流程。整套 pipeline 封装在 CI 配置里:签名密钥(APPLE_* 环境变量)配好后自动触发,配不上就降级为 ad-hoc 签名,不影响开发。

有了签名和公证,Homebrew 的安装路径就顺理成章了。brew install can1357/tap/omp 现在可以直接用——CI 中新增的 release_brew job 在每次发布时自动更新 formula(版本号 + 每个 bin 文件的 SHA256)。签名后的二进制走 Homebrew 安装,全程不弹 Gatekeeper 对话框。

有意思的是,签名 + 公证 + Homebrew 这三件事是互相依赖的:没有签名就不能走公证,没有公证 Homebrew 的 CI 验证不会通过,三件缺一不可。之前不做,可能是因为觉得「反正 curl 安装也能用」。但想做正规 macOS 分发,这三关绕不过去。(#776)

Anthropic OAuth 伪装也升级了

这个版本对 Anthropic 接口的适配做了两件事。

第一,指纹同步到了 Claude Code 2.1.165。 claudeCodeVersionclaudeCodeSystemInstruction(改成了"built on Anthropic's Claude Agent SDK")、cc_entrypoint(从 cli 改为 local-agent)——这些 OAuth 会话里的身份标识全都更新了。翻译成人话:omp 冒充 Claude Code 的伪装更逼真了,被 Anthropic OAuth 拒绝的概率更低。

第二,max_tokens 的 clamp 逻辑。 OAuth 请求的 max_tokens 现在取 Math.min(CLAUDE_CODE_MAX_OUTPUT_TOKENS, options.maxTokens || model.maxTokens),上限 64k。之前发请求时会带上 model 的全量 ceiling(比如 Opus 4.8 的 128k),但 Claude Code 自己从来不这么发。现在对齐了,OAuth 的请求特征更接近官方客户端。

还有一个细节:非内置工具的名称前缀从 proxy_ 改为 _。比如 bash 之前发给 Anthropic 是 proxy_bash,现在变成 _bash。粗看只是换个字符,但 Anthropic 可能对 proxy_ 前缀有 server 端的行为判断——改完后匿名性更进一步。

🛠️ 其余修复

ConPTY 会话恢复截断。 Windows 上用 Terminal 的用户可能遇到过:恢复一个大会话后,终端底部有几行内容被截断看不到,按下 Alt+Tab 强制重绘后才正常。根本原因是 ConPTY 在大量并发写入时,viewport 跟不上游标的移动位置。修复方案挺彻底的:大图渲染后设置 150ms 的 ConPTY 静默窗口,在这期间合并所有触发的 requestRender 调用;同时把每次 WriteFile 的块上限从 8 KiB 翻倍到 16 KiB,并且改用 UTF-8 字节计数而非 JS 码位计数——CJK 密集的会话不会膨胀到之前的 ~48 KiB 触发 viewport bug。(#2095)

JS eval worker 优雅关闭。 在 Bun 1.3.14 上热加载 canvas 等原生模块时,worker 被强行终止会触发 N-API teardown 崩溃。现在先发 close 信号让 worker 自己清理,再强制终止。(#2079)

completion() 三个小修复。 当未传 system 参数时始终发一个非空默认 system prompt(某些 provider 拒绝空指令);结构化输出模式下,当模型跳过了 respond 工具的强制调用时,从纯文本输出中解析回 JSON;慢 tier 推理请求只对支持推理能力的模型启用 effort 设置,并且限制到支持的级别。

升级

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

# Homebrew
brew install can1357/tap/omp
brew upgrade can1357/tap/omp

# Bun
bun install -g @oh-my-pi/pi-coding-agent
bun update -g @oh-my-pi/pi-coding-agent

完整更新日志:GitHub Release

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

#oh-my-pi #macOS #Homebrew #签名公证 #AI编程工具