Add experimental command output deferral to thread item listings (#59601)

## What changed

Allow experimental clients to request `deferOutput: {"commandExecution": true}` in `thread/items/list` for a specific `turnId`. Completed commands with a recorded completion time, exit code zero, and nonempty saved output return `outputDeferred: true` and `item.aggregatedOutput: null`. Clients can retrieve the complete output through `thread/items/read` using the turn and item IDs.

Keep full output by default and for listings across turns. Make conversion from `ThreadItemEntry` to `ThreadItem` fallible so Rust consumers detect deferred output instead of treating an incomplete item as complete.

## Testing

Add integration coverage for experimental capability gating, default and disabled options, command status and output eligibility, listings across turns, deferred-item conversion, and full output retrieval by item ID.

GitOrigin-RevId: d6bfac34bb68f7e3b327fd5553270ed6d0fe87cf
This commit is contained in:
Brent Traut
2026-11-05 08:10:44 +00:00
committed by copyberry
parent edd0d89820
commit e7f32cf192
26 changed files with 415 additions and 67 deletions
+12 -1
View File
@@ -1788,6 +1788,7 @@ async fn maybe_backfill_turn_completed_items(
cursor,
limit: None,
sort_direction: None,
defer_output: None,
},
})
.await
@@ -1798,7 +1799,17 @@ async fn maybe_backfill_turn_completed_items(
return;
}
};
items.extend(page.data.into_iter().map(ThreadItem::from));
for entry in page.data {
match ThreadItem::try_from(entry) {
Ok(item) => items.push(item),
Err(err) => {
warn!(
"thread/items/list returned an incomplete item during turn completion: {err}"
);
return;
}
}
}
let Some(next) = page.next_cursor else { break };
if !seen_cursors.insert(next.clone()) {
warn!("thread/items/list returned a repeated cursor during turn completion");