跳到主要内容
ansicode

APC —— 应用程序命令(`ESC _ … ESC \\` / `\x9F … \x9C`)

ECMA-48 为应用对应用消息保留的字符串引导符。现代用途:Kitty 图形协议、VS Code shell 集成、Windows Terminal API 补丁。与 DCS / OSC / PM / SOS 同辈。

字节形式

涵盖所有常见的字符串字面量写法,方便正反查找。

\\x1b[\x1b_<body>\x1b\\ (7-bit) \x9f<body>\x9c (8-bit C1)
\\033[\033_<body>\033\\ (7-bit) \237<body>\234 (8-bit C1)
\\e[\e_<body>\e\\
ESC [ESC _ <body> ESC \\ or 0x9F <body> 0x9C
hex1b 5f <body> 1b 5c / 9f <body> 9c

说明

APC(*应用程序命令*)是 ECMA-48 为应用对应用消息保留的字符串引导符 —— 终端应当透明转发给同 TTY 上监听的对端应用、不自行解释主体的字节。7 位形 \x1b_<body>\x1b\\(ESC _ + 不透明字节 + ST);8 位 C1 形为单字节 \x9F 引导 + 主体 + 单字节 \x9C 终止。与 DCS\x1bP,设备控制)、OSC\x1b],OS 命令)、PM\x1b^,隐私消息)、SOS\x1bX,串起始)同辈 —— 五个被同样 ST 框定的「串形」序列家族。

2026 年为何还讲 APC。 ECMA-48 原本设计 APC 用于在远程主机的应用与终端上的同位应用之间打隧道(早期瘦客户机 / 大型机前端)。该用例基本死了,但字节形状被借用为不愿动用 OSC 用户面语义的厂商专用协议的「安全脱逃口」:

- Kitty 图形协议是头号现代用户 —— Kitty + Konsole + WezTerm + Ghostty + Wayst 通过 \x1b_G<key1>=<val1>,<key2>=<val2>,...;<base64-像素>\x1b\\ 接收图像数据。_ 后的首字节 G 是 Kitty 家族标签;后续采纳 Kitty 图形协议的模拟器都保留这一字节。(slug 名 dcs-kitty-graphics 早于此澄清而定 —— 真实字节流是 APC,不是 DCS。) - VS Code shell 集成借 APC 带 633; 前缀作为私有通道,承载 VS Code 不愿用 OSC 133 公开广告的 shell 集成标记。 - Windows Terminal 在预览版里试验 \x1b_<JSON>\x1b\\ 作为终端 API JSON 消息载体(未稳定)。 - ConPTY 直通 —— Windows ConPTY 因安全考量过滤多数 OSC,历史上对 APC 直通更宽松,故部分 Windows 侧工具改走 APC 以原样抵达主机终端。

解析规则。 视主体为不透明字节,直至在包边界见到 ST。具体:ECMA-48 规定主体内任何「ESC \\ 之外的转义序列」终端须忽略却需透传;严格解析器把字节读入缓冲,扫描 \x1b 紧接 \\(唯一合法 7 位 ST)或单字节 \x9C天真的「按 ESC 分割」解析器对任何合法包含 ESC 字节的主体(Kitty 图形协议尤甚 —— 其内嵌二进制统计上一定撞 0x1B)会错框。

安全态度。 因 APC 本就要原样透传,*确实*实现 APC 分派的终端必须白名单已识别协议(Kitty 图形 G 标签、VS Code 633; 等)并静默丢弃未知 APC 主体 —— 把陌生人的 APC 主体送给用户会让一个应用冒充另一个。*不*实现任何 APC 协议的终端(Alacritty、gnome-terminal、macOS Terminal、Linux console)静默吞掉即可(事实如此);视 APC 为「要么抵达知道标签的对端,要么完全消失」—— 永远不要把它当作保证可见的通道。

与 DCS / OSC / PM / SOS 的区别。 - DCS\x1bP)—— 设备控制(数据面向*终端*本身:DECRQSS、DECDLD、DECUDK、Sixel、terminfo cap 查询)。 - OSC\x1b])—— OS 命令(数据面向「终端即 OS 垫层」:窗口标题、调色板、剪贴板、超链接、提示标记 —— 用户面语义)。 - APC\x1b_)—— 应用命令(数据面向*对端应用*,不面向终端 —— 终端只是搬运工)。 - PM\x1b^)—— 隐私消息(搬运语义类似 APC,实际采用更少;部分遗留瘦客户机硬件用过)。 - SOS\x1bX)—— 串起始(最通用引导符,无语义;2026 年几乎无人用)。

常见解析 Bug —— 实现了 OSC + DCS 却忘了 APC 的终端:见到 \x1b_G...,识别不出引导符,于是_G... 印到可见屏幕。Kitty 图形渲染坏了之后看见散落的 _G 串就是这个症。

覆盖度 —— APC 分派(即把主体路由到注册处理器)小众Kitty + WezTerm + Konsole + Ghostty = 部分到完整(各把 APC G 路由到图形处理器;其它 APC 主体丢弃)。xterm = 部分(识别 APC 框架,默认丢弃主体 —— 需 apcDispatch X 资源启用)。mlterm + iTerm2 + Windows Terminal = 部分(识别 APC 框架,厂商标签主体处理,其余丢弃)。Alacritty + gnome-terminal + macOS Terminal + Linux console + cmd / ConPTY = 无作用但正确(识别 APC 框架,主体静默吞 —— 无可见残留)。现代模拟器至少都把框架做对了 —— 把主体字面打印的 bug 仅在很老的构建里残存。

规范出处: ECMA-48 §8.3.2 (APC) / xterm-ctlseqs (APC) / Kitty graphics protocol

参数

Introducer (7-bit)ESC + 下划线(\x1b 0x5f)。C0 安全形;UTF-8 下亦可用。
Introducer (8-bit)单字节 0x9F。UTF-8 不安全(续字节)。除已知 Latin-1 通道外勿用。
TerminatorESC + 反斜杠(\x1b\\,7 位 ST)或单字节 0x9C(8 位 ST)。任一皆可;与引导符宽度匹配。
Body不透明字节 —— 终端不得解释。厂商协议以首字节家族标签区分(Kitty 'G'、VS Code '633;' 等)。

示例

bash
# Probe whether the terminal accepts a Kitty-graphics APC frame.\n# Empty image (1x1 transparent PNG, base64-tiny) — if rendered, terminal supports Kitty graphics.\nprintf '\\033_Gf=32,s=1,v=1,a=T;AAAAAA==\\033\\\\'\n# Emit a custom APC tag — most terminals will silently swallow.\nprintf '\\033_myapp;hello\\033\\\\'\necho 'still here'   # no visible artifact on any modern terminal
python
import sys\n# Send a Kitty-graphics-style APC frame.\ndef apc(body):\n    sys.stdout.write('\\x1b_' + body + '\\x1b\\\\')\n    sys.stdout.flush()\napc('Gf=32,s=1,v=1,a=T;AAAAAA==')   # Kitty 'G' tag + base64 payload\napc('myapp;state=ready')             # private tag — silently swallowed on non-vendor terminals
go
// Robust APC parser: read until ST at packet boundary, never split on raw ESC.\nfunc readAPC(r *bufio.Reader) ([]byte, error) {\n    var buf bytes.Buffer\n    for {\n        b, err := r.ReadByte()\n        if err != nil { return nil, err }\n        if b == 0x9c { return buf.Bytes(), nil }      // 8-bit ST\n        if b == 0x1b {\n            n, _ := r.ReadByte()\n            if n == '\\\\' { return buf.Bytes(), nil }    // 7-bit ST\n            buf.WriteByte(b); buf.WriteByte(n); continue\n        }\n        buf.WriteByte(b)\n    }\n}
javascript
// Emit APC + listen for response APC from a peer.\nfunction sendAPC(body) {\n  process.stdout.write('\\x1b_' + body + '\\x1b\\\\');\n}\n// Parse incoming APC (collect bytes until ST, never split on raw ESC).\nlet apcBuf = '';\nprocess.stdin.on('data', chunk => {\n  const m = /\\x1b_([^\\x1b]*(?:\\x1b[^\\\\][^\\x1b]*)*)\\x1b\\\\/.exec(apcBuf + chunk.toString('binary'));\n  if (m) console.error('APC body:', m[1]);\n});
c
/* Emit APC with custom vendor tag. */\n#include <stdio.h>\nvoid send_apc(const char* tag, const char* body) {\n    printf(\"\\x1b_%s;%s\\x1b\\\\\", tag, body);\n    fflush(stdout);\n}\n/* Usage: send_apc(\"myapp\", \"hello\");  // silently swallowed on non-vendor terms */

在哪里用到

实际会发出该序列的工具——把抽象字节锚定到你已经用过的命令上。

  • Kitty graphics protocol (`kitty +kitten icat`)Kitty 图像渲染器发 `\x1b_G<key>=<val>,...;<base64-像素>\x1b\\` 形式的 APC 帧 —— `_` 后的 `G` 标签是 Kitty 家族标识。Konsole、WezTerm、Ghostty、Wayst 原样接受同一协议。`kitten icat ~/image.png` 是面向用户的标准发射器
  • VS Code shell integration (`OSC 633;` channel)VS Code 终端发 APC `633;` 帧(如 `\x1b_633;A\x1b\\` 标记提示符开始、`\x1b_633;B\x1b\\` 标记命令开始)来界定提示符边界而不污染 OSC 133 —— 与基于 OSC 的公共 shell 集成规范有别。可由 `$VSCODE_INJECTION` shell 钩子发出的这些包装观察到
  • tmux passthrough mode (`set -g allow-passthrough on`)tmux ≥ 3.3 启用 `allow-passthrough on` 将内部 pane 的 APC 帧转发到外层终端 —— tmux 内 `kitten icat` 借此把 Kitty 图形递送到宿主终端。未启该选项时 tmux 在 pane 边界剥离 APC(默认拒绝姿态)
  • WezTerm `wezterm.action.SendString` raw APC handlersWezTerm 的 Lua 配置可通过 `wezterm.on('window-config-reloaded', ...)` 配合 `set_user_var` 注册自定义 APC 处理器 —— 终端侧脚本发 `\x1b_<自定义标签>;<载荷>\x1b\\`,WezTerm 把主体路由到注册的 Lua 回调。用于编辑器感知的状态栏集成与项目上下文叠层
  • mosh client-server diff protocolmosh 状态同步协议(SSP)用 APC 框架作为 mosh-client 与 mosh-server 间终端状态 diff 的线缆封装 —— SSP 帧 `\x1b_<base64-protobuf>\x1b\\` 透明穿越中间终端,利用 APC 的透传语义做隧道,不与 DCS / OSC 纠缠

常见问题

针对这条序列,开发者真正会去搜索的问题的简短回答。

APC 序列是什么 —— \x1b_...\x1b\\ —— 哪些真实工具使用它?
APC(Application Program Command,C1 控制 0x9F,7 比特引入为 \x1b_)是应用专属二进制 payload 的通用透明信封,以 ST \x1b\\ 终止。与 CSI / OSC / DCS 不同,payload 字节不由终端解释 —— 它们透传给应用在里面定义的任何协议。现代真实用户:(a) Kitty graphics protocol 把 base64 编码的 PNG / RGBA 块包在 \x1b_G<params>;<base64>\x1b\\ 里做内联图像渲染,(b) VS Code shell integration 用 OSC 633 通道(不是 APC,但架构类似 —— \x1b]633;...\x07)做 prompt-end / command-finished 标记,(c) WezTerm user vars 用 APC \x1b_<key>=<value>\x1b\\ 设 shell 状态供终端配置可见,(d) iTerm2 内联图像 用 OSC 1337(不是 APC)。APC 的优点是其全面透传语义 —— 行为良好的终端只是忽略未知 APC payload,不会乱码显示。
为什么我的 APC 序列在某些终端上被原样回显为文字?
两个原因。(1) 漏 ST 终止符 —— APC 在 \x1b_ 进入字符串解析模式,仅在 \x1b\\ 退出;漏发 ST,解析器继续吞字节包括后续程序输出,直到最终失同步。(2) 终端根本不识别 APC,落到字面显示 —— 适用于较旧 xterm 补丁号(< 332)、macOS Terminal(始终字面)、GNU screen ≤ 4.x(除非启用 bind ^_a apc 否则字面)、未 allow-passthrough on 的 tmux(按版本完全丢弃或字面回显 APC)。防御性模式:先用 XTVERSION 探测知道在和谁说话、在字面回显集合中的终端回退到普通输出,*始终*把 \x1b_ 与其配对的 \x1b\\ ST 一起发 —— 永远不要用 BEL(\x07)当替代终止符(BEL 终止 OSC,不是 APC)。

终端支持

xterm
部分
Linux console (fbcon)
不支持
macOS Terminal.app
不支持
iTerm2
部分
Windows Terminal
部分
cmd.exe / ConPTY
不支持
kitty
支持
alacritty
不支持
WezTerm
支持
Ghostty
支持
GNOME Terminal
不支持
Konsole
支持
tmux
支持
GNU screen
支持

相关序列

在家族食谱中

ESC 食谱 · 6. APC —— 现代应用对应用信封 `\x1b_ …… \x1b\\`