17485cbcd2
Port from openai/codex#31494: user-visible history replay must strip CSI sequences and control characters. Stored conversation history can carry raw terminal escapes (pasted content, gateway-origin text, model output echoing injected tool results). Replaying it via /resume's recap panel or build_recap (/status on CLI + gateway) wrote those bytes straight to the terminal — an injected message could clear the screen, retitle the window, move the cursor, or restyle the recap UI. Rich's Text() does not neutralize raw escape bytes. - tools/ansi_strip.py: add sanitize_display_text() — strip_ansi() plus bare C0/C1 control removal, preserving \n and \t, normalizing \r to \n (adapted to Python from Codex's sanitize_user_text; reuses the existing ECMA-48 stripper instead of transcribing their char-walk) - hermes_cli/cli_agent_setup_mixin.py: sanitize user + assistant text in _display_resumed_history() before building the Rich recap panel - hermes_cli/session_recap.py: sanitize preview lines in build_recap() (_truncate choke point) so /status recaps are clean on every platform - tests: 10 new sanitize_display_text cases (incl. the exact codex#31494 fixture), recap + resume-display leak assertions