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
agent_os/base_agent.py ADDED
@@ -0,0 +1,651 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Base Agent Module - Reusable base classes for Agent OS agents.
5
+
6
+ Provides a consistent pattern for building agents that run under
7
+ the Agent OS kernel with policy governance, audit logging, and
8
+ tool integration.
9
+
10
+ Example:
11
+ >>> from agent_os.base_agent import BaseAgent, AgentConfig
12
+ >>>
13
+ >>> class MyAgent(BaseAgent):
14
+ ... async def run(self, task: str) -> ExecutionResult:
15
+ ... return await self._execute("process", {"task": task})
16
+ >>>
17
+ >>> agent = MyAgent(AgentConfig(agent_id="my-agent", policies=["read_only"]))
18
+ >>> result = await agent.run("hello")
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import asyncio
24
+ import copy
25
+ import json
26
+ import os
27
+ import re
28
+ import sys
29
+ import time
30
+ from abc import ABC, abstractmethod
31
+ from dataclasses import dataclass, field
32
+ from datetime import datetime, timezone
33
+ from enum import Enum
34
+ from typing import Any, Callable, Generic, TypeVar
35
+ from uuid import uuid4
36
+
37
+ from agent_os.stateless import (
38
+ ExecutionContext,
39
+ ExecutionResult,
40
+ MemoryBackend,
41
+ StateBackend,
42
+ StatelessKernel,
43
+ )
44
+
45
+
46
+ class PolicyDecision(Enum):
47
+ """Result of policy evaluation."""
48
+ ALLOW = "allow"
49
+ DENY = "deny"
50
+ AUDIT = "audit" # Allow but log for review
51
+ ESCALATE = "escalate" # Route to human reviewer
52
+ DEFER = "defer" # Async policy evaluation with callback
53
+
54
+
55
+ @dataclass
56
+ class EscalationRequest:
57
+ """A request for human review of a policy decision.
58
+
59
+ Attributes:
60
+ action: The action that triggered escalation
61
+ reason: Why the action was escalated
62
+ requested_by: Agent ID that requested the escalation
63
+ timestamp: When the escalation was created
64
+ status: Current status (pending/approved/rejected)
65
+ """
66
+ action: str
67
+ reason: str
68
+ requested_by: str
69
+ timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
70
+ status: str = "pending"
71
+
72
+ def __post_init__(self) -> None:
73
+ if self.status not in ("pending", "approved", "rejected"):
74
+ raise ValueError(f"Invalid status: {self.status!r}")
75
+
76
+ def approve(self) -> None:
77
+ """Mark escalation as approved."""
78
+ self.status = "approved"
79
+
80
+ def reject(self) -> None:
81
+ """Mark escalation as rejected."""
82
+ self.status = "rejected"
83
+
84
+ def to_dict(self) -> dict[str, Any]:
85
+ return {
86
+ "action": self.action,
87
+ "reason": self.reason,
88
+ "requested_by": self.requested_by,
89
+ "timestamp": self.timestamp.isoformat(),
90
+ "status": self.status,
91
+ }
92
+
93
+
94
+ @dataclass
95
+ class AgentConfig:
96
+ """Configuration for an agent instance.
97
+
98
+ Attributes:
99
+ agent_id: Unique identifier for this agent instance
100
+ policies: List of policy names to apply (e.g., ["read_only", "no_pii"])
101
+ metadata: Additional metadata for the agent
102
+ state_backend: Optional custom state backend (defaults to in-memory)
103
+ max_audit_log_size: Maximum number of audit log entries to retain
104
+ max_metadata_size_bytes: Maximum size in bytes for metadata values
105
+ """
106
+ agent_id: str
107
+ policies: list[str] = field(default_factory=list)
108
+ metadata: dict[str, Any] = field(default_factory=dict)
109
+ state_backend: StateBackend | None = None
110
+ max_audit_log_size: int = 10000
111
+ max_metadata_size_bytes: int = 1_048_576 # 1 MB
112
+
113
+ _AGENT_ID_RE = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9-]{2,63}$")
114
+
115
+ def __post_init__(self) -> None:
116
+ if not self._AGENT_ID_RE.match(self.agent_id):
117
+ raise ValueError(
118
+ f"Invalid agent_id {self.agent_id!r}. "
119
+ "Must be 3-64 chars, alphanumeric with dashes, "
120
+ "starting with an alphanumeric character."
121
+ )
122
+
123
+ @classmethod
124
+ def from_file(cls, path: str) -> AgentConfig:
125
+ """Load agent configuration from a YAML or JSON file.
126
+
127
+ Args:
128
+ path: Path to a .yaml, .yml, or .json config file
129
+
130
+ Returns:
131
+ AgentConfig populated from the file
132
+
133
+ Raises:
134
+ FileNotFoundError: If the file does not exist
135
+ ValueError: If the file extension is not supported
136
+ """
137
+ if not os.path.isfile(path):
138
+ raise FileNotFoundError(f"Config file not found: {path}")
139
+
140
+ ext = os.path.splitext(path)[1].lower()
141
+ with open(path, encoding="utf-8") as fh:
142
+ if ext in (".yaml", ".yml"):
143
+ try:
144
+ import yaml
145
+ except ImportError as exc:
146
+ raise ImportError(
147
+ "PyYAML is required for YAML config: pip install pyyaml"
148
+ ) from exc
149
+ data = yaml.safe_load(fh) or {}
150
+ elif ext == ".json":
151
+ data = json.load(fh)
152
+ else:
153
+ raise ValueError(f"Unsupported config format: {ext}")
154
+
155
+ return cls(
156
+ agent_id=data.get("agent_id", data.get("agentId", "agent")),
157
+ policies=data.get("policies", []),
158
+ metadata=data.get("metadata", {}),
159
+ max_audit_log_size=data.get("max_audit_log_size", 10000),
160
+ max_metadata_size_bytes=data.get("max_metadata_size_bytes", 1_048_576),
161
+ )
162
+
163
+ def __repr__(self) -> str:
164
+ return f"AgentConfig(agent_id={self.agent_id!r}, policies={self.policies!r})"
165
+
166
+ def to_dict(self) -> dict[str, Any]:
167
+ """Serialize configuration to a dictionary."""
168
+ return {
169
+ "agent_id": self.agent_id,
170
+ "policies": self.policies,
171
+ "metadata": self.metadata,
172
+ "max_audit_log_size": self.max_audit_log_size,
173
+ "max_metadata_size_bytes": self.max_metadata_size_bytes,
174
+ }
175
+
176
+ @classmethod
177
+ def from_dict(cls, data: dict[str, Any]) -> AgentConfig:
178
+ """Deserialize an AgentConfig from a dictionary.
179
+
180
+ Args:
181
+ data: Dictionary as produced by ``to_dict()``.
182
+
183
+ Returns:
184
+ Reconstructed AgentConfig instance.
185
+ """
186
+ return cls(
187
+ agent_id=data["agent_id"],
188
+ policies=data.get("policies", []),
189
+ metadata=data.get("metadata", {}),
190
+ max_audit_log_size=data.get("max_audit_log_size", 10000),
191
+ max_metadata_size_bytes=data.get("max_metadata_size_bytes", 1_048_576),
192
+ )
193
+
194
+
195
+ @dataclass
196
+ class AuditEntry:
197
+ """An entry in the agent's audit log."""
198
+ timestamp: datetime
199
+ agent_id: str
200
+ request_id: str
201
+ action: str
202
+ params: dict[str, Any]
203
+ decision: PolicyDecision
204
+ result_success: bool | None = None
205
+ error: str | None = None
206
+ execution_time_ms: float | None = None
207
+
208
+ def __repr__(self) -> str:
209
+ return (
210
+ f"AuditEntry(agent_id={self.agent_id!r}, action={self.action!r}, "
211
+ f"decision={self.decision!r})"
212
+ )
213
+
214
+ def to_dict(self) -> dict[str, Any]:
215
+ return {
216
+ "timestamp": self.timestamp.isoformat(),
217
+ "agent_id": self.agent_id,
218
+ "request_id": self.request_id,
219
+ "action": self.action,
220
+ "params_keys": list(self.params.keys()), # Don't log full params
221
+ "decision": self.decision.value,
222
+ "result_success": self.result_success,
223
+ "error": self.error,
224
+ "execution_time_ms": self.execution_time_ms,
225
+ }
226
+
227
+ @classmethod
228
+ def from_dict(cls, data: dict[str, Any]) -> AuditEntry:
229
+ """Deserialize an AuditEntry from a dictionary.
230
+
231
+ Args:
232
+ data: Dictionary as produced by ``to_dict()``.
233
+
234
+ Returns:
235
+ Reconstructed AuditEntry instance.
236
+ """
237
+ return cls(
238
+ timestamp=datetime.fromisoformat(data["timestamp"]),
239
+ agent_id=data["agent_id"],
240
+ request_id=data["request_id"],
241
+ action=data["action"],
242
+ params=dict.fromkeys(data.get("params_keys", [])),
243
+ decision=PolicyDecision(data["decision"]),
244
+ result_success=data.get("result_success"),
245
+ error=data.get("error"),
246
+ execution_time_ms=data.get("execution_time_ms"),
247
+ )
248
+
249
+
250
+ class BaseAgent(ABC):
251
+ """Abstract base class for Agent OS agents.
252
+
253
+ Provides:
254
+ - Kernel integration with policy enforcement
255
+ - Execution context management
256
+ - Audit logging
257
+ - Common helper methods
258
+
259
+ Subclasses must implement the `run` method which defines
260
+ the agent's main task.
261
+
262
+ Example:
263
+ >>> class GreeterAgent(BaseAgent):
264
+ ... async def run(self, name: str) -> ExecutionResult:
265
+ ... result = await self._execute(
266
+ ... action="greet",
267
+ ... params={"name": name, "output": f"Hello, {name}!"}
268
+ ... )
269
+ ... return result
270
+ >>>
271
+ >>> agent = GreeterAgent(AgentConfig(agent_id="greeter"))
272
+ >>> result = await agent.run("World")
273
+ >>> print(result.data) # "Hello, World!"
274
+ """
275
+
276
+ def __init__(
277
+ self,
278
+ config: AgentConfig,
279
+ defer_timeout: float = 30.0,
280
+ ):
281
+ """Initialize the agent.
282
+
283
+ Args:
284
+ config: Agent configuration including ID, policies, and backend
285
+ defer_timeout: Timeout in seconds for DEFER async callbacks (default 30s)
286
+ """
287
+ self._config = config
288
+ self._kernel = StatelessKernel(
289
+ backend=config.state_backend or MemoryBackend()
290
+ )
291
+ self._audit_log: list[AuditEntry] = []
292
+ self._max_audit_entries = config.max_audit_log_size
293
+ self._escalation_queue: list[EscalationRequest] = []
294
+ self._defer_timeout = defer_timeout
295
+ self._defer_callback: Callable[[str, dict[str, Any]], asyncio.Future[PolicyDecision]] | None = None
296
+
297
+ @property
298
+ def agent_id(self) -> str:
299
+ """Get the agent's unique identifier."""
300
+ return self._config.agent_id
301
+
302
+ @property
303
+ def policies(self) -> list[str]:
304
+ """Get the agent's active policies."""
305
+ return self._config.policies.copy()
306
+
307
+ def _new_context(self, **extra_metadata: Any) -> ExecutionContext:
308
+ """Create a new execution context for a request.
309
+
310
+ Args:
311
+ **extra_metadata: Additional metadata to include
312
+
313
+ Returns:
314
+ Fresh ExecutionContext with agent's default settings
315
+
316
+ Raises:
317
+ ValueError: If any metadata value exceeds max_metadata_size_bytes
318
+ """
319
+ metadata = {**self._config.metadata, **extra_metadata}
320
+ max_size = self._config.max_metadata_size_bytes
321
+ for key, value in metadata.items():
322
+ size = sys.getsizeof(value)
323
+ if size > max_size:
324
+ raise ValueError(
325
+ f"Metadata key {key!r} value size ({size} bytes) "
326
+ f"exceeds limit ({max_size} bytes)"
327
+ )
328
+ metadata = copy.deepcopy(metadata)
329
+ return ExecutionContext(
330
+ agent_id=self._config.agent_id,
331
+ policies=self._config.policies.copy(),
332
+ metadata=metadata,
333
+ )
334
+
335
+ def set_defer_callback(
336
+ self,
337
+ callback: Callable[[str, dict[str, Any]], asyncio.Future[PolicyDecision]],
338
+ ) -> None:
339
+ """Register an async callback for DEFER policy decisions.
340
+
341
+ Args:
342
+ callback: Async callable receiving (action, params) and returning
343
+ a Future that resolves to a PolicyDecision.
344
+ """
345
+ self._defer_callback = callback
346
+
347
+ async def _enforce_policy(
348
+ self,
349
+ decision: PolicyDecision,
350
+ action: str,
351
+ params: dict[str, Any],
352
+ reason: str = "",
353
+ ) -> ExecutionResult:
354
+ """Handle a policy decision, including ESCALATE and DEFER.
355
+
356
+ Args:
357
+ decision: The PolicyDecision to enforce
358
+ action: Name of the action under evaluation
359
+ params: Parameters for the action
360
+ reason: Human-readable reason for the decision
361
+
362
+ Returns:
363
+ ExecutionResult representing the enforcement outcome
364
+ """
365
+ if decision == PolicyDecision.ESCALATE:
366
+ escalation = EscalationRequest(
367
+ action=action,
368
+ reason=reason or f"Action '{action}' escalated for human review",
369
+ requested_by=self._config.agent_id,
370
+ )
371
+ self._escalation_queue.append(escalation)
372
+ return ExecutionResult(
373
+ success=False,
374
+ data=escalation.to_dict(),
375
+ error=None,
376
+ signal="ESCALATE",
377
+ metadata={"pending_review": True},
378
+ )
379
+
380
+ if decision == PolicyDecision.DEFER:
381
+ if self._defer_callback is None:
382
+ return ExecutionResult(
383
+ success=False,
384
+ data=None,
385
+ error="DEFER requested but no callback registered",
386
+ signal="DEFER",
387
+ )
388
+ try:
389
+ future = self._defer_callback(action, params)
390
+ resolved = await asyncio.wait_for(
391
+ future, timeout=self._defer_timeout
392
+ )
393
+ if resolved == PolicyDecision.ALLOW:
394
+ return ExecutionResult(success=True, data=None)
395
+ return ExecutionResult(
396
+ success=False,
397
+ data=None,
398
+ error=f"Deferred evaluation resolved to {resolved.value}",
399
+ signal=resolved.value.upper(),
400
+ )
401
+ except asyncio.TimeoutError:
402
+ return ExecutionResult(
403
+ success=False,
404
+ data=None,
405
+ error=(
406
+ f"DEFER timeout after {self._defer_timeout}s "
407
+ f"for action '{action}'"
408
+ ),
409
+ signal="DEFER_TIMEOUT",
410
+ )
411
+
412
+ if decision == PolicyDecision.DENY:
413
+ return ExecutionResult(
414
+ success=False,
415
+ data=None,
416
+ error=reason or f"Action '{action}' denied by policy",
417
+ signal="SIGKILL",
418
+ )
419
+
420
+ # ALLOW / AUDIT — no blocking
421
+ return ExecutionResult(success=True, data=None)
422
+
423
+ async def _execute(
424
+ self,
425
+ action: str,
426
+ params: dict[str, Any],
427
+ context: ExecutionContext | None = None,
428
+ ) -> ExecutionResult:
429
+ """Execute an action through the kernel with policy checks.
430
+
431
+ This is the primary method for agents to perform actions.
432
+ All actions are:
433
+ 1. Checked against configured policies
434
+ 2. Logged for audit
435
+ 3. Executed through the kernel
436
+
437
+ Args:
438
+ action: Name of the action to execute
439
+ params: Parameters for the action
440
+ context: Optional custom context (uses default if None)
441
+
442
+ Returns:
443
+ ExecutionResult with success status, data, and any errors
444
+ """
445
+ ctx = context or self._new_context()
446
+ request_id = str(uuid4())[:16]
447
+
448
+ # Create audit entry
449
+ audit = AuditEntry(
450
+ timestamp=datetime.now(timezone.utc),
451
+ agent_id=self._config.agent_id,
452
+ request_id=request_id,
453
+ action=action,
454
+ params=params,
455
+ decision=PolicyDecision.ALLOW, # Will be updated
456
+ )
457
+
458
+ # Execute through kernel with timing
459
+ t0 = time.monotonic()
460
+ result = await self._kernel.execute(action, params, ctx)
461
+ elapsed_ms = (time.monotonic() - t0) * 1000.0
462
+
463
+ # Update audit entry with result
464
+ if result.signal == "SIGKILL":
465
+ audit.decision = PolicyDecision.DENY
466
+ elif result.signal == "ESCALATE":
467
+ audit.decision = PolicyDecision.ESCALATE
468
+ elif result.signal in ("DEFER", "DEFER_TIMEOUT"):
469
+ audit.decision = PolicyDecision.DEFER
470
+ audit.result_success = result.success
471
+ audit.error = result.error
472
+ audit.execution_time_ms = elapsed_ms
473
+
474
+ self._audit_log.append(audit)
475
+ if len(self._audit_log) > self._max_audit_entries:
476
+ self._audit_log = self._audit_log[-self._max_audit_entries:]
477
+
478
+ return result
479
+
480
+ def get_audit_log(self) -> list[dict[str, Any]]:
481
+ """Get the agent's audit log.
482
+
483
+ Returns:
484
+ List of audit entries as dictionaries
485
+ """
486
+ return [entry.to_dict() for entry in self._audit_log]
487
+
488
+ def clear_audit_log(self) -> None:
489
+ """Clear the agent's audit log."""
490
+ self._audit_log.clear()
491
+
492
+ def get_execution_stats(self) -> dict[str, Any]:
493
+ """Return execution time statistics from audit log entries.
494
+
495
+ Returns:
496
+ Dictionary with avg, min, max, p99 execution times in milliseconds,
497
+ and the total count of timed entries.
498
+ """
499
+ times = [
500
+ e.execution_time_ms
501
+ for e in self._audit_log
502
+ if e.execution_time_ms is not None
503
+ ]
504
+ if not times:
505
+ return {"count": 0, "avg_ms": 0.0, "min_ms": 0.0, "max_ms": 0.0, "p99_ms": 0.0}
506
+ times_sorted = sorted(times)
507
+ count = len(times_sorted)
508
+ p99_idx = min(int(count * 0.99), count - 1)
509
+ return {
510
+ "count": count,
511
+ "avg_ms": sum(times_sorted) / count,
512
+ "min_ms": times_sorted[0],
513
+ "max_ms": times_sorted[-1],
514
+ "p99_ms": times_sorted[p99_idx],
515
+ }
516
+
517
+ def query_audit_log(
518
+ self,
519
+ action: str | None = None,
520
+ decision: str | None = None,
521
+ since: datetime | None = None,
522
+ limit: int | None = None,
523
+ offset: int = 0,
524
+ ) -> list[dict[str, Any]]:
525
+ """Query audit log with optional filters.
526
+
527
+ Args:
528
+ action: Filter by action name (exact match).
529
+ decision: Filter by decision value (e.g. "allow", "deny").
530
+ since: Only include entries at or after this timestamp.
531
+ limit: Maximum number of entries to return.
532
+ offset: Number of matching entries to skip (for pagination).
533
+
534
+ Returns:
535
+ List of matching audit entries as dictionaries.
536
+ """
537
+ results: list[AuditEntry] = self._audit_log
538
+ if action is not None:
539
+ results = [e for e in results if e.action == action]
540
+ if decision is not None:
541
+ results = [e for e in results if e.decision.value == decision]
542
+ if since is not None:
543
+ results = [e for e in results if e.timestamp >= since]
544
+ results = results[offset:]
545
+ if limit is not None:
546
+ results = results[:limit]
547
+ return [e.to_dict() for e in results]
548
+
549
+ def get_escalation_queue(self) -> list[EscalationRequest]:
550
+ """Get pending escalation requests."""
551
+ return [e for e in self._escalation_queue if e.status == "pending"]
552
+
553
+ @abstractmethod
554
+ async def run(self, *args, **kwargs) -> ExecutionResult:
555
+ """Run the agent's main task.
556
+
557
+ Subclasses must implement this method to define the agent's
558
+ primary functionality.
559
+
560
+ Returns:
561
+ ExecutionResult with the outcome of the task
562
+ """
563
+ pass
564
+
565
+
566
+ class ToolUsingAgent(BaseAgent):
567
+ """Base class for agents that use registered tools from ATR.
568
+
569
+ Extends BaseAgent with tool discovery and execution capabilities.
570
+ Tools are executed through the kernel for policy enforcement.
571
+
572
+ Example:
573
+ >>> class AnalysisAgent(ToolUsingAgent):
574
+ ... async def run(self, data: str) -> ExecutionResult:
575
+ ... # Use registered tools
576
+ ... parsed = await self._use_tool("json_parser", {"text": data})
577
+ ... return parsed
578
+ """
579
+
580
+ def __init__(self, config: AgentConfig, tools: list[str] | None = None):
581
+ """Initialize the tool-using agent.
582
+
583
+ Args:
584
+ config: Agent configuration
585
+ tools: Optional list of tool names this agent is allowed to use
586
+ """
587
+ super().__init__(config)
588
+ self._allowed_tools = set(tools) if tools else None
589
+
590
+ async def _use_tool(
591
+ self,
592
+ tool_name: str,
593
+ params: dict[str, Any],
594
+ ) -> ExecutionResult:
595
+ """Use a registered tool through the kernel.
596
+
597
+ Args:
598
+ tool_name: Name of the tool to use
599
+ params: Parameters to pass to the tool
600
+
601
+ Returns:
602
+ ExecutionResult from tool execution
603
+ """
604
+ # Check tool allowlist if configured
605
+ if self._allowed_tools and tool_name not in self._allowed_tools:
606
+ return ExecutionResult(
607
+ success=False,
608
+ data=None,
609
+ error=f"Tool '{tool_name}' not in allowed tools list",
610
+ )
611
+
612
+ # Execute through kernel
613
+ return await self._execute(
614
+ action=f"tool:{tool_name}",
615
+ params=params,
616
+ )
617
+
618
+ def list_allowed_tools(self) -> list[str] | None:
619
+ """Get list of allowed tools, or None if all tools allowed."""
620
+ return list(self._allowed_tools) if self._allowed_tools else None
621
+
622
+
623
+ # Type variable for generic agent results
624
+ T = TypeVar("T")
625
+
626
+
627
+ @dataclass
628
+ class TypedResult(Generic[T]):
629
+ """A typed wrapper for execution results.
630
+
631
+ Useful when you want type hints on the result data.
632
+ """
633
+ success: bool
634
+ data: T | None = None
635
+ error: str | None = None
636
+
637
+ @classmethod
638
+ def from_execution_result(
639
+ cls,
640
+ result: ExecutionResult,
641
+ transform: Callable[[Any], T] | None = None,
642
+ ) -> TypedResult[T]:
643
+ """Create from an ExecutionResult with optional transformation."""
644
+ data = None
645
+ if result.success and result.data is not None:
646
+ data = transform(result.data) if transform else result.data
647
+ return cls(
648
+ success=result.success,
649
+ data=data,
650
+ error=result.error,
651
+ )
@@ -0,0 +1,34 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Circuit Breaker — backward-compatibility shim.
4
+
5
+ Attempts to import the canonical implementation from
6
+ ``agent_sre.cascade.circuit_breaker``. When ``agent_sre`` is not
7
+ installed the standalone fallback in ``agent_os._circuit_breaker_impl``
8
+ is re-exported so that ``agent_os`` (and in particular ``stateless.py``)
9
+ continues to work without requiring the optional SRE package.
10
+
11
+ .. deprecated::
12
+ Prefer importing directly from ``agent_sre.cascade.circuit_breaker``.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ try:
18
+ from agent_sre.cascade.circuit_breaker import ( # noqa: F401
19
+ CascadeDetector,
20
+ CircuitBreaker,
21
+ CircuitBreakerConfig,
22
+ CircuitBreakerOpen,
23
+ CircuitOpenError,
24
+ CircuitState,
25
+ )
26
+ except ImportError:
27
+ from agent_os._circuit_breaker_impl import ( # noqa: F401
28
+ CascadeDetector,
29
+ CircuitBreaker,
30
+ CircuitBreakerConfig,
31
+ CircuitBreakerOpen,
32
+ CircuitOpenError,
33
+ CircuitState,
34
+ )