跳到主要内容

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

查看所有标签

Oh My Pi v16.0.2 发布 —— 一个工具 schema 写错了,整个对话就炸了?现在不会了

· 阅读需 6 分钟

在 coding agent 越做越重的年代,这类「一个错误拖垮全局」的体验不是个例。v16.0.2 的主题很简单——不要因为一个工具的 schema 有瑕疵,就让整个回合替它陪葬。

Oh My Pi v16.0.2 封面

一个工具的 schema 错误,不该炸掉十几个工具的回合

这次最核心的修复在 @oh-my-pi/pi-aiconvertTools 方法里。之前 OpenAI Responses API 的 strict tool schema 验证逻辑是全有或全无——如果某个工具的 enum/consttype 不匹配(比如一个 type: "null" 的节点上挂了非空的 enum,或者 array 节点上挂了 enum),provider 直接返回 HTTP 400,整轮对话报废。现在 convertTools 对每个工具的 schema 做了逐项校验,发现违规的 只隔离那一个工具,记一条 logger.warn 标明哪个工具、哪个 schema 路径出了问题,其余所有工具照常可用(#2652)。

这个设计的聪明之处在于:之前你装了 MCP 工具栈之后出了 400 错误,排查路径是先怀疑 provider、再怀疑 prompt、最后翻几十个工具的 schema。现在 logger 直接告诉你「xxx 工具的第 3 个参数的 enum 和 type 打架了」,你可以选择修它或者暂时禁用它,不耽误继续用其他工具。

Provider 兼容性:不是 provider 不行,是适配方式太脆弱

这版本有好几个修复其实是一个主题——OpenAI Responses stream 下并行 tool call 的 delta 路由。如果你用 Ollama 或本地模型跑 Responses 兼容流,fc_<call_id> 前缀的 delta 会被上游模型丢弃或颠倒次序。之前 ast_grep 调用拿到的是 {},校验直接炸。现在 delta 路由做了前缀匹配修正,Ollama 本地模型下并行调用不会丢参数了(#2715)。

同样跟 Google Gemini 有关的一个隐藏很深的问题:Gemini 3 系列模型对 toolChoice: "auto" 的序列化方式很敏感。以前 toolConfig AUTO mode 被显式发送出去后,Gemini 3 不走工具交付流程,直接丢出了原始的规划 JSON——也就是你以为它在执行 ast_grep 找代码,实际它在输出「嗯让我想想先找什么」。现在 toolConfig 的序列化做了调整,Gemini 3 能正确执行工具调用了(#2776)。

OpenRouter 的路由选择器也有一个边缘 case:如果你的上游 slug 恰好和模型 id 里的一段字符串重复(比如 @deepseek:high 匹配到了 deepseek-ai 的模型 id 里面),openRouterRouting.only 配置会被无视,路由退化到 provider 级别的模糊匹配。现在路由选择器做了 slug 区分匹配,确保 @upstream 指示符不被模型 id 字段吃掉(#2708)。

TUI 和终端:Warp 用户终于不用忍受布局风暴了

如果你用 Warp 终端跑 Oh My Pi,可能遇到过这个现象:拖动窗口调整大小时,TUI 疯狂重绘,CPU 飙升,闪烁到根本没法操作。原因很搞:Warp 在 alt-screen 切换时会重新上报一个小一行或大一行的尺寸,每拖一次鼠标就触发一次 resize,然后 TUI 收到 ED3 全量刷新指令——全量刷新又触发 alt-screen 切换,又触发新的 resize。死循环。

修复方案很干脆:对在 alt-screen 切换后重新上报尺寸的终端,resize 不再借 alt-screen 刷 ED3 wrap,而是原地重绘。Warp 用户下次拖动窗口时不会再触发风暴了。可以通过 PI_TUI_RESIZE_IN_PLACE=1|0 自定义这个行为。

另外 CMUX 会话也被单独识别了——之前 CMUX 不设 TMUX/STY/ZELLIJ 环境变量,TERM 可能还是 dumb,导致 TUI 认为它是直接终端而非复用器,每次 reset 都会清空面板滚动缓冲区。现在 CMUX 的工作区/表面环境标记被纳入复用器检测条件,滚动缓冲区保住了。

编码体验加强:Emacs Lisp 用户终于有了 AST 工具

@oh-my-pi/pi-natives 新增了对 Emacs Lisp(.el.emacs)的 tree-sitter 语言推断支持。这意味着 astGrepastEditsummarizeCodeblockRangeAt 等操作在 .el 文件上都能用了。如果你用 .emacs 配置或拆成 .el 模块管理 emacs 配置,现在 agent 可以结构性地理解你的 init 逻辑、找到嵌套的 defunlet 块,而不是把它当纯文本逐行猜。

GitHub Copilot 的 .github/instructions/*.instructions.md 规则文件也被正确加载了——之前的发现逻辑没处理好 applyTo 作用域限定,现在 ** 全局规则和 rule://<name> 访问模式都能用了,Copilot 用户的项目级指令不会在 agent 侧被忽略(#2731)。

插件系统修复:测试不再 rm -rf 你的 node_modules

有一个 bug 我觉得值得单独拿出来写:plugin-extensions-discovery 测试套件会在某些环境下 rm -rf 开发者真实的 ~/.omp/plugins 目录。原因很经典——XDG 路径隔离只在 Linux/macOS 上生效,Windows 无视它;而在 XDG-migrated 的 Linux/macOS 环境里,隔离机制本身也失效了。测试直接对真实的 node_modules 下了 rm -rf 命令。现在整个测试用了 os.homedir() mock + 清空 XDG_* 变量,加上写入前检查——如果目标路径逃逸了临时 home 目录,测试直接 fail,不会动手删文件(#2721)。

其他值得注意的修复

  • 插件扩展目录解析:pi.extensions: ["./extensions"] 目录方式现在被正确支持了,安装时不会再报 declared extension entry not found on disk(#2713)
  • sub-agent 的 fallback 链:子 agent 的候选模型列表现在能按顺序 fallback 了,某个 provider 挂了可以自动切下一个模型(#2750)
  • omp plugin list --json 不再遗漏本地链接的插件(#2742)
  • /exit/quit 不再等待每一个扩展的 shutdown timeout——所有 session_shutdown handler 在同一窗口期内运行,退出更利索(#2736)
  • Windows bash-tool 子进程默认使用 UTF-8 编码,而不是 ANSI codepage,中文路径不再乱码(#2701)
  • Hashline 自动修复了 JSX/XML 闭合边界行的重复问题(#2705)
  • Kimi 输出上限的合规处理:Umans 网关下的 Kimi 请求现在正确使用 output cap,而不把上下文大小的 token 天花板当作请求上限

升级

Oh My Pi 支持多种安装方式:

# 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

# Pinned versions (mise)
mise use -g github:can1357/oh-my-pi

完整更新日志:GitHub Release

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

#OhMyPi #CodingAgent #AI编程 #Terminal #开源工具

oh-my-pi v15.13.1 发布 —— 专注力是程序员最稀缺的资源,这次版本全在为你省

· 阅读需 8 分钟

oh-my-pi(以下简称 Pi)v15.13.1 的更新清单里,一半以上的改动指向同一个目标——让你在编码时保持专注,减少哪怕一秒钟的不必要打断。

虽然这次没有"颠覆式"的新功能亮相,但 v15.13.1 可能是今年以来最"护心流"的一个版本。从配置文件隔离到大粘贴处理,从 Linux 兼容到循环检测,每一个改动的落点都很一致:在你最不想被打断的时候,它不打断你。

oh-my-pi v15.13.1 封面

一、一个 --profile,让每个项目都有自己的「分身」

如果你同时在维护两个以上项目,一定遇到过这个场景:项目 A 用 Claude Sonnet、配了一套 MCP server(模型上下文协议服务器)、开了自动学习(Auto-learn)技能;切到项目 B 的时候,所有的配置参数、插件、记忆全部混在一起。改一个项目想用 Gemini,发现全局设置被改了;想装一个新技能,结果另一个项目白名单里多了个奇怪的插件。

v15.13.1 的 --profile <name> 就是来解决这个的。

你只需要在启动时带上 --profile project-a,Pi 就会为该配置文件独立的状态根目录(profile root)——包含用户配置、扩展目录、MCP 凭据、技能目录全部按 profile 隔离。环境变量也能感知 profile:OMP_PROFILE 优先于 PI_PROFILE,profile 内的 .env 里声明的 XDG_DATA_HOME 等目录变量会被正确重建,不会泄漏到其他 profile。

更让我欣赏的是细节一致性的处理/mcp unauth 删除 OAuth 凭据时,会跳过其他 profile 的凭据行,不会误删兄弟配置的认证信息;Auto-learn Managed Skills 也使用活跃 profile 的 agent 目录,profile 级别的技能始终优先于全局 fallback。

这不是一个让你「记得切 profile」的功能,而是一个让你忘掉「切」这个动作本身的功能。用的人越多,越记不起没有它的日子。

二、输入方式不再只有键盘

说这是本版本我最喜欢的改动群也不为过——因为它们在降低「输入摩擦」这件事上,做得太细了。

语音转文字预览Editor 新增了 setVolatileText() / commitVolatileText() 接口。这意味着那些支持语音输入的扩展现在可以在编辑器里预览实时转录——看到文字一个个跳到光标处,而不是等语音说完了才啪一下出现。前者让你在说话时就知道错了可以重说,后者让你说完才默默祈祷正确。你感受一下区别。

大粘贴感知增强(Large-paste hook):当你粘贴了一大块内容(超过可配置阈值)时,Pi 不再默默缩成一个 [Paste #N] 标记。现在它会触发一个钩子(hook),你可以选择用代码块包裹、XML 标签包裹、或者作为附件保存。对于经常从浏览器复制代码段、从 API 文档粘贴响应体的开发者来说,这解决了长期以来"粘贴完格式全崩"的核心痛点。

Ctrl+J 换行:这个小细节值得单独说。很多终端工具在 shift+enter 被占用时,用户就永远失去了手动换行的权利。现在 ctrl+j 作为第二绑定始终可用——如果在现代终端上,它就是真正的换行;如果是传统终端(ctrl+jEnter 都发同一个字节),它仍然提交,不会崩溃。

三个改动各自都很小,但组合起来的体验提升是1+1+1>3。输入不只是打字,输入是所有你把自己脑子里的东西搬到工具里这个过程。 这次 Pi 至少在这条路上多了三种沟通渠道。

三、Linux 用户的救星:glibc 兼容修复

如果让我只能挑一个"最重要的修复",我选这个。

v15.13.1 修复了 Linux 原生扩展加载不上的问题:之前 Pi 的 native addon 构建在 Ubuntu 24.04 的自托管 runner 上,编译产物锁定了 glibc 2.39。这意味着任何 CentOS 7、Ubuntu 20.04、甚至某些 System 76 的 Pop!_OS 用户,都有可能遇到 version 'GLIBC_2.39' not found 的错误。native addon 加载即失败,整个工具无法使用。

解决方案很彻底:x64 和 arm64 的 native addon 现在都通过 cargo-zigbuild 构建,glibc 版本固定在 2.17——这是 CentOS 7 和 Ubuntu 14.04 时代的基线。任何 glibc >= 2.17 的 Linux 发行版,现在都能原生运行 Pi。 这一刀切掉了一整个"因为构建环境太新而无法在旧系统上运行"的痛点群。

同属这个类别还有另一个隐蔽问题:之前 v15.12.6 引入的 Tokio 运行时初始化放在了 #[module_init] 里,而 #[module_init] 在动态加载器持有锁的时候执行。如果在锁内启动多线程运行时,新创建的 worker 线程去获取加载器锁——死锁。现在运行时初始化被移到了 dlopen 返回之后,彻底解决了这个启动死锁。

这对双修桌面用户(比如用 System 76 跑开发、用 Arch 跑实验、或者公司发的是 Ubuntu 20.04 工作站)尤其友好。 一个安装脚本覆盖所有,不用再费心折腾。

四、看得见的改善:Snapcompact 和 Collab

Snapcompact 是 Pi 的屏幕截图紧凑化(snapshot compaction)引擎,负责把工具调用的输出渲染成图片塞给大模型(LLM)。之前的设计偏 SQuAD 文本评估(text eval)优化,密度取胜;但现在代码搜索和读文件对行号、缩进敏感,太密的字形把这些结构信息全抹掉了。

v15.13.1 新增了两个间距微调字形变体:11on16-bw(加宽字距)和 8on22-bw(加宽行距),并且作为 Anthropic(默认用 11on16-bw)和 OpenAI/Google(默认用 8on22-bw)的新的默认形状。工具调用输出的工整性和可读性大幅提升——opencode-codex 的用户也获得了 200 张图片的请求预算,与 OpenAI API 一致。

Collab-web 的移动端修复也值得用户点赞:iOS Safari 输入框聚焦时不再自动缩放,Safe-area padding 让刘海屏 / 挖孔屏不再遮挡界面元素,垂直弹跳手势被禁用避免误触刷新页面。如果你经常用手机查看 Pi 的 collab 会话,这次的体验提升是立竿见影的。

五、深水区的修复同样精彩

每个版本都有那些「用户不一定会注意到,但一旦注意到就极其痛苦」的修复。v15.13.1 的深水区同样不含糊:

Gemini 重复循环检测:在流式推理中,AI 模型有时会陷入重复输出同一个文本或思考块(thinking block)的循环。之前这种循环可能被误判为正常 long output,白白浪费 token 和等待时间。现在 Pi 通过一个 O(1) 的滚动尾队列(bounded rolling tail)实时检测重复单元,一旦发现就把重复块折叠成一个代表副本、终止 provider 请求,优雅结束本轮。纯数字和空白序列不被误报——写代码时的一堆数字输出不会被打断。这是一个看着简单、做起来极其麻烦的修复。

Hashline 对 SwiftUI 的修复:如果你用 Swift/SwiftUI,replace block 操作执行在空行后的 var body: some View { 上一直报错「无法解析语法块」。原因是 tree-sitter-swift 在空行后的语句前插入了零宽度分隔节点(zero-width separator),导致语法解析器查不到正确的代码块。现在查询范围覆盖了第一个内容字符(而非零宽度点),SwiftUI 用户终于可以在编辑器中随便用 replace block 了——这是一个极其罕见的语言解析 bug,值得为 tree-sitter-swift 的隐式行为点赞背后的修复者。

Todo 提醒不再自激:之前的 Todo completion 进度提醒会在一次短暂的用户暂停中 1/3 → 2/3 → 3/3 三级跳——因为 agent 的文本确认触发了重新检查,每次检查自动升一级。现在引入了 #todoReminderAwaitingProgress 标记:进度提醒设置后,必须等到真正的工具结果或用户输入才会触发下一次检查。你不会再被在发号施令后的几秒内连续三次追问进度。

Mnemopi 嵌入模型自动迁移:当你换了嵌入模型(embedding model)后,旧的向量嵌入全部失效。以前你得手动重建记忆库。现在 v15.13.1 的 reconcileEmbeddingModel 自动检测配置变化——删除旧嵌入、在后台按批次重新生成新嵌入。新的嵌入还没就绪前,系统优雅降级到全文搜索(FTS),不会留下查询盲区。

升级方式

如果你已经在用 Pi,升级过程一如既往的简单:

# 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 v15.13.1

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

#ohmyPi #AI编程 #开发者工具 #Linux兼容 #终端工具

oh-my-pi v15.13 发布 —— 以前只有 GLM 语法,现在给每个模型写了方言

· 阅读需 6 分钟

oh-my-pi(简称 Pi)今天一口气发了两版(v15.13.2 + v15.13.3),两版都在做同一件事:把 tool-calling 从「一套语法走天下」变成「每个模型用母语说话」。以前只有 GLM 一个 in-band 语法选项(用来在不能发 native tools 的场景下兜底),现在覆盖了 8 种;同时 Azure OpenAI 以一等公民正式入驻,还多了 interruptible 机制——agent 可以在工具执行中途听指挥改方向了。

oh-my-pi v15.13 封面

以前:不是不会 multi-tool,是只说一种方言

Pi 对比其他编程助手的核心差异之一,是有一套 in-band tool-calling 系统——当模型不支持原生 tool calls 或者你需要更精细的控制时,模型通过文本来「声明」它要调什么工具,而不是发 native tool calls。

但 v15.13 之前,这套系统只支持 GLM(智谱)一种语法。PI_OWNED_TOOLS=1 就是 GLM 模式,别无选择。问题在于:每个模型家族的 in-band 格式差异巨大。同一个 tool-calling 语义,Claude 用 <tool_use> XML 块表达,DeepSeek 用 DSML,GPT-5 是 Harmony 的 to=functions... headers,Hermes 用 <|tool_call|> token——它们写的是同一件事,但语法天差地别。

现在:8 种语法,每个模型说自己的语言

v15.13.2 最核心的变化,就是把 PI_OWNED_TOOLS 从布尔开关变成了语法选择器。PI_OWNED_TOOLS=<syntax> 直接指定语法,并且后台自动为每个模型匹配它最舒服的格式——preferredToolSyntax(modelId) 函数从模型家族 token 推断:Claude → anthropic、GLM → glm、Kimi → kimi、Qwen → qwen3、DeepSeek → deepseek、OpenAI/gpt-oss → harmony,其余 fallback 到通用 xml。

我特别喜欢这个设计的一个细节:每种语法都配了独立的 scanner、prompt、history renderer、stream adapter。不是「把不同语法转成统一格式再处理」,而是在各自的范式内部直接工作。代价是开发成本高——每新加一个语法都要从头写一套解析/渲染管线——但收益是 tool-calling 更准确,不会因为格式转换丢失边缘 case。

配套的 ToolExample 类型系统也同步重构了。13 个内置工具(evalbrowsertodopatch 等)的 examples 从手写 markdown <examples> 块变成了类型化的 ToolExample<z.input<typeof schema>> 数据,AI 层自动用模型的母语语法渲染。你在配置中看到的文档示例,现在是「到哪个山头唱哪首歌」。

v15.13.3:打断正在执行的工具、Azure OpenAI 正式入驻

v15.13.3 同一天跟进,集中在三个方向:

打断机制(interruptible):新增 interruptible 工具字段。当 agent 处于 immediate 中断模式时,如果用户在工具执行中途发了一条 steering 消息,agent 可以提前终止当前工具、立即处理这条指令。以前想发一条「别查了先看看这个文件」只能等工具跑完。现在不等了,说到一半就能打断。

Azure OpenAI 入驻:Azure 用户可以直接认领 GPT-4/4.1/4o、GPT-5 家族、o-series、Codex 等模型,通过 AZURE_OPENAI_API_KEY 激活,deployment URL 由 AZURE_OPENAI_BASE_URLAZURE_OPENAI_RESOURCE_NAME 决定。Azure 的 Responses API 在 reasoning 模型上有个小差异——xhigh 等 effort 控制项需要经过 Azure proxy 层额外处理——Pi 已经完成了兼容适配,推理模型的 effort 控制可以正常工作。

Gemini/Gemma 语法:增加了 Gemini 的 ```tool_code Python 风格语法和 Gemma 的 <|tool_call|> token 分隔语法。更重要的是修复了解析器在 Python 注释、字符串字面量等边界情况下的 bug——如果解析器在注释里看到 ```tool_code 就以为要调工具,session export 会多一堆虚假调用记录。

两个藏得很深的流式 bug 修复

Cursor MCP 参数的重复拼接(#2615):用 task 工具做多子 agent 调度时,Cursor provider 的 stream handler 在拼接 args_text_delta 时把累积的快照追加到缓冲区上,导致 JSON 被重复拼接——tasks 数组收到的是 ["task1"]["task1task2"] 而非 ["task1","task2"]。修复后 handler 会去掉已缓冲的前缀,并且把 McpArgs map 合并进流式参数而不是直接覆盖。

Codex 并发 tool-call 的 item_id 污染(#2619):同时并发两个 tool-call 时,args_text_delta 只跟踪一个 currentItem,第二个 tool-call 的 delta 被错误地追加到第一个的参数里。现在按 item_id 做 key 来路由 delta 事件,output_index 兜底,同一工具的多 call 不再互相污染。

这两个 bug 藏得都不浅——你不做多子 agent 并发调度、不用 Cursor/Codex provider,永远不会遇到。但对使用这些场景的人来说,每次 taskInvalid input: expected array, received undefined 的根源就在这里。

hashline DSL 大改名

hashline(Pi 的行内编辑 DSL)把所有操作符换成了缩写关键词。影响面不小——如果你在写自动化脚本或用 hashline 做批处理:

replace → SWAP delete → DEL
insert before → INS.PRE insert after → INS.POST
insert head → INS.HEAD insert tail → INS.TAIL
replace_block → SWAP.BLK delete_block → DEL.BLK
insert_after_block → INS.BLK.POST

Pi 对新旧格式同时解析(.= 也替代了 .. 作为推荐的范围分隔符),现有脚本不会立刻崩,但新写法应该开始适应了。我的建议是:写新脚本直接上缩写,旧脚本等跑出问题时顺手改。

Unexpected Stop 检测 + 模型默认更新

v15.13.3 加入了一个轻量检测器 unexpectedStopDetection:当模型 assistant 回复中说「我要调工具了」但最终没有发出任何 tool call 时,一个 tiny/smol 分类器识别出这个模式并继续这一轮,不让对话干等。默认最多重试 3 次。

Pi 还为大量 provider 更新了默认模型指向:OpenAI 家族 → GPT-5.5,Gemini → gemini-3.1-pro-preview,GLM → glm-5.2,Kimi → kimi-k2.7-code,Qwen → qwen3.7-plus,Claude 代理中转类 → Claude Opus 4.x。如果你在用默认模型,现在打开 Pi 就是新版本。Fireworks 还新增了 flux-1-schnell-fp8gpt-oss-20bqwen3-embedding-8bqwen3-reranker-8b,Google 新增了 Gemma 4 E2B ITGemma 4 E4B IT

升级

# macOS / Linux(重新运行即升级)
curl -fsSL https://omp.sh/install | sh

# Homebrew
brew upgrade can1357/tap/omp

# Bun(推荐,与 CI 同速)
bun update -g @oh-my-pi/pi-coding-agent

# mise 管理版本
mise use -g github:can1357/oh-my-pi

完整更新日志:v15.13.2 · v15.13.3

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

#ohMyPi #InBandToolCalling #AzureOpenAI #CodingAgent #开源工具

oh-my-pi v16.0.0 发布 —— 你的 AI 助手旁边,现在坐了个审计员

· 阅读需 4 分钟

这是 v15.13 那波 dialect 语法大重构之后的一个大版本——版本号直接从 15 跳到 16,配了一个 Passive Advisor 系统。

oh-my-pi v16.0.0 封面

Passive Advisor:第二双眼睛

这个版本最核心的添加,是让用户指定第二个模型作为 Advisor。主模型在跑 tool calls、读文件、写代码的时候,Advisor 在隔离的 ToolSession 里同步审查——只读权限,不碰主模型的快照,不干扰执行路径。

审查结果分三个严重级别:

  • nit:不打断,排在下一轮输出前一起展示
  • concern / blocker:直接通过 steering channel 打断正在跑的 agent。工具执行中?打断。agent 已经 yield 了?恢复它再插话

以前你设过「rule injection」之类的东西——那是被动防御:模型写错了被正则拦下来,注入提醒。Advisor 是主动进攻:还没写错它就有意见。

这个设计的聪明之处在于隔离方案。Advisor 有自己的 ToolSession,read / search / find 各自维护独立的 snapshot 和 seen-lines cache,两边互不影响。主模型管执行,Advisor 管质量,各自的上下文预算不打架。

Advisor 的设置也分层了:advisor.enabled 控制顶层 session,advisor.subagents 控制是否延伸到 task/eval 子智能体。如果你让子智能体也配上 advisor,等于一个团队里每个工程师身边都跟了一个 code reviewer。

不过话说回来,这个功能的双刃剑很明显——多一个模型的 token 消耗翻倍,至少在关键 session 上开就行。omp 默认全关,advisor.enabled 和 advisor 角色配置好才激活。

配套的还有 /advisor on/advisor off/advisor status/advisor dump 四个 slash command。状态行上会出现一个 ++ badge(成功色),让你一眼知道 advisor 在工作。

11 种 Dialect,每个人说自己的话

v15.13 把 tool-calling 从「一套语法走天下」变成了「每个模型用母语说话」——支持了 8 种 in-band syntax。v16.0.0 在此基础上做了两件事:

从 Syntax 到 Dialect——统一命名toolCallSyntaxdialectPI_OWNED_TOOLSPI_DIALECTGrammarDialectDefinition。这不是单纯改名——每个 dialect 现在有完整的 renderThinkingrenderTranscript 方法,序列化的是完整的原生对话记录,不只是 tool call/result block。

从 8 种到 11 种。新增了 Gemma、Kimi、Pi-native 三条路子。更重要的补齐是 thinking channel:Gemini 拿到了 ```thinking fence(跟 ```tool_code 对称),Gemma 用其原生 <|channel|>thought...<|channel|> channel。Kimi 加上了 <think>...</think>,Pi 自己也补了 <thinking>...</thinking>。每个 scanner 现在把 reasoning 解析成 thinking event,不再把 chain-of-thought 泄漏到 visible reply 里。

顺便提一个开发细节:renderToolInventory 会在工具描述的 markdown 包含顶层 # header 时自动降级一级——不会让工具名称的 # Tool: foo 和描述里的 # 争级别。

其他更新

syncBacklogadvisor.syncBacklog 设为 135 可以让主 session 在 advisor backlog 降到阈值之前不进入下一轮,最长等 30 秒。适合那些「宁可慢一点也要确认上一步没问题」的场景。

onTurnEnd hook:每轮对话结束时触发,可以执行记账/状态持久化操作,支持异步。aborted 或 error 的 turn 自动跳过回调。

WATCHDOG.md 自动发现:用户目录和项目目录下的 WATCHDOG.md 自动附加到 advisor 的 system prompt 里。让本地监护规则指导 advisor 的审查方向——团队可以在 repo 里放一个 WATCHDOG.md,advisor 自动就知道什么可以放行、什么要拦。

/dump [raw]:新增 raw flag,默认走 compact markdown 输出,用 raw 退回到旧版 uncompact 格式。

startup.quiet 修复(#2639):之前 startup.quiet 只压制了模型输出,MCP 和 LSP 的启动状态还是会冒出来。现在真的安静了。

Windows bash 路径修复(#2634):MSYS/Git-Bash 的 /d/project 和 WSL 式的 /mnt/d/project 在 bash tool cwd 验证和 brush filesystem builtins 里统一走 native 路径了。Windows 双系统用户应该能注意到之前某些跨驱动器操作会挂。

升级

# macOS/Linux
curl -fsSL https://omp.sh/install | sh

# Homebrew
brew upgrade can1357/tap/omp

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

# macOS/Linux/Windows — pinned with mise
mise use -g github:can1357/oh-my-pi

完整更新日志:GitHub Release

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

#oh-my-pi #AI编程助手 #PassiveAdvisor #开源

oh-my-pi v15.13.0 发布 —— 以前是打字对话,现在它能说会听还会自己整理笔记

· 阅读需 7 分钟

描述意图要打字,补充上下文要打字,改需求要打字。你说得越急敲得越快,对方越容易丢上下文。更别说那些跑了大半个小时后突然断掉的流式输出、看着一个大文件不知道从哪里下手等等,每个场景背后都是一样的核心矛盾——你和工具之间的带宽太窄了。

oh-my-pi(下文简称 Pi)v15.13.0 是一次大版本更新,48K 的更新日志里藏着一个清晰的方向:把 AI 编程助手从「打字机」变成「贴身助手」。

核心痛点:打字之外的事情,全都没人管

如果你天天用基于大语言模型的编程工具,下面这几件事大概率都遇到过:

说比写快,工具只认写。 想口述一段逻辑重构思路,手指跟不上脑子。遇到复杂的调用链分析,眼神扫了三遍代码还没理清上下文。

学到的新东西,下次还要重新学。 花了半小时让 AI 理解你们项目的特殊约定,它记住了、帮忙改了代码。但下一个任务一来,之前的对话全部作废,又要重新教一遍。

流式输出动不动就死。 大型上下文流式推理场景下,Bun 原生的抓取请求(fetch)超时机制会在恰好 300 秒时切断连接,冷一个大上下文的流逝全部白跑。

每次切目标都要重新对齐。 要开始一个新方向的任务,没有明确的引导机制,直接丢进「目标模式」经常因为意图模糊跑偏。

新功能怎么解决

Pi v15.13.0 针对这几个痛点,分别给出了非常具体的解决方案。

语音交互:从打字到说话

这是这次版本最直观的变化。Pi 现在支持完整的本地语音链路:

  • 本地文本转语音(TTS):基于 Kokoro-82M 模型 + ONNX 运行时(一种跨平台的深度学习推理引擎),在设备端完成神经网络语音合成,完全不需要联网。输出 24kHz 的 WAV 或 PCM16(一种原始音频格式)格式,因为合成工作在后台子进程持续运行,模型始终「热着」,响应速度极快。
  • 流式语音转文本(STT):实时录音并转录到编辑器中,通过能量检测来判断你是否说完了,不需要手动按键结束录音。
  • 按住空格键通话(Push-to-talk):替代了原来的 Alt+H 快捷键。系统检测到空格键快速重复触发时自动识别为按住通话,交互直觉无缝。

这三项加在一起意味着什么?你可以在写代码的过程中直接说出修改需求,Pi 实时转录、原地处理。重构思路不用先打字再解释,直接说出来就好。

自动学习(Auto-learn):教会一次,永久有效

这是目前标为实验性的功能(需在配置中启用 autolearn.enabled),但方向非常有趣。

当 Pi 完成一个任务(agent 停止运行)后,它会自动回顾这次对话中产生的「可复用经验」——比如你们项目的代码风格约定、特定 API 的使用模式、数据库查询的优化技巧——然后把这些经验封装成可管理的「技能」(managed skills)。下次再遇到类似任务,Pi 自动调用这些技能,不需要你重新解释上下文。

这解决了一个所有 AI 编程工具共同的盲区:每一次对话都是「失忆」的。Auto-learn 相当于给 Pi 装了一个长期记忆系统,而且这个记忆不是用户手动编写的,是工具自己从实践中提炼的。

引导式目标设置:不再从零开始猜

/guided-goal 命令是这次另一个值得关注的新增功能。进入目标模式之前,Pi 会通过最多六轮问答来澄清你的真实意图。这个设计看起来简单,实际价值很高——大多数 AI 编程工具的目标模式(goal mode)本质上就是一个自由输入框,用户给的指令越模糊,AI 产出的结果就越跑偏。

六轮引导可以把一个笼统的「优化查询性能」拆解成具体的目标、约束条件和验收标准。等引导完成后才真正启用目标模式执行,路径清晰很多。

连带改善

语音、自动学习、目标引导是这次版本最醒目的三大块。但在这些功能之下,有一批底层修复同样值得关注。

Bun 原生抓取请求超时禁用是这次最有诚意的修复之一。之前所有流式推理提供商(OpenAI、Claude、Gemini 等)都受 Bun 内置的 300 秒超时限制,这意味着你做一个多文件重构、读一个大型代码库的分析任务,刚好在第 5 分钟整断开连接。这个超时现在被禁用了,Bun 的抓取不再主动切断长流。

Gemini Antigravity 流修复是另一个大坑的填平。如果你在用 Gemini 模型,之前会遇到三种怪问题:空的文本块(流式输出突然吐不出内容却断开连接)、系统指令泄漏到对话中、以及 thoughtSignature 字段损坏。这次都修了。

Copilot 传输层修复:Pi 兼容 Copilot 代理的传输模式(transport)时,之前会带上 eager_input_streamingbeta 这两个请求头(header),Copilot 的代理服务器会直接拒绝这些头部导致请求失败。现在这两个头部被过滤掉了,Copilot 用户能用回原生 Pi 的传输层。

OpenRouter 目录刷新修复:刷新 OpenRouter 模型列表时,之前的代码会丢掉 pi-native 这个传输方式,导致后续请求发到 /chat/completions 返回 404。现在修好了。

渲染性能优化:工具调用结果的渲染做了记忆化(memoized),以前对大文件执行 grep/find/read 后,每次帧刷新都会重新计算布局,导致编辑器严重卡顿,现在不会了。并行调用的旋转加载动画也保持了同步,视觉上不再杂乱。

其他值得注意的更新

  • 大内容粘贴菜单:粘贴内容超过可配置阈值时,弹出菜单让你选择用代码块、XML 标签还是本地附件形式处理。解决了大段文本直接粘贴到编辑器中格式混乱的问题。
  • 三级积极性(Three-level eagerness)task.eagertodo.eager 现在支持 default / preferred / always 三级,精细控制 AI 的主动程度。
  • 快速模式作用域:新增 fastModeScope 配置项,可以设置为 both / openai / claude,只对特定提供商启用快速模式。
  • 嵌入向量变体mnemopi.embeddingVariant 新增 multilingual 选项,使用更强的多语言嵌入模型,对中文等非英语场景更友好。
  • LaTeX 数学公式修复:模型现在被明确指示在回复中使用纯文本而非 LaTeX 定界符,避免了公式渲染混乱。
  • Agent Hub 手势修复:双击触摸板左边缘不再打开空的 Hub 面板。

升级方式

如果你已经在用 Pi,升级命令非常简单:

curl -fsSL https://omp.sh/install | sh # macOS / Linux
brew install can1357/tap/omp # Homebrew

完整更新日志:GitHub Release

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

#oh-my-pi #AI编程 #语音交互 #本地模型 #自动学习 #开源工具

oh-my-pi v15.12.4 发布 —— 同时开三个 omp session 会 crash?SQLite 并发修复来了

· 阅读需 7 分钟

SQLite 并发崩溃、Gemini 静默停摆、模型切换卡住 UI 好几秒、session 恢复等十秒、Ollama 上传 WebP 图片返回 400、Mermaid 中文标签画歪、Mnemopi graph voice 永远返回空……

每个问题单独看都不致命,可合起来就是「这个 agent 怎么又不动了」的累积挫败感。

oh-my-pi v15.12.4 封面

痛点:oomp——开了多个 session,然后全崩了

cmux 用户有个习惯:开多个 pane,每个 pane 跑一个 omp --session。一个 debug,一个 review PR,一个写文档。这很自然——omp 的 session 管理强就强在能同时维持多条独立对话线。

但问题是:当你电脑意外关机、重启后 cmux 自动恢复所有 pane,这几个 omp --session同时启动。它们都去打开 SQLite 数据库,争着做 WAL recovery,然后——SQLITE_BUSY_RECOVERY 一路 crash 过去。每次启动都撞到同一个锁,谁也抢不到。

这个 bug 藏了多久?从同时支持多个 session 的那天就存在了。不是 omp 的问题,是 SQLite 本身的 WAL recovery 机制——第一次写操作发生时必须加锁,多个进程同时复苏就会互锁。

v15.12.4 的修法很务实:在所有 SQLite 打开路径之前先执行 PRAGMA busy_timeout = 5000,然后 retry SQLITE_BUSYSQLITE_BUSY_RECOVERYSQLITE_BUSY_SNAPSHOTSQLITE_BUSY_TIMEOUT 四种 busy 状态,带指数退避。修复覆盖了 auth credential store、AgentStorage、history、memories、github cache、auto-QA、catalog model cache、stats 全部 SQLite 路径(#2421)。

这是个挺隐蔽但用久了大概率会碰到的问题。如果你的经验里从来没遇到过,可能是还没经历过异常关机后恢复 session 的场景——遇到了就回不去了。

Gemini agent 悄悄不说话的问题

如果你用 Gemini 模型跑 omp,可能遇到过这种场景:agent 突然不回复了,没有错误提示,没有网络超时,session 安静得像什么都没发生。

原因是 Gemini 有时返回 finishReason: STOP 但附带一个空文本(只有空格或空字符串),也没有 tool call。omp 之前把这种情况当作"正常结束"——agent 收到空回复,无事可做,停止。这就是那个著名的"Gemini 静默"bug。

这个版本修了所有 Google 后端(public Generative Language、Vertex、Cloud Code Assist)的空响应处理。现在统一的 hasMeaningfulGoogleContent 检查把空文本视为"无效回复",自动重试到 MAX_EMPTY_STREAM_RETRIES 次后才报错。Cloud Code Assist 路径之前虽然有一个空流重试,但它用 hasContent 判断——空字符串也算"有内容"——所以从来没真正触发过重试。

类似的问题还有 Gemini/Antigravity 返回纯思考块(reasoning/thinking only)没有最后文本或 tool call 的 STOMP 响应:现在也自动重试了,不会静默停摆。

session 恢复等十秒、模型切换卡半分钟

这两个问题同根。无论是 pi -c 恢复 session 还是 Ctrl+P 切换模型,底层都会调用 modelRegistry.getApiKey(model)。可 getApiKey 做的事远不止"取 key"——它会同步执行命令式 key 程序(apiKey: "!cmd"execSync 带 10 秒超时),还会在 token 过期时去刷新 OAuth token。一个模型切换触发一次网络请求+子进程执行,偶尔卡几秒也就算了——但 restoreSessionModel 对每个候选模型都做一次,session 恢复就能卡十秒。

修法:switching 和 restore 改用同步的 hasConfiguredAuth 探测,不做任何有副作用的操作。真正的 key 解析延迟到第一次请求时才执行。改动很小,效果很明显——model switch 从 "fine for a few switches, then a multi-second stall" 变成瞬间切换。

Provider 流式全方位的补丁

v15.12.4 对 OpenAI-compatible 流式做了系统性加固:

  • SSE 直接解析:不再依赖 OpenAI SDK,改用内部的 postOpenAIStream JSON/SSE transport,自己解析 wire-level JSON frames 和 [DONE] 终结符
  • HTTP 408/429/5xx 自动重试:这些在 Provider 层面不是致命错误,之前却直接抛给了用户。现在带 Retry-After/quota-hint aware backoff
  • OpenRouter finish_reason: error 映射:OpenRouter 报告上游失败(如 Gemini MALFORMED_FUNCTION_CALL)时返回 finish_reason: "error"。omp 之前把它当作不可重试的错误,session 卡死。现在映射为 Provider returned error finish_reason,被 session retry classifier 识别为瞬态错误,自动重试
  • 客户端中断释放上游连接:用户打断 stream 时,之前只停了本地 reader,上游 request body 还在继续跑。现在 abortable stream 包装会真正 cancel 上游请求
  • MiniMax M3 推理去重:同一个 chunk 携带 <think>…</think> 和结构化 reasoning_content 时,structured reasoning 优先,累计的 MiniMax 推理快照 collapse 为 delta

这些修的不算"用户可见的功能",但如果你在用任何 OpenAI-compatible 后端(OpenRouter、Together、Fireworks 等等),session 卡死、流断掉、超频错误的场景会大幅减少。

Fireworks 新模型自动出现,不用等 catalog 更新

Fireworks 的用户之前有个隐藏痛点:他们在 model picker 里看不到 kimi-k2.7-codedeepseek-v4-flash 这些新模型——不是因为 omp 不支持,而是因为 omp 的 bundled catalog 还没更新。Fireworks 每周发新模型,而 bundled catalog 的更新滞后几天到一周。

修复:Fireworks 模型发现从 inference API(/v1/models,返回 account-specific 稀疏子集)切换到 control-plane List Models API。control-plane 枚举所有 serverless 模型,附带 capability metadata(supportsServerlesssupportsToolssupportsImageInputcontextLengthdisplayName)。新发布的 serverless 模型现在自动出现在 picker 里,不需要等 catalog 编辑。

此外 contextWindowmaxTokensnull 代替了之前硬编码的 222222/8888 哨兵值。模型列表里未知限制显示为 - 而不是伪造的 222K/8.9K

其他值得关注的修复

Mnemopi graph voice 终于能用了sleep/sleepAllSessions 背后的 consolidateToEpisodic 之前"运行了但什么都没写"——gistsgraph_edges 表永远是 0 行,Polyphonic Recall 的 graph voice 永远返回空。现在 consolidation 会实际 ingest episodic memory 进入 EpisodicGraph(#2435)。用了 3 个月 rerank voice 的人可以试试切回 graph 了。

Mermaid 中文标签不再画歪:ASCII 渲染时 CJK/emoji 标签的边框宽度用 UTF-16 code units 计算,但终端渲染东亚字符是 2 列宽。一个 patchedDependencies 入口把 beautiful-mermaid 的渲染器改成 grapheme-cluster aware wcwidth-style 测量(#2442)。这个 bug 影响所有在 agent 对话中用中文编写 Mermaid 流程图的人。

Mnemopi bank 不再被 .git 位置意外改变:之前 per-project/per-project-tagged bank 的 ID 从 git.repo.resolveSync(cwd)?.repoRoot || path.resolve(cwd) 哈希生成。如果你在 /home/x/projects/repo 下工作,然后在上层目录加了一个 .git,bank 的哈希值变了——所有之前的记忆都"消失"了(实际还在旧 bank 里,但找不到了)。现在直接哈希 path.resolve(cwd),启动时还会扫描兄弟 bank 找回那些"丢失"的记忆(#2412)。

Windows 粘贴修复:Ctrl+V 在 PowerShell 下走原生 clipboard reader;之前 reader 报 "no image" 后不会 fall back 到 PowerShell clipboard bridge。现在是 fallback 路径了(#2429)。

升级

# 已安装用户
omp update

# 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 #SQLite #AI-agent #bugfix

oh-my-pi v15.12 发布 —— 发给别人的协作链接,现在能分「能看」和「能改」了

· 阅读需 7 分钟

不是他故意捣乱,而是 collab 从设计上就没想过「只让看」这个场景。链接发出去,进来的就是 full access:能打字、能打断 agent、能触发命令。你想让老板看看进度,老板莫名其妙拿到了 root 权限。你想让实习生围观你 debug,他一个手滑打了一行字进去。

v15.12 这个版本把这件事拆清楚了——「能看」和「能改」现在是两个不同的权限级别。顺带还把分享链接从明文变成 AES-256-GCM 加密、HTML 导出从 mock 变成真正的 React 组件渲染。

oh-my-pi v15.12 封面

痛点:collab 只有一把「全通钥匙」

之前的 collab 链接只包含一个 room key(32 字节)。你发给任何人,他拿到的权限和你一样——能看、能打字、能 kill subagent。你不是不信任对方,是这个设计本身就没给「只读」选项。

实际情况是:collab 的目的是让你实时分享 session,不是让你开放 shell 权限给任何人。即使是一个团队内部,review 代码的同事和远程 pair 的同事需要的操作级别也不一样。

怎么解决的:读写分离 + 加密快照

v15.12.0 拆了两个关键概念:

写 token 和只读链接。 完整的 collab 链接现在有两个 segment:room key(32 字节,用于建立加密通道)和 write token(48 字节,用于授权写操作)。只传 room key 的链接进来就是只读——能看 transcript、能翻子 agent、能看到 streaming——但不能打字,不能打断 agent,不能发 prompt。如果你用 collab.view 命令生成的 spectator 链接,那就更绝了:它连写 token 都不带。

/share 命令从明文 gist 升级成加密快照。 之前 /share 会把 session 导出为 HTML 发到 gistpreview,全程明文。现在改了——session JSON 先 gzip 压缩,再用 AES-256-GCM 加密,密钥放在 URL fragment 里(# 后面,不经过 HTTP 请求),然后推送到 share server(默认 https://my.omp.sh/s)。浏览器的 viewer 页面拿到 blob 后本地解密,密钥从不离开客户端。配置了 share.redactSecrets(默认开)的话,敏感字段在快照前会被脱敏。

这个架构有个细节值得说:加密快照的 viewer 不需要登录,不需要安装,打开链接就能看。对于跨公司分享 debug 现场或者给客户演示的场景——对方不想装你的 CLI,你不想用明文传 session 数据——这个落地的扎实程度比之前直接扔 gist 高了不止一个等级。

连带改善:HTML 导出换了一副骨架

如果你用过 oh-my-pi 的 HTML session 导出,大概知道之前的版本是怎么回事——tool call 渲染用的是 string-built dummy renderer,看起来像「一个叫 bash 的工具执行了」的文本框,没有结构化展示。

v15.12.0 把所有 built-in tool 的渲染器用 React 重写了(在 src/tool-render/ 下维护,通过 <omp-tool-view> web component 嵌入)。bash 的渲染有命令、有输出、有退出码;edit diff 有增删行高亮;todo board 有完成状态;eval cell 有执行结果;browser screenshot 直接显示截图。而且这些渲染器跟 collab web client 用的是同一套——导出内容和在线观看内容完全一致,不出现「导出看起来不一样」的问题。

更实用的改进是子 agent transcript 的嵌入。之前导出只记主线 agent 的对话,子 agent 的活动完全丢失。现在 <session>/<AgentId>.jsonl 文件会随导出打包,agent id 变成了可点击的 drill-down 链接,点击展开对应子 agent 的完整 transcript——包括它自己的 tool card 和更深层的嵌套 agent。一次性 debug 现场,导出 HTML 发给同事,对方能完整看到 subagent 树干了什么。

Agent Hub 焦点模式:在主视图里监控子 agent

Agent Hub 之前的问题是:你想看某个子 agent 的状态,要么在表格里看缩略信息,要么切过去。没有「一边写当前 prompt,一边盯着特定子 agent 进展」的体验。

v15.12.0 的焦点模式解决了这个:在本地 agent 上按 Enter,主视图直接 retarget 到那个子 agent 的实时 transcript,流式渲染、tool card、状态行全有。Esc 返回主 session,←← 回到父 agent。聚焦期间模型 cycling 和 thinking 预设被屏蔽——防止你在看子 agent 的时候不小心切了父 session 的模型配置。

这个改动不大,但在多 agent 协作的场景里体验差异很明显。之前你只能「看表格→猜进度」,现在你能「进去看」。

Mnemopi 本地嵌入:终于不用默默下载 270MB

v15.12.0 另一个不那么 visible 但影响面很大的改动:Mnemopi 的 local embedding 从 eager dependency 变成了 on-demand runtime。

之前 npm installbun install -g 会一并拉 fastembedonnxruntime-node 的 ~270MB native 依赖。即使你从来不用本地嵌入,这 270MB 也得下。现在这两个包变成 optional peerDependencies,只有在 Mnemopi 第一次尝试用本地嵌入时才会 lazy-install 到 ~/.omp/cache/fastembed-runtime/ 下。

对于 bundler 用户(Bun 编译 CLI、pkg 二进制安装等),之前 fastembed 的 native module 根本加载不了——Bun bundle 把 loader 内联后 relative require 就断掉了,报 Cannot find module 'onnxruntime_binding.node'。现在 runtime-install 机制在缓存目录里 bun install 完整依赖,从那里 resolve native binding,bundled 和 compiled 安装都可以正常使用本地嵌入了(#2389)。

同一批补丁中的后续修复

v15.12.0 发布当天下午又陆续发了三个补丁:

v15.12.1(同一天 +36 分钟):引入了 pruneUseless 压缩配置,可以自动裁剪 tool result 中的无用输出(dropUseless 谓词)。但不是好消息——double-Esc 快捷键被改回了 message-history 模式(#2396),也就是你按两次 Esc 不再回到 /tree 视图,而是翻聊天记录。这个改动在用户层面争议很大。

v15.12.2(+1 小时):修了两个问题。collab 链接的 room secret 现在用 dot-join 避免 URL fragment 里出现 # 导致解析错误。Homebrew 用户遇到的 sandbox-exec 错误(#2398)也修了——做 macOS 沙箱兼容的时候漏了一个 code signing 路径。

v15.12.3(+45 分钟):最直接的修复——把 double-Esc 的默认行为改回 /tree 视图,同时修了 Esc 打开 selector 时终端的 scrollback 残留问题。v15.12.1 改出去大概 2 小时就意识到了问题,v15.12.3 追回来。

三个补丁前后用了不到 3 小时全部发完,修复链快速且目标明确。v15.12.0 的核心内容(collab 读写分离、加密快照、HTML 导出重构)不受影响。

升级

# 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

# 已安装用户
omp update

完整更新日志:GitHub Release v15.12.0

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

#oh-my-pi #collaboration #terminal-TUI

oh-my-pi v15.11.3 发布 —— 以前会话结束记忆就清零,现在真的能存住了

· 阅读需 6 分钟

如果答案是「什么都没记住」,不是你理解错了,是记忆合并就没跑过。

v15.11.3 的改动不是那种能截图秀出来的新功能。它是一轮针对「默默工作但不工作」的底层 bug 清扫:记忆合并从来不跑、LSP 格式化强塞 tabSize:3、multi-path 搜索塌缩成全局扫描。每个 bug 单独看都哑巴吃黄连,合在一起就是「总觉得哪里不对但又说不上来」的开发体验。

oh-my-pi v15.11.3 封面

以前:记忆合并只在 /memory enqueue 时才跑

oh-my-pi 的 memory 系统有一个完整的管线:新信息提取 → 转化为 gist/triples → 合并进入 session 记忆 → 最终落盘。这套逻辑没问题,问题在于触发时机。

MnemopiSessionState.dispose() ——也就是 session 关闭时的清理方法——从未执行过记忆合并流程。它只调用了关闭句柄,但没有 drain pending extractions,没有触发 sleepAllSessions。结果就是:episodic_memorygistsconsolidation_loggraph_edgestriples——整张记忆表——在任何非手工触发的场景下都是空的。

如果你从没用过 /memory enqueue 这个斜杠命令,那你的 oh-my-pi session,某种意义上,从来没有「记住」过任何东西。每次新对话都得重头再来,没有跨 session 积累。

这个 bug 的藏匿方式很聪明:它不报错。你不查 episodic_memory 表,永远不知道它没被写入。甚至 /memory enqueue 都能正常跑——甚至你跑了之后它确实能存上——但就是不在 session 关闭时自动触发。所以正常运行的使用者,认为"系统在工作"的那个路径,就断了。

修复很简单:dispose() 现在在关闭句柄之前调用 consolidate()——drain pending extractions、sleepAllSessions、处理所有共享 bank。[#2320]

顺便说一句,/memory clear 不受影响——mnemopiBackend.clear 传入 dispose({ consolidate: false }),清空时不花 tokens 做合并,因为马上要 wipe。而 /memory enqueue 从子 agent 调用时也不会被 aliasOf 短路——consolidate() 本身不检查 alias,alias 守卫在 dispose 生命周期层。所以正常的 session 关闭会合并,手动的 enqueue 也会合并,毁灭性的 clear 不会浪费 tokens。

以前:LSP 格式化强塞 tabSize:3

这是个特别有意思的 bug。它影响面不算大——只影响用了 lsp.formatOnWrite 的用户——但一旦遇上,非常隐蔽又令人抓狂。

v15.11.3 之前,lsp.formatOnWrite 对每一次 textDocument/formatting 请求发送的都是硬编码的 tabSize: 3, insertSpaces: true。不是你的编辑器配置——是代码里写死的常量。这意味着什么?你在一个 2 空格缩进的 YAML 文件上按保存,yaml-language-server 收到格式化请求,格式选项是「3 空格,用空格」——于是你的整个 YAML 文件被重新缩进了。indent_size: 2 变成 indent_size: 2——不是审美问题,是格式被静默改变了。更糟的是,两份相同的常量 DEFAULT_FORMAT_OPTIONS 散落在 lsp/index.tslsp/clients/lsp-linter-client.ts 里,改一个另一个不同步。

现在格式选项按 .editorconfig 解析——indent_sizeindent_styletab_width 逐个查询。如果项目没有 .editorconfig,fallback 到对 in-memory 内容的缩进嗅探(模型正要写入的那个文本的缩进模式)。如果还嗅不出来,最后回退到 2 空格默认。不再是 3。[#2329]

以前:多路径搜索塌缩成全局扫描

search 工具支持传入多条路径:paths: [".", "~/.gitconfig"]。逻辑上,这应该把 .~/.gitconfig 作为两条独立的作用域分别扫描。

但实际上,之前的行为是把它们折叠到共同祖先路径上做一次 glob 扫描。如果当前目录是 /Users/me/project,而 ~/.gitconfig/Users/me/.gitconfig,共同祖先是 /Users/me——于是扫描了整个 /Users/me 目录,直到 grep 的 30 秒超时触发。~/.gitconfig 这个文件条目本身,在 walker 已经剪枝过的目录(比如被 gitignored 的路径)下,永远匹配不到,因为被折叠到了目录 glob 的并集里。

现在多路径散射成每个路径独立扫描。普通文件条目直接作为自己的目标读取。重叠路径的匹配结果去重。30 秒超时不再是因为搜索 .gitconfig 而扫描了整个 home 目录。[#2329]

Working loader 不再留下假历史行

TUI 用户应该见过这个:你在等模型回复,状态行显示 "Working…",然后模型出结果了,但你往回翻,发现那行 "Working…" 被钉在了历史里——它本应是实时更新的占位符,却被当作了固定消息提交。

原因是 loader 组件在 useEffect 卸载时,会同步它的最终内容到 scrollback 上游。但如果主 transcript 也在同时提交内容,loader 的 commit 可能覆盖 transcript 的边界,导致 transcript 的可变行被当作「已完成」提交,留下假的历史行。[#2328]

修复方法很简洁:root compose 现在确保「顶层的 live seam」决定 commit 边界,而不是由第一个卸完的 loader 说了算。TUI 的交互状态容器只在它有 mounted 内容时才注册 seam。

Copilot 模型管理的几个更新

pi-catalog 这边的改动集中在 Copilot 模型变体的处理上。现在当 Copilot 有阶梯 token 定价时(base token + long-context 加价),系统会合成 -1m 后缀的模型变体。requestModelId 代表本地变体指向的上游真实模型 ID。Copilot discovery 现在请求 API 版本 2026-06-01。非 chat 模型的 /models 条目被过滤掉了,支持 vision 的模型自动启用 image input。mapModel 返回 null 时会跳过该条模型,而不是 fallback 到默认值。[#2328][#2329]

升级

# 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

# Pinned 版本(mise)
mise use -g github:can1357/oh-my-pi

完整更新日志:GitHub Release

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

#oh-my-pi #AI编程 #记忆持久化 #LSP #开源工具

oh-my-pi v15.11.4 发布 —— 34K body,100+ 变更,这版本每个子系统都被翻了一遍(含 v15.11.6 修复)

· 阅读需 10 分钟

v15.11.4 的 body 是 34,000 字符。

不是笔误。三万一四。大约是日常版本的 15 倍。单个 release note 就包含了 100+ 个变更条目。从设置面板交互模型、Agent Loop 状态机、认证层错误策略,到 LSP 格式化参数、Mnemopi 记忆系统的 snapcompact 实验特性、CLI 的鼠标和粘贴处理——每一个子系统都被拆开检修了一遍。更离谱的是,同一天还追了一个 v15.11.6,带了 Codex reset 命令改名和子 agent HUD 排序修复。

这不是一次常规提交。这是一次从 UI 到内核的完整翻新。

oh-my-pi v15.11.4 封面

/settings 变身全屏工作台

设置面板是 oh-my-pi 里 Ctrl+, 一按就蹦出来的东西。之前它是个浮动弹窗,布局紧凑,能完成工作但谈不上舒适——尤其是在终端里操作键盘导航时,上下翻页的体验和鼠标时代格格不入。

v15.11.4 把整个设置面板砸了重做。

新面板是全屏覆盖的——不是弹窗,是 overlay。顶部栏显示当前版本和搜索框,支持按标签页搜索(Files / Shell / Interaction),搜索时实时显示匹配计数,你可以按方向键在匹配项之间跳转,旁边的 counter 告诉你「找到了 8 个结果,你在第 3 个」。

更值得说的是鼠标支持。终端 TUI 做鼠标事件一向是个坑——CSI 序列、SGR 编码、Kitty 键盘协议,每个终端实现都不完全一样。但 v15.11.4 承诺 hover 高亮提示、点击跳转、滚轮翻页都跑通了。我试了 iTerm2 和 terminal.app,滚轮翻页确实顺滑,不需要额外配置。标签页的 section sidebar 在宽终端上自动出现在左侧,窄屏自动隐藏,算是个细节但很实用。

稳定高度也是一个不易察觉、一旦遇到过就很在意的修复。之前的浮动面板内容多了会用滚动撑大面板,少了又会缩回去,视觉上一直在「跳」。新面板用 fixed viewport 高度,内部滚动独立,不会再让眼睛追着面板高度跑了。

Agent Loop:状态化 SSE 与 pause_turn 落地

oh-my-pi 的 agent loop 是所有模型调用的调度核心。v15.11.4 之前,SSE(Server-Sent Events)的 turn 处理是相对无状态的——一个 streaming chunk 来,一个 chunk 去,逐段写入 transcript。如果你只用 OpenAI 系模型,问题不大。但如果你的主力模型是 Codex——它有 pause_turn 语义——事情就复杂了。

Codex 的 API 可以在一个 long-running streaming 任务中发出一条 pause 指令,表示「我暂停了,你处理一下中间结果,然后我再继续」。之前的 loop 遇到 pause 会直接退出全局 streaming 循环,让上层重新安排一个新的 loop 迭代。这逻辑上没错,但会丢失 SSE 的连续状态——比如已经传了一半的 tool_use_id、resolved tool 结果需要带回去。

新实现做了真正的状态化 turn 链:每个 turn 持有上一个 turn 的句柄,pause 只是一个「临时暂停」,循环进入重新采样(re-sampling)阶段,但上下文和工具结果链不被截断。连续 pause 有 8 次上限,超过后作为非临时终止处理,防止模型进入死循环。

这看上去是一个底层协议对接问题,但决定了一个模型能否在你用的工具中稳定工作。Codex 用户不需要理解这些细节——只需要知道之前偶尔遇到的「回复到一半停了,再发一次又从头开始」的情况应该消失了。

认证层:401 不再是终点

AI 编程工具的认证链正在变得越来越复杂。你可能同时在用 OpenAI API key、Codex OAuth token、GitHub Copilot token——每个都有自己的过期策略和刷新机制。如果某个 token 在请求中途过期,之前的处理方式是抛出一个通用错误,用户手动重试。

v15.11.4 在 provider 层引入了 withOAuthAccess 驱动模式和一个统一的 ProviderHttpError 类型系统。核心逻辑是:当任何 provider 返回 401 时,中央 auth-retry 策略自动触发——尝试 credential rotation(OAuth refresh 或 API key 换新),然后用新凭证重发原请求,整个过程对用户透明。新的 ApiKey resolver 模式让「发现 key 过期 → 刷新 → 重试」的闭环落在框架层,而不是每个 provider 各自写一套。

这项改动的影响在于:如果你用 oh-my-pi 对接多个 provider 的 API 做 agent 任务,中途某个 token 过期不会导致整个会话停在半路。对日常使用来说,你可能完全感知不到——因为成功的刷新不会产生任何交互。但失败的情况,ProviderHttpError 现在带有结构化的错误上下文(status、provider、retryAttempt),诊断起来比之前的一行 TypeError 可靠得多。

LSP 格式化:不再硬编码 tabSize:3

v15.11.3 修复了 lsp.formatOnWritetabSize: 3 硬写入 YAML 格式化请求的问题。v15.11.4 在这条线上更进一步——问题不在常量值,在于格式选项没有跟随用户的编辑器配置。

新行为:lsp.formatOnWrite 在触发格式化之前,先查询当前文件的 .editorconfig。如果存在 indent_sizeindent_style 配置,用配置值。没有 .editorconfig 的项目,fallback 到 in-memory 内容缩进嗅探——取即将写入的文本中实际使用的缩进模式。如果嗅探也失败,最后才回退到 2 空格默认。

这意味着在不同项目之间切换时——一个项目用 4 空格缩进 Python,另一个用 2 空格缩进 YAML——保存时的 LSP 格式化不再互相污染。我之前碰到过在一个 tabWidth: 4 的项目里写 YAML,保存后所有 4 空格变成了 2 空格,以为是模型写错了。其实是格式化参数被缓存成了上一个项目的配置。

Mnemopi 的实验性方向:Snapcompact + 人格选择器

记忆系统方面,v15.11.4 引入了两个实验性设置(默认关闭):snapcompact.systemPromptsnapcompact.toolResults

思路很直接:长上下文对话中,system prompt 和 tool results 会消耗大量 token。如果模型支持视觉(vision-capable),为什么不把这些内容渲染成 PNG 帧再传入?一帧图片替代几千 token 的文本——视觉模型的 token 损耗远低于文本 token。代价是分辨率损失和 OCR 回退的风险,所以默认关闭。但如果你用 GPT-4o 之类的视觉模型跑长 session,这个 trade-off 值得一试。

另外,记忆面板新增了一个 Personality 选择器(default / friendly / pragmatic / none)。不是提示词里的语气微调,是影响 agent 在代码审查、调试建议、代码生成时的行为倾向——friendly 会更多解释性输入,pragmatic 直奔改代码。none 则完全由模型自己发挥。实际体验上,pragmatic 在代码修复场景下效果最干脆——不需要模型在改之前先写一段分析,直接出 diff。

CLI 支持改进:Ctrl+V 回退、Kitty 键盘修正

几个 CLI 层面的打磨值得一提。Terminal 里 Ctrl+V 粘贴图片在很多现代终端模拟器中是默认行为。但如果你终端不支持图片粘贴(或者 SSH 到远端),之前的 oh-my-pi 会静默吞掉这个输入——没有错误提示,也没有回退到纯文本粘贴。现在 Ctrl+V 在图片粘贴失败时自动回退到文本粘贴模式,至少不会丢内容。

Kitty 键盘协议方面,全屏 overlay(设置面板就是全屏 overlay)在 Kitty 终端中之前有键盘焦点问题——某些按键事件被 overlay 层捕获后无法传递回底层组件。现在 Kitty 的 keyboard protocol 做了分层处理,全屏 overlay 下 Esc 也能正常退出。

SGR 鼠标报告在分屏模式下也有一个修复:之前左右分屏时,右侧分屏的鼠标滚轮事件会被误解析为左侧分屏的 SGR 序列。现在在多点鼠标报告事件上做了 split-aware 判据。

Snapcompact 导出重命名 & 模块清理

snapcompact 相关的导出被统一重命名——去掉了 snapcompact 前缀限定符。比如 snapcompactRenderrendersnapcompactResultresult。对已经有使用 snapcompact.* 导出的脚本和插件,这是一个 breaking change,但命名更整洁,也避免了命名空间的冗余前缀。

blockRangeAt 有一个边界修复:处理没有扩展名的 shell rc 文件(比如 .zshrc.bashrc)时,之前的 blockRangeAt 用扩展名做匹配,对这些 dotfile 返回了错误的范围。现在按文件 basename + 内容特征做 fallback 匹配。

顺带一提:v15.11.6 也发布了

就在当天晚些时候——v15.11.4 发布约两小时后——v15.11.6 上线了。3 个小型修复:

  1. Codex 的 reset 命令改名:旧的 /reset-usage 被废弃,新的命令是 /usage reset。同时新增了 /usage show 别名,用于查看当前 usage 数据。如果你之前习惯了 /reset-usage,需要改一下记忆,不过旧命令短期内不会删除,可以平稳过渡。
  2. 子 agent HUD 排序修复:之前在一个 agent 任务中启动多个子 agent 时,进度更新可能导致活跃行的顺序飘移——某行子 agent 的进度条会「跳到」其他行的位置。现在 HUD 的行序在进度更新时保持稳定。
  3. /settings 的 dirty-value 高亮在键盘焦点离开该行后仍然保持。之前当你修改了一个设置字段,按方向键跳到下一行时,旧行的高亮消失,你无法确认这一行已经被修改过。现在只要行级的键盘焦点存在,dirty 标记就不消失。

整体来说,v15.11.6 是 v15.11.4 质量密集型审核后的及时跟进。三个问题都不大,但都属于「遇到一次就印象深刻」的类型。

升级

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

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

# Homebrew
brew install can1357/tap/omp

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

# Pinned 版本(mise)
mise use -g github:can1357/oh-my-pi

完整更新日志:

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

#oh-my-pi #AI编程 #设置面板 #AgentLoop #OAuth #LSP

oh-my-pi v15.11.8 发布 —— 当你的 agent 可以被别人实时加入,会发生什么?

· 阅读需 7 分钟

v15.11.7 + v15.11.8 两个版本背靠背发布,核心词就一个:collab(协作)。但这只是冰山一角——模型目录整个重写了路由逻辑,冷启动快了 244ms,流式渲染减少 66% 的 CPU 开销。两个版本的更新量加起来相当于一次中型重构。

oh-my-pi v15.11.8 封面

最重磅更新:Collab 实时会话共享

v15.11.8 的核心成就——collab(协作模式)终于从实验代码变成产品功能。

怎么工作的? 你先在终端里跑 omp,然后 /collab start 生成一个链接。这个链接里嵌了 AES-256-GCM 的密钥在 URL fragment 里,relay 服务器(默认 relay.omp.sh)看到的只有加密后的 opaque 字节。你把链接发给同事,他在浏览器里打开 collab.omp.sh 或者直接在终端跑 omp join <link>,就能实时看到你的 TUI(终端用户界面)画面。

不只是看画面那么简单。 客人端渲染的是原生的 TUI——流式文字、tool card、footer 状态、ctrl+o 扩展、/dump 导出——全部都是实时的。客人还能在对话里发消息,agent 会看到带 author badge 的 prompt。有人问「你这步在干嘛」,客人直接打字就行,不用你手动转述。

子智能体也是透传的。客人可以看到 subagent 的实时状态、progress、甚至查看每个 subagent 的完整 transcript。host 的 Agent Hub 表格、hub chat/kill/revive——这些操作通过 collab link 路由到 host 端执行,会话关联的命令只能由 host 触发。

安全设计上也很讲究:relay 只转发加密后的字节,看不到明文内容。非本地的 ws:// relay URL 和无效的 room key 都会被拒绝(v15.11.8 的安全加固)。

另外 collab.relayUrlcollab.displayName 两个配置项可以自定义 relay 服务器和显示名。collab 状态行会显示参与者数量(host 端)或 guest 角标。

模型目录:变体折叠,告别凌乱的 thinking 后缀

这是 v15.11.7 最核心的底层改动——不是一次简单"重构",而是把整个模型路由逻辑翻了底朝天。

问题是什么? 很多 Provider 把同一个模型拆成多个 id:gemini-3.5-flashgemini-3.5-flash-lowclaude-sonnetclaude-sonnet-thinking。以前这些 entry 在目录里是独立条目,用户选模型时看到一堆几乎一样的名字,不知道哪个该选。

v15.11.7 的解决方式variant-collapse 机制自动检测这类变体对,把 XX-thinking(或 X-low/X-high)折叠成一个逻辑条目,在 thinking.effortRouting 里记录每个 effort 级别对应的上游 wire id。请求时通过 resolveWireModelId(model, effort) 动态解析出正确的 outbound id。

这个机制不只是对已知的 Provider 生效——它有一套自动推导规则(deriveThinkingPairFamilies):任何 Provider 目录里存在的 bare 和 thinking twin 都会自动折叠,门控条件是同一 API 通道且兼容的定价。还加入了防止 mandatory reasoning 被绕过的兜底:thinking.requiresEffort 为 Gemini 3.x、OpenAI o-series、MiniMax M2 等模型强制最低 effort 级别。

这意味什么? 你选 gemini-3.5-flash,告诉 omp 你想不用 thinking,它会自动路由到 -low 变体。选 thinking high,路由到 -high。你不用关心背后的 id 叫什么——omp 替你管了。

模型显示名也做了清理:去掉了 gateway 前缀(OpenAI:Google:)、促销标签((latest)(20% off))、Provider 归属((Antigravity))、价格档次(($$$$))。只保留变体区分标签((Thinking)(free)(Fast))和日期/地域标签。

性能:冷启动和流式渲染被翻了个遍

v15.11.7 和 v15.11.8 各自的性能改进叠加起来,效果相当显著。

冷启动被优化了两处:模型目录的 buildCanonicalModelIndex 从构造函数移到了首次读取时懒加载,节省约 210ms;createAgentSession 不再阻塞在 MCP server 发现上——它立即返回,MCP 工具在后台连接成功后通过已有的 live-refresh 通道补上来。合计约 244ms 从冷启动关键路径里扣掉了。在你输完第一条 prompt 之前,后台连接已经跑完了。

流式渲染也动了大手术。Markdown 渲染在流式输出时不再每次 tick 全量重解析——marked 没有可恢复的 lexer,但 v15.11.8 的 team 找到了一个 trick:在空白行边界处分段,冻结前缀 block token,只 re-lex 新增长的 tail。对于非追加编辑和 fence 平衡的情况有 full-lex fallback。一个 6,000 字符的 reveal 从 575ms 降到 89ms

Think 块 + Answer 流的渲染优化:在 think 块完成后、answer 还在流的时候,已经完成的 think 块不再每帧重新高亮,减少约 66% 的渲染工作量

Assistant 消息流也改了:组件复用了 Markdown subtree,只 re-render 增长的块而不是每次 tear down 后重建。grapheme count 也做了 memoization。

🔧 修复与优化

MCP(Model Context Protocol)与连接质量

  • 修复了 Windows 上从 npm .cmd shim 启动 CodeGraph MCP 的问题:直接解析 shim 的 Node 入口点启动,而不是走 transient 的 cmd.exe wrapper
  • MCP server 连接失败的"still connecting"占位符变成确定性错误,不再显示"unknown tool"。session 在后台连接未完成时被销毁也不会遗留下工具

TUI 体验

  • 修复了 Ctrl+T(切换 think block)清除待发送消息的问题——在 message_start 到来之前,user message 通过 optimisticUserMessageSignature 机制乐观渲染
  • 修复了 SSH 远程使用时报图片路径不可达的问题:不再是静默插入本地路径文本,而是提示用户使用剪贴板图片粘贴快捷键
  • 修复了 snapcompact archive 在 OpenRouter 上部分不可读的问题——OpenRouter 硬性限制每请求 8 张图片,超过部分静默丢弃;每个 Provider 现在有帧预算控制

模型与基准

  • omp bench 基准测试新增 --runs--max-tokens--prompt--json 参数,支持失败报告和退出码
  • 修复了 omp bench 在 OpenRouter 上因 response caching 导致 TPS 为零的假阳性——现在发送 X-OpenRouter-Cache: false
  • 修复了 omp bench 在 openai-codex 模型上 400 的问题——请求带上了最小默认 system prompt
  • 新增 snapcompact.shape 设置支持 auto 模式(每个 Provider 的 eval 胜者)和所有 research-eval 变体

超时与兼容性

  • 修复了 Kimi K2.6 OpenAI-compatible 的 stream watchdog:用 300s 下限覆盖 Fire Pass router id,防止超长 reasoning 开始阶段超时
  • 修复了 Hindsight retains 发送 UTC Z 字符串而非本地时间戳的问题

升级

目前 omp 的最新版是 v15.11.8,升级方式:

# macOS/Linux
curl -fsSL https://omp.sh/install | sh

# Homebrew
brew update && brew upgrade can1357/tap/omp

# Bun(推荐,包管理器和 runtime 合一)
bun update -g @oh-my-pi/pi-coding-agent

# mise(版本固定)
mise use -g github:can1357/oh-my-pi

完整更新日志:GitHub Release v15.11.8

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

#ohmy-pi #Collab #模型路由 #性能优化 #Agent共享