Propagate approval rejection reasons (#34400)

## What changed

- Change `ReviewDecision::Denied` to carry a rejection string and update the generated protocol schemas.
- Preserve specific rejection reasons through command, patch, network, MCP, delegated, and automatic approval flows so tool results can return them to the model.
- Distinguish invalid approval responses from user declines and truncate oversized rejection messages before adding them to model context.

## Testing

- Add coverage for denial serialization, invalid app-server approval responses, dropped automatic reviews, network rejection propagation, and rejection-message truncation.

GitOrigin-RevId: d48a55643aa03c7ecfa08a8e4b669913eec5464a
This commit is contained in:
Dylan Hurd authored and copyberry committed 2026-07-20 19:23:21 +00:00
1 parent 8c41ed33ce
commit e52c35b000
36 files changed
+499 -452

No files matched your search

+1 -1
View File
@@ -130,7 +130,7 @@ async fn on_exec_approval_response(
// If we cannot deserialize the response, we deny the request to be
// conservative.
ExecApprovalResponse {
decision: ReviewDecision::Denied,
decision: ReviewDecision::denied("approval request failed"),
}
});
+2 -2
View File
@@ -113,7 +113,7 @@ pub(crate) async fn on_patch_approval_response(
if let Err(submit_err) = codex
.submit(Op::PatchApproval {
id: approval_id.clone(),
decision: ReviewDecision::Denied,
decision: ReviewDecision::denied("approval request failed"),
})
.await
{
@@ -126,7 +126,7 @@ pub(crate) async fn on_patch_approval_response(
let response = serde_json::from_value::<PatchApprovalResponse>(value).unwrap_or_else(|err| {
error!("failed to deserialize PatchApprovalResponse: {err}");
PatchApprovalResponse {
decision: ReviewDecision::Denied,
decision: ReviewDecision::denied("approval request failed"),
}
});