Skip to content

fix(cli): stream agent output as plain text instead of Rich markup - #544

Open
michaelxu2288 wants to merge 1 commit into
scaleapi:mainfrom
michaelxu2288:fix/cli-plain-agent-logs
Open

michaelxu2288 wants to merge 1 commit into
scaleapi:mainfrom
michaelxu2288:fix/cli-plain-agent-logs

Conversation

@michaelxu2288

@michaelxu2288 michaelxu2288 commented Oct 1, 2026 •

Copy link
Copy Markdown

Problem

In agentex agents run, stream_process_output is the only reader of an agent's stdout pipe. It printed each line with

console.print(f"[dim]{prefix}:[/dim] {decoded_line}")

so the agent's own output was parsed as Rich markup. That has three effects:

  1. Markup-like output stops the reader. A line with an unmatched closing tag, such as [INST] hi [/INST] (common in prompt dumps) or loaded [/etc/hosts], raises MarkupError. The error reaches the outer handler and ends the loop. This is the freeze the function's docstring warns about: nothing drains the pipe any more, and the agent blocks once the pipe fills.
  2. Bracketed words are deleted. [info] starting prints as starting, and deps: [pkg==1.0] prints as deps: .
  3. Long lines block the event loop. Rendering markup for long lines is slow, and the reader can't drain while it runs: an 82,000-character line took 28.9 s here. This is also why test_large_line_within_the_limit_is_streamed_in_full times out on slower machines.

Fix

Print a rich.text.Text with the dim prefix and the line appended as plain text. The read loop, the limits and the error handling are unchanged. Child output loses Rich's automatic number/string highlighting; I think plain is the right default for raw logs.

Verification

I drove stream_process_output with an in-memory reader:

input main this branch
[INST] hi [/INST], loaded [/etc/hosts], [info] starting, deps: [pkg==1.0], after the markup warns Output streaming for TEST stopped on MarkupError(...); nothing printed all five lines printed verbatim
one 82,000-character line 28.89 s 0.01 s
  • New tests in tests/lib/cli/test_run_handlers_streaming.py:
    • a child that prints markup-like lines and then floods the pipe still exits 0, because the reader kept draining;
    • every markup-like line appears verbatim.
  • uv run pytest -n 0 tests/lib/cli/test_run_handlers_streaming.py: 7 passed in 1.5 s, including the 82k-character test.
  • ruff check and pyright are clean.

RetriggerConfidence Score: 5/5

This PR appears safe to merge; no actionable issues were found.

What we checked:

  • Brackets stop agent output: The runner passes the decoded line into Text, and the new tests check both intact output and a child that finishes after flooding the pipe.

Summary

The CLI now prints agent output as plain text while keeping its prefix dim. Markup-like content stays visible, and the reader can keep draining output after those lines.

  • Uses Rich text assembly to style only the prefix.
  • Adds checks for intact markup-like lines and continued output draining.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Agent stdout] --> B[Read and decode line]
  B --> C[Build plain Text with dim prefix]
  C --> D[Print to console]
  D --> B
Loading

Reviews (1) · Last reviewed commit: "fix(cli): stream agent output as plain t..."

stream_process_output is the only reader of an agent's stdout pipe in
`agentex agents run`, and it printed every line with
console.print(f"[dim]{prefix}:[/dim] {decoded_line}"), so the agent's
own output was parsed as Rich markup:

- A line with an unmatched closing tag, such as "[INST] hi [/INST]" or
  "loaded [/etc/hosts]", raised MarkupError. That reached the outer
  handler and ended the loop, which is the freeze the function's
  docstring warns about: nothing drains the pipe any more and the agent
  blocks once it fills.
- Bracketed words were eaten as styles: "[info] starting" printed as
  " starting", "deps: [pkg==1.0]" as "deps: ".
- Markup rendering of long lines is slow and blocks the event loop while
  it runs: an 82,000-character line took 28.9 s here, which is also why
  test_large_line_within_the_limit_is_streamed_in_full times out on
  slower machines.

Print a Text with a dim prefix and the line as plain text instead. The
read loop, limits and error handling are unchanged.

Verified by driving stream_process_output with an in-memory reader: on
main the first markup-like line stops streaming and nothing after it is
printed; here every line prints verbatim and the 82,000-character line
prints in 0.01 s. Two new tests cover the reader surviving markup-like
output and printing it verbatim; tests/lib/cli/test_run_handlers_streaming.py
passes (7 tests, 1.5 s).

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant