Appearance
OSC 7501 — Program Status
| Sequence | OSC 7501 ; state=<state> [ : <key>=<value> ]... ST |
| Spec | Program Status Protocol (rev 0.4) |
| Otty support | ✓ |
Description
Lets a program tell the terminal what it is doing: resting (idle), running (working, optionally with a percentage), stopped on you (blocked — a permission prompt, a question, a password), finished with a result you haven't seen (done), or failed (error). A short message says it in the program's own words.
Where OSC 9;4 only carries a progress bar, OSC 7501 carries why the program needs you, and one program can report several things at once — a deploy can have one region pushing at 40 % while another waits for approval.
Where it shows up
There is nothing to turn on: as soon as a program in a pane sends a report, Otty shows its most urgent record (see Which record is shown) in four places:
| Where | What you see |
|---|---|
| Tab badge | working → running spinner · blocked → awaiting-input badge · error → error badge · done → checkmark on the tab you're on, unread dot on any other tab · idle / no records → the badge clears. See Progress State → Tab badges. |
| Dock icon | working spins the icon when Animate on Progress is on (off by default); error tints it red when Red on Error is on (on by default). Both are in Settings → Appearance → Dock Icon. See Progress State → Dock icon. |
| Inbox and tab preview | The record's message becomes the tab's line in Inbox (the bell beside + in the titlebar, or View ▸ Inbox) and in the card you get by hovering a tab — ahead of an agent's step or the running command. A blocked record lists the tab with the ones that need you; a working one with the running ones. Once a coding agent runs in the pane, a settled (done / error / idle) message gives way to the agent's own line. |
| Notifications | A new blocked, done or error report posts a banner — see Notifications. |
The badge rules are the same as for OSC 9;4 and coding-agent hooks, and a badge another source put up (an agent's hooks, shell integration, OSC 9;4, otty tab badge) is never replaced by a program-status one.
Notifications
A banner fires when a report brings a new blocked, done or error record — unless you're already looking at that tab (it's the active tab of the front window and Otty is the active app). At most one banner per pane every 3 seconds; extras are dropped, not queued.
The banner's title is the record's title, else its app, else the pane's title; its body is the msg (followed by the pane's name), or a stock line such as Needs permission, Has a question, Failed or Finished when there is none.
Because the text is chosen by the program, a banner needs Allow App Notifications on (Settings → Shell → Notification, on by default) and the toggle for its state:
| State | Toggle | Default |
|---|---|---|
blocked | Notify When Awaiting Input (Settings → Agents) | on |
error | Notify on Error Exit (Settings → Shell → Notification) | on |
done | Notify on Command Finish (Settings → Shell → Notification) | off |
The blocked badge likewise follows Badge When Awaiting Input (Settings → Agents), and the unread dot for done follows When Command Finishes (Settings → View → Tab Icon & Badge); both are on by default. See Privilege and Notifications for sounds and the Dock bounce.
Wire format
OSC 7501 ; <key>=<value> [ : <key>=<value> ]... STSTisESC \orBEL. Pairs are separated by:; keys are lowercase ASCII; values use onlyA–Z a–z 0–9 _ . , + / = -.- Unknown keys are ignored, a malformed pair is skipped, and a repeated key takes its last value.
- The whole sequence is at most 4096 bytes.
- A report that breaks a rule below — a missing or unknown
state, a badid, a field over its limit, invalid base64, non-UTF-8 text or a control character — is discarded whole.
Fields
| Key | Value | Meaning |
|---|---|---|
state | idle · working · done · blocked · error · clear | Required. The record's state, or clear to remove records. |
id | seg/seg/… | Which record. Absent = the root record. Segments are A–Z a–z 0–9 _ . + -, 1–32 characters each, at most 8 deep and 128 characters in all. |
kind | permission · question · auth | With blocked only: what the program waits for. Ignored with any other state. |
progress | 0–100 | With working / blocked only. Absent = indeterminate. |
app | plain text, ≤ 32 | The program's name. A record without one takes its nearest parent's. |
title | base64 UTF-8, ≤ 192 bytes | A short label for the record (e.g. a region name). |
msg | base64 UTF-8, ≤ 2048 bytes | What the program is doing, in its own words. |
title and msg are shown as plain text, never markup. Bidi overrides and invisible formatting characters are stripped, so a message can't disguise what it says.
States
state | Meaning | Lasts until |
|---|---|---|
idle | At rest, nothing in flight | Replaced or cleared |
working | Running; progress if known | The program ends (see Lifetime) |
blocked | Waiting on the user; kind says for what | The program ends |
done | Finished, result not yet seen | Replaced or cleared |
error | Failed | Replaced or cleared |
clear | Not a state: removes the record named by id and every record beneath it; with no id, removes all of them | — |
A report replaces its record whole: a field you don't repeat is gone (send progress again on every working update).
Which record is shown
Each pane keeps up to 256 records (the least recently updated one goes first). The badge, Dock, Inbox and preview show one of them: the most demanding state — blocked > error > working > done > idle — and among equals, the most recent report.
Lifetime
working and blocked describe a live program, so Otty drops them when the program is gone:
- A new shell prompt — with shell integration, Otty's own prompt mark; without it, any
OSC 133;A. - The command ends in a nested shell — e.g. inside
ssh host, records reported while a remote command ran go when that command finishes. - The pane's process exits.
done, error and idle stay until the program replaces or clears them, so a finished result is still there when you come back. A full reset (RIS, ESC c) removes every record; a soft reset or a switch to or from the alternate screen keeps them.
Feature detection
OSC 7501 ; ? STOtty answers with every state and kind it supports:
OSC 7501 ; ? : states=idle,working,done,blocked,error : kinds=permission,question,auth STSend the query followed by a Primary Device Attributes request (CSI c). Otty replies to the query first; if the DA reply comes back without a 7501 reply ahead of it, the terminal doesn't support the protocol.
Example
bash
b64() { printf '%s' "$1" | base64; }
# Working, indeterminate
printf '\e]7501;state=working:app=brew:msg=%s\e\\' "$(b64 'Installing updates')"
# Waiting for a password
printf '\e]7501;state=blocked:kind=auth:app=brew:msg=%s\e\\' "$(b64 'Password required to install updates')"
# Finished — stays until replaced or cleared
printf '\e]7501;state=done:app=brew:msg=%s\e\\' "$(b64 '12 packages upgraded')"
# Remove every record
printf '\e]7501;state=clear\e\\'Several records at once — a deploy with one record per region under a root that names the app:
bash
printf '\e]7501;state=working:app=deploy\e\\'
printf '\e]7501;state=working:id=us-east:title=%s:progress=40:msg=%s\e\\' "$(b64 'US East')" "$(b64 'Pushing image')"
printf '\e]7501;state=blocked:id=eu-west:kind=permission:msg=%s\e\\' "$(b64 'Approve production rollout')"
# The tab shows eu-west (blocked outranks working), with app "deploy" inherited from the root.
printf '\e]7501;state=clear:id=eu-west\e\\' # drops eu-west and anything under itWrite to the terminal, not a pipe
Emit to /dev/tty when your stdout may be captured (piped, or run under another tool), so the sequence still reaches the terminal.
Related
- Progress State — tab badges, the Dock icon and the toggles behind them.
- Privilege and Notifications — banners, sounds and Allow App Notifications.
- OSC 9;4 — the older progress-only protocol; both can drive the same badge.
- OSC 133 — the prompt marks that end a program's
working/blockedrecords. - Program Status Protocol spec — the full specification (rev 0.4).
- Program Status (OSC 7501) — Mitchell Hashimoto's write-up on the protocol.