Repository navigation
feat: capture files an agent sent by path (images, MP4/MOV video) into the ledger and serve them as assets - #55
Open
jamesyong-42 wants to merge 2 commits into
Open
jamesyong-42 wants to merge 2 commits into
jamesyong-42 wants to merge 2 commits into
Conversation
…o 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/<digest>.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 `<video>` cannot seek); the Electron check plays and seeks a clip it records. - docs: ARCHITECTURE.md, AUTH.md, the endpoint page, ground-truth notes, atlas media domain. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
When Claude Code sends the user a file by path (
SendUserFile), the daemon now copies images and MP4/MOV video into the raw ledger at capture time and serves them through the existing asset route. The History room shows a video as a tile that plays in place.attachments[].path,.sizeand.media_typefrom a successful, single-resultSendUserFileline whose opener it remembers. Relative paths, and paths with a backslash,./..segment or trailing slash, are skipped rather than rewritten.IngestOp::PresentedMedia(a no-op in the store) asks the pipeline to read the file. Bytes are ledgered only when the file's size equals the recorded size and its magic bytes match the declared type. Caps: 32 MiB per image, 64 MiB per video, 128 MiB and 32 files per record. Device and process paths (/dev,/proc, also through a symlinked directory) are refused before any open.{media_type, expected_len}.ledger verify --jsonreports amediablock (frames, captured, missing, too large, unsupported, changed during read)./api/assets/<digest>.mp4|.movreturns the file whole to anyreadgrant. No Range support yet.readAsset; a host may offermediaAssetUrlonly for addresses that answer range requests.ROSETTA_PRESENTED_MEDIA=offdisables capture (for frozen benchmark pairs).No new table, column or wire type; no schema or protocol bump. The Claude normalizer version is unchanged, so existing rows gain attachments only after
rosettad rebuild; new sends are captured live.Exposure, stated plainly
The daemon opens any absolute path that a successful
SendUserFileresult names, and a captured file is then readable by any client holding areadgrant that knows its digest.docs/AUTH.md§5.4 is updated to say so.Known limits (documented in
docs/ARCHITECTURE.md)ColdEngine) passes have no ledger fallback.clients/app/design/transcript-record.htmlrecords it.Verification
cargo fmt --all --check,cargo clippy --workspace --all-targets --all-features --locked -- -D warnings,cargo test --workspace --all-features --locked(2,167 passed, 0 failed, 3 ignored).main: fmt, clippy and tests re-run for the touched crates; ground-truth check, Python tool tests,npm run build,npm test(626 pass),npm run check:packages, docs-site check.presented_media(append growth, replay, reread, snapshot),presented_media_e2e,rosettadpresented_media, and an Electron test that records an H.264 clip, then plays and seeks it on both the bytes path and a range-capable host.rule:module-size(warn) is reported because already-oversized files grew slightly; no file newly crossed the limit and the baseline is unchanged.🤖 Generated with Claude Code
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.