mirror of
https://github.com/openai/codex.git
synced 2026-11-10 16:44:38 +00:00
Carry environment config in turn selections (#38521)
## What changed - Add `EnvironmentConfigState` to `TurnEnvironmentSelection` so each environment attachment can inherit thread configuration or carry ready configuration for its login-shell policy and selected capability roots. - Apply ready configuration during thread creation and thread-settings updates while reusing the existing environment connection. - Validate selection configuration before applying settings, rejecting unsupported pending state and invalid capability roots. ## Testing - Cover attachment-scoped capability roots, ready configuration at thread startup and turn updates, and rejection of invalid selections without changing existing settings. GitOrigin-RevId: 86cf8f2148fdaa746ea0dd12890ab7b1577d7587
This commit is contained in:
1 parent
86b1123ff6
commit
fdbab67c66
28 files changed
+282
-79
No files matched your search
@@ -442,6 +442,7 @@ use codex_protocol::protocol::ConversationSpeechParams;
|
|||||||
use codex_protocol::protocol::ConversationStartParams;
|
use codex_protocol::protocol::ConversationStartParams;
|
||||||
use codex_protocol::protocol::ConversationStartTransport;
|
use codex_protocol::protocol::ConversationStartTransport;
|
||||||
use codex_protocol::protocol::ConversationTextParams;
|
use codex_protocol::protocol::ConversationTextParams;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use codex_protocol::protocol::GitInfo as CoreGitInfo;
|
use codex_protocol::protocol::GitInfo as CoreGitInfo;
|
||||||
@@ -637,6 +638,7 @@ fn resolve_turn_environment_selections(
|
|||||||
environment_id,
|
environment_id,
|
||||||
cwd,
|
cwd,
|
||||||
workspace_roots,
|
workspace_roots,
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
thread_manager
|
thread_manager
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ pub use codex_protocol::error::Result as CodexResult;
|
|||||||
pub use codex_protocol::models::PermissionProfile;
|
pub use codex_protocol::models::PermissionProfile;
|
||||||
pub use codex_protocol::openai_models::ModelPreset;
|
pub use codex_protocol::openai_models::ModelPreset;
|
||||||
pub use codex_protocol::protocol::AskForApproval;
|
pub use codex_protocol::protocol::AskForApproval;
|
||||||
|
pub use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
pub use codex_protocol::protocol::EventMsg;
|
pub use codex_protocol::protocol::EventMsg;
|
||||||
pub use codex_protocol::protocol::Op;
|
pub use codex_protocol::protocol::Op;
|
||||||
pub use codex_protocol::protocol::SessionConfiguredEvent;
|
pub use codex_protocol::protocol::SessionConfiguredEvent;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use codex_exec_server::RemoveOptions;
|
|||||||
use codex_extension_api::UserInstructions;
|
use codex_extension_api::UserInstructions;
|
||||||
use codex_features::Feature;
|
use codex_features::Feature;
|
||||||
use codex_protocol::models::PermissionProfile;
|
use codex_protocol::models::PermissionProfile;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||||
use codex_utils_path_uri::PathUri;
|
use codex_utils_path_uri::PathUri;
|
||||||
@@ -337,6 +338,7 @@ fn resolved_local_environments<const N: usize>(
|
|||||||
environment_id: environment_id.to_string(),
|
environment_id: environment_id.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&cwd),
|
cwd: PathUri::from_abs_path(&cwd),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
Arc::new(
|
Arc::new(
|
||||||
Environment::create_for_tests(/*exec_server_url*/ None)
|
Environment::create_for_tests(/*exec_server_url*/ None)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use codex_protocol::capabilities::SelectedCapabilityRoot;
|
|||||||
use codex_protocol::error::CodexErr;
|
use codex_protocol::error::CodexErr;
|
||||||
use codex_protocol::error::Result as CodexResult;
|
use codex_protocol::error::Result as CodexResult;
|
||||||
use codex_protocol::protocol::EnvironmentConfig;
|
use codex_protocol::protocol::EnvironmentConfig;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::EnvironmentConnectionEvent;
|
use codex_protocol::protocol::EnvironmentConnectionEvent;
|
||||||
use codex_protocol::protocol::Event;
|
use codex_protocol::protocol::Event;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
@@ -46,6 +47,7 @@ pub(crate) fn default_thread_environment_selections(
|
|||||||
environment_id,
|
environment_id,
|
||||||
cwd: PathUri::from_abs_path(cwd),
|
cwd: PathUri::from_abs_path(cwd),
|
||||||
workspace_roots: workspace_roots.iter().map(PathUri::from_abs_path).collect(),
|
workspace_roots: workspace_roots.iter().map(PathUri::from_abs_path).collect(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@@ -80,17 +82,25 @@ pub(crate) struct StartingTurnEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SelectedTurnEnvironment {
|
impl SelectedTurnEnvironment {
|
||||||
fn has_installed_environment_config(&self) -> bool {
|
fn apply_configuration(
|
||||||
self.config.selected_capability_roots.is_some()
|
&mut self,
|
||||||
}
|
selection_config: EnvironmentConfigState,
|
||||||
|
thread_config: &TurnEnvironmentConfig,
|
||||||
// Thread settings still own permissions, but cannot overwrite shell policy
|
) {
|
||||||
// that was installed specifically for this environment attachment.
|
self.config = match &selection_config {
|
||||||
fn update_thread_config(&mut self, config: &TurnEnvironmentConfig) {
|
EnvironmentConfigState::FromThread => thread_config.clone(),
|
||||||
if !self.has_installed_environment_config() {
|
EnvironmentConfigState::Ready(config) => TurnEnvironmentConfig {
|
||||||
self.config.allow_login_shell = config.allow_login_shell;
|
allow_login_shell: config.allow_login_shell,
|
||||||
}
|
// temp read from thread_config; will go away once perms on EnvironmentConfig,
|
||||||
self.config.permission_profile = config.permission_profile.clone();
|
// then we can just assign passed config directly
|
||||||
|
permission_profile: thread_config.permission_profile.clone(),
|
||||||
|
selected_capability_roots: Some(config.selected_capability_roots.clone()),
|
||||||
|
},
|
||||||
|
EnvironmentConfigState::Pending => {
|
||||||
|
unreachable!("pending environment configuration is not supported yet")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.selection.config = selection_config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +166,10 @@ impl ThreadEnvironments {
|
|||||||
};
|
};
|
||||||
// Child threads get their own settings, but inherit any
|
// Child threads get their own settings, but inherit any
|
||||||
// environment-owned policy that was already installed.
|
// environment-owned policy that was already installed.
|
||||||
inherited_environment.update_thread_config(&thread_environment_config);
|
inherited_environment.apply_configuration(
|
||||||
|
inherited_environment.selection.config.clone(),
|
||||||
|
&thread_environment_config,
|
||||||
|
);
|
||||||
Some(inherited_environment)
|
Some(inherited_environment)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -182,13 +195,18 @@ impl ThreadEnvironments {
|
|||||||
if !seen_environment_ids.insert(selected_environment.environment_id.as_str()) {
|
if !seen_environment_ids.insert(selected_environment.environment_id.as_str()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(environment) = previous
|
if let Some(environment) = previous.iter().find(|environment| {
|
||||||
.iter()
|
let previous = &environment.selection;
|
||||||
.find(|environment| environment.selection == *selected_environment)
|
previous.environment_id == selected_environment.environment_id
|
||||||
&& !matches!(environment.resolution.clone().now_or_never(), Some(Err(_)))
|
&& previous.cwd == selected_environment.cwd
|
||||||
|
&& previous.workspace_roots == selected_environment.workspace_roots
|
||||||
|
}) && !matches!(environment.resolution.clone().now_or_never(), Some(Err(_)))
|
||||||
{
|
{
|
||||||
let mut environment = environment.clone();
|
let mut environment = environment.clone();
|
||||||
environment.update_thread_config(thread_environment_config);
|
environment.apply_configuration(
|
||||||
|
selected_environment.config.clone(),
|
||||||
|
thread_environment_config,
|
||||||
|
);
|
||||||
next.push(environment);
|
next.push(environment);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -224,13 +242,18 @@ impl ThreadEnvironments {
|
|||||||
.remote_handle();
|
.remote_handle();
|
||||||
drop(tokio::spawn(resolution_task));
|
drop(tokio::spawn(resolution_task));
|
||||||
let resolution = resolution.boxed().shared();
|
let resolution = resolution.boxed().shared();
|
||||||
next.push(SelectedTurnEnvironment {
|
let mut selected = SelectedTurnEnvironment {
|
||||||
selection: selected_environment.clone(),
|
selection: selected_environment.clone(),
|
||||||
config: thread_environment_config.clone(),
|
config: thread_environment_config.clone(),
|
||||||
environment,
|
environment,
|
||||||
connection_events_task,
|
connection_events_task,
|
||||||
resolution,
|
resolution,
|
||||||
});
|
};
|
||||||
|
selected.apply_configuration(
|
||||||
|
selected_environment.config.clone(),
|
||||||
|
thread_environment_config,
|
||||||
|
);
|
||||||
|
next.push(selected);
|
||||||
}
|
}
|
||||||
let removed_connection_tasks = previous
|
let removed_connection_tasks = previous
|
||||||
.iter()
|
.iter()
|
||||||
@@ -290,7 +313,7 @@ impl ThreadEnvironments {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|environment| {
|
.map(|environment| {
|
||||||
let mut environment = environment.clone();
|
let mut environment = environment.clone();
|
||||||
environment.update_thread_config(config);
|
environment.apply_configuration(environment.selection.config.clone(), config);
|
||||||
environment
|
environment
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -315,8 +338,8 @@ impl ThreadEnvironments {
|
|||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.config.allow_login_shell = config.allow_login_shell;
|
let thread_config = environment.config.clone();
|
||||||
environment.config.selected_capability_roots = Some(config.selected_capability_roots);
|
environment.apply_configuration(EnvironmentConfigState::Ready(config), &thread_config);
|
||||||
self.environments.store(Arc::new(environments));
|
self.environments.store(Arc::new(environments));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -787,6 +810,7 @@ mod tests {
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: cwd_uri.clone(),
|
cwd: cwd_uri.clone(),
|
||||||
workspace_roots: vec![cwd_uri],
|
workspace_roots: vec![cwd_uri],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -820,11 +844,13 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: cwd_uri.clone(),
|
cwd: cwd_uri.clone(),
|
||||||
workspace_roots: vec![cwd_uri.clone()],
|
workspace_roots: vec![cwd_uri.clone()],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
TurnEnvironmentSelection {
|
TurnEnvironmentSelection {
|
||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: cwd_uri.clone(),
|
cwd: cwd_uri.clone(),
|
||||||
workspace_roots: vec![cwd_uri],
|
workspace_roots: vec![cwd_uri],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@@ -870,6 +896,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&cwd),
|
cwd: PathUri::from_abs_path(&cwd),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
}],
|
}],
|
||||||
&expected_config,
|
&expected_config,
|
||||||
);
|
);
|
||||||
@@ -890,6 +917,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: cwd_uri.clone(),
|
cwd: cwd_uri.clone(),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
|
|
||||||
let resolved = resolve_turn_environments(
|
let resolved = resolve_turn_environments(
|
||||||
@@ -900,6 +928,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: cwd_uri.join("other").expect("other cwd URI"),
|
cwd: cwd_uri.join("other").expect("other cwd URI"),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -921,6 +950,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: "local".to_string(),
|
environment_id: "local".to_string(),
|
||||||
cwd: selected_cwd_uri,
|
cwd: selected_cwd_uri,
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
}],
|
}],
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -960,6 +990,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: cwd_uri.clone(),
|
cwd: cwd_uri.clone(),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
|
|
||||||
let resolved = resolve_turn_environments(
|
let resolved = resolve_turn_environments(
|
||||||
@@ -969,6 +1000,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: "missing".to_string(),
|
environment_id: "missing".to_string(),
|
||||||
cwd: cwd_uri,
|
cwd: cwd_uri,
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
local.clone(),
|
local.clone(),
|
||||||
],
|
],
|
||||||
@@ -997,6 +1029,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&AbsolutePathBuf::current_dir().expect("cwd")),
|
cwd: PathUri::from_abs_path(&AbsolutePathBuf::current_dir().expect("cwd")),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
let environments = Arc::new(ThreadEnvironments::new(
|
let environments = Arc::new(ThreadEnvironments::new(
|
||||||
manager,
|
manager,
|
||||||
@@ -1056,11 +1089,13 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: cwd.clone(),
|
cwd: cwd.clone(),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
let local = TurnEnvironmentSelection {
|
let local = TurnEnvironmentSelection {
|
||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd,
|
cwd,
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
let turn_environments = ThreadEnvironments::new(
|
let turn_environments = ThreadEnvironments::new(
|
||||||
manager,
|
manager,
|
||||||
@@ -1163,6 +1198,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&AbsolutePathBuf::current_dir().expect("cwd")),
|
cwd: PathUri::from_abs_path(&AbsolutePathBuf::current_dir().expect("cwd")),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
let environments = ThreadEnvironments::new(
|
let environments = ThreadEnvironments::new(
|
||||||
Arc::clone(&manager),
|
Arc::clone(&manager),
|
||||||
@@ -1248,6 +1284,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&cwd),
|
cwd: PathUri::from_abs_path(&cwd),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
let (tx_event, rx_event) = async_channel::unbounded();
|
let (tx_event, rx_event) = async_channel::unbounded();
|
||||||
let environments = Arc::new(ThreadEnvironments::new(
|
let environments = Arc::new(ThreadEnvironments::new(
|
||||||
@@ -1337,6 +1374,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&cwd),
|
cwd: PathUri::from_abs_path(&cwd),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
let inherited_environment = Arc::new(
|
let inherited_environment = Arc::new(
|
||||||
Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string()))
|
Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string()))
|
||||||
@@ -1391,6 +1429,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&cwd),
|
cwd: PathUri::from_abs_path(&cwd),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
let root = |id: &str| SelectedCapabilityRoot {
|
let root = |id: &str| SelectedCapabilityRoot {
|
||||||
id: id.to_string(),
|
id: id.to_string(),
|
||||||
@@ -1482,6 +1521,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: cwd_uri.clone(),
|
cwd: cwd_uri.clone(),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
}],
|
}],
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -1496,6 +1536,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: remote_cwd_uri.clone(),
|
cwd: remote_cwd_uri.clone(),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
remote_environment.clone(),
|
remote_environment.clone(),
|
||||||
/*shell*/ None,
|
/*shell*/ None,
|
||||||
@@ -1509,6 +1550,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: remote_cwd_uri,
|
cwd: remote_cwd_uri,
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
remote_environment,
|
remote_environment,
|
||||||
/*shell*/ None,
|
/*shell*/ None,
|
||||||
@@ -1535,6 +1577,7 @@ url = "ws://127.0.0.1:8765"
|
|||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&cwd),
|
cwd: PathUri::from_abs_path(&cwd),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
config: test_environment_config(),
|
config: test_environment_config(),
|
||||||
resolution: futures::future::pending().boxed().shared(),
|
resolution: futures::future::pending().boxed().shared(),
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ use codex_hooks::HooksConfig;
|
|||||||
use codex_model_provider::create_model_provider;
|
use codex_model_provider::create_model_provider;
|
||||||
use codex_protocol::models::PermissionProfile;
|
use codex_protocol::models::PermissionProfile;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
use codex_protocol::protocol::GranularApprovalConfig;
|
use codex_protocol::protocol::GranularApprovalConfig;
|
||||||
use codex_protocol::protocol::McpInvocation;
|
use codex_protocol::protocol::McpInvocation;
|
||||||
@@ -1111,6 +1112,7 @@ async fn mcp_sandbox_cwd_uses_matching_server_environment_uri() -> anyhow::Resul
|
|||||||
environment_id: "remote".to_string(),
|
environment_id: "remote".to_string(),
|
||||||
cwd: secondary_cwd.clone(),
|
cwd: secondary_cwd.clone(),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
environment,
|
environment,
|
||||||
/*shell*/ None,
|
/*shell*/ None,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use codex_protocol::capabilities::CapabilityRootLocation;
|
|||||||
use codex_protocol::error::CodexErr;
|
use codex_protocol::error::CodexErr;
|
||||||
use codex_protocol::error::Result as CodexResult;
|
use codex_protocol::error::Result as CodexResult;
|
||||||
use codex_protocol::protocol::EnvironmentConfig;
|
use codex_protocol::protocol::EnvironmentConfig;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||||
|
|
||||||
use crate::config::ConstraintResult;
|
use crate::config::ConstraintResult;
|
||||||
@@ -13,6 +14,51 @@ use crate::session::session::Session;
|
|||||||
use crate::session::session::SessionConfiguration;
|
use crate::session::session::SessionConfiguration;
|
||||||
use crate::session::session::SessionSettingsUpdate;
|
use crate::session::session::SessionSettingsUpdate;
|
||||||
|
|
||||||
|
pub(super) fn validate_environment_selections(
|
||||||
|
selections: &[TurnEnvironmentSelection],
|
||||||
|
) -> CodexResult<()> {
|
||||||
|
for selection in selections {
|
||||||
|
match &selection.config {
|
||||||
|
EnvironmentConfigState::FromThread => {}
|
||||||
|
EnvironmentConfigState::Pending => {
|
||||||
|
return Err(CodexErr::InvalidRequest(
|
||||||
|
"pending environment configuration is not supported yet".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
EnvironmentConfigState::Ready(config) => {
|
||||||
|
validate_environment_config(selection, config)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_environment_config(
|
||||||
|
selection: &TurnEnvironmentSelection,
|
||||||
|
config: &EnvironmentConfig,
|
||||||
|
) -> CodexResult<()> {
|
||||||
|
if config.selected_capability_roots.len() > MAX_SELECTED_CAPABILITY_ROOTS {
|
||||||
|
return Err(CodexErr::InvalidRequest(format!(
|
||||||
|
"environment readiness contains more than {MAX_SELECTED_CAPABILITY_ROOTS} selected capability roots"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut root_ids = HashSet::with_capacity(config.selected_capability_roots.len());
|
||||||
|
for root in &config.selected_capability_roots {
|
||||||
|
let CapabilityRootLocation::Environment { environment_id, .. } = &root.location;
|
||||||
|
if root.id.trim().is_empty()
|
||||||
|
|| environment_id != &selection.environment_id
|
||||||
|
|| !root_ids.insert(root.id.as_str())
|
||||||
|
{
|
||||||
|
return Err(CodexErr::InvalidRequest(format!(
|
||||||
|
"selected capability roots must have unique non-empty IDs and belong to environment `{}`",
|
||||||
|
selection.environment_id
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
pub(super) fn apply_session_settings(
|
pub(super) fn apply_session_settings(
|
||||||
&self,
|
&self,
|
||||||
@@ -27,25 +73,7 @@ impl Session {
|
|||||||
selection: &TurnEnvironmentSelection,
|
selection: &TurnEnvironmentSelection,
|
||||||
config: EnvironmentConfig,
|
config: EnvironmentConfig,
|
||||||
) -> CodexResult<()> {
|
) -> CodexResult<()> {
|
||||||
if config.selected_capability_roots.len() > MAX_SELECTED_CAPABILITY_ROOTS {
|
validate_environment_config(selection, &config)?;
|
||||||
return Err(CodexErr::InvalidRequest(format!(
|
|
||||||
"environment readiness contains more than {MAX_SELECTED_CAPABILITY_ROOTS} selected capability roots"
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut root_ids = HashSet::with_capacity(config.selected_capability_roots.len());
|
|
||||||
for root in &config.selected_capability_roots {
|
|
||||||
let CapabilityRootLocation::Environment { environment_id, .. } = &root.location;
|
|
||||||
if root.id.trim().is_empty()
|
|
||||||
|| environment_id != &selection.environment_id
|
|
||||||
|| !root_ids.insert(root.id.as_str())
|
|
||||||
{
|
|
||||||
return Err(CodexErr::InvalidRequest(format!(
|
|
||||||
"selected capability roots must have unique non-empty IDs and belong to environment `{}`",
|
|
||||||
selection.environment_id
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// grab session lock so installation can't race w/ thread settings updates
|
// grab session lock so installation can't race w/ thread settings updates
|
||||||
let _state = self.state.lock().await;
|
let _state = self.state.lock().await;
|
||||||
|
|||||||
@@ -709,7 +709,7 @@ impl Session {
|
|||||||
user_shell_override,
|
user_shell_override,
|
||||||
};
|
};
|
||||||
session_configuration
|
session_configuration
|
||||||
.validate_auto_review_requirement(&environment_selections)
|
.validate(&environment_selections)
|
||||||
.map_err(|err| CodexErr::InvalidRequest(err.to_string()))?;
|
.map_err(|err| CodexErr::InvalidRequest(err.to_string()))?;
|
||||||
|
|
||||||
// Generate a unique ID for the lifetime of this session.
|
// Generate a unique ID for the lifetime of this session.
|
||||||
|
|||||||
@@ -243,7 +243,22 @@ impl SessionConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn validate_auto_review_requirement(
|
pub(super) fn validate(
|
||||||
|
&self,
|
||||||
|
environments: &[TurnEnvironmentSelection],
|
||||||
|
) -> ConstraintResult<()> {
|
||||||
|
self.validate_auto_review_requirement(environments)?;
|
||||||
|
super::environment::validate_environment_selections(environments).map_err(|error| {
|
||||||
|
ConstraintError::InvalidValue {
|
||||||
|
field_name: "environments",
|
||||||
|
candidate: "environment configuration".to_string(),
|
||||||
|
allowed: format!("valid selected capability roots ({error})"),
|
||||||
|
requirement_source: codex_config::RequirementSource::Unknown,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_auto_review_requirement(
|
||||||
&self,
|
&self,
|
||||||
environments: &[TurnEnvironmentSelection],
|
environments: &[TurnEnvironmentSelection],
|
||||||
) -> ConstraintResult<()> {
|
) -> ConstraintResult<()> {
|
||||||
@@ -463,7 +478,7 @@ impl SessionConfiguration {
|
|||||||
.map_or(current_environments, |environments| {
|
.map_or(current_environments, |environments| {
|
||||||
environments.environments.as_slice()
|
environments.environments.as_slice()
|
||||||
});
|
});
|
||||||
next_configuration.validate_auto_review_requirement(next_environments)?;
|
next_configuration.validate(next_environments)?;
|
||||||
Ok(next_configuration)
|
Ok(next_configuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ use codex_protocol::permissions::FileSystemPath;
|
|||||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||||
use codex_protocol::permissions::FileSystemSpecialPath;
|
use codex_protocol::permissions::FileSystemSpecialPath;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::ErrorEvent;
|
use codex_protocol::protocol::ErrorEvent;
|
||||||
use codex_protocol::protocol::SandboxPolicy;
|
use codex_protocol::protocol::SandboxPolicy;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelections;
|
use codex_protocol::protocol::TurnEnvironmentSelections;
|
||||||
@@ -6644,6 +6645,7 @@ async fn request_permissions_tool_resolves_relative_paths_against_selected_envir
|
|||||||
environment_id: "remote".to_string(),
|
environment_id: "remote".to_string(),
|
||||||
cwd: PathUri::from_abs_path(&environment_cwd),
|
cwd: PathUri::from_abs_path(&environment_cwd),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
current_environment.environment,
|
current_environment.environment,
|
||||||
current_environment.shell,
|
current_environment.shell,
|
||||||
@@ -7243,6 +7245,7 @@ async fn primary_environment_uses_first_turn_environment() {
|
|||||||
environment_id: "second".to_string(),
|
environment_id: "second".to_string(),
|
||||||
cwd: second_cwd_uri.clone(),
|
cwd: second_cwd_uri.clone(),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
Arc::clone(&first_environment.environment),
|
Arc::clone(&first_environment.environment),
|
||||||
/*shell*/ None,
|
/*shell*/ None,
|
||||||
@@ -8654,6 +8657,7 @@ async fn conflicting_ready_environment_root_ids_keep_first_location() {
|
|||||||
environment_id: environment_id.clone(),
|
environment_id: environment_id.clone(),
|
||||||
cwd: local_environment.cwd().clone(),
|
cwd: local_environment.cwd().clone(),
|
||||||
workspace_roots: local_environment.workspace_roots().to_vec(),
|
workspace_roots: local_environment.workspace_roots().to_vec(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
environment,
|
environment,
|
||||||
local_environment.shell.clone(),
|
local_environment.shell.clone(),
|
||||||
@@ -8913,6 +8917,7 @@ async fn record_context_updates_emits_environment_item_for_cwd_changes() {
|
|||||||
environment_id: environment.selection.environment_id,
|
environment_id: environment.selection.environment_id,
|
||||||
cwd: PathUri::from_abs_path(&cwd),
|
cwd: PathUri::from_abs_path(&cwd),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
environment.environment,
|
environment.environment,
|
||||||
environment.shell,
|
environment.shell,
|
||||||
@@ -8972,6 +8977,7 @@ async fn record_context_updates_use_environment_permission_profile_and_workspace
|
|||||||
environment_id: environment.selection.environment_id,
|
environment_id: environment.selection.environment_id,
|
||||||
cwd,
|
cwd,
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&workspace_root)],
|
workspace_roots: vec![PathUri::from_abs_path(&workspace_root)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
environment.environment,
|
environment.environment,
|
||||||
environment.shell,
|
environment.shell,
|
||||||
@@ -9047,6 +9053,7 @@ async fn record_context_updates_omits_environment_item_when_disabled() {
|
|||||||
environment_id: environment.selection.environment_id,
|
environment_id: environment.selection.environment_id,
|
||||||
cwd: PathUri::from_abs_path(&test_path_buf("/new-repo").abs()),
|
cwd: PathUri::from_abs_path(&test_path_buf("/new-repo").abs()),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
environment.environment,
|
environment.environment,
|
||||||
environment.shell,
|
environment.shell,
|
||||||
@@ -9515,6 +9522,7 @@ async fn turn_context_item_stores_local_cwd() {
|
|||||||
environment_id: "remote".to_string(),
|
environment_id: "remote".to_string(),
|
||||||
cwd,
|
cwd,
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
environment.environment,
|
environment.environment,
|
||||||
environment.shell,
|
environment.shell,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use codex_protocol::models::ReasoningItemReasoningSummary;
|
|||||||
use codex_protocol::models::ResponseItem;
|
use codex_protocol::models::ResponseItem;
|
||||||
use codex_protocol::openai_models::ModelsResponse;
|
use codex_protocol::openai_models::ModelsResponse;
|
||||||
use codex_protocol::protocol::AgentMessageEvent;
|
use codex_protocol::protocol::AgentMessageEvent;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::InternalSessionSource;
|
use codex_protocol::protocol::InternalSessionSource;
|
||||||
use codex_protocol::protocol::SessionMeta;
|
use codex_protocol::protocol::SessionMeta;
|
||||||
use codex_protocol::protocol::SessionMetaLine;
|
use codex_protocol::protocol::SessionMetaLine;
|
||||||
@@ -1143,6 +1144,7 @@ async fn resume_and_fork_do_not_restore_thread_environments_from_rollout() {
|
|||||||
environment_id: "local".to_string(),
|
environment_id: "local".to_string(),
|
||||||
cwd: PathUri::from_abs_path(&selected_cwd),
|
cwd: PathUri::from_abs_path(&selected_cwd),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
}];
|
}];
|
||||||
let default_cwd = config.cwd.clone();
|
let default_cwd = config.cwd.clone();
|
||||||
let mut source_config = config.clone();
|
let mut source_config = config.clone();
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use crate::tools::handlers::ShellCommandHandler;
|
|||||||
use crate::tools::hook_names::HookToolName;
|
use crate::tools::hook_names::HookToolName;
|
||||||
use crate::tools::registry::CoreToolRuntime;
|
use crate::tools::registry::CoreToolRuntime;
|
||||||
use crate::turn_diff_tracker::TurnDiffTracker;
|
use crate::turn_diff_tracker::TurnDiffTracker;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||||
use codex_shell_command::is_safe_command::is_known_safe_command;
|
use codex_shell_command::is_safe_command::is_known_safe_command;
|
||||||
use codex_shell_command::powershell::try_find_powershell_executable_blocking;
|
use codex_shell_command::powershell::try_find_powershell_executable_blocking;
|
||||||
@@ -108,6 +109,7 @@ async fn shell_command_handler_to_exec_params_uses_selected_environment() {
|
|||||||
environment_id: "selected-environment".to_string(),
|
environment_id: "selected-environment".to_string(),
|
||||||
cwd: PathUri::from_abs_path(&selected_cwd),
|
cwd: PathUri::from_abs_path(&selected_cwd),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
Arc::clone(
|
Arc::clone(
|
||||||
&turn_context
|
&turn_context
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use codex_protocol::models::AdditionalPermissionProfile;
|
|||||||
use codex_protocol::models::FileSystemPermissions;
|
use codex_protocol::models::FileSystemPermissions;
|
||||||
use codex_protocol::models::PermissionProfile;
|
use codex_protocol::models::PermissionProfile;
|
||||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::GranularApprovalConfig;
|
use codex_protocol::protocol::GranularApprovalConfig;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||||
use codex_sandboxing::SandboxManager;
|
use codex_sandboxing::SandboxManager;
|
||||||
@@ -23,6 +24,7 @@ fn test_turn_environment(environment_id: &str) -> crate::session::turn_context::
|
|||||||
environment_id: environment_id.to_string(),
|
environment_id: environment_id.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&std::env::temp_dir().abs()),
|
cwd: PathUri::from_abs_path(&std::env::temp_dir().abs()),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
std::sync::Arc::new(codex_exec_server::Environment::default_for_tests()),
|
std::sync::Arc::new(codex_exec_server::Environment::default_for_tests()),
|
||||||
/*shell*/ None,
|
/*shell*/ None,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use crate::session::turn_context::TurnEnvironmentConfig;
|
|||||||
use crate::tools::approvals::ApprovalCacheKey;
|
use crate::tools::approvals::ApprovalCacheKey;
|
||||||
use codex_exec_server::Environment;
|
use codex_exec_server::Environment;
|
||||||
use codex_protocol::models::PermissionProfile;
|
use codex_protocol::models::PermissionProfile;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||||
use codex_utils_path_uri::PathUri;
|
use codex_utils_path_uri::PathUri;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
@@ -20,6 +21,7 @@ async fn approval_key_uses_path_uri_and_includes_environment_id() {
|
|||||||
environment_id: "remote".to_string(),
|
environment_id: "remote".to_string(),
|
||||||
cwd: PathUri::from_abs_path(&cwd),
|
cwd: PathUri::from_abs_path(&cwd),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
Arc::new(Environment::default_for_tests()),
|
Arc::new(Environment::default_for_tests()),
|
||||||
/*shell*/ None,
|
/*shell*/ None,
|
||||||
|
|||||||
@@ -520,6 +520,7 @@ mod tests {
|
|||||||
use codex_exec_server::Environment;
|
use codex_exec_server::Environment;
|
||||||
use codex_exec_server::LOCAL_ENVIRONMENT_ID;
|
use codex_exec_server::LOCAL_ENVIRONMENT_ID;
|
||||||
use codex_protocol::models::PermissionProfile;
|
use codex_protocol::models::PermissionProfile;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||||
use codex_tools::ZshForkConfig;
|
use codex_tools::ZshForkConfig;
|
||||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||||
@@ -535,6 +536,7 @@ mod tests {
|
|||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd,
|
cwd,
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
Arc::new(Environment::default_for_tests()),
|
Arc::new(Environment::default_for_tests()),
|
||||||
/*shell*/ None,
|
/*shell*/ None,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use codex_protocol::openai_models::ConfigShellToolType;
|
|||||||
use codex_protocol::openai_models::InputModality;
|
use codex_protocol::openai_models::InputModality;
|
||||||
use codex_protocol::openai_models::ToolMode;
|
use codex_protocol::openai_models::ToolMode;
|
||||||
use codex_protocol::openai_models::WebSearchToolType;
|
use codex_protocol::openai_models::WebSearchToolType;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::MultiAgentVersion;
|
use codex_protocol::protocol::MultiAgentVersion;
|
||||||
use codex_protocol::protocol::SessionSource;
|
use codex_protocol::protocol::SessionSource;
|
||||||
use codex_protocol::protocol::SubAgentSource;
|
use codex_protocol::protocol::SubAgentSource;
|
||||||
@@ -907,6 +908,7 @@ async fn zsh_fork_unified_exec_keeps_shell_parameter_when_remote_environment_ava
|
|||||||
environment_id: "remote".to_string(),
|
environment_id: "remote".to_string(),
|
||||||
cwd: remote_cwd,
|
cwd: remote_cwd,
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
Arc::new(
|
Arc::new(
|
||||||
codex_exec_server::Environment::create_for_tests(Some(
|
codex_exec_server::Environment::create_for_tests(Some(
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ use codex_protocol::models::PermissionProfile;
|
|||||||
use codex_protocol::openai_models::ModelInfo;
|
use codex_protocol::openai_models::ModelInfo;
|
||||||
use codex_protocol::openai_models::ModelsResponse;
|
use codex_protocol::openai_models::ModelsResponse;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
use codex_protocol::protocol::RealtimeConversationVersion as RealtimeWsVersion;
|
use codex_protocol::protocol::RealtimeConversationVersion as RealtimeWsVersion;
|
||||||
use codex_protocol::protocol::SandboxPolicy;
|
use codex_protocol::protocol::SandboxPolicy;
|
||||||
@@ -118,6 +119,7 @@ pub fn local(cwd: AbsolutePathBuf) -> TurnEnvironmentSelection {
|
|||||||
environment_id: codex_exec_server::LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: codex_exec_server::LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&cwd),
|
cwd: PathUri::from_abs_path(&cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +152,7 @@ impl TestEnv {
|
|||||||
environment_id: codex_exec_server::REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: codex_exec_server::REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&cwd),
|
cwd: PathUri::from_abs_path(&cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
None => local(cwd.clone()),
|
None => local(cwd.clone()),
|
||||||
};
|
};
|
||||||
@@ -218,6 +221,7 @@ pub async fn test_env() -> Result<TestEnv> {
|
|||||||
environment_id: codex_exec_server::REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: codex_exec_server::REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: cwd_uri.clone(),
|
cwd: cwd_uri.clone(),
|
||||||
workspace_roots: vec![cwd_uri.clone()],
|
workspace_roots: vec![cwd_uri.clone()],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
let cwd = if remote_env == TestEnvironment::WineExec {
|
let cwd = if remote_env == TestEnvironment::WineExec {
|
||||||
// TODO(anp): Convert `Config::cwd` to `LegacyAppPathString` and remove this
|
// TODO(anp): Convert `Config::cwd` to `LegacyAppPathString` and remove this
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use codex_protocol::config_types::ModeKind;
|
|||||||
use codex_protocol::config_types::Settings;
|
use codex_protocol::config_types::Settings;
|
||||||
use codex_protocol::models::PermissionProfile;
|
use codex_protocol::models::PermissionProfile;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
use codex_protocol::protocol::ExecCommandStatus;
|
use codex_protocol::protocol::ExecCommandStatus;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||||
@@ -115,6 +116,7 @@ async fn windows_exec_server_runs_with_native_shell_and_cwd() -> Result<()> {
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: cwd.clone(),
|
cwd: cwd.clone(),
|
||||||
workspace_roots: vec![cwd],
|
workspace_roots: vec![cwd],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use codex_features::Feature;
|
|||||||
use codex_history::RolloutItem;
|
use codex_history::RolloutItem;
|
||||||
use codex_history::RolloutLine;
|
use codex_history::RolloutLine;
|
||||||
use codex_home::CodexHomeUserInstructionsProvider;
|
use codex_home::CodexHomeUserInstructionsProvider;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
use codex_protocol::protocol::Op;
|
use codex_protocol::protocol::Op;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||||
@@ -658,11 +659,13 @@ async fn multi_environment_project_instructions_share_one_byte_budget() -> Resul
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&test.config.cwd),
|
cwd: PathUri::from_abs_path(&test.config.cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
TurnEnvironmentSelection {
|
TurnEnvironmentSelection {
|
||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_host_native_path(local_root.path())?,
|
cwd: PathUri::from_host_native_path(local_root.path())?,
|
||||||
workspace_roots: vec![PathUri::from_host_native_path(local_root.path())?],
|
workspace_roots: vec![PathUri::from_host_native_path(local_root.path())?],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
..StartThreadOptions::new(test.config.clone())
|
..StartThreadOptions::new(test.config.clone())
|
||||||
@@ -736,11 +739,13 @@ async fn multi_environment_thread_loads_every_project_and_keeps_creation_snapsho
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&test.config.cwd),
|
cwd: PathUri::from_abs_path(&test.config.cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
TurnEnvironmentSelection {
|
TurnEnvironmentSelection {
|
||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_host_native_path(local_root.path())?,
|
cwd: PathUri::from_host_native_path(local_root.path())?,
|
||||||
workspace_roots: vec![PathUri::from_host_native_path(local_root.path())?],
|
workspace_roots: vec![PathUri::from_host_native_path(local_root.path())?],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
..StartThreadOptions::new(test.config.clone())
|
..StartThreadOptions::new(test.config.clone())
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ use codex_protocol::permissions::FileSystemSandboxPolicy;
|
|||||||
use codex_protocol::permissions::FileSystemSpecialPath;
|
use codex_protocol::permissions::FileSystemSpecialPath;
|
||||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
use codex_protocol::protocol::SandboxPolicy;
|
use codex_protocol::protocol::SandboxPolicy;
|
||||||
use codex_protocol::protocol::ThreadSettingsOverrides;
|
use codex_protocol::protocol::ThreadSettingsOverrides;
|
||||||
@@ -1886,6 +1887,7 @@ async fn apply_patch_turn_diff_tracks_local_and_remote_environment_paths() -> Re
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&shared_cwd),
|
cwd: PathUri::from_abs_path(&shared_cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&shared_cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&shared_cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
test.codex
|
test.codex
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use codex_protocol::config_types::ModeKind;
|
|||||||
use codex_protocol::config_types::Settings;
|
use codex_protocol::config_types::Settings;
|
||||||
use codex_protocol::models::PermissionProfile;
|
use codex_protocol::models::PermissionProfile;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
use codex_protocol::protocol::ThreadSettingsOverrides;
|
use codex_protocol::protocol::ThreadSettingsOverrides;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||||
@@ -217,6 +218,7 @@ async fn model_visible_environment_context_preserves_foreign_workspace_roots() -
|
|||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&test.config.cwd),
|
cwd: PathUri::from_abs_path(&test.config.cwd),
|
||||||
workspace_roots: vec![foreign_root],
|
workspace_roots: vec![foreign_root],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
}],
|
}],
|
||||||
)),
|
)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use codex_features::Feature;
|
|||||||
use codex_protocol::models::PermissionProfile;
|
use codex_protocol::models::PermissionProfile;
|
||||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
use codex_protocol::protocol::ThreadSettingsOverrides;
|
use codex_protocol::protocol::ThreadSettingsOverrides;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||||
@@ -190,11 +191,13 @@ async fn two_exec_servers_isolate_workspace_write_roots() -> Result<()> {
|
|||||||
environment_id: FIRST_ENVIRONMENT_ID.to_string(),
|
environment_id: FIRST_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: first_workspace_uri.clone(),
|
cwd: first_workspace_uri.clone(),
|
||||||
workspace_roots: vec![first_workspace_uri],
|
workspace_roots: vec![first_workspace_uri],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
TurnEnvironmentSelection {
|
TurnEnvironmentSelection {
|
||||||
environment_id: SECOND_ENVIRONMENT_ID.to_string(),
|
environment_id: SECOND_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: second_workspace_uri.clone(),
|
cwd: second_workspace_uri.clone(),
|
||||||
workspace_roots: vec![second_workspace_uri],
|
workspace_roots: vec![second_workspace_uri],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
let permission_profile = PermissionProfile::workspace_write_with(
|
let permission_profile = PermissionProfile::workspace_write_with(
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use codex_protocol::models::NetworkPermissions;
|
|||||||
use codex_protocol::models::PermissionProfile;
|
use codex_protocol::models::PermissionProfile;
|
||||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
use codex_protocol::protocol::ExecApprovalRequestEvent;
|
use codex_protocol::protocol::ExecApprovalRequestEvent;
|
||||||
use codex_protocol::protocol::GuardianAssessmentStatus;
|
use codex_protocol::protocol::GuardianAssessmentStatus;
|
||||||
@@ -1837,6 +1838,7 @@ async fn approved_network_host_for_one_environment_still_prompts_in_another() ->
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&remote_cwd),
|
cwd: PathUri::from_abs_path(&remote_cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ use codex_protocol::permissions::NetworkSandboxPolicy;
|
|||||||
use codex_protocol::protocol::ApplyPatchApprovalRequestEvent;
|
use codex_protocol::protocol::ApplyPatchApprovalRequestEvent;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
use codex_protocol::protocol::ENVIRONMENTS_INSTRUCTIONS_OPEN_TAG;
|
use codex_protocol::protocol::ENVIRONMENTS_INSTRUCTIONS_OPEN_TAG;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
use codex_protocol::protocol::Op;
|
use codex_protocol::protocol::Op;
|
||||||
use codex_protocol::protocol::ReviewDecision;
|
use codex_protocol::protocol::ReviewDecision;
|
||||||
@@ -415,6 +416,7 @@ async fn explicit_remote_shell_runs_in_remote_cwd() -> Result<()> {
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&test.config.cwd),
|
cwd: PathUri::from_abs_path(&test.config.cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
}]),
|
}]),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -693,6 +695,7 @@ async fn deferred_executor_promotes_primary_environment_when_startup_completes()
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&test.config.cwd),
|
cwd: PathUri::from_abs_path(&test.config.cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
|
|
||||||
test.submit_turn_with_environments(
|
test.submit_turn_with_environments(
|
||||||
@@ -1005,36 +1008,69 @@ async fn shared_executor_keeps_ready_capability_roots_scoped_to_each_attachment(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for config in [
|
||||||
|
EnvironmentConfigState::Pending,
|
||||||
|
EnvironmentConfigState::Ready(EnvironmentConfig {
|
||||||
|
allow_login_shell: false,
|
||||||
|
selected_capability_roots: vec![root("duplicate"), root("duplicate")],
|
||||||
|
}),
|
||||||
|
] {
|
||||||
|
let invalid_selection = TurnEnvironmentSelection {
|
||||||
|
config,
|
||||||
|
..selection.clone()
|
||||||
|
};
|
||||||
|
assert!(
|
||||||
|
test.codex
|
||||||
|
.preview_thread_settings_overrides(CodexThreadSettingsOverrides {
|
||||||
|
environments: Some(TurnEnvironmentSelections::new(
|
||||||
|
test.config.cwd.clone(),
|
||||||
|
vec![invalid_selection],
|
||||||
|
)),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
test.codex.environment_selections().await,
|
||||||
|
vec![selection.clone()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let mut second_thread_init = ExtensionDataInit::new();
|
let mut second_thread_init = ExtensionDataInit::new();
|
||||||
second_thread_init.insert(vec![root("startup-root")]);
|
second_thread_init.insert(vec![root("startup-root")]);
|
||||||
let second = test
|
let second = test
|
||||||
.thread_manager
|
.thread_manager
|
||||||
.start_thread(StartThreadOptions {
|
.start_thread(StartThreadOptions {
|
||||||
environments: Some(vec![selection.clone()]),
|
environments: Some(vec![TurnEnvironmentSelection {
|
||||||
|
config: EnvironmentConfigState::Ready(EnvironmentConfig {
|
||||||
|
allow_login_shell: true,
|
||||||
|
selected_capability_roots: vec![root("startup-root"), root("second-root")],
|
||||||
|
}),
|
||||||
|
..selection.clone()
|
||||||
|
}]),
|
||||||
thread_extension_init: second_thread_init,
|
thread_extension_init: second_thread_init,
|
||||||
..StartThreadOptions::new(test.config.clone())
|
..StartThreadOptions::new(test.config.clone())
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
test.codex
|
submit_thread_settings(
|
||||||
.environment_ready(
|
&test.codex,
|
||||||
&selection,
|
ThreadSettingsOverrides {
|
||||||
EnvironmentConfig {
|
environments: Some(TurnEnvironmentSelections::new(
|
||||||
allow_login_shell: false,
|
test.config.cwd.clone(),
|
||||||
selected_capability_roots: vec![root("first-root")],
|
vec![TurnEnvironmentSelection {
|
||||||
},
|
config: EnvironmentConfigState::Ready(EnvironmentConfig {
|
||||||
)
|
allow_login_shell: false,
|
||||||
.await?;
|
selected_capability_roots: vec![root("first-root")],
|
||||||
second
|
}),
|
||||||
.thread
|
..selection.clone()
|
||||||
.environment_ready(
|
}],
|
||||||
&selection,
|
)),
|
||||||
EnvironmentConfig {
|
..Default::default()
|
||||||
allow_login_shell: true,
|
},
|
||||||
selected_capability_roots: vec![root("startup-root"), root("second-root")],
|
)
|
||||||
},
|
.await?;
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
test.codex.inspect_selected_capability_roots().ready_roots,
|
test.codex.inspect_selected_capability_roots().ready_roots,
|
||||||
@@ -1071,24 +1107,27 @@ async fn shared_executor_keeps_ready_capability_roots_scoped_to_each_attachment(
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
|
let mut request = TurnInputRequest::user_input(vec![UserInput::Text {
|
||||||
|
text: prompt.to_string(),
|
||||||
|
text_elements: Vec::new(),
|
||||||
|
}]);
|
||||||
if index == 2 {
|
if index == 2 {
|
||||||
test.codex
|
request = request.with_thread_settings(ThreadSettingsOverrides {
|
||||||
.environment_ready(
|
environments: Some(TurnEnvironmentSelections::new(
|
||||||
&selection,
|
test.config.cwd.clone(),
|
||||||
EnvironmentConfig {
|
vec![TurnEnvironmentSelection {
|
||||||
allow_login_shell: false,
|
config: EnvironmentConfigState::Ready(EnvironmentConfig {
|
||||||
selected_capability_roots: vec![root("first-updated-root")],
|
allow_login_shell: false,
|
||||||
},
|
selected_capability_roots: vec![root("first-updated-root")],
|
||||||
)
|
}),
|
||||||
.await?;
|
..selection.clone()
|
||||||
|
}],
|
||||||
|
)),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
thread
|
thread.start_or_steer_turn(request).await?;
|
||||||
.start_or_steer_turn(TurnInputRequest::user_input(vec![UserInput::Text {
|
|
||||||
text: prompt.to_string(),
|
|
||||||
text_elements: Vec::new(),
|
|
||||||
}]))
|
|
||||||
.await?;
|
|
||||||
wait_for_event(thread, |event| matches!(event, EventMsg::TurnComplete(_))).await;
|
wait_for_event(thread, |event| matches!(event, EventMsg::TurnComplete(_))).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1288,6 +1327,7 @@ async fn ready_before_selection_exposes_remote_tools_and_capability_context_afte
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&test.config.cwd),
|
cwd: PathUri::from_abs_path(&test.config.cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
}]),
|
}]),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -1390,6 +1430,7 @@ async fn deferred_executor_stays_pending_after_materialization() -> Result<()> {
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&test.config.cwd),
|
cwd: PathUri::from_abs_path(&test.config.cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
}],
|
}],
|
||||||
)),
|
)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -1514,6 +1555,7 @@ async fn deferred_executor_spawn_agent_inherits_ready_step_environments(
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&test.config.cwd),
|
cwd: PathUri::from_abs_path(&test.config.cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
let expected_environments = vec![remote_selection, local(test.config.cwd.clone())];
|
let expected_environments = vec![remote_selection, local(test.config.cwd.clone())];
|
||||||
let mut created_threads = test.thread_manager.subscribe_thread_created();
|
let mut created_threads = test.thread_manager.subscribe_thread_created();
|
||||||
@@ -1646,6 +1688,7 @@ async fn deferred_executor_guardian_uses_newly_ready_step_environment() -> Resul
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&remote_cwd),
|
cwd: PathUri::from_abs_path(&remote_cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
let permission_profile = PermissionProfile::from_runtime_permissions(
|
let permission_profile = PermissionProfile::from_runtime_permissions(
|
||||||
&FileSystemSandboxPolicy::restricted(vec![
|
&FileSystemSandboxPolicy::restricted(vec![
|
||||||
@@ -2142,6 +2185,7 @@ async fn exec_command_routes_to_selected_remote_environment() -> Result<()> {
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&remote_cwd),
|
cwd: PathUri::from_abs_path(&remote_cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
let multi_env_output = exec_command_routing_output(
|
let multi_env_output = exec_command_routing_output(
|
||||||
&test,
|
&test,
|
||||||
@@ -2280,11 +2324,13 @@ async fn remote_exec_materializes_target_roots_before_sandbox_selection() -> Res
|
|||||||
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&local_cwd.path().abs()),
|
cwd: PathUri::from_abs_path(&local_cwd.path().abs()),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
TurnEnvironmentSelection {
|
TurnEnvironmentSelection {
|
||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: remote_cwd_uri.clone(),
|
cwd: remote_cwd_uri.clone(),
|
||||||
workspace_roots: vec![remote_cwd_uri.clone()],
|
workspace_roots: vec![remote_cwd_uri.clone()],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
@@ -2453,6 +2499,7 @@ async fn remote_request_permissions_grant_unblocks_later_remote_exec() -> Result
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&remote_cwd),
|
cwd: PathUri::from_abs_path(&remote_cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
AskForApproval::OnRequest,
|
AskForApproval::OnRequest,
|
||||||
@@ -2595,6 +2642,7 @@ async fn apply_patch_freeform_routes_to_selected_remote_environment() -> Result<
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&remote_cwd),
|
cwd: PathUri::from_abs_path(&remote_cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
@@ -2679,6 +2727,7 @@ async fn apply_patch_approvals_are_remembered_per_environment() -> Result<()> {
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&remote_cwd),
|
cwd: PathUri::from_abs_path(&remote_cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
let local_patch = format!(
|
let local_patch = format!(
|
||||||
@@ -2881,6 +2930,7 @@ async fn apply_patch_intercepted_exec_command_routes_to_selected_remote_environm
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: PathUri::from_abs_path(&remote_cwd),
|
cwd: PathUri::from_abs_path(&remote_cwd),
|
||||||
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use codex_protocol::permissions::FileSystemSandboxPolicy;
|
|||||||
use codex_protocol::permissions::FileSystemSpecialPath;
|
use codex_protocol::permissions::FileSystemSpecialPath;
|
||||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
use codex_protocol::protocol::Op;
|
use codex_protocol::protocol::Op;
|
||||||
use codex_protocol::protocol::ReviewDecision;
|
use codex_protocol::protocol::ReviewDecision;
|
||||||
@@ -704,6 +705,7 @@ timeout = 900
|
|||||||
PathUri::parse("file:///D:/other-workspace")
|
PathUri::parse("file:///D:/other-workspace")
|
||||||
.expect("valid Windows workspace root"),
|
.expect("valid Windows workspace root"),
|
||||||
],
|
],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
}],
|
}],
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ use codex_protocol::permissions::FileSystemSandboxEntry;
|
|||||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
use codex_protocol::protocol::ThreadSettingsOverrides;
|
use codex_protocol::protocol::ThreadSettingsOverrides;
|
||||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||||
@@ -748,6 +749,7 @@ async fn view_image_routes_to_selected_remote_environment() -> anyhow::Result<()
|
|||||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||||
cwd: remote_cwd_uri.clone(),
|
cwd: remote_cwd_uri.clone(),
|
||||||
workspace_roots: vec![remote_cwd_uri],
|
workspace_roots: vec![remote_cwd_uri],
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
let relative_call_id = "call-view-image-relative-multi-env";
|
let relative_call_id = "call-view-image-relative-multi-env";
|
||||||
let absolute_call_id = "call-view-image-absolute-multi-env";
|
let absolute_call_id = "call-view-image-absolute-multi-env";
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ use codex_otel::THREAD_SKILLS_KEPT_TOTAL_METRIC;
|
|||||||
use codex_otel::THREAD_SKILLS_TRUNCATED_METRIC;
|
use codex_otel::THREAD_SKILLS_TRUNCATED_METRIC;
|
||||||
use codex_protocol::capabilities::CapabilityRootLocation;
|
use codex_protocol::capabilities::CapabilityRootLocation;
|
||||||
use codex_protocol::capabilities::SelectedCapabilityRoot;
|
use codex_protocol::capabilities::SelectedCapabilityRoot;
|
||||||
|
use codex_protocol::protocol::EnvironmentConfigState;
|
||||||
use codex_protocol::protocol::Event;
|
use codex_protocol::protocol::Event;
|
||||||
use codex_protocol::protocol::SKILLS_INSTRUCTIONS_CLOSE_TAG;
|
use codex_protocol::protocol::SKILLS_INSTRUCTIONS_CLOSE_TAG;
|
||||||
use codex_protocol::protocol::SKILLS_INSTRUCTIONS_OPEN_TAG;
|
use codex_protocol::protocol::SKILLS_INSTRUCTIONS_OPEN_TAG;
|
||||||
@@ -1031,6 +1032,7 @@ async fn selected_executor_catalog_follows_step_availability_and_reuses_its_cach
|
|||||||
environment_id: "turn-env".to_string(),
|
environment_id: "turn-env".to_string(),
|
||||||
cwd: PathUri::parse("file:///workspace").expect("cwd URI"),
|
cwd: PathUri::parse("file:///workspace").expect("cwd URI"),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
};
|
};
|
||||||
let available_sections = registry.context_contributors()[0]
|
let available_sections = registry.context_contributors()[0]
|
||||||
.contribute_world_state(WorldStateContributionInput {
|
.contribute_world_state(WorldStateContributionInput {
|
||||||
@@ -1752,6 +1754,7 @@ async fn root_qualified_locator_selects_only_the_matching_executor_skill() -> Te
|
|||||||
environment_id: "env-1".to_string(),
|
environment_id: "env-1".to_string(),
|
||||||
cwd: PathUri::parse("file:///workspace").expect("cwd URI"),
|
cwd: PathUri::parse("file:///workspace").expect("cwd URI"),
|
||||||
workspace_roots: Vec::new(),
|
workspace_roots: Vec::new(),
|
||||||
|
config: EnvironmentConfigState::FromThread,
|
||||||
}],
|
}],
|
||||||
ready_selected_capability_roots: &selected_roots,
|
ready_selected_capability_roots: &selected_roots,
|
||||||
executor_capability_discovery: None,
|
executor_capability_discovery: None,
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
use crate::capabilities::SelectedCapabilityRoot;
|
use crate::capabilities::SelectedCapabilityRoot;
|
||||||
|
|
||||||
|
/// Configuration supplied for a thread's selected environment.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum EnvironmentConfigState {
|
||||||
|
/// Preserve the existing thread-derived environment configuration.
|
||||||
|
FromThread,
|
||||||
|
/// The owner will supply environment configuration later.
|
||||||
|
Pending,
|
||||||
|
/// The owner supplied configuration for this environment attachment.
|
||||||
|
Ready(EnvironmentConfig),
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolved configuration supplied by the owner of a thread/environment attachment.
|
/// Resolved configuration supplied by the owner of a thread/environment attachment.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct EnvironmentConfig {
|
pub struct EnvironmentConfig {
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ pub use crate::approvals::NetworkApprovalProtocol;
|
|||||||
pub use crate::approvals::NetworkPolicyAmendment;
|
pub use crate::approvals::NetworkPolicyAmendment;
|
||||||
pub use crate::approvals::NetworkPolicyRuleAction;
|
pub use crate::approvals::NetworkPolicyRuleAction;
|
||||||
pub use crate::environment::EnvironmentConfig;
|
pub use crate::environment::EnvironmentConfig;
|
||||||
|
pub use crate::environment::EnvironmentConfigState;
|
||||||
pub use crate::legacy_events::HasLegacyEvent;
|
pub use crate::legacy_events::HasLegacyEvent;
|
||||||
pub use crate::permissions::FileSystemAccessMode;
|
pub use crate::permissions::FileSystemAccessMode;
|
||||||
pub use crate::permissions::FileSystemPath;
|
pub use crate::permissions::FileSystemPath;
|
||||||
@@ -146,6 +147,7 @@ pub struct TurnEnvironmentSelection {
|
|||||||
pub environment_id: String,
|
pub environment_id: String,
|
||||||
pub cwd: PathUri,
|
pub cwd: PathUri,
|
||||||
pub workspace_roots: Vec<PathUri>,
|
pub workspace_roots: Vec<PathUri>,
|
||||||
|
pub config: EnvironmentConfigState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|||||||
Reference in new issue
Block a user