mnemo_core/query/
share.rs1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4use crate::error::{Error, Result};
5use crate::model::acl::{Acl, Permission, PrincipalType};
6use crate::model::event::EventType;
7use crate::model::memory::Scope;
8use crate::query::MnemoEngine;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct ShareRequest {
12 pub memory_id: Uuid,
13 pub agent_id: Option<String>,
14 pub target_agent_id: String,
15 pub target_agent_ids: Option<Vec<String>>,
16 pub permission: Option<Permission>,
17 pub expires_in_hours: Option<f64>,
18}
19
20impl ShareRequest {
21 pub fn new(memory_id: Uuid, target_agent_id: String) -> Self {
22 Self {
23 memory_id,
24 agent_id: None,
25 target_agent_id,
26 target_agent_ids: None,
27 permission: None,
28 expires_in_hours: None,
29 }
30 }
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct ShareResponse {
35 pub acl_id: Uuid,
36 pub acl_ids: Vec<Uuid>,
37 pub memory_id: Uuid,
38 pub shared_with: String,
39 pub shared_with_all: Vec<String>,
40 pub permission: Permission,
41}
42
43pub async fn execute(engine: &MnemoEngine, request: ShareRequest) -> Result<ShareResponse> {
44 let agent_id = request
45 .agent_id
46 .unwrap_or_else(|| engine.default_agent_id.clone());
47 let permission = request.permission.unwrap_or(Permission::Read);
48
49 let has_access = engine
51 .storage
52 .check_permission(request.memory_id, &agent_id, Permission::Admin)
53 .await?;
54
55 if !has_access {
56 return Err(Error::PermissionDenied(format!(
57 "agent {agent_id} cannot share memory {}",
58 request.memory_id
59 )));
60 }
61
62 let targets = if let Some(ref ids) = request.target_agent_ids {
64 ids.clone()
65 } else {
66 vec![request.target_agent_id.clone()]
67 };
68
69 let expires_at = request.expires_in_hours.map(|h| {
71 let exp = chrono::Utc::now() + chrono::Duration::seconds((h * 3600.0) as i64);
72 exp.to_rfc3339()
73 });
74
75 let now = chrono::Utc::now().to_rfc3339();
76 let mut acl_ids = Vec::new();
77
78 for target in &targets {
79 let acl_id = Uuid::now_v7();
80 let acl = Acl {
81 id: acl_id,
82 memory_id: request.memory_id,
83 principal_type: PrincipalType::Agent,
84 principal_id: target.clone(),
85 permission,
86 granted_by: agent_id.clone(),
87 created_at: now.clone(),
88 expires_at: expires_at.clone(),
89 };
90 engine.storage.insert_acl(&acl).await?;
91 acl_ids.push(acl_id);
92 }
93
94 if let Some(mut record) = engine.storage.get_memory(request.memory_id).await?
96 && record.scope == Scope::Private
97 {
98 record.scope = Scope::Shared;
99 record.updated_at = now.clone();
100 engine.storage.update_memory(&record).await?;
101 }
102
103 let mut event = super::event_builder::build_event(
105 engine,
106 &agent_id,
107 EventType::MemoryShare,
108 serde_json::json!({
109 "memory_id": request.memory_id.to_string(),
110 "shared_with": targets,
111 "permission": permission.to_string(),
112 }),
113 &request.memory_id.to_string(),
114 None,
115 )
116 .await;
117 if engine.embed_events
118 && let Ok(emb) = engine.embedding.embed(&event.payload.to_string()).await
119 {
120 event.embedding = Some(emb);
121 }
122 if let Err(e) = engine.storage.insert_event(&event).await {
123 tracing::error!(event_id = %event.id, error = %e, "failed to insert audit event");
124 }
125
126 let first_acl_id = acl_ids[0];
127 let first_target = targets[0].clone();
128
129 Ok(ShareResponse {
130 acl_id: first_acl_id,
131 acl_ids,
132 memory_id: request.memory_id,
133 shared_with: first_target,
134 shared_with_all: targets,
135 permission,
136 })
137}