Report the TUI's status to terminals that support OSC 7501 program status
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 56/100
- Issue type
- Feature
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- typescript
- Domain
- backend-api-design, cli, documentation, testing
Research direction
Start with .agents/skills/write-tui/SKILL.md, then read daemon-tui.ts around openSession, renderActivityState, dispose, and run-shell.ts cleanup paths. Use terminal-focus.ts as the input-listener example and inspect pi-tui’s DA1 negotiation before deciding how to coordinate the probe. Add unit and pseudo-terminal coverage for probe handling, state reports, exit cleanup, and encoding limits; update both interface guides. Done means the listed scenarios pass and any pi-tui divergence is documented and guarded by a test.
Written by the indexing model from the issue text.
Description
What feature would you like to see?
Terminals that show the state of the programs running in them (idle, working, waiting for the user, finished) cannot tell what Kiki's TUI is doing: Kiki does not report anything, so such a terminal can only guess from the screen. The OSC 7501 program-status protocol is a generic, vendor-neutral way for a program to tell its terminal its state. The TUI should probe for it and, where the terminal answers, report its state; in every other terminal (including tmux) nothing changes.
Decided
- Use the OSC 7501 program-status protocol (revision 0.4) exactly as summarised below; nothing in the implementation is specific to any terminal product.
app=kikion every report.- Report only after a successful probe; never emit any OSC 7501 sequence in a terminal that did not answer it.
- Emit
state=clearon every exit path that restores the terminal. - Coalesce: never emit two identical consecutive reports; no periodic re-emission.
- This issue reports the standard keys only. The session hand-off keys (
handoff,session,port,server) and ticket requests are a separate issue that depends on the server hand-off work.
Design
Protocol summary (what the TUI must implement).
- Report:
ESC ] 7501 ; key=value:key=value:… ST, whereSTisESC \orBEL. Pairs are separated by:, key and value by=, so a value can contain neither:nor;. The whole sequence is at most 4096 bytes. Receivers ignore unknown keys. Each report replaces the previous one completely, so every report carries every key the TUI wants kept. - Keys used by Kiki:
state(required):idle,working,done,blocked,error,clear.kind(only withstate=blocked):permission,question,auth.app:kiki.title: the session title as base64 of UTF-8, at most 192 bytes before encoding, no control characters; omitted when the session has no title.msg: base64 of UTF-8, one line, no control characters, at most 2048 bytes before encoding.progressandidare not used.
- A report that breaks a limit is discarded whole by the terminal, so truncate
titleandmsgon a UTF-8 boundary before encoding, strip control characters (newlines included) first, and if the whole sequence would still exceed 4096 bytes, shortenmsguntil it fits. - Probe: at start-up write
ESC ] 7501 ; ? STimmediately followed by a DA1 queryESC [ c. A supporting terminal writes back an OSC 7501 reply whose body starts with?(it may append further pairs, which the TUI ignores) before the DA1 reply. If the DA1 reply arrives first, the terminal does not support the protocol and the TUI never writes an OSC 7501 sequence in this process. (A body that starts with?ticket=is a ticket request, handled by the follow-up issue, not a probe reply.)
State mapping, projected from session-core SessionViewState (the TUI already derives most of this in renderActivityState):
| TUI situation | report |
|---|---|
| no turn running, no pending approval or question | state=idle |
| the main agent is in a turn | state=working |
| a permission approval is pending | state=blocked:kind=permission, msg = a short summary of what is being approved |
| a question is pending | state=blocked:kind=question, msg = the question text |
| sign-in is required before work can continue | state=blocked:kind=auth |
| a turn ended normally and the user has not looked yet | state=done; where the TUI cannot tell whether the user has looked, state=idle is acceptable |
| a turn ended with an error | state=error, msg = a short error summary |
When to report: once after the probe succeeds; whenever the reported state, kind, title or msg changes; whenever the open session changes by any route (/new, /clear, /sessions, /resume, persona switch, deleting the current session, a fork that moves the TUI) — all of which go through openSession; and state=clear on exit.
Input handling. The probe reply must be consumed before it reaches the editor or any key handler, whatever screen is showing, and must survive being split across reads. StdinBuffer already assembles OSC sequences (ESC ] … ST) into single sequences, and TUI.addInputListener runs listeners before components; apps/kimi-code/src/tui/utils/terminal-focus.ts (installed from DaemonTUI.start) consumes focus reports this way and is the pattern to follow.
DA1 coordination. pi-tui already writes a DA1 query as the sentinel after its kitty keyboard query (terminal.ts:17, written at :256) and consumes DA1 replies in handleKeyboardProtocolNegotiationSequence. The probe's ordering decision needs a DA1 reply too; it must neither steal the reply the keyboard negotiation waits for nor be confused by it. packages/pi-tui is vendored: any change there must be minimal and registered as a local divergence in packages/pi-tui/AGENTS.md ("Local divergences from upstream"), with a guarding test (it takes the next free divergence number; another open issue adds divergence 11), and pnpm --filter @kiki/pi-tui test must pass in full (the package is @kiki/pi-tui; packages/pi-tui/AGENTS.md still names the old filter).
Exit. Write state=clear (only if the probe succeeded) on: the normal stop path (DaemonTUI.dispose, before ui.stop()), the double Ctrl-C / Ctrl-D exit, and the emergency and signal exits in run-shell.ts.
Placement. Follow .agents/skills/write-tui/SKILL.md: run-shell.ts owns process and signal cleanup and terminal restoration; daemon-tui.ts coordinates; status is projected from SessionViewState, not rebuilt from ad hoc events; put the encoder, probe state machine and coalescing in a pure helper under src/tui/utils/ with unit tests.
Docs (en and zh, mirrored): a short paragraph in guides/interface.md saying that Kiki reports its state to terminals that support the OSC 7501 program-status protocol and that other terminals are unaffected.
Where
apps/kimi-code/src/tui/daemon/daemon-tui.ts:890—openSession(every session change);:1057—renderActivityState(current state derivation,firstPendingInteraction,view.busy);:278-345—dispose(normal exit;ui.stop()is last);:677—confirmExit.apps/kimi-code/src/cli/run-shell.ts:100—emergencyExit;:126,:134,:146,:163— its callers (crash and signal paths).packages/pi-tui/src/tui.ts:710—addInputListener;:830-845— listeners run before components.packages/pi-tui/src/stdin-buffer.ts:131-140— OSC completion.packages/pi-tui/src/terminal.ts:17,:240-280— DA1 sentinel and keyboard negotiation.apps/kimi-code/src/tui/utils/terminal-focus.ts:17-30— a live input listener that consumes terminal reports;daemon-tui.ts:243installs it.packages/protocol/src/session.ts:173-188—busy,main_turn_active,pending_interaction,last_turn_reason.
Done when
- With a pseudo-terminal script that does not answer the probe: no OSC 7501 sequence appears in Kiki's output, and keyboard protocol negotiation still works (test).
- With a script that answers the probe before DA1: after start-up there is one report with
stateandapp=kiki; sending a prompt producesworkingand thendoneoridle; triggering an approval producesstate=blocked:kind=permissionwith amsg; a question produceskind=question; exiting producesstate=clear(test). -
/newand/sessionseach produce a new report with the new session's title (or none); identical consecutive reports never appear (test). - A title or message with
:,;, newlines, control characters or more than the byte limits produces a valid report within all limits (unit tests). - The probe reply never shows up in the editor, a dialog or a picker, including when it is split across two reads (test).
- Any pi-tui change is listed as a divergence in
packages/pi-tui/AGENTS.mdwith a guarding test;pnpm --filter @kiki/pi-tui testpasses. - en and zh docs updated.
Additional information
- Nothing of this exists yet:
rg 7501finds nothing in the repository, and the TUI writes no terminal title. - The extension keys and the ticket request (session hand-off) are the follow-up issue; keep the encoder and the input listener easy to extend with more keys and a second request type.
- The protocol is published as the "Program Status Protocol (OSC 7501)", revision 0.4; the summary above is complete for this issue.
Order and scope. Paths and line numbers refer to the code after the pending GUI interaction and mobile pull request is merged into kiki.
Main files: apps/kimi-code/src/tui/utils/program-status.ts, apps/kimi-code/src/tui/daemon/daemon-tui.ts, apps/kimi-code/src/cli/run-shell.ts, packages/pi-tui/src/terminal.ts, packages/pi-tui/AGENTS.md, apps/kimi-code/test/**, docs/en/guides/interface.md, docs/zh/guides/interface.md.
- Dominant language
- TypeScript
- Stars
- 54
- Forks
- 5
- Avg merge
- 2h 54m
- Merged PRs (30d)
- 2
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from X-T-E-R/kiki
-
Document PowerShell shell semantics and read-only Bash deduplication in Built-in Tools referenceOpendocumentation
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
bug documentation
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
-
bug
Difficulty 4/5 3-5 days Newbie friendliness 54/100
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 1/100
-
documentation
Difficulty 5/5 Over a week Newbie friendliness 25/100
Similar issues
-
area: cli priority: P3 type: bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
appandflow/stim#5640 · 1 comment ·
Maintainers usually reply within 1 day
-
Claude Code: hook systemMessage lines never reach the chatPossibly taken @chphch claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
slopus/happy-agent#139 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
NangoHQ/nango#8285 · 1 comment ·
Maintainers usually reply within 1 day
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 85/100
lingdojo/kana-dojo#34009 · 1 comment · 5 reactions ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
paperclipai/paperclip#21197 ·
Maintainers usually reply within 1 day