Program Status Protocol
OSC 7501 lets a program report its current state to a terminal: working, waiting for input, finished, or failed. The terminal decides how to present that state.
This independent reference describes Mitchell Hashimoto's Program Status Protocol, revision 0.4 (2026-10-10). The linked specification is authoritative. Checked 2026-11-13.
# Sequence format
OSC 7501 ; pairs ST
OSC 7501 ; ? ST
pairs := pair (":" pair)*
pair := key "=" value
key := [a-z]+
value := [A-Za-z0-9_.,+/=-]*
OSC is ESC ] (1B 5D in hexadecimal). ST is either ESC \ (1B 5C) or BEL (07). The first form is a report, and the second is a feature query. Spaces shown around the syntax are explanatory, not part of the sequence. Whitespace around keys and values is stripped.
Pairs are separated by colons. The value alphabet excludes : and ;, so values do not need escaping. A pair without =, with an empty key, or with a byte outside the value character set is skipped and the rest of the report is processed. Unknown keys must be ignored. For repeated keys, the last value wins. msg and title contain standard base64 of UTF-8 text, with optional padding. Their decoded text must not contain control characters in U+0000–U+001F, U+007F, or U+0080–U+009F, including newline and tab. Both fields are single-line text. A report with invalid base64, a decoded control character, or a limit violation is discarded in full. Nothing from a discarded report is applied.
# Records
A terminal is one window, tab, split, or pane attached to one pseudo-terminal. Each terminal maintains records by id. An omitted id addresses the root record. An ID has this grammar:
id := segment ("/" segment)*
segment := [A-Za-z0-9_.+-]{1,32}
A report replaces the addressed record entirely. Include app, title, and other fields again when they must remain present. An invalid ID causes the report to be ignored, not applied to the root.
Slash-separated IDs form a hierarchy. For example, build/test is a child of build, which is a child of the root. Parents need not exist. Terminals must use the hierarchy for clearing and app inheritance and may use it for grouping. The hierarchy has no other meaning. Root and child records can coexist.
Records belong to the terminal rather than either screen buffer. Switching between primary and alternate screens leaves them intact. A full reset (RIS) removes all records. A soft reset (DECSTR) does not.
# States
state is required. A missing or unrecognized state causes the report to be ignored.
idle: At rest, waiting for the next user instruction: Until replaced or cleared.working: Running, optionally with progress: Until replaced, cleared, process exit, or the next shell prompt.blocked: Waiting for user action, optionally with progress.kindidentifies the action andmsgexplains why: Same asworking.done: Work finished and its result is ready for the user, who has not seen it yet: Until replaced or cleared. Survives process exit and the next prompt.error: Work failed and stopped: Same asdone.clear: Removes the addressed record and its descendants. With noid, removes all records: Not a stored state.
On interruption or cancellation, a program reports idle. A program exiting after completion should report done or error immediately before exit.
No heartbeat is required. Terminals must not require repeated reports to keep records alive. When the attached process exits or a new shell prompt begins (OSC 133 A), terminals must remove working and blocked records and may remove idle records. done and error survive both events. The terminal chooses when to stop displaying them.
# Keys
state: Required. One ofidle,working,done,blocked,error, orclear.id: Record path described above. Omission selects the root.kind:permission,question, orauth, forblockedonly. Other values and uses are treated as absent. These indicate approval, a typed answer, and credentials respectively.progress: Integer from0to100, forworkingorblockedonly. Missing or invalid means indeterminate: the program is busy without a percentage. Ignored for other states.app: Stable program identifier for grouping or filtering, matching[A-Za-z0-9_.+-]{1,32}. It is not a human-readable label. Invalid values are treated as absent. A record withoutappinherits it from the nearest ancestor that has one.title: Base64 UTF-8 short human-readable label, especially useful for multiple records. The root label is normally the window title.msg: Base64 UTF-8 single-line human-readable description. Terminals may shorten it and must not interpret its meaning.
# Feature detection
program → OSC 7501 ; ? ST
terminal → OSC 7501 ; ?:states=idle,working,done,blocked,error:kinds=permission,question,auth ST
Detection is optional. Terminals must ignore unknown OSC sequences, so programs may send reports without a query. A terminal supporting the protocol must reply with ? and may include comma-separated states and kinds lists. clear is excluded from states. If states is omitted, its default is idle,working,done,blocked,error. If kinds is omitted, its default is permission,question,auth. These defaults are fixed and do not change in later revisions. Programs must ignore reply pairs they do not understand and should check the lists before using later additions, falling back to a supported value.
A program that receives no reply within a timeout of its choosing treats the protocol as unsupported. To avoid waiting, it can send primary device attributes (CSI c) immediately after the query. If the device-attributes response arrives first, the protocol is unsupported.
# Limits and security
Terminals may impose lower limits. They must check every pair before changing stored records and discard the entire report if a limit is exceeded.
- Sequence, including
OSCandST: 4096 bytes. The largest legal report is under 3300 bytes - Key: 16 bytes
msg: 2732 encoded bytes and 2048 decoded bytestitle: 256 encoded bytes and 192 decoded bytesapp: 32 bytesid: 128 bytes total, 32 per segment, 8 levels- Records per terminal: 256. Terminals must support at least 64
Check encoded msg size before decoding. When a new record would exceed the terminal's record cap, remove the least recently updated record to make room.
Reports are untrusted. Terminals must reject decoded control characters and must not render msg or title as markup. When displaying text outside the terminal grid, they should neutralize direction overrides and other invisible formatting characters. Displays should identify the originating terminal. Effects outside the terminal, such as sounds or notifications, should be rate-limited.
Feature detection is the only response. It contains no report data. There is no record-reading operation.
# Related sequences
OSC 9;4 reports busy, a percentage, or error, without a message or identity. A program may send both OSC 9;4 and OSC 7501. Terminals may map OSC 9;4 to the root record. Because a mapped report replaces the root record, terminals should stop mapping OSC 9;4 after receiving OSC 7501 until the next full reset. OSC 9, 99, and 777 are notifications rather than persistent state. A terminal may produce a notification when a record changes. OSC 133 marks shell boundaries, including the prompt event used for record lifetime.
# Example
Spaces in these examples are explanatory. This report uses ESC \ as the terminator and sets a root record with the message Building:
ESC ] 7501 ; state=working:app=build:msg=QnVpbGRpbmc= ESC \
To clear all records on the terminal, send ESC ] 7501 ; state=clear ESC \.
# Support directory
This directory is selective. Support and releases change, so check the linked project for current availability. Entries checked 2026-11-13.
# Reporters
- Ruby tools: released reporters for Rake, RSpec, and Thor. A RuboCop reporter is underway.
- Crush: OSC 7501 reporting merged into the project. Check its releases for availability.
- Pi: version 1.1.0 reports working, blocked, done, and failed activity.
PI_PROGRAM_STATUS=1forces reporting on andPI_PROGRAM_STATUS=0disables it. See the implementation. - Codex CLI: version 0.163.0 includes OSC 7501 reporting.
- Hugo: version 0.168.0 reports
working,done,idle, anderror.
# Receivers
- Rex: displays program status in its session picker and tab headers.
- kitty: version 0.50.0 receives program status. Its 263-byte ID limit differs from revision 0.4 of the protocol.
# Multiplexers
- Zellij: receives and probes program status, displays it for panes and tabs, and offers an inbox. Host aggregation is optional. Check releases for availability.
- tmux: basic root-record support landed upstream. Named child records and forwarding to an outer terminal are not supported by this change. Check releases for availability.
# Developer libraries
- buildkite/osc7501: Go library for emitting, parsing, scanning, and stripping reports.
- libghostty-vt: OSC 7501 support merged in the terminal emulation library. This does not establish support in the Ghostty application.