Skip to main content

codetether_agent/tool/
swarm_share.rs

1//! Swarm Share Tool - Publish and query shared results between sub-agents
2//!
3//! This tool allows sub-agents in a swarm to share intermediate results,
4//! enabling real-time collaboration between concurrent agents.
5
6use super::{Tool, ToolResult};
7use crate::swarm::result_store::ResultStore;
8use anyhow::{Context, Result};
9use async_trait::async_trait;
10use serde::Deserialize;
11use serde_json::{Value, json};
12use std::sync::Arc;
13
14pub struct SwarmShareTool {
15    store: Arc<ResultStore>,
16    /// The subtask ID of the agent using this tool
17    producer_id: String,
18}
19
20impl SwarmShareTool {
21    pub fn new(store: Arc<ResultStore>, producer_id: String) -> Self {
22        Self { store, producer_id }
23    }
24
25    /// Create with default ResultStore and empty producer ID (for registry registration)
26    pub fn with_defaults() -> Self {
27        Self {
28            store: ResultStore::new_arc(),
29            producer_id: String::new(),
30        }
31    }
32}
33
34impl Default for SwarmShareTool {
35    fn default() -> Self {
36        Self::with_defaults()
37    }
38}
39
40#[derive(Deserialize)]
41struct Params {
42    action: String,
43    #[serde(default)]
44    key: Option<String>,
45    #[serde(default)]
46    value: Option<Value>,
47    #[serde(default)]
48    tags: Option<Vec<String>>,
49    #[serde(default)]
50    prefix: Option<String>,
51}
52
53#[async_trait]
54impl Tool for SwarmShareTool {
55    fn id(&self) -> &str {
56        "swarm_share"
57    }
58
59    fn name(&self) -> &str {
60        "Swarm Share"
61    }
62
63    fn description(&self) -> &str {
64        "Share results with other sub-agents in the swarm. Actions: publish (share a result), \
65         get (retrieve a result by key), query_tags (find results by tags), \
66         query_prefix (find results by key prefix), list (show all shared results)."
67    }
68
69    fn parameters(&self) -> Value {
70        json!({
71            "type": "object",
72            "properties": {
73                "action": {
74                    "type": "string",
75                    "enum": ["publish", "get", "query_tags", "query_prefix", "list"],
76                    "description": "Action to perform"
77                },
78                "key": {
79                    "type": "string",
80                    "description": "Result key (for publish/get)"
81                },
82                "value": {
83                    "description": "Result value to publish (any JSON value)"
84                },
85                "tags": {
86                    "type": "array",
87                    "items": {"type": "string"},
88                    "description": "Tags for publish or query_tags"
89                },
90                "prefix": {
91                    "type": "string",
92                    "description": "Key prefix for query_prefix"
93                },
94                "producer": {
95                    "type": "string",
96                    "description": "Producer subtask ID to filter by (for query)"
97                }
98            },
99            "required": ["action"]
100        })
101    }
102
103    async fn execute(&self, params: Value) -> Result<ToolResult> {
104        let p: Params = serde_json::from_value(params).context("Invalid params")?;
105
106        match p.action.as_str() {
107            "publish" => {
108                let key = p
109                    .key
110                    .ok_or_else(|| anyhow::anyhow!("key required for publish"))?;
111                let value = p
112                    .value
113                    .ok_or_else(|| anyhow::anyhow!("value required for publish"))?;
114                let tags = p.tags.unwrap_or_default();
115
116                let result = self
117                    .store
118                    .publish(&key, &self.producer_id, value, tags, None)
119                    .await?;
120
121                Ok(ToolResult::success(format!(
122                    "Published result '{}' (type: {})",
123                    key, result.schema.type_name
124                )))
125            }
126            "get" => {
127                let key = p
128                    .key
129                    .ok_or_else(|| anyhow::anyhow!("key required for get"))?;
130
131                match self.store.get(&key).await {
132                    Some(result) => {
133                        let output = json!({
134                            "key": result.key,
135                            "producer": result.producer_id,
136                            "value": result.value,
137                            "type": result.schema.type_name,
138                            "tags": result.tags,
139                            "published_at": result.published_at.to_rfc3339(),
140                        });
141                        Ok(ToolResult::success(
142                            serde_json::to_string_pretty(&output)
143                                .unwrap_or_else(|_| format!("{:?}", result.value)),
144                        ))
145                    }
146                    None => Ok(ToolResult::error(format!("No result found for key: {key}"))),
147                }
148            }
149            "query_tags" => {
150                let tags = p
151                    .tags
152                    .ok_or_else(|| anyhow::anyhow!("tags required for query_tags"))?;
153
154                let results = self.store.query_by_tags(&tags).await;
155                let output: Vec<Value> = results
156                    .iter()
157                    .map(|r| {
158                        json!({
159                            "key": r.key,
160                            "producer": r.producer_id,
161                            "type": r.schema.type_name,
162                            "tags": r.tags,
163                        })
164                    })
165                    .collect();
166
167                Ok(ToolResult::success(format!(
168                    "Found {} results matching tags {:?}:\n{}",
169                    output.len(),
170                    tags,
171                    serde_json::to_string_pretty(&output).unwrap_or_default()
172                )))
173            }
174            "query_prefix" => {
175                let prefix = p
176                    .prefix
177                    .ok_or_else(|| anyhow::anyhow!("prefix required for query_prefix"))?;
178
179                let results = self.store.query_by_prefix(&prefix).await;
180                let output: Vec<Value> = results
181                    .iter()
182                    .map(|r| {
183                        json!({
184                            "key": r.key,
185                            "producer": r.producer_id,
186                            "type": r.schema.type_name,
187                            "tags": r.tags,
188                        })
189                    })
190                    .collect();
191
192                Ok(ToolResult::success(format!(
193                    "Found {} results with prefix '{}':\n{}",
194                    output.len(),
195                    prefix,
196                    serde_json::to_string_pretty(&output).unwrap_or_default()
197                )))
198            }
199            "list" => {
200                let results = self.store.get_all().await;
201                if results.is_empty() {
202                    return Ok(ToolResult::success(
203                        "No shared results in store".to_string(),
204                    ));
205                }
206
207                let output: Vec<Value> = results
208                    .iter()
209                    .map(|r| {
210                        json!({
211                            "key": r.key,
212                            "producer": r.producer_id,
213                            "type": r.schema.type_name,
214                            "tags": r.tags,
215                        })
216                    })
217                    .collect();
218
219                Ok(ToolResult::success(format!(
220                    "{} shared results:\n{}",
221                    output.len(),
222                    serde_json::to_string_pretty(&output).unwrap_or_default()
223                )))
224            }
225            _ => Ok(ToolResult::error(format!("Unknown action: {}", p.action))),
226        }
227    }
228}