← 文章 / 编程开发
Hacker News 5小时前 · 2026-10-09 10:09:01 · 9 阅读

用于报告程序状态的终端转义序列规范 (OSC 7501)

我撰写了一份新的终端转义序列规范: OSC 7501,即程序状态协议。 它允许任意程序向终端报告当前状态:空闲、正在工作、等待用户输入、已完成或已失败,并说明原因。

例如,Terraform 可以借此表明它正因等待用户输入而阻塞,并附带一条 消息 "Apply 3 to add, 1 to change, 0 to destroy?"(经 base64 编码)。终端(或任何运行 Terraform 的其他工具)可以按合适的方式展示该信息:弹出通知、收件箱、状态图标等。

ESC ] 7501 ; state=blocked:kind=permission:app=terraform:msg=QXBwbHkgMyB0byBhZGQsIDEgdG8gY2hhbmdlLCAwIHRvIGRlc3Ryb3k/ ESC \

本文讨论了我认为该协议存在必要的原因,为什么现有方案不够好(尤其针对编码代理),以及该协议如何运作。

这是一份完全通用、原生终端的规范和协议。 它源于我在 Superlogical 和 Ghostty 上的工作,但 该规范 不含任何特定产品功能或术语。它被设计为一份地道、结构良好的规范,任何终端开发者都能熟悉理解。


问题所在

终端中常出现长时间运行的任务:构建、部署、软件包升级、数据处理,以及如今日益增多的编码代理。这些程序在自主运行、等待用户介入和结束之间反复切换。与此同时,用户通常去忙别的事,并希望在任务完成或需要用户时得到提醒。

数十年来,人们已用多种方式解决了该问题的部分方面。例如,某些终端会监控前台活动进程,并在其变化时发出通知;或等待输出保持“安静”一段时间(“安静”有多种定义)。该规范 还 列出了现有序列为何不够用的原因。

说到底,我觉得目前并不存在一个统一的、与交互方式无关的通用方案,能同时表达进度、阻塞、完成以及任务树这些状态。而且,把现有的转义序列拼凑起来,也无法稳健地实现这个目标。


特别聊聊「Agent 收件箱」

不关心 AI、LLM 这些?可以跳过本节。这个问题本身是通用的,不扯 AI 也同样成立。只不过在 AI 场景下它格外棘手,所以我想专门点出来,但如果你对这些不感兴趣,直接跳过就好。

如今,出于各种原因同时运行大量长时间运行的 agent 已经越来越常见:后台调研、issue 监控、修 bug、开发大型功能等等。每个 agent 干一阵子活,就会停下来请求权限、提问,或者报告任务完成。

由此催生了一类新工具,我姑且称之为 agentic inbox(agent 收件箱):用一个统一视图展示所有正在运行的 agent,谁在工作、谁已完成、谁在等你回复,一目了然。Herdr、cmux 和 Agent Deck 只是 成百上千同类工具中的几个例子。

由于没有专门的协议,这些工具只能靠两种方式解决 agent 状态问题:启发式猜测和非终端 API。

启发式

第一种方式是读取屏幕内容或窗口标题,与已知模式做匹配来猜测状态。

Herdr 就是个很好的例子——它做得不错,而且公开了实现文档。它的 detection manifests 是一组 TOML 规则,用来把 agent 分类为空闲、工作或阻塞。下面是针对 Claude Code 的 16 条规则中的第一条:

[[rules]]
id = "osc_title_working"
state = "working"
priority = 1100
region = "osc_title"
visible_working = true
# Braille covers <= 2.1.227; half-circles are the 2.1.228 busy spinner.
regex = ['^[\x{2800}-\x{28FF}\x{25D0}-\x{25D3}] ']

Claude Code 的窗口标题若以盲文旋转字符开头,或其版本达到 2.1.228 及以上且以半圆符号开头,即被判定为“工作中”。该文件的提交历史显示,仅针对 Claude Code,三个月内就改动了十次。

这并非对 Herdr 的批评。 其维护者已利用现有工具做到了极致。但这很好地说明了 统一协议 带来的优势。

非终端 API

第二种方法是让程序通过带外 API(如 Herdr 的 Socket API 或 cmux notify)向收件箱报告自身状态。在某些方面,这比启发式方法更好,因为真正知晓自身状态的是正在报告状态的那个程序。但每个程序都需要单独集成每个收件箱,且本地 Socket 在 SSH 或容器环境中需额外桥接才能工作。而 pty 天然支持所有这些场景。


程序状态协议

OSC 7501 是解决此问题的终端原生方案。程序通过始终存在的 pty 直接报告自身状态,使用一种通用安全的格式(行为良好的终端会忽略未知的 OSC)。

该序列的主体是由 : 分隔的 key=value 键值对列表。唯一必需的键是 state,取值为:

state含义
idle处于静止状态,等待用户下一条指令。
working正在运行。可包含 progress 百分比。
done已完成。结果已就绪,用户尚未查看。
blocked在用户执行某操作前无法继续。kind 指明原因类型(permission、question 或 auth),msg 说明具体细节。
error已失败并停止。

可选字段包括 app,即类似 cargo 或 claude-code 的稳定机器可读程序名称;以及 msg,即编码为 base64 的单行人类可读信息。

同时运行多个任务的程序可以通过使用层级 ID 来上报多条记录。部署工具在根节点上可能处于 working 状态,此时 us-east 正在以 40% 的进度推送镜像,而 eu-west 则处于 blocked 状态,等待生产环境部署的审批。这两种情况同时成立,由终端决定展示内容。clear 状态用于移除记录。

以下是一个完整的集成示例,用于让 shell 脚本包装 rsync 以参与该协议:

status() {
  printf '\e]7501;state=%s:msg=%s\e\\' "$1" "$(printf '%s' "$2" | base64 | tr -d '\n')"
}
status working "Syncing photos"
rsync -a ~/Photos backup:/photos && status done "Photos synced" || status error "rsync failed"

使用传统的 POSIX sh 编写脚本极其简单。 无需 SDK,无需套接字,无需环境变量,无需 JSON。 不偏向特定的 GUI 呈现方式,也不偏向任何特定的工作负载(如 AI)。 它构成了一个结构良好且通用的基础,任何人都可以在其上构建功能并参与其中。

完整规范涵盖了其余内容:记录生命周期、 特性检测、 terminfo、大小限制, 以及安全性。篇幅很短,全部由我手写完成。请务必阅读。


实现

我维护终端模拟器已有多年,基于这些经验写下了这份规范。它的设计原则是:应用开发者容易输出,终端模拟器也容易解析和消费。

我已经实现过两次这个协议:一次在 libghostty,另一次是在 Rex 中的对应实现。此外,我还通过插件或 fork 的方式,在 Terraform、Claude Code、Codex 和 Homebrew 中做了概念验证,每种实现都不超过十几行代码。

我和许多热门终端程序及模拟器的维护者都有过交流,他们帮忙审阅并完善了这份规范。如果你还有其他反馈,我很乐意听取。

如果你已经实现了这份规范,请通过邮件告诉我(页脚有邮件图标),我会把你加入支持该规范的工具列表。谢谢。

希望大家不必再靠读取程序屏幕或进程树来猜测它在干什么。程序自己最清楚自己的状态,给它一个渠道告诉我们吧!

October 6, 2026
原始来源: Hacker News

评论 (0)