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.
Problem
Sending a steer to an OpenCode target via
fm-sendfrequently prints: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 viafm_tmux_composer_state, which reads ONLY the single cursor row and returnspendingif real content remains. Its busy-footer allowance only matches a busy footer that lands ON the cursor row: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 returnspending-> the false error.Proposed fix (needs care - shared with the away-mode daemon)
fm_tmux_submit_coreis shared: the away-mode daemon consumes its verdict strictly (clears its escalation buffer only onempty), 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-sendconsumer (bin/fm-send.sh), NOT the shared core. When the verdict ispending, 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 strictemptypolicy 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.