Root cause: ctx.MediaPaths was overloaded with two incompatible meanings —
sandbox-relative for the agent runtime, host-absolute for host-side
media-understanding. The previous "absolutize in chat.send + set
MediaStaged=true" path made media-understanding work but shipped an
unreadable host path to the agent inside the sandbox.
- Keep ctx.MediaPaths sandbox-relative after prestage; carry a separate
ctx.MediaWorkspaceDir so host-side media-understanding can still resolve
the staged files via localPathRoots / attachment cache.
- stageSandboxMedia returns an authoritative {source -> relpath} map so
prestageNonImageOffloads detects partial staging failures (files admitted
by the 20MB RPC cap but rejected by the 5MB staging cap) and surfaces
them as 5xx MediaOffloadError UNAVAILABLE.
- Reject images above MAX_IMAGE_BYTES at parse time: the agent-side
hydration path drops them silently otherwise, producing a successful
response with a missing image.
- Scope imageOrder to image offloads only and split persistChatSendImages
offloaded refs by mime so non-image files append to the transcript tail
instead of consuming image slots in mixed batches.
Signed-off-by: samzong <samzong.lu@gmail.com>
* fix(export): fix broken template placeholders in session export HTML
The {{MARKED_JS}}, {{HIGHLIGHT_JS}}, and {{JS}} placeholders in the
export HTML template were split across multiple lines by a code
formatter, turning them into JS block statements instead of template
tokens. The generateHtml() function uses .replace('{{MARKED_JS}}', ...)
which requires contiguous strings, so the vendor JS and app code were
never injected — producing a 2MB HTML file that opens with styles and
session data but renders blank (no JS to parse/display the data).
Fix: collapse placeholders to single-line {{TOKEN}} format and add
prettier-ignore comments to prevent re-formatting.
Introduced in 9d403fd.
* fix(export): use function replacers for vendor JS injection
String.replace() interprets $ sequences ($&, $$, $', etc.) in
replacement strings. The minified vendor libraries (highlight.min.js,
marked.min.js) and the template JS contain literal $ characters that
get mutated during injection — e.g. $& becomes the matched placeholder
text, $$ becomes a single $.
Fix: use arrow function replacers for JS content so replacement text
is injected verbatim without $ interpretation. CSS and session data
use string replacers since they don't contain problematic $ patterns.
Flagged by Codex review (P2).
* ci: retrigger checks
* fix(export-session): restore inline export scripts
---------
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>