Files
crewAI/lib/cli/tests
João MouraandClaude Opus 5.5 94508c971f feat(cli): with no terminal, crewai eval sends the crew's own output to a log (#8325)
* feat(cli): with no terminal, crewai eval sends the crew's own output to a log

A coding agent reads every line `crewai eval` prints, and pays for it. When
`crewai eval` ran the crew itself, the crew's panels, final answer and tool
calls came first: 341 of 378 lines in a measured run, all before the
verdict, and an agent that cuts long output could lose the verdict and the
brief at the end.

With no terminal, what the crew prints while it runs now goes to
`.crewai/last_run.log`, and the agent gets one line saying so, one saying
how long the run took, then the verdict and brief as before: 40 lines for
the same crew. A run that leaves no trace to evaluate shows its last 40
lines, which usually hold the error. At a terminal nothing changes.

It moves this process's stdout and stderr while the crew runs, which every
child inherits, so every way a project runs is covered; anything that keeps
them from moving leaves the output on screen. `.crewai/` is already left out
of the project-changed check, so the log never triggers a re-run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): the run log is the owner's alone, and both streams always come back

Review on #8325:
- the run log holds final answers, tool output and tracebacks that can carry
  a credential: it is now opened owner-only (0600) and never through a link,
  and a log an earlier run left is emptied and tightened;
- both descriptors are saved before either moves, and a move that fails
  halfway puts back what it moved, so the verdict still reaches the screen;
- putting them back is best effort for each on its own and never raises, so
  the run's own error is the one that surfaces;
- whether to keep the transcript off screen is decided by stdout, as for the
  brief (`nobody_watching()`): an agent can hand the command a terminal for
  stdin and still read a captured stdout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(cli): the run log is a file object closed by its with; a test's raise moves into a helper

Code-quality review on #8325: the log descriptor was closed by hand on every
path; it is now a file object (os.fdopen) the redirect holds in a with, so
it is closed however the block ends. The put-back test raised inline inside
pytest.raises, which reads as unreachable code after it; the raise is now in
a helper. No behaviour changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 14:40:28 -03:00
..