agent_os_kernel 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 (337) hide show
  1. agent_control_plane/__init__.py +662 -0
  2. agent_control_plane/a2a_adapter.py +543 -0
  3. agent_control_plane/adapter.py +417 -0
  4. agent_control_plane/agent_hibernation.py +394 -0
  5. agent_control_plane/agent_kernel.py +470 -0
  6. agent_control_plane/compliance.py +720 -0
  7. agent_control_plane/constraint_graphs.py +478 -0
  8. agent_control_plane/control_plane.py +854 -0
  9. agent_control_plane/example_executors.py +195 -0
  10. agent_control_plane/execution_engine.py +231 -0
  11. agent_control_plane/flight_recorder.py +846 -0
  12. agent_control_plane/governance_layer.py +435 -0
  13. agent_control_plane/hf_utils.py +563 -0
  14. agent_control_plane/interfaces/__init__.py +55 -0
  15. agent_control_plane/interfaces/kernel_interface.py +361 -0
  16. agent_control_plane/interfaces/plugin_interface.py +497 -0
  17. agent_control_plane/interfaces/protocol_interfaces.py +387 -0
  18. agent_control_plane/kernel_space.py +1009 -0
  19. agent_control_plane/langchain_adapter.py +424 -0
  20. agent_control_plane/lifecycle.py +3113 -0
  21. agent_control_plane/mcp_adapter.py +653 -0
  22. agent_control_plane/ml_safety.py +563 -0
  23. agent_control_plane/multimodal.py +727 -0
  24. agent_control_plane/mute_agent.py +422 -0
  25. agent_control_plane/observability.py +787 -0
  26. agent_control_plane/orchestrator.py +482 -0
  27. agent_control_plane/plugin_registry.py +750 -0
  28. agent_control_plane/policy_engine.py +954 -0
  29. agent_control_plane/process_isolation.py +777 -0
  30. agent_control_plane/shadow_mode.py +310 -0
  31. agent_control_plane/signals.py +493 -0
  32. agent_control_plane/supervisor_agents.py +430 -0
  33. agent_control_plane/time_travel_debugger.py +557 -0
  34. agent_control_plane/tool_registry.py +452 -0
  35. agent_control_plane/vfs.py +697 -0
  36. agent_kernel/__init__.py +69 -0
  37. agent_kernel/analyzer.py +435 -0
  38. agent_kernel/auditor.py +36 -0
  39. agent_kernel/completeness_auditor.py +237 -0
  40. agent_kernel/detector.py +203 -0
  41. agent_kernel/kernel.py +744 -0
  42. agent_kernel/memory_manager.py +85 -0
  43. agent_kernel/models.py +374 -0
  44. agent_kernel/nudge_mechanism.py +263 -0
  45. agent_kernel/outcome_analyzer.py +338 -0
  46. agent_kernel/patcher.py +582 -0
  47. agent_kernel/semantic_analyzer.py +316 -0
  48. agent_kernel/semantic_purge.py +349 -0
  49. agent_kernel/simulator.py +449 -0
  50. agent_kernel/teacher.py +85 -0
  51. agent_kernel/triage.py +152 -0
  52. agent_os/__init__.py +409 -0
  53. agent_os/_adversarial_impl.py +200 -0
  54. agent_os/_circuit_breaker_impl.py +232 -0
  55. agent_os/_mcp_metrics.py +193 -0
  56. agent_os/adversarial.py +20 -0
  57. agent_os/agents_compat.py +490 -0
  58. agent_os/audit_logger.py +135 -0
  59. agent_os/base_agent.py +651 -0
  60. agent_os/circuit_breaker.py +34 -0
  61. agent_os/cli/__init__.py +659 -0
  62. agent_os/cli/cmd_audit.py +128 -0
  63. agent_os/cli/cmd_init.py +152 -0
  64. agent_os/cli/cmd_policy.py +41 -0
  65. agent_os/cli/cmd_policy_gen.py +180 -0
  66. agent_os/cli/cmd_validate.py +258 -0
  67. agent_os/cli/mcp_scan.py +265 -0
  68. agent_os/cli/output.py +192 -0
  69. agent_os/cli/policy_checker.py +330 -0
  70. agent_os/compat.py +74 -0
  71. agent_os/constraint_graph.py +234 -0
  72. agent_os/content_governance.py +140 -0
  73. agent_os/context_budget.py +305 -0
  74. agent_os/credential_redactor.py +224 -0
  75. agent_os/diff_policy.py +89 -0
  76. agent_os/egress_policy.py +159 -0
  77. agent_os/escalation.py +276 -0
  78. agent_os/event_bus.py +124 -0
  79. agent_os/exceptions.py +180 -0
  80. agent_os/execution_context_policy.py +141 -0
  81. agent_os/github_enterprise.py +96 -0
  82. agent_os/health.py +20 -0
  83. agent_os/integrations/__init__.py +279 -0
  84. agent_os/integrations/a2a_adapter.py +279 -0
  85. agent_os/integrations/agent_lightning/__init__.py +30 -0
  86. agent_os/integrations/anthropic_adapter.py +420 -0
  87. agent_os/integrations/autogen_adapter.py +620 -0
  88. agent_os/integrations/base.py +1137 -0
  89. agent_os/integrations/compat.py +229 -0
  90. agent_os/integrations/config.py +98 -0
  91. agent_os/integrations/conversation_guardian.py +957 -0
  92. agent_os/integrations/crewai_adapter.py +467 -0
  93. agent_os/integrations/drift_detector.py +425 -0
  94. agent_os/integrations/dry_run.py +124 -0
  95. agent_os/integrations/escalation.py +582 -0
  96. agent_os/integrations/gemini_adapter.py +364 -0
  97. agent_os/integrations/google_adk_adapter.py +633 -0
  98. agent_os/integrations/guardrails_adapter.py +394 -0
  99. agent_os/integrations/health.py +197 -0
  100. agent_os/integrations/langchain_adapter.py +654 -0
  101. agent_os/integrations/llamafirewall.py +343 -0
  102. agent_os/integrations/llamaindex_adapter.py +188 -0
  103. agent_os/integrations/logging.py +191 -0
  104. agent_os/integrations/maf_adapter.py +631 -0
  105. agent_os/integrations/mistral_adapter.py +365 -0
  106. agent_os/integrations/openai_adapter.py +816 -0
  107. agent_os/integrations/openai_agents_sdk.py +406 -0
  108. agent_os/integrations/policy_compose.py +171 -0
  109. agent_os/integrations/profiling.py +144 -0
  110. agent_os/integrations/pydantic_ai_adapter.py +420 -0
  111. agent_os/integrations/rate_limiter.py +130 -0
  112. agent_os/integrations/rbac.py +143 -0
  113. agent_os/integrations/registry.py +113 -0
  114. agent_os/integrations/scope_guard.py +303 -0
  115. agent_os/integrations/semantic_kernel_adapter.py +769 -0
  116. agent_os/integrations/smolagents_adapter.py +629 -0
  117. agent_os/integrations/templates.py +178 -0
  118. agent_os/integrations/token_budget.py +134 -0
  119. agent_os/integrations/tool_aliases.py +190 -0
  120. agent_os/integrations/webhooks.py +177 -0
  121. agent_os/lite.py +208 -0
  122. agent_os/mcp_gateway.py +385 -0
  123. agent_os/mcp_message_signer.py +273 -0
  124. agent_os/mcp_protocols.py +161 -0
  125. agent_os/mcp_response_scanner.py +232 -0
  126. agent_os/mcp_security.py +924 -0
  127. agent_os/mcp_session_auth.py +231 -0
  128. agent_os/mcp_sliding_rate_limiter.py +184 -0
  129. agent_os/memory_guard.py +409 -0
  130. agent_os/metrics.py +134 -0
  131. agent_os/mute.py +428 -0
  132. agent_os/mute_agent.py +209 -0
  133. agent_os/policies/__init__.py +77 -0
  134. agent_os/policies/async_evaluator.py +275 -0
  135. agent_os/policies/backends.py +670 -0
  136. agent_os/policies/bridge.py +169 -0
  137. agent_os/policies/budget.py +85 -0
  138. agent_os/policies/cli.py +294 -0
  139. agent_os/policies/conflict_resolution.py +270 -0
  140. agent_os/policies/data_classification.py +252 -0
  141. agent_os/policies/evaluator.py +239 -0
  142. agent_os/policies/policy_schema.json +228 -0
  143. agent_os/policies/rate_limiting.py +145 -0
  144. agent_os/policies/schema.py +115 -0
  145. agent_os/policies/shared.py +331 -0
  146. agent_os/prompt_injection.py +694 -0
  147. agent_os/providers.py +182 -0
  148. agent_os/py.typed +0 -0
  149. agent_os/retry.py +81 -0
  150. agent_os/reversibility.py +251 -0
  151. agent_os/sandbox.py +432 -0
  152. agent_os/sandbox_provider.py +140 -0
  153. agent_os/secure_codegen.py +525 -0
  154. agent_os/security_skills.py +538 -0
  155. agent_os/semantic_policy.py +422 -0
  156. agent_os/server/__init__.py +15 -0
  157. agent_os/server/__main__.py +25 -0
  158. agent_os/server/app.py +277 -0
  159. agent_os/server/models.py +104 -0
  160. agent_os/shift_left_metrics.py +130 -0
  161. agent_os/stateless.py +742 -0
  162. agent_os/supervisor.py +148 -0
  163. agent_os/task_outcome.py +148 -0
  164. agent_os/transparency.py +181 -0
  165. agent_os/trust_root.py +128 -0
  166. agent_os_kernel-3.1.0.dist-info/METADATA +1269 -0
  167. agent_os_kernel-3.1.0.dist-info/RECORD +337 -0
  168. agent_os_kernel-3.1.0.dist-info/WHEEL +4 -0
  169. agent_os_kernel-3.1.0.dist-info/entry_points.txt +2 -0
  170. agent_os_kernel-3.1.0.dist-info/licenses/LICENSE +21 -0
  171. agent_os_observability/__init__.py +27 -0
  172. agent_os_observability/dashboards.py +898 -0
  173. agent_os_observability/metrics.py +398 -0
  174. agent_os_observability/server.py +223 -0
  175. agent_os_observability/tracer.py +232 -0
  176. agent_primitives/__init__.py +24 -0
  177. agent_primitives/failures.py +84 -0
  178. agent_primitives/py.typed +0 -0
  179. amb_core/__init__.py +177 -0
  180. amb_core/adapters/__init__.py +57 -0
  181. amb_core/adapters/aws_sqs_broker.py +376 -0
  182. amb_core/adapters/azure_servicebus_broker.py +340 -0
  183. amb_core/adapters/kafka_broker.py +260 -0
  184. amb_core/adapters/nats_broker.py +285 -0
  185. amb_core/adapters/rabbitmq_broker.py +235 -0
  186. amb_core/adapters/redis_broker.py +262 -0
  187. amb_core/broker.py +145 -0
  188. amb_core/bus.py +481 -0
  189. amb_core/cloudevents.py +509 -0
  190. amb_core/dlq.py +345 -0
  191. amb_core/hf_utils.py +536 -0
  192. amb_core/memory_broker.py +410 -0
  193. amb_core/models.py +141 -0
  194. amb_core/persistence.py +529 -0
  195. amb_core/schema.py +294 -0
  196. amb_core/tracing.py +358 -0
  197. atr/__init__.py +640 -0
  198. atr/access.py +348 -0
  199. atr/composition.py +645 -0
  200. atr/decorator.py +357 -0
  201. atr/executor.py +384 -0
  202. atr/health.py +557 -0
  203. atr/hf_utils.py +449 -0
  204. atr/injection.py +422 -0
  205. atr/metrics.py +440 -0
  206. atr/policies.py +403 -0
  207. atr/py.typed +2 -0
  208. atr/registry.py +452 -0
  209. atr/schema.py +480 -0
  210. atr/tools/safe/__init__.py +75 -0
  211. atr/tools/safe/calculator.py +467 -0
  212. atr/tools/safe/datetime_tool.py +443 -0
  213. atr/tools/safe/file_reader.py +402 -0
  214. atr/tools/safe/http_client.py +316 -0
  215. atr/tools/safe/json_parser.py +374 -0
  216. atr/tools/safe/text_tool.py +537 -0
  217. atr/tools/safe/toolkit.py +175 -0
  218. caas/__init__.py +162 -0
  219. caas/api/__init__.py +7 -0
  220. caas/api/server.py +1328 -0
  221. caas/caching.py +834 -0
  222. caas/cli.py +210 -0
  223. caas/conversation.py +223 -0
  224. caas/decay.py +72 -0
  225. caas/detection/__init__.py +9 -0
  226. caas/detection/detector.py +238 -0
  227. caas/enrichment.py +130 -0
  228. caas/gateway/__init__.py +27 -0
  229. caas/gateway/trust_gateway.py +474 -0
  230. caas/hf_utils.py +479 -0
  231. caas/ingestion/__init__.py +23 -0
  232. caas/ingestion/processors.py +253 -0
  233. caas/ingestion/structure_parser.py +188 -0
  234. caas/models.py +356 -0
  235. caas/pragmatic_truth.py +444 -0
  236. caas/routing/__init__.py +10 -0
  237. caas/routing/heuristic_router.py +58 -0
  238. caas/storage/__init__.py +9 -0
  239. caas/storage/store.py +389 -0
  240. caas/triad.py +213 -0
  241. caas/tuning/__init__.py +9 -0
  242. caas/tuning/tuner.py +329 -0
  243. caas/vfs/__init__.py +14 -0
  244. caas/vfs/filesystem.py +452 -0
  245. cmvk/__init__.py +218 -0
  246. cmvk/audit.py +402 -0
  247. cmvk/benchmarks.py +478 -0
  248. cmvk/constitutional.py +904 -0
  249. cmvk/hf_utils.py +301 -0
  250. cmvk/metrics.py +473 -0
  251. cmvk/profiles.py +300 -0
  252. cmvk/py.typed +0 -0
  253. cmvk/types.py +12 -0
  254. cmvk/verification.py +956 -0
  255. emk/__init__.py +89 -0
  256. emk/causal.py +352 -0
  257. emk/hf_utils.py +421 -0
  258. emk/indexer.py +83 -0
  259. emk/py.typed +0 -0
  260. emk/schema.py +204 -0
  261. emk/sleep_cycle.py +347 -0
  262. emk/store.py +281 -0
  263. iatp/__init__.py +166 -0
  264. iatp/attestation.py +461 -0
  265. iatp/cli.py +317 -0
  266. iatp/hf_utils.py +472 -0
  267. iatp/ipc_pipes.py +580 -0
  268. iatp/main.py +412 -0
  269. iatp/models/__init__.py +447 -0
  270. iatp/policy_engine.py +337 -0
  271. iatp/py.typed +2 -0
  272. iatp/recovery.py +321 -0
  273. iatp/security/__init__.py +270 -0
  274. iatp/sidecar/__init__.py +519 -0
  275. iatp/telemetry/__init__.py +164 -0
  276. iatp/tests/__init__.py +1 -0
  277. iatp/tests/test_attestation.py +370 -0
  278. iatp/tests/test_cli.py +131 -0
  279. iatp/tests/test_ed25519_attestation.py +211 -0
  280. iatp/tests/test_models.py +130 -0
  281. iatp/tests/test_policy_engine.py +347 -0
  282. iatp/tests/test_recovery.py +281 -0
  283. iatp/tests/test_security.py +222 -0
  284. iatp/tests/test_sidecar.py +167 -0
  285. iatp/tests/test_telemetry.py +175 -0
  286. mcp_kernel_server/__init__.py +28 -0
  287. mcp_kernel_server/cli.py +274 -0
  288. mcp_kernel_server/resources.py +217 -0
  289. mcp_kernel_server/server.py +564 -0
  290. mcp_kernel_server/tools.py +1174 -0
  291. mute_agent/__init__.py +68 -0
  292. mute_agent/core/__init__.py +1 -0
  293. mute_agent/core/execution_agent.py +166 -0
  294. mute_agent/core/handshake_protocol.py +201 -0
  295. mute_agent/core/reasoning_agent.py +238 -0
  296. mute_agent/knowledge_graph/__init__.py +1 -0
  297. mute_agent/knowledge_graph/graph_elements.py +65 -0
  298. mute_agent/knowledge_graph/multidimensional_graph.py +170 -0
  299. mute_agent/knowledge_graph/subgraph.py +224 -0
  300. mute_agent/listener/__init__.py +43 -0
  301. mute_agent/listener/adapters/__init__.py +31 -0
  302. mute_agent/listener/adapters/base_adapter.py +189 -0
  303. mute_agent/listener/adapters/caas_adapter.py +344 -0
  304. mute_agent/listener/adapters/control_plane_adapter.py +436 -0
  305. mute_agent/listener/adapters/iatp_adapter.py +332 -0
  306. mute_agent/listener/adapters/scak_adapter.py +251 -0
  307. mute_agent/listener/listener.py +610 -0
  308. mute_agent/listener/state_observer.py +436 -0
  309. mute_agent/listener/threshold_config.py +313 -0
  310. mute_agent/super_system/__init__.py +1 -0
  311. mute_agent/super_system/router.py +204 -0
  312. mute_agent/visualization/__init__.py +10 -0
  313. mute_agent/visualization/graph_debugger.py +502 -0
  314. nexus/README.md +60 -0
  315. nexus/__init__.py +51 -0
  316. nexus/arbiter.py +359 -0
  317. nexus/client.py +466 -0
  318. nexus/dmz.py +444 -0
  319. nexus/escrow.py +430 -0
  320. nexus/exceptions.py +286 -0
  321. nexus/pyproject.toml +36 -0
  322. nexus/registry.py +393 -0
  323. nexus/reputation.py +425 -0
  324. nexus/schemas/__init__.py +51 -0
  325. nexus/schemas/compliance.py +276 -0
  326. nexus/schemas/escrow.py +251 -0
  327. nexus/schemas/manifest.py +225 -0
  328. nexus/schemas/receipt.py +208 -0
  329. nexus/tests/__init__.py +0 -0
  330. nexus/tests/conftest.py +146 -0
  331. nexus/tests/test_arbiter.py +192 -0
  332. nexus/tests/test_dmz.py +194 -0
  333. nexus/tests/test_escrow.py +276 -0
  334. nexus/tests/test_exceptions.py +225 -0
  335. nexus/tests/test_registry.py +232 -0
  336. nexus/tests/test_reputation.py +328 -0
  337. nexus/tests/test_schemas.py +295 -0
@@ -0,0 +1,409 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Memory & Context Poisoning Detection — OWASP ASI06.
4
+
5
+ Guards agent memory stores (RAG, episodic, working memory) against
6
+ poisoning attacks where adversaries inject malicious data to manipulate
7
+ agent behaviour.
8
+
9
+ Public Preview protections:
10
+ - **Hash integrity**: SHA-256 hash per memory entry; detects tampering.
11
+ - **Injection pattern detection**: Blocks prompt-injection payloads
12
+ written into memory.
13
+ - **Content validation**: Rejects entries with dangerous code or
14
+ excessive special-character manipulation.
15
+ - **Write audit trail**: Logs every memory write with timestamp and
16
+ source for forensic review.
17
+
18
+ Architecture:
19
+ MemoryGuard
20
+ ├─ validate_write() — pre-write content screening
21
+ ├─ verify_integrity() — post-read hash verification
22
+ └─ scan_memory() — batch scan for poisoning indicators
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import hashlib
28
+ import logging
29
+ import re
30
+ import unicodedata
31
+ from collections.abc import Sequence
32
+ from dataclasses import dataclass, field
33
+ from datetime import datetime, timezone
34
+ from enum import Enum
35
+
36
+ logger = logging.getLogger(__name__)
37
+
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # Data models
41
+ # ---------------------------------------------------------------------------
42
+
43
+ class AlertSeverity(Enum):
44
+ """Severity level for memory poisoning alerts."""
45
+ LOW = "low"
46
+ MEDIUM = "medium"
47
+ HIGH = "high"
48
+ CRITICAL = "critical"
49
+
50
+
51
+ class AlertType(Enum):
52
+ """Classification of a memory poisoning alert."""
53
+ INJECTION_PATTERN = "injection_pattern"
54
+ CODE_INJECTION = "code_injection"
55
+ INTEGRITY_VIOLATION = "integrity_violation"
56
+ UNICODE_MANIPULATION = "unicode_manipulation"
57
+ EXCESSIVE_SPECIAL_CHARS = "excessive_special_chars"
58
+
59
+
60
+ @dataclass
61
+ class MemoryEntry:
62
+ """A single entry in agent memory with integrity metadata.
63
+
64
+ Attributes:
65
+ content: The text content stored in memory.
66
+ source: Identifier of the component that wrote this entry.
67
+ timestamp: UTC timestamp of when the entry was created.
68
+ content_hash: SHA-256 hex digest of ``content``.
69
+ """
70
+ content: str
71
+ source: str
72
+ timestamp: datetime
73
+ content_hash: str
74
+
75
+ @staticmethod
76
+ def compute_hash(content: str) -> str:
77
+ """Compute SHA-256 hash of content."""
78
+ return hashlib.sha256(content.encode("utf-8")).hexdigest()
79
+
80
+ @classmethod
81
+ def create(cls, content: str, source: str) -> MemoryEntry:
82
+ """Factory that auto-generates timestamp and hash."""
83
+ return cls(
84
+ content=content,
85
+ source=source,
86
+ timestamp=datetime.now(timezone.utc),
87
+ content_hash=cls.compute_hash(content),
88
+ )
89
+
90
+
91
+ @dataclass
92
+ class Alert:
93
+ """A poisoning indicator found during memory scanning.
94
+
95
+ Attributes:
96
+ alert_type: Classification of the alert.
97
+ severity: How critical the finding is.
98
+ message: Human-readable description.
99
+ entry_source: Source field of the offending entry (if available).
100
+ matched_pattern: The pattern that triggered this alert.
101
+ """
102
+ alert_type: AlertType
103
+ severity: AlertSeverity
104
+ message: str
105
+ entry_source: str | None = None
106
+ matched_pattern: str | None = None
107
+
108
+
109
+ @dataclass
110
+ class ValidationResult:
111
+ """Outcome of a memory write validation.
112
+
113
+ Attributes:
114
+ allowed: Whether the write should be permitted.
115
+ alerts: Any alerts raised during validation.
116
+ """
117
+ allowed: bool
118
+ alerts: list[Alert] = field(default_factory=list)
119
+
120
+
121
+ @dataclass
122
+ class AuditRecord:
123
+ """Immutable record of a memory write attempt.
124
+
125
+ Attributes:
126
+ timestamp: When the write was attempted.
127
+ source: Component that requested the write.
128
+ content_hash: SHA-256 of the content.
129
+ allowed: Whether the write was permitted.
130
+ alerts: Alerts raised (may be empty).
131
+ """
132
+ timestamp: datetime
133
+ source: str
134
+ content_hash: str
135
+ allowed: bool
136
+ alerts: list[Alert] = field(default_factory=list)
137
+
138
+
139
+ # ---------------------------------------------------------------------------
140
+ # Injection patterns (CE basics)
141
+ # ---------------------------------------------------------------------------
142
+
143
+ _INJECTION_PATTERNS: list[re.Pattern[str]] = [
144
+ re.compile(r"ignore\s+(all\s+)?previous\s+instructions", re.IGNORECASE),
145
+ re.compile(r"you\s+are\s+now\b", re.IGNORECASE),
146
+ re.compile(r"system\s*prompt\s*:", re.IGNORECASE),
147
+ re.compile(r"disregard\s+(all\s+)?(prior|above)\s+", re.IGNORECASE),
148
+ re.compile(r"forget\s+(everything|all|your)\s+", re.IGNORECASE),
149
+ re.compile(r"new\s+instructions?\s*:", re.IGNORECASE),
150
+ re.compile(r"override\s+(previous\s+)?instructions", re.IGNORECASE),
151
+ ]
152
+
153
+ _CODE_INJECTION_PATTERNS: list[re.Pattern[str]] = [
154
+ re.compile(r"```\s*python\s*\n\s*import\s+os\b", re.IGNORECASE),
155
+ re.compile(r"```\s*python\s*\n\s*import\s+subprocess\b", re.IGNORECASE),
156
+ re.compile(r"```\s*python\s*\n\s*import\s+shutil\b", re.IGNORECASE),
157
+ re.compile(r"exec\s*\(", re.IGNORECASE),
158
+ re.compile(r"eval\s*\(", re.IGNORECASE),
159
+ re.compile(r"__import__\s*\(", re.IGNORECASE),
160
+ ]
161
+
162
+ # Fraction of characters that are "special" before we flag the entry
163
+ _SPECIAL_CHAR_THRESHOLD = 0.3
164
+
165
+
166
+ # ---------------------------------------------------------------------------
167
+ # MemoryGuard
168
+ # ---------------------------------------------------------------------------
169
+
170
+ class MemoryGuard:
171
+ """Guards agent memory against poisoning attacks (OWASP ASI06).
172
+
173
+ Usage::
174
+
175
+ guard = MemoryGuard()
176
+ result = guard.validate_write("some content", source="rag-loader")
177
+ if result.allowed:
178
+ store.save(MemoryEntry.create("some content", "rag-loader"))
179
+ """
180
+
181
+ def __init__(self) -> None:
182
+ self._audit_log: list[AuditRecord] = []
183
+
184
+ # -- public API ---------------------------------------------------------
185
+
186
+ def validate_write(self, content: str, source: str) -> ValidationResult:
187
+ """Check content for injection patterns before writing to memory.
188
+
189
+ Returns a ``ValidationResult`` indicating whether the write should
190
+ proceed and any alerts raised.
191
+ """
192
+ alerts: list[Alert] = []
193
+
194
+ try:
195
+ alerts.extend(self._check_injection_patterns(content, source))
196
+ alerts.extend(self._check_code_injection(content, source))
197
+ alerts.extend(self._check_special_characters(content, source))
198
+ alerts.extend(self._check_unicode_manipulation(content, source))
199
+ except Exception:
200
+ # Fail closed: block the write if validation itself errors
201
+ logger.error(
202
+ "Memory validation error — blocking write (fail closed) | source=%s",
203
+ source, exc_info=True,
204
+ )
205
+ alerts.append(Alert(
206
+ alert_type=AlertType.INJECTION_PATTERN,
207
+ severity=AlertSeverity.CRITICAL,
208
+ message=f"Validation error — write blocked (fail closed) for source {source}",
209
+ entry_source=source,
210
+ ))
211
+
212
+ allowed = not any(
213
+ a.severity in (AlertSeverity.HIGH, AlertSeverity.CRITICAL)
214
+ for a in alerts
215
+ )
216
+
217
+ result = ValidationResult(allowed=allowed, alerts=alerts)
218
+
219
+ # Audit trail
220
+ record = AuditRecord(
221
+ timestamp=datetime.now(timezone.utc),
222
+ source=source,
223
+ content_hash=MemoryEntry.compute_hash(content),
224
+ allowed=allowed,
225
+ alerts=list(alerts),
226
+ )
227
+ self._audit_log.append(record)
228
+
229
+ if not allowed:
230
+ logger.warning(
231
+ "Memory write BLOCKED from source=%s alerts=%d",
232
+ source,
233
+ len(alerts),
234
+ )
235
+ else:
236
+ logger.debug(
237
+ "Memory write allowed from source=%s alerts=%d",
238
+ source,
239
+ len(alerts),
240
+ )
241
+
242
+ return result
243
+
244
+ def verify_integrity(self, entry: MemoryEntry) -> bool:
245
+ """Verify hash integrity of a memory entry.
246
+
247
+ Returns ``True`` if the stored hash matches a fresh computation.
248
+ """
249
+ expected = MemoryEntry.compute_hash(entry.content)
250
+ intact = expected == entry.content_hash
251
+ if not intact:
252
+ logger.warning(
253
+ "Integrity violation for entry from source=%s "
254
+ "(expected=%s, stored=%s)",
255
+ entry.source,
256
+ expected,
257
+ entry.content_hash,
258
+ )
259
+ return intact
260
+
261
+ def scan_memory(self, entries: Sequence[MemoryEntry]) -> list[Alert]:
262
+ """Scan existing memory entries for poisoning indicators.
263
+
264
+ Checks both content patterns and hash integrity for every entry.
265
+ """
266
+ all_alerts: list[Alert] = []
267
+ for entry in entries:
268
+ try:
269
+ # Integrity check
270
+ if not self.verify_integrity(entry):
271
+ all_alerts.append(Alert(
272
+ alert_type=AlertType.INTEGRITY_VIOLATION,
273
+ severity=AlertSeverity.CRITICAL,
274
+ message=f"Hash mismatch for entry from {entry.source}",
275
+ entry_source=entry.source,
276
+ ))
277
+
278
+ # Content checks (reuse validate_write logic)
279
+ all_alerts.extend(self._check_injection_patterns(entry.content, entry.source))
280
+ all_alerts.extend(self._check_code_injection(entry.content, entry.source))
281
+ all_alerts.extend(self._check_special_characters(entry.content, entry.source))
282
+ all_alerts.extend(self._check_unicode_manipulation(entry.content, entry.source))
283
+ except Exception:
284
+ logger.error(
285
+ "Error scanning memory entry — flagging as suspicious | source=%s",
286
+ entry.source, exc_info=True,
287
+ )
288
+ all_alerts.append(Alert(
289
+ alert_type=AlertType.INTEGRITY_VIOLATION,
290
+ severity=AlertSeverity.CRITICAL,
291
+ message=f"Scan error for entry from {entry.source} — flagged as suspicious",
292
+ entry_source=entry.source,
293
+ ))
294
+
295
+ return all_alerts
296
+
297
+ @property
298
+ def audit_log(self) -> list[AuditRecord]:
299
+ """Return a copy of the audit trail."""
300
+ return list(self._audit_log)
301
+
302
+ # -- internal checks ----------------------------------------------------
303
+
304
+ def _check_injection_patterns(
305
+ self, content: str, source: str
306
+ ) -> list[Alert]:
307
+ alerts: list[Alert] = []
308
+ for pattern in _INJECTION_PATTERNS:
309
+ if pattern.search(content):
310
+ alerts.append(Alert(
311
+ alert_type=AlertType.INJECTION_PATTERN,
312
+ severity=AlertSeverity.HIGH,
313
+ message=f"Prompt injection pattern detected: {pattern.pattern}",
314
+ entry_source=source,
315
+ matched_pattern=pattern.pattern,
316
+ ))
317
+ return alerts
318
+
319
+ def _check_code_injection(
320
+ self, content: str, source: str
321
+ ) -> list[Alert]:
322
+ alerts: list[Alert] = []
323
+ for pattern in _CODE_INJECTION_PATTERNS:
324
+ if pattern.search(content):
325
+ alerts.append(Alert(
326
+ alert_type=AlertType.CODE_INJECTION,
327
+ severity=AlertSeverity.HIGH,
328
+ message=f"Code injection pattern detected: {pattern.pattern}",
329
+ entry_source=source,
330
+ matched_pattern=pattern.pattern,
331
+ ))
332
+ return alerts
333
+
334
+ def _check_special_characters(
335
+ self, content: str, source: str
336
+ ) -> list[Alert]:
337
+ if not content:
338
+ return []
339
+ special = sum(
340
+ 1 for c in content
341
+ if not c.isalnum() and not c.isspace()
342
+ )
343
+ ratio = special / len(content)
344
+ if ratio > _SPECIAL_CHAR_THRESHOLD:
345
+ return [Alert(
346
+ alert_type=AlertType.EXCESSIVE_SPECIAL_CHARS,
347
+ severity=AlertSeverity.MEDIUM,
348
+ message=(
349
+ f"Excessive special characters ({ratio:.0%}) "
350
+ f"from source {source}"
351
+ ),
352
+ entry_source=source,
353
+ )]
354
+ return []
355
+
356
+ def _check_unicode_manipulation(
357
+ self, content: str, source: str
358
+ ) -> list[Alert]:
359
+ alerts: list[Alert] = []
360
+ # Detect right-to-left override and other bidi control characters
361
+ bidi_chars = {
362
+ "\u200e", # LRM
363
+ "\u200f", # RLM
364
+ "\u202a", # LRE
365
+ "\u202b", # RLE
366
+ "\u202c", # PDF
367
+ "\u202d", # LRO
368
+ "\u202e", # RLO
369
+ "\u2066", # LRI
370
+ "\u2067", # RLI
371
+ "\u2068", # FSI
372
+ "\u2069", # PDI
373
+ }
374
+ found = [c for c in content if c in bidi_chars]
375
+ if found:
376
+ alerts.append(Alert(
377
+ alert_type=AlertType.UNICODE_MANIPULATION,
378
+ severity=AlertSeverity.HIGH,
379
+ message=(
380
+ f"Bidirectional unicode control characters detected "
381
+ f"({len(found)} occurrences) from source {source}"
382
+ ),
383
+ entry_source=source,
384
+ ))
385
+
386
+ # Detect homoglyph-heavy content (characters from mixed scripts)
387
+ scripts: set[str] = set()
388
+ for c in content:
389
+ if c.isalpha():
390
+ # Use unicodedata to get script-like categorisation
391
+ name = unicodedata.name(c, "")
392
+ if name.startswith("LATIN"):
393
+ scripts.add("LATIN")
394
+ elif name.startswith("CYRILLIC"):
395
+ scripts.add("CYRILLIC")
396
+ elif name.startswith("GREEK"):
397
+ scripts.add("GREEK")
398
+ if len(scripts) > 1:
399
+ alerts.append(Alert(
400
+ alert_type=AlertType.UNICODE_MANIPULATION,
401
+ severity=AlertSeverity.MEDIUM,
402
+ message=(
403
+ f"Mixed unicode scripts detected ({', '.join(sorted(scripts))}) "
404
+ f"— possible homoglyph attack from source {source}"
405
+ ),
406
+ entry_source=source,
407
+ ))
408
+
409
+ return alerts
agent_os/metrics.py ADDED
@@ -0,0 +1,134 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Governance Metrics Collector — Tracks policy enforcement statistics.
5
+
6
+ Thread-safe singleton that records policy checks, violations, approvals,
7
+ and blocked tool calls across all governance adapters.
8
+
9
+ Example:
10
+ >>> from agent_os.metrics import metrics
11
+ >>>
12
+ >>> metrics.record_check("langchain", latency_ms=1.2, approved=True)
13
+ >>> metrics.record_violation("crewai")
14
+ >>> metrics.record_blocked("crewai")
15
+ >>> snap = metrics.snapshot()
16
+ >>> snap["total_checks"] # 1
17
+ >>> snap["violations"] # 1
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import threading
23
+ from dataclasses import dataclass, field
24
+
25
+
26
+ @dataclass
27
+ class GovernanceMetrics:
28
+ """Collects governance enforcement metrics across adapters.
29
+
30
+ All public methods are thread-safe via an internal lock.
31
+ """
32
+
33
+ total_checks: int = 0
34
+ violations: int = 0
35
+ approvals: int = 0
36
+ blocked: int = 0
37
+ avg_latency_ms: float = 0.0
38
+
39
+ _adapter_checks: dict[str, int] = field(default_factory=dict)
40
+ _adapter_violations: dict[str, int] = field(default_factory=dict)
41
+ _adapter_blocked: dict[str, int] = field(default_factory=dict)
42
+ _total_latency_ms: float = field(default=0.0, repr=False)
43
+ _lock: threading.Lock = field(default_factory=threading.Lock, repr=False)
44
+
45
+ def record_check(self, adapter: str, latency_ms: float, approved: bool) -> None:
46
+ """Record a policy check result.
47
+
48
+ Args:
49
+ adapter: Adapter name (e.g. ``"langchain"``).
50
+ latency_ms: Time taken for the check in milliseconds.
51
+ approved: Whether the action was approved.
52
+ """
53
+ with self._lock:
54
+ self.total_checks += 1
55
+ self._total_latency_ms += latency_ms
56
+ self.avg_latency_ms = self._total_latency_ms / self.total_checks
57
+ self._adapter_checks[adapter] = self._adapter_checks.get(adapter, 0) + 1
58
+ if approved:
59
+ self.approvals += 1
60
+ else:
61
+ self.violations += 1
62
+ self._adapter_violations[adapter] = (
63
+ self._adapter_violations.get(adapter, 0) + 1
64
+ )
65
+
66
+ def record_violation(self, adapter: str) -> None:
67
+ """Record a standalone policy violation.
68
+
69
+ Args:
70
+ adapter: Adapter name.
71
+ """
72
+ with self._lock:
73
+ self.violations += 1
74
+ self._adapter_violations[adapter] = (
75
+ self._adapter_violations.get(adapter, 0) + 1
76
+ )
77
+
78
+ def record_blocked(self, adapter: str) -> None:
79
+ """Record a blocked tool call.
80
+
81
+ Args:
82
+ adapter: Adapter name.
83
+ """
84
+ with self._lock:
85
+ self.blocked += 1
86
+ self._adapter_blocked[adapter] = (
87
+ self._adapter_blocked.get(adapter, 0) + 1
88
+ )
89
+
90
+ def snapshot(self) -> dict:
91
+ """Return a JSON-serializable snapshot of all metrics.
92
+
93
+ Returns:
94
+ Dictionary containing global and per-adapter metrics.
95
+ """
96
+ with self._lock:
97
+ return {
98
+ "total_checks": self.total_checks,
99
+ "violations": self.violations,
100
+ "approvals": self.approvals,
101
+ "blocked": self.blocked,
102
+ "avg_latency_ms": round(self.avg_latency_ms, 4),
103
+ "adapters": {
104
+ adapter: {
105
+ "checks": self._adapter_checks.get(adapter, 0),
106
+ "violations": self._adapter_violations.get(adapter, 0),
107
+ "blocked": self._adapter_blocked.get(adapter, 0),
108
+ }
109
+ for adapter in sorted(
110
+ set(self._adapter_checks)
111
+ | set(self._adapter_violations)
112
+ | set(self._adapter_blocked)
113
+ )
114
+ },
115
+ }
116
+
117
+ def reset(self) -> None:
118
+ """Reset all counters to zero (useful for test isolation)."""
119
+ with self._lock:
120
+ self.total_checks = 0
121
+ self.violations = 0
122
+ self.approvals = 0
123
+ self.blocked = 0
124
+ self.avg_latency_ms = 0.0
125
+ self._total_latency_ms = 0.0
126
+ self._adapter_checks.clear()
127
+ self._adapter_violations.clear()
128
+ self._adapter_blocked.clear()
129
+
130
+
131
+ # Module-level singleton
132
+ metrics = GovernanceMetrics()
133
+
134
+ __all__ = ["GovernanceMetrics", "metrics"]