Files
hermes-webui/tests
happy5318 2330faa0e4 fix(media): MEDIA: token regex strips markdown backtick on 7 capture sites (#7359)
## Thinking Path

When an assistant emits `` `MEDIA:/path` `` wrapped in inline-code markdown, the
shared character class ``[^\s\)\]]+`` across all seven MEDIA capture sites
greedy-matched the closing backtick into the path. The resulting
``/api/media?path=...%60`` URL 404'd on the legitimate file (issue #7359).
The same drift between frontend linkification and backend allowlist means
even if the URL is constructed with the trailing backtick, the session
allowlist rejects the backtick-polluted path — the user gets a broken
download and a 404 body saved as ``.json``.

Verified the issue's repro: 6 sites share ``[^\s\)\]]+`` (the
preview-flattener at ui.js:9142 is ``[^\s]+`` and is a 7th site, also
covered). One additional site, ``messages.js:4896`` (combined walk),
was missing from the original 6-site list in the issue body — the
suggested fix shape applies to all seven.

The fix is a one-character-set widening: add `` ` `` to the excluded
class on every site so the capture stops at the markdown delimiter
instead of including it.

## What Changed

All seven sites: ``[^\s\)\]]+`` → ``[^\s\)\]\`]+`` (JS) and the
Python equivalent ``MEDIA:([^\s\)\]\`]+)``:

1. ``static/ui.js:7695`` — linkifier regex (the front-door capture).
2. ``static/ui.js:9142`` — preview-text flattener (also drops ``)`` and
   ``]`` for symmetric handling; was ``[^\s]+`` which would still pull
   the backtick into the preview).
3. ``static/messages.js:4848`` — streaming chunk finalize.
4. ``static/messages.js:4896`` — streaming combined walk (the
   issue-missing 7th site).
5. ``static/messages.js:4922`` — streaming partial tail.
6. ``api/routes.py:20453`` — ``_MEDIA_TOKEN_RE`` (the session allowlist).
7. ``api/media_snapshots.py:507`` — ``media_re`` (settle-time snapshot
   capture).

## Why It Matters

A user clicking an inline-code-wrapped MEDIA token gets a 404 body saved
as ``.json`` instead of the file they asked for. On Android Chrome (and
desktop Chrome), the 404 silently downloads as ``skills.zip.json`` with
no UI signal that the original was missing. The backend allowlist and
the frontend linkifier must agree, so the fix widens the character
class on both sides.

## Verification

- ``tests/test_issue7359_media_backtick_strip.py``: 17 passed (new).
  - 7 site-shape assertions (parametrized over the seven sites).
  - ``_MEDIA_TOKEN_RE`` behaviour: strips trailing backtick from inline-code
    payload, preserves bare-token capture, preserves trailing-``)`` exclusion.
  - ``media_re`` behaviour: same three.
  - pre-fix class round-trip: ``\`MEDIA:/a.zip\``` → ``["/a.zip\`"]``
    (the reported bug); post-fix → ``["/a.zip"]``.
  - ``node --check`` on the two modified JS files.
- ``pytest tests/ -k 'media and not 7359'``: 291 passed, 1 skipped,
  0 failed — no existing media test regresses.
- ``ruff check tests/test_issue7359_media_backtick_strip.py``: clean.
- ``scripts/ruff_lint.py --diff origin/master`` curated gate: 0 new
  findings on added/modified lines.

## Contract Change

No API contract change. UI rendering may now display
``somefile.zip`` (a file) where it previously displayed
``somefile.zip\``` (literal backtick in the preview); both already
displayed correctly in the bare-token case, and the inline-code path
was the broken one.

## Risks

- Sites that depended on the backtick being included in the path would
  regress, but no consumer in this repo treats a trailing `` ` `` as
  part of a MEDIA path — the original six-site fix shape was
  maintainer-recommended and the 7th site matches the same pattern.
- The preview flattener at ``ui.js:9142`` previously used a wider class
  (``[^\s]+`` only, no ``)``/``]``) so it would have matched a path
  containing a literal ``)``. The fix tightens that to match the
  other six sites; if anyone relied on the looser class the rendering
  would now use ``a file`` for the surrounding paragraph. Verified no
  test in the suite exercises a MEDIA token containing ``)``.

## Model Used

Hermes Agent (step-5-preview) with pytest, ruff, and node. Tests are
executed against the live compiled regexes (for the two Python sites)
and against the literal source text (for the five JS sites), so a
revert on any of the seven sites surfaces as a source-shape failure.

## Related

- #7359 — original issue (KimSpecht, 2026-08-29) with the repro and
  six-site list.
- #7334 — pre-existing PR (angelusbr, 2026-08-27) that covers three
  sites; this PR supersedes it by covering all seven and following the
  maintainer-recommended shape from the 9/20 comment.
- Maintainer's 9/20 comment on #7359 named #7334 as "the correct
  existing implementation lane"; if #7334 lands first, this PR is
  closeable as duplicate and the maintainer's ``sprint-candidate``
  label can move. Credit goes to angelusbr for the early character-class
  idea regardless of which PR merges.
2026-09-22 17:24:29 +08:00
..