Stream real-time output from long-running shell commands
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>
7 Comments
Potential duplicates detected:
Powered by Codex Action
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
local_shell) and commands executed via MCP tools. The TUI should not need to differentiate between them.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:
stdoutandstderrchunks in real-time.---
3. Detailed Component Breakdown
Component 1: Core Execution Layer (
codex-core)This is where the command process is spawned and its output is read.
codex-rs/core/src/protocol/protocol.rs, we will define a new event,ExecCommandOutputDeltaEvent.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 (stdoutorstderr).data: The chunk of output as aStringorVec<u8>.process_exec_tool_callincodex-rs/core/src/exec.rswaits for the command to finish and returns a completeExecToolCallOutput. This needs to be adapted.event_sender(atokio::sync::mpsc::Sender<Event>) into the execution context, likely passed intoprocess_exec_tool_callvia a new field in theStdoutStreamstruct.read_cappedfunction, 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 sendExecCommandOutputDeltaEvents through the provided channel for each chunk it reads.ExecCommandEndEventwill 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.
codex-rs/tui/src/chatwidget.rs, the main event loop (ChatWidget::handle_codex_event) will get a new case forEventMsg::ExecCommandOutputDelta.ExecCell):ExecCellstruct incodex-rs/tui/src/exec_cell/model.rs, which represents a command execution in the UI, currently only holds the finalCommandOutput.ExecCellto buffer the live, streamed output lines, for example:live_output: Vec<Line<'static>>. This buffer will be populated asExecCommandOutputDeltaEvents arrive.codex-rs/tui/src/exec_cell/render.rsfor an activeExecCell(one that is still running) will be updated.live_outputbuffer directly below the command line, effectively creating a mini-terminal view for the running command.live_outputto prevent memory issues from extremely verbose commands, showing only the most recent N lines.ExecCommandBeginEventarrives, anExecCellis created and stored as theactive_cellinChatWidget.ExecCommandOutputDeltaEventevents arrive,ChatWidgetfinds theactive_cell, downcasts it toExecCell, and appends the new output lines to itslive_outputbuffer. It then requests a TUI redraw.ExecCommandEndEventarrives, theactive_cellis finalized with the full output and exit code, and it is moved from theactive_cellslot 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.
codex-mcp-server):codex_tool_runnerwhich spawns and manages theCodexConversationfor atools/callrequest will listen for the newExecCommandOutputDeltaEvent.tool/streamnotification type if the spec supports it, or a customcodex/exec_output_deltanotification.codex-core):McpConnectionManagerwill be updated to listen for these new streaming notifications from the server.ExecCommandOutputDeltaEventand send it to theSession's event queue (tx_event).ExecCommandOutputDeltaEventregardless of whether the command was executed by the local shell or a remote MCP server, achieving the desired generality.---
4. Implementation Stages
ExecCommandOutputDeltaEventincodex-protocolandcodex-core.codex-core/src/exec.rsand the TUI'sExecCellto handle and display the new event. This provides the core functionality quickly.ChatWidgetandExecCellto render the streamed output, including handling line wrapping and scrolling within the active cell.This plan establishes a robust pipeline for real-time data, significantly enhancing the user experience by providing the requested visibility into long-running commands.
Yes, I think this would assist with MCP setup issues.
More logging
does not yield new information in
tail -F ~/.codex/log/codex-tui.log:That's because this is tailing the output of the CLI program, not the sub-shells it opens.
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 :
for some existing directory - this path should show up in /status
Then run for example:
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 :)
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
ExecCommandOutputDeltaEventexists withcall_id,chunk, andstream(stdout/stderr) fields. Full event lifecycle:ExecCommandBegin→ExecCommandOutputDelta(streamed) →ExecCommandEnd.start_streaming_output()inunified_exec/async_watcher.rsreads from the PTY output receiver and emitsEventMsg::ExecCommandOutputDeltaevents in real-time.CommandExecutionOutputDeltaNotificationis forwarded to connected clients, so IDE-based consumers can display streaming output.What's NOT implemented
TUI rendering — The
ChatWidgetreceives delta events but discards them with a TODO:The
ExecCellhas nolive_outputbuffer 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_pathconfig 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
ExecCellrendering logic (live output buffer, circular/capped display, line wrapping).Closing as resolved in current releases.