zag_agent/providers/
common.rs1use crate::agent::OnSpawnHook;
2use crate::output::AgentOutput;
3use crate::sandbox::SandboxConfig;
4use anyhow::Context;
5use std::path::Path;
6use std::process::Stdio;
7use tokio::process::{Child, Command};
8
9pub struct CommonAgentState {
14 pub system_prompt: String,
15 pub model: String,
16 pub root: Option<String>,
17 pub skip_permissions: bool,
18 pub output_format: Option<String>,
19 pub add_dirs: Vec<String>,
20 pub capture_output: bool,
21 pub sandbox: Option<SandboxConfig>,
22 pub max_turns: Option<u32>,
23 pub env_vars: Vec<(String, String)>,
24 pub on_spawn_hook: Option<OnSpawnHook>,
30}
31
32impl CommonAgentState {
33 pub fn new(default_model: &str) -> Self {
34 Self {
35 system_prompt: String::new(),
36 model: default_model.to_string(),
37 root: None,
38 skip_permissions: false,
39 output_format: None,
40 add_dirs: Vec::new(),
41 capture_output: false,
42 sandbox: None,
43 max_turns: None,
44 env_vars: Vec::new(),
45 on_spawn_hook: None,
46 }
47 }
48
49 pub fn notify_spawn(&self, child: &Child) {
52 if let (Some(cb), Some(pid)) = (self.on_spawn_hook.as_ref(), child.id()) {
53 cb(pid);
54 }
55 }
56
57 pub fn get_base_path(&self) -> &Path {
59 self.root.as_ref().map(Path::new).unwrap_or(Path::new("."))
60 }
61
62 pub fn make_command(&self, binary_name: &str, agent_args: Vec<String>) -> Command {
68 if let Some(ref sb) = self.sandbox {
69 let std_cmd = crate::sandbox::build_sandbox_command(sb, agent_args);
70 Command::from(std_cmd)
71 } else {
72 let mut cmd = Command::new(binary_name);
73 if let Some(ref root) = self.root {
74 cmd.current_dir(root);
75 }
76 cmd.args(&agent_args);
77 for (key, value) in &self.env_vars {
78 cmd.env(key, value);
79 }
80 cmd
81 }
82 }
83
84 pub async fn run_interactive_command(
88 cmd: &mut Command,
89 agent_display_name: &str,
90 ) -> anyhow::Result<()> {
91 Self::run_interactive_command_with_hook(cmd, agent_display_name, None).await
92 }
93
94 pub async fn run_interactive_command_with_hook(
101 cmd: &mut Command,
102 agent_display_name: &str,
103 on_spawn: Option<&OnSpawnHook>,
104 ) -> anyhow::Result<()> {
105 cmd.stdin(Stdio::inherit())
106 .stdout(Stdio::inherit())
107 .stderr(Stdio::inherit());
108 let mut child = cmd.spawn().with_context(|| {
109 format!(
110 "Failed to execute '{}' CLI. Is it installed and in PATH?",
111 agent_display_name.to_lowercase()
112 )
113 })?;
114 if let (Some(cb), Some(pid)) = (on_spawn, child.id()) {
115 cb(pid);
116 }
117 let status = child.wait().await.with_context(|| {
118 format!(
119 "Failed waiting on '{}' CLI",
120 agent_display_name.to_lowercase()
121 )
122 })?;
123 if !status.success() {
124 return Err(crate::process::ProcessError {
125 exit_code: status.code(),
126 stderr: String::new(),
127 agent_name: agent_display_name.to_string(),
128 }
129 .into());
130 }
131 Ok(())
132 }
133
134 pub async fn run_non_interactive_simple(
142 &self,
143 cmd: &mut Command,
144 agent_display_name: &str,
145 ) -> anyhow::Result<Option<AgentOutput>> {
146 if self.capture_output {
147 let text = crate::process::run_captured(cmd, agent_display_name).await?;
148 log::debug!(
149 "{} raw response ({} bytes): {}",
150 agent_display_name,
151 text.len(),
152 text
153 );
154 Ok(Some(AgentOutput::from_text(
155 &agent_display_name.to_lowercase(),
156 &text,
157 )))
158 } else {
159 cmd.stdin(Stdio::inherit()).stdout(Stdio::inherit());
160 crate::process::run_with_captured_stderr(cmd).await?;
161 Ok(None)
162 }
163 }
164}
165
166macro_rules! impl_common_agent_setters {
171 () => {
172 fn system_prompt(&self) -> &str {
173 &self.common.system_prompt
174 }
175
176 fn set_system_prompt(&mut self, prompt: String) {
177 self.common.system_prompt = prompt;
178 }
179
180 fn get_model(&self) -> &str {
181 &self.common.model
182 }
183
184 fn set_model(&mut self, model: String) {
185 self.common.model = model;
186 }
187
188 fn set_root(&mut self, root: String) {
189 self.common.root = Some(root);
190 }
191
192 fn set_output_format(&mut self, format: Option<String>) {
193 self.common.output_format = format;
194 }
195
196 fn set_add_dirs(&mut self, dirs: Vec<String>) {
197 self.common.add_dirs = dirs;
198 }
199
200 fn set_env_vars(&mut self, vars: Vec<(String, String)>) {
201 self.common.env_vars = vars;
202 }
203
204 fn set_capture_output(&mut self, capture: bool) {
205 self.common.capture_output = capture;
206 }
207
208 fn set_sandbox(&mut self, config: crate::sandbox::SandboxConfig) {
209 self.common.sandbox = Some(config);
210 }
211
212 fn set_max_turns(&mut self, turns: u32) {
213 self.common.max_turns = Some(turns);
214 }
215
216 fn set_on_spawn_hook(&mut self, hook: crate::agent::OnSpawnHook) {
217 self.common.on_spawn_hook = Some(hook);
218 }
219 };
220}
221pub(crate) use impl_common_agent_setters;
222
223macro_rules! impl_as_any {
225 () => {
226 fn as_any_ref(&self) -> &dyn std::any::Any {
227 self
228 }
229
230 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
231 self
232 }
233 };
234}
235pub(crate) use impl_as_any;
236
237#[cfg(test)]
238#[path = "common_tests.rs"]
239mod tests;