Files
penguin-harness/changelog/0.2.1/2026-08-04-ansi-tool-output.md
Yaowei Zheng 88916880eb release: 0.2.1 (#204)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 00:30:55 +08:00

1.5 KiB

Fix: ANSI color codes no longer leak into tool output

A nested penguin run driven through exec_command and polled with input_command filled Web tool cards with [36m/[0m fragments spliced into words (#102). Three layers each contributed, and each is fixed:

  • CLI: the renderer wrote escape codes unconditionally. Color is now decided once per output stream — TTY, NO_COLOR unset, TERM not dumb, with a non-empty FORCE_COLOR overriding in either direction, matching Node's own semantics — and every renderer escape routes through that palette, so piped output is plain text.
  • Command tool environment: the child environment always set NO_COLOR=1 and TERM=dumb, but an inherited FORCE_COLOR silently won (Node ignores NO_COLOR when FORCE_COLOR is set). FORCE_COLOR and CLICOLOR_FORCE are now stripped — removed, not blanked, since Node reads an empty FORCE_COLOR as "on" — so the hardening finally holds; a vault-provided value still passes through by design.
  • Web: tool output renders through a defensive ANSI stripper (CSI including multi-parameter SGR, OSC, two-byte escapes, and an incomplete trailing sequence cut mid-stream), applied at render time only — historical Traces display clean without their files being rewritten. The Trace event inspector deliberately keeps raw payloads: it is the raw-data view.

Regression tests cover all three layers, including the reported FORCE_COLOR=3 + NO_COLOR=1 + TERM=dumb combination and sequences split across streaming chunk boundaries.