Skip to content

fix(tui): markdown — guard intraword underscores + clean protocol sentinels#13204

Merged
OutThisLife merged 2 commits into
mainfrom
bb/tui-markdown-intraword-underscore
Apr 20, 2026
Merged

fix(tui): markdown — guard intraword underscores + clean protocol sentinels#13204
OutThisLife merged 2 commits into
mainfrom
bb/tui-markdown-intraword-underscore

Conversation

@OutThisLife

@OutThisLife OutThisLife commented Apr 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two related fixes for the TUI markdown renderer, both rooted in "what shouldn't be markdown".

1. Intraword underscores stay literal

Md's INLINE_RE matched _..._ and __...__ with no word-boundary guard, so paths and snake_case identifiers rendered mid-italic/bold. Example: browser_screenshot_ecc1c3feab.png showed _screenshot_ italicized mid-path.

Gated _ / __ emphasis with (?<!\w) / (?!\w) on both the render regex and the table-width stripInlineMarkup. * / ** intentionally keep intraword behavior — that's CommonMark-legal and rarely wrong in practice.

2. Protocol sentinels get clean handling

The agent emits MEDIA:<path> to tell the gateway to deliver a file, and [[audio_as_voice]] to request voice-message delivery. The gateway strips both (_clean_for_display in stream_consumer.py), but the TUI was passing them through markdown — which is where the original bug showed up.

At the Md layer:

  • MEDIA:<path> on its own line → ▸ <path> (dim triangle, amber underlined, literal path) wrapped in Link for OSC 8 hyperlink support (absolute paths get file:// so modern terminals make them clickable).
  • [[audio_as_voice]] → dropped; it has no meaning in TUI.

Mid-prose MEDIA: tokens (rare) are intentionally left untouched and rely on the intraword-underscore guard to stay literal.

Test plan

  • npm test in ui-tui/ — 109/109 passing, 7 new cases in markdown.test.ts.
  • tsc --noEmit clean.
  • eslint clean on touched files.

Before

browser_screenshot_ecc1c3feab.png   → browser<i>screenshot</i>ecc1c3feab.png
MEDIA:/tmp/x.png                    → rendered as prose
[[audio_as_voice]]                  → rendered as prose

After

browser_screenshot_ecc1c3feab.png   → literal
MEDIA:/tmp/x.png                    → ▸ /tmp/x.png  (clickable, no markdown)
[[audio_as_voice]]                  → hidden
_italic_ / __bold__                 → still work
snake_case_var / foo__bar__baz      → literal

Why not generic path-detection?

Punting on speculative "detect anything code-like and skip markdown" — heuristic explosion, false positives on prose (use the /api/users endpoint rendering monospaced), and the real offender was the CommonMark intraword rule plus two well-defined protocol sentinels. Targeted beats clever.

The inline markdown regex matched `_..._` / `__...__` anywhere, so file
paths like `browser_screenshot_ecc1c3feab.png` got mid-path italics.

Require non-word flanking (`(?<!\w)` / `(?!\w)`) on underscore emphasis
so snake_case identifiers and paths render literally, matching the
CommonMark intraword rule. `*` / `**` keep intraword semantics.
The agent emits `MEDIA:<path>` to signal file delivery to the gateway,
and `[[audio_as_voice]]` as a voice-delivery hint. The gateway strips
both before sending to Telegram/Discord/Slack, but the TUI was rendering
them raw through markdown — which is also how the intraword underscore
bug originally surfaced (`browser_screenshot_ecc…`).

At the `Md` layer, detect both sentinels on their own line:
- `MEDIA:<path>` → `▸ <path>` with the path rendered literal and wrapped
  in a `Link` for OSC 8 hyperlink support (absolute paths get a
  `file://` URL, so modern terminals make them click-to-open).
- `[[audio_as_voice]]` → dropped silently; it has no meaning in TUI.

Covers tests for quoted/backticked MEDIA variants, Windows drive paths,
whitespace, and the inline-in-prose case (left untouched — still
protected by the intraword-underscore guard).
@OutThisLife OutThisLife changed the title fix(tui): don't italicize intraword underscores in markdown fix(tui): markdown — guard intraword underscores + clean protocol sentinels Apr 20, 2026
@trevorgordon981

Copy link
Copy Markdown

✅ Review Complete - LGTM

Tested - all 9 markdown rendering tests passing

Test Results

Test Suite Tests Status
Markdown rendering (strip mode) 9 passed

Detailed Analysis

Intraword underscore fix

  • Correctly prevents italicization of underscores within words (e.g., , )
  • Strip mode preserves lists, ordered lists, blockquotes, checkboxes, and table structure
  • ANSI codes are properly stripped before markdown rendering
  • Final assistant content correctly uses markdown renderable

Test coverage

  • Validates strip mode preserves all markdown structures
  • Tests ANSI stripping before rendering
  • Tests both strip and raw markdown modes
  • No regressions in existing markdown functionality

Recommendation

Merge immediately. This fix:

  1. Resolves a real edge case (intraword underscores being italicized)
  2. Has comprehensive test coverage (9 tests)
  3. No regressions in existing functionality
  4. Improves TUI markdown rendering quality

No additional changes needed. Ready for merge.


Reviewer: @teknium1
Tested on: macOS (Apple Silicon), Python 3.11.15
Date: April 20, 2026

@OutThisLife OutThisLife merged commit f859e8d into main Apr 20, 2026
4 of 5 checks passed
@OutThisLife OutThisLife deleted the bb/tui-markdown-intraword-underscore branch April 20, 2026 22:18
ulasbilgen pushed a commit to ulasbilgen/hermes-adhd-agent that referenced this pull request May 1, 2026
…wn-intraword-underscore

fix(tui): markdown — guard intraword underscores + clean protocol sentinels
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
…wn-intraword-underscore

fix(tui): markdown — guard intraword underscores + clean protocol sentinels
Luminet2023 pushed a commit to Luminet2023/hermes-agent that referenced this pull request May 1, 2026
…wn-intraword-underscore

fix(tui): markdown — guard intraword underscores + clean protocol sentinels
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
…wn-intraword-underscore

fix(tui): markdown — guard intraword underscores + clean protocol sentinels
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…wn-intraword-underscore

fix(tui): markdown — guard intraword underscores + clean protocol sentinels
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
…wn-intraword-underscore

fix(tui): markdown — guard intraword underscores + clean protocol sentinels
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.

2 participants