put workspace roots in the environment context (#3375)
to keep the tool description constant when the writable roots change.
This commit is contained in:
@@ -26,6 +26,7 @@ pub(crate) struct EnvironmentContext {
|
|||||||
pub approval_policy: Option<AskForApproval>,
|
pub approval_policy: Option<AskForApproval>,
|
||||||
pub sandbox_mode: Option<SandboxMode>,
|
pub sandbox_mode: Option<SandboxMode>,
|
||||||
pub network_access: Option<NetworkAccess>,
|
pub network_access: Option<NetworkAccess>,
|
||||||
|
pub writable_roots: Option<Vec<PathBuf>>,
|
||||||
pub shell: Option<Shell>,
|
pub shell: Option<Shell>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +58,16 @@ impl EnvironmentContext {
|
|||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
},
|
},
|
||||||
|
writable_roots: match sandbox_policy {
|
||||||
|
Some(SandboxPolicy::WorkspaceWrite { writable_roots, .. }) => {
|
||||||
|
if writable_roots.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(writable_roots.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
shell,
|
shell,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,6 +83,7 @@ impl EnvironmentContext {
|
|||||||
/// <cwd>...</cwd>
|
/// <cwd>...</cwd>
|
||||||
/// <approval_policy>...</approval_policy>
|
/// <approval_policy>...</approval_policy>
|
||||||
/// <sandbox_mode>...</sandbox_mode>
|
/// <sandbox_mode>...</sandbox_mode>
|
||||||
|
/// <writable_roots>...</writable_roots>
|
||||||
/// <network_access>...</network_access>
|
/// <network_access>...</network_access>
|
||||||
/// <shell>...</shell>
|
/// <shell>...</shell>
|
||||||
/// </environment_context>
|
/// </environment_context>
|
||||||
@@ -94,6 +106,16 @@ impl EnvironmentContext {
|
|||||||
" <network_access>{network_access}</network_access>"
|
" <network_access>{network_access}</network_access>"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if let Some(writable_roots) = self.writable_roots {
|
||||||
|
lines.push(" <writable_roots>".to_string());
|
||||||
|
for writable_root in writable_roots {
|
||||||
|
lines.push(format!(
|
||||||
|
" <root>{}</root>",
|
||||||
|
writable_root.to_string_lossy()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
lines.push(" </writable_roots>".to_string());
|
||||||
|
}
|
||||||
if let Some(shell) = self.shell
|
if let Some(shell) = self.shell
|
||||||
&& let Some(shell_name) = shell.name()
|
&& let Some(shell_name) = shell.name()
|
||||||
{
|
{
|
||||||
@@ -115,3 +137,77 @@ impl From<EnvironmentContext> for ResponseItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
fn workspace_write_policy(writable_roots: Vec<&str>, network_access: bool) -> SandboxPolicy {
|
||||||
|
SandboxPolicy::WorkspaceWrite {
|
||||||
|
writable_roots: writable_roots.into_iter().map(PathBuf::from).collect(),
|
||||||
|
network_access,
|
||||||
|
exclude_tmpdir_env_var: false,
|
||||||
|
exclude_slash_tmp: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_workspace_write_environment_context() {
|
||||||
|
let context = EnvironmentContext::new(
|
||||||
|
Some(PathBuf::from("/repo")),
|
||||||
|
Some(AskForApproval::OnRequest),
|
||||||
|
Some(workspace_write_policy(vec!["/repo", "/tmp"], false)),
|
||||||
|
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>"#;
|
||||||
|
|
||||||
|
assert_eq!(context.serialize_to_xml(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_read_only_environment_context() {
|
||||||
|
let context = EnvironmentContext::new(
|
||||||
|
None,
|
||||||
|
Some(AskForApproval::Never),
|
||||||
|
Some(SandboxPolicy::ReadOnly),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
let expected = r#"<environment_context>
|
||||||
|
<approval_policy>never</approval_policy>
|
||||||
|
<sandbox_mode>read-only</sandbox_mode>
|
||||||
|
<network_access>restricted</network_access>
|
||||||
|
</environment_context>"#;
|
||||||
|
|
||||||
|
assert_eq!(context.serialize_to_xml(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_full_access_environment_context() {
|
||||||
|
let context = EnvironmentContext::new(
|
||||||
|
None,
|
||||||
|
Some(AskForApproval::OnFailure),
|
||||||
|
Some(SandboxPolicy::DangerFullAccess),
|
||||||
|
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>"#;
|
||||||
|
|
||||||
|
assert_eq!(context.serialize_to_xml(), expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -240,17 +240,20 @@ fn create_shell_tool_for_sandbox(sandbox_policy: &SandboxPolicy) -> OpenAiTool {
|
|||||||
let description = match sandbox_policy {
|
let description = match sandbox_policy {
|
||||||
SandboxPolicy::WorkspaceWrite {
|
SandboxPolicy::WorkspaceWrite {
|
||||||
network_access,
|
network_access,
|
||||||
writable_roots,
|
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
let network_line = if !network_access {
|
||||||
|
"\n - Commands that require network access"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
format!(
|
format!(
|
||||||
r#"
|
r#"
|
||||||
The shell tool is used to execute shell commands.
|
The shell tool is used to execute shell commands.
|
||||||
- When invoking the shell tool, your call will be running in a sandbox, and some shell commands will require escalated privileges:
|
- When invoking the shell tool, your call will be running in a sandbox, and some shell commands will require escalated privileges:
|
||||||
- Types of actions that require escalated privileges:
|
- Types of actions that require escalated privileges:
|
||||||
- Writing files other than those in the writable roots
|
- Writing files other than those in the writable roots (see the environment context for the allowed directories){network_line}
|
||||||
- writable roots:
|
|
||||||
{}{}
|
|
||||||
- Examples of commands that require escalated privileges:
|
- Examples of commands that require escalated privileges:
|
||||||
- git commit
|
- git commit
|
||||||
- npm install or pnpm install
|
- npm install or pnpm install
|
||||||
@@ -259,12 +262,6 @@ The shell tool is used to execute shell commands.
|
|||||||
- When invoking a command that will require escalated privileges:
|
- When invoking a command that will require escalated privileges:
|
||||||
- Provide the with_escalated_permissions parameter with the boolean value true
|
- Provide the with_escalated_permissions parameter with the boolean value true
|
||||||
- Include a short, 1 sentence explanation for why we need to run with_escalated_permissions in the justification parameter."#,
|
- Include a short, 1 sentence explanation for why we need to run with_escalated_permissions in the justification parameter."#,
|
||||||
writable_roots.iter().map(|wr| format!(" - {}", wr.to_string_lossy())).collect::<Vec<String>>().join("\n"),
|
|
||||||
if !network_access {
|
|
||||||
"\n - Commands that require network access\n"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SandboxPolicy::DangerFullAccess => {
|
SandboxPolicy::DangerFullAccess => {
|
||||||
@@ -1105,11 +1102,8 @@ mod tests {
|
|||||||
The shell tool is used to execute shell commands.
|
The shell tool is used to execute shell commands.
|
||||||
- When invoking the shell tool, your call will be running in a sandbox, and some shell commands will require escalated privileges:
|
- When invoking the shell tool, your call will be running in a sandbox, and some shell commands will require escalated privileges:
|
||||||
- Types of actions that require escalated privileges:
|
- Types of actions that require escalated privileges:
|
||||||
- Writing files other than those in the writable roots
|
- Writing files other than those in the writable roots (see the environment context for the allowed directories)
|
||||||
- writable roots:
|
|
||||||
- workspace
|
|
||||||
- Commands that require network access
|
- Commands that require network access
|
||||||
|
|
||||||
- Examples of commands that require escalated privileges:
|
- Examples of commands that require escalated privileges:
|
||||||
- git commit
|
- git commit
|
||||||
- npm install or pnpm install
|
- npm install or pnpm install
|
||||||
|
|||||||
@@ -426,11 +426,17 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() {
|
|||||||
// After overriding the turn context, the environment context should be emitted again
|
// After overriding the turn context, the environment context should be emitted again
|
||||||
// reflecting the new approval policy and sandbox settings. Omit cwd because it did
|
// reflecting the new approval policy and sandbox settings. Omit cwd because it did
|
||||||
// not change.
|
// not change.
|
||||||
let expected_env_text_2 = r#"<environment_context>
|
let expected_env_text_2 = format!(
|
||||||
|
r#"<environment_context>
|
||||||
<approval_policy>never</approval_policy>
|
<approval_policy>never</approval_policy>
|
||||||
<sandbox_mode>workspace-write</sandbox_mode>
|
<sandbox_mode>workspace-write</sandbox_mode>
|
||||||
<network_access>enabled</network_access>
|
<network_access>enabled</network_access>
|
||||||
</environment_context>"#;
|
<writable_roots>
|
||||||
|
<root>{}</root>
|
||||||
|
</writable_roots>
|
||||||
|
</environment_context>"#,
|
||||||
|
writable.path().to_string_lossy()
|
||||||
|
);
|
||||||
let expected_env_msg_2 = serde_json::json!({
|
let expected_env_msg_2 = serde_json::json!({
|
||||||
"type": "message",
|
"type": "message",
|
||||||
"role": "user",
|
"role": "user",
|
||||||
|
|||||||
Reference in New Issue
Block a user