fix: preview local reply images safely

Merged
TE
13d ago
+822-49

Fixes local image replies so a path such as /tmp/cx-lanes/sidebar-clean-cmux.png loads inline as a bounded thumbnail and opens visibly in a WebKit file viewer.

Leo reported: "why dont we have a preview for thsi".

Changes:

  • allow image reads from the chat cwd, /tmp, and the user's home while keeping ordinary path chips on their existing project-root gate;
  • reject non-image extensions at the native image boundary and normalize fragments, overflow, color space, and large PNGs;
  • route image/PDF file tabs through WebKit's scoped local reader;
  • fetch remote-chat images from the owning host through the session attachment media reader, never from a same-named local path;
  • add web, Swift, and host-side media coverage for local thumbnails, outside-root gating, remote paths, and large PNG visibility.

The image roots are read-only and image-only. Paths outside the cwd, /tmp, and home remain gated for privacy. Remote chats use their host's cwd, /tmp, home, and upload roots through the media reader; the local app never resolves those paths.

Checks:

  • bun test test/inline-media.test.tsx test/agent-message-card.test.tsx test/image-viewer-actions.test.tsx (62 passed)
  • exact-head fleet Swift package suite: pending
  • exact-head fleet Rust media suite: pending
  • git diff --check

State: checks pending
Owner: Codex session 01a16a5b-a74a-7071-b5ae-aa82ac49b237
Head: 1fb9bd483efa730ddb11357cfb4333093efa9bef
Next: reconcile exact-head fleet checks, review, and guarded merge
Evidence: fleet jobs 6f964d89a2b297c0e2b8fb1c and 48f5b6b1cc3726ade2f3c36d
Stop: none

Agent session: huge-blue

Before / after capture

Fleet gallery advisory run 40032701815 rendered the local reply-image chat state (agent-pane.inline-media/path-in-sentence) from the merge base and landed head in both themes. The landed view shows the local image thumbnail inline.

LightDark
Before — #24619 basebefore lightbefore dark
After — landed #24619after lightafter dark

The pre-existing Rust media-reader race is tracked in #24656: hub::media_read::tests::a_file_swapped_for_a_symlink_is_never_followed fails when run alone.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes local image replies so a path like /tmp/cx-lanes/sidebar-clean-cmux.png loads inline as a bounded thumbnail and opens visibly in a WebKit file viewer.

Image scope

  • Local reply images may be read from the chat's cwd, /tmp, and the user's home; ordinary path chips stay gated to the project root.
  • Local file:// URLs are accepted only for the local host, and query/fragment decoration is stripped before path resolution; foreign-host URLs are rejected.
  • Remote chat images are read from the owning host through the acpmux media reader, so a same-named local file is never used; the host scopes paths to its own cwd, /tmp, home, and upload roots.
  • The native image boundary rejects non-image extensions and normalizes PNGs (overflow, color profile, large size) before display.

Viewer

  • Image and PDF tabs now route through WebKit's scoped loadFileURL reader, which displays large RGBA PNGs that CEF painted black.