Store generated image items as attachment references (#54398)

## What changed

- Upload generated images to the attachment store and expose `fileId` in image-generation items when available, with inline `result` fallback on upload or decoding failure.
- Preserve file references through legacy events, persisted rollouts, thread history, and task output summaries. Keep model-facing image preparation separate.
- Add attachment request tags and mark generated-image uploads with `usage=image_gen`, preserving the thread's ephemeral setting.
- Update public schemas and SDK types to accept either image representation, retain the Python `ImageGenerationThreadItem` alias, and prefer `fileId` when reading records containing both fields.

## Testing

Add coverage for file-backed generation and rollout persistence, legacy history replay, inline and transitional serialization, task output visibility, and Python SDK alias compatibility.

GitOrigin-RevId: f7a2bb5707e2f277348a547a988bc33c8e96d891
This commit is contained in:
Krish Chainani
2026-10-15 19:51:22 +00:00
committed by copyberry
parent 1ef30bcbe7
commit aabf13307a
64 changed files with 1229 additions and 125 deletions
@@ -217,7 +217,7 @@ pub(super) fn completed_item(
id: event.call_id.clone(),
status: event.status.clone(),
revised_prompt: event.revised_prompt.clone(),
result: event.result.clone(),
image: event.image.clone(),
transparent_background: event.transparent_background,
failure: event.failure.clone(),
saved_path: event.saved_path.clone(),
@@ -9,6 +9,7 @@ use std::os::unix::fs::PermissionsExt;
use codex_app_server_protocol::build_turns_from_rollout_items;
use codex_extension_items::ExtensionItem;
use codex_extension_items::image_generation::GeneratedImageRef;
use codex_extension_items::image_generation::ImageGenerationFailure;
use codex_extension_items::image_generation::ImageGenerationItem;
use codex_protocol::AgentPath;
@@ -532,7 +533,9 @@ async fn migration_preserves_image_generation_failure_metadata() {
id: "image-call".to_string(),
status: "failed".to_string(),
revised_prompt: Some("paint a blue whale".to_string()),
result: String::new(),
image: GeneratedImageRef::Inline {
result: String::new(),
},
transparent_background: None,
failure: Some(ImageGenerationFailure::UsageLimitExceeded {
limit_id: "image_gen".to_string(),
@@ -547,7 +550,7 @@ async fn migration_preserves_image_generation_failure_metadata() {
call_id: expected_item.id.clone(),
status: expected_item.status.clone(),
revised_prompt: expected_item.revised_prompt.clone(),
result: expected_item.result.clone(),
image: expected_item.image.clone(),
transparent_background: expected_item.transparent_background,
failure: expected_item.failure.clone(),
saved_path: expected_item.saved_path.clone(),