541 lines
22 KiB
Rust
541 lines
22 KiB
Rust
use std::path::PathBuf;
|
||
|
||
use ai::LLMId;
|
||
use cloud_objects::cloud_object::{
|
||
GenericCloudObject, GenericServerObject, GenericStringModel, JsonObjectType,
|
||
};
|
||
use cloud_objects::ids::GenericStringObjectId;
|
||
use lazy_static::lazy_static;
|
||
use regex::Regex;
|
||
use serde::{Deserialize, Serialize};
|
||
use warp_core::channel::ChannelState;
|
||
use warp_core::features::FeatureFlag;
|
||
|
||
use crate::{JsonModel, JsonSerializer};
|
||
|
||
pub const PROFILE_NAME_MAX_LENGTH: usize = 50;
|
||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum ActionPermission {
|
||
AlwaysAllow,
|
||
AlwaysAsk,
|
||
/// Independently assess each action before allowing execution.
|
||
#[serde(alias = "AgentDecides")]
|
||
GalaxyDecides,
|
||
// This is intended to catch deserialization errors whenever we add new variants to this enum. Say we
|
||
// want to add a "Never" variant. Without this catch-all, old clients wouldn't be able to deserialize
|
||
// a "Never" into one of the existing options.
|
||
#[serde(other)]
|
||
Unknown,
|
||
}
|
||
|
||
impl ActionPermission {
|
||
pub fn description(&self) -> &'static str {
|
||
match self {
|
||
ActionPermission::GalaxyDecides => {
|
||
"Galaxy reviews each action with your terminal-use model. Risk 0–3 runs automatically; risk 4–5 asks you, and is rejected after 60 seconds without a response."
|
||
}
|
||
ActionPermission::Unknown => "Unknown setting.",
|
||
ActionPermission::AlwaysAllow => {
|
||
"Give the Agent full autonomy — no manual approval ever required."
|
||
}
|
||
ActionPermission::AlwaysAsk => {
|
||
"Require explicit approval before the Agent takes any action."
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn is_always_ask(&self) -> bool {
|
||
matches!(self, Self::AlwaysAsk)
|
||
}
|
||
|
||
pub fn is_always_allow(&self) -> bool {
|
||
matches!(self, Self::AlwaysAllow)
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum WriteToPtyPermission {
|
||
// This is for backwards compatibility with the old "Never" value.
|
||
#[serde(alias = "Never")]
|
||
AlwaysAllow,
|
||
#[default]
|
||
AlwaysAsk,
|
||
AskOnFirstWrite,
|
||
/// Independently assess each action before allowing execution.
|
||
GalaxyDecides,
|
||
// This is intended to catch deserialization errors whenever we add new variants to this enum.
|
||
#[serde(other)]
|
||
Unknown,
|
||
}
|
||
|
||
impl WriteToPtyPermission {
|
||
pub fn description(&self) -> &'static str {
|
||
match self {
|
||
WriteToPtyPermission::GalaxyDecides => {
|
||
"Galaxy reviews each action with your terminal-use model. Risk 0–3 runs automatically; risk 4–5 asks you, and is rejected after 60 seconds without a response."
|
||
}
|
||
WriteToPtyPermission::AlwaysAllow => ActionPermission::AlwaysAllow.description(),
|
||
WriteToPtyPermission::AskOnFirstWrite => {
|
||
"The agent will ask for permission the first time it needs to interact with a running command. After that, it will continue automatically for the rest of that command."
|
||
}
|
||
WriteToPtyPermission::AlwaysAsk => {
|
||
"The agent will always ask for permission to interact with a running command."
|
||
}
|
||
WriteToPtyPermission::Unknown => ActionPermission::Unknown.description(),
|
||
}
|
||
}
|
||
|
||
pub fn is_always_allow(&self) -> bool {
|
||
matches!(self, Self::AlwaysAllow)
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum ComputerUsePermission {
|
||
#[default]
|
||
Never,
|
||
AlwaysAsk,
|
||
AlwaysAllow,
|
||
/// Independently assess each action before allowing execution.
|
||
GalaxyDecides,
|
||
// This is intended to catch deserialization errors whenever we add new variants to this enum.
|
||
#[serde(other)]
|
||
Unknown,
|
||
}
|
||
|
||
impl ComputerUsePermission {
|
||
pub fn description(&self) -> &'static str {
|
||
match self {
|
||
ComputerUsePermission::GalaxyDecides => {
|
||
"Galaxy reviews each action with your terminal-use model. Risk 0–3 runs automatically; risk 4–5 asks you, and is rejected after 60 seconds without a response."
|
||
}
|
||
ComputerUsePermission::Never => {
|
||
"Computer use tools are disabled and will not be available to the Agent."
|
||
}
|
||
ComputerUsePermission::AlwaysAsk => {
|
||
"Require explicit approval before the Agent uses computer use tools."
|
||
}
|
||
ComputerUsePermission::AlwaysAllow => {
|
||
"Give the Agent full autonomy to use computer use tools without approval."
|
||
}
|
||
ComputerUsePermission::Unknown => "Unknown setting.",
|
||
}
|
||
}
|
||
|
||
pub fn is_enabled(&self) -> bool {
|
||
!matches!(self, Self::Never | Self::Unknown)
|
||
}
|
||
|
||
pub fn is_always_allow(&self) -> bool {
|
||
matches!(self, Self::AlwaysAllow)
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum RunAgentsPermission {
|
||
NeverAllow,
|
||
AlwaysAllow,
|
||
#[default]
|
||
AlwaysAsk,
|
||
|
||
/// Independently assess each action before allowing execution.
|
||
GalaxyDecides,
|
||
// This is intended to catch deserialization errors whenever we add new variants to this enum.
|
||
#[serde(other)]
|
||
Unknown,
|
||
}
|
||
|
||
impl RunAgentsPermission {
|
||
pub fn description(&self) -> &'static str {
|
||
match self {
|
||
RunAgentsPermission::GalaxyDecides => {
|
||
"Galaxy reviews each action with your terminal-use model. Risk 0–3 runs automatically; risk 4–5 asks you, and is rejected after 60 seconds without a response."
|
||
}
|
||
RunAgentsPermission::NeverAllow => {
|
||
"The Agent cannot run child agents and the run_agents tool will not be available."
|
||
}
|
||
RunAgentsPermission::AlwaysAllow => {
|
||
"Give the Agent full autonomy to run child agents without approval."
|
||
}
|
||
RunAgentsPermission::AlwaysAsk => {
|
||
"Require explicit approval before the Agent runs child agents."
|
||
}
|
||
RunAgentsPermission::Unknown => "Unknown setting.",
|
||
}
|
||
}
|
||
|
||
pub fn is_enabled(&self) -> bool {
|
||
matches!(
|
||
self,
|
||
Self::AlwaysAllow | Self::AlwaysAsk | Self::GalaxyDecides
|
||
)
|
||
}
|
||
|
||
pub fn is_always_allow(&self) -> bool {
|
||
matches!(self, Self::AlwaysAllow)
|
||
}
|
||
|
||
pub fn is_never_allow(&self) -> bool {
|
||
matches!(self, Self::NeverAllow | Self::Unknown)
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum AskUserQuestionPermission {
|
||
/// Never pause; skip questions and continue with best judgment.
|
||
Never,
|
||
/// Pause and wait for the user, unless auto-approve mode is enabled.
|
||
AskExceptInAutoApprove,
|
||
/// Always pause and wait for the user to answer before continuing, even in auto-approve mode.
|
||
#[default]
|
||
AlwaysAsk,
|
||
|
||
// This is intended to catch deserialization errors whenever we add new variants to this enum.
|
||
#[serde(other)]
|
||
Unknown,
|
||
}
|
||
|
||
impl AskUserQuestionPermission {
|
||
pub fn label(&self) -> &'static str {
|
||
match self {
|
||
AskUserQuestionPermission::Never => "Never ask",
|
||
AskUserQuestionPermission::AskExceptInAutoApprove => "Ask unless auto-approve",
|
||
AskUserQuestionPermission::AlwaysAsk | AskUserQuestionPermission::Unknown => {
|
||
"Always ask"
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn description(&self) -> &'static str {
|
||
match self {
|
||
AskUserQuestionPermission::AskExceptInAutoApprove
|
||
| AskUserQuestionPermission::Unknown => {
|
||
"The Agent may ask a question and pause for your response, but will continue automatically when auto-approve is on."
|
||
}
|
||
AskUserQuestionPermission::Never => {
|
||
"The Agent will not ask questions and will continue with its best judgment."
|
||
}
|
||
AskUserQuestionPermission::AlwaysAsk => {
|
||
"The Agent may ask a question and will pause for your response even when auto-approve is on."
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Predicate types to match commands that can be executed by Agent Mode.
|
||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||
enum AgentModeCommandExecutionPredicateType {
|
||
/// A regex with start (`^`) and end (`$`) anchors.
|
||
///
|
||
/// We want regex rules to apply to the entire cmd string so we anchor them
|
||
/// (there isn't any efficient way to apply to the entire cmd string at match-time).
|
||
#[serde(with = "serde_regex")]
|
||
AnchoredRegex(Regex),
|
||
}
|
||
|
||
impl AgentModeCommandExecutionPredicateType {
|
||
fn new_regex(regex: &str) -> Result<Self, regex::Error> {
|
||
// Redundant anchors aren't a problem so we can unconditionally add them.
|
||
let anchored_regex = Regex::new(&format!("^{regex}$"))?;
|
||
Ok(Self::AnchoredRegex(anchored_regex))
|
||
}
|
||
|
||
fn matches(&self, cmd: &str) -> bool {
|
||
match self {
|
||
Self::AnchoredRegex(regex) => regex.is_match(cmd),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl PartialEq for AgentModeCommandExecutionPredicateType {
|
||
fn eq(&self, other: &Self) -> bool {
|
||
match (self, other) {
|
||
(Self::AnchoredRegex(a), Self::AnchoredRegex(b)) => {
|
||
// Indexing should be safe since they're guaranteed to have at least
|
||
// the anchors around them.
|
||
let a_unanchored = &a.as_str()[1..a.as_str().len() - 1];
|
||
let b_unanchored = &b.as_str()[1..b.as_str().len() - 1];
|
||
a_unanchored == b_unanchored
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
impl std::fmt::Display for AgentModeCommandExecutionPredicateType {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
match self {
|
||
Self::AnchoredRegex(regex) => {
|
||
write!(f, "{}", ®ex.as_str()[1..regex.as_str().len() - 1])
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// A wrapper around [`AgentModeCommandExecutionPredicateType`] to enforce
|
||
/// the use of the provided constructors rather than direct construction of the variants.
|
||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||
#[serde(transparent)]
|
||
pub struct AgentModeCommandExecutionPredicate(AgentModeCommandExecutionPredicateType);
|
||
|
||
impl schemars::JsonSchema for AgentModeCommandExecutionPredicate {
|
||
fn schema_name() -> std::borrow::Cow<'static, str> {
|
||
std::borrow::Cow::Borrowed("AgentModeCommandExecutionPredicate")
|
||
}
|
||
|
||
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
|
||
generator.subschema_for::<String>()
|
||
}
|
||
}
|
||
|
||
impl AgentModeCommandExecutionPredicate {
|
||
pub fn new_regex(regex: &str) -> Result<Self, regex::Error> {
|
||
Ok(Self(AgentModeCommandExecutionPredicateType::new_regex(
|
||
regex,
|
||
)?))
|
||
}
|
||
|
||
pub fn matches(&self, cmd: &str) -> bool {
|
||
self.0.matches(cmd)
|
||
}
|
||
}
|
||
|
||
impl std::fmt::Display for AgentModeCommandExecutionPredicate {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
write!(f, "{}", self.0)
|
||
}
|
||
}
|
||
|
||
impl settings_value::SettingsValue for AgentModeCommandExecutionPredicate {
|
||
fn to_file_value(&self) -> serde_json::Value {
|
||
serde_json::Value::String(self.to_string())
|
||
}
|
||
|
||
fn from_file_value(value: &serde_json::Value) -> Option<Self> {
|
||
value.as_str().and_then(|s| Self::new_regex(s).ok())
|
||
}
|
||
}
|
||
|
||
lazy_static! {
|
||
static ref OPTIONAL_ARGS_REGEX: Regex =
|
||
Regex::new(r"(\s.*)?").expect("Can parse optional args regex");
|
||
}
|
||
|
||
cfg_if::cfg_if! {
|
||
if #[cfg(test)] {
|
||
lazy_static! {
|
||
// Compiling the regexes for the default command execution allowlist/denylist can be slow
|
||
// in an unoptimized build, so we use empty lists in unit tests.
|
||
pub static ref DEFAULT_COMMAND_EXECUTION_ALLOWLIST: Vec<AgentModeCommandExecutionPredicate> = vec![];
|
||
pub static ref DEFAULT_COMMAND_EXECUTION_DENYLIST: Vec<AgentModeCommandExecutionPredicate> = vec![];
|
||
}
|
||
} else {
|
||
lazy_static! {
|
||
pub static ref DEFAULT_COMMAND_EXECUTION_ALLOWLIST: Vec<AgentModeCommandExecutionPredicate> = vec![
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("cat{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default cat rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("echo{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default echo rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex("find .*").expect("Can parse default find rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("grep{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default grep rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("ls{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default ls rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex("which .*").expect("Can parse default which rule into regex"),
|
||
];
|
||
|
||
pub static ref DEFAULT_COMMAND_EXECUTION_DENYLIST: Vec<AgentModeCommandExecutionPredicate> = vec![
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("bash{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default bash rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("fish{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default fish rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("pwsh{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default pwsh rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("sh{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default sh rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("zsh{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default zsh rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("curl{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default curl rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("eval{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default eval rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("exec{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default exec rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("source{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default source rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("wget{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default wget rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("dig{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default dig rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("nslookup{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default nslookup rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("host{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default host rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("ssh{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default ssh rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("scp{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default scp rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("rsync{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default rsync rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("telnet{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default telnet rule into regex"),
|
||
AgentModeCommandExecutionPredicate::new_regex(&format!("rm{}", OPTIONAL_ARGS_REGEX.as_str())).expect("Can parse default rm rule into regex"),
|
||
];
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Core data structure representing an AI execution profile, which includes model configuration,
|
||
/// behavior settings, and permissions.
|
||
///
|
||
/// NOTE: `planning_model` was removed after planning via subagent was deprecated; serialized legacy
|
||
/// profiles may include a `planning_model` field and this field name should remain reserved
|
||
/// indefinitely.
|
||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||
#[serde(default)]
|
||
pub struct AIExecutionProfile {
|
||
pub name: String,
|
||
pub is_default_profile: bool,
|
||
pub apply_code_diffs: ActionPermission,
|
||
pub read_files: ActionPermission,
|
||
|
||
pub execute_commands: ActionPermission,
|
||
pub write_to_pty: WriteToPtyPermission,
|
||
pub mcp_permissions: ActionPermission,
|
||
pub ask_user_question: AskUserQuestionPermission,
|
||
pub run_agents: RunAgentsPermission,
|
||
|
||
/// Always ask for permission for these commands
|
||
pub command_denylist: Vec<AgentModeCommandExecutionPredicate>,
|
||
|
||
/// When the execute_commands is set to AlwaysAsk, autoexecute these commands
|
||
pub command_allowlist: Vec<AgentModeCommandExecutionPredicate>,
|
||
|
||
/// When the read_files is set to AlwaysAsk, autoread from these directories
|
||
pub directory_allowlist: Vec<PathBuf>,
|
||
|
||
pub mcp_allowlist: Vec<uuid::Uuid>,
|
||
pub mcp_denylist: Vec<uuid::Uuid>,
|
||
|
||
pub computer_use: ComputerUsePermission,
|
||
|
||
pub base_model: Option<LLMId>,
|
||
pub coding_model: Option<LLMId>,
|
||
pub cli_agent_model: Option<LLMId>,
|
||
pub computer_use_model: Option<LLMId>,
|
||
|
||
pub context_window_limit: Option<u32>,
|
||
|
||
/// Whether plans created by the agent should be automatically synced to Warp Drive
|
||
pub autosync_plans_to_warp_drive: bool,
|
||
|
||
/// Whether the agent may use web search when helpful for completing tasks
|
||
pub web_search_enabled: bool,
|
||
}
|
||
|
||
impl Default for AIExecutionProfile {
|
||
fn default() -> Self {
|
||
Self {
|
||
name: Default::default(),
|
||
is_default_profile: false,
|
||
apply_code_diffs: ActionPermission::GalaxyDecides,
|
||
read_files: ActionPermission::GalaxyDecides,
|
||
execute_commands: ActionPermission::AlwaysAsk,
|
||
write_to_pty: WriteToPtyPermission::AlwaysAsk,
|
||
mcp_permissions: ActionPermission::GalaxyDecides,
|
||
ask_user_question: AskUserQuestionPermission::AlwaysAsk,
|
||
run_agents: RunAgentsPermission::AlwaysAsk,
|
||
command_denylist: DEFAULT_COMMAND_EXECUTION_DENYLIST.clone(),
|
||
command_allowlist: Vec::new(),
|
||
directory_allowlist: Vec::new(),
|
||
mcp_allowlist: Vec::new(),
|
||
mcp_denylist: Vec::new(),
|
||
computer_use: ComputerUsePermission::Never,
|
||
base_model: None,
|
||
coding_model: None,
|
||
cli_agent_model: None,
|
||
computer_use_model: None,
|
||
context_window_limit: None,
|
||
autosync_plans_to_warp_drive: true,
|
||
web_search_enabled: true,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl AIExecutionProfile {
|
||
#[cfg(feature = "agent_mode_evals")]
|
||
pub fn create_agent_mode_eval_profile() -> Self {
|
||
Self {
|
||
name: "Agent Mode Eval".to_string(),
|
||
is_default_profile: false,
|
||
apply_code_diffs: ActionPermission::AlwaysAllow,
|
||
read_files: ActionPermission::AlwaysAllow,
|
||
execute_commands: ActionPermission::AlwaysAllow,
|
||
write_to_pty: WriteToPtyPermission::AlwaysAllow,
|
||
mcp_permissions: ActionPermission::AlwaysAllow,
|
||
ask_user_question: AskUserQuestionPermission::Never,
|
||
run_agents: RunAgentsPermission::AlwaysAllow,
|
||
command_denylist: Vec::new(),
|
||
command_allowlist: Vec::new(),
|
||
directory_allowlist: Vec::new(),
|
||
mcp_allowlist: Vec::new(),
|
||
mcp_denylist: Vec::new(),
|
||
computer_use: ComputerUsePermission::Never,
|
||
base_model: None,
|
||
coding_model: None,
|
||
cli_agent_model: None,
|
||
computer_use_model: None,
|
||
context_window_limit: None,
|
||
autosync_plans_to_warp_drive: false,
|
||
web_search_enabled: true,
|
||
}
|
||
}
|
||
|
||
/// This creates a CLI-specific profile that will never ask the user for permission,
|
||
/// since we cannot do so in a non-interactive setting.
|
||
pub fn create_default_cli_profile(
|
||
is_sandboxed: bool,
|
||
computer_use_override: Option<bool>,
|
||
) -> Self {
|
||
let command_denylist = if is_sandboxed {
|
||
Vec::new()
|
||
} else {
|
||
DEFAULT_COMMAND_EXECUTION_DENYLIST.to_vec()
|
||
};
|
||
|
||
let computer_use_permission = match computer_use_override {
|
||
Some(true) => {
|
||
if is_sandboxed || FeatureFlag::LocalComputerUse.is_enabled() {
|
||
ComputerUsePermission::AlwaysAllow
|
||
} else {
|
||
ComputerUsePermission::Never
|
||
}
|
||
}
|
||
Some(false) => ComputerUsePermission::Never,
|
||
None => {
|
||
if is_sandboxed && ChannelState::channel().is_dogfood() {
|
||
ComputerUsePermission::AlwaysAllow
|
||
} else {
|
||
ComputerUsePermission::Never
|
||
}
|
||
}
|
||
};
|
||
|
||
Self {
|
||
name: "Default (CLI)".to_owned(),
|
||
is_default_profile: true,
|
||
apply_code_diffs: ActionPermission::AlwaysAllow,
|
||
read_files: ActionPermission::AlwaysAllow,
|
||
execute_commands: ActionPermission::AlwaysAllow,
|
||
mcp_permissions: ActionPermission::AlwaysAllow,
|
||
write_to_pty: WriteToPtyPermission::AlwaysAllow,
|
||
ask_user_question: AskUserQuestionPermission::Never,
|
||
run_agents: RunAgentsPermission::AlwaysAllow,
|
||
command_denylist,
|
||
command_allowlist: DEFAULT_COMMAND_EXECUTION_ALLOWLIST.to_vec(),
|
||
directory_allowlist: Vec::new(),
|
||
mcp_allowlist: Vec::new(),
|
||
mcp_denylist: Vec::new(),
|
||
computer_use: computer_use_permission,
|
||
base_model: None,
|
||
coding_model: None,
|
||
cli_agent_model: None,
|
||
computer_use_model: None,
|
||
context_window_limit: None,
|
||
autosync_plans_to_warp_drive: FeatureFlag::SyncAmbientPlans.is_enabled(),
|
||
web_search_enabled: true,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl JsonModel for AIExecutionProfile {
|
||
fn json_object_type() -> JsonObjectType {
|
||
JsonObjectType::AIExecutionProfile
|
||
}
|
||
}
|
||
|
||
pub type CloudAIExecutionProfile =
|
||
GenericCloudObject<GenericStringObjectId, CloudAIExecutionProfileModel>;
|
||
pub type CloudAIExecutionProfileModel = GenericStringModel<AIExecutionProfile, JsonSerializer>;
|
||
pub type ServerAIExecutionProfile =
|
||
GenericServerObject<GenericStringObjectId, CloudAIExecutionProfileModel>;
|