mirror of
https://github.com/openai/codex.git
synced 2026-02-01 22:47:52 +00:00
Compare commits
5 Commits
rust-v0.82
...
dh--env-co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66858e4b29 | ||
|
|
78aafa465b | ||
|
|
c9c5d694c9 | ||
|
|
86d636cf33 | ||
|
|
9f7097160d |
@@ -20,6 +20,14 @@ pub enum NetworkAccess {
|
||||
Restricted,
|
||||
Enabled,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct OperatingSystemInfo {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub is_likely_windows_subsystem_for_linux: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename = "environment_context", rename_all = "snake_case")]
|
||||
pub(crate) struct EnvironmentContext {
|
||||
@@ -29,6 +37,7 @@ pub(crate) struct EnvironmentContext {
|
||||
pub network_access: Option<NetworkAccess>,
|
||||
pub writable_roots: Option<Vec<PathBuf>>,
|
||||
pub shell: Option<Shell>,
|
||||
pub operating_system: Option<OperatingSystemInfo>,
|
||||
}
|
||||
|
||||
impl EnvironmentContext {
|
||||
@@ -70,6 +79,7 @@ impl EnvironmentContext {
|
||||
_ => None,
|
||||
},
|
||||
shell,
|
||||
operating_system: Self::operating_system_info(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +93,7 @@ impl EnvironmentContext {
|
||||
sandbox_mode,
|
||||
network_access,
|
||||
writable_roots,
|
||||
operating_system,
|
||||
// should compare all fields except shell
|
||||
shell: _,
|
||||
} = other;
|
||||
@@ -92,6 +103,7 @@ impl EnvironmentContext {
|
||||
&& self.sandbox_mode == *sandbox_mode
|
||||
&& self.network_access == *network_access
|
||||
&& self.writable_roots == *writable_roots
|
||||
&& self.operating_system == *operating_system
|
||||
}
|
||||
|
||||
pub fn diff(before: &TurnContext, after: &TurnContext) -> Self {
|
||||
@@ -110,7 +122,12 @@ impl EnvironmentContext {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
EnvironmentContext::new(cwd, approval_policy, sandbox_policy, None)
|
||||
// Diff messages should only include fields that changed between turns.
|
||||
// Operating system is a static property of the host and should not be
|
||||
// emitted as part of a per-turn diff.
|
||||
let mut ec = EnvironmentContext::new(cwd, approval_policy, sandbox_policy, None);
|
||||
ec.operating_system = None;
|
||||
ec
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,10 +158,11 @@ impl EnvironmentContext {
|
||||
/// <shell>...</shell>
|
||||
/// </environment_context>
|
||||
/// ```
|
||||
pub fn serialize_to_xml(self) -> String {
|
||||
pub fn serialize_to_xml(&self) -> String {
|
||||
let mut lines = vec![ENVIRONMENT_CONTEXT_OPEN_TAG.to_string()];
|
||||
if let Some(cwd) = self.cwd {
|
||||
lines.push(format!(" <cwd>{}</cwd>", cwd.to_string_lossy()));
|
||||
if let Some(cwd) = self.cwd.as_ref() {
|
||||
let cwd = cwd.to_string_lossy();
|
||||
lines.push(format!(" <cwd>{cwd}</cwd>"));
|
||||
}
|
||||
if let Some(approval_policy) = self.approval_policy {
|
||||
lines.push(format!(
|
||||
@@ -154,29 +172,44 @@ impl EnvironmentContext {
|
||||
if let Some(sandbox_mode) = self.sandbox_mode {
|
||||
lines.push(format!(" <sandbox_mode>{sandbox_mode}</sandbox_mode>"));
|
||||
}
|
||||
if let Some(network_access) = self.network_access {
|
||||
if let Some(network_access) = self.network_access.as_ref() {
|
||||
lines.push(format!(
|
||||
" <network_access>{network_access}</network_access>"
|
||||
));
|
||||
}
|
||||
if let Some(writable_roots) = self.writable_roots {
|
||||
if let Some(writable_roots) = self.writable_roots.as_ref() {
|
||||
lines.push(" <writable_roots>".to_string());
|
||||
for writable_root in writable_roots {
|
||||
lines.push(format!(
|
||||
" <root>{}</root>",
|
||||
writable_root.to_string_lossy()
|
||||
));
|
||||
let writable_root = writable_root.to_string_lossy();
|
||||
lines.push(format!(" <root>{writable_root}</root>"));
|
||||
}
|
||||
lines.push(" </writable_roots>".to_string());
|
||||
}
|
||||
if let Some(shell) = self.shell
|
||||
if let Some(shell) = self.shell.as_ref()
|
||||
&& let Some(shell_name) = shell.name()
|
||||
{
|
||||
lines.push(format!(" <shell>{shell_name}</shell>"));
|
||||
}
|
||||
if let Some(operating_system) = self.operating_system.as_ref() {
|
||||
lines.push(" <operating_system>".to_string());
|
||||
let name = operating_system.name.as_str();
|
||||
lines.push(format!(" <name>{name}</name>"));
|
||||
let version = operating_system.version.as_str();
|
||||
lines.push(format!(" <version>{version}</version>"));
|
||||
if let Some(is_wsl) = operating_system.is_likely_windows_subsystem_for_linux {
|
||||
lines.push(format!(
|
||||
" <is_likely_windows_subsystem_for_linux>{is_wsl}</is_likely_windows_subsystem_for_linux>"
|
||||
));
|
||||
}
|
||||
lines.push(" </operating_system>".to_string());
|
||||
}
|
||||
lines.push(ENVIRONMENT_CONTEXT_CLOSE_TAG.to_string());
|
||||
lines.join("\n")
|
||||
}
|
||||
|
||||
fn operating_system_info() -> Option<OperatingSystemInfo> {
|
||||
operating_system_info_impl()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EnvironmentContext> for ResponseItem {
|
||||
@@ -191,6 +224,47 @@ impl From<EnvironmentContext> for ResponseItem {
|
||||
}
|
||||
}
|
||||
|
||||
// Restrict Operating System Info to Windows and Linux inside WSL for now
|
||||
#[cfg(target_os = "windows")]
|
||||
fn operating_system_info_impl() -> Option<OperatingSystemInfo> {
|
||||
let info = os_info::get();
|
||||
Some(OperatingSystemInfo {
|
||||
name: info.os_type().to_string(),
|
||||
version: info.version().to_string(),
|
||||
is_likely_windows_subsystem_for_linux: Some(has_wsl_env_markers()),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
fn operating_system_info_impl() -> Option<OperatingSystemInfo> {
|
||||
let info = os_info::get();
|
||||
match has_wsl_env_markers() {
|
||||
true => Some(OperatingSystemInfo {
|
||||
name: info.os_type().to_string(),
|
||||
version: info.version().to_string(),
|
||||
is_likely_windows_subsystem_for_linux: Some(true),
|
||||
}),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn operating_system_info_impl() -> Option<OperatingSystemInfo> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn has_wsl_env_markers() -> bool {
|
||||
// Cache detection result since env vars are stable across process lifetime
|
||||
// and this function may be called multiple times.
|
||||
static CACHE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
|
||||
*CACHE.get_or_init(|| {
|
||||
std::env::var_os("WSL_INTEROP").is_some()
|
||||
|| std::env::var_os("WSLENV").is_some()
|
||||
|| std::env::var_os("WSL_DISTRO_NAME").is_some()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::shell::BashShell;
|
||||
@@ -198,6 +272,58 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
fn expected_environment_context(mut body_lines: Vec<String>) -> String {
|
||||
let mut lines = vec!["<environment_context>".to_string()];
|
||||
lines.append(&mut body_lines);
|
||||
if let Some(os) = EnvironmentContext::operating_system_info() {
|
||||
lines.push(" <operating_system>".to_string());
|
||||
lines.push(format!(" <name>{}</name>", os.name));
|
||||
lines.push(format!(" <version>{}</version>", os.version));
|
||||
if let Some(is_wsl) = os.is_likely_windows_subsystem_for_linux {
|
||||
lines.push(format!(
|
||||
" <is_likely_windows_subsystem_for_linux>{is_wsl}</is_likely_windows_subsystem_for_linux>"
|
||||
));
|
||||
}
|
||||
lines.push(" </operating_system>".to_string());
|
||||
}
|
||||
lines.push("</environment_context>".to_string());
|
||||
lines.join("\n")
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[test]
|
||||
fn operating_system_info_on_windows_includes_os_details() {
|
||||
let info = operating_system_info_impl().expect("expected Windows operating system info");
|
||||
let os_details = os_info::get();
|
||||
|
||||
assert_eq!(info.name, os_details.os_type().to_string());
|
||||
assert_eq!(info.version, os_details.version().to_string());
|
||||
assert_eq!(
|
||||
info.is_likely_windows_subsystem_for_linux,
|
||||
Some(has_wsl_env_markers())
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
#[test]
|
||||
fn operating_system_info_matches_wsl_detection_on_unix() {
|
||||
let info = operating_system_info_impl();
|
||||
let os_details = os_info::get();
|
||||
if has_wsl_env_markers() {
|
||||
let info = info.expect("expected WSL operating system info");
|
||||
assert_eq!(info.name, os_details.os_type().to_string());
|
||||
assert_eq!(info.version, os_details.version().to_string());
|
||||
assert_eq!(info.is_likely_windows_subsystem_for_linux, Some(true));
|
||||
} else {
|
||||
assert_eq!(info, None);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[test]
|
||||
fn operating_system_info_is_none_on_macos() {
|
||||
assert_eq!(operating_system_info_impl(), None);
|
||||
}
|
||||
|
||||
fn workspace_write_policy(writable_roots: Vec<&str>, network_access: bool) -> SandboxPolicy {
|
||||
SandboxPolicy::WorkspaceWrite {
|
||||
@@ -217,16 +343,16 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
|
||||
let expected = r#"<environment_context>
|
||||
<cwd>/repo</cwd>
|
||||
<approval_policy>on-request</approval_policy>
|
||||
<sandbox_mode>workspace-write</sandbox_mode>
|
||||
<network_access>restricted</network_access>
|
||||
<writable_roots>
|
||||
<root>/repo</root>
|
||||
<root>/tmp</root>
|
||||
</writable_roots>
|
||||
</environment_context>"#;
|
||||
let expected = expected_environment_context(vec![
|
||||
" <cwd>/repo</cwd>".to_string(),
|
||||
" <approval_policy>on-request</approval_policy>".to_string(),
|
||||
" <sandbox_mode>workspace-write</sandbox_mode>".to_string(),
|
||||
" <network_access>restricted</network_access>".to_string(),
|
||||
" <writable_roots>".to_string(),
|
||||
" <root>/repo</root>".to_string(),
|
||||
" <root>/tmp</root>".to_string(),
|
||||
" </writable_roots>".to_string(),
|
||||
]);
|
||||
|
||||
assert_eq!(context.serialize_to_xml(), expected);
|
||||
}
|
||||
@@ -240,11 +366,11 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
|
||||
let expected = r#"<environment_context>
|
||||
<approval_policy>never</approval_policy>
|
||||
<sandbox_mode>read-only</sandbox_mode>
|
||||
<network_access>restricted</network_access>
|
||||
</environment_context>"#;
|
||||
let expected = expected_environment_context(vec![
|
||||
" <approval_policy>never</approval_policy>".to_string(),
|
||||
" <sandbox_mode>read-only</sandbox_mode>".to_string(),
|
||||
" <network_access>restricted</network_access>".to_string(),
|
||||
]);
|
||||
|
||||
assert_eq!(context.serialize_to_xml(), expected);
|
||||
}
|
||||
@@ -258,11 +384,11 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
|
||||
let expected = r#"<environment_context>
|
||||
<approval_policy>on-failure</approval_policy>
|
||||
<sandbox_mode>danger-full-access</sandbox_mode>
|
||||
<network_access>enabled</network_access>
|
||||
</environment_context>"#;
|
||||
let expected = expected_environment_context(vec![
|
||||
" <approval_policy>on-failure</approval_policy>".to_string(),
|
||||
" <sandbox_mode>danger-full-access</sandbox_mode>".to_string(),
|
||||
" <network_access>enabled</network_access>".to_string(),
|
||||
]);
|
||||
|
||||
assert_eq!(context.serialize_to_xml(), expected);
|
||||
}
|
||||
|
||||
@@ -36,19 +36,53 @@ fn text_user_input(text: String) -> serde_json::Value {
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn has_wsl_env_markers() -> bool {
|
||||
std::env::var_os("WSL_INTEROP").is_some()
|
||||
|| std::env::var_os("WSLENV").is_some()
|
||||
|| std::env::var_os("WSL_DISTRO_NAME").is_some()
|
||||
}
|
||||
|
||||
fn operating_system_context_block() -> String {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let info = os_info::get();
|
||||
let name = info.os_type().to_string();
|
||||
let version = info.version().to_string();
|
||||
let is_wsl = has_wsl_env_markers();
|
||||
format!(
|
||||
" <operating_system>\n <name>{name}</name>\n <version>{version}</version>\n <is_likely_windows_subsystem_for_linux>{is_wsl}</is_likely_windows_subsystem_for_linux>\n </operating_system>\n"
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
{
|
||||
if has_wsl_env_markers() {
|
||||
" <operating_system>\n <name>{name}</name>\n <version></version>\n <is_likely_windows_subsystem_for_linux>true</is_likely_windows_subsystem_for_linux>\n </operating_system>\n".to_string()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn default_env_context_str(cwd: &str, shell: &Shell) -> String {
|
||||
let shell_line = match shell.name() {
|
||||
Some(name) => format!(" <shell>{name}</shell>\n"),
|
||||
None => String::new(),
|
||||
};
|
||||
let os_block = operating_system_context_block();
|
||||
format!(
|
||||
r#"<environment_context>
|
||||
<cwd>{}</cwd>
|
||||
<cwd>{cwd}</cwd>
|
||||
<approval_policy>on-request</approval_policy>
|
||||
<sandbox_mode>read-only</sandbox_mode>
|
||||
<network_access>restricted</network_access>
|
||||
{}</environment_context>"#,
|
||||
cwd,
|
||||
match shell.name() {
|
||||
Some(name) => format!(" <shell>{name}</shell>\n"),
|
||||
None => String::new(),
|
||||
}
|
||||
{shell_line}{os_block}</environment_context>"#
|
||||
)
|
||||
}
|
||||
|
||||
@@ -341,22 +375,10 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests
|
||||
|
||||
let shell = default_user_shell().await;
|
||||
|
||||
let expected_env_text = format!(
|
||||
r#"<environment_context>
|
||||
<cwd>{}</cwd>
|
||||
<approval_policy>on-request</approval_policy>
|
||||
<sandbox_mode>read-only</sandbox_mode>
|
||||
<network_access>restricted</network_access>
|
||||
{}</environment_context>"#,
|
||||
cwd.path().to_string_lossy(),
|
||||
match shell.name() {
|
||||
Some(name) => format!(" <shell>{name}</shell>\n"),
|
||||
None => String::new(),
|
||||
}
|
||||
);
|
||||
let cwd_str = cwd.path().to_string_lossy().into_owned();
|
||||
let expected_env_text = default_env_context_str(&cwd_str, &shell);
|
||||
let expected_ui_text = format!(
|
||||
"# AGENTS.md instructions for {}\n\n<INSTRUCTIONS>\nbe consistent and helpful\n</INSTRUCTIONS>",
|
||||
cwd.path().to_string_lossy()
|
||||
"# AGENTS.md instructions for {cwd_str}\n\n<INSTRUCTIONS>\nbe consistent and helpful\n</INSTRUCTIONS>"
|
||||
);
|
||||
|
||||
let expected_env_msg = serde_json::json!({
|
||||
|
||||
Reference in New Issue
Block a user