Skip to content

fix(cli): preserve terminal output during capability approval - #1527

Open
wswsmao wants to merge 1 commit into
nolabs-ai:mainfrom
wswsmao:fixtty
Open

fix(cli): preserve terminal output during capability approval#1527
wswsmao wants to merge 1 commit into
nolabs-ai:mainfrom
wswsmao:fixtty

Conversation

@wswsmao

@wswsmao wswsmao commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Linked Issue

Closes #1526

Summary

Fix terminal output corruption when an interactive capability approval prompt is shown while the supervised process is attached through a PTY.

Before this change, pending child-process output in the PTY could be relayed after the supervisor printed the approval prompt. Restoring terminal modes also did not normalize the cursor position or clear the parent-owned output area before displaying the prompt.

This change:

  • drains pending PTY output before displaying the approval prompt;
  • detects terminal disconnection while draining output;
  • restores the terminal state before prompting;
  • preserves child-process output that does not end with a newline;
  • normalizes the parent terminal output area before rendering the prompt.

The existing capability approval behavior is unchanged. The supervisor still waits for the user to enter y or n.

No sandbox policy, Landlock behavior, seccomp-notify authorization semantics, or capability resolution logic was changed.

Agent Disclosure

This change was implemented with assistance from an AI coding agent.

The following repository guidance and files were consulted:

  • AGENTS.md
  • CLAUDE.md
  • CONTRIBUTING.md
  • .github/ISSUE_TEMPLATE/bug_report.yml
  • .github/pull_request_template.md
  • crates/nono-cli/src/pty_proxy.rs
  • crates/nono-cli/src/timeouts.rs
  • crates/nono-cli/src/exec_strategy.rs
  • crates/nono-cli/src/terminal_approval.rs
  • crates/nono-cli/src/exec_strategy/supervisor_linux.rs

The implementation follows the repository requirements for error handling, terminal safety, security-sensitive behavior, formatting, testing, and DCO sign-off.

Test Plan

The following checks passed:

  • cargo test -p nono-cli
    • 1845 tests passed
  • cargo clippy -p nono-cli --all-targets -- -D warnings
  • cargo fmt --all -- --check
  • cargo check -p nono-cli
  • git diff --check

Manual verification was also performed using a real interactive PTY with automated y/n input. The capability approval prompt was displayed at a stable terminal position and the process completed normally.

The non-interactive stdin=/dev/null path was retained as a regression comparison.

Checklist

  • An issue exists and is linked above
  • All commits are signed-off, using DCO
  • All new code follows the project's coding standards (CLAUDE.md) and is covered by tests
  • Public-facing changes are paired with documentation updates (not applicable: no public API or user-facing configuration change)

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions github-actions Bot added bug Something isn't working nono-cli size/small labels Jul 29, 2026
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

PR Review Summary

Size

Metric Value
Lines added +94
Lines removed -6
Total changed 100
Classification Medium (50–300 lines)

Affected crates

  • crates/nono-cli — CLI changes. Verify argument parsing, flag documentation, and UX behaviour across supported platforms.

Blast radius — Contained

This PR touches: source code


Updated automatically on each push to this PR.

@nogent-nolabs-ai nogent-nolabs-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nogent code review

1 performance/UX concern.

Automated code + security review. CI already covers clippy, rustfmt, tests, cargo-audit and commit-lint.

Comment thread crates/nono-cli/src/pty_proxy.rs Outdated
return false;
}

self.drain_master_output(timeouts::pty_drain_timeout());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

[MEDIUM · perf] Calling self.drain_master_output(timeouts::pty_drain_timeout()) will block the parent process for up to 100 milliseconds if the PTY master is quiet/idle (which is the common case when pausing the terminal for a prompt, because the child process is blocked). This introduces a noticeable 100ms delay/stutter before displaying the interactive prompt to the user. To avoid this latency, perform a non-blocking check first (e.g. using libc::poll with a 0 timeout) to see if there is actually any pending data to relay. If there is no data, proceed immediately without waiting. Alternatively, define a much smaller quiet timeout (e.g., 5-10ms) specifically for prompt pausing rather than reusing timeouts::pty_drain_timeout().

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the contibution @wswsmao - the above from nogent is actually quite on point. As a TUI or another thread emitting output at intervals below 100 ms could keep this loop running indefinitely, preventing the approval prompt from ever appearing. SHould be pretty simple to amend though, either use a nonblocking drain or a fixed absolute deadline that incoming data cannot extend. rest of the PR is looking good 🥇

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review — agreed.

I replaced the post-exit quiet drain on the approval path with a
prompt-specific zero-timeout poll that relays one immediately ready PTY output
chunk. It does not wait for future output, and a continuously writing process
cannot extend the approval-prompt delay.

I added regression coverage for pending output and the single-chunk bound, then
rebased the patch on current main (0f08b477).

I also verified the interactive PTY path with
NONO_PTY_DRAIN_TIMEOUT=5000; the first approval prompt appeared in 363ms,
confirming that the prompt path no longer reuses the post-exit quiet timeout.

Environment:

  • nono 0.71.0
  • OpenCloudOS Stream 23 (release 2410), x86_64
  • Linux 6.6.119-49.20.tl4.x86_64
  • glibc 2.38
  • rustc/cargo 1.95.0
  • Landlock V3

Signed-off-by: abushwang <abushwang@tencent.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working nono-cli size/medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Interactive capability approval corrupts terminal output when stdin is a TTY

2 participants