use std::path::PathBuf;
use async_trait::async_trait;
use tokio::sync::mpsc;
use crate::channels::{Channel, IncomingMessage, OutgoingMessage};
#[derive(Debug, Clone, Default)]
pub enum AgentMode {
#[default]
Auto,
BypassPermissions,
Coding {
plan_approval: bool,
project_path: Option<PathBuf>,
},
Swarm { parallelism: usize },
}
pub fn agent_mode_from_permission_profile(profile: &str) -> AgentMode {
let p = profile.trim().to_ascii_lowercase().replace(['-', ' '], "_");
match p.as_str() {
"full" => AgentMode::BypassPermissions,
"prompt" => AgentMode::Coding {
plan_approval: true,
project_path: None,
},
_ => AgentMode::Auto,
}
}
impl AgentMode {
pub fn is_coding(&self) -> bool {
matches!(self, Self::Coding { .. })
}
pub fn is_swarm(&self) -> bool {
matches!(self, Self::Swarm { .. })
}
pub fn bypass_permissions(&self) -> bool {
matches!(self, Self::BypassPermissions)
}
pub fn plan_approval(&self) -> bool {
if self.bypass_permissions() {
return false;
}
matches!(
self,
Self::Coding {
plan_approval: true,
..
}
)
}
pub fn swarm_parallelism(&self) -> usize {
match self {
Self::Swarm { parallelism } => *parallelism,
_ => 1,
}
}
pub fn system_prompt_injection(&self) -> Option<&'static str> {
match self {
Self::BypassPermissions => Some(BYPASS_PERMISSIONS_PROMPT),
Self::Coding { .. } => Some(CODING_MODE_PROMPT),
Self::Swarm { .. } => Some(SWARM_MODE_PROMPT),
Self::Auto => None,
}
}
pub fn prefer_heavy_model(&self) -> bool {
matches!(
self,
Self::BypassPermissions | Self::Coding { .. } | Self::Swarm { .. }
)
}
pub fn always_plan(&self) -> bool {
matches!(
self,
Self::BypassPermissions | Self::Coding { .. } | Self::Swarm { .. }
)
}
}
pub struct NullChannel {
name: String,
}
impl NullChannel {
pub fn new(name: impl Into<String>) -> Self {
Self { name: name.into() }
}
}
#[async_trait]
impl Channel for NullChannel {
fn name(&self) -> &str {
&self.name
}
async fn start(&mut self) -> anyhow::Result<mpsc::Receiver<IncomingMessage>> {
let (_tx, rx) = mpsc::channel(1);
Ok(rx) }
async fn send(&self, _message: OutgoingMessage) -> anyhow::Result<Option<String>> {
Ok(None)
}
async fn stop(&mut self) -> anyhow::Result<()> {
Ok(())
}
}
const BYPASS_PERMISSIONS_PROMPT: &str = "\
## Autonomous Mode (Bypass Permissions)
You are running with all permission checks disabled. You have full autonomy to:
- Execute shell commands without confirmation
- Read and write any files in the workspace
- Make and push commits autonomously
- Install dependencies and run builds
Operate decisively. Do not ask for permission before taking actions. \
Complete the task end-to-end and report what was done.";
const CODING_MODE_PROMPT: &str = "\
## Coding Mode Active
You are in coding mode. Follow these rules:
- Read files before editing them
- Make surgical, minimal changes — don't reformat code you don't touch
- After edits, verify correctness (cargo build, npm test, or equivalent)
- Prefer targeted `edit` operations over full file rewrites
- For multi-file changes: enumerate all affected files first, then execute in order
- Diagnose root causes before retrying failures
- Commit at natural checkpoints with clear, descriptive messages
- Never break working code — test incrementally";
const SWARM_MODE_PROMPT: &str = "\
## Swarm Deployment Mode Active
You are coordinating parallel coding agents. Follow these rules:
- Decompose the goal into independent, non-overlapping subtasks
- Assign each agent a clear, bounded scope (a file, module, or feature)
- Avoid shared-state conflicts between agents
- Validate and integrate each agent's output before merging
- Reassign or handle failed tasks yourself
- Report the outcome of each agent with status and a brief summary";
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_agent_mode_from_permission_profile() {
assert!(matches!(
agent_mode_from_permission_profile("full"),
AgentMode::BypassPermissions
));
assert!(matches!(
agent_mode_from_permission_profile(" FULL "),
AgentMode::BypassPermissions
));
assert!(matches!(
agent_mode_from_permission_profile("prompt"),
AgentMode::Coding {
plan_approval: true,
project_path: None
}
));
assert!(matches!(
agent_mode_from_permission_profile(" ProMpT\n"),
AgentMode::Coding {
plan_approval: true,
project_path: None
}
));
assert!(matches!(
agent_mode_from_permission_profile(""),
AgentMode::Auto
));
assert!(matches!(
agent_mode_from_permission_profile("unknown-profile"),
AgentMode::Auto
));
}
}