feat(#3996): save generated media with no-resend placeholders - #4025
Open
aheritier wants to merge 4 commits into
Open
feat(#3996): save generated media with no-resend placeholders#4025aheritier wants to merge 4 commits into
aheritier wants to merge 4 commits into
Conversation
aheritier
force-pushed
the
workspace-media-materialization
branch
from
August 24, 2026 15:13
9ffaa5a to
2b15b2e
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
from
August 25, 2026 07:59
2b15b2e to
3dadcf6
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
from
August 25, 2026 08:16
3dadcf6 to
b07ac83
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
from
August 25, 2026 20:24
b07ac83 to
5e896ae
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
from
August 26, 2026 11:09
5e896ae to
ef641a7
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
2 times, most recently
from
September 1, 2026 16:12
3d8769a to
ef641a7
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
2 times, most recently
from
September 1, 2026 19:58
7bc0365 to
41c56eb
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
from
September 6, 2026 16:14
41c56eb to
d8df5cd
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
from
September 7, 2026 11:19
d8df5cd to
94cbcfa
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
from
September 8, 2026 07:57
170abf1 to
868e798
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
from
September 8, 2026 17:31
868e798 to
529ebfc
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
from
September 8, 2026 20:35
529ebfc to
9d7da80
Compare
aheritier
marked this pull request as ready for review
September 8, 2026 20:38
dgageot
force-pushed
the
workspace-media-materialization
branch
from
September 9, 2026 07:41
9d7da80 to
0dac5fe
Compare
dgageot
previously approved these changes
Sep 9, 2026
aheritier
force-pushed
the
workspace-media-materialization
branch
from
September 9, 2026 11:56
0dac5fe to
a953251
Compare
dgageot
force-pushed
the
workspace-media-materialization
branch
from
September 9, 2026 12:15
a953251 to
2569c9e
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
from
September 9, 2026 12:56
2569c9e to
b985006
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
from
September 9, 2026 13:32
b985006 to
16f0138
Compare
aheritier
force-pushed
the
workspace-media-materialization
branch
from
September 9, 2026 14:07
16f0138 to
7148760
Compare
Save accumulated media into the owning session's workspace, resolving parent-session WorkingDir provenance. Persist owner-qualified workspace references and MIME/name/size rather than generated base64 in session JSON. Use generic naming fallback for refused provider names and per-item warnings when no workspace/save is available; there is no alternate storage target in this patch. Exempt media-bearing results from the empty-response error. Replace saved generated parts in ordinary later provider turns with bounded metadata placeholders and avoid counting mirrored text twice in compaction. If all saves in a media-only reply fail, an empty assistant record may remain alongside warnings; successful siblings and reply text are retained. At a text-only stop, match phrases in the last user prompt for a nonfatal missing-image notice. A terminal error skips this branch; current-agent structured-output config suppresses it, but arbitrary per-call overrides and reply content are not classified. Negated/question/context phrases can match, capability is unchecked, and continuations are not tracked as one submission. Characterization tests pin those limits. Use mockRuntime.events for the independent CLI text-before-warning test, not a later helper.
Route provider display names through chat.SanitizeDisplayName and normalize MIME metadata to bounded conservative type/subtype tokens, falling back to application/octet-stream. This token check is not strict RFC 6838 validation. Bound formatted warnings/placeholders to 512 bytes and fields to 128 bytes without splitting valid UTF-8 runes. Sanitize harness interpolation and keep raw OS errors and source paths out of warnings. Make unsupported-input stripping skip generated artifacts independently of transform registration order. Cover hostile/overlong metadata, fallbacks, ownerless markers, converter placeholders, harness injection and partial-success batches.
Record owner session, final workspace-relative path, sanitized MIME and creation time after successful writes in SQLite migration 028 and the memory store. Validate path shapes on insertion and lookup, prune records on session deletion, and avoid a foreign key because materialization may precede the persisted session row. The manifest provides authorization for file resolution; generated references still require a known root kind. Persist collision-adjusted paths and issue bounded extension-correction notices. Missing or unwritable roots retain per-item warnings without a data-directory fallback. Reject unclean stored WorkingDir values. This does not yet wire the TUI resolver or prompt-directed workspace redirection.
Replace generic materialization and manifest warnings with fixed reasons: missing workspace provenance, unwritable or deleted locations, exhausted collision names, refused paths, and files saved without a manifest record. Keep the item index and bounded, sanitized MIME/display-name metadata; never expose raw errors, absolute paths, or session IDs in these warnings. Unclassified failures advise --debug and retry, with detailed causes kept in debug logs. Add ErrNameExhausted to classify collision exhaustion without echoing the requested path.
aheritier
dismissed
dgageot’s stale review
September 9, 2026 15:55
The merge-base changed after approval.
aheritier
force-pushed
the
workspace-media-materialization
branch
from
September 9, 2026 15:55
7148760 to
bc659a1
Compare
dgageot
approved these changes
Sep 9, 2026
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 and why
Materialize completed media as workspace files and record owner-qualified manifest references with actual collision/MIME-adjusted names. Session JSON stores references rather than generated base64. Ordinary later turns and compaction receive bounded metadata placeholders instead of bytes. Per-item failures preserve text and successful siblings. The missing-image notice is a phrase heuristic with accepted false positives and negatives; it is not NLP, has no retry, and does not test output capability.
Part of #3996. Review this PR against its immediate parent, #4024, rather than the aggregate stack against
main.Commit inventory
Head:
9d7da808954352fc5fd7c54aeeacc8240c0bcc2d; parent SHA:8cd7c8d6182ec1a60d9a404268f88887f19c80fa.234102aaac7eb6a1b720d3986465c554c9624330— feat(Epic: Gemini image output with workspace deliverables and portable session copies #3996): materialize generated media with no-resend history4b0864f749590722771e884337b904c716c4e90c— fix(Epic: Gemini image output with workspace deliverables and portable session copies #3996): sanitize and bound generated-media metadata90add6ab157cc3ba350eb736bf3c7406c853911e— feat(Epic: Gemini image output with workspace deliverables and portable session copies #3996): record generated media in a manifest trust anchor9d7da808954352fc5fd7c54aeeacc8240c0bcc2d— feat(Epic: Gemini image output with workspace deliverables and portable session copies #3996): classify generated-media save failures into safe reasonsValidation
Build, test compilation, owning-package tests and the named fixture passed at this PR head.
Exact deterministic fixture command:
Matched top-level tests:
pkg/cli: 1;pkg/model/provider/gemini: 4;pkg/runtime: 28;pkg/session: 6.Deterministic scope: the named local fixture exercises this PR boundary with disposable configuration/stores and fake or loopback providers as applicable. Every listed package ran nonzero matching top-level tests.
Deferred/live scope: Live provider, remote CI, and platform execution are not claimed by this deterministic receipt. The final stack head passed build, lint, full tests, an uncached full suite, focused race tests and documentation checks in disposable environments. Remote CI is tracked by the checks below; no new paid-provider or active-database validation was run.