nesquena/hermes-webui

MEDIA: download links 404 when token is wrapped in markdown emphasis or trailing punctuation

開放

#6,890 建立於 2026年8月10日

 (4 則留言) (0 個反應) (0 位負責人)Python (2,359 個分叉)github user discovery
bughelp wantedrenderer

倉庫指標

星標
 (17,224 顆星)
PR 合併指標
 (平均合併 14小時 31分鐘) (30 天內合併 314 個 PR)

描述

Summary

Chat MEDIA:<path> download links 404 when the model wraps the token in markdown (bold/italics/code) or glues on sentence punctuation. The file on disk is fine — the frontend builds a URL that includes the trailing markup.

Repro

Agent reply containing:

Done. Here's the sheet.

**MEDIA:/data/workspace/100-camera-cctv-turnkey-comparison.xlsx**

Rendered link label becomes 📎 100-camera-cctv-turnkey-comparison.xlsx** and the href is:

api/media?path=%2Fdata%2Fworkspace%2F100-camera-cctv-turnkey-comparison.xlsx%2A%2A&download=1

GET /api/media with the clean path → 200 + xlsx bytes. Same request with the trailing **404 {"error":"not found"}.

Root cause

In static/ui.js renderMd() (and the matching streaming path in static/messages.js), MEDIA tokens are stashed before markdown runs:

s=s.replace(/MEDIA:([^\s\)\]]+)/g,(_,raw_ref)=>{
  media_stash.push(raw_ref);
  return '\x00D'+(media_stash.length-1)+'\x00';
});

[^\s\)\]]+ does not stop at *, `, _, or sentence punctuation, so closing ** / backticks / a trailing . are captured into the path. _inlineMediaHtmlForRef then encodeURIComponents that polluted ref into /api/media.

The plain-URL autolink pass already strips trailing [.,;:!?)] — MEDIA does not.

Scale

On one production device: 106 / 317 MEDIA tokens (~33%) had trailing junk captured this way (mostly trailing ., then backticks, then **).

Proposed fix

Mirror what the Hermes iOS client already does in TranscriptMediaParser (see nesquena/hermes-webui consumers / Performance Hub iOS TranscriptMedia.swift):

  1. After capturing the ref (or inside _inlineMediaHtmlForRef), strip trailing sentence punctuation [.,;:!?]+.
  2. If the token is immediately preceded by a markdown emphasis/code delimiter (***/___/**/__/*/_/`), also strip the matching closing delimiter from the end of the ref so it stays outside the path.
  3. Apply the same normalisation in the streaming messages.js MEDIA path (it shares _inlineMediaHtmlForRef today, so fixing that helper once covers both settled + live rendering + historical transcripts on re-paint).

Happy to send a PR if useful — smallest change is a _normalizeMediaRef(ref) helper called from _inlineMediaHtmlForRef (and optionally from the stash callback so leftover opening ** can still bold-wrap the link cleanly).

Environment

Observed on hermes-webui 0.52.151 (ghcr.io/nesquena/hermes-webui@sha256:75efbb5f…); master static/ui.js still has the same stash regex as of this report.

貢獻者指南