fix(agent): accept pixel-correct image downscale when bytes grow (#48013) - #49140
Merged
Conversation
) The image-too-large reactive shrink (try_shrink_image_parts_in_messages) conflated two independent constraints: it always rejected a resize whose re-encoded bytes were >= the original, even when the shrink was driven by a PIXEL-DIMENSION cap (Anthropic many-image 2000px) rather than the byte budget. Downscaled screenshot PNGs routinely re-encode LARGER in bytes, so the dimension-correct result was discarded and the image left oversized -> the provider re-rejected on retry and the session wedged forever. Fix: track which constraint triggered the shrink (bytes vs dimension) and gate the accept on the SAME axis. * dimension path: accept the result as long as it is now within max_dimension, regardless of byte size (verify via Pillow; fall back to the byte gate only when the re-encode can't be decoded). * bytes path: still require bytes to shrink, but ALSO re-check the per-side cap when it's active — _resize_image_for_vision returns a best-effort, possibly over-cap blob when it exhausts its halving budget on a very-high-aspect image, so a byte-shrink alone can leave it over the dimension cap and re-brick on retry. Extend the unshrinkable-oversized guard to the pixel axis so a partial shrink doesn't burn the one-shot retry. Single shared agent path -> fixes CLI, TUI, and gateway alike. Adds a real-Pillow runnable proof (repro_48013_image_shrink_brick.py) that reproduces the issue's per-image table (bricks 3/5 before, passes 5/5 after) plus unit invariants for the dimension and bytes accept/reject paths, partial-progress accounting, and the bytes-path still-over-cap regression surfaced by adversarial review. Closes #48013
Contributor
🔎 Lint report:
|
| Rule | Count |
|---|---|
unresolved-attribute |
2 |
unresolved-import |
2 |
First entries
run_agent.py:2971: [unresolved-attribute] unresolved-attribute: Object of type `Self@get_credits_spent_micros` has no attribute `_credits_session_start_micros`
tests/run_agent/test_credits_notices_toggle.py:76: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_credits_session_start_micros` on type `AIAgent`
tests/run_agent/repro_48013_image_shrink_brick.py:41: [unresolved-import] unresolved-import: Cannot resolve imported module `PIL`
tests/run_agent/repro_48013_image_shrink_brick.py:33: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
✅ Fixed issues (1):
| Rule | Count |
|---|---|
invalid-assignment |
1 |
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [invalid-assignment] invalid-assignment: Object of type `None` is not assignable to attribute `_credits_session_start_micros` of type `int`
Unchanged: 5846 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
This was referenced Jun 19, 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.
Summary
Image-shrink recovery now accepts a pixel-correct downscale even when the re-encoded PNG grows in bytes — fixing the image-dimension 400 session brick (#48013).
Root cause:
try_shrink_image_parts_in_messagesgated accept/reject on byte size (len(resized) >= len(url) → discard), but the failing constraint was pixel dimensions. A downscaled Retina screenshot routinely re-encodes to more PNG bytes than the original, so the pixel-correct result was thrown away, the image stayed oversized, and the session wedged forever on the Anthropic many-image 2000px path.Changes
agent/conversation_compression.py: track which constraint triggered the shrink (bytesvsdimension) and gate accept on that same axis. Dimension path accepts on pixel-cap satisfaction regardless of byte growth; bytes path additionally re-checks the per-side cap (a very-high-aspect image can byte-shrink yet stay over-cap);unshrinkable_oversizedextended to the pixel axis so a partial shrink doesn't burn the one-shot retry.tests/run_agent/repro_48013_image_shrink_brick.py: runnable real-Pillow proof (no mocks) reproducing the issue's per-image table.tests/run_agent/test_image_shrink_recovery.py: 21 tests covering dimension accept-on-byte-growth, dimension-failure-blocks-retry, mixed partial-progress, and the bytes-path-still-over-cap regression.Single shared call site → fixes CLI, TUI, and gateway alike.
Validation
test_image_shrink_recovery.py+ reproThe proof is discriminating: reverting
conversation_compression.pyto currentmainflips it toFAIL: 3 image(s) still over the pixel cap.Salvage / credit
Salvaged from #49124 by @alt-glitch (Siddharth Balyan), cherry-picked onto current
mainwith authorship preserved. Builds on the approach in #48035 by @Tranquil-Flow (same core idea: track the binding constraint). Supersedes #48046 (@kyssta-exe) — that one-liner predated themax_dimensionplumbing and would revert #45979's cap parsing.Closes #48013
Infographic