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.

贡献者指南