codetether_agent/tool/
swarm_share.rs1use 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 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 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}