fix(checkpoint): externalize images in subagent transcripts before size cap - #2069
fix(checkpoint): externalize images in subagent transcripts before size cap#2069suhaanthayyil wants to merge 4 commits into
Conversation
…ze cap prepareSubagentTranscript only sanitized then size-checked, while the main session path runs sanitize → externalize → redact. Inline images in subagent transcripts therefore inflated past MaxChunkSize (dropped whole) or reached redaction still as base64 (Codex input_image corrupted). Externalize via the agent image codec when ENTIRE_EXTERNALIZE_IMAGES is on, size-check the shrunk transcript, and write returned assets under tasks/<id>/assets/ in both persistent and ephemeral stores.
There was a problem hiding this comment.
Pull request overview
This PR updates the subagent-transcript storage pipeline in the checkpoint subsystem so that (when enabled) inline base64 images are externalized into checkpoint assets before enforcing the 50MB blob cap, aligning subagent handling with the main session transcript path and preventing image-heavy transcripts from being dropped or corrupted during redaction.
Changes:
- Extend
prepareSubagentTranscriptto: sanitize → (opt-in) image externalize → size-check, returning extracted assets alongside the rewritten transcript. - Wire extracted task assets into both persistent (
treeWriter) and ephemeral (shadow-branch) task checkpoint writers undertasks/<id>/assets/. - Add focused unit tests covering Claude + Codex image externalization, disabled gating, and size-cap behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| cmd/entire/cli/checkpoint/subagent_transcript.go | Adds image externalization to the subagent transcript preparation pipeline and returns extracted assets. |
| cmd/entire/cli/checkpoint/subagent_transcript_test.go | Adds unit tests validating extraction/gating and size-cap behavior for Claude and Codex formats. |
| cmd/entire/cli/checkpoint/persistent.go | Persists extracted task assets into the committed checkpoint tree under the task subtree. |
| cmd/entire/cli/checkpoint/ephemeral.go | Persists extracted task assets into the shadow-branch task subtree via tree-surgery changes. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Address Copilot review: prepareSubagentTranscript() ran even when os.ReadFile failed, wasting work and risking misleading size/ externalization warnings from partial/empty reads before the readErr check. Guard the call behind readErr == nil in both ephemeral and persistent task writers. Entire-Checkpoint: 01M0DMQ8A29WGK3S3VCAWA0THH
|
Fixed Copilot review comment: gated SHA: 0f73c38 |
…re task bundles atomically Image externalization lifted any sufficiently long base64 out of an image-shaped field on the strength of the transcript's declared media type. Assets are written to git as raw blobs and never pass through the redaction the transcript body gets, so a non-image payload in that field — an agent packing a file dump into a data-URI, a mislabeled upload — was moved out of the redacted body and committed verbatim. Externalization now requires the decoded bytes to carry a recognized image signature; anything else stays inline, where redaction still sees it. Extraction was also unbounded in aggregate: the per-image cap said nothing about the set, whose decoded bytes are all retained until the caller has written them, and the cap was applied only after DecodeString had already allocated the full output. Add a hard total-bytes bound, a count bound, and an encoded-length check that refuses an oversized value before decoding it. A task's subagent transcript and its assets are one unit — once externalized the image bytes exist nowhere else — but each was written independently, so a failed asset write published a transcript whose placeholders could never resolve, and a failed transcript write left orphan blobs. Both stores now stage the pair and publish only once every blob exists. The asset set is also a replacement rather than an addition: a rewrite clears the task's assets/ subtree first, including when the new set is empty, which is what previously left the last attempt's screenshots and manifest behind, referenced by nothing. A dropped write (source unreadable, or oversize) still leaves the stored pair untouched, since those two agree with each other. Co-authored-by: Cursor <cursoragent@cursor.com> Entire-Checkpoint: 01M0KSBJ7AS3MV7HB26WTRW82Z
Trail: https://entire.io/gh/entireio/cli/trails/1100
What: Run image externalization on subagent transcripts (when enabled) before the 50MB size check, and store returned assets under
tasks/<id>/assets/.Why / how it helps: Subagent path only sanitized then size-checked/redacted, so image-heavy transcripts were dropped whole and Codex
input_imagebase64 reached redaction intact (corruption risk).How:
prepareSubagentTranscript: sanitize → optionalimageextract→ size-check externalized bytes; wire assets into persistent + ephemeral task writers.Testing:
go test ./cmd/entire/cli/checkpoint/ -count=1(375 passed)go test ./cmd/entire/cli/transcript/imageextract/go build ./...cleanFixes #2063
Related: #2060 (redact skip for
input_image), #2058 (durable subagent storage still open)