mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
Standardize shell execution on unified exec (#39772)
## What changed - Use `exec_command` and `write_stdin` as the shell tool surface. - Treat legacy `default`, `local`, and `shell_command` model metadata as `unified_exec`. - Remove obsolete shell-selection configuration and runtime paths while preserving the feature and policy gates for zsh fork execution. GitOrigin-RevId: d743cbe598630d73052f1fecad680c4cde17977d
This commit is contained in:
@@ -112,7 +112,6 @@ impl ToolCallRuntime {
|
||||
let router = &self.step_context.tool_router;
|
||||
let supports_parallel = router.tool_supports_parallel(&call);
|
||||
let tool_runtime = router.tool_runtime(&call);
|
||||
let wait_for_runtime_cancellation = router.tool_waits_for_runtime_cancellation(&call);
|
||||
let router = Arc::clone(router);
|
||||
let session = Arc::clone(&self.session);
|
||||
let step_context = Arc::clone(&self.step_context);
|
||||
@@ -185,24 +184,11 @@ impl ToolCallRuntime {
|
||||
} else {
|
||||
let secs = started.elapsed().as_secs_f32().max(0.1);
|
||||
abort_dispatch_span.record("aborted", true);
|
||||
if wait_for_runtime_cancellation {
|
||||
if terminal_outcome_reached.swap(true, Ordering::AcqRel) {
|
||||
return dispatch_handle.await.map_err(Self::tool_task_join_error)?;
|
||||
}
|
||||
// The abort owns the terminal outcome; await only so
|
||||
// the runtime can finish process teardown.
|
||||
match dispatch_handle.await {
|
||||
Ok(_) => {}
|
||||
Err(err) if err.is_cancelled() => {}
|
||||
Err(err) => return Err(Self::tool_task_join_error(err)),
|
||||
}
|
||||
} else {
|
||||
dispatch_handle.abort();
|
||||
match dispatch_handle.await {
|
||||
Ok(result) => return result,
|
||||
Err(err) if err.is_cancelled() => {}
|
||||
Err(err) => return Err(Self::tool_task_join_error(err)),
|
||||
}
|
||||
dispatch_handle.abort();
|
||||
match dispatch_handle.await {
|
||||
Ok(result) => return result,
|
||||
Err(err) if err.is_cancelled() => {}
|
||||
Err(err) => return Err(Self::tool_task_join_error(err)),
|
||||
}
|
||||
let response = Self::aborted_response(&call, secs);
|
||||
notify_tool_aborted(
|
||||
@@ -266,12 +252,7 @@ impl ToolCallRuntime {
|
||||
}
|
||||
|
||||
fn abort_message(call: &ToolCall, secs: f32) -> String {
|
||||
if call.tool_name.is_default_namespace()
|
||||
&& matches!(
|
||||
call.tool_name.name.as_str(),
|
||||
"exec_command" | "unified_exec"
|
||||
)
|
||||
{
|
||||
if call.tool_name.is_default_namespace() && call.tool_name.name == "exec_command" {
|
||||
format!("Wall time: {secs:.1} seconds\naborted by user")
|
||||
} else {
|
||||
format!("aborted by user after {secs:.1}s")
|
||||
@@ -568,90 +549,6 @@ mod tests {
|
||||
|
||||
impl CoreToolRuntime for ImmediateHandler {}
|
||||
|
||||
struct CancellationCleanupHandler {
|
||||
tool_name: codex_tools::ToolName,
|
||||
started: std::sync::Mutex<Option<oneshot::Sender<()>>>,
|
||||
cleanup_started: std::sync::Mutex<Option<oneshot::Sender<()>>>,
|
||||
allow_cleanup: Arc<Notify>,
|
||||
}
|
||||
|
||||
impl ToolExecutor<ToolInvocation> for CancellationCleanupHandler {
|
||||
fn tool_name(&self) -> codex_tools::ToolName {
|
||||
self.tool_name.clone()
|
||||
}
|
||||
|
||||
fn spec(&self) -> codex_tools::ToolSpec {
|
||||
codex_tools::ToolSpec::Function(codex_tools::ResponsesApiTool {
|
||||
name: self.tool_name.name.clone(),
|
||||
description: "Cancellation cleanup test tool.".to_string(),
|
||||
strict: false,
|
||||
defer_loading: None,
|
||||
parameters: codex_tools::JsonSchema::default(),
|
||||
output_schema: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {
|
||||
Box::pin(self.handle_call(invocation))
|
||||
}
|
||||
}
|
||||
|
||||
impl CancellationCleanupHandler {
|
||||
async fn handle_call(
|
||||
&self,
|
||||
invocation: ToolInvocation,
|
||||
) -> Result<Box<dyn crate::tools::context::ToolOutput>, FunctionCallError> {
|
||||
let started = self
|
||||
.started
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.take();
|
||||
if let Some(started) = started {
|
||||
let _ = started.send(());
|
||||
}
|
||||
invocation.cancellation_token.cancelled().await;
|
||||
let cleanup_started = self
|
||||
.cleanup_started
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.take();
|
||||
if let Some(cleanup_started) = cleanup_started {
|
||||
let _ = cleanup_started.send(());
|
||||
}
|
||||
self.allow_cleanup.notified().await;
|
||||
Ok(Box::new(FunctionToolOutput::from_text(
|
||||
"cleanup complete".to_string(),
|
||||
Some(false),
|
||||
)) as Box<dyn crate::tools::context::ToolOutput>)
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreToolRuntime for CancellationCleanupHandler {
|
||||
fn waits_for_runtime_cancellation(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
struct FinishRecorder {
|
||||
records: Arc<std::sync::Mutex<Vec<ToolCallOutcome>>>,
|
||||
}
|
||||
|
||||
impl codex_extension_api::ToolLifecycleContributor for FinishRecorder {
|
||||
fn on_tool_finish<'a>(
|
||||
&'a self,
|
||||
input: codex_extension_api::ToolFinishInput<'a>,
|
||||
) -> codex_extension_api::ToolLifecycleFuture<'a> {
|
||||
let records = Arc::clone(&self.records);
|
||||
let outcome = input.outcome;
|
||||
Box::pin(async move {
|
||||
records
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.push(outcome);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct BlockingFinishContributor {
|
||||
records: Arc<std::sync::Mutex<Vec<ToolCallOutcome>>>,
|
||||
finish_started: std::sync::Mutex<Option<oneshot::Sender<()>>>,
|
||||
@@ -756,78 +653,4 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn cancellation_waiting_for_runtime_cleanup_emits_only_aborted_lifecycle()
|
||||
-> anyhow::Result<()> {
|
||||
let (mut session, turn_context) = crate::session::tests::make_session_and_context().await;
|
||||
let records = Arc::new(std::sync::Mutex::new(Vec::new()));
|
||||
let mut builder =
|
||||
codex_extension_api::ExtensionRegistryBuilder::<crate::config::Config>::new();
|
||||
builder.tool_lifecycle_contributor(Arc::new(FinishRecorder {
|
||||
records: Arc::clone(&records),
|
||||
}));
|
||||
session.services.extensions = Arc::new(builder.build());
|
||||
|
||||
let session = Arc::new(session);
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let tool_name = codex_tools::ToolName::plain("cleanup_tool");
|
||||
let (started_tx, started_rx) = oneshot::channel();
|
||||
let (cleanup_started_tx, cleanup_started_rx) = oneshot::channel();
|
||||
let allow_cleanup = Arc::new(Notify::new());
|
||||
let handler = Arc::new(CancellationCleanupHandler {
|
||||
tool_name: tool_name.clone(),
|
||||
started: std::sync::Mutex::new(Some(started_tx)),
|
||||
cleanup_started: std::sync::Mutex::new(Some(cleanup_started_tx)),
|
||||
allow_cleanup: Arc::clone(&allow_cleanup),
|
||||
}) as Arc<dyn CoreToolRuntime>;
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
let router = Arc::new(ToolRouter::from_parts(
|
||||
ToolRegistry::from_tools([handler]),
|
||||
Vec::new(),
|
||||
));
|
||||
let step_context = step_context.with_tool_router_for_test(router);
|
||||
let tracker = Arc::new(tokio::sync::Mutex::new(TurnDiffTracker::new()));
|
||||
let runtime = ToolCallRuntime::new(session, step_context, tracker);
|
||||
let cancellation_token = CancellationToken::new();
|
||||
let call = ToolCall {
|
||||
tool_name,
|
||||
call_id: "call-1".to_string(),
|
||||
payload: ToolPayload::Function {
|
||||
arguments: "{}".to_string(),
|
||||
},
|
||||
encrypted_function_args: None,
|
||||
};
|
||||
|
||||
let response_task =
|
||||
tokio::spawn(runtime.handle_tool_call(call, cancellation_token.clone()));
|
||||
started_rx.await.expect("handler should start");
|
||||
cancellation_token.cancel();
|
||||
cleanup_started_rx
|
||||
.await
|
||||
.expect("handler should start cleanup");
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
allow_cleanup.notify_one();
|
||||
|
||||
let response = tokio::time::timeout(Duration::from_secs(1), response_task)
|
||||
.await
|
||||
.expect("timed out waiting for tool response")
|
||||
.expect("tool response task should join")?;
|
||||
let ResponseInputItem::FunctionCallOutput { output, .. } = response else {
|
||||
anyhow::bail!("cancelled tool should return function output");
|
||||
};
|
||||
let FunctionCallOutputBody::Text(text) = output.body else {
|
||||
anyhow::bail!("cancelled tool output should be text");
|
||||
};
|
||||
assert!(text.contains("aborted by user"));
|
||||
|
||||
let actual = records
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.drain(..)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(vec![ToolCallOutcome::Aborted], actual);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,12 +85,6 @@ pub(crate) trait CoreToolRuntime: ToolExecutor<ToolInvocation> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Whether cancellation should let the handler finish teardown before the
|
||||
/// host returns an aborted tool response.
|
||||
fn waits_for_runtime_cancellation(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn telemetry_tags(&self, _invocation: &ToolInvocation) -> ToolTelemetryTags {
|
||||
Vec::new()
|
||||
}
|
||||
@@ -478,11 +472,6 @@ impl ToolRegistry {
|
||||
Some(tool.exposure != ToolExposure::Hidden && tool.runtime.supports_parallel_tool_calls())
|
||||
}
|
||||
|
||||
pub(crate) fn waits_for_runtime_cancellation(&self, name: &ToolName) -> Option<bool> {
|
||||
let tool = self.tool(name)?;
|
||||
Some(tool.waits_for_runtime_cancellation())
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::await_holding_invalid_type,
|
||||
reason = "tool dispatch must keep active-turn accounting atomic"
|
||||
|
||||
@@ -144,12 +144,6 @@ impl ToolRouter {
|
||||
self.registry.tool(&call.tool_name)
|
||||
}
|
||||
|
||||
pub fn tool_waits_for_runtime_cancellation(&self, call: &ToolCall) -> bool {
|
||||
self.registry
|
||||
.waits_for_runtime_cancellation(&call.tool_name)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all, err)]
|
||||
pub fn build_tool_call(item: ResponseItem) -> Result<Option<ToolCall>, FunctionCallError> {
|
||||
match item {
|
||||
|
||||
@@ -31,8 +31,8 @@ use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
pub(crate) mod apply_patch;
|
||||
pub(crate) mod shell;
|
||||
pub(crate) mod unified_exec;
|
||||
pub(crate) mod zsh_fork;
|
||||
|
||||
pub(crate) fn exec_env_for_sandbox_permissions(
|
||||
env: &HashMap<String, String>,
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#[cfg(unix)]
|
||||
pub(crate) mod unix_escalation;
|
||||
pub(crate) mod zsh_fork_backend;
|
||||
@@ -23,7 +23,7 @@ use crate::tools::runtimes::apply_zsh_fork_path_prepend;
|
||||
use crate::tools::runtimes::disable_powershell_profile_for_elevated_windows_sandbox;
|
||||
use crate::tools::runtimes::exec_env_for_sandbox_permissions;
|
||||
use crate::tools::runtimes::maybe_wrap_shell_lc_with_snapshot;
|
||||
use crate::tools::runtimes::shell::zsh_fork_backend;
|
||||
use crate::tools::runtimes::zsh_fork;
|
||||
use crate::tools::sandboxing::Approvable;
|
||||
use crate::tools::sandboxing::ApprovalAction;
|
||||
use crate::tools::sandboxing::ExecApprovalRequirement;
|
||||
@@ -422,14 +422,8 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecAttempt> for UnifiedExecRunt
|
||||
)
|
||||
.map_err(ToolError::Codex)?;
|
||||
exec_env.exec_server_env_config = req.exec_server_env_config.clone();
|
||||
match zsh_fork_backend::maybe_prepare_unified_exec(
|
||||
req,
|
||||
attempt,
|
||||
ctx,
|
||||
exec_env,
|
||||
zsh_fork_config,
|
||||
)
|
||||
.await?
|
||||
match zsh_fork::maybe_prepare_unified_exec(req, attempt, ctx, exec_env, zsh_fork_config)
|
||||
.await?
|
||||
{
|
||||
Some(prepared) => {
|
||||
if req.turn_environment.environment.is_remote() {
|
||||
|
||||
+5
-1
@@ -27,10 +27,14 @@ pub(crate) async fn maybe_prepare_unified_exec(
|
||||
imp::maybe_prepare_unified_exec(req, attempt, ctx, exec_request, zsh_fork_config).await
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[path = "zsh_fork/unix_escalation.rs"]
|
||||
mod unix_escalation;
|
||||
|
||||
#[cfg(unix)]
|
||||
mod imp {
|
||||
use super::*;
|
||||
use crate::tools::runtimes::shell::unix_escalation;
|
||||
use crate::tools::runtimes::zsh_fork::unix_escalation;
|
||||
use crate::unified_exec::SpawnLifecycle;
|
||||
use codex_shell_escalation::ESCALATE_SOCKET_ENV_VAR;
|
||||
use codex_shell_escalation::EscalationSession;
|
||||
@@ -90,7 +90,6 @@ use codex_tools::collect_code_mode_exec_prompt_tool_definitions;
|
||||
use codex_tools::collect_request_plugin_install_entries;
|
||||
use codex_tools::default_namespace_description;
|
||||
use codex_tools::request_user_input_available_modes;
|
||||
use codex_tools::shell_type_for_model_and_features;
|
||||
use futures::future::BoxFuture;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
@@ -960,29 +959,30 @@ fn add_shell_tools(context: &CoreToolPlanContext<'_>, registry: &mut ToolRegistr
|
||||
let turn_context = context.turn_context;
|
||||
let features = turn_context.config.features.get();
|
||||
let environment_mode = tool_environment_mode(context.environments);
|
||||
if !environment_mode.has_environment() {
|
||||
if !environment_mode.has_environment()
|
||||
|| !features.enabled(Feature::ShellTool)
|
||||
|| !features.enabled(Feature::UnifiedExec)
|
||||
|| matches!(
|
||||
turn_context.model_info.shell_type,
|
||||
ConfigShellToolType::Disabled
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let allow_login_shell = any_environment_allows_login_shell(context.environments);
|
||||
let exec_permission_approvals_enabled = features.enabled(Feature::ExecPermissionApprovals);
|
||||
let include_environment_id = matches!(environment_mode, ToolEnvironmentMode::Multiple);
|
||||
match shell_type_for_model_and_features(&turn_context.model_info, features) {
|
||||
ConfigShellToolType::UnifiedExec => {
|
||||
registry.add(ExecCommandHandler::new(ExecCommandHandlerOptions {
|
||||
allow_login_shell,
|
||||
exec_permission_approvals_enabled,
|
||||
include_environment_id,
|
||||
include_shell_parameter: unified_exec_should_include_shell_parameter(
|
||||
turn_context,
|
||||
context.environments,
|
||||
),
|
||||
}));
|
||||
registry.add(WriteStdinHandler);
|
||||
}
|
||||
ConfigShellToolType::Disabled => {}
|
||||
ConfigShellToolType::Default | ConfigShellToolType::Local => {}
|
||||
}
|
||||
registry.add(ExecCommandHandler::new(ExecCommandHandlerOptions {
|
||||
allow_login_shell,
|
||||
exec_permission_approvals_enabled,
|
||||
include_environment_id,
|
||||
include_shell_parameter: unified_exec_should_include_shell_parameter(
|
||||
turn_context,
|
||||
context.environments,
|
||||
),
|
||||
}));
|
||||
registry.add(WriteStdinHandler);
|
||||
}
|
||||
|
||||
fn unified_exec_should_include_shell_parameter(
|
||||
|
||||
@@ -662,7 +662,7 @@ async fn request_user_input_stays_direct_in_code_mode_only() {
|
||||
#[tokio::test]
|
||||
async fn shell_family_registers_only_unified_exec_tools() {
|
||||
let plan = probe(|turn| {
|
||||
set_features(turn, &[Feature::ShellTool, Feature::UnifiedExec]);
|
||||
set_features(turn, &[Feature::ShellTool]);
|
||||
set_feature(turn, Feature::ShellZshFork, /*enabled*/ false);
|
||||
Arc::make_mut(&mut turn.model_info).shell_type = ConfigShellToolType::UnifiedExec;
|
||||
})
|
||||
@@ -679,7 +679,6 @@ async fn login_shell_parameter_follows_selected_environment() {
|
||||
for allow_login_shell in [false, true] {
|
||||
let plan = probe(|turn| {
|
||||
set_feature(turn, Feature::ShellTool, /*enabled*/ true);
|
||||
set_feature(turn, Feature::UnifiedExec, /*enabled*/ true);
|
||||
set_feature(turn, Feature::ShellZshFork, /*enabled*/ false);
|
||||
Arc::make_mut(&mut turn.model_info).shell_type = ConfigShellToolType::UnifiedExec;
|
||||
update_config(turn, |config| {
|
||||
@@ -715,7 +714,7 @@ async fn login_shell_parameter_follows_selected_environment() {
|
||||
#[tokio::test]
|
||||
async fn login_shell_parameter_is_available_when_any_environment_allows_it() {
|
||||
let plan = probe(|turn| {
|
||||
set_features(turn, &[Feature::ShellTool, Feature::UnifiedExec]);
|
||||
set_features(turn, &[Feature::ShellTool]);
|
||||
set_feature(turn, Feature::ShellZshFork, /*enabled*/ false);
|
||||
update_config(turn, |config| {
|
||||
config.permissions.allow_login_shell = false;
|
||||
@@ -756,8 +755,8 @@ async fn disabling_shell_tools_disables_command_tools_for_all_environments() {
|
||||
);
|
||||
})
|
||||
.await;
|
||||
remote_environment.assert_visible_lacks(&["shell_command", "exec_command", "write_stdin"]);
|
||||
remote_environment.assert_registered_lacks(&["shell_command", "exec_command", "write_stdin"]);
|
||||
remote_environment.assert_visible_lacks(&["exec_command", "write_stdin"]);
|
||||
remote_environment.assert_registered_lacks(&["exec_command", "write_stdin"]);
|
||||
|
||||
let multiple_local_environments = probe(|turn| {
|
||||
set_feature(turn, Feature::ShellTool, /*enabled*/ false);
|
||||
@@ -765,16 +764,8 @@ async fn disabling_shell_tools_disables_command_tools_for_all_environments() {
|
||||
duplicate_primary_environment(turn);
|
||||
})
|
||||
.await;
|
||||
multiple_local_environments.assert_visible_lacks(&[
|
||||
"shell_command",
|
||||
"exec_command",
|
||||
"write_stdin",
|
||||
]);
|
||||
multiple_local_environments.assert_registered_lacks(&[
|
||||
"shell_command",
|
||||
"exec_command",
|
||||
"write_stdin",
|
||||
]);
|
||||
multiple_local_environments.assert_visible_lacks(&["exec_command", "write_stdin"]);
|
||||
multiple_local_environments.assert_registered_lacks(&["exec_command", "write_stdin"]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -808,7 +799,7 @@ async fn dynamic_tools_cannot_reclaim_the_reserved_exec_command_name() {
|
||||
#[tokio::test]
|
||||
async fn shell_zsh_fork_keeps_unified_exec_available() {
|
||||
let without_composition = probe(|turn| {
|
||||
set_features(turn, &[Feature::ShellTool, Feature::UnifiedExec]);
|
||||
set_features(turn, &[Feature::ShellTool]);
|
||||
set_feature(turn, Feature::ShellZshFork, /*enabled*/ true);
|
||||
set_feature(turn, Feature::UnifiedExecZshFork, /*enabled*/ false);
|
||||
Arc::make_mut(&mut turn.model_info).shell_type = ConfigShellToolType::UnifiedExec;
|
||||
@@ -823,7 +814,6 @@ async fn shell_zsh_fork_keeps_unified_exec_available() {
|
||||
turn,
|
||||
&[
|
||||
Feature::ShellTool,
|
||||
Feature::UnifiedExec,
|
||||
Feature::ShellZshFork,
|
||||
Feature::UnifiedExecZshFork,
|
||||
],
|
||||
@@ -847,7 +837,6 @@ async fn zsh_fork_unified_exec_hides_shell_parameter() {
|
||||
turn,
|
||||
&[
|
||||
Feature::ShellTool,
|
||||
Feature::UnifiedExec,
|
||||
Feature::ShellZshFork,
|
||||
Feature::UnifiedExecZshFork,
|
||||
],
|
||||
@@ -872,7 +861,6 @@ async fn zsh_fork_unified_exec_keeps_shell_parameter_when_remote_environment_ava
|
||||
turn,
|
||||
&[
|
||||
Feature::ShellTool,
|
||||
Feature::UnifiedExec,
|
||||
Feature::ShellZshFork,
|
||||
Feature::UnifiedExecZshFork,
|
||||
],
|
||||
@@ -923,8 +911,6 @@ async fn zsh_fork_unified_exec_keeps_shell_parameter_when_remote_environment_ava
|
||||
.await;
|
||||
|
||||
plan.assert_visible_contains(&["exec_command", "write_stdin"]);
|
||||
plan.assert_visible_lacks(&["shell_command"]);
|
||||
plan.assert_registered_lacks(&["shell_command"]);
|
||||
assert!(has_parameter(plan.visible_spec("exec_command"), "shell"));
|
||||
assert!(has_parameter(
|
||||
plan.visible_spec("exec_command"),
|
||||
@@ -960,7 +946,6 @@ async fn environment_count_controls_environment_backed_tools() {
|
||||
let multiple_environments = probe(|turn| {
|
||||
duplicate_primary_environment(turn);
|
||||
set_feature(turn, Feature::ShellTool, /*enabled*/ true);
|
||||
set_feature(turn, Feature::UnifiedExec, /*enabled*/ true);
|
||||
set_feature(turn, Feature::RequestPermissionsTool, /*enabled*/ true);
|
||||
Arc::make_mut(&mut turn.model_info).apply_patch_tool_type =
|
||||
Some(ApplyPatchToolType::Freeform);
|
||||
@@ -972,8 +957,6 @@ async fn environment_count_controls_environment_backed_tools() {
|
||||
"view_image",
|
||||
"request_permissions",
|
||||
]);
|
||||
multiple_environments.assert_visible_lacks(&["shell_command"]);
|
||||
multiple_environments.assert_registered_lacks(&["shell_command"]);
|
||||
assert!(has_parameter(
|
||||
multiple_environments.visible_spec("exec_command"),
|
||||
"environment_id"
|
||||
@@ -990,7 +973,6 @@ async fn environment_count_controls_environment_backed_tools() {
|
||||
#[tokio::test]
|
||||
async fn environment_tools_follow_the_step_context() {
|
||||
let (_session, mut turn) = make_session_and_context().await;
|
||||
set_feature(&mut turn, Feature::UnifiedExec, /*enabled*/ true);
|
||||
Arc::make_mut(&mut turn.model_info).apply_patch_tool_type = Some(ApplyPatchToolType::Freeform);
|
||||
|
||||
let environments = turn.environments.clone();
|
||||
@@ -1346,7 +1328,7 @@ async fn strict_namespace_ownership_requires_tool_namespace_inventory_opt_in() {
|
||||
async fn unified_tool_runtimes_preserve_source_order_and_collision_priority() {
|
||||
let plan = probe_with(
|
||||
|turn| {
|
||||
set_features(turn, &[Feature::ShellTool, Feature::UnifiedExec]);
|
||||
set_features(turn, &[Feature::ShellTool]);
|
||||
set_feature(turn, Feature::ShellZshFork, /*enabled*/ false);
|
||||
Arc::make_mut(&mut turn.model_info).shell_type = ConfigShellToolType::UnifiedExec;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user