Skip to content

Submit sent input into agent TUIs instead of their composer - #1190

Merged
edwin-zvs merged 1 commit into
mainfrom
worktree-send-input-harness-framing
Aug 3, 2026
Merged

Submit sent input into agent TUIs instead of their composer#1190
edwin-zvs merged 1 commit into
mainfrom
worktree-send-input-harness-framing

Conversation

@edwin-zvs

Copy link
Copy Markdown
Contributor

Problem

construct send and the MCP input tool both arrive as the SESSION_INPUT IPC method, which called send_input. That writes the text LF-terminated, and LF is not the byte a terminal's Enter key sends — in a crossterm TUI in raw mode it's Ctrl+J, "insert a newline".

Hit live while debugging #1189: a prompt sent to a codex session rendered in the pane, returned {"ok":true}, and then nothing happened. No request reached the router; the text was sitting in the composer. It took a manual CR via construct_send_keys to submit it. From the caller's side this is indistinguishable from a session that simply chose not to respond.

Fix

The daemon already models this — session_input_delivery classifies each harness, and interactive_codex_takes_the_typed_submit_framing already asserts codex needs typed submit. The service ingress path was fixed for exactly this reason and says so:

Not send_input: an interactive service session is a live agent TUI, and LF-terminated input lands in its composer without submitting.

The IPC dispatch never got the same treatment. Routing it through deliver_user_text gives:

harness delivery
codex, claude, antigravity, grok, hermes (PTY) bracketed paste + Enter
smith, shell (PTY line editor) CR-terminated
headless send_input, unchanged

Transcript recording is unaffected: deliver_user_text records only for harnesses that don't mirror their own transcript, and codex reports its turn itself.

Test

The regression is invisible from the caller's side — the old path returned Ok — so asserting against deliver_user_text would test the helper, not the wiring. The test drives a real SESSION_INPUT request through dispatch against a codex-classified session with a mock PTY adapter, and asserts the recorded bytes contain the bracketed paste followed by a submit Enter.

Verified it fails against the old wiring:

expected a bracketed paste then a submit Enter, got ""

dispatch and SubCmd become pub(crate) so the test can reach that entry point.

cargo test -p construct-daemon: 641 passed, 0 failed. Added lines are fmt-clean; both files carry pre-existing fmt drift on main that this PR leaves alone.

Binary

crates/daemon only — ships in construct:

/Users/moon/construct/.claude/worktrees/send-input-harness-framing/target/debug/construct

No visual change to record: the fix is that a send now starts a turn at all.

`construct send` and the MCP input tool both arrive as the SESSION_INPUT
IPC method, which called `send_input`. That writes the text LF-terminated,
and LF is not the byte a terminal's Enter key sends — in a crossterm TUI
in raw mode it is Ctrl+J, "insert a newline". Against codex the message
landed in the composer and sat there unsubmitted, so the send looked like
a silent no-op: the caller got an Ok, the text was visible in the pane,
and no turn ever started.

The daemon already knows the framing each harness submits on, and the
service ingress path was fixed to use it. Route the dispatch through
`deliver_user_text` too, so PTY-backed agent TUIs get bracketed paste
plus Enter, PTY line editors get CR, and headless harnesses keep going
through `send_input` unchanged.

The regression is invisible from the caller's side, so the test drives a
real SESSION_INPUT request through `dispatch` rather than asserting
against the manager helper it happens to delegate to today; it fails
against the old wiring. `dispatch` and `SubCmd` become `pub(crate)` to
let the test reach that entry point.
@edwin-zvs
edwin-zvs merged commit ed148ac into main Aug 3, 2026
1 check passed
@edwin-zvs
edwin-zvs deleted the worktree-send-input-harness-framing branch August 3, 2026 13:38
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