Skip to content

Commit 0f64bec

Browse files
update docs and skills
1 parent ba18c40 commit 0f64bec

17 files changed

Lines changed: 264 additions & 175 deletions

ARCHITECTURE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ The SDK leverages several object-oriented and functional design patterns:
4141
- `post_tool_call()`
4242
- `on_tool_error()`
4343
- `on_interaction()`
44-
- **HookRunner**: Coordinates a thread-safe list of observers (`Arc<dyn Hook>`) and dispatches events asynchronously.
44+
- **HookRunner**: Coordinates a thread-safe list of observers (`Arc<dyn DynHook>`) and dispatches events asynchronously.
4545

4646
### 3. Middleware / Interceptor Pattern (`policy.rs`)
4747
- **Policy**: Acts as a middleware layer to authorize, deny, or intercept tool calls before they are executed.
@@ -139,7 +139,7 @@ Standard native runtimes run on multi-threaded thread pools. In contrast, WASM r
139139
The SDK has been fully refactored to leverage native async traits (stable since Rust 1.75 / Rust 2024), completely removing the dependency on the `#[async_trait]` macro.
140140

141141
- **Native Async Traits**: Traits like `Connection`, `Hook`, `Tool`, and `Trigger` are implemented as native async traits using standard `async fn` syntax or returning `impl Future + Send` to ensure compiler-enforced thread safety boundaries.
142-
- **Companion Trait Pattern for Dynamic Dispatch**: Native async traits are not directly object-safe (`dyn Trait` compatible) because they return anonymous concrete futures. To support dynamic dispatch, the SDK defines companion traits `DynConnection`, `DynHook`, `DynTool`, and `DynTrigger` which are object-safe and return boxed futures (`BoxFuture`).
142+
- **Companion Trait Pattern for Dynamic Dispatch**: Native async traits are not directly object-safe (`dyn Trait` compatible) because they return anonymous concrete futures. To support dynamic dispatch, the SDK defines companion traits `DynHook`, `DynTool`, and `DynTrigger` which are object-safe and return boxed futures (`BoxFuture`).
143143
- **Zero-overhead Blanket Implementations**: The companion traits are automatically implemented via blanket implementations for any type implementing the base trait:
144144
```rust
145145
pub trait DynHook: Send + Sync {
@@ -154,4 +154,4 @@ The SDK has been fully refactored to leverage native async traits (stable since
154154
// ...
155155
}
156156
```
157-
This provides the best of both worlds: clean, idiomatic implementation of async traits for developers using standard Rust 2024 features, while preserving the internal ability to handle collections of dynamic trait objects (e.g. `Arc<dyn DynHook>` in `HookRunner`, `Box<dyn DynTool>` in `ToolRunner`, `AnyConnection` enum dispatch, etc.).
157+
This provides the best of both worlds: clean, idiomatic implementation of async traits for developers using standard Rust 2024 features, while preserving the internal ability to handle collections of dynamic trait objects (e.g. `Arc<dyn DynHook>` in `HookRunner`, `Arc<dyn DynTool>` in `ToolRunner`, `AnyConnection` enum dispatch, etc.).

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,9 @@ impl Tool for WeatherTool {
139139
}"#
140140
}
141141
142-
fn call(&self, args: Value) -> impl std::future::Future<Output = Result<Value, anyhow::Error>> + Send {
143-
async move {
144-
let city = args.get("city").and_then(|c| c.as_str()).unwrap_or("Tokyo");
145-
Ok(serde_json::json!({ "weather": format!("It's sunny in {}", city) }))
146-
}
142+
async fn call(&self, args: Value) -> Result<Value, anyhow::Error> {
143+
let city = args.get("city").and_then(|c| c.as_str()).unwrap_or("Tokyo");
144+
Ok(serde_json::json!({ "weather": format!("It's sunny in {}", city) }))
147145
}
148146
}
149147
```

skills/google-antigravity-sdk-rust/SKILL.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,24 @@ Ensure the following prerequisites are met before building or running agents:
2323

2424
---
2525

26+
## WebAssembly (wasm32-wasip1) Target Guidelines
27+
28+
When designing and deploying agents to WebAssembly targets such as `wasm32-wasip1` (e.g., inside Spin or standard WASI runtimes):
29+
30+
1. **Compilation Check**: Always verify clean target compilation:
31+
```sh
32+
cargo check --target wasm32-wasip1
33+
```
34+
2. **Connection Protocol**: Be aware that process spawning is not supported inside WebAssembly sandboxes. Instead of native subprocess IPC, use the direct network socket strategy (`WasmConnectionStrategy` / `WasmConnection`) to connect to a host-side `localharness` WebSocket server.
35+
3. **Core Async Traits**: Do NOT use the `#[async_trait]` attribute macro. The SDK uses native async traits (stable since Rust 1.75 / Rust 2024). Standard traits like `Connection`, `Hook`, `Tool`, and `Trigger` are implemented as native async traits (using `impl Future + Send` returns for trait-level `Send` bounds). For dynamic dispatch and runtime storage (e.g. `Arc<dyn DynHook>`), the SDK provides companion object-safe traits (`DynHook`, `DynTool`, `DynTrigger`) which are automatically implemented via blanket implementations for any type implementing the base trait.
36+
4. **Mock Test Suite Guidelines**: To test connection, event loops, and tool extractions under simulated WebSocket messaging, run:
37+
```sh
38+
cargo test wasm::tests
39+
```
40+
* **Writing Mock Tests**: Mock tests should spin up a local WebSocket listener, bind to port `0` to allocate a free TCP port dynamically, record that port, configure `WasmConnectionStrategy` to point to it, and mock standard JSON frames (`StepUpdate`, `TrajectoryStateUpdate`, etc.) to verify client state transitions.
41+
42+
---
43+
2644
## Routing Table
2745

2846
Use the links below to navigate specific topics and code configurations:
@@ -72,3 +90,5 @@ Use the links below to navigate specific topics and code configurations:
7290
See [agent_skills.md](file:///Volumes/goldcoders/antigravity-sdk-rust/skills/google-antigravity-sdk-rust/examples/getting_started/agent_skills.md).
7391
* **Policies**: Lock workspace directories and filter command lines.
7492
See [policies.md](file:///Volumes/goldcoders/antigravity-sdk-rust/skills/google-antigravity-sdk-rust/examples/getting_started/policies.md).
93+
* **Streaming**: Stream chat responses and distinguish reasoning thoughts from response text in real time.
94+
See [streaming.md](file:///Volumes/goldcoders/antigravity-sdk-rust/skills/google-antigravity-sdk-rust/examples/getting_started/streaming.md).

skills/google-antigravity-sdk-rust/examples/getting_started/custom_tool.md

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,12 @@ use antigravity_sdk_rust::agent::{Agent, AgentConfig};
1111
use antigravity_sdk_rust::policy;
1212
use antigravity_sdk_rust::tools::Tool;
1313
use antigravity_sdk_rust::types::GeminiConfig;
14-
use async_trait::async_trait;
1514
use serde_json::Value;
1615
use std::sync::Arc;
1716

1817
// 1. Define the custom tool struct
1918
pub struct WeatherTool;
2019

21-
#[async_trait]
2220
impl Tool for WeatherTool {
2321
fn name(&self) -> &str {
2422
"get_current_temperature"
@@ -41,15 +39,17 @@ impl Tool for WeatherTool {
4139
}"#
4240
}
4341

44-
async fn call(&self, args: Value) -> Result<Value, anyhow::Error> {
45-
let location = args
46-
.get("location")
47-
.and_then(Value::as_str)
48-
.ok_or_else(|| anyhow::anyhow!("Missing 'location' parameter"))?;
49-
50-
// In a real application, call an external weather API here
51-
let temperature = format!("The temperature in {} is 72°F.", location);
52-
Ok(Value::String(temperature))
42+
fn call(&self, args: Value) -> impl std::future::Future<Output = Result<Value, anyhow::Error>> + Send {
43+
async move {
44+
let location = args
45+
.get("location")
46+
.and_then(Value::as_str)
47+
.ok_or_else(|| anyhow::anyhow!("Missing 'location' parameter"))?;
48+
49+
// In a real application, call an external weather API here
50+
let temperature = format!("The temperature in {} is 72°F.", location);
51+
Ok(Value::String(temperature))
52+
}
5353
}
5454
}
5555

@@ -86,7 +86,6 @@ Here is how to implement a stateful fruit count tracker tool:
8686
use antigravity_sdk_rust::agent::{Agent, AgentConfig};
8787
use antigravity_sdk_rust::policy;
8888
use antigravity_sdk_rust::tools::Tool;
89-
use async_trait::async_trait;
9089
use serde_json::Value;
9190
use std::collections::HashMap;
9291
use std::sync::{Arc, Mutex};
@@ -104,7 +103,6 @@ impl FruitInventoryTool {
104103
}
105104
}
106105

107-
#[async_trait]
108106
impl Tool for FruitInventoryTool {
109107
fn name(&self) -> &str {
110108
"record_fruit"
@@ -131,28 +129,30 @@ impl Tool for FruitInventoryTool {
131129
}"#
132130
}
133131

134-
async fn call(&self, args: Value) -> Result<Value, anyhow::Error> {
135-
let fruit_name = args
136-
.get("fruit_name")
137-
.and_then(Value::as_str)
138-
.ok_or_else(|| anyhow::anyhow!("Missing 'fruit_name'"))?;
139-
let count = args
140-
.get("count")
141-
.and_then(Value::as_i64)
142-
.ok_or_else(|| anyhow::anyhow!("Missing 'count'"))? as i32;
143-
144-
let mut inv = self
145-
.inventory
146-
.lock()
147-
.map_err(|e| anyhow::anyhow!("Mutex lock poisoned: {}", e))?;
148-
let entry = inv.entry(fruit_name.to_string()).or_insert(0);
149-
*entry += count;
150-
151-
let result_msg = format!(
152-
"Recorded {} {}(s). Total {} count is now {}.",
153-
count, fruit_name, fruit_name, *entry
154-
);
155-
Ok(Value::String(result_msg))
132+
fn call(&self, args: Value) -> impl std::future::Future<Output = Result<Value, anyhow::Error>> + Send {
133+
async move {
134+
let fruit_name = args
135+
.get("fruit_name")
136+
.and_then(Value::as_str)
137+
.ok_or_else(|| anyhow::anyhow!("Missing 'fruit_name'"))?;
138+
let count = args
139+
.get("count")
140+
.and_then(Value::as_i64)
141+
.ok_or_else(|| anyhow::anyhow!("Missing 'count'"))? as i32;
142+
143+
let mut inv = self
144+
.inventory
145+
.lock()
146+
.map_err(|e| anyhow::anyhow!("Mutex lock poisoned: {}", e))?;
147+
let entry = inv.entry(fruit_name.to_string()).or_insert(0);
148+
*entry += count;
149+
150+
let result_msg = format!(
151+
"Recorded {} {}(s). Total {} count is now {}.",
152+
count, fruit_name, fruit_name, *entry
153+
);
154+
Ok(Value::String(result_msg))
155+
}
156156
}
157157
}
158158

skills/google-antigravity-sdk-rust/examples/getting_started/hooks.md

Lines changed: 62 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,59 +9,63 @@ To create custom hooks, implement the `Hook` trait:
99
```rust
1010
use antigravity_sdk_rust::hooks::Hook;
1111
use antigravity_sdk_rust::types::{AskQuestionEntry, HookResult, QuestionHookResult, ToolCall, ToolResult};
12-
use async_trait::async_trait;
1312

14-
#[async_trait]
1513
pub trait Hook: Send + Sync {
1614
/// Triggered when the agent establishes a connection and starts a session.
17-
async fn on_session_start(&self) -> Result<(), anyhow::Error> {
18-
Ok(())
15+
fn on_session_start(&self) -> impl std::future::Future<Output = Result<(), anyhow::Error>> + Send {
16+
async { Ok(()) }
1917
}
2018

2119
/// Intercepts the start of a user turn before the LLM processes the prompt.
2220
/// Returns `allow: false` to halt execution.
23-
async fn pre_turn(&self) -> Result<HookResult, anyhow::Error> {
24-
Ok(HookResult {
25-
allow: true,
26-
message: String::new(),
27-
})
21+
fn pre_turn(&self) -> impl std::future::Future<Output = Result<HookResult, anyhow::Error>> + Send {
22+
async {
23+
Ok(HookResult {
24+
allow: true,
25+
message: String::new(),
26+
})
27+
}
2828
}
2929

3030
/// Intercepts a tool call immediately before it is executed by the runner.
3131
/// Returns `allow: false` to prevent execution.
32-
async fn pre_tool_call(&self, _tool_call: &ToolCall) -> Result<HookResult, anyhow::Error> {
33-
Ok(HookResult {
34-
allow: true,
35-
message: String::new(),
36-
})
32+
fn pre_tool_call<'a>(&'a self, _tool_call: &'a ToolCall) -> impl std::future::Future<Output = Result<HookResult, anyhow::Error>> + Send {
33+
async {
34+
Ok(HookResult {
35+
allow: true,
36+
message: String::new(),
37+
})
38+
}
3739
}
3840

3941
/// Triggered after a tool successfully returns a result.
40-
async fn post_tool_call(&self, _result: &ToolResult) -> Result<(), anyhow::Error> {
41-
Ok(())
42+
fn post_tool_call<'a>(&'a self, _result: &'a ToolResult) -> impl std::future::Future<Output = Result<(), anyhow::Error>> + Send {
43+
async { Ok(()) }
4244
}
4345

4446
/// Triggered when a tool execution encounters an error.
4547
/// Allows fallback logic or customized error payloads.
46-
async fn on_tool_error(
47-
&self,
48-
error: &anyhow::Error,
49-
) -> Result<(HookResult, Option<serde_json::Value>), anyhow::Error> {
50-
Ok((
51-
HookResult {
52-
allow: false,
53-
message: error.to_string(),
54-
},
55-
None,
56-
))
48+
fn on_tool_error<'a>(
49+
&'a self,
50+
error: &'a anyhow::Error,
51+
) -> impl std::future::Future<Output = Result<(HookResult, Option<serde_json::Value>), anyhow::Error>> + Send {
52+
async move {
53+
Ok((
54+
HookResult {
55+
allow: false,
56+
message: error.to_string(),
57+
},
58+
None,
59+
))
60+
}
5761
}
5862

5963
/// Intercepts a prompt to ask the user clarifying questions.
60-
async fn on_interaction(
61-
&self,
62-
_questions: &[AskQuestionEntry],
63-
) -> Result<Option<QuestionHookResult>, anyhow::Error> {
64-
Ok(None)
64+
fn on_interaction<'a>(
65+
&'a self,
66+
_questions: &'a [AskQuestionEntry],
67+
) -> impl std::future::Future<Output = Result<Option<QuestionHookResult>, anyhow::Error>> + Send {
68+
async { Ok(None) }
6569
}
6670
}
6771
```
@@ -77,38 +81,44 @@ use antigravity_sdk_rust::agent::{Agent, AgentConfig};
7781
use antigravity_sdk_rust::hooks::Hook;
7882
use antigravity_sdk_rust::policy;
7983
use antigravity_sdk_rust::types::{HookResult, ToolCall, ToolResult};
80-
use async_trait::async_trait;
8184
use std::sync::Arc;
8285

8386
// 1. Define custom hook struct
8487
struct LoggerHook;
8588

86-
#[async_trait]
8789
impl Hook for LoggerHook {
88-
async fn on_session_start(&self) -> Result<(), anyhow::Error> {
89-
println!("[Hook] Session has successfully started!");
90-
Ok(())
90+
fn on_session_start(&self) -> impl std::future::Future<Output = Result<(), anyhow::Error>> + Send {
91+
async {
92+
println!("[Hook] Session has successfully started!");
93+
Ok(())
94+
}
9195
}
9296

93-
async fn pre_turn(&self) -> Result<HookResult, anyhow::Error> {
94-
println!("[Hook] Preparing next turn...");
95-
Ok(HookResult {
96-
allow: true,
97-
message: String::new(),
98-
})
97+
fn pre_turn(&self) -> impl std::future::Future<Output = Result<HookResult, anyhow::Error>> + Send {
98+
async {
99+
println!("[Hook] Preparing next turn...");
100+
Ok(HookResult {
101+
allow: true,
102+
message: String::new(),
103+
})
104+
}
99105
}
100106

101-
async fn pre_tool_call(&self, tool_call: &ToolCall) -> Result<HookResult, anyhow::Error> {
102-
println!("[Hook] Inspecting tool call request: {}", tool_call.name);
103-
Ok(HookResult {
104-
allow: true,
105-
message: String::new(),
106-
})
107+
fn pre_tool_call<'a>(&'a self, tool_call: &'a ToolCall) -> impl std::future::Future<Output = Result<HookResult, anyhow::Error>> + Send {
108+
async move {
109+
println!("[Hook] Inspecting tool call request: {}", tool_call.name);
110+
Ok(HookResult {
111+
allow: true,
112+
message: String::new(),
113+
})
114+
}
107115
}
108116

109-
async fn post_tool_call(&self, result: &ToolResult) -> Result<(), anyhow::Error> {
110-
println!("[Hook] Tool call completed with result: {:?}", result.result);
111-
Ok(())
117+
fn post_tool_call<'a>(&'a self, result: &'a ToolResult) -> impl std::future::Future<Output = Result<(), anyhow::Error>> + Send {
118+
async move {
119+
println!("[Hook] Tool call completed with result: {:?}", result.result);
120+
Ok(())
121+
}
112122
}
113123
}
114124

skills/google-antigravity-sdk-rust/examples/getting_started/multimodal.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ To enable the agent to generate images on request, you must add `BuiltinTools::G
99
```rust
1010
use antigravity_sdk_rust::agent::{Agent, AgentConfig};
1111
use antigravity_sdk_rust::policy;
12-
use antigravity_sdk_rust::types::{BuiltinTools, CapabilitiesConfig, GeminiConfig};
13-
use std::sync::Arc;
12+
use antigravity_sdk_rust::types::{BuiltinTools, CapabilitiesConfig};
1413

1514
#[tokio::main]
1615
async fn main() -> Result<(), anyhow::Error> {

0 commit comments

Comments
 (0)