#Πίνακας Περιεχομένων

Terminal Protocol Analysis · Spec v0.3 (2026-10-07)

OSC 7501: το terminal επιτέλους ξέρει τι κάνει το πρόγραμμά σου

Ο Mitchell Hashimoto δημοσίευσε στις 6 Οκτωβρίου 2026 μια προδιαγραφή για μια νέα terminal escape sequence: OSC 7501 — Program Status Protocol. Κάθε πρόγραμμα μπορεί, γράφοντας μερικές δεκάδες bytes στην pty, να δηλώσει idle, working, done, blocked ή error — και γιατί. Χωρίς SDK, χωρίς sockets, χωρίς heuristics.

Sequence
OSC 7501
ESC ] 7501 ; … ST
States
5 + clear
idle · working · done · blocked · error
Max report
4096 B
μέγιστο νόμιμο < 3300 B
Payload
key=value
msg/title σε base64 UTF-8
01.

TL;DR — τι είναι και γιατί σε νοιάζει

Το OSC 7501 είναι μια προδιαγραφή για terminal escape sequence που επιτρέπει σε οποιοδήποτε πρόγραμμα να πει στο terminal σε τι κατάσταση βρίσκεται. Το σώμα της ακολουθίας είναι μια λίστα από key=value ζεύγη, χωρισμένα με :. Το μόνο υποχρεωτικό key είναι το state.

  • Terminal-native: ταξιδεύει μέσα στην pty που υπάρχει ήδη — δουλεύει over SSH, μέσα σε containers και μέσα από multiplexers.
  • Χωρίς εξαρτήσεις: μια γραμμή printf σε POSIX sh αρκεί. Χωρίς SDK, χωρίς socket, χωρίς JSON.
  • Δεν περιγράφει UI: λέει τι συμβαίνει, όχι πώς θα το δείξεις. Αποφεύγει όρους όπως «notification» ή «focus».
  • Δέντρα εργασιών: με hierarchical id (π.χ. build/test) ένα πρόγραμμα μπορεί να είναι working στη ρίζα ενώ ένα παιδί είναι blocked.
  • Ασφαλές by default: τα σωστά terminals αγνοούν άγνωστα OSC, οπότε η αποστολή χωρίς feature detection δεν «σπάει» τίποτα.
02.

Το πρόβλημα: το terminal δεν ξέρει τι κάνεις

Οι μακροχρόνιες εργασίες είναι ο κανόνας στο terminal: builds, deployments, αναβαθμίσεις πακέτων, επεξεργασία δεδομένων και — όλο και περισσότερο — coding agents. Τα προγράμματα αυτά εναλλάσσονται συνεχώς ανάμεσα σε τρεις φάσεις: δουλεύουν μόνα τους, περιμένουν εσένα, ή τελείωσαν. Ο χρήστης, στο μεταξύ, κάνει κάτι άλλο και θέλει να μάθει πότε η δουλειά τελείωσε ή πότε τον χρειάζεται.

Το terminal όμως βλέπει μόνο bytes. Δεν ξεχωρίζει «χτίζω» από «σε περιμένω» — βλέπει output. Έτσι γεννήθηκαν τρεις οικογένειες λύσεων:

Παρακολούθηση process

Το terminal κοιτάει την foreground process group και ειδοποιεί όταν αλλάξει. Δεν ξέρει τι κάνει η διεργασία ούτε αν κόλλησε.

Ανίχνευση «ησυχίας»

Αν δεν υπάρχει output για Χ δευτερόλεπτα, υποθέτει ότι τελείωσε. Ένα build που εκτυπώνει σπάνια progress ή ένα agent που σκέφτεται είναι αόρατα.

Title / screen scraping

Το πρόγραμμα «κωδικοποιεί» την κατάστασή του σε glyphs στον τίτλο ή στην οθόνη, και κάποιος άλλος τα μαντεύει με regex.

Το τελευταίο είναι το χειρότερο: η πληροφορία υπάρχει, αλλά δεν είναι machine-readable. Είναι ένα emoji σε έναν τίτλο, και όποιος θέλει να το διαβάσει πρέπει να το μαντέψει.

03.

Agentic inboxes: γιατί το πρόβλημα έγινε επείγον

Σήμερα είναι συνηθισμένο να τρέχει κανείς πολλούς μακροχρόνιους agents ταυτόχρονα: background research, παρακολούθηση issues, bug fixing, μεγάλα features. Ο καθένας δουλεύει λίγο και μετά σταματά για να ζητήσει άδεια, να κάνει μια ερώτηση ή να αναφέρει ότι τελείωσε.

Από αυτή την ανάγκη γεννήθηκε μια νέα κατηγορία εργαλείων, που ο Hashimoto τα ονομάζει agentic inbox: μία ενιαία προβολή όλων των running agents, που δείχνει ποιοι δουλεύουν, ποιοι τελείωσαν και ποιοι σε περιμένουν (Herdr, cmux, Agent Deck — και εκατοντάδες άλλα).

Λύση Α: heuristics

Μάντεψε διαβάζοντας την οθόνη ή τον τίτλο του παραθύρου και ταιριάζοντάς τα με γνωστά μοτίβα. Ο Herdr το κάνει σωστά και το τεκμηριώνει ανοιχτά: detection manifests σε TOML που χαρακτηρίζουν έναν agent ως idle / working / blocked.

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

→ 10 αλλαγές σε 3 μήνες, μόνο για το Claude Code.

Λύση Β: out-of-band APIs

Το πρόγραμμα αναφέρει μόνο του την κατάστασή του μέσω socket ή CLI εντολής του inbox (π.χ. cmux notify). Καλύτερο από τα heuristics, γιατί ξέρει τι κάνει.

  • Κάθε πρόγραμμα πρέπει να ενσωματωθεί ξεχωριστά με κάθε inbox.
  • Ένα τοπικό socket δεν δουλεύει over SSH ή μέσα σε container χωρίς επιπλέον bridging.
  • Η pty, αντίθετα, δουλεύει πάντα — υπάρχει ήδη.
Mockup: agentic inbox 3 sessions · 1 χρειάζεται εσένα
feature-auth WORKING
progress 40% · refactor middleware
deploy-eu BLOCKED
permission: «Approve deploy to eu-west (production)?»
migrate-db DONE
«Upgraded 12 packages»
04.

Γιατί οι υπάρχουσες ακολουθίες δεν φτάνουν

Δεν είναι ότι το πρόβλημα δεν έχει αντιμετωπιστεί ποτέ. Είναι ότι κάθε υπάρχουσα λύση καλύπτει ένα κομμάτι του, και δεν μπορούν να συνδυαστούν ώστε να δώσουν κάτι robust.

Ακολουθία Τι κάνει Τι της λείπει
OSC 9;4 Progress: busy, ποσοστό, error. Δεν λέει ότι περιμένει τον χρήστη ούτε τι κάνει ή ποιο πρόγραμμα είναι. Μοιράζεται το «9» με τις ειδοποιήσεις του iTerm2 — γι' αυτό έχουν εμφανιστεί garbled output σε terminals που ξέρουν μόνο το ένα.
OSC 9, OSC 99, OSC 777 Desktop notifications. Είναι one-time events: μόλις εμφανιστούν, το terminal δεν έχει καμία μνήμη του τι ισχύει τώρα. Δεν ξέρει αν το πρόγραμμα ακόμη περιμένει ή αν συνήλθε. Υπονοεί και διακοπή του χρήστη, που δεν είναι πάντα σωστό.
OSC 133 Shell integration: όρια prompts και εντολών. Το στέλνει το shell, όχι το πρόγραμμα. Ξέρει μόνο «τρέχει εντολή» / «τελείωσε με exit code» — ποτέ τι κάνει το πρόγραμμα ή αν κόλλησε.
OSC 0 / OSC 2 Window title. Ένα ελεύθερο string χωρίς δομή. Όσοι δείχνουν state σήμερα το υπερφορτώνουν με glyphs και spinners — μηχανικά αναξιόπιστο.
OSC 21337, OSC 9999 iTerm2 session status, Orca. Προγενέστερη δουλειά στην ίδια ιδέα, αλλά περιγράφουν παρουσίαση (χρώματα, indicators) ή απαιτούν JSON parsing. Όχι interaction-agnostic.

Σχεδιαστική αρχή: το πρωτόκολλο μεταφέρει state και αφήνει την παρουσίαση στο terminal. Δεν λέει πώς θα εμφανιστεί κάτι, και αποφεύγει όρους δεμένους με συγκεκριμένα GUI στοιχεία.

05.

Η σύνταξη: λίγη και αυστηρή

Οι όροι MUST / MUST NOT / SHOULD / MAY χρησιμοποιούνται όπως στα RFC 2119 / RFC 8174, και μόνο όταν γράφονται κεφαλαία.

Bytes

ΌνομαBytes
ESC0x1B
OSCESC ] → 0x1B 0x5D
STESC \ → 0x1B 0x5C, ή BEL → 0x07

Γραμματική

OSC 7501 ; pairs ST        # report
OSC 7501 ; ? ST            # feature detection

pairs    := pair (":" pair)*
pair     := key "=" value
key      := [a-z]+
value    := [A-Za-z0-9_.,+/=-]*
  • Το σώμα είναι λίστα key=value χωρισμένη μόνο με :. Το σύνολο χαρακτήρων της τιμής αποκλείει τα : και ; — άρα δεν χρειάζεται escaping πουθενά. Κενά γύρω από keys/values αφαιρούνται.
  • Ένα malformed pair (χωρίς =, κενό key, ή byte εκτός συνόλου) παραλείπεται και το υπόλοιπο report εξακολουθεί να επεξεργάζεται.
  • Άγνωστα keys MUST να αγνοούνται — έτσι επεκτείνεται το πρωτόκολλο χωρίς versioning.
  • Αν ένα key επαναληφθεί, κερδίζει η τελευταία τιμή.
  • Τα ελεύθερου κειμένου keys (msg, title) είναι standard base64 του UTF-8. Το padding είναι προαιρετικό. Το αποκωδικοποιημένο κείμενο MUST NOT περιέχει control characters (U+0000–U+001F, U+007F, U+0080–U+009F).
  • Ένα report απορρίπτεται ολόκληρο αν ξεπερνά κάποιο όριο, αν το base64 δεν αποκωδικοποιείται, ή αν το κείμενο περιέχει control character. Τίποτα από ένα απορριφθέν report δεν εφαρμόζεται.
06.

Τα 5 states (+ clear)

Το state είναι υποχρεωτικό. Report χωρίς state, ή με τιμή που το terminal δεν αναγνωρίζει, αγνοείται ολόκληρο — έτσι ένα state που θα προστεθεί στο μέλλον δεν μετατρέπεται ποτέ σιωπηλά σε idle σε παλιότερο terminal.

stateΣημασίαLifetime
idleΣε ηρεμία, περιμένει την επόμενη εντολή του χρήστη.Μέχρι να αντικατασταθεί ή να καθαριστεί.
workingΤρέχει. Μπορεί να φέρει progress.Μέχρι αντικατάσταση, clear, exit της διεργασίας ή επόμενο shell prompt.
doneΤελείωσε μια δουλειά. Το αποτέλεσμα είναι έτοιμο και ο χρήστης δεν το έχει δει.Μέχρι αντικατάσταση ή clear. Επιβιώνει από exit και shell prompt.
blockedΔεν μπορεί να συνεχίσει μέχρι να κάνει κάτι ο χρήστης. Το kind λέει τι, το msg γιατί. Μπορεί να φέρει progress.Ίδιο με working.
errorΑπέτυχε και σταμάτησε.Ίδιο με done.
clearΔεν είναι state: αφαιρεί το record που απευθύνεται και κάθε record από κάτω του. Χωρίς id, αφαιρεί όλα τα records του terminal.—

Όταν ο χρήστης διακόπτει ή ακυρώνει ένα πρόγραμμα, αυτό πρέπει να αναφέρει idle. Ένα πρόγραμμα που τερματίζει μόλις τελειώσει SHOULD να στείλει done ή error ακριβώς πριν βγει, ώστε να μείνει ένα record για τον χρήστη.

Δεν υπάρχει heartbeat. Τα records δένονται με events που το terminal βλέπει ήδη (exit διεργασίας, νέο prompt) και τα terminals MUST NOT απαιτούν επαναποστολή για να κρατήσουν ένα record ζωντανό.

07.

Τα keys: kind, progress, app, title, msg

KeyΤιμέςΣημειώσεις
stateidle | working | done | blocked | error | clearΥποχρεωτικό. Άγνωστη τιμή → το report αγνοείται ολόκληρο.
idpath (δές §8)Αν λείπει, απευθύνεται στο root record. Ένα πρόγραμμα που αναφέρει μόνο τη δική του κατάσταση χρησιμοποιεί το root και ξεχνά τα υπόλοιπα.
kindpermission | question | authΜόνο με blocked, αγνοείται αλλιώς. permission: έγκριση για κάτι. question: ο χρήστης πρέπει να πληκτρολογήσει απάντηση. auth: login, token ή credential. Άγνωστες τιμές θεωρούνται απόντος.
progressακέραιος 0–100Με working ή blocked. Απών = indeterminate: το πρόγραμμα είναι απασχολημένο αλλά δεν έχει ποσοστό.
app[A-Za-z0-9_.+-]{1,32}Σταθερό machine-readable όνομα (cargo, terraform, claude-code). Record χωρίς app το κληρονομεί από τον πλησιέστερο πρόγονο που το έχει.
titlebase64 UTF-8Σύντομη ετικέτα για το record, χρήσιμη όταν ένα πρόγραμμα αναφέρει πολλά records. Η ετικέτα του root είναι συνήθως ο τίτλος του παραθύρου.
msgbase64 UTF-8Μία γραμμή ανθρώπινου κειμένου: τι κάνει, τι περιμένει, τι τελείωσε. Τα terminals MAY το συντομεύσουν και MUST NOT βγάλουν νόημα από αυτό.

Καλά νέα για ελληνικά: επειδή το msg είναι base64 του UTF-8, μπορείς να στείλεις «Χτίσιμο 42/120 πακέτων» χωρίς κανένα πρόβλημα κωδικοποίησης. Δοκιμασμένο: zqfPhM6vz4POuc68zr8gNDIvMTIwIM+AzrHOus6tz4TPic69 → Χτίσιμο 42/120 πακέτων.

08.

Records & ids: το δέντρο των καταστάσεων

Το terminal κρατά ένα σύνολο records, ένα ανά id. Το κάθε record κρατά ακριβώς τα keys του τελευταίου report που χρησιμοποίησε αυτό το id.

id      := segment ("/" segment)*
segment := [A-Za-z0-9_.+-]{1,32}
  • Κάθε report αντικαθιστά το record ολόκληρο. Ένα key που λείπει από το report λείπει και από το record μετά. Γι' αυτό τα παραδείγματα επαναλαμβάνουν το app σε κάθε αποστολή.
  • Report με id που δεν ταιριάζει στη γραμματική αγνοείται. Δεν πέφτει στο root: αλλιώς ένα malformed id θα μπορούσε να σβήσει το root record.
  • Το / δίνει σχέση γονέα–παιδιού: το build/test είναι παιδί του build, που είναι παιδί του root. Ο γονέας δεν χρειάζεται να υπάρχει. Η σχέση χρησιμοποιείται μόνο για clearing, κληρονομικότητα του app και (προαιρετικά) grouping.
  • Root και παιδιά συνυπάρχουν: το πρόγραμμα μπορεί να είναι idle στο prompt του ενώ ένας worker του είναι blocked.
  • Τα records ανήκουν στο terminal, όχι στην οθόνη: η εναλλαγή primary/alternate screen δεν τα επηρεάζει. Το RIS (full reset) σβήνει τα πάντα, το DECSTR (soft reset) όχι.
Δέντρο records: ένα deploy με 2 regions
root: app=deploy state=working «Deploying v2.4.1» id=us-east state=working · progress=40 title=«US East» · «Pushing image» id=eu-west state=blocked · kind=permission «Approve deploy to eu-west (production)?» id=us-east/plan → done κληρονομεί app=deploy Και τα τρία ισχύουν ταυτόχρονα. Το terminal αποφασίζει τι δείχνει.
09.

Πρακτικά παραδείγματα

Ένα πρόγραμμα που αναφέρει τη ροή του (Homebrew-style)

OSC 7501 ; state=working:app=brew:msg=SW5zdGFsbGluZyB1cGRhdGVz ST                          # Installing updates
OSC 7501 ; state=blocked:kind=auth:app=brew:msg=UGFzc3dvcmQgcmVxdWlyZWQgdG8gaW5zdGFsbCB1cGRhdGVz ST  # Password required to install updates
OSC 7501 ; state=working:app=brew:msg=SW5zdGFsbGluZyB1cGRhdGVz ST                          # Installing updates
OSC 7501 ; state=done:app=brew:msg=VXBncmFkZWQgMTIgcGFja2FnZXM= ST                        # Upgraded 12 packages

Το τελευταίο done επιβιώνει από το shell prompt που ακολουθεί — γι' αυτό το βλέπεις όταν γυρίσεις στο παράθυρο.

Πολλά records: deploy σε δύο regions

OSC 7501 ; state=working:app=deploy:msg=RGVwbG95aW5nIHYyLjQuMQ== ST                               # Deploying v2.4.1
OSC 7501 ; state=working:id=us-east:title=VVMgRWFzdA==:progress=40:msg=UHVzaGluZyBpbWFnZQ== ST          # US East / Pushing image
OSC 7501 ; state=blocked:kind=permission:id=eu-west:title=RVUgV2VzdA==:msg=QXBwcm92ZSBkZXBsb3kgdG8gZXUtd2VzdCAocHJvZHVjdGlvbik/ ST
OSC 7501 ; state=done:id=us-east:title=VVMgRWFzdA==:msg=SGVhbHRoeQ== ST                              # US East / Healthy
OSC 7501 ; state=clear:id=us-east ST
OSC 7501 ; state=clear:id=eu-west ST
OSC 7501 ; state=done:app=deploy:msg=RGVwbG95ZWQgdG8gMyByZWdpb25z ST                          # Deployed to 3 regions

Τα records των regions παίρνουν app=deploy από το root. Όσο το eu-west είναι blocked, το root λέει ακόμη working — και τα δύο είναι αληθή.

Σε ελληνικά, με πραγματικό base64

# «Απαιτείται έγκριση για διαγραφή 1.204 αρχείων»
OSC 7501 ; state=blocked:kind=permission:app=cleanup:msg=zpHPgM6xzrnPhM61zq/PhM6xzrkgzq3Os866z4HOuc+DzrcgzrPOuc6xIM60zrnOsc6zz4HOsc+Gzq4gMS4yMDQgzrHPgc+HzrXOr8+Jzr0= ST

# «Ολοκληρώθηκε: 3 επιτυχίες, 0 σφάλματα»
OSC 7501 ; state=done:app=cleanup:msg=zp/Ou86/zrrOu863z4HPjs64zrfOus61OiAzIM61z4DOuc+Ez4XPh86vzrXPgiwgMCDPg8+GzqzOu868zrHPhM6x ST

Η πλήρης ενσωμάτωση: μια shell function

status() {
  printf '\e]7501;state=%s:msg=%s\e\\' "$1" "$(printf '%s' "$2" | base64 | tr -d '\n')"
}
status working "Συγχρονισμός φωτογραφιών"
rsync -a ~/Photos backup:/photos && status done "Ολοκληρώθηκε ο συγχρονισμός" || status error "Αποτυχία: ο rsync δεν βρήκε τον προορισμό"

Είναι απλό POSIX sh: χωρίς SDK, χωρίς sockets, χωρίς environment variables, χωρίς JSON. Ο Hashimoto το χαρακτηρίζει «trivial to script» — και είναι όντως.

10.

Lifetime: πότε «πεθαίνει» ένα record

Το πιο έξυπνο σημείο της προδιαγραφής: τα records δένονται με events που το terminal βλέπει ήδη, ώστε να μην απαιτείται heartbeat ή polling.

working · blocked

Το terminal MUST τα ρίχνει όταν η διεργασία που είναι συνδεδεμένη στο terminal βγει, ή όταν ξεκινήσει νέο shell prompt (OSC 133 A).

done · error

Επιβιώνουν και από τα δύο events — μένουν ως «κάτι σε περιμένει». Το terminal αποφασίζει πότε θα σταματήσει να τα δείχνει (π.χ. όταν γυρίσεις στο παράθυρο και πατήσεις πλήκτρο).

idle

Το terminal MAY να το ρίξει στο νέο prompt — προαιρετικό, γιατί ένα idle record δεν είναι «εκκρεμότητα».

Πρακτική συνέπεια: χωρίς shell integration (OSC 133) το «σβήσιμο» των working/blocked records βασίζεται κυρίως στο exit της διεργασίας. Σε ένα μακρόβιο shell session αυτό αξίζει έλεγχο στη δική σου υλοποίηση.

11.

Feature detection & terminfo

Ο μόνος τρόπος να μάθεις αν το terminal υποστηρίζει το πρωτόκολλο:

program  → OSC 7501 ; ? ST
terminal → OSC 7501 ; ? ST
  • Ένα υποστηρίζον terminal MUST απαντήσει με το ίδιο σώμα, ?.
  • Η ανίχνευση είναι προαιρετική: μπορείς να στέλνεις reports χωρίς query — τα σωστά terminals αγνοούν άγνωστα OSC, άρα είναι ασφαλές.
  • Trick για να μην περιμένεις: στείλε CSI c (primary device attributes) αμέσως μετά το query. Απαντούν όλα τα terminals — αν η απάντησή τους φτάσει πρώτη, το πρωτόκολλο δεν υποστηρίζεται.
  • Μελλοντική revision μπορεί να προσθέσει ζεύγη μετά το ? στην απάντηση· τα προγράμματα MUST αγνοούν ό,τι δεν καταλαβαίνουν.

terminfo

Τα terminals που το υλοποιούν SHOULD προσθέτουν την εκτεταμένη δυνατότητα Pst:

Pst=\E]7501;%p1%s\E\\
  • Η Pst διαφημίζει υποστήριξη — δεν μεταφέρει state και δεν αντικαθιστά το query.
  • Αν τη βρεις, MAY στέλνεις reports χωρίς query.
  • Αν δεν τη βρεις, MUST NOT συμπεράνεις ότι δεν υποστηρίζεται: το entry μπορεί να είναι παλιό, π.χ. over ssh ή μέσα σε multiplexer. Η απάντηση στο query είναι πάντα η αυθεντία.

Πρώτο βήμα για κάθε νέο πρωτόκολλο terminal: ρώτα, μην υποθέτεις — και ποτέ μην «σπάσεις» αν η απάντηση δεν έρθει.

12.

Ασφάλεια: untrusted input by design

Η βασική παραδοχή είναι σκληρή και σωστή: ό,τι περιέχει ένα report είναι untrusted input από ένα πρόγραμμα που ελέγχει ήδη την οθόνη. Δεν υπάρχει αυθεντικοποίηση — και δεν μπορεί να υπάρξει μέσα στην pty.

Τι MUST κάνει το terminal

  • Να αρνείται report του οποίου το αποκωδικοποιημένο msg/title περιέχει control character.
  • Να μην το ερμηνεύει ποτέ ως markup.
  • Να «αφοπλίζει» text-direction overrides και άλλα αόρατα formatting characters όταν τα δείχνει έξω από το grid

Επιφάνεια επίθεσης

  • Τα μόνα bytes που γράφει το terminal πίσω είναι η σταθερή απάντηση του feature detection. Ids, titles και messages δεν επιστρέφονται ποτέ· δεν υπάρχει τρόπος να διαβάσεις records.
  • Κάθε εμφανιζόμενο record SHOULD δηλώνει από ποιο terminal προέρχεται, ώστε ένα πρόγραμμα να μην μπορεί να προσποιηθεί πρόγραμμα άλλου terminal.
  • Ένα πρόγραμμα μπορεί να αλλάζει state όσο γρήγορα γράφει: τα terminals SHOULD κάνουν rate-limit σε ό,τι προκαλεί το record έξω από το terminal (ειδοποιήσεις, ήχοι).

Spoofing παραμένει δυνατό: οποιοδήποτε πρόγραμμα στην ίδια pty μπορεί να στείλει app=terraform:state=blocked και να «δείχνει» σαν το Terraform. Η προδιαγραφή το αντιμετωπίζει με προέλευση (origin labeling) και rate limiting — όχι με κρυπτογραφία. Μην χτίσεις security decision πάνω σε records.

13.

Όρια: hard caps για να μην πνιγεί το terminal

Κάθε όριο είναι σκληρό, ώστε ένα hostile ή buggy πρόγραμμα να μην μπορεί να αυξήσει τη μνήμη ή να προκαλέσει απεριόριστη δουλειά. Τα terminals MAY επιλέξουν χαμηλότερα όρια. Report που σπάει ένα όριο απορρίπτεται ολόκληρο.

ΣτοιχείοΌριο
Ολόκληρη η ακολουθία (OSC → ST)4096 bytes (το μέγιστο νόμιμο < 3300)
Μήκος key16 bytes
msg2732 encoded / 2048 decoded bytes
title256 encoded / 192 decoded bytes
app32 bytes
id128 bytes σύνολο, 32 ανά segment, 8 επίπεδα βάθος
Records ανά terminal256 (MUST τουλάχιστον 64)

Όταν ένα report θα δημιουργούσε record πάνω από το cap, το terminal αφαιρεί το record που ενημερώθηκε λιγότερο πρόσφατα (LRU) για να κάνει χώρο.

Check πριν αγγίξεις storage: «Έλεγξε κάθε pair πριν αγγίξεις οποιοδήποτε αποθηκευμένο record» — δηλαδή validated-then-apply, όχι apply-while-parsing. Καλή πρακτική που οι parser writers ξεχνούν συχνά.

Πρακτικά: ένα msg έως ~2 KB φτάνει για κάθε άνθρωπο-αναγνώσιμο status· τα υπόλοιπα ανήκουν σε logs, όχι στο terminal.

14.

Interactive: φτιάξε το δικό σου OSC 7501 report

Συμπλήρωσε τα πεδία και πάρε έτοιμη την ακολουθία (με σωστό base64 για ελληνικά). Η κωδικοποίηση γίνεται τοπικά στον browser — τίποτα δεν φεύγει από τη σελίδα.

progress
40
Πώς θα μπορούσε να το δείξει ένα terminal
working app=deploy · progress=40

Απαιτείται έγκριση για deploy στο eu-west

Η ακολουθία (readable)


                    
msg σε base64

                        
Shell one-liner

                        
Μέγεθος report: — / 4096 bytes
Decoded msg: — / 2048 bytes
15.

Υλοποιήσεις & οικοσύστημα

Terminal emulators

  • libghostty — υλοποίηση στο PR #14560.
  • Rex — παράλληλη υλοποίηση, με session picker indicators, spinners σε unfocused tabs και events (terminal.program_status_changed) για Lua scripts.
  • Σχεδιαζόμενα: push notifications σε mobile apps, unified priority inbox.

Proof-of-concept προγράμματα

Ο Hashimoto το υλοποίησε σε:

  • · Terraform
  • · Claude Code
  • · Codex
  • · Homebrew

Σε κάθε περίπτωση μέσω plugin ή fork, με λιγότερες από δώδεκα γραμμές κώδικα.

Agentic inboxes

«Θα ήθελα να σταματήσουμε όλοι να μαντεύουμε τι κάνουν τα προγράμματα διαβάζοντας τις οθόνες ή τα process trees. Το πρόγραμμα το ξέρει ήδη. Ας του δώσουμε έναν τρόπο να μας το πει.»

RevisionΗμερομηνίαΑλλαγές
0.32026-10-07Το απών progress σημαίνει indeterminate. Τα προγράμματα μπορούν να στέλνουν reports χωρίς feature detection.
0.22026-10-06Διευκρινίσεις σε MUST NOT και εφαρμογή keywords σε περισσότερα σημεία.
0.12026-09-28Αρχικό draft.
16.

Δοκιμή σήμερα & κώδικας σε 5 γλώσσες

Δεν χρειάζεται να περιμένεις υιοθέτηση για να στείλεις: η αποστολή είναι ασφαλής no-op σε terminals που δεν το ξέρουν. Αυτό που χρειάζεσαι είναι ένα terminal που το καταναλώνει (σήμερα: build του libghostty με το PR #14560, ή Rex).

# 1) Στείλε ένα report
printf '\e]7501;state=working:app=demo:msg=%s\e\\' "$(printf '%s' 'Λήψη δεδομένων αισθητήρων' | base64 | tr -d '\n')"

# 2) Ρώτα αν υποστηρίζεται (περίμενε απάντηση στην ίδια pty)
printf '\e]7501;?\e\\'

# 3) Δες τα raw bytes που έστειλες, καταγράφοντας την pty
script -q /dev/null -c "printf '\e]7501;state=done:app=demo'" | cat -v

Σημείωση: ένα report μπορεί να τερματιστεί είτε με ST (ESC \) είτε με BEL (0x07). Το \e\\ στα shells είναι το πιο φορητό και το πιο ευανάγνωστο σε logs.

POSIX sh (function)

status() {
  printf '\e]7501;state=%s:app=%s:msg=%s\e\\' "$1" "$2" "$(printf '%s' "$3" | base64 | tr -d '\n')"
}
status working demo "Χτίσιμο 42/120 πακέτων"

Python

import base64, sys

def status(state, app="demo", msg="", **extra):
    pairs = [f"state={state}", f"app={app}"]
    if msg:
        pairs.append("msg=" + base64.b64encode(msg.encode()).decode())
    pairs += [f"{k}={v}" for k, v in extra.items()]
    sys.stdout.write("\x1b]7501;" + ":".join(pairs) + "\x1b\\")
    sys.stdout.flush()

status("blocked", kind="permission", msg="Απαιτείται έγκριση για διαγραφή 1.204 αρχείων")

Go

package main

import (
    "encoding/base64"
    "fmt"
)

func status(state, app, msg string, progress int) {
    m := base64.StdEncoding.EncodeToString([]byte(msg))
    fmt.Printf("\x1b]7501;state=%s:app=%s:progress=%d:msg=%s\x1b\\", state, app, progress, m)
}

Rust

use std::io::{self, Write};
use base64::{engine::general_purpose::STANDARD, Engine};

fn status(state: &str, app: &str, msg: &str) -> io::Result<()> {
    let m = STANDARD.encode(msg);
    let seq = format!("\x1b]7501;state={state}:app={app}:msg={m}\x1b\\");
    io::stdout().write_all(seq.as_bytes())?;
    io::stdout().flush()
}

Node.js

const status = (state, app, msg = "", extra = {}) => {
  const pairs = [`state=${state}`, `app=${app}`];
  if (msg) pairs.push(`msg=${Buffer.from(msg, "utf8").toString("base64")}`);
  for (const [k, v] of Object.entries(extra)) pairs.push(`${k}=${v}`);
  process.stdout.write(`\x1b]7501;${pairs.join(":")}\x1b\\`);
};

status("done", "deploy", "Ολοκληρώθηκε: 3 επιτυχίες, 0 σφάλματα");

Κοινός παρονομαστής: και στις πέντε γλώσσες η υλοποίηση είναι μία συνάρτηση, χωρίς εξάρτηση πέρα από base64. Αυτό είναι το πλεονέκτημα ενός πρωτοκόλλου που «κάθεται» πάνω στην pty αντί να απαιτεί API.

17.

Κριτική & ανοιχτά ζητήματα

Chicken-and-egg υιοθέτηση

Χωρίς terminals που το καταναλώνουν, τα reports είναι αόρατα. Ευτυχώς η αποστολή δεν «σπάει» τίποτα, οπότε η υιοθέτηση από apps μπορεί να προηγηθεί — αλλά η αξία φαίνεται μόνο όταν και οι δύο πλευρές το μιλήσουν.

Multiplexers

tmux/screen μπορούν να «καταναλώσουν» την ακολουθία. Χρειάζεται passthrough (DCS wrapping / allow-passthrough) για να φτάσει στο εξωτερικό terminal — και τότε τίθεται το ερώτημα: τα records ανήκουν στο pane ή στο παράθυρο;

Κίνδυνος κατακερματισμού

Υπάρχουν ήδη το OSC 21337 (iTerm2) και το OSC 9999 (Orca) με άλλη φιλοσοφία. Αν υιοθετηθούν παράλληλα, οι agentic inboxes θα γράφουν N adapters — ακριβώς το πρόβλημα που λύνει το 7501.

Write-only πρωτόκολλο

Δεν μπορείς να διαβάσεις τα τρέχοντα records — απόφαση ασφαλείας, αλλά σημαίνει ότι κάθε εργαλείο πρέπει να κρατά τη δική του cache και να παράγει το τελικό state μόνο του.

Σημασιολογία progress

Μόνο ακέραιο 0–100, χωρίς ETA, μονάδες ή βάρη βημάτων. Για σύνθετες ροές, το δέντρο records είναι η απάντηση: progress σε κάθε κόμβο αντί σε ένα συνολικό ποσοστό.

Νεαρή προδιαγραφή

Revision 0.3, τρεις εβδομάδες ζωής, γραμμένη από έναν συντάκτη και ένα προϊόν (Rex/Superlogical). Είναι καλογραμμένη και δηλώνει generic — αλλά το adoption test είναι μπροστά, όχι πίσω.

Η δική μας εκτίμηση: το βασικό στοίχημα — «χρησιμοποίησε την pty, όχι ένα καινούργιο κανάλι» — είναι σωστό και είναι ο λόγος που αυτό το πρωτόκολλο μπορεί να περάσει εκεί που τα sockets απέτυχαν. Το στοίχημα που δεν έχει κριθεί είναι αν οι emulators θα το υιοθετήσουν μαζί, αντί να προσθέσει ο καθένας το δικό του OSC.

18.

Γλωσσάρι

OSC — Operating System Command· escape sequence που ξεκινά με ESC ].
ST — String Terminator, ESC \ (ή BEL): κλείνει την ακολουθία.
pty — pseudo-terminal: το ζεύγος master/slave που συνδέει terminal και πρόγραμμα.
report — μία ακολουθία που στέλνει ένα πρόγραμμα.
record — ό,τι αποθηκεύει το terminal ως αποτέλεσμα ενός report, ανά id.
root record — το record χωρίς id: η προεπιλογή για προγράμματα που αναφέρουν μόνο τον εαυτό τους.
agentic inbox — ενιαία προβολή της κατάστασης όλων των running agents.
terminfo — βάση δυνατοτήτων τερματικών· εδώ η επέκταση Pst.
RIS / DECSTR — full reset (σβήνει τα πάντα) / soft reset (δεν σβήνει records).
OSC 133 — shell integration: σηματοδοτεί prompts και όρια εντολών.
19.

Συχνές Ερωτήσεις

Τι είναι το OSC 7501 με μία πρόταση;

Ένα terminal-native πρωτόκολλο που επιτρέπει σε κάθε πρόγραμμα να δηλώσει μέσω της pty τι κάνει — idle, working, done, blocked, error — με προαιρετικό progress, app name και μήνυμα.

Χρειάζομαι βιβλιοθήκη ή SDK;

Όχι. Είναι bytes στην pty. Μία printf και ένα base64 αρκούν — δες τη shell function στην §9 και τον κώδικα 5 γλωσσών στην §16.

Δουλεύει πάνω από SSH, tmux ή μέσα σε container;

Ναι — αυτό είναι το βασικό του πλεονέκτημα έναντι των sockets, γιατί η pty υπάρχει σε όλες αυτές τις περιπτώσεις. Ο multiplexer χρειάζεται passthrough για να μη «καταναλώσει» την ακολουθία.

Τι γίνεται αν το terminal μου δεν το υποστηρίζει;

Τίποτα κακό: τα σωστά terminals αγνοούν άγνωστα OSC. Μπορείς να ρωτήσεις με OSC 7501 ; ? και να αποφασίσεις, ή να στέλνεις πάντα και να μην περιμένεις τίποτα.

Γιατί base64 αντί για JSON στο msg;

Επειδή το base64 δεν περιέχει :, ; ή control characters: μηδέν escaping, σταθερό μήκος, ίδιο payload παντού. Το κόστος είναι ότι δεν είναι άμεσα διαβάσιμο σε cat -v.

Μπορεί κακόβουλο πρόγραμμα να στείλει ψεύτικο status;

Ναι. Δεν υπάρχει αυθεντικοποίηση — όποιος γράφει στην pty μπορεί να δηλώσει ό,τι θέλει. Γι' αυτό η προδιαγραφή επιβάλλει προέλευση, απόρριψη control characters, απαγόρευση markup και rate limiting. Μην παίρνεις αποφάσεις ασφαλείας από records.

Υποστηρίζει ελληνικά μηνύματα;

Ναι, επειδή το msg είναι base64 του UTF-8. Δοκίμασέ το με το interactive εργαλείο της §14 — η κωδικοποίηση γίνεται τοπικά στον browser.

Ποια terminals το υποστηρίζουν σήμερα;

Δημόσια υλοποίηση υπάρχει στο libghostty (PR #14560) και στο Rex. Ο συγγραφέας έχει μιλήσει με maintainers πολλών emulators και ζητά να του δηλώνονται νέες υλοποιήσεις.

20.

Πηγές & παραπέρα διάβασμα