pub struct Agent { /* private fields */ }Expand description
High-level orchestrator that manages an agentic execution session.
An Agent encapsulates binary discovery, WebSocket upgrades, tool wiring, safety policy enforcement,
and observer hook dispatch. It provides a simple chat API for sending prompts and retrieving responses.
§Examples
use antigravity_sdk_rust::agent::{Agent, AgentConfig};
use antigravity_sdk_rust::policy;
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let mut config = AgentConfig::default();
config.policies = Some(vec![policy::allow_all()]);
let mut agent = Agent::new(config);
agent.start().await?;
let response = agent.chat("What is 2+2?").await?;
println!("Agent: {}", response.text);
agent.stop().await?;
Ok(())
}Implementations§
Source§impl Agent
impl Agent
Sourcepub fn new(config: AgentConfig) -> Self
pub fn new(config: AgentConfig) -> Self
Creates a new Agent with the given configuration.
Examples found in repository?
7async fn main() -> Result<(), anyhow::Error> {
8 // Initialize tracing subscriber to print info/debug logs by default
9 tracing_subscriber::fmt()
10 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
11 .init();
12
13 // Load environment variables from .env file if present
14 dotenvy::dotenv().ok();
15
16 let mut config = AgentConfig::default();
17
18 // Check if the user specified a binary path or check the environment variable
19 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
20 config.binary_path = Some(harness_path);
21 }
22
23 // Configure Gemini Configuration with API Key and explicit model name
24 let mut gemini_config = GeminiConfig::default();
25 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
26 gemini_config.api_key = Some(api_key);
27 }
28 // Explicitly set the model to gemini-3.5-flash
29 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
30 config.gemini_config = gemini_config;
31
32 config.policies = Some(vec![policy::allow_all()]);
33
34 let mut agent = Agent::new(config);
35 println!("Starting agent...");
36 agent.start().await?;
37
38 let prompt = "Say 'Hello World!'";
39 println!(" User: {}", prompt);
40
41 let response = agent.chat(prompt).await?;
42 println!(" Agent: {}", response.text);
43
44 agent.stop().await?;
45 Ok(())
46}More examples
61async fn main() -> Result<(), anyhow::Error> {
62 // Initialize tracing subscriber
63 tracing_subscriber::fmt()
64 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
65 .init();
66
67 // Load environment variables from .env file if present
68 dotenvy::dotenv().ok();
69
70 let mut config = AgentConfig::default();
71
72 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
73 config.binary_path = Some(harness_path);
74 }
75
76 let mut gemini_config = GeminiConfig::default();
77 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
78 gemini_config.api_key = Some(api_key);
79 }
80 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
81 config.gemini_config = gemini_config;
82
83 // Enable subagents capability and file viewing
84 let capabilities = CapabilitiesConfig {
85 enabled_tools: Some(vec![
86 BuiltinTools::StartSubagent,
87 BuiltinTools::ListDir,
88 BuiltinTools::ViewFile,
89 BuiltinTools::Finish,
90 ]),
91 ..Default::default()
92 };
93 config.capabilities = capabilities;
94
95 // Add Hook for visibility
96 let subagent_active = Arc::new(AtomicBool::new(false));
97 config
98 .hooks
99 .push(Arc::new(SubagentHook { subagent_active }));
100
101 // Allow tools
102 config.policies = Some(vec![policy::allow_all()]);
103
104 let mut agent = Agent::new(config);
105 println!("Starting agent...");
106 agent.start().await?;
107
108 let prompt = "Use a subagent to research the files in the current directory. \
109 Delegate the task of listing the directory to the subagent, and then \
110 tell me what files you found.";
111
112 println!(" User: {}", prompt);
113 let response = agent.chat(prompt).await?;
114 println!("\n Agent:\n{}", response.text);
115
116 agent.stop().await?;
117 Ok(())
118}8async fn main() -> Result<(), anyhow::Error> {
9 // Initialize tracing subscriber
10 tracing_subscriber::fmt()
11 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
12 .init();
13
14 // Load environment variables from .env file if present
15 dotenvy::dotenv().ok();
16
17 let mut config = AgentConfig::default();
18
19 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
20 config.binary_path = Some(harness_path);
21 }
22
23 let mut gemini_config = GeminiConfig::default();
24 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
25 gemini_config.api_key = Some(api_key);
26 }
27 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
28 config.gemini_config = gemini_config;
29
30 config.policies = Some(vec![policy::allow_all()]);
31
32 let mut agent = Agent::new(config);
33 println!("Starting agent...");
34 agent.start().await?;
35
36 let prompt = "Solve this riddle: I speak without a mouth and hear without ears. I have no body, but I come alive with wind. What am I? Explain your reasoning.";
37 println!("\n User: {}\n", prompt);
38
39 let conversation = agent.conversation()?;
40 let mut stream = conversation.chat(prompt).await?;
41
42 println!(" Agent (Streaming response):");
43 println!(" -------------------------------------------------------");
44
45 while let Some(chunk_res) = stream.next().await {
46 match chunk_res? {
47 StreamChunk::Thought { text, .. } => {
48 // Print thought chunks (e.g. in grey or wrapped in brackets if desired, or directly)
49 print!("{}", text);
50 std::io::Write::flush(&mut std::io::stdout())?;
51 }
52 StreamChunk::Text { text, .. } => {
53 // Print response text chunks
54 print!("{}", text);
55 std::io::Write::flush(&mut std::io::stdout())?;
56 }
57 StreamChunk::ToolCall(call) => {
58 println!(
59 "\n[Tool Call Requested: {} with args: {}]",
60 call.name, call.args
61 );
62 }
63 }
64 }
65 println!("\n -------------------------------------------------------\n");
66
67 agent.stop().await?;
68 Ok(())
69}111async fn main() -> Result<(), anyhow::Error> {
112 // Initialize tracing subscriber
113 tracing_subscriber::fmt()
114 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
115 .init();
116
117 // Load environment variables from .env file if present
118 dotenvy::dotenv().ok();
119
120 let mut config = AgentConfig::default();
121
122 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
123 config.binary_path = Some(harness_path);
124 }
125
126 let mut gemini_config = GeminiConfig::default();
127 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
128 gemini_config.api_key = Some(api_key);
129 }
130 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
131 config.gemini_config = gemini_config;
132
133 config.system_instructions = Some(antigravity_sdk_rust::types::SystemInstructions::Custom(
134 antigravity_sdk_rust::types::CustomSystemInstructions {
135 text:
136 "You keep track of fruit inventory. To record fruits, you MUST first look up the \
137 fruit's SKU using lookup_fruit_sku, and then use that SKU with record_fruit."
138 .to_string(),
139 },
140 ));
141
142 // Initialize shared mutable state for stateful tool
143 let inventory = Arc::new(Mutex::new(HashMap::new()));
144
145 // Register our custom tools
146 config.tools = vec![
147 Arc::new(LookupSkuTool),
148 Arc::new(RecordFruitTool {
149 inventory: inventory.clone(),
150 }),
151 ];
152
153 // Restrict agent to ONLY these tools
154 config.policies = Some(vec![
155 policy::deny_all(),
156 policy::allow("lookup_fruit_sku"),
157 policy::allow("record_fruit"),
158 ]);
159
160 let mut agent = Agent::new(config);
161 println!("Starting agent...");
162 agent.start().await?;
163
164 println!(" === Custom Tools Demo ===");
165
166 // Turn 1: Lookup fruit SKU
167 let prompt1 = "What is the SKU for apples? We need to order more.";
168 println!("\n User: {}", prompt1);
169 let response1 = agent.chat(prompt1).await?;
170 println!(" Agent: {}", response1.text);
171
172 // Stateful interaction: record fruits across multiple turns
173 println!("\n === Stateful Tool (Fruit Counter) Demo ===");
174
175 let turns = vec![
176 "I have 5 apples.",
177 "And I just got 3 bananas.",
178 "Oh, and another 2 apples.",
179 ];
180
181 for user_input in turns {
182 println!("\n User: {}", user_input);
183 let response = agent.chat(user_input).await?;
184 println!(" Agent: {}", response.text);
185 }
186
187 agent.stop().await?;
188 Ok(())
189}42async fn main() -> Result<(), anyhow::Error> {
43 // Initialize tracing subscriber
44 tracing_subscriber::fmt()
45 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
46 .init();
47
48 // Load environment variables from .env file if present
49 dotenvy::dotenv().ok();
50
51 let mut config = AgentConfig::default();
52
53 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
54 config.binary_path = Some(harness_path);
55 }
56
57 let mut gemini_config = GeminiConfig::default();
58 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
59 gemini_config.api_key = Some(api_key);
60 }
61 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
62 config.gemini_config = gemini_config;
63
64 // Configure policies using the recommended "Deny by Default" posture.
65 let policies = vec![
66 // 1. Deny everything by default
67 policy::deny_all(),
68 // 2. Allow listing directories
69 policy::allow("LIST_DIR"),
70 // 3. Allow running commands, but block dangerous 'rm' commands
71 Policy::new(
72 "RUN_COMMAND".to_string(),
73 Decision::Deny,
74 Some(Arc::new(block_rm_predicate)),
75 None,
76 "block-rm".to_string(),
77 ),
78 // Fallback: Allow general RUN_COMMAND calls if they don't match the rm block predicate
79 policy::allow("RUN_COMMAND"),
80 // 4. Allow editing/creating files, but ask the user first if it's a critical file
81 Policy::new(
82 "WRITE_TO_FILE".to_string(),
83 Decision::AskUser,
84 Some(Arc::new(critical_file_predicate)),
85 Some(Arc::new(programmatic_approval_handler)),
86 "ask-for-critical-writes".to_string(),
87 ),
88 policy::allow("WRITE_TO_FILE"),
89 ];
90 config.policies = Some(policies);
91
92 let mut agent = Agent::new(config);
93 println!("Starting agent...");
94 agent.start().await?;
95
96 println!("\n Chatting with agent...");
97
98 // 1. Try a safe command (should be allowed)
99 let prompt1 = "List the files in the current directory.";
100 println!("\n User: {}", prompt1);
101 let response1 = agent.chat(prompt1).await?;
102 println!(" Agent: {}", response1.text);
103
104 // 2. Try a dangerous command (should be denied by policy)
105 let prompt2 = "Delete all files using rm -rf.";
106 println!("\n User: {}", prompt2);
107 let response2 = agent.chat(prompt2).await?;
108 println!(" Agent: {}", response2.text);
109
110 // 3. Try creating a critical file (triggers programmatic ask_user handler)
111 let prompt3 = "Create a new configuration file named production.key with content 'debug=true'.";
112 println!("\n User: {}", prompt3);
113 let response3 = agent.chat(prompt3).await?;
114 println!(" Agent: {}", response3.text);
115
116 agent.stop().await?;
117 Ok(())
118}8async fn main() -> Result<(), anyhow::Error> {
9 // Initialize tracing subscriber
10 tracing_subscriber::fmt()
11 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
12 .init();
13
14 // Load environment variables from .env file if present
15 dotenvy::dotenv().ok();
16
17 // Create a temporary path for save_dir
18 let mut save_dir = std::env::temp_dir();
19 let epoch = SystemTime::now()
20 .duration_since(SystemTime::UNIX_EPOCH)
21 .unwrap_or_default()
22 .as_secs();
23 save_dir.push(format!("agent_session_{}", epoch));
24 let save_dir_str = save_dir.to_string_lossy().to_string();
25 println!(" Save directory: {}", save_dir_str);
26
27 // Ensure the save_dir exists
28 std::fs::create_dir_all(&save_dir)?;
29
30 println!("\n === Session 1: establishing context ===");
31
32 let mut config1 = AgentConfig::default();
33 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
34 config1.binary_path = Some(harness_path);
35 }
36 let mut gemini_config1 = GeminiConfig::default();
37 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
38 gemini_config1.api_key = Some(api_key);
39 }
40 gemini_config1.models.default.name = "gemini-3.5-flash".to_string();
41 config1.gemini_config = gemini_config1;
42 config1.policies = Some(vec![policy::allow_all()]);
43 config1.save_dir = Some(save_dir_str.clone());
44
45 let conversation_id = {
46 let mut agent1 = Agent::new(config1);
47 agent1.start().await?;
48
49 let prompt1 = "Remember this: my favorite color is blue.";
50 println!(" User: {}", prompt1);
51 let response1 = agent1.chat(prompt1).await?;
52 println!(" Agent: {}", response1.text);
53
54 let conv_id = agent1
55 .conversation_id()
56 .ok_or_else(|| anyhow::anyhow!("Failed to retrieve conversation ID"))?;
57 println!(" Assigned conversation ID: {}", conv_id);
58
59 agent1.stop().await?;
60 println!(" Session 1 ended.\n");
61 conv_id
62 };
63
64 println!(" === Session 2: resuming and verifying recall ===");
65
66 let mut config2 = AgentConfig::default();
67 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
68 config2.binary_path = Some(harness_path);
69 }
70 let mut gemini_config2 = GeminiConfig::default();
71 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
72 gemini_config2.api_key = Some(api_key);
73 }
74 gemini_config2.models.default.name = "gemini-3.5-flash".to_string();
75 config2.gemini_config = gemini_config2;
76 config2.policies = Some(vec![policy::allow_all()]);
77 config2.save_dir = Some(save_dir_str.clone());
78 config2.conversation_id = Some(conversation_id);
79
80 let mut agent2 = Agent::new(config2);
81 agent2.start().await?;
82
83 let prompt2 = "What is my favorite color?";
84 println!(" User: {}", prompt2);
85 let response2 = agent2.chat(prompt2).await?;
86 println!(" Agent: {}", response2.text);
87
88 agent2.stop().await?;
89 println!(" Session 2 ended.");
90
91 // Clean up temporary session directory
92 let _ = std::fs::remove_dir_all(&save_dir);
93
94 Ok(())
95}Sourcepub fn register_hook(&self, hook: Arc<dyn DynHook>)
pub fn register_hook(&self, hook: Arc<dyn DynHook>)
Registers a custom lifecycle hook. Hooks can observe or modify agent transitions.
Sourcepub fn register_trigger(
&mut self,
trigger: Arc<dyn DynTrigger>,
) -> Result<(), Error>
pub fn register_trigger( &mut self, trigger: Arc<dyn DynTrigger>, ) -> Result<(), Error>
Registers a custom background trigger. Triggers must be registered before the agent starts.
§Errors
Returns an error if the agent session has already been started.
Sourcepub fn register_tool(&self, tool: Arc<dyn DynTool>)
pub fn register_tool(&self, tool: Arc<dyn DynTool>)
Registers a custom tool available for execution by the agent.
Sourcepub async fn start(&mut self) -> Result<(), Error>
pub async fn start(&mut self) -> Result<(), Error>
Spawns the subprocess communication harness, initializes safety policies, registers tools/hooks, establishes the WebSocket session, and starts any configured triggers.
§Errors
Returns an error if:
- The
localharnessbinary cannot be resolved. - Write tools are enabled but no safety policies are configured.
- The WebSocket upgrade or subprocess connection fails.
Examples found in repository?
7async fn main() -> Result<(), anyhow::Error> {
8 // Initialize tracing subscriber to print info/debug logs by default
9 tracing_subscriber::fmt()
10 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
11 .init();
12
13 // Load environment variables from .env file if present
14 dotenvy::dotenv().ok();
15
16 let mut config = AgentConfig::default();
17
18 // Check if the user specified a binary path or check the environment variable
19 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
20 config.binary_path = Some(harness_path);
21 }
22
23 // Configure Gemini Configuration with API Key and explicit model name
24 let mut gemini_config = GeminiConfig::default();
25 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
26 gemini_config.api_key = Some(api_key);
27 }
28 // Explicitly set the model to gemini-3.5-flash
29 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
30 config.gemini_config = gemini_config;
31
32 config.policies = Some(vec![policy::allow_all()]);
33
34 let mut agent = Agent::new(config);
35 println!("Starting agent...");
36 agent.start().await?;
37
38 let prompt = "Say 'Hello World!'";
39 println!(" User: {}", prompt);
40
41 let response = agent.chat(prompt).await?;
42 println!(" Agent: {}", response.text);
43
44 agent.stop().await?;
45 Ok(())
46}More examples
61async fn main() -> Result<(), anyhow::Error> {
62 // Initialize tracing subscriber
63 tracing_subscriber::fmt()
64 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
65 .init();
66
67 // Load environment variables from .env file if present
68 dotenvy::dotenv().ok();
69
70 let mut config = AgentConfig::default();
71
72 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
73 config.binary_path = Some(harness_path);
74 }
75
76 let mut gemini_config = GeminiConfig::default();
77 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
78 gemini_config.api_key = Some(api_key);
79 }
80 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
81 config.gemini_config = gemini_config;
82
83 // Enable subagents capability and file viewing
84 let capabilities = CapabilitiesConfig {
85 enabled_tools: Some(vec![
86 BuiltinTools::StartSubagent,
87 BuiltinTools::ListDir,
88 BuiltinTools::ViewFile,
89 BuiltinTools::Finish,
90 ]),
91 ..Default::default()
92 };
93 config.capabilities = capabilities;
94
95 // Add Hook for visibility
96 let subagent_active = Arc::new(AtomicBool::new(false));
97 config
98 .hooks
99 .push(Arc::new(SubagentHook { subagent_active }));
100
101 // Allow tools
102 config.policies = Some(vec![policy::allow_all()]);
103
104 let mut agent = Agent::new(config);
105 println!("Starting agent...");
106 agent.start().await?;
107
108 let prompt = "Use a subagent to research the files in the current directory. \
109 Delegate the task of listing the directory to the subagent, and then \
110 tell me what files you found.";
111
112 println!(" User: {}", prompt);
113 let response = agent.chat(prompt).await?;
114 println!("\n Agent:\n{}", response.text);
115
116 agent.stop().await?;
117 Ok(())
118}8async fn main() -> Result<(), anyhow::Error> {
9 // Initialize tracing subscriber
10 tracing_subscriber::fmt()
11 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
12 .init();
13
14 // Load environment variables from .env file if present
15 dotenvy::dotenv().ok();
16
17 let mut config = AgentConfig::default();
18
19 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
20 config.binary_path = Some(harness_path);
21 }
22
23 let mut gemini_config = GeminiConfig::default();
24 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
25 gemini_config.api_key = Some(api_key);
26 }
27 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
28 config.gemini_config = gemini_config;
29
30 config.policies = Some(vec![policy::allow_all()]);
31
32 let mut agent = Agent::new(config);
33 println!("Starting agent...");
34 agent.start().await?;
35
36 let prompt = "Solve this riddle: I speak without a mouth and hear without ears. I have no body, but I come alive with wind. What am I? Explain your reasoning.";
37 println!("\n User: {}\n", prompt);
38
39 let conversation = agent.conversation()?;
40 let mut stream = conversation.chat(prompt).await?;
41
42 println!(" Agent (Streaming response):");
43 println!(" -------------------------------------------------------");
44
45 while let Some(chunk_res) = stream.next().await {
46 match chunk_res? {
47 StreamChunk::Thought { text, .. } => {
48 // Print thought chunks (e.g. in grey or wrapped in brackets if desired, or directly)
49 print!("{}", text);
50 std::io::Write::flush(&mut std::io::stdout())?;
51 }
52 StreamChunk::Text { text, .. } => {
53 // Print response text chunks
54 print!("{}", text);
55 std::io::Write::flush(&mut std::io::stdout())?;
56 }
57 StreamChunk::ToolCall(call) => {
58 println!(
59 "\n[Tool Call Requested: {} with args: {}]",
60 call.name, call.args
61 );
62 }
63 }
64 }
65 println!("\n -------------------------------------------------------\n");
66
67 agent.stop().await?;
68 Ok(())
69}111async fn main() -> Result<(), anyhow::Error> {
112 // Initialize tracing subscriber
113 tracing_subscriber::fmt()
114 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
115 .init();
116
117 // Load environment variables from .env file if present
118 dotenvy::dotenv().ok();
119
120 let mut config = AgentConfig::default();
121
122 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
123 config.binary_path = Some(harness_path);
124 }
125
126 let mut gemini_config = GeminiConfig::default();
127 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
128 gemini_config.api_key = Some(api_key);
129 }
130 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
131 config.gemini_config = gemini_config;
132
133 config.system_instructions = Some(antigravity_sdk_rust::types::SystemInstructions::Custom(
134 antigravity_sdk_rust::types::CustomSystemInstructions {
135 text:
136 "You keep track of fruit inventory. To record fruits, you MUST first look up the \
137 fruit's SKU using lookup_fruit_sku, and then use that SKU with record_fruit."
138 .to_string(),
139 },
140 ));
141
142 // Initialize shared mutable state for stateful tool
143 let inventory = Arc::new(Mutex::new(HashMap::new()));
144
145 // Register our custom tools
146 config.tools = vec![
147 Arc::new(LookupSkuTool),
148 Arc::new(RecordFruitTool {
149 inventory: inventory.clone(),
150 }),
151 ];
152
153 // Restrict agent to ONLY these tools
154 config.policies = Some(vec![
155 policy::deny_all(),
156 policy::allow("lookup_fruit_sku"),
157 policy::allow("record_fruit"),
158 ]);
159
160 let mut agent = Agent::new(config);
161 println!("Starting agent...");
162 agent.start().await?;
163
164 println!(" === Custom Tools Demo ===");
165
166 // Turn 1: Lookup fruit SKU
167 let prompt1 = "What is the SKU for apples? We need to order more.";
168 println!("\n User: {}", prompt1);
169 let response1 = agent.chat(prompt1).await?;
170 println!(" Agent: {}", response1.text);
171
172 // Stateful interaction: record fruits across multiple turns
173 println!("\n === Stateful Tool (Fruit Counter) Demo ===");
174
175 let turns = vec![
176 "I have 5 apples.",
177 "And I just got 3 bananas.",
178 "Oh, and another 2 apples.",
179 ];
180
181 for user_input in turns {
182 println!("\n User: {}", user_input);
183 let response = agent.chat(user_input).await?;
184 println!(" Agent: {}", response.text);
185 }
186
187 agent.stop().await?;
188 Ok(())
189}42async fn main() -> Result<(), anyhow::Error> {
43 // Initialize tracing subscriber
44 tracing_subscriber::fmt()
45 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
46 .init();
47
48 // Load environment variables from .env file if present
49 dotenvy::dotenv().ok();
50
51 let mut config = AgentConfig::default();
52
53 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
54 config.binary_path = Some(harness_path);
55 }
56
57 let mut gemini_config = GeminiConfig::default();
58 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
59 gemini_config.api_key = Some(api_key);
60 }
61 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
62 config.gemini_config = gemini_config;
63
64 // Configure policies using the recommended "Deny by Default" posture.
65 let policies = vec![
66 // 1. Deny everything by default
67 policy::deny_all(),
68 // 2. Allow listing directories
69 policy::allow("LIST_DIR"),
70 // 3. Allow running commands, but block dangerous 'rm' commands
71 Policy::new(
72 "RUN_COMMAND".to_string(),
73 Decision::Deny,
74 Some(Arc::new(block_rm_predicate)),
75 None,
76 "block-rm".to_string(),
77 ),
78 // Fallback: Allow general RUN_COMMAND calls if they don't match the rm block predicate
79 policy::allow("RUN_COMMAND"),
80 // 4. Allow editing/creating files, but ask the user first if it's a critical file
81 Policy::new(
82 "WRITE_TO_FILE".to_string(),
83 Decision::AskUser,
84 Some(Arc::new(critical_file_predicate)),
85 Some(Arc::new(programmatic_approval_handler)),
86 "ask-for-critical-writes".to_string(),
87 ),
88 policy::allow("WRITE_TO_FILE"),
89 ];
90 config.policies = Some(policies);
91
92 let mut agent = Agent::new(config);
93 println!("Starting agent...");
94 agent.start().await?;
95
96 println!("\n Chatting with agent...");
97
98 // 1. Try a safe command (should be allowed)
99 let prompt1 = "List the files in the current directory.";
100 println!("\n User: {}", prompt1);
101 let response1 = agent.chat(prompt1).await?;
102 println!(" Agent: {}", response1.text);
103
104 // 2. Try a dangerous command (should be denied by policy)
105 let prompt2 = "Delete all files using rm -rf.";
106 println!("\n User: {}", prompt2);
107 let response2 = agent.chat(prompt2).await?;
108 println!(" Agent: {}", response2.text);
109
110 // 3. Try creating a critical file (triggers programmatic ask_user handler)
111 let prompt3 = "Create a new configuration file named production.key with content 'debug=true'.";
112 println!("\n User: {}", prompt3);
113 let response3 = agent.chat(prompt3).await?;
114 println!(" Agent: {}", response3.text);
115
116 agent.stop().await?;
117 Ok(())
118}8async fn main() -> Result<(), anyhow::Error> {
9 // Initialize tracing subscriber
10 tracing_subscriber::fmt()
11 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
12 .init();
13
14 // Load environment variables from .env file if present
15 dotenvy::dotenv().ok();
16
17 // Create a temporary path for save_dir
18 let mut save_dir = std::env::temp_dir();
19 let epoch = SystemTime::now()
20 .duration_since(SystemTime::UNIX_EPOCH)
21 .unwrap_or_default()
22 .as_secs();
23 save_dir.push(format!("agent_session_{}", epoch));
24 let save_dir_str = save_dir.to_string_lossy().to_string();
25 println!(" Save directory: {}", save_dir_str);
26
27 // Ensure the save_dir exists
28 std::fs::create_dir_all(&save_dir)?;
29
30 println!("\n === Session 1: establishing context ===");
31
32 let mut config1 = AgentConfig::default();
33 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
34 config1.binary_path = Some(harness_path);
35 }
36 let mut gemini_config1 = GeminiConfig::default();
37 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
38 gemini_config1.api_key = Some(api_key);
39 }
40 gemini_config1.models.default.name = "gemini-3.5-flash".to_string();
41 config1.gemini_config = gemini_config1;
42 config1.policies = Some(vec![policy::allow_all()]);
43 config1.save_dir = Some(save_dir_str.clone());
44
45 let conversation_id = {
46 let mut agent1 = Agent::new(config1);
47 agent1.start().await?;
48
49 let prompt1 = "Remember this: my favorite color is blue.";
50 println!(" User: {}", prompt1);
51 let response1 = agent1.chat(prompt1).await?;
52 println!(" Agent: {}", response1.text);
53
54 let conv_id = agent1
55 .conversation_id()
56 .ok_or_else(|| anyhow::anyhow!("Failed to retrieve conversation ID"))?;
57 println!(" Assigned conversation ID: {}", conv_id);
58
59 agent1.stop().await?;
60 println!(" Session 1 ended.\n");
61 conv_id
62 };
63
64 println!(" === Session 2: resuming and verifying recall ===");
65
66 let mut config2 = AgentConfig::default();
67 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
68 config2.binary_path = Some(harness_path);
69 }
70 let mut gemini_config2 = GeminiConfig::default();
71 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
72 gemini_config2.api_key = Some(api_key);
73 }
74 gemini_config2.models.default.name = "gemini-3.5-flash".to_string();
75 config2.gemini_config = gemini_config2;
76 config2.policies = Some(vec![policy::allow_all()]);
77 config2.save_dir = Some(save_dir_str.clone());
78 config2.conversation_id = Some(conversation_id);
79
80 let mut agent2 = Agent::new(config2);
81 agent2.start().await?;
82
83 let prompt2 = "What is my favorite color?";
84 println!(" User: {}", prompt2);
85 let response2 = agent2.chat(prompt2).await?;
86 println!(" Agent: {}", response2.text);
87
88 agent2.stop().await?;
89 println!(" Session 2 ended.");
90
91 // Clean up temporary session directory
92 let _ = std::fs::remove_dir_all(&save_dir);
93
94 Ok(())
95}Sourcepub async fn chat(&self, prompt: &str) -> Result<ChatResponse, Error>
pub async fn chat(&self, prompt: &str) -> Result<ChatResponse, Error>
Sends a prompt message to the active agent session and awaits the final completed response.
§Errors
Returns an error if the agent is not yet started or if the execution stream encounters a failure.
Examples found in repository?
7async fn main() -> Result<(), anyhow::Error> {
8 // Initialize tracing subscriber to print info/debug logs by default
9 tracing_subscriber::fmt()
10 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
11 .init();
12
13 // Load environment variables from .env file if present
14 dotenvy::dotenv().ok();
15
16 let mut config = AgentConfig::default();
17
18 // Check if the user specified a binary path or check the environment variable
19 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
20 config.binary_path = Some(harness_path);
21 }
22
23 // Configure Gemini Configuration with API Key and explicit model name
24 let mut gemini_config = GeminiConfig::default();
25 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
26 gemini_config.api_key = Some(api_key);
27 }
28 // Explicitly set the model to gemini-3.5-flash
29 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
30 config.gemini_config = gemini_config;
31
32 config.policies = Some(vec![policy::allow_all()]);
33
34 let mut agent = Agent::new(config);
35 println!("Starting agent...");
36 agent.start().await?;
37
38 let prompt = "Say 'Hello World!'";
39 println!(" User: {}", prompt);
40
41 let response = agent.chat(prompt).await?;
42 println!(" Agent: {}", response.text);
43
44 agent.stop().await?;
45 Ok(())
46}More examples
61async fn main() -> Result<(), anyhow::Error> {
62 // Initialize tracing subscriber
63 tracing_subscriber::fmt()
64 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
65 .init();
66
67 // Load environment variables from .env file if present
68 dotenvy::dotenv().ok();
69
70 let mut config = AgentConfig::default();
71
72 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
73 config.binary_path = Some(harness_path);
74 }
75
76 let mut gemini_config = GeminiConfig::default();
77 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
78 gemini_config.api_key = Some(api_key);
79 }
80 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
81 config.gemini_config = gemini_config;
82
83 // Enable subagents capability and file viewing
84 let capabilities = CapabilitiesConfig {
85 enabled_tools: Some(vec![
86 BuiltinTools::StartSubagent,
87 BuiltinTools::ListDir,
88 BuiltinTools::ViewFile,
89 BuiltinTools::Finish,
90 ]),
91 ..Default::default()
92 };
93 config.capabilities = capabilities;
94
95 // Add Hook for visibility
96 let subagent_active = Arc::new(AtomicBool::new(false));
97 config
98 .hooks
99 .push(Arc::new(SubagentHook { subagent_active }));
100
101 // Allow tools
102 config.policies = Some(vec![policy::allow_all()]);
103
104 let mut agent = Agent::new(config);
105 println!("Starting agent...");
106 agent.start().await?;
107
108 let prompt = "Use a subagent to research the files in the current directory. \
109 Delegate the task of listing the directory to the subagent, and then \
110 tell me what files you found.";
111
112 println!(" User: {}", prompt);
113 let response = agent.chat(prompt).await?;
114 println!("\n Agent:\n{}", response.text);
115
116 agent.stop().await?;
117 Ok(())
118}111async fn main() -> Result<(), anyhow::Error> {
112 // Initialize tracing subscriber
113 tracing_subscriber::fmt()
114 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
115 .init();
116
117 // Load environment variables from .env file if present
118 dotenvy::dotenv().ok();
119
120 let mut config = AgentConfig::default();
121
122 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
123 config.binary_path = Some(harness_path);
124 }
125
126 let mut gemini_config = GeminiConfig::default();
127 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
128 gemini_config.api_key = Some(api_key);
129 }
130 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
131 config.gemini_config = gemini_config;
132
133 config.system_instructions = Some(antigravity_sdk_rust::types::SystemInstructions::Custom(
134 antigravity_sdk_rust::types::CustomSystemInstructions {
135 text:
136 "You keep track of fruit inventory. To record fruits, you MUST first look up the \
137 fruit's SKU using lookup_fruit_sku, and then use that SKU with record_fruit."
138 .to_string(),
139 },
140 ));
141
142 // Initialize shared mutable state for stateful tool
143 let inventory = Arc::new(Mutex::new(HashMap::new()));
144
145 // Register our custom tools
146 config.tools = vec![
147 Arc::new(LookupSkuTool),
148 Arc::new(RecordFruitTool {
149 inventory: inventory.clone(),
150 }),
151 ];
152
153 // Restrict agent to ONLY these tools
154 config.policies = Some(vec![
155 policy::deny_all(),
156 policy::allow("lookup_fruit_sku"),
157 policy::allow("record_fruit"),
158 ]);
159
160 let mut agent = Agent::new(config);
161 println!("Starting agent...");
162 agent.start().await?;
163
164 println!(" === Custom Tools Demo ===");
165
166 // Turn 1: Lookup fruit SKU
167 let prompt1 = "What is the SKU for apples? We need to order more.";
168 println!("\n User: {}", prompt1);
169 let response1 = agent.chat(prompt1).await?;
170 println!(" Agent: {}", response1.text);
171
172 // Stateful interaction: record fruits across multiple turns
173 println!("\n === Stateful Tool (Fruit Counter) Demo ===");
174
175 let turns = vec![
176 "I have 5 apples.",
177 "And I just got 3 bananas.",
178 "Oh, and another 2 apples.",
179 ];
180
181 for user_input in turns {
182 println!("\n User: {}", user_input);
183 let response = agent.chat(user_input).await?;
184 println!(" Agent: {}", response.text);
185 }
186
187 agent.stop().await?;
188 Ok(())
189}42async fn main() -> Result<(), anyhow::Error> {
43 // Initialize tracing subscriber
44 tracing_subscriber::fmt()
45 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
46 .init();
47
48 // Load environment variables from .env file if present
49 dotenvy::dotenv().ok();
50
51 let mut config = AgentConfig::default();
52
53 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
54 config.binary_path = Some(harness_path);
55 }
56
57 let mut gemini_config = GeminiConfig::default();
58 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
59 gemini_config.api_key = Some(api_key);
60 }
61 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
62 config.gemini_config = gemini_config;
63
64 // Configure policies using the recommended "Deny by Default" posture.
65 let policies = vec![
66 // 1. Deny everything by default
67 policy::deny_all(),
68 // 2. Allow listing directories
69 policy::allow("LIST_DIR"),
70 // 3. Allow running commands, but block dangerous 'rm' commands
71 Policy::new(
72 "RUN_COMMAND".to_string(),
73 Decision::Deny,
74 Some(Arc::new(block_rm_predicate)),
75 None,
76 "block-rm".to_string(),
77 ),
78 // Fallback: Allow general RUN_COMMAND calls if they don't match the rm block predicate
79 policy::allow("RUN_COMMAND"),
80 // 4. Allow editing/creating files, but ask the user first if it's a critical file
81 Policy::new(
82 "WRITE_TO_FILE".to_string(),
83 Decision::AskUser,
84 Some(Arc::new(critical_file_predicate)),
85 Some(Arc::new(programmatic_approval_handler)),
86 "ask-for-critical-writes".to_string(),
87 ),
88 policy::allow("WRITE_TO_FILE"),
89 ];
90 config.policies = Some(policies);
91
92 let mut agent = Agent::new(config);
93 println!("Starting agent...");
94 agent.start().await?;
95
96 println!("\n Chatting with agent...");
97
98 // 1. Try a safe command (should be allowed)
99 let prompt1 = "List the files in the current directory.";
100 println!("\n User: {}", prompt1);
101 let response1 = agent.chat(prompt1).await?;
102 println!(" Agent: {}", response1.text);
103
104 // 2. Try a dangerous command (should be denied by policy)
105 let prompt2 = "Delete all files using rm -rf.";
106 println!("\n User: {}", prompt2);
107 let response2 = agent.chat(prompt2).await?;
108 println!(" Agent: {}", response2.text);
109
110 // 3. Try creating a critical file (triggers programmatic ask_user handler)
111 let prompt3 = "Create a new configuration file named production.key with content 'debug=true'.";
112 println!("\n User: {}", prompt3);
113 let response3 = agent.chat(prompt3).await?;
114 println!(" Agent: {}", response3.text);
115
116 agent.stop().await?;
117 Ok(())
118}8async fn main() -> Result<(), anyhow::Error> {
9 // Initialize tracing subscriber
10 tracing_subscriber::fmt()
11 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
12 .init();
13
14 // Load environment variables from .env file if present
15 dotenvy::dotenv().ok();
16
17 // Create a temporary path for save_dir
18 let mut save_dir = std::env::temp_dir();
19 let epoch = SystemTime::now()
20 .duration_since(SystemTime::UNIX_EPOCH)
21 .unwrap_or_default()
22 .as_secs();
23 save_dir.push(format!("agent_session_{}", epoch));
24 let save_dir_str = save_dir.to_string_lossy().to_string();
25 println!(" Save directory: {}", save_dir_str);
26
27 // Ensure the save_dir exists
28 std::fs::create_dir_all(&save_dir)?;
29
30 println!("\n === Session 1: establishing context ===");
31
32 let mut config1 = AgentConfig::default();
33 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
34 config1.binary_path = Some(harness_path);
35 }
36 let mut gemini_config1 = GeminiConfig::default();
37 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
38 gemini_config1.api_key = Some(api_key);
39 }
40 gemini_config1.models.default.name = "gemini-3.5-flash".to_string();
41 config1.gemini_config = gemini_config1;
42 config1.policies = Some(vec![policy::allow_all()]);
43 config1.save_dir = Some(save_dir_str.clone());
44
45 let conversation_id = {
46 let mut agent1 = Agent::new(config1);
47 agent1.start().await?;
48
49 let prompt1 = "Remember this: my favorite color is blue.";
50 println!(" User: {}", prompt1);
51 let response1 = agent1.chat(prompt1).await?;
52 println!(" Agent: {}", response1.text);
53
54 let conv_id = agent1
55 .conversation_id()
56 .ok_or_else(|| anyhow::anyhow!("Failed to retrieve conversation ID"))?;
57 println!(" Assigned conversation ID: {}", conv_id);
58
59 agent1.stop().await?;
60 println!(" Session 1 ended.\n");
61 conv_id
62 };
63
64 println!(" === Session 2: resuming and verifying recall ===");
65
66 let mut config2 = AgentConfig::default();
67 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
68 config2.binary_path = Some(harness_path);
69 }
70 let mut gemini_config2 = GeminiConfig::default();
71 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
72 gemini_config2.api_key = Some(api_key);
73 }
74 gemini_config2.models.default.name = "gemini-3.5-flash".to_string();
75 config2.gemini_config = gemini_config2;
76 config2.policies = Some(vec![policy::allow_all()]);
77 config2.save_dir = Some(save_dir_str.clone());
78 config2.conversation_id = Some(conversation_id);
79
80 let mut agent2 = Agent::new(config2);
81 agent2.start().await?;
82
83 let prompt2 = "What is my favorite color?";
84 println!(" User: {}", prompt2);
85 let response2 = agent2.chat(prompt2).await?;
86 println!(" Agent: {}", response2.text);
87
88 agent2.stop().await?;
89 println!(" Session 2 ended.");
90
91 // Clean up temporary session directory
92 let _ = std::fs::remove_dir_all(&save_dir);
93
94 Ok(())
95}52async fn main() -> Result<(), anyhow::Error> {
53 // Initialize tracing subscriber
54 tracing_subscriber::fmt()
55 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
56 .init();
57
58 // Load environment variables from .env file if present
59 dotenvy::dotenv().ok();
60
61 let mut config = AgentConfig::default();
62
63 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
64 config.binary_path = Some(harness_path);
65 }
66
67 let mut gemini_config = GeminiConfig::default();
68 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
69 gemini_config.api_key = Some(api_key);
70 }
71 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
72 config.gemini_config = gemini_config;
73
74 // Define response schema for meeting summaries and action items
75 let response_schema = r#"{
76 "type": "object",
77 "properties": {
78 "action_items": {
79 "type": "array",
80 "items": {
81 "type": "object",
82 "properties": {
83 "assignee": { "type": "string" },
84 "task": { "type": "string" },
85 "deadline": { "type": "string" }
86 },
87 "required": ["assignee", "task", "deadline"]
88 }
89 }
90 },
91 "required": ["action_items"]
92 }"#;
93 config.response_schema = Some(response_schema.to_string());
94
95 // Register our custom tool
96 config.tools = vec![Arc::new(FetchNotesTool)];
97
98 // Allow our tool and standard tools
99 config.policies = Some(vec![
100 policy::deny_all(),
101 policy::allow("fetch_unstructured_meeting_notes"),
102 ]);
103
104 let mut agent = Agent::new(config);
105 println!("Starting agent...");
106 agent.start().await?;
107
108 let prompt = "Use the fetch_unstructured_meeting_notes tool to retrieve notes for \
109 'meeting-2026-05' and return the meeting summary with the appropriate \
110 action item list. Ensure each action item includes 'assignee', \
111 'task', and 'deadline'.";
112
113 println!("\n Sending prompt to agent...\n {}", prompt);
114 let response = agent.chat(prompt).await?;
115
116 println!("\n Extracting structured meeting action items...");
117
118 // Find the step that contains structured_output
119 let mut found = false;
120 for step in &response.steps {
121 if let Some(ref structured) = step.structured_output {
122 println!("\n === Structured Meeting Action Items ===");
123 if let Some(items) = structured.get("action_items").and_then(Value::as_array) {
124 for item in items {
125 println!(
126 " - Assignee: {:?}",
127 item.get("assignee").and_then(Value::as_str).unwrap_or("")
128 );
129 println!(
130 " Task: {:?}",
131 item.get("task").and_then(Value::as_str).unwrap_or("")
132 );
133 println!(
134 " Deadline: {:?}",
135 item.get("deadline").and_then(Value::as_str).unwrap_or("")
136 );
137 println!();
138 }
139 } else {
140 println!("No action_items field or not an array: {:?}", structured);
141 }
142 found = true;
143 break;
144 }
145 }
146
147 if !found {
148 println!("\n Failed to extract structured summary natively.");
149 println!(" Final Text Response: {}", response.text);
150 }
151
152 agent.stop().await?;
153 Ok(())
154}Sourcepub fn conversation(&self) -> Result<Arc<Conversation>, Error>
pub fn conversation(&self) -> Result<Arc<Conversation>, Error>
Examples found in repository?
8async fn main() -> Result<(), anyhow::Error> {
9 // Initialize tracing subscriber
10 tracing_subscriber::fmt()
11 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
12 .init();
13
14 // Load environment variables from .env file if present
15 dotenvy::dotenv().ok();
16
17 let mut config = AgentConfig::default();
18
19 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
20 config.binary_path = Some(harness_path);
21 }
22
23 let mut gemini_config = GeminiConfig::default();
24 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
25 gemini_config.api_key = Some(api_key);
26 }
27 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
28 config.gemini_config = gemini_config;
29
30 config.policies = Some(vec![policy::allow_all()]);
31
32 let mut agent = Agent::new(config);
33 println!("Starting agent...");
34 agent.start().await?;
35
36 let prompt = "Solve this riddle: I speak without a mouth and hear without ears. I have no body, but I come alive with wind. What am I? Explain your reasoning.";
37 println!("\n User: {}\n", prompt);
38
39 let conversation = agent.conversation()?;
40 let mut stream = conversation.chat(prompt).await?;
41
42 println!(" Agent (Streaming response):");
43 println!(" -------------------------------------------------------");
44
45 while let Some(chunk_res) = stream.next().await {
46 match chunk_res? {
47 StreamChunk::Thought { text, .. } => {
48 // Print thought chunks (e.g. in grey or wrapped in brackets if desired, or directly)
49 print!("{}", text);
50 std::io::Write::flush(&mut std::io::stdout())?;
51 }
52 StreamChunk::Text { text, .. } => {
53 // Print response text chunks
54 print!("{}", text);
55 std::io::Write::flush(&mut std::io::stdout())?;
56 }
57 StreamChunk::ToolCall(call) => {
58 println!(
59 "\n[Tool Call Requested: {} with args: {}]",
60 call.name, call.args
61 );
62 }
63 }
64 }
65 println!("\n -------------------------------------------------------\n");
66
67 agent.stop().await?;
68 Ok(())
69}Sourcepub fn conversation_id(&self) -> Option<String>
pub fn conversation_id(&self) -> Option<String>
Returns the active conversation ID if the session has started.
Examples found in repository?
8async fn main() -> Result<(), anyhow::Error> {
9 // Initialize tracing subscriber
10 tracing_subscriber::fmt()
11 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
12 .init();
13
14 // Load environment variables from .env file if present
15 dotenvy::dotenv().ok();
16
17 // Create a temporary path for save_dir
18 let mut save_dir = std::env::temp_dir();
19 let epoch = SystemTime::now()
20 .duration_since(SystemTime::UNIX_EPOCH)
21 .unwrap_or_default()
22 .as_secs();
23 save_dir.push(format!("agent_session_{}", epoch));
24 let save_dir_str = save_dir.to_string_lossy().to_string();
25 println!(" Save directory: {}", save_dir_str);
26
27 // Ensure the save_dir exists
28 std::fs::create_dir_all(&save_dir)?;
29
30 println!("\n === Session 1: establishing context ===");
31
32 let mut config1 = AgentConfig::default();
33 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
34 config1.binary_path = Some(harness_path);
35 }
36 let mut gemini_config1 = GeminiConfig::default();
37 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
38 gemini_config1.api_key = Some(api_key);
39 }
40 gemini_config1.models.default.name = "gemini-3.5-flash".to_string();
41 config1.gemini_config = gemini_config1;
42 config1.policies = Some(vec![policy::allow_all()]);
43 config1.save_dir = Some(save_dir_str.clone());
44
45 let conversation_id = {
46 let mut agent1 = Agent::new(config1);
47 agent1.start().await?;
48
49 let prompt1 = "Remember this: my favorite color is blue.";
50 println!(" User: {}", prompt1);
51 let response1 = agent1.chat(prompt1).await?;
52 println!(" Agent: {}", response1.text);
53
54 let conv_id = agent1
55 .conversation_id()
56 .ok_or_else(|| anyhow::anyhow!("Failed to retrieve conversation ID"))?;
57 println!(" Assigned conversation ID: {}", conv_id);
58
59 agent1.stop().await?;
60 println!(" Session 1 ended.\n");
61 conv_id
62 };
63
64 println!(" === Session 2: resuming and verifying recall ===");
65
66 let mut config2 = AgentConfig::default();
67 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
68 config2.binary_path = Some(harness_path);
69 }
70 let mut gemini_config2 = GeminiConfig::default();
71 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
72 gemini_config2.api_key = Some(api_key);
73 }
74 gemini_config2.models.default.name = "gemini-3.5-flash".to_string();
75 config2.gemini_config = gemini_config2;
76 config2.policies = Some(vec![policy::allow_all()]);
77 config2.save_dir = Some(save_dir_str.clone());
78 config2.conversation_id = Some(conversation_id);
79
80 let mut agent2 = Agent::new(config2);
81 agent2.start().await?;
82
83 let prompt2 = "What is my favorite color?";
84 println!(" User: {}", prompt2);
85 let response2 = agent2.chat(prompt2).await?;
86 println!(" Agent: {}", response2.text);
87
88 agent2.stop().await?;
89 println!(" Session 2 ended.");
90
91 // Clean up temporary session directory
92 let _ = std::fs::remove_dir_all(&save_dir);
93
94 Ok(())
95}Sourcepub async fn stop(&mut self) -> Result<(), Error>
pub async fn stop(&mut self) -> Result<(), Error>
Gracefully stops the agent connection and disconnects the underlying harness.
§Errors
Returns an error if closing the connection fails.
Examples found in repository?
7async fn main() -> Result<(), anyhow::Error> {
8 // Initialize tracing subscriber to print info/debug logs by default
9 tracing_subscriber::fmt()
10 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
11 .init();
12
13 // Load environment variables from .env file if present
14 dotenvy::dotenv().ok();
15
16 let mut config = AgentConfig::default();
17
18 // Check if the user specified a binary path or check the environment variable
19 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
20 config.binary_path = Some(harness_path);
21 }
22
23 // Configure Gemini Configuration with API Key and explicit model name
24 let mut gemini_config = GeminiConfig::default();
25 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
26 gemini_config.api_key = Some(api_key);
27 }
28 // Explicitly set the model to gemini-3.5-flash
29 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
30 config.gemini_config = gemini_config;
31
32 config.policies = Some(vec![policy::allow_all()]);
33
34 let mut agent = Agent::new(config);
35 println!("Starting agent...");
36 agent.start().await?;
37
38 let prompt = "Say 'Hello World!'";
39 println!(" User: {}", prompt);
40
41 let response = agent.chat(prompt).await?;
42 println!(" Agent: {}", response.text);
43
44 agent.stop().await?;
45 Ok(())
46}More examples
61async fn main() -> Result<(), anyhow::Error> {
62 // Initialize tracing subscriber
63 tracing_subscriber::fmt()
64 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
65 .init();
66
67 // Load environment variables from .env file if present
68 dotenvy::dotenv().ok();
69
70 let mut config = AgentConfig::default();
71
72 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
73 config.binary_path = Some(harness_path);
74 }
75
76 let mut gemini_config = GeminiConfig::default();
77 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
78 gemini_config.api_key = Some(api_key);
79 }
80 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
81 config.gemini_config = gemini_config;
82
83 // Enable subagents capability and file viewing
84 let capabilities = CapabilitiesConfig {
85 enabled_tools: Some(vec![
86 BuiltinTools::StartSubagent,
87 BuiltinTools::ListDir,
88 BuiltinTools::ViewFile,
89 BuiltinTools::Finish,
90 ]),
91 ..Default::default()
92 };
93 config.capabilities = capabilities;
94
95 // Add Hook for visibility
96 let subagent_active = Arc::new(AtomicBool::new(false));
97 config
98 .hooks
99 .push(Arc::new(SubagentHook { subagent_active }));
100
101 // Allow tools
102 config.policies = Some(vec![policy::allow_all()]);
103
104 let mut agent = Agent::new(config);
105 println!("Starting agent...");
106 agent.start().await?;
107
108 let prompt = "Use a subagent to research the files in the current directory. \
109 Delegate the task of listing the directory to the subagent, and then \
110 tell me what files you found.";
111
112 println!(" User: {}", prompt);
113 let response = agent.chat(prompt).await?;
114 println!("\n Agent:\n{}", response.text);
115
116 agent.stop().await?;
117 Ok(())
118}8async fn main() -> Result<(), anyhow::Error> {
9 // Initialize tracing subscriber
10 tracing_subscriber::fmt()
11 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
12 .init();
13
14 // Load environment variables from .env file if present
15 dotenvy::dotenv().ok();
16
17 let mut config = AgentConfig::default();
18
19 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
20 config.binary_path = Some(harness_path);
21 }
22
23 let mut gemini_config = GeminiConfig::default();
24 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
25 gemini_config.api_key = Some(api_key);
26 }
27 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
28 config.gemini_config = gemini_config;
29
30 config.policies = Some(vec![policy::allow_all()]);
31
32 let mut agent = Agent::new(config);
33 println!("Starting agent...");
34 agent.start().await?;
35
36 let prompt = "Solve this riddle: I speak without a mouth and hear without ears. I have no body, but I come alive with wind. What am I? Explain your reasoning.";
37 println!("\n User: {}\n", prompt);
38
39 let conversation = agent.conversation()?;
40 let mut stream = conversation.chat(prompt).await?;
41
42 println!(" Agent (Streaming response):");
43 println!(" -------------------------------------------------------");
44
45 while let Some(chunk_res) = stream.next().await {
46 match chunk_res? {
47 StreamChunk::Thought { text, .. } => {
48 // Print thought chunks (e.g. in grey or wrapped in brackets if desired, or directly)
49 print!("{}", text);
50 std::io::Write::flush(&mut std::io::stdout())?;
51 }
52 StreamChunk::Text { text, .. } => {
53 // Print response text chunks
54 print!("{}", text);
55 std::io::Write::flush(&mut std::io::stdout())?;
56 }
57 StreamChunk::ToolCall(call) => {
58 println!(
59 "\n[Tool Call Requested: {} with args: {}]",
60 call.name, call.args
61 );
62 }
63 }
64 }
65 println!("\n -------------------------------------------------------\n");
66
67 agent.stop().await?;
68 Ok(())
69}111async fn main() -> Result<(), anyhow::Error> {
112 // Initialize tracing subscriber
113 tracing_subscriber::fmt()
114 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
115 .init();
116
117 // Load environment variables from .env file if present
118 dotenvy::dotenv().ok();
119
120 let mut config = AgentConfig::default();
121
122 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
123 config.binary_path = Some(harness_path);
124 }
125
126 let mut gemini_config = GeminiConfig::default();
127 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
128 gemini_config.api_key = Some(api_key);
129 }
130 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
131 config.gemini_config = gemini_config;
132
133 config.system_instructions = Some(antigravity_sdk_rust::types::SystemInstructions::Custom(
134 antigravity_sdk_rust::types::CustomSystemInstructions {
135 text:
136 "You keep track of fruit inventory. To record fruits, you MUST first look up the \
137 fruit's SKU using lookup_fruit_sku, and then use that SKU with record_fruit."
138 .to_string(),
139 },
140 ));
141
142 // Initialize shared mutable state for stateful tool
143 let inventory = Arc::new(Mutex::new(HashMap::new()));
144
145 // Register our custom tools
146 config.tools = vec![
147 Arc::new(LookupSkuTool),
148 Arc::new(RecordFruitTool {
149 inventory: inventory.clone(),
150 }),
151 ];
152
153 // Restrict agent to ONLY these tools
154 config.policies = Some(vec![
155 policy::deny_all(),
156 policy::allow("lookup_fruit_sku"),
157 policy::allow("record_fruit"),
158 ]);
159
160 let mut agent = Agent::new(config);
161 println!("Starting agent...");
162 agent.start().await?;
163
164 println!(" === Custom Tools Demo ===");
165
166 // Turn 1: Lookup fruit SKU
167 let prompt1 = "What is the SKU for apples? We need to order more.";
168 println!("\n User: {}", prompt1);
169 let response1 = agent.chat(prompt1).await?;
170 println!(" Agent: {}", response1.text);
171
172 // Stateful interaction: record fruits across multiple turns
173 println!("\n === Stateful Tool (Fruit Counter) Demo ===");
174
175 let turns = vec![
176 "I have 5 apples.",
177 "And I just got 3 bananas.",
178 "Oh, and another 2 apples.",
179 ];
180
181 for user_input in turns {
182 println!("\n User: {}", user_input);
183 let response = agent.chat(user_input).await?;
184 println!(" Agent: {}", response.text);
185 }
186
187 agent.stop().await?;
188 Ok(())
189}42async fn main() -> Result<(), anyhow::Error> {
43 // Initialize tracing subscriber
44 tracing_subscriber::fmt()
45 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
46 .init();
47
48 // Load environment variables from .env file if present
49 dotenvy::dotenv().ok();
50
51 let mut config = AgentConfig::default();
52
53 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
54 config.binary_path = Some(harness_path);
55 }
56
57 let mut gemini_config = GeminiConfig::default();
58 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
59 gemini_config.api_key = Some(api_key);
60 }
61 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
62 config.gemini_config = gemini_config;
63
64 // Configure policies using the recommended "Deny by Default" posture.
65 let policies = vec![
66 // 1. Deny everything by default
67 policy::deny_all(),
68 // 2. Allow listing directories
69 policy::allow("LIST_DIR"),
70 // 3. Allow running commands, but block dangerous 'rm' commands
71 Policy::new(
72 "RUN_COMMAND".to_string(),
73 Decision::Deny,
74 Some(Arc::new(block_rm_predicate)),
75 None,
76 "block-rm".to_string(),
77 ),
78 // Fallback: Allow general RUN_COMMAND calls if they don't match the rm block predicate
79 policy::allow("RUN_COMMAND"),
80 // 4. Allow editing/creating files, but ask the user first if it's a critical file
81 Policy::new(
82 "WRITE_TO_FILE".to_string(),
83 Decision::AskUser,
84 Some(Arc::new(critical_file_predicate)),
85 Some(Arc::new(programmatic_approval_handler)),
86 "ask-for-critical-writes".to_string(),
87 ),
88 policy::allow("WRITE_TO_FILE"),
89 ];
90 config.policies = Some(policies);
91
92 let mut agent = Agent::new(config);
93 println!("Starting agent...");
94 agent.start().await?;
95
96 println!("\n Chatting with agent...");
97
98 // 1. Try a safe command (should be allowed)
99 let prompt1 = "List the files in the current directory.";
100 println!("\n User: {}", prompt1);
101 let response1 = agent.chat(prompt1).await?;
102 println!(" Agent: {}", response1.text);
103
104 // 2. Try a dangerous command (should be denied by policy)
105 let prompt2 = "Delete all files using rm -rf.";
106 println!("\n User: {}", prompt2);
107 let response2 = agent.chat(prompt2).await?;
108 println!(" Agent: {}", response2.text);
109
110 // 3. Try creating a critical file (triggers programmatic ask_user handler)
111 let prompt3 = "Create a new configuration file named production.key with content 'debug=true'.";
112 println!("\n User: {}", prompt3);
113 let response3 = agent.chat(prompt3).await?;
114 println!(" Agent: {}", response3.text);
115
116 agent.stop().await?;
117 Ok(())
118}8async fn main() -> Result<(), anyhow::Error> {
9 // Initialize tracing subscriber
10 tracing_subscriber::fmt()
11 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
12 .init();
13
14 // Load environment variables from .env file if present
15 dotenvy::dotenv().ok();
16
17 // Create a temporary path for save_dir
18 let mut save_dir = std::env::temp_dir();
19 let epoch = SystemTime::now()
20 .duration_since(SystemTime::UNIX_EPOCH)
21 .unwrap_or_default()
22 .as_secs();
23 save_dir.push(format!("agent_session_{}", epoch));
24 let save_dir_str = save_dir.to_string_lossy().to_string();
25 println!(" Save directory: {}", save_dir_str);
26
27 // Ensure the save_dir exists
28 std::fs::create_dir_all(&save_dir)?;
29
30 println!("\n === Session 1: establishing context ===");
31
32 let mut config1 = AgentConfig::default();
33 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
34 config1.binary_path = Some(harness_path);
35 }
36 let mut gemini_config1 = GeminiConfig::default();
37 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
38 gemini_config1.api_key = Some(api_key);
39 }
40 gemini_config1.models.default.name = "gemini-3.5-flash".to_string();
41 config1.gemini_config = gemini_config1;
42 config1.policies = Some(vec![policy::allow_all()]);
43 config1.save_dir = Some(save_dir_str.clone());
44
45 let conversation_id = {
46 let mut agent1 = Agent::new(config1);
47 agent1.start().await?;
48
49 let prompt1 = "Remember this: my favorite color is blue.";
50 println!(" User: {}", prompt1);
51 let response1 = agent1.chat(prompt1).await?;
52 println!(" Agent: {}", response1.text);
53
54 let conv_id = agent1
55 .conversation_id()
56 .ok_or_else(|| anyhow::anyhow!("Failed to retrieve conversation ID"))?;
57 println!(" Assigned conversation ID: {}", conv_id);
58
59 agent1.stop().await?;
60 println!(" Session 1 ended.\n");
61 conv_id
62 };
63
64 println!(" === Session 2: resuming and verifying recall ===");
65
66 let mut config2 = AgentConfig::default();
67 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
68 config2.binary_path = Some(harness_path);
69 }
70 let mut gemini_config2 = GeminiConfig::default();
71 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
72 gemini_config2.api_key = Some(api_key);
73 }
74 gemini_config2.models.default.name = "gemini-3.5-flash".to_string();
75 config2.gemini_config = gemini_config2;
76 config2.policies = Some(vec![policy::allow_all()]);
77 config2.save_dir = Some(save_dir_str.clone());
78 config2.conversation_id = Some(conversation_id);
79
80 let mut agent2 = Agent::new(config2);
81 agent2.start().await?;
82
83 let prompt2 = "What is my favorite color?";
84 println!(" User: {}", prompt2);
85 let response2 = agent2.chat(prompt2).await?;
86 println!(" Agent: {}", response2.text);
87
88 agent2.stop().await?;
89 println!(" Session 2 ended.");
90
91 // Clean up temporary session directory
92 let _ = std::fs::remove_dir_all(&save_dir);
93
94 Ok(())
95}