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