Problem
Assistant Markdown cannot embed session attachment images. MarkdownBody rewrites every  whose src is not an absolute http(s) URL into escaped literal text before parsing (neutralizeUnsafeMarkdownImages in packages/ui/src/markdown-body.tsx), and MarkdownImage repeats the same http(s)-only allowlist at the component level. Models receive attachments as maka://runtime/attachments/... refs (packages/runtime/src/model-history.ts) and naturally try to embed them — e.g.  — and the transcript shows the raw Markdown source instead of an image. There is no supported path from a session artifact to an <img> in assistant output; user-message attachments render only because chat-turn.tsx uses a dedicated AttachmentImage component outside Markdown.
Proposed behavior
- Accept exactly one additional image scheme in assistant Markdown:
maka://runtime/attachments/<artifactId>, parsed with the existing parseAttachmentResourceRef and resolved against the session that owns the rendered message (refs stay session-implicit; model text cannot name another session).
- Resolve bytes through the existing
ReadAttachmentBytes channel (attachments:readBytes), producing a data URL inside the component. Astryx's sanitizeUrl blocks data: before components.image, so resolution must live in the component anyway — which also keeps base64 out of the transcript text.
- Thread the reader into the Markdown layer through a React context mirroring
MakaUriContext, provided per-session by the chat view (activeSession.id). Extract the async load logic from AttachmentImage into a shared hook. When no reader is installed (non-desktop hosts), fall back to the [alt] placeholder.
- Collapse enforcement to a single chokepoint: Astryx 0.5.0 routes both inline and standalone image branches through
components.image, so the pre-parse scanner neutralizeUnsafeMarkdownImages and its stale comment are obsolete — delete it and move its coverage to component-level assertions.
- Document the syntax to the model: extend the attachment resource block in
model-history.ts so the ref is stated to be a valid Markdown image src (models currently guess, producing output like ).
- All other non-http(s) sources keep current behavior.
Astryx seam notes
components.image is the only image seam in @astryxdesign/core 0.5.0 (latest published); its {src, alt} signature is synchronous, which the component-internal async resolution accommodates.
- The override path skips Astryx's block spacing wrapper and carries no placement/density info. Render attachment images with our own block presentation (max-width, margins); keep http(s) badge images inline. Optional upstream contribution, not a blocker: pass placement (inline vs standalone) to
components.image.
Non-goals
- No new IPC or protocol; reuse
attachments:readBytes.
- No cross-session resolution; no
file:, data:, or other maka:// hosts as image sources.
- No change to the link URL policy.
- No fork or patch of Astryx.
Validation
- Component tests: an attachment ref renders an
<img> with the resolved data URL; unknown or deleted artifactId falls back to placeholder; other schemes (including reference-style ![alt][ref]) stay blocked.
- Cover both streaming and settled rendering paths.
- A story or desktop e2e where a turn embeds a real session attachment image.
Related: #4079 (attachment refs surfaced to the model on the input side).
Problem
Assistant Markdown cannot embed session attachment images.
MarkdownBodyrewrites everywhose src is not an absolute http(s) URL into escaped literal text before parsing (neutralizeUnsafeMarkdownImagesinpackages/ui/src/markdown-body.tsx), andMarkdownImagerepeats the same http(s)-only allowlist at the component level. Models receive attachments asmaka://runtime/attachments/...refs (packages/runtime/src/model-history.ts) and naturally try to embed them — e.g.— and the transcript shows the raw Markdown source instead of an image. There is no supported path from a session artifact to an<img>in assistant output; user-message attachments render only becausechat-turn.tsxuses a dedicatedAttachmentImagecomponent outside Markdown.Proposed behavior
maka://runtime/attachments/<artifactId>, parsed with the existingparseAttachmentResourceRefand resolved against the session that owns the rendered message (refs stay session-implicit; model text cannot name another session).ReadAttachmentByteschannel (attachments:readBytes), producing a data URL inside the component. Astryx'ssanitizeUrlblocksdata:beforecomponents.image, so resolution must live in the component anyway — which also keeps base64 out of the transcript text.MakaUriContext, provided per-session by the chat view (activeSession.id). Extract the async load logic fromAttachmentImageinto a shared hook. When no reader is installed (non-desktop hosts), fall back to the[alt]placeholder.components.image, so the pre-parse scannerneutralizeUnsafeMarkdownImagesand its stale comment are obsolete — delete it and move its coverage to component-level assertions.model-history.tsso the ref is stated to be a valid Markdown image src (models currently guess, producing output like).Astryx seam notes
components.imageis the only image seam in@astryxdesign/core0.5.0 (latest published); its{src, alt}signature is synchronous, which the component-internal async resolution accommodates.components.image.Non-goals
attachments:readBytes.file:,data:, or othermaka://hosts as image sources.Validation
<img>with the resolved data URL; unknown or deleted artifactId falls back to placeholder; other schemes (including reference-style![alt][ref]) stay blocked.Related: #4079 (attachment refs surfaced to the model on the input side).