mirror of
https://github.com/openai/codex.git
synced 2026-10-21 17:48:44 +00:00
Use issuing-step context for Guardian MCP elicitation reviews (#51067)
## Why MCP elicitation reviews used turn-start context, which could omit remote environments that became ready before the tool invocation and use stale environment permissions. ## What changed Capture the issuing step's Guardian review context with each live MCP invocation and reuse it for elicitation reviews. Fall back to turn context when no matching live invocation is available, and preserve the captured context for in-flight actions. ## Testing Extend the deferred-executor regression test to cover MCP elicitations, checking that Guardian receives the newly ready remote environment and its denied paths, excludes stale local denied paths, and returns the denial to the parent model. GitOrigin-RevId: 0cc58b8f30543a9af87d53e1a90f574a8e6a2f84
This commit is contained in:
@@ -2,6 +2,7 @@ use super::mcp_refresh::McpRefreshInvalidationGuard;
|
||||
use super::*;
|
||||
use crate::environment_selection::TurnEnvironmentSnapshot;
|
||||
use crate::environment_selection::combine_selected_capability_roots;
|
||||
use crate::mcp_tool_call::McpToolApprovalContext;
|
||||
use codex_exec_server::ExecutorCapabilityDiscoveryCache;
|
||||
use codex_exec_server::ExecutorCapabilityDiscoverySnapshot;
|
||||
use codex_exec_server::MAX_SELECTED_CAPABILITY_ROOTS;
|
||||
@@ -824,9 +825,14 @@ async fn review_guardian_mcp_elicitation(
|
||||
_ => meta.get("callId"),
|
||||
})
|
||||
.and_then(Value::as_str);
|
||||
let approval_context = call_id
|
||||
.and_then(|call_id| session.mcp_tool_approval_metadata(&request.server_name, call_id));
|
||||
let (originating_call_id, guardian_scope) = if let Some(call_id) = call_id
|
||||
&& let Some((Some(invocation), metadata)) =
|
||||
session.mcp_tool_approval_metadata(&request.server_name, call_id)
|
||||
&& let Some(McpToolApprovalContext {
|
||||
invocation: Some(invocation),
|
||||
metadata,
|
||||
..
|
||||
}) = approval_context.as_ref()
|
||||
&& invocation.server == request.server_name
|
||||
&& is_node_repl_backed_connector(&invocation.server, metadata.connector_id.as_deref())
|
||||
{
|
||||
@@ -843,7 +849,12 @@ async fn review_guardian_mcp_elicitation(
|
||||
Some(Value::Bool(true))
|
||||
);
|
||||
|
||||
let mut review_context = crate::guardian::GuardianReviewContext::from(&turn_context);
|
||||
// Bind the review to the issuing step, which may include environments that
|
||||
// became ready after the turn started. Do not refresh an in-flight action.
|
||||
let mut review_context = approval_context
|
||||
.as_ref()
|
||||
.map(|context| context.review_context.clone())
|
||||
.unwrap_or_else(|| crate::guardian::GuardianReviewContext::from(&turn_context));
|
||||
let strict_auto_review = matches!(
|
||||
request
|
||||
.elicitation
|
||||
@@ -858,18 +869,14 @@ async fn review_guardian_mcp_elicitation(
|
||||
let review_outer_invocation =
|
||||
request.server_name == CODEX_APPS_MCP_SERVER_NAME && originating_call_id.is_none();
|
||||
let trusted_guardian_request = if review_outer_invocation {
|
||||
let Some(call_id) = request
|
||||
.elicitation
|
||||
.meta()
|
||||
.and_then(|meta| meta.get(MCP_TOOL_CODEX_APPS_META_KEY))
|
||||
.and_then(Value::as_object)
|
||||
.and_then(|meta| meta.get("call_id"))
|
||||
.and_then(Value::as_str)
|
||||
else {
|
||||
let Some(call_id) = call_id else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some((Some(invocation), metadata)) =
|
||||
session.mcp_tool_approval_metadata(&request.server_name, call_id)
|
||||
let Some(McpToolApprovalContext {
|
||||
invocation: Some(invocation),
|
||||
metadata,
|
||||
..
|
||||
}) = approval_context.as_ref()
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
@@ -886,8 +893,8 @@ async fn review_guardian_mcp_elicitation(
|
||||
Some(
|
||||
crate::mcp_tool_call::build_guardian_mcp_tool_review_request(
|
||||
call_id,
|
||||
&invocation,
|
||||
Some(&metadata),
|
||||
invocation,
|
||||
Some(metadata),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::config::ConstraintError;
|
||||
use crate::environment_selection::ThreadEnvironments;
|
||||
use crate::environment_selection::TurnEnvironmentSnapshot;
|
||||
use crate::hook_mcp_executor::CoreHookMcpExecutor;
|
||||
use crate::mcp_tool_call::McpToolApprovalMetadata;
|
||||
use crate::mcp_tool_call::McpToolApprovalContext;
|
||||
use crate::responses_metadata::CodexResponsesMetadata;
|
||||
use crate::responses_metadata::CodexResponsesRequestKind;
|
||||
use crate::responses_metadata::CompactionTurnMetadata;
|
||||
@@ -39,7 +39,6 @@ use codex_protocol::permissions::FileSystemPath;
|
||||
use codex_protocol::permissions::FileSystemSpecialPath;
|
||||
use codex_protocol::protocol::EnvironmentConfig;
|
||||
use codex_protocol::protocol::HookCompletedEvent;
|
||||
use codex_protocol::protocol::McpInvocation;
|
||||
use codex_protocol::protocol::MultiAgentVersion;
|
||||
use codex_protocol::protocol::ThreadHistoryMode;
|
||||
use codex_protocol::protocol::ThreadSource;
|
||||
@@ -52,7 +51,7 @@ use std::sync::OnceLock;
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
type McpToolApprovalMetadataMap =
|
||||
HashMap<(String, String), std::sync::Weak<(Option<McpInvocation>, McpToolApprovalMetadata)>>;
|
||||
HashMap<(String, String), std::sync::Weak<McpToolApprovalContext>>;
|
||||
|
||||
/// Context for an initialized model agent
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user