mirror of
https://github.com/openai/codex.git
synced 2026-11-13 06:45:07 +00:00
Attribute nested REPL reviews to their tool calls (#42082)
## What changed - Retain MCP invocation metadata for Node-backed REPL servers so nested elicitations can resolve the enclosing tool call. - Use a valid, same-server `callId` from the current turn as the Guardian assessment target while continuing to review each nested action and connector independently. - Fall back to an elicitation-specific target when `callId` is missing, malformed, unknown, from another server, or from an earlier turn. ## Testing - Cover ordinary and strict reviews for `node_repl` and `cua_repl`, multiple nested decisions, unrelated servers, and invalid or stale call IDs. GitOrigin-RevId: adea4b5cff96726a540ff3b3ea902b0f5dd99299
This commit is contained in:
1 parent
2350823caa
commit
67cc3c318d
4 files changed
+366
-24
No files matched your search
@@ -13,6 +13,7 @@ use codex_mcp::MCP_TOOL_CODEX_APPS_META_KEY;
|
||||
use codex_protocol::capabilities::CapabilityRootLocation;
|
||||
use codex_protocol::capabilities::SelectedCapabilityRoot;
|
||||
use codex_protocol::config_types::ApprovalsReviewer;
|
||||
use codex_protocol::mcp::is_node_repl_backed_server;
|
||||
use codex_protocol::mcp_approval_meta::APPROVAL_KIND_KEY as MCP_ELICITATION_APPROVAL_KIND_KEY;
|
||||
use codex_protocol::mcp_approval_meta::APPROVAL_KIND_MCP_TOOL_CALL as MCP_ELICITATION_APPROVAL_KIND_MCP_TOOL_CALL;
|
||||
use codex_protocol::mcp_approval_meta::APPROVAL_KIND_TOOL_SUGGESTION as MCP_ELICITATION_APPROVAL_KIND_TOOL_SUGGESTION;
|
||||
@@ -724,6 +725,24 @@ async fn review_guardian_mcp_elicitation(
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// The invocation identifies the tool event, but a nested elicitation can
|
||||
// review a different action and connector than the enclosing JavaScript.
|
||||
let originating_call_id = if is_node_repl_backed_server(&request.server_name)
|
||||
&& let Some(call_id) = request
|
||||
.elicitation
|
||||
.meta()
|
||||
.and_then(|meta| meta.get("callId"))
|
||||
.and_then(Value::as_str)
|
||||
&& let Some((Some(invocation), _)) = session
|
||||
.mcp_tool_approval_metadata(&turn_context.sub_id, call_id)
|
||||
.await
|
||||
&& invocation.server == request.server_name
|
||||
{
|
||||
Some(call_id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let require_synchronous_review = matches!(
|
||||
request
|
||||
.elicitation
|
||||
@@ -804,7 +823,7 @@ async fn review_guardian_mcp_elicitation(
|
||||
}
|
||||
|
||||
let GuardianElicitationReview::ApprovalRequest(guardian_request) =
|
||||
guardian_elicitation_review_request(&request)
|
||||
guardian_elicitation_review_request(&request, originating_call_id)
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
@@ -884,7 +903,8 @@ async fn review_guardian_mcp_elicitation(
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let guardian_request = match guardian_elicitation_review_request(&request) {
|
||||
let guardian_request = match guardian_elicitation_review_request(&request, originating_call_id)
|
||||
{
|
||||
GuardianElicitationReview::NotRequested => return Ok(None),
|
||||
GuardianElicitationReview::Decline(reason) => {
|
||||
warn!(
|
||||
@@ -921,6 +941,7 @@ async fn review_guardian_mcp_elicitation(
|
||||
|
||||
fn guardian_elicitation_review_request(
|
||||
request: &ElicitationReviewRequest,
|
||||
originating_call_id: Option<&str>,
|
||||
) -> GuardianElicitationReview {
|
||||
let (meta, requested_schema) = match &request.elicitation {
|
||||
Elicitation::Mcp(rmcp::model::ElicitRequestParams::FormElicitationParams {
|
||||
@@ -987,11 +1008,13 @@ fn guardian_elicitation_review_request(
|
||||
|
||||
GuardianElicitationReview::ApprovalRequest(Box::new(
|
||||
crate::guardian::GuardianApprovalRequest::McpToolCall {
|
||||
id: format!(
|
||||
"mcp_elicitation:{}:{}",
|
||||
request.server_name,
|
||||
mcp_elicitation_request_id(&request.request_id)
|
||||
),
|
||||
id: originating_call_id.map(str::to_owned).unwrap_or_else(|| {
|
||||
format!(
|
||||
"mcp_elicitation:{}:{}",
|
||||
request.server_name,
|
||||
mcp_elicitation_request_id(&request.request_id)
|
||||
)
|
||||
}),
|
||||
server: request.server_name.clone(),
|
||||
tool_name,
|
||||
arguments,
|
||||
|
||||
@@ -47,7 +47,7 @@ fn guardian_elicitation_review_request_builds_mcp_tool_call() {
|
||||
}))));
|
||||
|
||||
let GuardianElicitationReview::ApprovalRequest(guardian_request) =
|
||||
guardian_elicitation_review_request(&request)
|
||||
guardian_elicitation_review_request(&request, /*originating_call_id*/ None)
|
||||
else {
|
||||
panic!("expected Guardian MCP tool call request");
|
||||
};
|
||||
@@ -86,7 +86,7 @@ fn guardian_elicitation_review_request_defaults_missing_tool_params() {
|
||||
let request = form_request(guardian_meta(/*tool_params*/ None));
|
||||
|
||||
let GuardianElicitationReview::ApprovalRequest(guardian_request) =
|
||||
guardian_elicitation_review_request(&request)
|
||||
guardian_elicitation_review_request(&request, /*originating_call_id*/ None)
|
||||
else {
|
||||
panic!("expected Guardian MCP tool call request");
|
||||
};
|
||||
@@ -163,7 +163,7 @@ fn guardian_elicitation_review_request_requires_opt_in() {
|
||||
})));
|
||||
|
||||
assert_eq!(
|
||||
guardian_elicitation_review_request(&request),
|
||||
guardian_elicitation_review_request(&request, /*originating_call_id*/ None),
|
||||
GuardianElicitationReview::NotRequested
|
||||
);
|
||||
}
|
||||
@@ -181,7 +181,7 @@ fn guardian_elicitation_review_request_declines_unsupported_opt_in_shapes() {
|
||||
}),
|
||||
};
|
||||
assert!(matches!(
|
||||
guardian_elicitation_review_request(&url_request),
|
||||
guardian_elicitation_review_request(&url_request, /*originating_call_id*/ None),
|
||||
GuardianElicitationReview::Decline(_)
|
||||
));
|
||||
|
||||
@@ -201,7 +201,10 @@ fn guardian_elicitation_review_request_declines_unsupported_opt_in_shapes() {
|
||||
}),
|
||||
};
|
||||
assert!(matches!(
|
||||
guardian_elicitation_review_request(&non_empty_schema_request),
|
||||
guardian_elicitation_review_request(
|
||||
&non_empty_schema_request,
|
||||
/*originating_call_id*/ None
|
||||
),
|
||||
GuardianElicitationReview::Decline(_)
|
||||
));
|
||||
|
||||
@@ -210,7 +213,10 @@ fn guardian_elicitation_review_request_declines_unsupported_opt_in_shapes() {
|
||||
"codex_request_type": "approval_request",
|
||||
})));
|
||||
assert!(matches!(
|
||||
guardian_elicitation_review_request(&missing_tool_name_request),
|
||||
guardian_elicitation_review_request(
|
||||
&missing_tool_name_request,
|
||||
/*originating_call_id*/ None
|
||||
),
|
||||
GuardianElicitationReview::Decline(_)
|
||||
));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user