Skip to main content

Agent

Struct Agent 

Source
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

Source

pub fn new(config: AgentConfig) -> Self

Creates a new Agent with the given configuration.

Examples found in repository?
examples/hello_world.rs (line 34)
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
Hide additional examples
examples/subagents.rs (line 104)
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}
examples/streaming.rs (line 32)
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}
examples/custom_tools.rs (line 160)
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}
examples/policies.rs (line 92)
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}
examples/persistence.rs (line 46)
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}
Source

pub fn register_hook(&self, hook: Arc<dyn DynHook>)

Registers a custom lifecycle hook. Hooks can observe or modify agent transitions.

Source

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.

Source

pub fn register_tool(&self, tool: Arc<dyn DynTool>)

Registers a custom tool available for execution by the agent.

Source

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 localharness binary cannot be resolved.
  • Write tools are enabled but no safety policies are configured.
  • The WebSocket upgrade or subprocess connection fails.
Examples found in repository?
examples/hello_world.rs (line 36)
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
Hide additional examples
examples/subagents.rs (line 106)
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}
examples/streaming.rs (line 34)
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}
examples/custom_tools.rs (line 162)
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}
examples/policies.rs (line 94)
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}
examples/persistence.rs (line 47)
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}
Source

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?
examples/hello_world.rs (line 41)
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
Hide additional examples
examples/subagents.rs (line 113)
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}
examples/custom_tools.rs (line 169)
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}
examples/policies.rs (line 101)
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}
examples/persistence.rs (line 51)
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}
examples/structured_output.rs (line 114)
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}
Source

pub fn conversation(&self) -> Result<Arc<Conversation>, Error>

Returns the active Conversation session.

§Errors

Returns an error if the agent is not yet started.

Examples found in repository?
examples/streaming.rs (line 39)
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}
Source

pub fn conversation_id(&self) -> Option<String>

Returns the active conversation ID if the session has started.

Examples found in repository?
examples/persistence.rs (line 55)
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}
Source

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?
examples/hello_world.rs (line 44)
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
Hide additional examples
examples/subagents.rs (line 116)
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}
examples/streaming.rs (line 67)
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}
examples/custom_tools.rs (line 187)
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}
examples/policies.rs (line 116)
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}
examples/persistence.rs (line 59)
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}

Trait Implementations§

Source§

impl Debug for Agent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Agent

§

impl !RefUnwindSafe for Agent

§

impl Send for Agent

§

impl Sync for Agent

§

impl Unpin for Agent

§

impl UnsafeUnpin for Agent

§

impl !UnwindSafe for Agent

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more