Stream real-time output from long-running shell commands

Resolved 💬 7 comments Opened Oct 5, 2025 by coygeek Closed Jun 12, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What feature would you like to see?

I would like the ability to see the real-time stdout and stderr from long-running shell commands that Codex executes, especially those run via MCP tools.

Currently, when Codex runs a command like npm run test:e2e or a large npm install, the TUI only displays a generic spinner (e.g., ⠏ Running npm run test:e2e). This provides no visibility into the progress, output, or potential errors of the underlying process.

The ideal behavior would be for the output of that command to be streamed directly into the TUI, below the "Running..." indicator. This would allow users to:

  • Monitor the progress of the task (e.g., see test suites running, files being compiled).
  • Debug issues in real-time if the command produces errors.
  • Know that the process has not hung or stalled.

This would significantly improve the user experience and build confidence that Codex is actively working on the task.

Additional information

I encountered this issue while using the Playwright MCP to run end-to-end tests on a project. Codex correctly identified the task, created a plan, installed dependencies, and then initiated the test command: npm run test:e2e.

At this point, the UI showed a spinner and the message ⠏ Running npm run test:e2e for over 20 minutes with no further updates or information. I had no way of knowing if the tests were running, stalled, or had failed. The Playwright test runner typically has a rich, detailed output showing which tests are running, passing, or failing, all of which was completely hidden.

While my specific case was with Playwright, this feature is essential for any potentially long-running process, such as:

  • Running database migrations (prisma migrate dev).
  • Complex build scripts (npm run build).
  • Large dependency installations (npm install).

Providing this visibility is critical for a tool designed for developers.

TUI log showing the stall point:
<details>
<summary>Click to expand agent log</summary>

› use the playwright mcp to extensively test this project. make a plan first.

• Updated Plan
  └ □ Review available Playwright tooling and existing test structure to understand required setup.
    □ Install dependencies and ensure test environment prerequisites are satisfied.
    □ Execute Playwright end-to-end suites via MCP integration with appropriate configurations.
    □ Summarize results and note any observed failures or follow-ups.

• Explored
  └ List e2e
    List ls
    Read playwright.config.ts, playwright-global-setup.ts
    Search MCP

• Updated Plan
  └ ✔ Review available Playwright tooling and existing test structure to understand required setup.
    □ Install dependencies and ensure test environment prerequisites are satisfied.
    □ Execute Playwright end-to-end suites via MCP integration with appropriate configurations.
    □ Summarize results and note any observed failures or follow-ups.

• Explored
  └ Read env.setup.js
    List ls -a
    Read .env.test
    List supabase
    Read package.json

• Ran node -v
  └ v24.8.0

• Ran npm install
  └
    added 14 packages, and audited 1113 packages in 2s
    … +3 lines

    found 0 vulnerabilities

• Explored
  └ Read .env

• I’m considering the implications of running tests that connect to a remote Supabase instance using provided
  credentials, which might modify real data. I want to ensure safe setup by verifying connectivity without
  destructive actions and confirming necessary dependencies like Playwright are installed before proceeding.

• Ran npx playwright install
  └ (no output)

• Updated Plan
  └ ✔ Review available Playwright tooling and existing test structure to understand required setup.
    ✔ Install dependencies and ensure test environment prerequisites are satisfied.
    □ Execute Playwright end-to-end suites via MCP integration with appropriate configurations.
    □ Summarize results and note any observed failures or follow-ups.

• Explored
  └ Read video-features-mcp-headless.spec.ts

• I’m making sure the app server and Supabase environment are properly set up for running npm run test:e2e. The
  tests rely on remote Supabase for login and API routes, so environment variables from .env must be loaded. I'll
  run all tests across browsers, noting they might be time-consuming but should pass since some tests auto-pass
  while others expect manual steps.

⠏ Running npm run test:e2e

• Preparing to run Playwright tests (23m 49s • esc to interrupt)

</details>

View original on GitHub ↗

7 Comments

github-actions[bot] contributor · 9 months ago

Potential duplicates detected:

  • #3675
  • #4179
  • #4550

Powered by Codex Action

coygeek · 9 months ago

Here is a potential high-level architectural plan to implement real-time output streaming for shell commands in the Codex TUI.

Architectural Plan: Real-Time Command Output Streaming

1. Guiding Principles
  • Decoupling: The solution should be layered. The command execution logic should not know about the TUI's rendering details. Communication should happen via a well-defined event protocol.
  • Generality: The streaming mechanism should work for both local shell commands (local_shell) and commands executed via MCP tools. The TUI should not need to differentiate between them.
  • Efficiency: We must stream output in chunks without waiting for the command to complete, but also without overwhelming the TUI with too many render updates.
2. High-Level Overview

The core idea is to change the command execution flow from a synchronous "request-response" model (where the full output is returned at the end) to an asynchronous, event-driven streaming model.

This involves three main areas of work:

  1. Core Execution Layer: Modify the command execution logic to capture and stream stdout and stderr chunks in real-time.
  2. Protocol Layer: Introduce a new event type to carry these real-time output chunks from the execution layer back to the UI.
  3. TUI Presentation Layer: Update the TUI's history and active command cells to receive and render these streaming updates.

---

3. Detailed Component Breakdown
Component 1: Core Execution Layer (codex-core)

This is where the command process is spawned and its output is read.

  1. Introduce a New Protocol Event:
  • In codex-rs/core/src/protocol/protocol.rs, we will define a new event, ExecCommandOutputDeltaEvent.
  • This event will contain:
  • call_id: The unique identifier for the specific tool call, allowing the TUI to associate the output with the correct running command.
  • stream: An enum indicating the source (stdout or stderr).
  • data: The chunk of output as a String or Vec<u8>.
  1. Modify the Execution Pipeline:
  • The current function process_exec_tool_call in codex-rs/core/src/exec.rs waits for the command to finish and returns a complete ExecToolCallOutput. This needs to be adapted.
  • We will introduce an optional event_sender (a tokio::sync::mpsc::Sender<Event>) into the execution context, likely passed into process_exec_tool_call via a new field in the StdoutStream struct.
  • The read_capped function, which already reads from the child process's streams in a separate task, is the ideal place to implement the streaming. Instead of just appending to a buffer, it will now also send ExecCommandOutputDeltaEvents through the provided channel for each chunk it reads.
  1. Update Final Output:
  • The final ExecCommandEndEvent will still be sent when the command completes. Its payload (stdout, stderr, aggregated_output) will contain the full captured output, ensuring that even if the TUI misses some delta events (e.g., due to high volume), the final state is consistent. The live streaming is for UX, while the final event is for the permanent record.
Component 2: TUI Presentation Layer (codex-tui)

This layer will receive the new events and render the live output.

  1. Handle the New Event:
  • In codex-rs/tui/src/chatwidget.rs, the main event loop (ChatWidget::handle_codex_event) will get a new case for EventMsg::ExecCommandOutputDelta.
  1. Update the Active Command Cell Model (ExecCell):
  • The ExecCell struct in codex-rs/tui/src/exec_cell/model.rs, which represents a command execution in the UI, currently only holds the final CommandOutput.
  • We will add a new field to ExecCell to buffer the live, streamed output lines, for example: live_output: Vec<Line<'static>>. This buffer will be populated as ExecCommandOutputDeltaEvents arrive.
  1. Enhance Rendering Logic:
  • The rendering logic in codex-rs/tui/src/exec_cell/render.rs for an active ExecCell (one that is still running) will be updated.
  • Currently, it just shows a spinner and the command. The new logic will render the contents of the live_output buffer directly below the command line, effectively creating a mini-terminal view for the running command.
  • We will implement a capped-size circular buffer for live_output to prevent memory issues from extremely verbose commands, showing only the most recent N lines.
  1. Event Flow in the TUI:
  • When ExecCommandBeginEvent arrives, an ExecCell is created and stored as the active_cell in ChatWidget.
  • As ExecCommandOutputDeltaEvent events arrive, ChatWidget finds the active_cell, downcasts it to ExecCell, and appends the new output lines to its live_output buffer. It then requests a TUI redraw.
  • When ExecCommandEndEvent arrives, the active_cell is finalized with the full output and exit code, and it is moved from the active_cell slot into the permanent history log (transcript_cells).
Component 3: MCP Tool Integration (Generalization)

To support tools like Playwright running over MCP, the streaming mechanism must be extended.

  1. MCP Server (codex-mcp-server):
  • The codex_tool_runner which spawns and manages the CodexConversation for a tools/call request will listen for the new ExecCommandOutputDeltaEvent.
  • Upon receiving this event, it will forward the data to the connected MCP client using an appropriate MCP notification, likely a tool/stream notification type if the spec supports it, or a custom codex/exec_output_delta notification.
  1. MCP Client (codex-core):
  • The McpConnectionManager will be updated to listen for these new streaming notifications from the server.
  • When it receives a streaming notification, it will transform it back into a local ExecCommandOutputDeltaEvent and send it to the Session's event queue (tx_event).
  1. Result: From the TUI's perspective, there is no difference. It receives an ExecCommandOutputDeltaEvent regardless of whether the command was executed by the local shell or a remote MCP server, achieving the desired generality.

---

4. Implementation Stages
  1. Protocol First: Define ExecCommandOutputDeltaEvent in codex-protocol and codex-core.
  2. Local Shell Implementation: Implement the streaming logic for local commands first. This involves modifying codex-core/src/exec.rs and the TUI's ExecCell to handle and display the new event. This provides the core functionality quickly.
  3. TUI Presentation: Update the ChatWidget and ExecCell to render the streamed output, including handling line wrapping and scrolling within the active cell.
  4. MCP Extension: Extend the functionality to the MCP server and client layers to enable streaming for all tool types.

This plan establishes a robust pipeline for real-time data, significantly enhancing the user experience by providing the requested visibility into long-running commands.

peterbecich · 9 months ago
especially those run via MCP tools.

Yes, I think this would assist with MCP setup issues.

More logging

$ RUST_LOG=codex_core=trace,codex_tui=trace codex

does not yield new information in tail -F ~/.codex/log/codex-tui.log:

ERROR MCP client for `...` failed to start: request timed out
Falven · 9 months ago
> especially those run via MCP tools. Yes, I think this would assist with MCP setup issues. More logging `` $ RUST_LOG=codex_core=trace,codex_tui=trace codex ` does not yield new information in tail -F ~/.codex/log/codex-tui.log: ` ERROR MCP client for ... failed to start: request timed out ``

That's because this is tailing the output of the CLI program, not the sub-shells it opens.

CarloWood · 8 months ago

I worked on something similar (before I saw this issue) because I basically had the same need.
If you are interested, please checkout the branch https://github.com/openai/codex/compare/main...CarloWood:openai-codex:cw_exec_socket_tap

Add to ~/.codex/config.toml :

exec_socket_path = "/opt/ext4/nvme2/codex/workspace/shell_exec.sock"

for some existing directory - this path should show up in /status

Then run for example:

ncat -kl --unixsock "/opt/ext4/nvme2/codex/workspace/shell_exec.sock"

to create the UNIX socket.

From that moment on the CLI should show all commands and output (it does for me),
but I am not sure it will show long running shell output from MCP servers just yet.
Please let me know :)

coygeek · 4 months ago

Status Update (Feb 2026)

After reviewing the current codebase, this issue is partially resolved. The streaming infrastructure is fully built, but the TUI — where this issue was originally reported — still doesn't render it.

What's been implemented
  1. Protocol layerExecCommandOutputDeltaEvent exists with call_id, chunk, and stream (stdout/stderr) fields. Full event lifecycle: ExecCommandBeginExecCommandOutputDelta (streamed) → ExecCommandEnd.
  2. Core executionstart_streaming_output() in unified_exec/async_watcher.rs reads from the PTY output receiver and emits EventMsg::ExecCommandOutputDelta events in real-time.
  3. App-server (VS Code extension)CommandExecutionOutputDeltaNotification is forwarded to connected clients, so IDE-based consumers can display streaming output.
What's NOT implemented

TUI rendering — The ChatWidget receives delta events but discards them with a TODO:

// codex-rs/tui/src/chatwidget.rs
fn handle_output_chunk(&mut self, _call_id: &str, _chunk: &[u8]) -> Vec<ThreadEvent> {
    //TODO see how we want to process them
    vec![]
}

The ExecCell has no live_output buffer or streaming renderer. Users still see only the spinner (⠏ Running npm run test:e2e) with no visibility into command progress.

Related issues

| Issue | Status | Relationship |
|-------|--------|-------------|
| #3675 (Show stdout/stderr) | Closed (NOT_PLANNED) | Direct predecessor |
| #4550 (Configurable output folding/truncation) | Open | Complementary (rendering decisions) |

Community workaround

@CarloWood has a branch that adds an exec_socket_path config option, streaming all command output to a Unix socket that can be tailed externally. Not merged.

Recommendation

Keep open — the plumbing is complete but the TUI presentation (the core ask) remains a TODO. The streaming infrastructure is ready; what's needed is the ExecCell rendering logic (live output buffer, circular/capped display, line wrapping).

ax-openai · 1 month ago

Closing as resolved in current releases.