agentmesh-platform 1.0.0a1__py3-none-any.whl → 1.0.0a2__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.
- agentmesh/__init__.py +6 -13
- agentmesh/cli/main.py +131 -0
- agentmesh/cli/proxy.py +448 -0
- agentmesh/core/__init__.py +7 -0
- agentmesh/core/identity/__init__.py +17 -0
- agentmesh/core/identity/ca.py +386 -0
- agentmesh/governance/policy.py +14 -11
- agentmesh/observability/__init__.py +16 -0
- agentmesh/observability/metrics.py +237 -0
- agentmesh/observability/tracing.py +203 -0
- agentmesh/services/__init__.py +10 -0
- agentmesh/services/audit/__init__.py +14 -0
- agentmesh/services/registry/__init__.py +12 -0
- agentmesh/services/registry/agent_registry.py +249 -0
- agentmesh/services/reward_engine/__init__.py +14 -0
- agentmesh/storage/__init__.py +18 -0
- agentmesh/storage/memory_provider.py +232 -0
- agentmesh/storage/postgres_provider.py +463 -0
- agentmesh/storage/provider.py +231 -0
- agentmesh/storage/redis_provider.py +223 -0
- agentmesh/trust/__init__.py +2 -1
- agentmesh/trust/bridge.py +37 -0
- {agentmesh_platform-1.0.0a1.dist-info → agentmesh_platform-1.0.0a2.dist-info}/METADATA +132 -6
- agentmesh_platform-1.0.0a2.dist-info/RECORD +45 -0
- agentmesh_platform-1.0.0a1.dist-info/RECORD +0 -28
- {agentmesh_platform-1.0.0a1.dist-info → agentmesh_platform-1.0.0a2.dist-info}/WHEEL +0 -0
- {agentmesh_platform-1.0.0a1.dist-info → agentmesh_platform-1.0.0a2.dist-info}/entry_points.txt +0 -0
- {agentmesh_platform-1.0.0a1.dist-info → agentmesh_platform-1.0.0a2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Abstract Storage Provider Interface.
|
|
3
|
+
|
|
4
|
+
Defines the contract that all storage backends must implement.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import Any, Optional
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class StorageConfig(BaseModel):
|
|
13
|
+
"""Configuration for storage provider."""
|
|
14
|
+
|
|
15
|
+
backend: str = Field(default="memory", description="Storage backend type")
|
|
16
|
+
connection_string: Optional[str] = Field(default=None, description="Connection string")
|
|
17
|
+
pool_size: int = Field(default=10, ge=1, le=100, description="Connection pool size")
|
|
18
|
+
timeout_seconds: int = Field(default=30, ge=1, le=300, description="Operation timeout")
|
|
19
|
+
|
|
20
|
+
# Redis-specific
|
|
21
|
+
redis_host: Optional[str] = Field(default="localhost")
|
|
22
|
+
redis_port: int = Field(default=6379, ge=1, le=65535)
|
|
23
|
+
redis_db: int = Field(default=0, ge=0)
|
|
24
|
+
redis_password: Optional[str] = None
|
|
25
|
+
redis_ssl: bool = False
|
|
26
|
+
|
|
27
|
+
# PostgreSQL-specific
|
|
28
|
+
postgres_host: Optional[str] = Field(default="localhost")
|
|
29
|
+
postgres_port: int = Field(default=5432, ge=1, le=65535)
|
|
30
|
+
postgres_database: Optional[str] = Field(default="agentmesh")
|
|
31
|
+
postgres_user: Optional[str] = None
|
|
32
|
+
postgres_password: Optional[str] = None
|
|
33
|
+
postgres_ssl_mode: str = Field(default="prefer")
|
|
34
|
+
|
|
35
|
+
# Cache settings
|
|
36
|
+
cache_ttl_seconds: int = Field(default=300, ge=0)
|
|
37
|
+
enable_cache: bool = True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AbstractStorageProvider(ABC):
|
|
41
|
+
"""
|
|
42
|
+
Abstract storage provider.
|
|
43
|
+
|
|
44
|
+
All storage backends (Redis, Postgres, etc.) must implement this interface.
|
|
45
|
+
Supports:
|
|
46
|
+
- Key-value operations
|
|
47
|
+
- Hash operations (for structured data)
|
|
48
|
+
- List operations (for audit logs, events)
|
|
49
|
+
- TTL support
|
|
50
|
+
- Async operations
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, config: StorageConfig):
|
|
54
|
+
"""Initialize storage provider with configuration."""
|
|
55
|
+
self.config = config
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
async def connect(self) -> None:
|
|
59
|
+
"""Establish connection to storage backend."""
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
@abstractmethod
|
|
63
|
+
async def disconnect(self) -> None:
|
|
64
|
+
"""Close connection to storage backend."""
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
async def health_check(self) -> bool:
|
|
69
|
+
"""Check if storage backend is healthy."""
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
# Key-Value Operations
|
|
73
|
+
|
|
74
|
+
@abstractmethod
|
|
75
|
+
async def get(self, key: str) -> Optional[str]:
|
|
76
|
+
"""Get value by key."""
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
async def set(
|
|
81
|
+
self,
|
|
82
|
+
key: str,
|
|
83
|
+
value: str,
|
|
84
|
+
ttl_seconds: Optional[int] = None,
|
|
85
|
+
) -> bool:
|
|
86
|
+
"""Set value with optional TTL."""
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
@abstractmethod
|
|
90
|
+
async def delete(self, key: str) -> bool:
|
|
91
|
+
"""Delete key."""
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
@abstractmethod
|
|
95
|
+
async def exists(self, key: str) -> bool:
|
|
96
|
+
"""Check if key exists."""
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
# Hash Operations (for structured data)
|
|
100
|
+
|
|
101
|
+
@abstractmethod
|
|
102
|
+
async def hget(self, key: str, field: str) -> Optional[str]:
|
|
103
|
+
"""Get hash field value."""
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
@abstractmethod
|
|
107
|
+
async def hset(self, key: str, field: str, value: str) -> bool:
|
|
108
|
+
"""Set hash field value."""
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
@abstractmethod
|
|
112
|
+
async def hgetall(self, key: str) -> dict[str, str]:
|
|
113
|
+
"""Get all hash fields."""
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
@abstractmethod
|
|
117
|
+
async def hdel(self, key: str, field: str) -> bool:
|
|
118
|
+
"""Delete hash field."""
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
@abstractmethod
|
|
122
|
+
async def hkeys(self, key: str) -> list[str]:
|
|
123
|
+
"""Get all hash field names."""
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
# List Operations (for audit logs, events)
|
|
127
|
+
|
|
128
|
+
@abstractmethod
|
|
129
|
+
async def lpush(self, key: str, value: str) -> int:
|
|
130
|
+
"""Push value to head of list. Returns new list length."""
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
@abstractmethod
|
|
134
|
+
async def rpush(self, key: str, value: str) -> int:
|
|
135
|
+
"""Push value to tail of list. Returns new list length."""
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
@abstractmethod
|
|
139
|
+
async def lrange(self, key: str, start: int, stop: int) -> list[str]:
|
|
140
|
+
"""Get list range [start, stop]."""
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
@abstractmethod
|
|
144
|
+
async def llen(self, key: str) -> int:
|
|
145
|
+
"""Get list length."""
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
# Sorted Set Operations (for trust scores)
|
|
149
|
+
|
|
150
|
+
@abstractmethod
|
|
151
|
+
async def zadd(
|
|
152
|
+
self,
|
|
153
|
+
key: str,
|
|
154
|
+
score: float,
|
|
155
|
+
member: str,
|
|
156
|
+
) -> bool:
|
|
157
|
+
"""Add member to sorted set with score."""
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
@abstractmethod
|
|
161
|
+
async def zscore(self, key: str, member: str) -> Optional[float]:
|
|
162
|
+
"""Get score of member in sorted set."""
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
@abstractmethod
|
|
166
|
+
async def zrange(
|
|
167
|
+
self,
|
|
168
|
+
key: str,
|
|
169
|
+
start: int,
|
|
170
|
+
stop: int,
|
|
171
|
+
with_scores: bool = False,
|
|
172
|
+
) -> list[str] | list[tuple[str, float]]:
|
|
173
|
+
"""Get sorted set range."""
|
|
174
|
+
pass
|
|
175
|
+
|
|
176
|
+
@abstractmethod
|
|
177
|
+
async def zrangebyscore(
|
|
178
|
+
self,
|
|
179
|
+
key: str,
|
|
180
|
+
min_score: float,
|
|
181
|
+
max_score: float,
|
|
182
|
+
with_scores: bool = False,
|
|
183
|
+
) -> list[str] | list[tuple[str, float]]:
|
|
184
|
+
"""Get sorted set range by score."""
|
|
185
|
+
pass
|
|
186
|
+
|
|
187
|
+
# Atomic Operations
|
|
188
|
+
|
|
189
|
+
@abstractmethod
|
|
190
|
+
async def incr(self, key: str) -> int:
|
|
191
|
+
"""Increment value atomically. Returns new value."""
|
|
192
|
+
pass
|
|
193
|
+
|
|
194
|
+
@abstractmethod
|
|
195
|
+
async def decr(self, key: str) -> int:
|
|
196
|
+
"""Decrement value atomically. Returns new value."""
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
@abstractmethod
|
|
200
|
+
async def incrby(self, key: str, amount: int) -> int:
|
|
201
|
+
"""Increment value by amount. Returns new value."""
|
|
202
|
+
pass
|
|
203
|
+
|
|
204
|
+
# Batch Operations
|
|
205
|
+
|
|
206
|
+
@abstractmethod
|
|
207
|
+
async def mget(self, keys: list[str]) -> list[Optional[str]]:
|
|
208
|
+
"""Get multiple values."""
|
|
209
|
+
pass
|
|
210
|
+
|
|
211
|
+
@abstractmethod
|
|
212
|
+
async def mset(self, mapping: dict[str, str]) -> bool:
|
|
213
|
+
"""Set multiple key-value pairs."""
|
|
214
|
+
pass
|
|
215
|
+
|
|
216
|
+
# Pattern Operations
|
|
217
|
+
|
|
218
|
+
@abstractmethod
|
|
219
|
+
async def keys(self, pattern: str) -> list[str]:
|
|
220
|
+
"""Get keys matching pattern."""
|
|
221
|
+
pass
|
|
222
|
+
|
|
223
|
+
@abstractmethod
|
|
224
|
+
async def scan(
|
|
225
|
+
self,
|
|
226
|
+
cursor: int = 0,
|
|
227
|
+
match: Optional[str] = None,
|
|
228
|
+
count: int = 100,
|
|
229
|
+
) -> tuple[int, list[str]]:
|
|
230
|
+
"""Scan keys with cursor. Returns (new_cursor, keys)."""
|
|
231
|
+
pass
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Redis Storage Provider.
|
|
3
|
+
|
|
4
|
+
Production-ready Redis backend with connection pooling and error handling.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
from .provider import AbstractStorageProvider, StorageConfig
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RedisStorageProvider(AbstractStorageProvider):
|
|
14
|
+
"""
|
|
15
|
+
Redis storage provider.
|
|
16
|
+
|
|
17
|
+
Features:
|
|
18
|
+
- Connection pooling
|
|
19
|
+
- Automatic reconnection
|
|
20
|
+
- TTL support
|
|
21
|
+
- High-performance caching
|
|
22
|
+
|
|
23
|
+
Requires: redis[asyncio] package
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, config: StorageConfig):
|
|
27
|
+
"""Initialize Redis storage."""
|
|
28
|
+
super().__init__(config)
|
|
29
|
+
self._client = None
|
|
30
|
+
self._pool = None
|
|
31
|
+
|
|
32
|
+
async def connect(self) -> None:
|
|
33
|
+
"""Establish connection to Redis."""
|
|
34
|
+
try:
|
|
35
|
+
import redis.asyncio as aioredis
|
|
36
|
+
except ImportError:
|
|
37
|
+
raise ImportError(
|
|
38
|
+
"redis package is required for RedisStorageProvider. "
|
|
39
|
+
"Install with: pip install redis[asyncio]"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Create connection pool
|
|
43
|
+
self._pool = aioredis.ConnectionPool(
|
|
44
|
+
host=self.config.redis_host,
|
|
45
|
+
port=self.config.redis_port,
|
|
46
|
+
db=self.config.redis_db,
|
|
47
|
+
password=self.config.redis_password,
|
|
48
|
+
ssl=self.config.redis_ssl,
|
|
49
|
+
max_connections=self.config.pool_size,
|
|
50
|
+
socket_timeout=self.config.timeout_seconds,
|
|
51
|
+
socket_connect_timeout=self.config.timeout_seconds,
|
|
52
|
+
decode_responses=True,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
self._client = aioredis.Redis(connection_pool=self._pool)
|
|
56
|
+
|
|
57
|
+
# Test connection
|
|
58
|
+
await self._client.ping()
|
|
59
|
+
|
|
60
|
+
async def disconnect(self) -> None:
|
|
61
|
+
"""Close connection to Redis."""
|
|
62
|
+
if self._client:
|
|
63
|
+
await self._client.close()
|
|
64
|
+
if self._pool:
|
|
65
|
+
await self._pool.disconnect()
|
|
66
|
+
|
|
67
|
+
async def health_check(self) -> bool:
|
|
68
|
+
"""Check if Redis is healthy."""
|
|
69
|
+
try:
|
|
70
|
+
if self._client:
|
|
71
|
+
await self._client.ping()
|
|
72
|
+
return True
|
|
73
|
+
except Exception:
|
|
74
|
+
pass
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
# Key-Value Operations
|
|
78
|
+
|
|
79
|
+
async def get(self, key: str) -> Optional[str]:
|
|
80
|
+
"""Get value by key."""
|
|
81
|
+
return await self._client.get(key)
|
|
82
|
+
|
|
83
|
+
async def set(
|
|
84
|
+
self,
|
|
85
|
+
key: str,
|
|
86
|
+
value: str,
|
|
87
|
+
ttl_seconds: Optional[int] = None,
|
|
88
|
+
) -> bool:
|
|
89
|
+
"""Set value with optional TTL."""
|
|
90
|
+
if ttl_seconds is not None:
|
|
91
|
+
return await self._client.setex(key, ttl_seconds, value)
|
|
92
|
+
return await self._client.set(key, value)
|
|
93
|
+
|
|
94
|
+
async def delete(self, key: str) -> bool:
|
|
95
|
+
"""Delete key."""
|
|
96
|
+
result = await self._client.delete(key)
|
|
97
|
+
return result > 0
|
|
98
|
+
|
|
99
|
+
async def exists(self, key: str) -> bool:
|
|
100
|
+
"""Check if key exists."""
|
|
101
|
+
result = await self._client.exists(key)
|
|
102
|
+
return result > 0
|
|
103
|
+
|
|
104
|
+
# Hash Operations
|
|
105
|
+
|
|
106
|
+
async def hget(self, key: str, field: str) -> Optional[str]:
|
|
107
|
+
"""Get hash field value."""
|
|
108
|
+
return await self._client.hget(key, field)
|
|
109
|
+
|
|
110
|
+
async def hset(self, key: str, field: str, value: str) -> bool:
|
|
111
|
+
"""Set hash field value."""
|
|
112
|
+
result = await self._client.hset(key, field, value)
|
|
113
|
+
return result >= 0
|
|
114
|
+
|
|
115
|
+
async def hgetall(self, key: str) -> dict[str, str]:
|
|
116
|
+
"""Get all hash fields."""
|
|
117
|
+
return await self._client.hgetall(key)
|
|
118
|
+
|
|
119
|
+
async def hdel(self, key: str, field: str) -> bool:
|
|
120
|
+
"""Delete hash field."""
|
|
121
|
+
result = await self._client.hdel(key, field)
|
|
122
|
+
return result > 0
|
|
123
|
+
|
|
124
|
+
async def hkeys(self, key: str) -> list[str]:
|
|
125
|
+
"""Get all hash field names."""
|
|
126
|
+
return await self._client.hkeys(key)
|
|
127
|
+
|
|
128
|
+
# List Operations
|
|
129
|
+
|
|
130
|
+
async def lpush(self, key: str, value: str) -> int:
|
|
131
|
+
"""Push value to head of list."""
|
|
132
|
+
return await self._client.lpush(key, value)
|
|
133
|
+
|
|
134
|
+
async def rpush(self, key: str, value: str) -> int:
|
|
135
|
+
"""Push value to tail of list."""
|
|
136
|
+
return await self._client.rpush(key, value)
|
|
137
|
+
|
|
138
|
+
async def lrange(self, key: str, start: int, stop: int) -> list[str]:
|
|
139
|
+
"""Get list range [start, stop]."""
|
|
140
|
+
return await self._client.lrange(key, start, stop)
|
|
141
|
+
|
|
142
|
+
async def llen(self, key: str) -> int:
|
|
143
|
+
"""Get list length."""
|
|
144
|
+
return await self._client.llen(key)
|
|
145
|
+
|
|
146
|
+
# Sorted Set Operations
|
|
147
|
+
|
|
148
|
+
async def zadd(self, key: str, score: float, member: str) -> bool:
|
|
149
|
+
"""Add member to sorted set with score."""
|
|
150
|
+
result = await self._client.zadd(key, {member: score})
|
|
151
|
+
return result >= 0
|
|
152
|
+
|
|
153
|
+
async def zscore(self, key: str, member: str) -> Optional[float]:
|
|
154
|
+
"""Get score of member in sorted set."""
|
|
155
|
+
return await self._client.zscore(key, member)
|
|
156
|
+
|
|
157
|
+
async def zrange(
|
|
158
|
+
self,
|
|
159
|
+
key: str,
|
|
160
|
+
start: int,
|
|
161
|
+
stop: int,
|
|
162
|
+
with_scores: bool = False,
|
|
163
|
+
) -> list[str] | list[tuple[str, float]]:
|
|
164
|
+
"""Get sorted set range."""
|
|
165
|
+
return await self._client.zrange(
|
|
166
|
+
key, start, stop, withscores=with_scores
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
async def zrangebyscore(
|
|
170
|
+
self,
|
|
171
|
+
key: str,
|
|
172
|
+
min_score: float,
|
|
173
|
+
max_score: float,
|
|
174
|
+
with_scores: bool = False,
|
|
175
|
+
) -> list[str] | list[tuple[str, float]]:
|
|
176
|
+
"""Get sorted set range by score."""
|
|
177
|
+
return await self._client.zrangebyscore(
|
|
178
|
+
key, min_score, max_score, withscores=with_scores
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Atomic Operations
|
|
182
|
+
|
|
183
|
+
async def incr(self, key: str) -> int:
|
|
184
|
+
"""Increment value atomically."""
|
|
185
|
+
return await self._client.incr(key)
|
|
186
|
+
|
|
187
|
+
async def decr(self, key: str) -> int:
|
|
188
|
+
"""Decrement value atomically."""
|
|
189
|
+
return await self._client.decr(key)
|
|
190
|
+
|
|
191
|
+
async def incrby(self, key: str, amount: int) -> int:
|
|
192
|
+
"""Increment value by amount."""
|
|
193
|
+
return await self._client.incrby(key, amount)
|
|
194
|
+
|
|
195
|
+
# Batch Operations
|
|
196
|
+
|
|
197
|
+
async def mget(self, keys: list[str]) -> list[Optional[str]]:
|
|
198
|
+
"""Get multiple values."""
|
|
199
|
+
return await self._client.mget(keys)
|
|
200
|
+
|
|
201
|
+
async def mset(self, mapping: dict[str, str]) -> bool:
|
|
202
|
+
"""Set multiple key-value pairs."""
|
|
203
|
+
return await self._client.mset(mapping)
|
|
204
|
+
|
|
205
|
+
# Pattern Operations
|
|
206
|
+
|
|
207
|
+
async def keys(self, pattern: str) -> list[str]:
|
|
208
|
+
"""Get keys matching pattern."""
|
|
209
|
+
return await self._client.keys(pattern)
|
|
210
|
+
|
|
211
|
+
async def scan(
|
|
212
|
+
self,
|
|
213
|
+
cursor: int = 0,
|
|
214
|
+
match: Optional[str] = None,
|
|
215
|
+
count: int = 100,
|
|
216
|
+
) -> tuple[int, list[str]]:
|
|
217
|
+
"""Scan keys with cursor."""
|
|
218
|
+
new_cursor, keys = await self._client.scan(
|
|
219
|
+
cursor=cursor,
|
|
220
|
+
match=match,
|
|
221
|
+
count=count,
|
|
222
|
+
)
|
|
223
|
+
return new_cursor, keys
|
agentmesh/trust/__init__.py
CHANGED
|
@@ -7,7 +7,7 @@ Native A2A and MCP support with transparent protocol translation.
|
|
|
7
7
|
|
|
8
8
|
from .bridge import TrustBridge, ProtocolBridge
|
|
9
9
|
from .handshake import TrustHandshake, HandshakeResult
|
|
10
|
-
from .capability import CapabilityScope, CapabilityGrant
|
|
10
|
+
from .capability import CapabilityScope, CapabilityGrant, CapabilityRegistry
|
|
11
11
|
|
|
12
12
|
__all__ = [
|
|
13
13
|
"TrustBridge",
|
|
@@ -16,4 +16,5 @@ __all__ = [
|
|
|
16
16
|
"HandshakeResult",
|
|
17
17
|
"CapabilityScope",
|
|
18
18
|
"CapabilityGrant",
|
|
19
|
+
"CapabilityRegistry",
|
|
19
20
|
]
|
agentmesh/trust/bridge.py
CHANGED
|
@@ -246,6 +246,43 @@ class ProtocolBridge(BaseModel):
|
|
|
246
246
|
"parameters": params.get("arguments", {}),
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
def add_verification_footer(
|
|
250
|
+
self,
|
|
251
|
+
content: str,
|
|
252
|
+
trust_score: int,
|
|
253
|
+
agent_did: str,
|
|
254
|
+
metadata: Optional[dict] = None
|
|
255
|
+
) -> str:
|
|
256
|
+
"""
|
|
257
|
+
Add AgentMesh verification footer to content.
|
|
258
|
+
|
|
259
|
+
This creates the "viral" verification message that shows
|
|
260
|
+
AgentMesh is securing the interaction.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
content: Original content
|
|
264
|
+
trust_score: Current trust score
|
|
265
|
+
agent_did: Agent DID
|
|
266
|
+
metadata: Optional metadata to include
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Content with verification footer appended
|
|
270
|
+
"""
|
|
271
|
+
footer = (
|
|
272
|
+
f"\n\n> 🔒 Verified by AgentMesh (Trust Score: {trust_score}/1000)\n"
|
|
273
|
+
f"> Agent: {agent_did[:40]}...\n"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
if metadata:
|
|
277
|
+
if "policy" in metadata:
|
|
278
|
+
footer += f"> Policy: {metadata['policy']}\n"
|
|
279
|
+
if "audit" in metadata:
|
|
280
|
+
footer += f"> Audit: {metadata['audit']}\n"
|
|
281
|
+
if "view_log" in metadata:
|
|
282
|
+
footer += f"> [View Audit Log]({metadata['view_log']})\n"
|
|
283
|
+
|
|
284
|
+
return content + footer
|
|
285
|
+
|
|
249
286
|
async def _send(self, peer_did: str, message: Any, protocol: str) -> Any:
|
|
250
287
|
"""Send message via protocol handler."""
|
|
251
288
|
# In production, would dispatch to actual protocol handlers
|