Program status protocol (OSC 7501)

Programs running in Solo can report what they're doing with OSC 7501, the program status protocol: working, blocked on you, done, failed, or idle. Solo uses those reports to drive agent activity indicators and attention alerts, and to hold automated messages while a program waits for you.

This is useful when you build a CLI agent, a long-running script, or a TUI and want Solo to know its real state instead of guessing from output.

Send a report

A report is an OSC sequence with key=value pairs separated by colons:

ESC ] 7501 ; key=value:key=value BEL

You can end the sequence with BEL (\007) or ESC \. The whole sequence, from ESC ] to the terminator, can be at most 4,096 bytes.

For example, report that a build finished:

printf '\033]7501;state=done:msg=%s\007' "$(printf 'Build finished' | base64)"

Report that the program is working, then that it's waiting for permission:

printf '\033]7501;state=working:progress=40\007'
printf '\033]7501;state=blocked:kind=permission:msg=%s\007' "$(printf 'Allow deploy?' | base64)"

Keys

Key Value
state Required. idle, working, blocked, done, error, or clear.
kind Only with blocked: permission, question, or auth.
progress Only with working or blocked: a whole number from 0 to 100.
id Optional record ID, such as build or build/tests. Up to 8 /-separated segments of letters, digits, _, ., +, or -; at most 32 bytes per segment and 128 bytes in total.
app Optional program name, up to 32 bytes.
title Optional notification title, base64-encoded UTF-8, up to 192 bytes once decoded.
msg Optional notification message, base64-encoded UTF-8, up to 2,048 bytes once decoded.

Keys are lowercase. Base64 padding is optional. Solo rejects a report that is missing state, uses an unknown state, or breaks a length limit; it ignores keys it doesn't recognize.

Failure is reported as state=error.

Records and clearing

Each report replaces the record for its id. A report without id sets the program's root record, so a simple program never needs one. Programs that track several tasks can give each its own ID.

state=clear removes records instead of setting one:

  • With an id, it clears that record and everything under it. state=clear:id=build clears build and build/tests, but not builder.
  • Without an id, it clears every record for the terminal.

Check for support

To find out whether the terminal supports the protocol, send the feature query:

ESC ] 7501 ; ? BEL

Solo answers with the same sequence, using the terminator your query used. This reply is the only data the protocol ever writes back to the program.

How Solo uses reports

Activity

For agents and terminals, working and blocked reports take precedence over Solo's own activity heuristics. A program that reports working stays busy through quiet periods, and a blocked program isn't shown as working even while it redraws the screen. When no working or blocked record remains, Solo goes back to its usual detection.

Attention

A record that newly becomes blocked, done, or error, or whose blocked kind changes, marks the process as needing attention and can send a notification. Repeating the same report or updating progress doesn't alert you again.

The notification title is the record's title, or app when there's no title. The message is msg, or a default based on the state:

State Default message
blocked with kind=permission Waiting for permission
blocked with kind=question Waiting for an answer
blocked with kind=auth Waiting for sign-in
blocked without kind Waiting for input
error Failed
done Finished

When the record leaves that state, for example the program reports working again, Solo removes the attention it raised. Other unread notifications for the process stay.

Program status alerts follow the same rule as BEL and other OSC alerts: they only appear when the effective notification level for the project and process is All.

Automated messages

While a program reports blocked, Solo holds timer and MCP messages for that process and delivers them after it continues. For idle-triggered timers, a blocked process counts as idle, because it's waiting on you.

When records expire

  • working, blocked, and idle records end when the program's process exits, when a new shell prompt starts, and when the terminal is reset.
  • done and error records survive the process exiting, so a finished or failed result stays visible.
  • Starting a new run of the process starts with no records.

Are you an agent? Read this doc via API: /api/v1/docs/terminal/program-status