1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//! Integration layer with TAP ecosystem components
use crate::error::{Error, Result};
use std::path::PathBuf;
use std::sync::Arc;
use tap_agent::TapAgent;
use tap_node::{NodeConfig, TapNode};
use tracing::{debug, error, info};
/// TAP ecosystem integration - thin wrapper around TapNode
pub struct TapIntegration {
node: Arc<TapNode>,
/// Custom storage path for testing (if set, overrides default ~/.tap)
storage_path: Option<PathBuf>,
}
impl TapIntegration {
/// Create new TAP integration using TapNode with agent registration
pub async fn new(
agent_did: Option<&str>,
tap_root: Option<&str>,
agent: Option<Arc<TapAgent>>,
) -> Result<Self> {
// Create node configuration
let mut config = NodeConfig::default();
// Set agent DID for proper storage organization
if let Some(did) = agent_did {
config.agent_did = Some(did.to_string());
}
// Set custom TAP root if provided
if let Some(root) = tap_root {
config.tap_root = Some(PathBuf::from(root));
}
// Enable storage features
config.enable_message_logging = true;
config.log_message_content = true;
// Create the node
let mut node = TapNode::new(config);
// Initialize storage with DID-based structure
node.init_storage().await.map_err(|e| {
Error::configuration(format!("Failed to initialize TAP node storage: {}", e))
})?;
info!("Initialized TAP integration with DID-based storage");
let node_arc = Arc::new(node);
// Register the primary agent if provided
if let Some(agent) = agent {
node_arc
.register_agent(agent)
.await
.map_err(|e| Error::configuration(format!("Failed to register agent: {}", e)))?;
info!("Registered primary agent with TAP Node");
}
// Load and register all additional agents from storage
match tap_agent::storage::KeyStorage::load_default() {
Ok(storage) => {
let stored_dids: Vec<String> = storage.keys.keys().cloned().collect();
info!("Found {} total keys in storage", stored_dids.len());
for stored_did in &stored_dids {
// Skip the primary agent if it's already registered
if agent_did.is_some_and(|did| stored_did == did) {
continue;
}
info!("Registering additional agent: {}", stored_did);
match TapAgent::from_stored_keys(Some(stored_did.clone()), true).await {
Ok(additional_agent) => {
let additional_agent_arc = Arc::new(additional_agent);
if let Err(e) = node_arc.register_agent(additional_agent_arc).await {
error!("Failed to register additional agent {}: {}", stored_did, e);
} else {
info!("Successfully registered additional agent: {}", stored_did);
}
}
Err(e) => {
error!("Failed to load additional agent {}: {}", stored_did, e);
}
}
}
}
Err(e) => {
debug!("Could not load additional keys from storage: {}", e);
}
}
Ok(Self {
node: node_arc,
storage_path: None,
})
}
/// Create new TAP integration for testing with custom paths
#[allow(dead_code)]
pub async fn new_for_testing(tap_root: Option<&str>, agent_did: &str) -> Result<Self> {
let mut config = NodeConfig::default();
// Set custom TAP root for testing
if let Some(root) = tap_root {
config.tap_root = Some(PathBuf::from(root));
}
// Set agent DID
config.agent_did = Some(agent_did.to_string());
config.enable_message_logging = true;
config.log_message_content = true;
let mut node = TapNode::new(config);
node.init_storage().await.map_err(|e| {
Error::configuration(format!("Failed to initialize TAP node storage: {}", e))
})?;
debug!(
"Created TAP integration for testing with DID: {}",
agent_did
);
// For testing, use the keys.json file in the TAP root
let storage_path = tap_root.map(|root| PathBuf::from(root).join("keys.json"));
// Create a test agent for testing
let (test_agent, _) = TapAgent::from_ephemeral_key()
.await
.map_err(|e| Error::configuration(format!("Failed to create test agent: {}", e)))?;
let node_arc = Arc::new(node);
node_arc
.register_agent(Arc::new(test_agent))
.await
.map_err(|e| Error::configuration(format!("Failed to register test agent: {}", e)))?;
Ok(Self {
node: node_arc,
storage_path,
})
}
/// Get reference to underlying TapNode
#[allow(dead_code)]
pub fn node(&self) -> &Arc<TapNode> {
&self.node
}
/// Get storage path (if available)
#[allow(dead_code)]
pub fn storage_path(&self) -> Option<&PathBuf> {
self.storage_path.as_ref()
}
/// Get storage reference (if available) - uses the primary node storage
pub fn storage(&self) -> Option<&Arc<tap_node::storage::Storage>> {
self.node.storage()
}
/// Get storage for a specific agent DID
/// This delegates to TAP Node's AgentStorageManager for proper agent isolation
pub async fn storage_for_agent(
&self,
agent_did: &str,
) -> Result<Arc<tap_node::storage::Storage>> {
// Use TAP Node's agent storage manager for consistent storage access
if let Some(storage_manager) = self.node.agent_storage_manager() {
storage_manager
.get_agent_storage(agent_did)
.await
.map_err(|e| {
Error::configuration(format!(
"Failed to get storage for agent {}: {}",
agent_did, e
))
})
} else {
Err(Error::configuration(
"Agent storage manager not available".to_string(),
))
}
}
/// List all registered agents (from storage and in-memory registry)
pub async fn list_agents(&self) -> Result<Vec<AgentInfo>> {
let mut agents = Vec::new();
// Load agents directly from KeyStorage to get labels
use tap_agent::storage::KeyStorage;
let key_storage = if let Some(ref storage_path) = self.storage_path {
KeyStorage::load_from_path(storage_path)
} else {
KeyStorage::load_default()
};
match key_storage {
Ok(storage) => {
// Process each stored key
for (did, stored_key) in &storage.keys {
let mut metadata = std::collections::HashMap::new();
// Include the label from the stored key
if !stored_key.label.is_empty() {
metadata.insert("label".to_string(), stored_key.label.clone());
}
// Also include any additional metadata from the stored key
for (key, value) in &stored_key.metadata {
metadata.insert(key.clone(), value.clone());
}
// Try to load policies for this agent
let policies = storage.load_agent_policies(did).unwrap_or_default();
agents.push(AgentInfo {
id: did.clone(),
role: "Agent".to_string(), // Default role, will be determined per transaction
for_party: did.clone(), // Default to self, will be determined per transaction
policies,
metadata,
});
}
}
Err(e) => {
debug!("Could not load key storage: {}", e);
}
}
// Also include any agents only registered in TapNode (for backward compatibility)
let node_agent_dids = self.node.list_agents();
for did in node_agent_dids {
// Check if we already have this agent from key storage
if !agents.iter().any(|a| a.id == did) {
agents.push(AgentInfo {
id: did.clone(),
role: "Agent".to_string(),
for_party: did,
policies: vec![],
metadata: std::collections::HashMap::new(),
});
}
}
Ok(agents)
}
}
/// Agent information for MCP interface
#[derive(Debug, Clone)]
pub struct AgentInfo {
pub id: String,
pub role: String,
pub for_party: String,
pub policies: Vec<String>,
pub metadata: std::collections::HashMap<String, String>,
}