agent_hypervisor 3.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 (60) hide show
  1. agent_hypervisor-3.1.0.dist-info/METADATA +824 -0
  2. agent_hypervisor-3.1.0.dist-info/RECORD +60 -0
  3. agent_hypervisor-3.1.0.dist-info/WHEEL +4 -0
  4. agent_hypervisor-3.1.0.dist-info/entry_points.txt +2 -0
  5. agent_hypervisor-3.1.0.dist-info/licenses/LICENSE +21 -0
  6. hypervisor/__init__.py +160 -0
  7. hypervisor/api/__init__.py +7 -0
  8. hypervisor/api/models.py +285 -0
  9. hypervisor/api/server.py +742 -0
  10. hypervisor/audit/__init__.py +4 -0
  11. hypervisor/audit/commitment.py +76 -0
  12. hypervisor/audit/delta.py +135 -0
  13. hypervisor/audit/gc.py +99 -0
  14. hypervisor/cli/__init__.py +3 -0
  15. hypervisor/cli/formatters.py +99 -0
  16. hypervisor/cli/session_commands.py +200 -0
  17. hypervisor/constants.py +106 -0
  18. hypervisor/core.py +352 -0
  19. hypervisor/integrations/__init__.py +10 -0
  20. hypervisor/integrations/iatp_adapter.py +142 -0
  21. hypervisor/integrations/nexus_adapter.py +108 -0
  22. hypervisor/integrations/verification_adapter.py +122 -0
  23. hypervisor/liability/__init__.py +142 -0
  24. hypervisor/liability/attribution.py +86 -0
  25. hypervisor/liability/ledger.py +121 -0
  26. hypervisor/liability/quarantine.py +119 -0
  27. hypervisor/liability/slashing.py +80 -0
  28. hypervisor/liability/vouching.py +134 -0
  29. hypervisor/models.py +277 -0
  30. hypervisor/observability/__init__.py +27 -0
  31. hypervisor/observability/causal_trace.py +70 -0
  32. hypervisor/observability/event_bus.py +222 -0
  33. hypervisor/observability/prometheus_collector.py +248 -0
  34. hypervisor/observability/saga_span_exporter.py +341 -0
  35. hypervisor/providers.py +121 -0
  36. hypervisor/py.typed +0 -0
  37. hypervisor/reversibility/__init__.py +3 -0
  38. hypervisor/reversibility/registry.py +108 -0
  39. hypervisor/rings/__init__.py +21 -0
  40. hypervisor/rings/breach_detector.py +200 -0
  41. hypervisor/rings/classifier.py +78 -0
  42. hypervisor/rings/elevation.py +219 -0
  43. hypervisor/rings/enforcer.py +97 -0
  44. hypervisor/saga/__init__.py +22 -0
  45. hypervisor/saga/checkpoint.py +110 -0
  46. hypervisor/saga/dsl.py +190 -0
  47. hypervisor/saga/fan_out.py +126 -0
  48. hypervisor/saga/orchestrator.py +229 -0
  49. hypervisor/saga/schema.py +244 -0
  50. hypervisor/saga/state_machine.py +157 -0
  51. hypervisor/security/__init__.py +13 -0
  52. hypervisor/security/kill_switch.py +200 -0
  53. hypervisor/security/rate_limiter.py +190 -0
  54. hypervisor/session/__init__.py +194 -0
  55. hypervisor/session/intent_locks.py +118 -0
  56. hypervisor/session/isolation.py +37 -0
  57. hypervisor/session/sso.py +169 -0
  58. hypervisor/session/vector_clock.py +118 -0
  59. hypervisor/verification/__init__.py +3 -0
  60. hypervisor/verification/history.py +173 -0
@@ -0,0 +1,106 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Centralized constants for the Agent Hypervisor package.
4
+
5
+ All thresholds, limits, and magic numbers used across modules are defined
6
+ here so they can be maintained in a single place. Modules should import
7
+ from ``hypervisor.constants`` rather than hard-coding values locally.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ # ---------------------------------------------------------------------------
13
+ # Ring trust-score thresholds
14
+ # ---------------------------------------------------------------------------
15
+ RING_1_TRUST_THRESHOLD: float = 0.95
16
+ """Minimum effective score (with consensus) for Ring 1 (Privileged)."""
17
+
18
+ RING_2_TRUST_THRESHOLD: float = 0.60
19
+ """Minimum effective score for Ring 2 (Standard)."""
20
+
21
+ RING_1_ENFORCER_THRESHOLD: float = 0.70
22
+ """Trust threshold used by the RingEnforcer for Ring 1 access."""
23
+
24
+ # ---------------------------------------------------------------------------
25
+ # Rate-limiter defaults (requests/sec, burst capacity)
26
+ # ---------------------------------------------------------------------------
27
+ RATE_LIMIT_RING_0: tuple[float, float] = (100.0, 200.0)
28
+ """Ring 0 (Root/SRE): generous rate limit."""
29
+
30
+ RATE_LIMIT_RING_1: tuple[float, float] = (50.0, 100.0)
31
+ """Ring 1 (Privileged): moderate rate limit."""
32
+
33
+ RATE_LIMIT_RING_2: tuple[float, float] = (20.0, 40.0)
34
+ """Ring 2 (Standard): conservative rate limit."""
35
+
36
+ RATE_LIMIT_RING_3: tuple[float, float] = (5.0, 10.0)
37
+ """Ring 3 (Sandbox): strict rate limit."""
38
+
39
+ RATE_LIMIT_FALLBACK: tuple[float, float] = RATE_LIMIT_RING_2
40
+ """Fallback rate limit when a ring is not found in the limits map."""
41
+
42
+ # ---------------------------------------------------------------------------
43
+ # Vouching / sponsorship thresholds
44
+ # ---------------------------------------------------------------------------
45
+ VOUCHING_SCORE_SCALE: float = 1000.0
46
+ """Maximum trust-score scale used by the vouching engine."""
47
+
48
+ VOUCHING_MIN_VOUCHER_SCORE: float = 0.50
49
+ """Minimum score required to sponsor another agent."""
50
+
51
+ VOUCHING_DEFAULT_BOND_PCT: float = 0.20
52
+ """Default percentage of sigma bonded when sponsoring."""
53
+
54
+ VOUCHING_DEFAULT_MAX_EXPOSURE: float = 0.80
55
+ """Maximum exposure percentage for bonding."""
56
+
57
+ # ---------------------------------------------------------------------------
58
+ # Saga orchestrator defaults
59
+ # ---------------------------------------------------------------------------
60
+ SAGA_DEFAULT_MAX_RETRIES: int = 2
61
+ """Default maximum retries per saga step."""
62
+
63
+ SAGA_DEFAULT_RETRY_DELAY_SECONDS: float = 1.0
64
+ """Default delay between saga step retries (multiplied by attempt number)."""
65
+
66
+ SAGA_DEFAULT_STEP_TIMEOUT_SECONDS: int = 300
67
+ """Default timeout for a single saga step (5 minutes)."""
68
+
69
+ # ---------------------------------------------------------------------------
70
+ # Validation limits (models.py)
71
+ # ---------------------------------------------------------------------------
72
+ MAX_AGENT_ID_LENGTH: int = 256
73
+ """Maximum length of an agent identifier string."""
74
+
75
+ MAX_NAME_LENGTH: int = 256
76
+ """Maximum length of resource names."""
77
+
78
+ MAX_API_PATH_LENGTH: int = 2048
79
+ """Maximum length of an API path."""
80
+
81
+ MAX_PARTICIPANTS_LIMIT: int = 1000
82
+ """Maximum number of participants in a session."""
83
+
84
+ MAX_DURATION_LIMIT: int = 604_800
85
+ """Maximum session duration in seconds (7 days)."""
86
+
87
+ MAX_UNDO_WINDOW: int = 86_400
88
+ """Maximum undo window in seconds (24 hours)."""
89
+
90
+ # ---------------------------------------------------------------------------
91
+ # SessionConfig defaults
92
+ # ---------------------------------------------------------------------------
93
+ SESSION_DEFAULT_MIN_EFF_SCORE: float = 0.60
94
+ """Default minimum effective score for session participation."""
95
+
96
+ # ---------------------------------------------------------------------------
97
+ # Risk-weight ranges by ReversibilityLevel
98
+ # ---------------------------------------------------------------------------
99
+ RISK_WEIGHT_FULL: tuple[float, float] = (0.1, 0.3)
100
+ """Risk weight range for fully reversible actions."""
101
+
102
+ RISK_WEIGHT_PARTIAL: tuple[float, float] = (0.5, 0.8)
103
+ """Risk weight range for partially reversible actions."""
104
+
105
+ RISK_WEIGHT_NONE: tuple[float, float] = (0.9, 1.0)
106
+ """Risk weight range for non-reversible actions."""
hypervisor/core.py ADDED
@@ -0,0 +1,352 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Hypervisor — Top-level orchestrator for multi-agent Shared Sessions.
5
+
6
+ Composes all submodules (Session, Liability, Rings, Reversibility,
7
+ Saga, Audit, Verification) into a unified governance runtime.
8
+
9
+ Optionally integrates with external trust scoring and behavioral
10
+ verification backends when adapters are provided.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import logging
16
+ from typing import Any
17
+
18
+ from hypervisor.audit.commitment import CommitmentEngine
19
+ from hypervisor.audit.delta import DeltaEngine
20
+ from hypervisor.audit.gc import EphemeralGC, RetentionPolicy
21
+ from hypervisor.liability.slashing import SlashingEngine
22
+ from hypervisor.liability.vouching import VouchingEngine
23
+ from hypervisor.models import (
24
+ ActionDescriptor,
25
+ ConsistencyMode,
26
+ ExecutionRing,
27
+ SessionConfig,
28
+ SessionState,
29
+ )
30
+ from hypervisor.reversibility.registry import ReversibilityRegistry
31
+ from hypervisor.rings.classifier import ActionClassifier
32
+ from hypervisor.rings.enforcer import RingEnforcer
33
+ from hypervisor.saga.orchestrator import SagaOrchestrator
34
+ from hypervisor.session import SharedSessionObject
35
+ from hypervisor.verification.history import TransactionHistoryVerifier
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+ # States considered inactive (no longer need monitoring)
40
+ _INACTIVE_STATES = frozenset({SessionState.ARCHIVED, SessionState.TERMINATING})
41
+
42
+
43
+ class ManagedSession:
44
+ """A session with all its associated engines wired together."""
45
+
46
+ __slots__ = ("sso", "reversibility", "delta_engine", "saga")
47
+
48
+ def __init__(self, sso: SharedSessionObject) -> None:
49
+ self.sso = sso
50
+ self.reversibility = ReversibilityRegistry(sso.session_id)
51
+ self.delta_engine = DeltaEngine(sso.session_id)
52
+ self.saga = SagaOrchestrator()
53
+
54
+
55
+ class Hypervisor:
56
+ """
57
+ Top-level orchestrator for the Agent Hypervisor.
58
+
59
+ Usage (basic — sigma_raw passed directly):
60
+ hv = Hypervisor()
61
+ session = await hv.create_session(config, creator_did="did:mesh:admin")
62
+ await hv.join_session(session.sso.session_id, "did:mesh:agent-1", sigma_raw=0.85)
63
+
64
+ Usage (enriched — adapters resolve sigma and parse manifests):
65
+ hv = Hypervisor(
66
+ nexus=trust_adapter,
67
+ policy_check=verification_adapter,
68
+ iatp=manifest_adapter,
69
+ )
70
+ """
71
+
72
+ def __init__(
73
+ self,
74
+ retention_policy: RetentionPolicy | None = None,
75
+ max_exposure: float | None = None,
76
+ nexus: Any | None = None,
77
+ policy_check: Any | None = None,
78
+ iatp: Any | None = None,
79
+ ) -> None:
80
+ # Shared engines
81
+ self.vouching = VouchingEngine(max_exposure=max_exposure)
82
+ self.slashing = SlashingEngine(self.vouching)
83
+ self.ring_enforcer = RingEnforcer()
84
+ self.classifier = ActionClassifier()
85
+ self.verifier = TransactionHistoryVerifier()
86
+ self.commitment = CommitmentEngine()
87
+ self.gc = EphemeralGC(retention_policy)
88
+
89
+ # Aliases expected by API layer
90
+ self.commitment_engine = self.commitment
91
+ self.history_verifier = self.verifier
92
+
93
+ # Integration adapters (optional)
94
+ self.nexus = nexus
95
+ self.policy_check = policy_check
96
+ self.iatp = iatp
97
+
98
+ # Active sessions
99
+ self._sessions: dict[str, ManagedSession] = {}
100
+ # Index of session IDs still requiring monitoring (non-archived/terminating)
101
+ self._active_ids: set[str] = set()
102
+
103
+ async def create_session(
104
+ self,
105
+ config: SessionConfig,
106
+ creator_did: str,
107
+ ) -> ManagedSession:
108
+ """Create a new Shared Session."""
109
+ sso = SharedSessionObject(config=config, creator_did=creator_did)
110
+ sso.begin_handshake()
111
+ managed = ManagedSession(sso)
112
+ self._sessions[sso.session_id] = managed
113
+ self._active_ids.add(sso.session_id)
114
+ return managed
115
+
116
+ async def join_session(
117
+ self,
118
+ session_id: str,
119
+ agent_did: str,
120
+ actions: list[ActionDescriptor] | None = None,
121
+ sigma_raw: float = 0.0,
122
+ manifest: Any | None = None,
123
+ agent_history: Any | None = None,
124
+ ) -> ExecutionRing:
125
+ """
126
+ Join an agent to a session via extended IATP handshake.
127
+
128
+ Steps:
129
+ 1. Parse IATP manifest (if adapter + manifest provided)
130
+ 2. Register actions in Reversibility Registry
131
+ 3. Force Strong mode if non-reversible actions exist
132
+ 4. Verify DID transaction history
133
+ 5. Resolve eff_score (Nexus adapter or raw fallback) and assign ring
134
+ """
135
+ managed = self._get_session(session_id)
136
+
137
+ # Step 1: IATP manifest enrichment
138
+ if self.iatp and manifest:
139
+ if isinstance(manifest, dict):
140
+ analysis = self.iatp.analyze_manifest_dict(manifest)
141
+ else:
142
+ analysis = self.iatp.analyze_manifest(manifest)
143
+ # Use manifest actions if none explicitly provided
144
+ if not actions:
145
+ actions = analysis.actions
146
+ # Use IATP sigma hint as fallback
147
+ if sigma_raw == 0.0:
148
+ sigma_raw = analysis.sigma_hint
149
+ logger.debug("IATP manifest parsed for %s: ring_hint=%s", agent_did, analysis.ring_hint)
150
+
151
+ # Step 2: Register actions
152
+ if actions:
153
+ managed.reversibility.register_from_manifest(actions)
154
+
155
+ # Step 3: Mode negotiation
156
+ if managed.reversibility.has_non_reversible_actions():
157
+ managed.sso.force_consistency_mode(ConsistencyMode.STRONG)
158
+
159
+ # Step 4: Verify history
160
+ verification = self.verifier.verify(agent_did)
161
+
162
+ # Step 5: Resolve effective score
163
+ eff_score = sigma_raw
164
+
165
+ # Nexus enrichment: if adapter is available and no explicit sigma given
166
+ if self.nexus and sigma_raw == 0.0:
167
+ eff_score = self.nexus.resolve_sigma(
168
+ agent_did,
169
+ history=agent_history,
170
+ )
171
+ logger.debug("Nexus resolved sigma=%.3f for %s", eff_score, agent_did)
172
+ elif self.nexus and agent_history:
173
+ # Even with explicit sigma, Nexus can verify/enrich
174
+ nexus_sigma = self.nexus.resolve_sigma(
175
+ agent_did,
176
+ history=agent_history,
177
+ )
178
+ # Use the lower of provided vs Nexus (conservative)
179
+ eff_score = min(sigma_raw, nexus_sigma)
180
+
181
+ ring = self.ring_enforcer.compute_ring(eff_score)
182
+
183
+ # Probationary agents get sandbox
184
+ if not verification.is_trustworthy:
185
+ ring = ExecutionRing.RING_3_SANDBOX
186
+
187
+ # Join the session
188
+ managed.sso.join(
189
+ agent_did=agent_did,
190
+ sigma_raw=sigma_raw,
191
+ eff_score=eff_score,
192
+ ring=ring,
193
+ )
194
+
195
+ return ring
196
+
197
+ async def activate_session(self, session_id: str) -> None:
198
+ """Activate a session after handshaking is complete."""
199
+ managed = self._get_session(session_id)
200
+ managed.sso.activate()
201
+
202
+ async def terminate_session(self, session_id: str) -> str | None:
203
+ """
204
+ Terminate a session and commit audit trail.
205
+
206
+ Returns:
207
+ audit log root summary hash, or None if audit disabled
208
+ """
209
+ managed = self._get_session(session_id)
210
+ managed.sso.terminate()
211
+
212
+ hash_chain_root = self._commit_audit(session_id, managed)
213
+ self._cleanup_session(session_id, managed)
214
+
215
+ return hash_chain_root
216
+
217
+ def _commit_audit(self, session_id: str, managed: ManagedSession) -> str | None:
218
+ """Commit audit trail and return hash chain root (None if audit disabled)."""
219
+ if not managed.sso.config.enable_audit:
220
+ return None
221
+ hash_chain_root = managed.delta_engine.compute_hash_chain_root()
222
+ if hash_chain_root:
223
+ self.commitment.commit(
224
+ session_id=session_id,
225
+ hash_chain_root=hash_chain_root,
226
+ participant_dids=[p.agent_did for p in managed.sso.participants],
227
+ delta_count=managed.delta_engine.turn_count,
228
+ )
229
+ return hash_chain_root
230
+
231
+ def _cleanup_session(self, session_id: str, managed: ManagedSession) -> None:
232
+ """Release bonds, purge VFS data, and archive session."""
233
+ self.vouching.release_session_bonds(session_id)
234
+ self.gc.collect(
235
+ session_id=session_id,
236
+ vfs=managed.sso.vfs if hasattr(managed.sso, "vfs") else None,
237
+ delta_engine=managed.delta_engine,
238
+ delta_count=managed.delta_engine.turn_count,
239
+ )
240
+ managed.sso.archive()
241
+ # Remove from active index after archiving
242
+ self._active_ids.discard(session_id)
243
+
244
+ def get_session(self, session_id: str) -> ManagedSession | None:
245
+ return self._sessions.get(session_id)
246
+
247
+ async def verify_behavior(
248
+ self,
249
+ session_id: str,
250
+ agent_did: str,
251
+ claimed_embedding: Any,
252
+ observed_embedding: Any,
253
+ action_id: str | None = None,
254
+ ) -> Any | None:
255
+ """
256
+ Verify agent behavior via Verification adapter.
257
+
258
+ If drift exceeds threshold, automatically slashes the agent and
259
+ reports to Nexus (if adapter is available).
260
+
261
+ Returns:
262
+ DriftCheckResult if Verification adapter is configured, else None.
263
+ """
264
+ if not self.policy_check:
265
+ return None
266
+
267
+ result = self.policy_check.check_behavioral_drift(
268
+ agent_did=agent_did,
269
+ session_id=session_id,
270
+ claimed_embedding=claimed_embedding,
271
+ observed_embedding=observed_embedding,
272
+ action_id=action_id,
273
+ )
274
+
275
+ if result.should_slash:
276
+ managed = self._get_session(session_id)
277
+ participant = managed.sso.get_participant(agent_did)
278
+ # Build scores dict only for the slash path (avoid on healthy agents)
279
+ agent_scores = {
280
+ p.agent_did: p.eff_score
281
+ for p in managed.sso.participants
282
+ }
283
+ self.slashing.slash(
284
+ vouchee_did=agent_did,
285
+ session_id=session_id,
286
+ vouchee_sigma=participant.eff_score,
287
+ risk_weight=0.95,
288
+ reason=f"Verification drift: {result.drift_score:.3f} ({result.severity.value})",
289
+ agent_scores=agent_scores,
290
+ )
291
+ # Propagate to Nexus
292
+ if self.nexus:
293
+ severity = "critical" if result.drift_score >= 0.75 else "high"
294
+ self.nexus.report_slash(
295
+ agent_did=agent_did,
296
+ reason=f"Behavioral drift: {result.drift_score:.3f}",
297
+ severity=severity,
298
+ )
299
+ logger.warning("Agent %s penalized: drift=%.3f", agent_did, result.drift_score)
300
+
301
+ return result
302
+
303
+ @property
304
+ def active_sessions(self) -> list[ManagedSession]:
305
+ # Use the active index to skip archived/terminated sessions
306
+ return [self._sessions[sid] for sid in self._active_ids
307
+ if sid in self._sessions]
308
+
309
+ def _get_session(self, session_id: str) -> ManagedSession:
310
+ managed = self._sessions.get(session_id)
311
+ if not managed:
312
+ raise ValueError(f"Session {session_id} not found")
313
+ return managed
314
+
315
+ async def monitor_sessions(
316
+ self,
317
+ drift_threshold: float = 0.5,
318
+ ) -> list[dict[str, Any]]:
319
+ """
320
+ Batch-monitor all active sessions with early exits.
321
+
322
+ Skips archived/terminated sessions via the active index and skips
323
+ healthy agents (those with eff_score above the drift threshold) to
324
+ reduce per-iteration overhead.
325
+
326
+ Returns a list of issues found (empty if all healthy).
327
+ """
328
+ issues: list[dict[str, Any]] = []
329
+ # Iterate only over active session IDs (O(active) not O(total))
330
+ for sid in list(self._active_ids):
331
+ managed = self._sessions.get(sid)
332
+ if managed is None:
333
+ self._active_ids.discard(sid)
334
+ continue
335
+ state = managed.sso.state
336
+ # Early exit: skip sessions that have transitioned to inactive
337
+ if state in _INACTIVE_STATES:
338
+ self._active_ids.discard(sid)
339
+ continue
340
+ # Batch-check participants; skip healthy agents
341
+ for p in managed.sso.participants:
342
+ if p.eff_score >= drift_threshold:
343
+ continue
344
+ # Only flag agents below threshold
345
+ issues.append({
346
+ "session_id": sid,
347
+ "agent_did": p.agent_did,
348
+ "eff_score": p.eff_score,
349
+ "ring": p.ring,
350
+ "state": state.value,
351
+ })
352
+ return issues
@@ -0,0 +1,10 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Hypervisor Integration Adapters
5
+
6
+ Bridges between the Hypervisor and other Agent-OS modules:
7
+ - Nexus (trust scoring) → ring assignment
8
+ - Verification (behavioral verification) → drift-triggered penalty
9
+ - IATP (capability manifests) → reversibility + trust enrichment
10
+ """
@@ -0,0 +1,142 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ IATP Integration Stub — Capability Manifest parsing.
5
+
6
+ Provides the interface for parsing agent capability manifests
7
+ into Hypervisor-compatible action descriptors and ring hints.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass, field
13
+ from datetime import UTC, datetime
14
+ from enum import Enum
15
+ from typing import Any, Protocol
16
+
17
+ from hypervisor.models import (
18
+ ActionDescriptor,
19
+ ExecutionRing,
20
+ ReversibilityLevel,
21
+ )
22
+
23
+
24
+ class IATPManifest(Protocol):
25
+ """Protocol for a capability manifest."""
26
+
27
+ agent_id: str
28
+ trust_level: Any
29
+ capabilities: Any
30
+ scopes: list[str]
31
+
32
+ def calculate_trust_score(self) -> int: ...
33
+
34
+
35
+ class IATPTrustLevel(str, Enum):
36
+ VERIFIED_PARTNER = "verified_partner"
37
+ TRUSTED = "trusted"
38
+ STANDARD = "standard"
39
+ UNKNOWN = "unknown"
40
+ UNTRUSTED = "untrusted"
41
+
42
+
43
+ TRUST_LEVEL_RING_HINTS = {
44
+ IATPTrustLevel.VERIFIED_PARTNER: ExecutionRing.RING_1_PRIVILEGED,
45
+ IATPTrustLevel.TRUSTED: ExecutionRing.RING_2_STANDARD,
46
+ IATPTrustLevel.STANDARD: ExecutionRing.RING_2_STANDARD,
47
+ IATPTrustLevel.UNKNOWN: ExecutionRing.RING_3_SANDBOX,
48
+ IATPTrustLevel.UNTRUSTED: ExecutionRing.RING_3_SANDBOX,
49
+ }
50
+
51
+ REVERSIBILITY_MAP = {
52
+ "full": ReversibilityLevel.FULL,
53
+ "partial": ReversibilityLevel.PARTIAL,
54
+ "none": ReversibilityLevel.NONE,
55
+ }
56
+
57
+
58
+ @dataclass
59
+ class ManifestAnalysis:
60
+ """Result of analyzing a capability manifest."""
61
+
62
+ agent_did: str
63
+ trust_level: IATPTrustLevel
64
+ ring_hint: ExecutionRing
65
+ iatp_trust_score: int
66
+ sigma_hint: float
67
+ actions: list[ActionDescriptor]
68
+ scopes: list[str]
69
+ has_reversible_actions: bool
70
+ has_non_reversible_actions: bool
71
+ analyzed_at: datetime = field(default_factory=lambda: datetime.now(UTC))
72
+
73
+
74
+ class IATPAdapter:
75
+ """Stub adapter for capability manifest parsing."""
76
+
77
+ def __init__(self) -> None:
78
+ self._manifest_cache: dict[str, ManifestAnalysis] = {}
79
+
80
+ def analyze_manifest(self, manifest: IATPManifest) -> ManifestAnalysis:
81
+ """Analyze a manifest and extract ring hints and actions."""
82
+ agent_did = manifest.agent_id
83
+ trust_str = str(getattr(manifest.trust_level, "value", manifest.trust_level))
84
+ try:
85
+ trust_level = IATPTrustLevel(trust_str)
86
+ except ValueError:
87
+ trust_level = IATPTrustLevel.UNKNOWN
88
+ ring_hint = TRUST_LEVEL_RING_HINTS.get(trust_level, ExecutionRing.RING_3_SANDBOX)
89
+ iatp_score = manifest.calculate_trust_score()
90
+ import math
91
+ if not isinstance(iatp_score, (int, float)) or not math.isfinite(iatp_score):
92
+ iatp_score = 0.0
93
+ iatp_score = min(max(iatp_score, 0.0), 100.0)
94
+ sigma_hint = min(max(iatp_score / 10.0, 0.0), 1.0)
95
+ analysis = ManifestAnalysis(
96
+ agent_did=agent_did, trust_level=trust_level, ring_hint=ring_hint,
97
+ iatp_trust_score=iatp_score, sigma_hint=sigma_hint, actions=[],
98
+ scopes=list(manifest.scopes) if manifest.scopes else [],
99
+ has_reversible_actions=False, has_non_reversible_actions=False,
100
+ )
101
+ self._manifest_cache[agent_did] = analysis
102
+ return analysis
103
+
104
+ def analyze_manifest_dict(self, manifest_dict: dict) -> ManifestAnalysis:
105
+ """Analyze a manifest provided as a dictionary."""
106
+ agent_did = manifest_dict.get("agent_id", "unknown")
107
+ trust_str = manifest_dict.get("trust_level", "unknown")
108
+ try:
109
+ trust_level = IATPTrustLevel(trust_str)
110
+ except ValueError:
111
+ trust_level = IATPTrustLevel.UNKNOWN
112
+ ring_hint = TRUST_LEVEL_RING_HINTS.get(trust_level, ExecutionRing.RING_3_SANDBOX)
113
+ iatp_score = manifest_dict.get("trust_score", 5)
114
+ import math
115
+ if not isinstance(iatp_score, (int, float)) or not math.isfinite(iatp_score):
116
+ iatp_score = 0.0
117
+ iatp_score = min(max(iatp_score, 0.0), 100.0)
118
+ sigma_hint = min(max(iatp_score / 10.0, 0.0), 1.0)
119
+ actions = []
120
+ for cap in manifest_dict.get("actions", []):
121
+ rev_str = cap.get("reversibility", "none")
122
+ actions.append(ActionDescriptor(
123
+ action_id=cap.get("action_id", "unknown"),
124
+ name=cap.get("name", ""),
125
+ execute_api=cap.get("execute_api", ""),
126
+ undo_api=cap.get("undo_api"),
127
+ reversibility=REVERSIBILITY_MAP.get(rev_str, ReversibilityLevel.NONE),
128
+ is_read_only=cap.get("is_read_only", False),
129
+ is_admin=cap.get("is_admin", False),
130
+ ))
131
+ analysis = ManifestAnalysis(
132
+ agent_did=agent_did, trust_level=trust_level, ring_hint=ring_hint,
133
+ iatp_trust_score=iatp_score, sigma_hint=sigma_hint, actions=actions,
134
+ scopes=manifest_dict.get("scopes", []),
135
+ has_reversible_actions=any(a.reversibility != ReversibilityLevel.NONE for a in actions),
136
+ has_non_reversible_actions=any(a.reversibility == ReversibilityLevel.NONE and not a.is_read_only for a in actions),
137
+ )
138
+ self._manifest_cache[agent_did] = analysis
139
+ return analysis
140
+
141
+ def get_cached_analysis(self, agent_did: str) -> ManifestAnalysis | None:
142
+ return self._manifest_cache.get(agent_did)