From 8240db68df65028fa39cd5cc34a6b6648852421d Mon Sep 17 00:00:00 2001 From: James Yong Date: Thu, 12 Nov 2026 21:40:18 -0800 Subject: [PATCH] feat: capture files an agent sent by path (images, MP4/MOV video) into the ledger and serve them as assets A Claude Code `SendUserFile` result names files by path; the bytes are not in the transcript. The adapter now emits a path-reference attachment on the tool row plus one pure fact per sent image or MP4/MOV video. The pipeline's capture seam copies the file into a `Document` ledger frame carrying a media claim, under an identity check (current size equals the recorded size, magic matches the declared type; 32 MiB per image, 64 MiB per video, 128 MiB per record), and upgrades the attachment to a content-addressed member served whole by `GET /api/assets/.mp4|.mov`. Rebuild reads only the ledger. - core: `PresentedMedia` fact and `IngestOp::PresentedMedia` (a no-op in the store); mp4/mov member types. No table, column, wire or protocol change. - capture: optional media claim on the Document frame (frames without one are byte-identical); encode refuses what decode rejects; the prefix walk knows the Document tag, so such segments verify clean and compact; `ledger verify` tallies media frames by status. - media: pure MP4/MOV header probe (ftyp brands, tkhd size through the track matrix). No codec. - pipeline: ledger-first capture on re-frame, replacement and snapshot passes; a ledger fallback after a failed observation on append growth; a `reframing` checkpoint flag so an interrupted re-frame resumes as one; replay joins the first captured frame; `/dev` and `/proc` refused, also through a symlinked directory; `ROSETTA_PRESENTED_MEDIA=off` turns observation off. - claude-code adapter: reads only `attachments[].path`, `.size`, `.media_type`; a path is used as written or skipped. Normalizer stays 39. - room: a video tile that requests nothing until pressed and plays an object URL read through the session (the daemon serves a member whole, which a `