powermem 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. powermem/__init__.py +103 -0
  2. powermem/agent/__init__.py +35 -0
  3. powermem/agent/abstract/__init__.py +22 -0
  4. powermem/agent/abstract/collaboration.py +259 -0
  5. powermem/agent/abstract/context.py +187 -0
  6. powermem/agent/abstract/manager.py +232 -0
  7. powermem/agent/abstract/permission.py +217 -0
  8. powermem/agent/abstract/privacy.py +267 -0
  9. powermem/agent/abstract/scope.py +199 -0
  10. powermem/agent/agent.py +791 -0
  11. powermem/agent/components/__init__.py +18 -0
  12. powermem/agent/components/collaboration_coordinator.py +645 -0
  13. powermem/agent/components/permission_controller.py +586 -0
  14. powermem/agent/components/privacy_protector.py +767 -0
  15. powermem/agent/components/scope_controller.py +685 -0
  16. powermem/agent/factories/__init__.py +16 -0
  17. powermem/agent/factories/agent_factory.py +266 -0
  18. powermem/agent/factories/config_factory.py +308 -0
  19. powermem/agent/factories/memory_factory.py +229 -0
  20. powermem/agent/implementations/__init__.py +16 -0
  21. powermem/agent/implementations/hybrid.py +728 -0
  22. powermem/agent/implementations/multi_agent.py +1040 -0
  23. powermem/agent/implementations/multi_user.py +1020 -0
  24. powermem/agent/types.py +53 -0
  25. powermem/agent/wrappers/__init__.py +14 -0
  26. powermem/agent/wrappers/agent_memory_wrapper.py +427 -0
  27. powermem/agent/wrappers/compatibility_wrapper.py +520 -0
  28. powermem/config_loader.py +318 -0
  29. powermem/configs.py +249 -0
  30. powermem/core/__init__.py +19 -0
  31. powermem/core/async_memory.py +1493 -0
  32. powermem/core/audit.py +258 -0
  33. powermem/core/base.py +165 -0
  34. powermem/core/memory.py +1567 -0
  35. powermem/core/setup.py +162 -0
  36. powermem/core/telemetry.py +215 -0
  37. powermem/integrations/__init__.py +17 -0
  38. powermem/integrations/embeddings/__init__.py +13 -0
  39. powermem/integrations/embeddings/aws_bedrock.py +100 -0
  40. powermem/integrations/embeddings/azure_openai.py +55 -0
  41. powermem/integrations/embeddings/base.py +31 -0
  42. powermem/integrations/embeddings/config/base.py +132 -0
  43. powermem/integrations/embeddings/configs.py +31 -0
  44. powermem/integrations/embeddings/factory.py +48 -0
  45. powermem/integrations/embeddings/gemini.py +39 -0
  46. powermem/integrations/embeddings/huggingface.py +41 -0
  47. powermem/integrations/embeddings/langchain.py +35 -0
  48. powermem/integrations/embeddings/lmstudio.py +29 -0
  49. powermem/integrations/embeddings/mock.py +11 -0
  50. powermem/integrations/embeddings/ollama.py +53 -0
  51. powermem/integrations/embeddings/openai.py +49 -0
  52. powermem/integrations/embeddings/qwen.py +102 -0
  53. powermem/integrations/embeddings/together.py +31 -0
  54. powermem/integrations/embeddings/vertexai.py +54 -0
  55. powermem/integrations/llm/__init__.py +18 -0
  56. powermem/integrations/llm/anthropic.py +87 -0
  57. powermem/integrations/llm/base.py +132 -0
  58. powermem/integrations/llm/config/anthropic.py +56 -0
  59. powermem/integrations/llm/config/azure.py +56 -0
  60. powermem/integrations/llm/config/base.py +62 -0
  61. powermem/integrations/llm/config/deepseek.py +56 -0
  62. powermem/integrations/llm/config/ollama.py +56 -0
  63. powermem/integrations/llm/config/openai.py +79 -0
  64. powermem/integrations/llm/config/qwen.py +68 -0
  65. powermem/integrations/llm/config/qwen_asr.py +46 -0
  66. powermem/integrations/llm/config/vllm.py +56 -0
  67. powermem/integrations/llm/configs.py +26 -0
  68. powermem/integrations/llm/deepseek.py +106 -0
  69. powermem/integrations/llm/factory.py +118 -0
  70. powermem/integrations/llm/gemini.py +201 -0
  71. powermem/integrations/llm/langchain.py +65 -0
  72. powermem/integrations/llm/ollama.py +106 -0
  73. powermem/integrations/llm/openai.py +166 -0
  74. powermem/integrations/llm/openai_structured.py +80 -0
  75. powermem/integrations/llm/qwen.py +207 -0
  76. powermem/integrations/llm/qwen_asr.py +171 -0
  77. powermem/integrations/llm/vllm.py +106 -0
  78. powermem/integrations/rerank/__init__.py +20 -0
  79. powermem/integrations/rerank/base.py +43 -0
  80. powermem/integrations/rerank/config/__init__.py +7 -0
  81. powermem/integrations/rerank/config/base.py +27 -0
  82. powermem/integrations/rerank/configs.py +23 -0
  83. powermem/integrations/rerank/factory.py +68 -0
  84. powermem/integrations/rerank/qwen.py +159 -0
  85. powermem/intelligence/__init__.py +17 -0
  86. powermem/intelligence/ebbinghaus_algorithm.py +354 -0
  87. powermem/intelligence/importance_evaluator.py +361 -0
  88. powermem/intelligence/intelligent_memory_manager.py +284 -0
  89. powermem/intelligence/manager.py +148 -0
  90. powermem/intelligence/plugin.py +229 -0
  91. powermem/prompts/__init__.py +29 -0
  92. powermem/prompts/graph/graph_prompts.py +217 -0
  93. powermem/prompts/graph/graph_tools_prompts.py +469 -0
  94. powermem/prompts/importance_evaluation.py +246 -0
  95. powermem/prompts/intelligent_memory_prompts.py +163 -0
  96. powermem/prompts/templates.py +193 -0
  97. powermem/storage/__init__.py +14 -0
  98. powermem/storage/adapter.py +896 -0
  99. powermem/storage/base.py +109 -0
  100. powermem/storage/config/base.py +13 -0
  101. powermem/storage/config/oceanbase.py +58 -0
  102. powermem/storage/config/pgvector.py +52 -0
  103. powermem/storage/config/sqlite.py +27 -0
  104. powermem/storage/configs.py +159 -0
  105. powermem/storage/factory.py +59 -0
  106. powermem/storage/migration_manager.py +438 -0
  107. powermem/storage/oceanbase/__init__.py +8 -0
  108. powermem/storage/oceanbase/constants.py +162 -0
  109. powermem/storage/oceanbase/oceanbase.py +1384 -0
  110. powermem/storage/oceanbase/oceanbase_graph.py +1441 -0
  111. powermem/storage/pgvector/__init__.py +7 -0
  112. powermem/storage/pgvector/pgvector.py +420 -0
  113. powermem/storage/sqlite/__init__.py +0 -0
  114. powermem/storage/sqlite/sqlite.py +218 -0
  115. powermem/storage/sqlite/sqlite_vector_store.py +311 -0
  116. powermem/utils/__init__.py +35 -0
  117. powermem/utils/utils.py +605 -0
  118. powermem/version.py +23 -0
  119. powermem-0.1.0.dist-info/METADATA +187 -0
  120. powermem-0.1.0.dist-info/RECORD +123 -0
  121. powermem-0.1.0.dist-info/WHEEL +5 -0
  122. powermem-0.1.0.dist-info/licenses/LICENSE +206 -0
  123. powermem-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,586 @@
1
+ """
2
+ Multi-Agent Permission Controller
3
+
4
+ Manages access permissions for memories across different agents and scopes.
5
+ Implements role-based and attribute-based access control. Migrated and
6
+ refactored from the original multi_agent implementation.
7
+ """
8
+
9
+ import logging
10
+ from datetime import datetime
11
+ from typing import Any, Dict, List, Optional, Set
12
+
13
+ from typing import Any, Dict
14
+ from powermem.agent.types import AccessPermission
15
+ from powermem.agent.abstract.permission import AgentPermissionManagerBase
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class PermissionController(AgentPermissionManagerBase):
21
+ """
22
+ Multi-agent permission controller implementation.
23
+
24
+ Manages access permissions for memories, implements RBAC and ABAC,
25
+ handles permission grants, revocations, and audits.
26
+ """
27
+
28
+ def __init__(self, config: Dict[str, Any]):
29
+ """
30
+ Initialize the permission controller.
31
+
32
+ Args:
33
+ config: Memory configuration object
34
+ """
35
+ super().__init__(config.agent_memory.multi_agent_config.__dict__)
36
+ self.config = config
37
+ self.multi_agent_config = config.agent_memory.multi_agent_config
38
+
39
+ # Permission storage: memory_id -> agent_id -> permissions
40
+ self.memory_permissions: Dict[str, Dict[str, Set[AccessPermission]]] = {}
41
+
42
+ # Role-based permissions
43
+ self.role_permissions = self.multi_agent_config.default_permissions
44
+
45
+ # Access audit log
46
+ self.access_log: List[Dict[str, Any]] = []
47
+
48
+ # Agent roles mapping
49
+ self.agent_roles: Dict[str, List[str]] = {}
50
+
51
+ # Initialize default permissions
52
+ self._initialize_default_permissions()
53
+
54
+ def initialize(self) -> None:
55
+ """
56
+ Initialize the permission controller.
57
+ """
58
+ try:
59
+ # Initialize default permissions
60
+ self._initialize_default_permissions()
61
+
62
+ # Initialize agent roles
63
+ self._initialize_agent_roles()
64
+
65
+ self.initialized = True
66
+ logger.info("Permission controller initialized successfully")
67
+
68
+ except Exception as e:
69
+ logger.error(f"Failed to initialize permission controller: {e}")
70
+ raise
71
+
72
+ def _initialize_default_permissions(self) -> None:
73
+ """Initialize default permissions from configuration."""
74
+ # Default permissions are already loaded from config
75
+ pass
76
+
77
+ def _initialize_agent_roles(self) -> None:
78
+ """Initialize agent roles from configuration."""
79
+ # Initialize agent roles from config if available
80
+ if hasattr(self.multi_agent_config, 'agent_groups'):
81
+ for group_name, group_config in self.multi_agent_config.agent_groups.items():
82
+ for agent_id in group_config.get('members', []):
83
+ if agent_id not in self.agent_roles:
84
+ self.agent_roles[agent_id] = []
85
+ self.agent_roles[agent_id].append(group_name)
86
+
87
+ def check_permission(
88
+ self,
89
+ agent_id: str,
90
+ memory_id: str,
91
+ permission: AccessPermission
92
+ ) -> bool:
93
+ """
94
+ Check if an agent has a specific permission for a memory.
95
+
96
+ Args:
97
+ agent_id: ID of the agent
98
+ memory_id: ID of the memory
99
+ permission: Permission to check
100
+
101
+ Returns:
102
+ True if the agent has the permission, False otherwise
103
+ """
104
+ try:
105
+ # Check direct memory permissions
106
+ if memory_id in self.memory_permissions:
107
+ if agent_id in self.memory_permissions[memory_id]:
108
+ if permission in self.memory_permissions[memory_id][agent_id]:
109
+ self._log_access(agent_id, memory_id, permission, "granted")
110
+ return True
111
+
112
+ # Check role-based permissions
113
+ if agent_id in self.agent_roles:
114
+ for role in self.agent_roles[agent_id]:
115
+ if role in self.role_permissions:
116
+ if permission in self.role_permissions[role]:
117
+ self._log_access(agent_id, memory_id, permission, "granted_by_role")
118
+ return True
119
+
120
+ # Check default permissions
121
+ if "public" in self.role_permissions:
122
+ if permission in self.role_permissions["public"]:
123
+ self._log_access(agent_id, memory_id, permission, "granted_by_default")
124
+ return True
125
+
126
+ # Log detailed debug information
127
+ logger.debug(f"Permission check failed for agent {agent_id} on memory {memory_id}: "
128
+ f"memory_permissions={memory_id in self.memory_permissions}, "
129
+ f"agent_roles={agent_id in self.agent_roles}, "
130
+ f"permission={permission.value}")
131
+
132
+ self._log_access(agent_id, memory_id, permission, "denied")
133
+ return False
134
+
135
+ except Exception as e:
136
+ logger.error(f"Error checking permission: {e}")
137
+ return False
138
+
139
+ def grant_permission(
140
+ self,
141
+ memory_id: str,
142
+ agent_id: str,
143
+ permission: AccessPermission,
144
+ granted_by: str
145
+ ) -> Dict[str, Any]:
146
+ """
147
+ Grant a permission to an agent for a memory.
148
+
149
+ Args:
150
+ memory_id: ID of the memory
151
+ agent_id: ID of the agent to grant permission to
152
+ permission: Permission to grant
153
+ granted_by: ID of the agent granting the permission
154
+
155
+ Returns:
156
+ Dictionary containing the grant result
157
+ """
158
+ try:
159
+ # Initialize memory permissions if not exists
160
+ if memory_id not in self.memory_permissions:
161
+ self.memory_permissions[memory_id] = {}
162
+
163
+ if agent_id not in self.memory_permissions[memory_id]:
164
+ self.memory_permissions[memory_id][agent_id] = set()
165
+
166
+ # Grant permission
167
+ self.memory_permissions[memory_id][agent_id].add(permission)
168
+
169
+ # Log the grant
170
+ self._log_permission_change(
171
+ memory_id, agent_id, permission, "granted", granted_by
172
+ )
173
+
174
+ logger.info(f"Granted {permission.value} permission to {agent_id} for memory {memory_id}")
175
+
176
+ return {
177
+ 'success': True,
178
+ 'memory_id': memory_id,
179
+ 'agent_id': agent_id,
180
+ 'permission': permission.value,
181
+ 'granted_by': granted_by,
182
+ 'granted_at': datetime.now().isoformat(),
183
+ }
184
+
185
+ except Exception as e:
186
+ logger.error(f"Error granting permission: {e}")
187
+ return {
188
+ 'success': False,
189
+ 'error': str(e),
190
+ 'memory_id': memory_id,
191
+ 'agent_id': agent_id,
192
+ }
193
+
194
+ def revoke_permission(
195
+ self,
196
+ memory_id: str,
197
+ agent_id: str,
198
+ permission: AccessPermission,
199
+ revoked_by: str
200
+ ) -> Dict[str, Any]:
201
+ """
202
+ Revoke a permission from an agent for a memory.
203
+
204
+ Args:
205
+ memory_id: ID of the memory
206
+ agent_id: ID of the agent to revoke permission from
207
+ permission: Permission to revoke
208
+ revoked_by: ID of the agent revoking the permission
209
+
210
+ Returns:
211
+ Dictionary containing the revoke result
212
+ """
213
+ try:
214
+ # Check if memory and agent exist
215
+ if memory_id not in self.memory_permissions:
216
+ return {
217
+ 'success': False,
218
+ 'error': 'Memory not found',
219
+ 'memory_id': memory_id,
220
+ }
221
+
222
+ if agent_id not in self.memory_permissions[memory_id]:
223
+ return {
224
+ 'success': False,
225
+ 'error': 'Agent not found for this memory',
226
+ 'memory_id': memory_id,
227
+ 'agent_id': agent_id,
228
+ }
229
+
230
+ # Revoke permission
231
+ if permission in self.memory_permissions[memory_id][agent_id]:
232
+ self.memory_permissions[memory_id][agent_id].remove(permission)
233
+
234
+ # Log the revocation
235
+ self._log_permission_change(
236
+ memory_id, agent_id, permission, "revoked", revoked_by
237
+ )
238
+
239
+ logger.info(f"Revoked {permission.value} permission from {agent_id} for memory {memory_id}")
240
+
241
+ return {
242
+ 'success': True,
243
+ 'memory_id': memory_id,
244
+ 'agent_id': agent_id,
245
+ 'permission': permission.value,
246
+ 'revoked_by': revoked_by,
247
+ 'revoked_at': datetime.now().isoformat(),
248
+ }
249
+ else:
250
+ return {
251
+ 'success': False,
252
+ 'error': 'Permission not found',
253
+ 'memory_id': memory_id,
254
+ 'agent_id': agent_id,
255
+ 'permission': permission.value,
256
+ }
257
+
258
+ except Exception as e:
259
+ logger.error(f"Error revoking permission: {e}")
260
+ return {
261
+ 'success': False,
262
+ 'error': str(e),
263
+ 'memory_id': memory_id,
264
+ 'agent_id': agent_id,
265
+ }
266
+
267
+ def get_permissions(
268
+ self,
269
+ memory_id: str,
270
+ agent_id: Optional[str] = None
271
+ ) -> Dict[str, Any]:
272
+ """
273
+ Get permissions for a memory or agent.
274
+
275
+ Args:
276
+ memory_id: ID of the memory
277
+ agent_id: Optional ID of the agent
278
+
279
+ Returns:
280
+ Dictionary containing permission information
281
+ """
282
+ try:
283
+ if agent_id:
284
+ # Get permissions for specific agent
285
+ if memory_id in self.memory_permissions:
286
+ if agent_id in self.memory_permissions[memory_id]:
287
+ permissions = [
288
+ perm.value for perm in self.memory_permissions[memory_id][agent_id]
289
+ ]
290
+ return {
291
+ 'memory_id': memory_id,
292
+ 'agent_id': agent_id,
293
+ 'permissions': permissions,
294
+ 'permission_count': len(permissions),
295
+ }
296
+
297
+ return {
298
+ 'memory_id': memory_id,
299
+ 'agent_id': agent_id,
300
+ 'permissions': [],
301
+ 'permission_count': 0,
302
+ }
303
+ else:
304
+ # Get all permissions for memory
305
+ if memory_id in self.memory_permissions:
306
+ all_permissions = {}
307
+ for agent_id, permissions in self.memory_permissions[memory_id].items():
308
+ all_permissions[agent_id] = [perm.value for perm in permissions]
309
+
310
+ return {
311
+ 'memory_id': memory_id,
312
+ 'all_permissions': all_permissions,
313
+ 'agent_count': len(all_permissions),
314
+ }
315
+
316
+ return {
317
+ 'memory_id': memory_id,
318
+ 'all_permissions': {},
319
+ 'agent_count': 0,
320
+ }
321
+
322
+ except Exception as e:
323
+ logger.error(f"Error getting permissions: {e}")
324
+ return {
325
+ 'memory_id': memory_id,
326
+ 'error': str(e),
327
+ }
328
+
329
+ def set_default_permissions(
330
+ self,
331
+ memory_id: str,
332
+ permissions: Dict[str, List[AccessPermission]],
333
+ set_by: str
334
+ ) -> Dict[str, Any]:
335
+ """
336
+ Set default permissions for a memory.
337
+
338
+ Args:
339
+ memory_id: ID of the memory
340
+ permissions: Dictionary of default permissions
341
+ set_by: ID of the agent setting the permissions
342
+
343
+ Returns:
344
+ Dictionary containing the set result
345
+ """
346
+ try:
347
+ # Initialize memory permissions if not exists
348
+ if memory_id not in self.memory_permissions:
349
+ self.memory_permissions[memory_id] = {}
350
+
351
+ # Set default permissions
352
+ for agent_id, agent_permissions in permissions.items():
353
+ if agent_id not in self.memory_permissions[memory_id]:
354
+ self.memory_permissions[memory_id][agent_id] = set()
355
+
356
+ self.memory_permissions[memory_id][agent_id].update(agent_permissions)
357
+
358
+ # Log the setting
359
+ self._log_permission_change(
360
+ memory_id, "default", AccessPermission.ADMIN, "set_default", set_by
361
+ )
362
+
363
+ logger.info(f"Set default permissions for memory {memory_id}")
364
+
365
+ return {
366
+ 'success': True,
367
+ 'memory_id': memory_id,
368
+ 'permissions': {k: [p.value for p in v] for k, v in permissions.items()},
369
+ 'set_by': set_by,
370
+ 'set_at': datetime.now().isoformat(),
371
+ }
372
+
373
+ except Exception as e:
374
+ logger.error(f"Error setting default permissions: {e}")
375
+ return {
376
+ 'success': False,
377
+ 'error': str(e),
378
+ 'memory_id': memory_id,
379
+ }
380
+
381
+ def inherit_permissions(
382
+ self,
383
+ target_memory_id: str,
384
+ source_memory_id: str,
385
+ inherited_by: str
386
+ ) -> Dict[str, Any]:
387
+ """
388
+ Inherit permissions from another memory.
389
+
390
+ Args:
391
+ target_memory_id: ID of the target memory
392
+ source_memory_id: ID of the source memory
393
+ inherited_by: ID of the agent inheriting the permissions
394
+
395
+ Returns:
396
+ Dictionary containing the inherit result
397
+ """
398
+ try:
399
+ # Check if source memory exists
400
+ if source_memory_id not in self.memory_permissions:
401
+ return {
402
+ 'success': False,
403
+ 'error': 'Source memory not found',
404
+ 'source_memory_id': source_memory_id,
405
+ }
406
+
407
+ # Initialize target memory permissions if not exists
408
+ if target_memory_id not in self.memory_permissions:
409
+ self.memory_permissions[target_memory_id] = {}
410
+
411
+ # Copy permissions from source to target
412
+ source_permissions = self.memory_permissions[source_memory_id]
413
+ inherited_count = 0
414
+
415
+ for agent_id, permissions in source_permissions.items():
416
+ if agent_id not in self.memory_permissions[target_memory_id]:
417
+ self.memory_permissions[target_memory_id][agent_id] = set()
418
+
419
+ self.memory_permissions[target_memory_id][agent_id].update(permissions)
420
+ inherited_count += len(permissions)
421
+
422
+ # Log the inheritance
423
+ self._log_permission_change(
424
+ target_memory_id, "inherited", AccessPermission.ADMIN, "inherited", inherited_by
425
+ )
426
+
427
+ logger.info(f"Inherited permissions from {source_memory_id} to {target_memory_id}")
428
+
429
+ return {
430
+ 'success': True,
431
+ 'target_memory_id': target_memory_id,
432
+ 'source_memory_id': source_memory_id,
433
+ 'inherited_count': inherited_count,
434
+ 'inherited_by': inherited_by,
435
+ 'inherited_at': datetime.now().isoformat(),
436
+ }
437
+
438
+ except Exception as e:
439
+ logger.error(f"Error inheriting permissions: {e}")
440
+ return {
441
+ 'success': False,
442
+ 'error': str(e),
443
+ 'target_memory_id': target_memory_id,
444
+ 'source_memory_id': source_memory_id,
445
+ }
446
+
447
+ def get_permission_history(
448
+ self,
449
+ memory_id: str,
450
+ agent_id: Optional[str] = None,
451
+ limit: Optional[int] = None
452
+ ) -> List[Dict[str, Any]]:
453
+ """
454
+ Get permission history for a memory or agent.
455
+
456
+ Args:
457
+ memory_id: ID of the memory
458
+ agent_id: Optional ID of the agent
459
+ limit: Optional limit on number of entries
460
+
461
+ Returns:
462
+ List of permission history entries
463
+ """
464
+ try:
465
+ history = []
466
+
467
+ # Filter access log for this memory and agent
468
+ for entry in self.access_log:
469
+ if entry.get('memory_id') == memory_id:
470
+ if agent_id is None or entry.get('agent_id') == agent_id:
471
+ history.append(entry)
472
+
473
+ # Sort by timestamp (newest first)
474
+ history.sort(key=lambda x: x.get('timestamp', ''), reverse=True)
475
+
476
+ # Apply limit if specified
477
+ if limit:
478
+ history = history[:limit]
479
+
480
+ return history
481
+
482
+ except Exception as e:
483
+ logger.error(f"Error getting permission history: {e}")
484
+ return []
485
+
486
+ def validate_permission_chain(
487
+ self,
488
+ agent_id: str,
489
+ memory_id: str,
490
+ permission: AccessPermission
491
+ ) -> Dict[str, Any]:
492
+ """
493
+ Validate the permission chain for an agent and memory.
494
+
495
+ Args:
496
+ agent_id: ID of the agent
497
+ memory_id: ID of the memory
498
+ permission: Permission to validate
499
+
500
+ Returns:
501
+ Dictionary containing validation results
502
+ """
503
+ try:
504
+ validation_result = {
505
+ 'valid': False,
506
+ 'agent_id': agent_id,
507
+ 'memory_id': memory_id,
508
+ 'permission': permission.value,
509
+ 'validation_path': [],
510
+ 'errors': [],
511
+ }
512
+
513
+ # Check direct memory permissions
514
+ if memory_id in self.memory_permissions:
515
+ if agent_id in self.memory_permissions[memory_id]:
516
+ if permission in self.memory_permissions[memory_id][agent_id]:
517
+ validation_result['valid'] = True
518
+ validation_result['validation_path'].append('direct_memory_permission')
519
+ return validation_result
520
+
521
+ # Check role-based permissions
522
+ if agent_id in self.agent_roles:
523
+ for role in self.agent_roles[agent_id]:
524
+ if role in self.role_permissions:
525
+ if permission in self.role_permissions[role]:
526
+ validation_result['valid'] = True
527
+ validation_result['validation_path'].append(f'role_permission:{role}')
528
+ return validation_result
529
+
530
+ # Check default permissions
531
+ if "public" in self.role_permissions:
532
+ if permission in self.role_permissions["public"]:
533
+ validation_result['valid'] = True
534
+ validation_result['validation_path'].append('default_permission')
535
+ return validation_result
536
+
537
+ validation_result['errors'].append('No valid permission path found')
538
+ return validation_result
539
+
540
+ except Exception as e:
541
+ logger.error(f"Error validating permission chain: {e}")
542
+ return {
543
+ 'valid': False,
544
+ 'error': str(e),
545
+ 'agent_id': agent_id,
546
+ 'memory_id': memory_id,
547
+ 'permission': permission.value,
548
+ }
549
+
550
+ def _log_access(
551
+ self,
552
+ agent_id: str,
553
+ memory_id: str,
554
+ permission: AccessPermission,
555
+ result: str
556
+ ) -> None:
557
+ """Log access attempt."""
558
+ log_entry = {
559
+ 'timestamp': datetime.now().isoformat(),
560
+ 'agent_id': agent_id,
561
+ 'memory_id': memory_id,
562
+ 'permission': permission.value,
563
+ 'result': result,
564
+ 'type': 'access_check',
565
+ }
566
+ self.access_log.append(log_entry)
567
+
568
+ def _log_permission_change(
569
+ self,
570
+ memory_id: str,
571
+ agent_id: str,
572
+ permission: AccessPermission,
573
+ action: str,
574
+ performed_by: str
575
+ ) -> None:
576
+ """Log permission change."""
577
+ log_entry = {
578
+ 'timestamp': datetime.now().isoformat(),
579
+ 'memory_id': memory_id,
580
+ 'agent_id': agent_id,
581
+ 'permission': permission.value,
582
+ 'action': action,
583
+ 'performed_by': performed_by,
584
+ 'type': 'permission_change',
585
+ }
586
+ self.access_log.append(log_entry)