Allow deferring plain-text MCP output in thread item listings (#60390)

## What changed

Add experimental `deferOutput.mcpToolCall` support to `thread/items/list` requests scoped to a `turnId`. Successful, completed MCP calls with nonempty, unannotated plain-text results can return empty `result.content` with `outputDeferred: true`. Clients can retrieve the saved output through `thread/items/read`; connector identity remains on the listed item.

Keep rich results, JSON or fenced text, app UI results, and built-in `codex_app` results inline. Preserve plain-text output when creating bounded previews of oversized MCP results so those previews remain eligible for deferral. Reads return the saved preview when output exceeds the 64 KiB history limit.

## Testing

Extend thread history integration coverage for independent and combined command/MCP deferral, experimental capability gating, excluded result types, connector identity, oversized previews, and retrieval of deferred items.

GitOrigin-RevId: 5c160b30a4eac2a9b70e3fa65b6dbd83e935338b
This commit is contained in:
Brent Traut
2026-11-09 12:54:12 +00:00
committed by copyberry
parent e787af9624
commit d2b4d63e95
7 changed files with 307 additions and 64 deletions
@@ -1879,6 +1879,10 @@ pub struct ThreadItemOutputDeferral {
#[experimental("thread/items/list.deferOutput.commandExecution")]
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub command_execution: bool,
/// Defer nonempty plain-text results from successful MCP tool calls.
#[experimental("thread/items/list.deferOutput.mcpToolCall")]
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub mcp_tool_call: bool,
}
/// Starting position for an item-history page.
@@ -1909,7 +1913,7 @@ pub struct ThreadItemEntry {
/// Turn containing this item.
pub turn_id: String,
pub item: ThreadItem,
/// The saved command output was omitted. Fetch the complete item with `thread/items/read`.
/// The saved output was omitted. Fetch the complete item with `thread/items/read`.
#[experimental("thread/items/list.outputDeferred")]
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub output_deferred: bool,