core: resize all history images behind a feature flag (#27247)

## Summary

Adds complete client-side image preparation behind the default-off
`resize_all_images` feature flag.

When enabled, local image producers defer decoding and resizing. Images
are prepared centrally before insertion into conversation history,
covering user input, `view_image`, and structured tool-output images.

## Behavior

- Processes base64 `data:` images in messages and function/custom tool
outputs.
- Leaves non-data URLs, including HTTP(S) URLs, unchanged.
- Applies image-detail budgets:
  - `high` and omitted: 2048px maximum dimension and 2.5K 32px patches.
  - `original`: 6000px maximum dimension and 10K 32px patches.
  - `auto`: uses the same 2048px / 2.5K-patch budget as high.
  - `low`: unsupported and replaced with an actionable placeholder.
- Preserves original image bytes when no resize or format conversion is
needed.
- Enforces the shared 1 GiB encoded and decoded data-URL sanity limits.
- Replaces only an image that fails preparation, preserving sibling
content and tool-output metadata.
- Uses bounded placeholders distinguishing generic processing failures,
oversized images, and unsupported `low` detail.
- Prepares resumed and forked history before installing it as live
history without modifying persisted rollouts.

## Flag-Off Behavior

When `resize_all_images` is disabled:

- Existing local user-input and `view_image` processing remains
unchanged.
- Existing decoding and error behavior remains unchanged.
- Arbitrary tool-output images are not processed.
- HTTP(S) image URLs continue to be forwarded unchanged.


#### [git stack](https://github.com/magus/git-stack-cli)
-  `1` https://github.com/openai/codex/pull/27245
- 👉 `2` https://github.com/openai/codex/pull/27247
-  `3` https://github.com/openai/codex/pull/27246
-  `4` https://github.com/openai/codex/pull/27266
This commit is contained in:
Curtis 'Fjord' Hawthorne
2026-06-10 19:21:24 -07:00
committed by GitHub
Unverified
parent 9d87b771ce
commit a6f435ea94
15 changed files with 907 additions and 58 deletions
+2 -4
View File
@@ -105,8 +105,7 @@ async fn preserves_large_image_in_original_mode() {
async fn data_url_processing_preserves_supported_source_bytes() {
let image = ImageBuffer::from_pixel(64, 32, Rgba([10u8, 20, 30, 255]));
let original_bytes = image_bytes(&image, ImageFormat::Png);
let encoded = BASE64_STANDARD.encode(&original_bytes);
let image_url = format!("data:image/png;base64,{encoded}")
let image_url = data_url_from_bytes("image/png", &original_bytes)
.replacen("data:", "DATA:", 1)
.replacen(";base64,", ";BASE64,", 1);
@@ -123,8 +122,7 @@ async fn data_url_processing_preserves_supported_source_bytes() {
async fn data_url_processing_converts_gif_to_png() {
let image = ImageBuffer::from_pixel(64, 32, Rgba([10u8, 20, 30, 255]));
let gif_bytes = image_bytes(&image, ImageFormat::Gif);
let encoded = BASE64_STANDARD.encode(&gif_bytes);
let image_url = format!("data:image/gif;base64,{encoded}");
let image_url = data_url_from_bytes("image/gif", &gif_bytes);
let processed = load_data_url_for_prompt(&image_url, PromptImageMode::ResizeToFit)
.expect("process GIF data URL");
+7 -2
View File
@@ -40,11 +40,16 @@ pub struct EncodedImage {
impl EncodedImage {
pub fn into_data_url(self) -> String {
let encoded = BASE64_STANDARD.encode(&self.bytes);
format!("data:{};base64,{encoded}", self.mime)
data_url_from_bytes(&self.mime, &self.bytes)
}
}
/// Wraps image bytes in a data URL without decoding or validating them.
pub fn data_url_from_bytes(mime: &str, bytes: &[u8]) -> String {
let encoded = BASE64_STANDARD.encode(bytes);
format!("data:{mime};base64,{encoded}")
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PromptImageMode {
ResizeToFit,