Route MCP elicitations through the shared approval decision path (#43447)

## What changed

- Use `decide_approval` for MCP elicitations, carrying the effective approval policy, reviewer, and synchronous-review requirement into the decision.
- Preserve unsupported form and URL elicitations for user review.
- Use `computer_use_review_required()` for review evidence, session policy, and turn metadata so model policy takes precedence over the legacy flag.
- Derive strict-review notifications from each assessment's review reason.

## Testing

Add coverage for model policy precedence, user and full-access modes, sensitive elicitations, independent code-mode and nested-tool policies, cached score reuse, and user review of unsupported elicitations.

GitOrigin-RevId: d0a9c40572dca7b3dbf876230d2162b72728084d
This commit is contained in:
jif
2026-09-07 12:04:27 +00:00
committed by copyberry
parent c0b6285711
commit ce5c4133bd
12 changed files with 585 additions and 129 deletions
@@ -97,7 +97,6 @@ use codex_app_server_protocol::item_event_to_server_notification;
use codex_core::CodexThread;
use codex_core::ThreadManager;
use codex_features::Feature;
use codex_guardian_v2::StrictReviewReason;
use codex_protocol::ThreadId;
use codex_protocol::items::CollabAgentTool as CoreCollabAgentTool;
use codex_protocol::items::TurnItem as CoreTurnItem;
@@ -355,21 +354,16 @@ pub(crate) async fn apply_bespoke_event_handling(
);
outgoing.send_server_notification(notification).await;
if assessment.status == codex_protocol::protocol::GuardianAssessmentStatus::InProgress
&& match assessment.review_reason {
Some(reason) => matches!(
reason,
&& matches!(
assessment.review_reason,
Some(
codex_protocol::approvals::GuardianReviewReason::ElevatedRisk
| codex_protocol::approvals::GuardianReviewReason::StaleScore
| codex_protocol::approvals::GuardianReviewReason::IncompatibleCompaction
| codex_protocol::approvals::GuardianReviewReason::ScoringFailure
| codex_protocol::approvals::GuardianReviewReason::AuthorizationChanged
),
// MCP elicitations migrate to per-approval reasons next.
None => conversation
.thread_extension_data()
.remove::<StrictReviewReason>()
.is_some(),
}
)
)
{
outgoing
.send_server_notification(ServerNotification::StrictReviewRequired(