Skip to content

fm-send reports a false "Enter swallowed / text not submitted" for OpenCode targets #583

Description

@boyangwang

Problem

Sending a steer to an OpenCode target via fm-send frequently prints:

error: text not submitted to <target> (Enter swallowed; text left in composer; ...)

even though the message DID land and the agent processed it (the pane goes busy immediately after). Observed repeatedly 2026-07-15 (OpenCode 1.18.0, GLM 5.2) both when a main firstmate steered a secondmate and when a secondmate steered its own OpenCode crewmate.

Root cause

fm_tmux_submit_enter_core (bin/fm-tmux-lib.sh) verifies submission via fm_tmux_composer_state, which reads ONLY the single cursor row and returns pending if real content remains. Its busy-footer allowance only matches a busy footer that lands ON the cursor row:

# A busy footer landing on the cursor line is not pending input ...
if [ -n "$stripped" ] && printf '%s' "$stripped" | grep -qiE "$BUSY_REGEX"; then ...

OpenCode renders its busy footer (esc interrupt) on a SEPARATE row from the composer cursor row, and its composer clears asynchronously (slower than the ~0.4s verify), so the verify can read the cursor row as still-pending while the agent is already busy processing the submitted message. After the retries it returns pending -> the false error.

Proposed fix (needs care - shared with the away-mode daemon)

fm_tmux_submit_core is shared: the away-mode daemon consumes its verdict strictly (clears its escalation buffer only on empty), so a naive "busy => empty" in the shared core could make the daemon treat a queued-to-a-busy-pane escalation as delivered and drop it.

Safe direction: scope the busy-aware downgrade to the fm-send consumer (bin/fm-send.sh), NOT the shared core. When the verdict is pending, do a WHOLE-PANE busy check; if the pane is busy (a turn is running), treat the steer as delivered. This leaves the daemon's strict empty policy untouched. A cleaner variant guards on an idle->busy transition (capture busy state before typing) so it never false-positives when steering an already-busy pane.

Offer

Happy to open a PR once the safe variant is picked and reproduced against live OpenCode, if welcome.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions