make event emits with the effective command

This commit is contained in:
Qiyao Qin
2026-03-14 16:22:35 -07:00
parent bccfefd43c
commit 4c5d2f4f07
6 changed files with 81 additions and 44 deletions
@@ -216,6 +216,7 @@ impl ToolHandler for ApplyPatchHandler {
call_id: call_id.clone(),
tool_name: tool_name.to_string(),
command_override: None,
effective_command: None,
};
let out = orchestrator
.run(
@@ -321,6 +322,7 @@ pub(crate) async fn intercept_apply_patch(
call_id: call_id.to_string(),
tool_name: tool_name.to_string(),
command_override: None,
effective_command: None,
};
let out = orchestrator
.run(
+9 -8
View File
@@ -411,14 +411,6 @@ impl ShellHandler {
}
let source = ExecCommandSource::Agent;
let emitter = ToolEmitter::shell(
exec_params.command.clone(),
exec_params.cwd.clone(),
source,
freeform,
);
let event_ctx = ToolEventCtx::new(session.as_ref(), turn.as_ref(), &call_id, None);
emitter.begin(event_ctx).await;
let exec_approval_requirement = session
.services
@@ -462,12 +454,15 @@ impl ShellHandler {
}
}
};
let effective_command =
std::sync::Arc::new(tokio::sync::Mutex::new(exec_params.command.clone()));
let tool_ctx = ToolCtx {
session: session.clone(),
turn: turn.clone(),
call_id: call_id.clone(),
tool_name,
command_override: None,
effective_command: Some(std::sync::Arc::clone(&effective_command)),
};
let out = orchestrator
.run(
@@ -479,6 +474,12 @@ impl ShellHandler {
)
.await
.map(|result| result.output);
let emitter = ToolEmitter::shell(
effective_command.lock().await.clone(),
exec_params.cwd.clone(),
source,
freeform,
);
let event_ctx = ToolEventCtx::new(session.as_ref(), turn.as_ref(), &call_id, None);
let content = emitter.finish(event_ctx, out).await?;
Ok(FunctionToolOutput::from_text(content, Some(true)))
+27 -24
View File
@@ -52,7 +52,6 @@ impl ToolOrchestrator {
req: &Rq,
tool_ctx: &ToolCtx,
attempt: &SandboxAttempt<'_>,
command_override: Option<Vec<String>>,
has_managed_network_requirements: bool,
) -> (Result<Out, ToolError>, Option<DeferredNetworkApproval>)
where
@@ -66,14 +65,7 @@ impl ToolOrchestrator {
)
.await;
let attempt_tool_ctx = ToolCtx {
session: tool_ctx.session.clone(),
turn: tool_ctx.turn.clone(),
call_id: tool_ctx.call_id.clone(),
tool_name: tool_ctx.tool_name.clone(),
command_override,
};
let run_result = tool.run(req, attempt, &attempt_tool_ctx).await;
let run_result = tool.run(req, attempt, tool_ctx).await;
let Some(network_approval) = network_approval else {
return (run_result, None);
@@ -115,7 +107,14 @@ impl ToolOrchestrator {
let otel_ci = &tool_ctx.call_id;
let otel_user = ToolDecisionSource::User;
let otel_cfg = ToolDecisionSource::Config;
let mut command_override: Option<Vec<String>> = None;
let mut effective_tool_ctx = ToolCtx {
session: tool_ctx.session.clone(),
turn: tool_ctx.turn.clone(),
call_id: tool_ctx.call_id.clone(),
tool_name: tool_ctx.tool_name.clone(),
command_override: tool_ctx.command_override.clone(),
effective_command: tool_ctx.effective_command.clone(),
};
// 1) Approval
let mut already_approved = false;
@@ -132,18 +131,21 @@ impl ToolOrchestrator {
}
ExecApprovalRequirement::NeedsApproval { reason, .. } => {
let approval_ctx = ApprovalCtx {
session: &tool_ctx.session,
turn: &tool_ctx.turn,
call_id: &tool_ctx.call_id,
session: &effective_tool_ctx.session,
turn: &effective_tool_ctx.turn,
call_id: &effective_tool_ctx.call_id,
retry_reason: reason,
network_approval_context: None,
command_override: None,
command_override: effective_tool_ctx.command_override.clone(),
};
let decision = tool.start_approval_async(req, approval_ctx).await;
otel.tool_decision(otel_tn, otel_ci, &decision, otel_user.clone());
if let Some(command) = decision.override_command() {
command_override = Some(command.to_vec());
effective_tool_ctx.command_override = Some(command.to_vec());
if let Some(effective_command) = &effective_tool_ctx.effective_command {
*effective_command.lock().await = command.to_vec();
}
}
match decision {
@@ -213,9 +215,8 @@ impl ToolOrchestrator {
let (first_result, first_deferred_network_approval) = Self::run_attempt(
tool,
req,
tool_ctx,
&effective_tool_ctx,
&initial_attempt,
command_override.clone(),
has_managed_network_requirements,
)
.await;
@@ -287,18 +288,21 @@ impl ToolOrchestrator {
&& network_approval_context.is_none();
if !bypass_retry_approval {
let approval_ctx = ApprovalCtx {
session: &tool_ctx.session,
turn: &tool_ctx.turn,
call_id: &tool_ctx.call_id,
session: &effective_tool_ctx.session,
turn: &effective_tool_ctx.turn,
call_id: &effective_tool_ctx.call_id,
retry_reason: Some(retry_reason),
network_approval_context: network_approval_context.clone(),
command_override: command_override.clone(),
command_override: effective_tool_ctx.command_override.clone(),
};
let decision = tool.start_approval_async(req, approval_ctx).await;
otel.tool_decision(otel_tn, otel_ci, &decision, otel_user);
if let Some(command) = decision.override_command() {
command_override = Some(command.to_vec());
effective_tool_ctx.command_override = Some(command.to_vec());
if let Some(effective_command) = &effective_tool_ctx.effective_command {
*effective_command.lock().await = command.to_vec();
}
}
match decision {
@@ -346,9 +350,8 @@ impl ToolOrchestrator {
let (retry_result, retry_deferred_network_approval) = Self::run_attempt(
tool,
req,
tool_ctx,
&effective_tool_ctx,
&escalated_attempt,
command_override,
has_managed_network_requirements,
)
.await;
+13
View File
@@ -225,6 +225,19 @@ impl ToolRuntime<ShellRequest, ExecToolCallOutput> for ShellRuntime {
) -> Result<ExecToolCallOutput, ToolError> {
let session_shell = ctx.session.user_shell();
let base_command = ctx.command_override.as_ref().unwrap_or(&req.command);
crate::tools::events::ToolEmitter::shell(
base_command.to_vec(),
req.cwd.clone(),
ExecCommandSource::Agent,
false,
)
.begin(crate::tools::events::ToolEventCtx::new(
ctx.session.as_ref(),
ctx.turn.as_ref(),
&ctx.call_id,
None,
))
.await;
let command = maybe_wrap_shell_lc_with_snapshot(
base_command,
session_shell.as_ref(),
+1
View File
@@ -303,6 +303,7 @@ pub(crate) struct ToolCtx {
pub call_id: String,
pub tool_name: String,
pub command_override: Option<Vec<String>>,
pub effective_command: Option<Arc<tokio::sync::Mutex<Vec<String>>>>,
}
#[derive(Debug)]
@@ -165,10 +165,12 @@ impl UnifiedExecProcessManager {
.open_session_with_sandbox(&request, cwd.clone(), context)
.await;
let (process, mut deferred_network_approval) = match process {
Ok((process, deferred_network_approval)) => {
(Arc::new(process), deferred_network_approval)
}
let (process, mut deferred_network_approval, effective_command) = match process {
Ok((process, deferred_network_approval, effective_command)) => (
Arc::new(process),
deferred_network_approval,
effective_command,
),
Err(err) => {
self.release_process_id(request.process_id).await;
return Err(err);
@@ -183,7 +185,7 @@ impl UnifiedExecProcessManager {
None,
);
let emitter = ToolEmitter::unified_exec(
&request.command,
&effective_command,
cwd.clone(),
ExecCommandSource::UnifiedExecStartup,
Some(request.process_id.to_string()),
@@ -236,7 +238,7 @@ impl UnifiedExecProcessManager {
Arc::clone(&context.session),
Arc::clone(&context.turn),
context.call_id.clone(),
request.command.clone(),
effective_command.clone(),
cwd.clone(),
Some(process_id.to_string()),
Arc::clone(&transcript),
@@ -264,7 +266,7 @@ impl UnifiedExecProcessManager {
self.store_process(
Arc::clone(&process),
context,
&request.command,
&effective_command,
cwd.clone(),
start,
process_id,
@@ -289,7 +291,7 @@ impl UnifiedExecProcessManager {
},
exit_code,
original_token_count: Some(original_token_count),
session_command: Some(request.command.clone()),
session_command: Some(effective_command.clone()),
};
Ok(response)
@@ -572,7 +574,14 @@ impl UnifiedExecProcessManager {
request: &ExecCommandRequest,
cwd: PathBuf,
context: &UnifiedExecContext,
) -> Result<(UnifiedExecProcess, Option<DeferredNetworkApproval>), UnifiedExecError> {
) -> Result<
(
UnifiedExecProcess,
Option<DeferredNetworkApproval>,
Vec<String>,
),
UnifiedExecError,
> {
let env = apply_unified_exec_env(create_env(
&context.turn.shell_environment_policy,
Some(context.session.conversation_id),
@@ -613,14 +622,17 @@ impl UnifiedExecProcessManager {
justification: request.justification.clone(),
exec_approval_requirement,
};
let effective_command =
std::sync::Arc::new(tokio::sync::Mutex::new(request.command.clone()));
let tool_ctx = ToolCtx {
session: context.session.clone(),
turn: context.turn.clone(),
call_id: context.call_id.clone(),
tool_name: "exec_command".to_string(),
command_override: None,
effective_command: Some(std::sync::Arc::clone(&effective_command)),
};
orchestrator
let result = orchestrator
.run(
&mut runtime,
&req,
@@ -629,8 +641,13 @@ impl UnifiedExecProcessManager {
context.turn.approval_policy.value(),
)
.await
.map(|result| (result.output, result.deferred_network_approval))
.map_err(|e| UnifiedExecError::create_process(format!("{e:?}")))
.map_err(|e| UnifiedExecError::create_process(format!("{e:?}")))?;
let effective_command = effective_command.lock().await.clone();
Ok((
result.output,
result.deferred_network_approval,
effective_command,
))
}
pub(super) async fn collect_output_until_deadline(