Skip to content

fix(terminal): refuse to submit a CLI prompt over an unsent composer draft - #16290

Open
luisgonzaleznf wants to merge 17 commits into
stablyai:mainfrom
luisgonzaleznf:fix/terminal-send-pending-draft
Open

luisgonzaleznf wants to merge 17 commits into
stablyai:mainfrom
luisgonzaleznf:fix/terminal-send-pending-draft

Conversation

@luisgonzaleznf

@luisgonzaleznf luisgonzaleznf commented Aug 24, 2026 •

Copy link
Copy Markdown

ELI5

When one agent messages another terminal with orca terminal send --text ... --enter, Orca pastes the text and presses Enter without looking at the target's input box. If the user had a half-typed prompt there, both get sent as one prompt. Now Orca looks first: if there is unsent text, it refuses and tells the sender what the draft is. --force keeps the old behavior on purpose.

What Changed

  • OrcaRuntimeService.writeTerminalAgentPrompt (the Claude Code / Codex paste-and-submit path used by terminal send --text --enter — now in rpc/methods/terminal/terminal-send-method.ts after the module split on main — and by orchestration dispatch/inject/worker preambles) now checks the rendered composer inside the serialized submission, right after the existing permission check and before any byte is written. Evidence is readVisibleTerminalState — the same host-side emulator state terminal read --screen returns — so it works for local, daemon, SSH-provider and orca serve hosts.
  • Detection is Fix terminal reads that flatten composer drafts into output #16711's detectTerminalComposerDraft — the same draft that terminal.read now exposes — read from readVisibleTerminalState(ptyId).draft, so the guard shares one projection with reads (headless emulator, provider snapshot, and renderer-serializer fallback). No detector of its own any more.
  • One fix to that shared detector, found by replaying a real Claude Code 2.1.246 screen: Claude writes a mode label into the composer's top rule (──── ultracode ─), which COMPOSER_FRAME_LINE rejected, so any such session lost draft detection for both read and this guard. The frame regex now allows a label between rule characters; the capture is the regression test.
  • terminal.send RPC maps the refusal to { accepted: false, bytesWritten: 0, refusedReason: 'pending-input', pendingInput: <draft> } and accepts an optional allowPendingInput: true. agent_prompt_pending_input is registered as a passthrough error code for the orchestration callers, which fail the dispatch instead of corrupting the draft.
  • CLI: terminal send --force → allowPendingInput; formatTerminalSend prints the refusal and the draft instead of Sent 0 bytes; refusals exit non-zero. orca-cli skill guide documents the rule.
  • The guard uses the freshly detected foreground agent (falling back to the launch agent), and retries a discarded provider frame (output advanced during the snapshot — i.e. the user is typing) up to 3× before falling open.

Related (no overlap found)

Follow-ups (not in this PR)

  • Concede on a repeated identical attempt. Default is reject-always; the only bypass is the explicit --force. A softer variant — refuse the first attempt, then concede when the same sender retries the same text against the same unchanged draft — would let unattended agent flows recover without a flag. Happy to do that in a separate PR if you want it.
  • Extend the guard to the other submitting paths (desktop review-note two-phase send, plugin terminal.sendText, agent-teams tmux dispatch, --text then --enter in separate calls) and to more agents once their composer glyphs are pinned.

Why

An agent in terminal A cannot see terminal B's composer, so it cannot know a draft is pending. Verified on Orca 1.4.188 (macOS) with both agents: draft Refactor the login page so that it + sent Status update from the other terminal: the build is green. → the target ran Refactor the login page so that itStatus update from the other terminal: the build is green. as one prompt. The contract that is violated — "this text lands as its own prompt" — lives in the host runtime's agent-prompt submission, so the guard lives there and every sender inherits it. The refusal is structured (Rule 1 of docs/reference/remote-wire-compatibility.md: new optional fields/params only): old CLIs against a new host see accepted: false; new CLIs against an old host keep today's appending behavior.

Linked Issue

Fixes #16289

Visual Proof

CLI behavior, no UI change. Screen reads from the reproduction (Claude Code 2.1.241; Codex reproduces identically with ›):

Before — draft present, then orca terminal send --terminal <B> --text "Status update from the other terminal: the build is green." --enter:

❯ Refactor the login page so that it                      ← unsent draft in B
...
{"send": {"accepted": true, "bytesWritten": 59}}          ← sender sees success
❯ Refactor the login page so that itStatus update from the other terminal: the build is green.
· Drizzling…                                              ← B runs the glued prompt

After (same state):

Refused: terminal term_… has unsent input in its composer: "Refactor the login page so that it"
Wait for the user to send or clear it, or re-run with --force to append to it.
(exit 1;  --json → "accepted": false, "refusedReason": "pending-input", "pendingInput": "Refactor the login page so that it")
❯ Refactor the login page so that it                      ← draft untouched

Testing

  • src/main/runtime/rpc/terminal-send-pending-draft.integration.test.ts — real OrcaRuntimeService fed the rendered frame via onPtyData, foreground claude: refuses and reports the draft (fails before the fix: the paste is written); still refuses with the caret moved home (\x1b[3G); pastes into an empty composer, including one showing a dim placeholder, and into a trust dialog's option list without reading it as a draft — each proceeds-case asserts accepted: true, the pasted payload, and the Enter write; appends with allowPendingInput.
  • src/main/daemon/terminal-composer-draft-captures.test.ts — replays four raw PTY captures (__fixtures__/: Claude Code 2.1.246 and Codex 0.149.1, composer-with-draft and permission/trust dialog) through the real HeadlessEmulator into Fix terminal reads that flatten composer drafts into output #16711's detector: both drafts detected, both dialogs rejected via the hidden cursor. The Claude draft capture is the one that exposed the frame-label bug.
  • CLI: --force → allowPendingInput, refusal output + exit code, formatTerminalSend; errors.test.ts passthrough code.
  • Ran src/main/runtime/rpc, src/shared, src/cli, src/main/daemon, orca-runtime.test.ts (9.3k tests), typecheck:tsc:node/cli, oxlint, check:code-quality:changed, check:max-lines-ratchet, verify:bundled-skill-guides. tests/e2e/cross-version-wire/cross-version-terminal-wire.unit.test.ts fails on outputPause capability negotiation identically with this branch's files reverted to origin/main (pre-existing in this environment).
  • Reproduced the bug live on macOS against the installed 1.4.188 for Claude Code and Codex. The fix itself was verified through the runtime integration tests, not against a dev build of the app; not run on Linux, Windows, or SSH hosts.

Scope not covered (same append behavior as today): mobile native-chat sends (already prefix Ctrl+U), the desktop review-note two-phase send, plugin terminal.sendText, agent-teams tmux dispatch, a separate --text then --enter, and agents other than Claude Code/Codex or plain shells. On Windows ConPTY a draft with the caret moved to column 1 is not detected (style signal disabled there).

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

AI Disclosure

Investigated, implemented, and tested with Claude Code (Claude Fable 5) under my direction; the reproduction and review were mine.

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Wire: only optional additions (allowPendingInput param; refusedReason: 'pending-input', pendingInput fields). The renderer's note-send treats an unknown refusedReason as not-writable; mobile reads only accepted.
  • SSH: the check runs on the runtime that owns the PTY, using its own mirror of the output stream; loss of screen evidence proceeds (unknown never blocks a write).
  • Cost: guarded sends to renderer-backed panes may add one provider snapshot read (750 ms timeout, 1 s retry backoff) before the paste.

Checklist

  • This PR is small and focused
  • I explained what changed and why (including ELI5)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

Generated with Claude Code | Created with /quick-pr

…draft

`orca terminal send --text ... --enter` into a Claude Code or Codex
session pastes at the composer caret and presses Enter without looking
at the composer. When the user has a half-typed draft there, the draft
and the agent's message are submitted as one glued prompt.

The host runtime now reads the rendered composer (the same emulator
state `terminal read --screen` returns) inside the serialized
agent-prompt submission, before any byte is written. Typed text left of
the caret, or undimmed text right of it (both agents draw their
placeholder dim), counts as a draft and the send is refused with
`accepted: false`, `refusedReason: 'pending-input'` and the draft in
`pendingInput`. An unreadable screen never blocks a send, and native
ConPTY panes skip the style signal because SGR-dim fidelity there is
unverified. `--force` (RPC `allowPendingInput`) opts back into appending.
Orchestration dispatch/inject/worker preambles share the guard and fail
with `agent_prompt_pending_input` instead of corrupting the draft.

Fixes stablyai#16289

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Understand this PR’s impact

Explore downstream dependencies and potential security impact with Blast Radius.

View blast radius →

Important

Review skipped

We couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting @coderabbitai full review.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: e679c5f2-2fad-486f-9d48-1f9152067d76

📥 Commits

Reviewing files that changed from the base of the PR and between 893909e and 324bc51.

📒 Files selected for processing (4)
  • src/main/runtime/orca-runtime-write-terminal-agent-prompt.ts
  • src/main/runtime/rpc/terminal-send-pending-draft.integration.test.ts
  • src/shared/terminal-composer-draft.test.ts
  • src/shared/terminal-composer-draft.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/shared/terminal-composer-draft.ts
  • src/main/runtime/rpc/terminal-send-pending-draft.integration.test.ts
  • src/shared/terminal-composer-draft.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

terminal.send now checks supported Claude Code and Codex composers for unsent input before writing. It returns a structured pending-input refusal with the draft. The CLI exposes --force to append intentionally and reports refusal guidance. Runtime error passthrough, shared contracts, help text, documentation, fixtures, and integration tests were updated.

Priority: ➖ Normal

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.89% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 32 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: refusing CLI prompt submission when an unsent composer draft exists.
Description check ✅ Passed The description covers the required sections, linked issue, user-facing behavior, implementation, testing, compatibility, scope, and checklist. The Visual Proof section provides before-and-after CLI b…
Linked Issues check ✅ Passed Issue [#16289] requires terminal send --enter to avoid submitting an existing composer draft and to refuse or report the send. The runtime reads the rendered composer before writing and raises `Agen…
Out of Scope Changes check ✅ Passed The changes remain connected to [#16289]. Detection logic, runtime guards, RPC and CLI contracts, refusal formatting, help text, fixtures, and tests implement or document draft protection. The visual-…

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2c5383d9-5272-4650-a138-30ec404d6f37

📥 Commits

Reviewing files that changed from the base of the PR and between 9d87532 and d387cf3.

📒 Files selected for processing (19)
  • skill-guides/orca-cli.md
  • src/cli/bundled-skill-guides.ts
  • src/cli/handlers/terminal.test.ts
  • src/cli/handlers/terminal.ts
  • src/cli/help.ts
  • src/cli/specs/core.ts
  • src/cli/terminal-format.test.ts
  • src/cli/terminal-format.ts
  • src/main/daemon/headless-emulator.test.ts
  • src/main/daemon/headless-emulator.ts
  • src/main/daemon/terminal-cursor-line-context.ts
  • src/main/runtime/orca-runtime.ts
  • src/main/runtime/rpc/errors.test.ts
  • src/main/runtime/rpc/errors.ts
  • src/main/runtime/rpc/methods/terminal.ts
  • src/main/runtime/rpc/terminal-send-pending-draft.integration.test.ts
  • src/shared/agent-composer-pending-input.test.ts
  • src/shared/agent-composer-pending-input.ts
  • src/shared/runtime-types.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread src/cli/terminal-format.ts Outdated
Comment thread src/shared/agent-composer-pending-input.ts Outdated
…g drafts

Run the composer check after the permission check so a trust/permission
dialog whose selected option also starts with `❯` still yields
agent_prompt_blocked, and never treat a numbered option row as the
composer. Cross blank rows so multi-paragraph drafts count, stop at
transcript markers instead, and drop dim placeholder cells from every
row, not just right of the caret. Prefer the detected foreground agent
over the launch agent after an agent swap, and retry a provider frame
discarded by the user's own keystroke echo before falling open.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 84200b5b-3732-4292-a7b6-d0e28e2b88e2

📥 Commits

Reviewing files that changed from the base of the PR and between d387cf3 and 0c33381.

📒 Files selected for processing (6)
  • src/main/daemon/headless-emulator.test.ts
  • src/main/daemon/terminal-cursor-line-context.ts
  • src/main/runtime/orca-runtime.ts
  • src/main/runtime/rpc/terminal-send-pending-draft.integration.test.ts
  • src/shared/agent-composer-pending-input.test.ts
  • src/shared/agent-composer-pending-input.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.

Comment thread src/main/runtime/rpc/terminal-send-pending-draft.integration.test.ts Outdated
Comment thread src/shared/agent-composer-pending-input.ts Outdated
luisgonzaleznf and others added 2 commits August 25, 2026 13:03
Re-apply the terminal.send refusal mapping and allowPendingInput param onto
the split rpc/methods/terminal/* modules, and the RuntimeTerminalSend fields
onto runtime-terminal-contracts.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A draft may legitimately start with `1.` or continue with a bullet, so a
numbered glyph row is a dialog only when a numbered option row sits
directly under it (the cursor context now carries two rows below), and a
row ends the draft only when its content starts at column 0 — both agents
indent every continuation row. The proceeds-cases in the integration test
now assert the accepted response and the pasted payload.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

luisgonzaleznf and others added 2 commits August 25, 2026 13:05
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

Regenerate the bundled skill guides from the merged orca-cli.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 87cd84d2-6e85-4e0d-8dae-9b74bcf14d18

📥 Commits

Reviewing files that changed from the base of the PR and between 885106b and 1c5cba7.

📒 Files selected for processing (20)
  • skill-guides/orca-cli.md
  • src/cli/bundled-skill-guides.ts
  • src/cli/handlers/terminal.test.ts
  • src/cli/handlers/terminal.ts
  • src/cli/help.ts
  • src/cli/specs/core.ts
  • src/cli/terminal-format.test.ts
  • src/cli/terminal-format.ts
  • src/main/daemon/headless-emulator.test.ts
  • src/main/daemon/headless-emulator.ts
  • src/main/daemon/terminal-cursor-line-context.ts
  • src/main/runtime/orca-runtime.ts
  • src/main/runtime/rpc/errors.test.ts
  • src/main/runtime/rpc/errors.ts
  • src/main/runtime/rpc/methods/terminal/terminal-send-method.ts
  • src/main/runtime/rpc/methods/terminal/unary-schemas.ts
  • src/main/runtime/rpc/terminal-send-pending-draft.integration.test.ts
  • src/shared/agent-composer-pending-input.test.ts
  • src/shared/agent-composer-pending-input.ts
  • src/shared/runtime-terminal-contracts.ts
🚧 Files skipped from review as they are similar to previous changes (19)
  • src/cli/specs/core.ts
  • src/main/runtime/rpc/errors.test.ts
  • src/cli/handlers/terminal.ts
  • src/main/runtime/rpc/methods/terminal/terminal-send-method.ts
  • src/main/runtime/rpc/methods/terminal/unary-schemas.ts
  • src/cli/handlers/terminal.test.ts
  • src/cli/terminal-format.test.ts
  • src/main/runtime/rpc/errors.ts
  • src/shared/runtime-terminal-contracts.ts
  • src/cli/help.ts
  • src/cli/bundled-skill-guides.ts
  • src/cli/terminal-format.ts
  • src/main/daemon/headless-emulator.ts
  • src/main/daemon/headless-emulator.test.ts
  • skill-guides/orca-cli.md
  • src/main/daemon/terminal-cursor-line-context.ts
  • src/shared/agent-composer-pending-input.test.ts
  • src/main/runtime/orca-runtime.ts
  • src/main/runtime/rpc/terminal-send-pending-draft.integration.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread src/shared/agent-composer-pending-input.ts Outdated
…ow text

A numbered-list draft (`❯ 1. First step` / `  2. Second step`) has the
same row shape as a trust or permission dialog, so any text rule either
misses that draft or misreads the dialog. Captured from Claude Code
2.1.246 and Codex 0.149.1: both agents show the terminal cursor whenever
the composer has a caret and hide it (DECTCEM) while a dialog is open —
evidence a draft cannot fake. The detector now answers null on a hidden
cursor and drops the option-list heuristic and the rows-below lookahead.
The four raw captures ship as fixtures and replay through the real
emulator in terminal-cursor-line-context.test.ts. Ink paints gaps as
empty cells, so typed text now renders those as spaces instead of
losing them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keep the pending-draft check ahead of the paste-ingest sizing (stablyai#16586)
that now feeds createAgentPromptRenderGate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
luisgonzaleznf and others added 2 commits August 28, 2026 17:19
Drop this branch's composer-draft detector and cursor-line-context module in
favour of stablyai#16711's detectTerminalComposerDraft: the guard now refuses on the
same draft projection terminal.read exposes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rafts

Claude Code writes a mode label into the composer's top rule
(`──── ultracode ─`), which COMPOSER_FRAME_LINE rejected, so every such
session lost draft detection for terminal.read and for the send guard.
The four raw captures from Claude Code 2.1.246 and Codex 0.149.1 now
replay through the real emulator as regression coverage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@luisgonzaleznf

Copy link
Copy Markdown
Author

Reconciled with #16711 (merged yesterday), which added the same terminal-cursor-line-context.ts and a shared detectTerminalComposerDraft for terminal.read. This branch no longer carries a detector of its own: the send guard now refuses on readVisibleTerminalState(ptyId).draft, the same projection reads expose, so both surfaces agree by construction. Net diff shrank accordingly.

Replaying real captures through that detector turned up one gap, fixed in d80e6cc: Claude Code writes a mode label into the composer's top rule (──── ultracode ─), which COMPOSER_FRAME_LINE rejected — sessions with a label lost draft detection for read and for this guard alike. The regex now allows a label between rule characters, and the four raw captures (Claude Code 2.1.246 / Codex 0.149.1, draft and dialog) ship as fixtures under src/main/daemon/__fixtures__/ with terminal-composer-draft-captures.test.ts.

…ding-draft

# Conflicts:
#	src/cli/bundled-skill-guides.ts
#	src/cli/handlers/terminal.ts
#	src/cli/root-help-text-secondary.ts
#	src/cli/specs/core.ts
#	src/cli/terminal-format.test.ts
#	src/cli/terminal-format.ts
#	src/main/runtime/orca-runtime.ts
#	src/main/runtime/rpc/methods/terminal/terminal-send-method.ts
#	src/main/runtime/rpc/methods/terminal/unary-schemas.ts
#	src/shared/runtime-terminal-contracts.ts
#	src/shared/terminal-composer-draft.ts
@luisgonzaleznf

Copy link
Copy Markdown
Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed and review finished.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 3c7b4374-2174-4dce-9f79-e9146e82724d

📥 Commits

Reviewing files that changed from the base of the PR and between 8621757 and 893909e.

📒 Files selected for processing (21)
  • skill-guides/orca-cli.md
  • src/cli/bundled-skill-guides.ts
  • src/cli/handlers/terminal-send.ts
  • src/cli/handlers/terminal.test.ts
  • src/cli/help.ts
  • src/cli/root-help-text-secondary.ts
  • src/cli/specs/terminal-send.ts
  • src/cli/terminal-format.test.ts
  • src/cli/terminal-format.ts
  • src/main/daemon/terminal-composer-draft-captures.test.ts
  • src/main/runtime/orca-runtime-write-terminal-agent-prompt.ts
  • src/main/runtime/rpc/errors.test.ts
  • src/main/runtime/rpc/errors.ts
  • src/main/runtime/rpc/methods/terminal/terminal-send-method.ts
  • src/main/runtime/runtime-terminal-contracts.ts
  • src/shared/agent-prompt-pending-input-error.ts
  • src/shared/rpc-contract/terminal-unary-params.ts
  • src/shared/runtime-terminal-contracts.ts
  • src/shared/runtime-terminal-visual-layout.ts
  • src/shared/terminal-composer-draft.test.ts
  • src/shared/terminal-composer-draft.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • skill-guides/orca-cli.md

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.

Comment thread src/main/runtime/orca-runtime-write-terminal-agent-prompt.ts
Comment thread src/shared/terminal-composer-draft.ts
@luisgonzaleznf

Copy link
Copy Markdown
Author

Merge conflicts resolved — now MERGEABLE

Merged origin/main into this branch (merge 893909e2f5). It was 2093 commits behind.

Please re-read the diff rather than the original review — the merge changed more than conflict markers, and two of the changes are deliberate and reviewable in their own right (both flagged below).

The 11 conflicts

Upstream split the 42,418-line orca-runtime.ts into ~200 mixin modules, so it is now a 58-line re-export shell. Most of this PR's additions had to be re-homed rather than merged in place:

Area Resolution
orca-runtime.ts Took main's re-export shell; the PR's 55 lines moved into orca-runtime-write-terminal-agent-prompt.ts, which now owns the write path. Verified readVisibleTerminalState, ptysById and providerSnapshotPreferredPtys all resolve on ancestors in the 134-deep mixin chain.
terminal-send-method.ts Kept both option spreads and both catch arms; this PR's arm first, main's acceptedPromptCheckpoint fallback intact.
unary-schemas.ts, cli/handlers/terminal.ts, cli/specs/core.ts Took main's extractions; allowPendingInput / --force re-homed into the new per-command modules.
terminal-format.ts (+ test) Took main's rewritten formatTerminalSend, grafted the pending-input arm ahead of the generic refusal; tests moved into main's describe and updated to its new refusal wording.
terminal-composer-draft.ts, runtime-terminal-contracts.ts, root-help-text-secondary.ts Both sides kept — main's new types/flags plus this PR's.
bundled-skill-guides.ts Regenerated, never hand-merged.

Guard ordering is unchanged in substance: the composer-draft check still runs after the permission check, and now sits after agentSessionPtyWriteGate.assertAdmitted so the cheap lease gate refuses before paying for a screen read.

Two changes that are NOT conflict resolution — please review these

  1. max-lines refactors. Two files sit at exactly the 300-line limit on main, so any addition trips the gate, and AGENTS.md forbids a max-lines disable. Both were shrunk instead:
    • terminal-send-method.ts — hoisted the repeated { handle, accepted: false, bytesWritten: 0 } payload into one refused const, collapsing five catch arms to one-liners. Arm order and behaviour unchanged.
    • runtime-terminal-contracts.ts — moved the six RuntimeTerminalVisual* layout types to a new src/shared/runtime-terminal-visual-layout.ts, re-exported from the barrel. Pure type move; no importer changed.
  2. Two test fixes. The PR's tests stubbed the client as { call }, but main's handler now runs a prompt-delivery preflight — they failed with TypeError: client.getCliStatus is not a function. Switched to main's own promptClient(call, true) helper plus the prompt receipt it requires.

Validation

pnpm tc clean · repo-wide oxlint clean · verify:rpc-params-catalog, verify:bundled-skill-guides, verify:skill-bundle-manifest, check:max-lines-ratchet, check:ts-nocheck-ratchet all pass. Vitest: 1068 files / 11359 tests across src/main/runtime/rpc + src/shared, 129 files / 1328 tests across src/cli, plus terminal-send-pending-draft.integration.test.ts 6/6 end-to-end through the re-homed guard.

No failure was written off as pre-existing — the only two were the real fallout above, and they are fixed.

One note for anyone merging older branches in this repo

git merge first refused with "unrelated histories". The checkout is a shallow clone (boundary ≈ 2026-09-08), so origin/main looks parentless and shares no ancestor with a branch cut in August. git fetch origin main --shallow-since=2026-06-01 fixes it. The pre-deepen "951 commits behind" is an artifact — the real number was 2093.

CodeRabbit had been paused on this branch since August (its status comment said so, and the 28 Aug push drew no review), so it never saw the last few pushes. Resumed it — a fresh review should land on this merge.

luisgonzaleznf and others added 2 commits September 21, 2026 18:47
…draft before the paste

Two review findings on the composer-draft send guard.

A labelled top rule is no longer evidence on its own. `──────── build ─` above an
ordinary `❯ ls` shell prompt matched the widened frame regex, so the detector read
`ls` as composer input and the guard could refuse a legitimate send. A labelled rule
now only counts as the composer's top border when the closing border is below the
prompt — the box an agent draws, which a section divider in a shell transcript does
not have. A plain rule still stands alone, so main's behaviour is unchanged.

The composer is also re-read after `beforeWrite`. The write path already
re-validates request activity, generation, permission state and PTY admission across
that hook; composer state is an invariant of the same standing, and it was the only
one not rechecked, so a draft started during the hook would have been pasted onto.

Both are covered by tests that fail without the change. The real captured Claude
Code 2.1.246 screen still reads its draft, which is what rules out over-tightening.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ding-draft

# Conflicts:
#	src/cli/bundled-skill-guides.ts
#	src/cli/terminal-format.ts
#	src/main/runtime/orca-runtime-write-terminal-agent-prompt.ts
#	src/main/runtime/rpc/methods/terminal/terminal-send-method.ts
#	src/shared/runtime-terminal-contracts.ts
luisgonzaleznf and others added 3 commits October 7, 2026 20:01
… writes

`RuntimeAgentPromptWriteOptions.inputKind` became required on main. The
draft-during-beforeWrite regression test drives `sendTerminalAgentPrompt`
directly, so it states the `driving` kind `terminal.send` uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ding-draft

# Conflicts:
#	src/cli/bundled-skill-guides.ts

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.

[Bug]: terminal send --enter appends to the target's unsent draft and submits both

1 participant