Skip to content

fix(terminal): recognize Claude drafts with labelled input boxes and the default hidden cursor - #37171

Merged
brennanb2025 merged 4 commits into
mainfrom
brennanb2025/composer-labelled-1101
Nov 2, 2026
Merged

brennanb2025 merged 4 commits into
mainfrom
brennanb2025/composer-labelled-1101

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 18 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​576 $\color{#cf222e}{\Huge{\mathbf{−}}}$​1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​575
Prod 2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​49 $\color{#cf222e}{\Huge{\mathbf{−}}}$​9 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​40

Problem. When Claude Code is waiting for input, Orca reads its input box off the terminal screen so it can keep the user's unsent draft apart from the agent's output and hold back automatic messages (mail from other agents) until the draft is sent or cleared. Two current Claude Code shapes defeated that. A mode label in the box's top border (for example a custom agent name such as ──── ultracode ──) made Orca reject the box. And Claude Code 2.1.316 hides the terminal's real cursor by default and draws its own caret; Orca took any hidden cursor to mean "no input box", so on default settings no Claude draft was recognized at all, labelled or not. The result: orca terminal read --screen and screen/tail reads mixed the draft into output, and automatic messages could be typed into the middle of the user's unsent text.

What changes for users. On default Claude Code settings, with or without a top-border label, a typed but unsent draft is now recognized: screen reads return it separately from output, automatic messages wait for it, and they deliver once it is sent or cleared. This also holds after the user switches to another pane (the agent stops drawing its caret when its pane loses focus, but the draft is still recognized). Existing bare-border Claude, Codex and boxed-composer detection are unchanged. Tracked in #36478 and STA-13041.

How it works. The box is still recognized from its shape: the ❯ prompt with a top border above it (plain, or a rule of at least 8 characters followed by a label) and a plain closing border right where the draft ends. Bottom-border matching stays strict, so a draft line such as -------- coverage -------- stays part of the draft. When the real cursor is hidden, Orca accepts it as the caret only if it is parked inside that complete box, and only if the terminal is on its normal screen or the cell under the cursor is drawn in reverse video (the agent's own caret). Full-screen apps that draw a similar box therefore stay unrecognized. Both facts are read fresh from the terminal on every read; nothing is saved or sent anywhere, and a missing fact counts as unknown. The closing-border check reuses the existing row scan, so a read does no extra row work.

Plan: full review path because this detector feeds a hold on automatic messages. Screen-only box recognition is needed because Orca reads arbitrary terminals, user-started and SSH agents, provider snapshots and IME composition, where no agent identity is available. A design panel chose accepting a parked hidden cursor on the normal screen, plus reverse-video evidence elsewhere, after a recording showed the agent's caret disappears when its pane loses focus. No saved state or wire change.

Intended differences from the common pattern: the closest terminal classifier first verifies the agent was launched by the tool and then requires a visible cursor on the prompt row. Orca keeps structural box evidence because it must work for SSH panes (whose foreground process is ssh), identity-free snapshot replays and renderer-only IME handling, and accepts a hidden parked cursor because current Claude Code hides the real cursor by default. It also reconstructs drafts whose caret sits below the prompt row (wrapped or multi-line input), which a prompt-on-cursor-row rule misses.

Validation: unedited Claude Code 2.1.316 terminal recordings on default settings, labelled and unlabelled, at 100 columns, including a mid-draft caret, an empty box, and the same draft after the pane loses focus; plus native-cursor recordings at 100 and 40 columns (a wrapped draft). Reviewers also replayed the default 40-column recordings and 135 other recorded agent and TUI screens without a false match. Tests run them through the real terminal emulator and cover the terminal read --screen command, screen/tail projection, holding and then delivering an automatic message, provider snapshot round trips (a normal-screen draft is detected, a full-screen lookalike is not), placeholder masking during IME composition, shell ❯ prompts and rule-prefixed draft text. QA is the CLI/orchestration tests row; no layout or app lifecycle changed.

Known limits: the empty Claude Code 2.1.316 custom-agent box draws no placeholder, so placeholder masking is covered by a synthetic case. A snapshot replayed without its screen-mode sequences would be read as the normal screen; Orca's snapshot producers always include them.

@brennanb2025
brennanb2025 marked this pull request as ready for review November 1, 2026 21:41
@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 2 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d121831c-50f1-4fb5-a32f-f140921cb5b2
Commits

Reviewing files that changed from the base of the PR and between d337a1e and 2ae10e4.

Files selected for processing: 20
  • src/cli/terminal-read-screen.test.ts
  • src/main/daemon/terminal-cursor-line-context.test.ts
  • src/main/runtime/__fixtures__/claude-2-1-316-default-labelled-blurred.meta.json
  • src/main/runtime/__fixtures__/claude-2-1-316-default-labelled-blurred.txt
  • src/main/runtime/__fixtures__/claude-2-1-316-default-labelled-middle.meta.json
  • src/main/runtime/__fixtures__/claude-2-1-316-default-labelled-middle.txt
  • src/main/runtime/__fixtures__/claude-2-1-316-default-unlabelled-empty.meta.json
  • src/main/runtime/__fixtures__/claude-2-1-316-default-unlabelled-empty.txt
  • src/main/runtime/__fixtures__/claude-2-1-316-default-unlabelled-middle.meta.json
  • src/main/runtime/__fixtures__/claude-2-1-316-default-unlabelled-middle.txt
  • src/main/runtime/__fixtures__/claude-2-1-316-labelled-empty.meta.json
  • src/main/runtime/__fixtures__/claude-2-1-316-labelled-empty.txt
  • src/main/runtime/__fixtures__/claude-2-1-316-labelled-wrapped.meta.json
  • src/main/runtime/__fixtures__/claude-2-1-316-labelled-wrapped.txt
  • src/main/runtime/orchestration-mailbox-delivery-recheck.test.ts
  • src/main/runtime/terminal-projection.test.ts
  • src/renderer/src/components/terminal-pane/terminal-ime-composer-placeholder-mask.test.ts
  • src/shared/terminal-composer-draft.test.ts
  • src/shared/terminal-composer-draft.ts
  • src/shared/terminal-cursor-line-context.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

…poser

Claude Code's default caret hides the native cursor and parks it at a
painted caret cell, so its composer (labelled or bare) was never detected:
the draft leaked into screen reads and automatic mail was not held.

Accept a hidden cursor on the Claude prompt only inside a complete composer
frame with the cursor right of the glyph. On the normal buffer the parked
cursor suffices (Claude drops the inverse caret paint on focus-out); on an
alternate or unknown buffer the cursor cell must also be inverse. Both facts
are read per call from xterm, only while the cursor is hidden. The closing
rule now comes from the row where the continuation walk stopped, removing
the extra rows-below scan. Codex and boxed composers keep rejecting hidden
cursors.
@brennanb2025 brennanb2025 changed the title Recognize labelled Claude composer frames fix(terminal): recognize Claude drafts with labelled input boxes and the default hidden cursor Nov 1, 2026
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review loop status

The problem. Orca reads Claude Code's input box off the terminal screen so it can keep an unsent draft separate from the agent's output and hold automatic messages (mail from other agents) until the draft is sent or cleared. Two current Claude Code shapes broke that. A label in the box's top border (for example a custom agent name) made Orca reject the box. And Claude Code 2.1.316 hides the real terminal cursor by default and paints its own caret, which Orca read as "no input box", so on default settings no Claude draft was recognized at all. Users saw drafts mixed into orca terminal read --screen output, and automatic messages could be typed into the middle of their unsent text.

The change for users. On default Claude Code settings, labelled or not, an unsent draft is now recognized: screen reads return it separately, automatic messages wait for it and deliver once it is sent or cleared, including after the user switches to another pane. Bare-border Claude, Codex and boxed-composer detection are unchanged.

What the review found and fixed.

  • Round 1 (three reviewers) confirmed the labelled-border fix but found that on default settings the box was still never recognized, on main and on this branch, because the hidden cursor was rejected first. That was brought into scope and settled by a three-model design panel: a hidden cursor counts as the caret only when it is parked inside a complete input box, and on a full-screen app's screen (or an unknown one) only when the cell under it is also painted as the agent's caret. A recording of the same draft after the pane loses focus showed the agent stops painting its caret but keeps the cursor parked, which is why the normal screen does not require the paint.
  • The extra per-read scan for the box's closing border was removed; the existing row scan now provides it.
  • CI caught a 40-character folder name in one recording's status line that the transcript secret scan treats as a token; it was scrubbed to the same length (not a credential).

Deferred (no failing input found).

  • A labelled top border needs at least 8 rule characters; a narrower one would not be recognized. No real capture shows a shorter one.
  • A made-up shell screen with a -------- coverage -------- row above a visible ❯ git status prompt and a plain rule below it reads as a draft. No ordinary shell reproduction.
  • A full-screen app snapshot replayed without its screen-mode sequences would be read as the normal screen. Orca's snapshot producers always include them.

Verified. Unedited Claude Code 2.1.316 recordings on default settings (labelled and unlabelled, mid-draft caret, empty box, draft after focus loss) and native-cursor recordings at 100 and 40 columns, run through the real terminal emulator: the terminal read --screen command, screen/tail projection, holding and then delivering an automatic message, provider snapshot round trips, IME placeholder masking, shell lookalikes and rule-prefixed drafts. Reviewers replayed 135 other recorded agent and full-screen app screens (about 23,000 redraws) with no false match. QA is the CLI/orchestration tests row; no app run was needed because nothing in layout, lifecycle or processes changed. Not verified: Claude Code versions other than 2.1.310 and 2.1.316.

For Brennan's review.

Intended differences from the common pattern are listed in the PR description. Main was synced once before the final CI run (merge d388986827b); the only commit since is the fixture scrub above.

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