Skip to content

Terminal: no backend-neutral command-handoff signal; queued drains can consume feedback before an unsubmitted command reaches the shell #2278

Open

Description

@myk1yt

Problem

The terminal backends have no backend-neutral "command handed to the shell" signal. ExecuteCommandTool's agent-timeout racer can return the still-running result ([false, "Command is still running...", true]) while Terminal.runCommand has only deferred the submission: Terminal.ts (L124-131) awaits shell integration and only then calls process.run(command). When the racer wins, the true flows into shouldDrainQueuedMessages, so queued user messages can be consumed toward the next model turn before the command text ever reached the shell.

Verified at 95a13d719 (PR #1711 head) during that PR's review (thread 4422477230; review reply 4422646288 promised this follow-up issue; CodeRabbit accepted the deferral in 4422650671).

Impact / non-impact

  • Exposure is submission latency and feedback ordering only: when agentTimeout < shell-integration activation time, a model turn can read queued feedback before seeing the command's output.
  • Not a loss or an approval bypass: the queued message is retained, the exclusion rules hold (a retained drain submission cannot approve a command ask), and the command still runs and reports in the following turn.

Why the existing signals can't be reused

  • commandSubmitted (ExecuteCommandTool.ts :684) is set by onShellExecutionStarted, which fires from BaseTerminal.setActiveStream (stream availability, not handoff). On the Execa backend the stream arrives only after first output, so using it would skip the drain for commands already handed off but not yet streaming.
  • process.ownExecution is VS Code-only (TerminalProcess.ts :177; absent from types.ts RooTerminalProcess); ExecaTerminalProcess never sets it.
  • A handoff-time emit does not exist in either backend's process class.

Suggested minimal fix shape (priced, ~11 counted exec lines + ~30 test lines)

  • src/integrations/terminal/types.ts: add onCommandHandedOff?: (process) => void to RooTerminalCallbacks.
  • TerminalProcess.ts: call it immediately after executeCommand returns (:172-178) and after sendText in the no-integration branch (:69-78).
  • ExecaTerminalProcess.ts: call it at run() entry before the execa spawn (:44).
  • Terminal.ts / ExecaTerminal.ts: forward the event to the callback.
  • ExecuteCommandTool.ts: set (or OR) commandSubmitted from the new callback.
  • Tests: submission-timing fixtures for both backends (cross-backend test surface is the real cost, not the line count).

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions