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,424 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ LangChain Client Adapter - Drop-In Middleware for Agent Control Plane
5
+
6
+ This adapter wraps LangChain clients to automatically intercept and govern
7
+ tool calls made by LangChain agents. It provides similar integration as the
8
+ OpenAI adapter, but for LangChain's framework.
9
+
10
+ Usage:
11
+ from langchain.chat_models import ChatOpenAI
12
+ from agent_control_plane import AgentControlPlane
13
+ from agent_control_plane.langchain_adapter import LangChainAdapter
14
+
15
+ # Standard setup
16
+ llm = ChatOpenAI(temperature=0)
17
+ control_plane = AgentControlPlane()
18
+ agent_context = control_plane.create_agent("my-agent", permissions)
19
+
20
+ # Wrap with adapter
21
+ governed_llm = LangChainAdapter(
22
+ control_plane=control_plane,
23
+ agent_context=agent_context,
24
+ langchain_client=llm
25
+ )
26
+
27
+ # Use in LangChain agents
28
+ from langchain.agents import initialize_agent
29
+ agent = initialize_agent(tools, governed_llm, agent="zero-shot-react-description")
30
+ agent.run("Your task here")
31
+ # Tool calls are automatically governed by the control plane!
32
+ """
33
+
34
+ from typing import Any, Dict, List, Optional, Callable, Sequence
35
+ import json
36
+ import logging
37
+ from datetime import datetime
38
+
39
+ from .agent_kernel import ActionType, AgentContext
40
+ from .control_plane import AgentControlPlane
41
+
42
+
43
+ # Mapping from common LangChain tool names to ActionType
44
+ DEFAULT_LANGCHAIN_TOOL_MAPPING = {
45
+ # File operations
46
+ "read_file": ActionType.FILE_READ,
47
+ "write_file": ActionType.FILE_WRITE,
48
+ "file_read": ActionType.FILE_READ,
49
+ "file_write": ActionType.FILE_WRITE,
50
+ "readfile": ActionType.FILE_READ,
51
+ "writefile": ActionType.FILE_WRITE,
52
+
53
+ # Code execution
54
+ "python_repl": ActionType.CODE_EXECUTION,
55
+ "python": ActionType.CODE_EXECUTION,
56
+ "terminal": ActionType.CODE_EXECUTION,
57
+ "shell": ActionType.CODE_EXECUTION,
58
+ "bash": ActionType.CODE_EXECUTION,
59
+
60
+ # Database operations
61
+ "sql_db_query": ActionType.DATABASE_QUERY,
62
+ "sql_db_schema": ActionType.DATABASE_QUERY,
63
+ "sql_db_list_tables": ActionType.DATABASE_QUERY,
64
+ "sql_db_query_checker": ActionType.DATABASE_QUERY,
65
+
66
+ # API calls
67
+ "requests_get": ActionType.API_CALL,
68
+ "requests_post": ActionType.API_CALL,
69
+ "requests": ActionType.API_CALL,
70
+ "api_request": ActionType.API_CALL,
71
+
72
+ # Search and retrieval
73
+ "google_search": ActionType.API_CALL,
74
+ "serpapi": ActionType.API_CALL,
75
+ "wikipedia": ActionType.API_CALL,
76
+ }
77
+
78
+
79
+ class LangChainAdapter:
80
+ """
81
+ LangChain Client Adapter with Agent Control Plane Governance.
82
+
83
+ This class wraps a LangChain LLM or agent to provide automatic governance
84
+ of tool calls. It intercepts tool invocations and checks them against the
85
+ control plane's policies before allowing execution.
86
+
87
+ The adapter works by wrapping the tool execution layer, similar to how
88
+ the OpenAI adapter wraps chat completions.
89
+
90
+ Example:
91
+ # Before (ungoverned):
92
+ from langchain.chat_models import ChatOpenAI
93
+ llm = ChatOpenAI()
94
+ agent = initialize_agent(tools, llm, agent="zero-shot-react-description")
95
+
96
+ # After (governed):
97
+ governed_llm = LangChainAdapter(control_plane, agent_context, llm)
98
+ agent = initialize_agent(tools, governed_llm, agent="zero-shot-react-description")
99
+ # Same API, but now with governance!
100
+ """
101
+
102
+ def __init__(
103
+ self,
104
+ control_plane: AgentControlPlane,
105
+ agent_context: AgentContext,
106
+ langchain_client: Any,
107
+ tool_mapping: Optional[Dict[str, ActionType]] = None,
108
+ on_block: Optional[Callable[[str, Dict, Dict], None]] = None,
109
+ logger: Optional[logging.Logger] = None
110
+ ):
111
+ """
112
+ Initialize the LangChain adapter.
113
+
114
+ Args:
115
+ control_plane: The AgentControlPlane instance for governance
116
+ agent_context: The AgentContext for the agent using this client
117
+ langchain_client: The original LangChain LLM or agent instance
118
+ tool_mapping: Optional custom mapping from tool names to ActionTypes
119
+ on_block: Optional callback called when an action is blocked
120
+ Signature: on_block(tool_name: str, tool_args: dict, result: dict)
121
+ logger: Optional logger instance
122
+ """
123
+ self.control_plane = control_plane
124
+ self.agent_context = agent_context
125
+ self.client = langchain_client
126
+ self.logger = logger or logging.getLogger("LangChainAdapter")
127
+ self.on_block = on_block
128
+
129
+ # Merge default mapping with custom mapping
130
+ self.tool_mapping = DEFAULT_LANGCHAIN_TOOL_MAPPING.copy()
131
+ if tool_mapping:
132
+ self.tool_mapping.update({k.lower(): v for k, v in tool_mapping.items()})
133
+
134
+ # Store original methods to wrap
135
+ self._original_call = None
136
+ self._original_generate = None
137
+ self._original_invoke = None
138
+
139
+ # Wrap the client methods
140
+ self._wrap_client()
141
+
142
+ self.logger.info(
143
+ f"Initialized LangChainAdapter for agent {agent_context.agent_id}"
144
+ )
145
+
146
+ def _wrap_client(self):
147
+ """Wrap the LangChain client's methods to intercept tool calls."""
148
+ # LangChain uses different methods depending on the version
149
+ # We wrap common invocation methods
150
+
151
+ if hasattr(self.client, '__call__'):
152
+ self._original_call = self.client.__call__
153
+ self.client.__call__ = self._governed_call
154
+
155
+ if hasattr(self.client, 'generate'):
156
+ self._original_generate = self.client.generate
157
+ self.client.generate = self._governed_generate
158
+
159
+ if hasattr(self.client, 'invoke'):
160
+ self._original_invoke = self.client.invoke
161
+ self.client.invoke = self._governed_invoke
162
+
163
+ def _governed_call(self, *args, **kwargs):
164
+ """Wrapped __call__ method with governance."""
165
+ return self._execute_with_governance(self._original_call, *args, **kwargs)
166
+
167
+ def _governed_generate(self, *args, **kwargs):
168
+ """Wrapped generate method with governance."""
169
+ return self._execute_with_governance(self._original_generate, *args, **kwargs)
170
+
171
+ def _governed_invoke(self, *args, **kwargs):
172
+ """Wrapped invoke method with governance."""
173
+ return self._execute_with_governance(self._original_invoke, *args, **kwargs)
174
+
175
+ def _execute_with_governance(self, original_method, *args, **kwargs):
176
+ """
177
+ Execute the original method while intercepting tool calls.
178
+
179
+ This is the core governance logic that checks tool calls against
180
+ the control plane before allowing execution.
181
+ """
182
+ # Call the original method
183
+ result = original_method(*args, **kwargs)
184
+
185
+ # Check if the result contains tool calls or actions
186
+ # LangChain formats vary, so we handle multiple formats
187
+ tool_calls = self._extract_tool_calls(result)
188
+
189
+ if tool_calls:
190
+ self.logger.info(
191
+ f"Agent {self.agent_context.agent_id}: Intercepting {len(tool_calls)} tool call(s)"
192
+ )
193
+
194
+ # Check each tool call
195
+ for tool_call in tool_calls:
196
+ self._check_tool_call(tool_call)
197
+
198
+ return result
199
+
200
+ def _extract_tool_calls(self, result: Any) -> List[Dict]:
201
+ """
202
+ Extract tool calls from LangChain result.
203
+
204
+ LangChain can return results in various formats depending on the
205
+ agent type and version. This method handles common formats.
206
+ """
207
+ tool_calls = []
208
+
209
+ # Handle AIMessage format (newer LangChain versions)
210
+ if hasattr(result, 'tool_calls') and result.tool_calls:
211
+ for tc in result.tool_calls:
212
+ tool_calls.append({
213
+ 'name': tc.get('name', ''),
214
+ 'args': tc.get('args', {}),
215
+ 'id': tc.get('id', '')
216
+ })
217
+
218
+ # Handle additional_kwargs format
219
+ elif hasattr(result, 'additional_kwargs') and 'tool_calls' in result.additional_kwargs:
220
+ for tc in result.additional_kwargs['tool_calls']:
221
+ if isinstance(tc, dict):
222
+ tool_calls.append({
223
+ 'name': tc.get('name', tc.get('function', {}).get('name', '')),
224
+ 'args': tc.get('args', tc.get('function', {}).get('arguments', {})),
225
+ 'id': tc.get('id', '')
226
+ })
227
+
228
+ # Handle list of generations
229
+ elif isinstance(result, list):
230
+ for item in result:
231
+ if hasattr(item, 'message'):
232
+ tool_calls.extend(self._extract_tool_calls(item.message))
233
+
234
+ return tool_calls
235
+
236
+ def _check_tool_call(self, tool_call: Dict):
237
+ """
238
+ Check a single tool call against the control plane.
239
+
240
+ Args:
241
+ tool_call: Dictionary with 'name', 'args', and optionally 'id'
242
+ """
243
+ tool_name = tool_call.get('name', '')
244
+ tool_args = tool_call.get('args', {})
245
+
246
+ # Parse arguments if they're a JSON string
247
+ if isinstance(tool_args, str):
248
+ try:
249
+ tool_args = json.loads(tool_args)
250
+ except json.JSONDecodeError:
251
+ self.logger.warning(
252
+ f"Could not parse arguments for tool '{tool_name}': {tool_args}"
253
+ )
254
+ tool_args = {}
255
+
256
+ # Map tool name to ActionType
257
+ action_type = self._map_tool_to_action(tool_name)
258
+
259
+ if action_type is None:
260
+ # Security: Unknown tools are denied by default
261
+ self.logger.warning(f"Unknown tool '{tool_name}', denying by default")
262
+ raise PermissionError(f"Unknown tool: {tool_name}. Tool must be mapped to an ActionType.")
263
+
264
+ # THE KERNEL CHECK - This is where governance happens
265
+ self.logger.debug(f"Checking permission for {tool_name} -> {action_type.value}")
266
+
267
+ # Check permission through control plane
268
+ check_result = self.control_plane.execute_action(
269
+ self.agent_context,
270
+ action_type,
271
+ tool_args
272
+ )
273
+
274
+ if not check_result['success']:
275
+ # Action is BLOCKED by the control plane
276
+ error_msg = (
277
+ f"BLOCKED: Agent {self.agent_context.agent_id} attempted {tool_name} "
278
+ f"but was denied: {check_result.get('error', 'Unknown reason')}"
279
+ )
280
+ self.logger.warning(error_msg)
281
+
282
+ # Call the optional callback
283
+ if self.on_block:
284
+ self.on_block(tool_name, tool_args, check_result)
285
+
286
+ # Raise an exception to prevent execution
287
+ # This follows the "Mute Agent" pattern - return NULL/error
288
+ raise PermissionError(
289
+ f"Action blocked by Agent Control Plane: {check_result.get('error', 'Policy violation')}"
290
+ )
291
+ else:
292
+ self.logger.info(f"ALLOWED: {tool_name} for agent {self.agent_context.agent_id}")
293
+
294
+ def _map_tool_to_action(self, tool_name: str) -> Optional[ActionType]:
295
+ """
296
+ Map a LangChain tool name to an ActionType.
297
+
298
+ This uses both the provided mapping and pattern matching
299
+ to handle various naming conventions.
300
+
301
+ Args:
302
+ tool_name: The name of the tool from LangChain
303
+
304
+ Returns:
305
+ ActionType if mapped, None if unknown
306
+ """
307
+ # Check exact match first
308
+ tool_name_lower = tool_name.lower()
309
+ if tool_name_lower in self.tool_mapping:
310
+ return self.tool_mapping[tool_name_lower]
311
+
312
+ # Pattern matching for common variations
313
+ if any(pattern in tool_name_lower for pattern in ['read', 'get', 'fetch', 'load']) and \
314
+ any(pattern in tool_name_lower for pattern in ['file', 'document']):
315
+ return ActionType.FILE_READ
316
+
317
+ if any(pattern in tool_name_lower for pattern in ['write', 'save', 'create', 'update']) and \
318
+ any(pattern in tool_name_lower for pattern in ['file', 'document']):
319
+ return ActionType.FILE_WRITE
320
+
321
+ if any(pattern in tool_name_lower for pattern in ['exec', 'run', 'execute', 'eval', 'repl', 'python', 'terminal', 'shell', 'bash']):
322
+ return ActionType.CODE_EXECUTION
323
+
324
+ if any(pattern in tool_name_lower for pattern in ['sql', 'query', 'database', 'db']):
325
+ # Check if it's a write operation
326
+ if any(pattern in tool_name_lower for pattern in ['insert', 'update', 'delete', 'drop', 'create', 'alter']):
327
+ return ActionType.DATABASE_WRITE
328
+ return ActionType.DATABASE_QUERY
329
+
330
+ if any(pattern in tool_name_lower for pattern in ['api', 'http', 'request', 'search', 'serpapi', 'google', 'wikipedia']):
331
+ return ActionType.API_CALL
332
+
333
+ if any(pattern in tool_name_lower for pattern in ['workflow', 'trigger', 'pipeline']):
334
+ return ActionType.WORKFLOW_TRIGGER
335
+
336
+ return None
337
+
338
+ def add_tool_mapping(self, tool_name: str, action_type: ActionType):
339
+ """
340
+ Add a custom tool name to ActionType mapping.
341
+
342
+ Args:
343
+ tool_name: The name of the tool as used in LangChain
344
+ action_type: The ActionType it should map to
345
+ """
346
+ self.tool_mapping[tool_name.lower()] = action_type
347
+ self.logger.debug(f"Added tool mapping: {tool_name} -> {action_type.value}")
348
+
349
+ def get_statistics(self) -> Dict[str, Any]:
350
+ """
351
+ Get statistics about the adapter's activity.
352
+
353
+ Returns:
354
+ Dictionary with statistics from the control plane
355
+ """
356
+ return {
357
+ "agent_id": self.agent_context.agent_id,
358
+ "session_id": self.agent_context.session_id,
359
+ "control_plane_audit": self.control_plane.get_audit_log(limit=100),
360
+ "execution_history": self.control_plane.get_execution_history(
361
+ agent_id=self.agent_context.agent_id,
362
+ limit=100
363
+ )
364
+ }
365
+
366
+ # Proxy common LangChain methods to the wrapped client
367
+ def __getattr__(self, name):
368
+ """Proxy all other attributes to the wrapped client."""
369
+ return getattr(self.client, name)
370
+
371
+
372
+ def create_governed_langchain_client(
373
+ control_plane: AgentControlPlane,
374
+ agent_id: str,
375
+ langchain_client: Any,
376
+ permissions: Optional[Dict[ActionType, Any]] = None,
377
+ tool_mapping: Optional[Dict[str, ActionType]] = None
378
+ ) -> LangChainAdapter:
379
+ """
380
+ Convenience function to create a governed LangChain client.
381
+
382
+ This creates both the agent in the control plane and the adapter,
383
+ providing a one-line setup for governed LangChain interactions.
384
+
385
+ Args:
386
+ control_plane: The AgentControlPlane instance
387
+ agent_id: ID for the agent
388
+ langchain_client: The LangChain LLM or agent to wrap
389
+ permissions: Optional permissions for the agent
390
+ tool_mapping: Optional custom tool name mappings
391
+
392
+ Returns:
393
+ A LangChainAdapter ready to use
394
+
395
+ Example:
396
+ from langchain.chat_models import ChatOpenAI
397
+ from agent_control_plane import AgentControlPlane, PermissionLevel, ActionType
398
+ from agent_control_plane.langchain_adapter import create_governed_langchain_client
399
+
400
+ control_plane = AgentControlPlane()
401
+ llm = ChatOpenAI(temperature=0)
402
+
403
+ governed_llm = create_governed_langchain_client(
404
+ control_plane,
405
+ "my-agent",
406
+ llm,
407
+ permissions={
408
+ ActionType.FILE_READ: PermissionLevel.READ_ONLY,
409
+ ActionType.DATABASE_QUERY: PermissionLevel.READ_ONLY
410
+ }
411
+ )
412
+
413
+ # Use in LangChain agents
414
+ from langchain.agents import initialize_agent
415
+ agent = initialize_agent(tools, governed_llm, agent="zero-shot-react-description")
416
+ """
417
+ agent_context = control_plane.create_agent(agent_id, permissions)
418
+
419
+ return LangChainAdapter(
420
+ control_plane=control_plane,
421
+ agent_context=agent_context,
422
+ langchain_client=langchain_client,
423
+ tool_mapping=tool_mapping
424
+ )