Preserve complete actions in Guardian approval reviews (#44569)

## Why

Truncating action arguments can leave approval reviewers evaluating incomplete actions. Large actions need complete review input and explicit handling when they exceed the review budget.

## What changed

- Remove action truncation and the fixed synchronous action byte limit. Admit complete actions against the whole-request budget, splitting long text losslessly into bounded transport parts and accounting for their framing.
- Route actions exceeding the asynchronous action budget to synchronous review. Prevent cached scores from covering oversized calls, including expanded approval arguments, while allowing later small actions to recover score reuse.
- Request user approval when optional review exhausts its local input budget. Keep required review and compaction service failures closed to approval, and retire exhausted review sessions.

## Testing

Add coverage for complete large-action delivery, optional user fallback, required-review denial, subsequent review recovery, async overflow through MCP approval routing, and lossless text splitting with budget accounting.

GitOrigin-RevId: 08f06e68a94b2f779480dd6b6cf5bc30b241f6dd
This commit is contained in:
felixxia-oai
2026-09-10 16:01:02 +00:00
committed by copyberry
parent 3319d9b296
commit 9c4879f3a5
33 changed files with 841 additions and 547 deletions
@@ -865,11 +865,9 @@ impl UnifiedExecProcessManager {
.map_err(|_| unreviewable_input_error())?;
// Bound the entire serialized action plus its reason, including JSON
// escaping. Reject, never execute an unreviewed tail.
let oversized = reviewed.text.len().saturating_add(approval_reason.len())
> MAX_STDIN_APPROVAL_BYTES;
let size_check_result = if reviewed.truncated {
"formatter_truncated"
} else if oversized {
let oversized =
reviewed.len().saturating_add(approval_reason.len()) > MAX_STDIN_APPROVAL_BYTES;
let size_check_result = if oversized {
"over_limit"
} else {
"within_limit"
@@ -884,7 +882,7 @@ impl UnifiedExecProcessManager {
/*inc*/ 1,
&[("result", size_check_result), ("input_kind", input_kind)],
);
if reviewed.truncated || oversized {
if oversized {
return Err(unreviewable_input_error());
}
let approval_context = ApprovalContext {