Skip to content

Input awareness: show when a command is waiting for you - #348

Draft
raiseCatError wants to merge 10 commits into
release/v0.18.0from
feature/input-awareness
Draft

raiseCatError wants to merge 10 commits into
release/v0.18.0from
feature/input-awareness

Conversation

@raiseCatError

@raiseCatError raiseCatError commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

NMSh now makes it obvious when a running command is waiting for you (e2fsck's Fix<y>?, a password, rm -i, read), shows the question where it stays in view, and routes typing the way the program reads it. Draft until physically validated.

Base: release/v0.18.0 (#345). This remains an unmerged workflow-stack PR; combined acceptance integration follows the stabilization and workspace freezes.

◆ Waiting for input · e2fsck · 18.0s · 1m 40s total
  Padding at end of inode bitmap is not set. Fix<y>?  ·  each key goes straight to e2fsck
❯ Keys go straight to e2fsck

How it decides

Waiting detection reads terminal state and the process table. Fish builtin readers explicitly report terminal ownership through a private authenticated shell event.

  • Confirmed (◆): no-echo line input (passwords) and single-key input with a question left open, from stty. On Linux, also a foreground thread blocked reading the terminal (/proc).
  • Probably (◇): a question-shaped open line, quiet output, and an idle foreground group across two samples. This covers ordinary line reads on macOS, which exposes no wait channel.
  • Never reported: builds, sleep, network waits, progress bars, Downloading... status lines, log streams, and programs that own the terminal (full-screen, bracketed paste, Fish's own read).

Input ownership and timing

  • Hidden lines and single keys: each key goes straight to the program as plain terminal bytes. It is never drawn, journaled or logged. This fixes a stray Enter after single-key answers, and passwords appearing in the composer.
  • Line input: stays in the composer, as before. A reply left unsent when the command ends is discarded, never run.
  • Timing: total time stays wall-clock. Waiting is measured separately and recorded per command (18s waiting for input (2 prompts)).
  • Everywhere else: /sessions, /resume, the window title and one cross-session notice after 3 s. Older frontends never receive the new message.

Design: docs/design/input-awareness.md.

Tests

Unit tests and real-PTY tests on zsh, Bash and Fish:

  • reads (likely, hidden, single key) and an e2fsck-style yes/no fixture;
  • Ctrl+C and false positives;
  • detach and reattach, and a notice in another window;
  • narrow width with NO_COLOR and Safe glyphs, and the discarded stale reply.

Fish 3.7 builtin readers now signal authenticated terminal ownership from fish_read, including hidden reads and reads inside functions. The marker precedes the prompt and ownership survives detach/reattach. See #356 and docs/development/fish-typeahead.md for the separate version and fixture findings.

Still needs physical validation

  • e2fsck-style prompts, sudo, rm -i and read in Ghostty and macOS Terminal: the indicator, the composer hint, and that nothing is echoed for passwords.
  • Linux: physical terminal behavior of the /proc layer (unit and real-PTY coverage now run on Ubuntu).
  • Narrow widths, NO_COLOR, Safe glyphs and Reduced Motion. Two windows: the notice and /resume.

Stabilization checkpoint — 2026-10-11

Head: 684a3fbc837be97f185cc938f4fb13cbccf1533b. Canonical npm run verify passed on macOS with isolated fixtures, as did direct typechecking. Ubuntu 24.04/Fish 3.7.0 reproduced both published queue read failures; the fixed stack passed inputAwarenessLive, typingAfterRead, queryOrder and commandQueueLive, including hidden-reader reattach.

GitHub CI run 38122464924 succeeded at that published head. Physical Ghostty/Terminal.app QA remains required. Refs #356, #359.

Additive stabilization candidate — 2026-10-11

Local candidate: 86fb4e43b84fbe368fb134763941a635e06ac4de. Published PR head remains 684a3fbc837be97f185cc938f4fb13cbccf1533b. The candidate preserves published ancestry through additive merges; final canonical verification is running and its publication/Actions gate is pending.

The macOS input canonical passed at 9465eb5. The latest Ubuntu workflow run exposed a stale Report assertion, missing VM tools, stat-only timestamp and host-installer assumptions, and a timing failure. Corrections and unchanged-threshold rerun evidence are recorded in #356 and the workflow ledger; a fresh full run is required.

Refs #356, #359. Physical terminal QA remains outstanding. The shared frontend is not frozen; no master/release merge is authorized.

…ing the way it reads

A command that waits for an answer no longer looks like any other running command. InputWatch, beside the PTY
(session service or in-process), decides from positive evidence only:
- the terminal's line settings (stty): echo off with line input is a hidden read; line input off with a prompt
  left open is single-key input (e2fsck's Fix<y>?); both confirmed;
- on Linux, the kernel's report of a blocked read of the terminal (confirmed), or of a block elsewhere (veto);
- otherwise a question-shaped line left open, quiet output and an idle foreground group: "probably".
Programs that own the terminal, builds, sleep, network waits, progress rewrites, statuses and log streams are
never reported.

One state reaches every surface: the live activity rows show the attention line and the open question (no
extra rows, no PTY resize), the window title, /sessions, /resume, and one sticky cross-session notice per
wait. A new input-state message and the "input" notice kind go only to frontends that list the feature.

Hidden and single-key input goes straight to the program key by key (never shown, kept or logged), which
also fixes the stray Enter that answered the next single-key question with its default and the password
drawn in the composer. Keys typed after the question but before detection are handed over; a line reply
left unsent when the command ends is discarded, never run. Waiting time is measured apart from wall-clock
duration, carried with the completing prompt, and recorded on the transcript record.
The pause stood in for a Fish keystroke loss that was the test typing
ahead: a bare /Completed/ matched the previous command's row, so the
next command was typed while read -s still ran. The loss itself is
fixed on the release branch.
…me counted once

CI found it on macOS: after a reattach, the completion row read
'5.7s · 7.6s waiting for input (3 prompts)'. Reattaching resizes the
PTY; the waiting program wakes for SIGWINCH and one look sees it
running, so the wait closed and was found again, restarting at the old
prompt time and counted as a new prompt. A new wait now never starts
before the previous one ended, and a prompt is counted once per stretch
of output or input.

The live tests expect what each platform can know: on Linux (first run
in CI) the kernel confirms an ordinary line read and names the program
python3; macOS infers it and says Python.
@raiseCatError
raiseCatError force-pushed the feature/input-awareness branch from f25c1f3 to 6fd2964 Compare October 10, 2026 05:38
zsh and bash 'read -t' wait in select/pselect6/ppoll, not read(), so the
kernel's report never confirms them; the discard test now accepts either
certainty, and the syscall test pins timed reads as waits.
…wn start

When the evidence for a waiting program lapsed for a look (a slow probe, a resize while a window
detaches) and returned with nothing written or typed in between, the watcher reopened it as a new
wait starting at the lapse. Other windows and /sessions then showed a fresh wait, and macOS CI saw
the start move across a detach. Now the same prompt keeps its start, and the time that closing it
added is taken back, so it is still counted once.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant