|
1 | 1 | use std::collections::BTreeMap; |
2 | | -use std::collections::HashMap; |
3 | | -use std::sync::Arc; |
4 | 2 |
|
| 3 | +use crate::apply_patch; |
| 4 | +use crate::apply_patch::InternalApplyPatchInvocation; |
| 5 | +use crate::apply_patch::convert_apply_patch_to_protocol; |
5 | 6 | use crate::client_common::tools::FreeformTool; |
6 | 7 | use crate::client_common::tools::FreeformToolFormat; |
7 | 8 | use crate::client_common::tools::ResponsesApiTool; |
8 | 9 | use crate::client_common::tools::ToolSpec; |
9 | | -use crate::exec::ExecParams; |
10 | 10 | use crate::function_tool::FunctionCallError; |
11 | 11 | use crate::tools::context::ToolInvocation; |
12 | 12 | use crate::tools::context::ToolOutput; |
13 | 13 | use crate::tools::context::ToolPayload; |
14 | | -use crate::tools::handle_container_exec_with_params; |
| 14 | +use crate::tools::events::ToolEmitter; |
| 15 | +use crate::tools::events::ToolEventCtx; |
| 16 | +use crate::tools::orchestrator::ToolOrchestrator; |
15 | 17 | use crate::tools::registry::ToolHandler; |
16 | 18 | use crate::tools::registry::ToolKind; |
| 19 | +use crate::tools::runtimes::apply_patch::ApplyPatchRequest; |
| 20 | +use crate::tools::runtimes::apply_patch::ApplyPatchRuntime; |
| 21 | +use crate::tools::sandboxing::ToolCtx; |
17 | 22 | use crate::tools::spec::ApplyPatchToolArgs; |
18 | 23 | use crate::tools::spec::JsonSchema; |
19 | 24 | use async_trait::async_trait; |
@@ -64,30 +69,85 @@ impl ToolHandler for ApplyPatchHandler { |
64 | 69 | } |
65 | 70 | }; |
66 | 71 |
|
67 | | - let exec_params = ExecParams { |
68 | | - command: vec!["apply_patch".to_string(), patch_input.clone()], |
69 | | - cwd: turn.cwd.clone(), |
70 | | - timeout_ms: None, |
71 | | - env: HashMap::new(), |
72 | | - with_escalated_permissions: None, |
73 | | - justification: None, |
74 | | - arg0: None, |
75 | | - }; |
| 72 | + // Re-parse and verify the patch so we can compute changes and approval. |
| 73 | + // Avoid building temporary ExecParams/command vectors; derive directly from inputs. |
| 74 | + let cwd = turn.cwd.clone(); |
| 75 | + let command = vec!["apply_patch".to_string(), patch_input.clone()]; |
| 76 | + match codex_apply_patch::maybe_parse_apply_patch_verified(&command, &cwd) { |
| 77 | + codex_apply_patch::MaybeApplyPatchVerified::Body(changes) => { |
| 78 | + match apply_patch::apply_patch(session.as_ref(), turn.as_ref(), &call_id, changes) |
| 79 | + .await |
| 80 | + { |
| 81 | + InternalApplyPatchInvocation::Output(item) => { |
| 82 | + let content = item?; |
| 83 | + Ok(ToolOutput::Function { |
| 84 | + content, |
| 85 | + success: Some(true), |
| 86 | + }) |
| 87 | + } |
| 88 | + InternalApplyPatchInvocation::DelegateToExec(apply) => { |
| 89 | + let emitter = ToolEmitter::apply_patch( |
| 90 | + convert_apply_patch_to_protocol(&apply.action), |
| 91 | + !apply.user_explicitly_approved_this_action, |
| 92 | + ); |
| 93 | + let event_ctx = ToolEventCtx::new( |
| 94 | + session.as_ref(), |
| 95 | + turn.as_ref(), |
| 96 | + &call_id, |
| 97 | + Some(&tracker), |
| 98 | + ); |
| 99 | + emitter.begin(event_ctx).await; |
76 | 100 |
|
77 | | - let content = handle_container_exec_with_params( |
78 | | - tool_name.as_str(), |
79 | | - exec_params, |
80 | | - Arc::clone(&session), |
81 | | - Arc::clone(&turn), |
82 | | - Arc::clone(&tracker), |
83 | | - call_id.clone(), |
84 | | - ) |
85 | | - .await?; |
| 101 | + let req = ApplyPatchRequest { |
| 102 | + patch: apply.action.patch.clone(), |
| 103 | + cwd, |
| 104 | + timeout_ms: None, |
| 105 | + user_explicitly_approved: apply.user_explicitly_approved_this_action, |
| 106 | + codex_exe: turn.codex_linux_sandbox_exe.clone(), |
| 107 | + }; |
86 | 108 |
|
87 | | - Ok(ToolOutput::Function { |
88 | | - content, |
89 | | - success: Some(true), |
90 | | - }) |
| 109 | + let mut orchestrator = ToolOrchestrator::new(); |
| 110 | + let mut runtime = ApplyPatchRuntime::new(); |
| 111 | + let tool_ctx = ToolCtx { |
| 112 | + session: session.as_ref(), |
| 113 | + turn: turn.as_ref(), |
| 114 | + call_id: call_id.clone(), |
| 115 | + tool_name: tool_name.to_string(), |
| 116 | + }; |
| 117 | + let out = orchestrator |
| 118 | + .run(&mut runtime, &req, &tool_ctx, &turn, turn.approval_policy) |
| 119 | + .await; |
| 120 | + let event_ctx = ToolEventCtx::new( |
| 121 | + session.as_ref(), |
| 122 | + turn.as_ref(), |
| 123 | + &call_id, |
| 124 | + Some(&tracker), |
| 125 | + ); |
| 126 | + let content = emitter.finish(event_ctx, out).await?; |
| 127 | + Ok(ToolOutput::Function { |
| 128 | + content, |
| 129 | + success: Some(true), |
| 130 | + }) |
| 131 | + } |
| 132 | + } |
| 133 | + } |
| 134 | + codex_apply_patch::MaybeApplyPatchVerified::CorrectnessError(parse_error) => { |
| 135 | + Err(FunctionCallError::RespondToModel(format!( |
| 136 | + "apply_patch verification failed: {parse_error}" |
| 137 | + ))) |
| 138 | + } |
| 139 | + codex_apply_patch::MaybeApplyPatchVerified::ShellParseError(error) => { |
| 140 | + tracing::trace!("Failed to parse apply_patch input, {error:?}"); |
| 141 | + Err(FunctionCallError::RespondToModel( |
| 142 | + "apply_patch handler received invalid patch input".to_string(), |
| 143 | + )) |
| 144 | + } |
| 145 | + codex_apply_patch::MaybeApplyPatchVerified::NotApplyPatch => { |
| 146 | + Err(FunctionCallError::RespondToModel( |
| 147 | + "apply_patch handler received non-apply_patch input".to_string(), |
| 148 | + )) |
| 149 | + } |
| 150 | + } |
91 | 151 | } |
92 | 152 | } |
93 | 153 |
|
|
0 commit comments