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,537 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Safe Text Processing Tool.
5
+
6
+ Provides safe text operations with:
7
+ - No regex with catastrophic backtracking
8
+ - Size limits
9
+ - Safe string operations only
10
+ """
11
+
12
+ import logging
13
+ import re
14
+ import hashlib
15
+ from typing import Any, Dict, List, Optional
16
+
17
+ from atr.decorator import tool
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class TextTool:
23
+ """
24
+ Safe text processing operations.
25
+
26
+ Features:
27
+ - String manipulation (split, join, replace, etc.)
28
+ - Safe regex with timeout protection
29
+ - Text analysis (word count, etc.)
30
+ - Encoding/decoding
31
+ - No arbitrary code execution
32
+
33
+ Example:
34
+ ```python
35
+ text = TextTool(max_length=100000)
36
+
37
+ # Basic operations
38
+ result = text.split("hello world", " ")
39
+ result = text.replace("hello", "hi", "hello world")
40
+
41
+ # Analysis
42
+ stats = text.analyze("Hello world!")
43
+
44
+ # Safe regex
45
+ matches = text.regex_find(r"\\d+", "abc123def456")
46
+ ```
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ max_length: int = 1_000_000,
52
+ max_regex_length: int = 200,
53
+ max_matches: int = 1000
54
+ ):
55
+ """
56
+ Initialize text tool.
57
+
58
+ Args:
59
+ max_length: Maximum text length to process
60
+ max_regex_length: Maximum regex pattern length
61
+ max_matches: Maximum regex matches to return
62
+ """
63
+ self.max_length = max_length
64
+ self.max_regex_length = max_regex_length
65
+ self.max_matches = max_matches
66
+
67
+ def _check_length(self, text: str, name: str = "text"):
68
+ """Check text length."""
69
+ if len(text) > self.max_length:
70
+ raise ValueError(f"{name} too long: {len(text)}. Max: {self.max_length}")
71
+
72
+ def _validate_regex(self, pattern: str):
73
+ """Validate regex pattern for safety."""
74
+ if len(pattern) > self.max_regex_length:
75
+ raise ValueError(f"Regex pattern too long. Max: {self.max_regex_length}")
76
+
77
+ # Check for potentially catastrophic patterns
78
+ dangerous_patterns = [
79
+ r"(.+)+", # Nested quantifiers
80
+ r"(.*)*",
81
+ r"(a+)+",
82
+ r"(a*)*",
83
+ ]
84
+
85
+ for dangerous in dangerous_patterns:
86
+ if dangerous in pattern:
87
+ raise ValueError(f"Potentially dangerous regex pattern detected")
88
+
89
+ # Try to compile
90
+ try:
91
+ re.compile(pattern)
92
+ except re.error as e:
93
+ raise ValueError(f"Invalid regex: {e}")
94
+
95
+ @tool(
96
+ name="text_split",
97
+ description="Split text by a delimiter",
98
+ tags=["text", "split", "safe"]
99
+ )
100
+ def split(
101
+ self,
102
+ text: str,
103
+ delimiter: str = " ",
104
+ max_splits: Optional[int] = None
105
+ ) -> Dict[str, Any]:
106
+ """
107
+ Split text by delimiter.
108
+
109
+ Args:
110
+ text: Text to split
111
+ delimiter: Delimiter string
112
+ max_splits: Maximum number of splits
113
+
114
+ Returns:
115
+ Dict with parts list
116
+ """
117
+ try:
118
+ self._check_length(text)
119
+
120
+ if max_splits:
121
+ parts = text.split(delimiter, max_splits)
122
+ else:
123
+ parts = text.split(delimiter)
124
+
125
+ return {
126
+ "success": True,
127
+ "parts": parts,
128
+ "count": len(parts)
129
+ }
130
+ except Exception as e:
131
+ return {"success": False, "error": str(e)}
132
+
133
+ @tool(
134
+ name="text_join",
135
+ description="Join text parts with a delimiter",
136
+ tags=["text", "join", "safe"]
137
+ )
138
+ def join(self, parts: List[str], delimiter: str = " ") -> Dict[str, Any]:
139
+ """
140
+ Join text parts.
141
+
142
+ Args:
143
+ parts: List of strings to join
144
+ delimiter: Delimiter string
145
+
146
+ Returns:
147
+ Dict with joined text
148
+ """
149
+ try:
150
+ result = delimiter.join(parts)
151
+ self._check_length(result, "result")
152
+
153
+ return {
154
+ "success": True,
155
+ "result": result,
156
+ "length": len(result)
157
+ }
158
+ except Exception as e:
159
+ return {"success": False, "error": str(e)}
160
+
161
+ @tool(
162
+ name="text_replace",
163
+ description="Replace occurrences in text",
164
+ tags=["text", "replace", "safe"]
165
+ )
166
+ def replace(
167
+ self,
168
+ text: str,
169
+ old: str,
170
+ new: str,
171
+ count: int = -1
172
+ ) -> Dict[str, Any]:
173
+ """
174
+ Replace text occurrences.
175
+
176
+ Args:
177
+ text: Input text
178
+ old: String to find
179
+ new: Replacement string
180
+ count: Max replacements (-1 for all)
181
+
182
+ Returns:
183
+ Dict with result
184
+ """
185
+ try:
186
+ self._check_length(text)
187
+
188
+ if count == -1:
189
+ result = text.replace(old, new)
190
+ else:
191
+ result = text.replace(old, new, count)
192
+
193
+ replacements = text.count(old) if count == -1 else min(text.count(old), count)
194
+
195
+ return {
196
+ "success": True,
197
+ "result": result,
198
+ "replacements": replacements
199
+ }
200
+ except Exception as e:
201
+ return {"success": False, "error": str(e)}
202
+
203
+ @tool(
204
+ name="text_analyze",
205
+ description="Analyze text and return statistics",
206
+ tags=["text", "analyze", "safe"]
207
+ )
208
+ def analyze(self, text: str) -> Dict[str, Any]:
209
+ """
210
+ Analyze text statistics.
211
+
212
+ Args:
213
+ text: Text to analyze
214
+
215
+ Returns:
216
+ Dict with text statistics
217
+ """
218
+ try:
219
+ self._check_length(text)
220
+
221
+ words = text.split()
222
+ lines = text.splitlines()
223
+ sentences = re.split(r'[.!?]+', text)
224
+ sentences = [s.strip() for s in sentences if s.strip()]
225
+
226
+ return {
227
+ "success": True,
228
+ "characters": len(text),
229
+ "characters_no_spaces": len(text.replace(" ", "")),
230
+ "words": len(words),
231
+ "lines": len(lines),
232
+ "sentences": len(sentences),
233
+ "paragraphs": len(text.split("\n\n")),
234
+ "avg_word_length": round(sum(len(w) for w in words) / max(len(words), 1), 2),
235
+ "unique_words": len(set(w.lower() for w in words))
236
+ }
237
+ except Exception as e:
238
+ return {"success": False, "error": str(e)}
239
+
240
+ @tool(
241
+ name="text_regex_find",
242
+ description="Find all regex matches in text",
243
+ tags=["text", "regex", "safe"]
244
+ )
245
+ def regex_find(
246
+ self,
247
+ pattern: str,
248
+ text: str,
249
+ flags: Optional[str] = None
250
+ ) -> Dict[str, Any]:
251
+ """
252
+ Find regex matches.
253
+
254
+ Args:
255
+ pattern: Regex pattern
256
+ text: Text to search
257
+ flags: Optional flags (i=ignorecase, m=multiline, s=dotall)
258
+
259
+ Returns:
260
+ Dict with matches
261
+ """
262
+ try:
263
+ self._check_length(text)
264
+ self._validate_regex(pattern)
265
+
266
+ # Parse flags
267
+ re_flags = 0
268
+ if flags:
269
+ if 'i' in flags.lower():
270
+ re_flags |= re.IGNORECASE
271
+ if 'm' in flags.lower():
272
+ re_flags |= re.MULTILINE
273
+ if 's' in flags.lower():
274
+ re_flags |= re.DOTALL
275
+
276
+ matches = re.findall(pattern, text, re_flags)
277
+
278
+ # Limit matches
279
+ if len(matches) > self.max_matches:
280
+ matches = matches[:self.max_matches]
281
+ truncated = True
282
+ else:
283
+ truncated = False
284
+
285
+ return {
286
+ "success": True,
287
+ "matches": matches,
288
+ "count": len(matches),
289
+ "truncated": truncated
290
+ }
291
+ except Exception as e:
292
+ return {"success": False, "error": str(e)}
293
+
294
+ @tool(
295
+ name="text_regex_replace",
296
+ description="Replace regex matches in text",
297
+ tags=["text", "regex", "safe"]
298
+ )
299
+ def regex_replace(
300
+ self,
301
+ pattern: str,
302
+ replacement: str,
303
+ text: str,
304
+ count: int = 0
305
+ ) -> Dict[str, Any]:
306
+ """
307
+ Replace regex matches.
308
+
309
+ Args:
310
+ pattern: Regex pattern
311
+ replacement: Replacement string
312
+ text: Text to process
313
+ count: Max replacements (0 for all)
314
+
315
+ Returns:
316
+ Dict with result
317
+ """
318
+ try:
319
+ self._check_length(text)
320
+ self._validate_regex(pattern)
321
+
322
+ result, num_subs = re.subn(pattern, replacement, text, count=count)
323
+
324
+ return {
325
+ "success": True,
326
+ "result": result,
327
+ "replacements": num_subs
328
+ }
329
+ except Exception as e:
330
+ return {"success": False, "error": str(e)}
331
+
332
+ @tool(
333
+ name="text_trim",
334
+ description="Trim whitespace from text",
335
+ tags=["text", "trim", "safe"]
336
+ )
337
+ def trim(
338
+ self,
339
+ text: str,
340
+ chars: Optional[str] = None,
341
+ side: str = "both"
342
+ ) -> Dict[str, Any]:
343
+ """
344
+ Trim characters from text.
345
+
346
+ Args:
347
+ text: Text to trim
348
+ chars: Characters to trim (default: whitespace)
349
+ side: "left", "right", or "both"
350
+
351
+ Returns:
352
+ Dict with trimmed text
353
+ """
354
+ try:
355
+ if side == "left":
356
+ result = text.lstrip(chars)
357
+ elif side == "right":
358
+ result = text.rstrip(chars)
359
+ else:
360
+ result = text.strip(chars)
361
+
362
+ return {
363
+ "success": True,
364
+ "result": result,
365
+ "removed": len(text) - len(result)
366
+ }
367
+ except Exception as e:
368
+ return {"success": False, "error": str(e)}
369
+
370
+ @tool(
371
+ name="text_case",
372
+ description="Change text case",
373
+ tags=["text", "case", "safe"]
374
+ )
375
+ def change_case(self, text: str, case: str = "lower") -> Dict[str, Any]:
376
+ """
377
+ Change text case.
378
+
379
+ Args:
380
+ text: Text to transform
381
+ case: "lower", "upper", "title", "capitalize", "swapcase"
382
+
383
+ Returns:
384
+ Dict with transformed text
385
+ """
386
+ try:
387
+ self._check_length(text)
388
+
389
+ case_funcs = {
390
+ "lower": str.lower,
391
+ "upper": str.upper,
392
+ "title": str.title,
393
+ "capitalize": str.capitalize,
394
+ "swapcase": str.swapcase
395
+ }
396
+
397
+ if case not in case_funcs:
398
+ return {
399
+ "success": False,
400
+ "error": f"Unknown case: {case}. Use: {', '.join(case_funcs.keys())}"
401
+ }
402
+
403
+ result = case_funcs[case](text)
404
+
405
+ return {
406
+ "success": True,
407
+ "result": result,
408
+ "case": case
409
+ }
410
+ except Exception as e:
411
+ return {"success": False, "error": str(e)}
412
+
413
+ @tool(
414
+ name="text_hash",
415
+ description="Generate hash of text",
416
+ tags=["text", "hash", "safe"]
417
+ )
418
+ def hash(self, text: str, algorithm: str = "sha256") -> Dict[str, Any]:
419
+ """
420
+ Generate hash of text.
421
+
422
+ Args:
423
+ text: Text to hash
424
+ algorithm: Hash algorithm (md5, sha1, sha256, sha512)
425
+
426
+ Returns:
427
+ Dict with hash
428
+ """
429
+ try:
430
+ algorithms = {
431
+ "md5": hashlib.md5,
432
+ "sha1": hashlib.sha1,
433
+ "sha256": hashlib.sha256,
434
+ "sha512": hashlib.sha512
435
+ }
436
+
437
+ if algorithm in ("md5", "sha1"):
438
+ logger.warning(
439
+ "Algorithm '%s' is deprecated due to CWE-328. Use 'sha256' or 'sha512'.",
440
+ algorithm,
441
+ )
442
+
443
+ if algorithm not in algorithms:
444
+ return {
445
+ "success": False,
446
+ "error": f"Unknown algorithm. Use: {', '.join(algorithms.keys())}"
447
+ }
448
+
449
+ hasher = algorithms[algorithm]()
450
+ hasher.update(text.encode('utf-8'))
451
+
452
+ return {
453
+ "success": True,
454
+ "hash": hasher.hexdigest(),
455
+ "algorithm": algorithm
456
+ }
457
+ except Exception as e:
458
+ return {"success": False, "error": str(e)}
459
+
460
+ @tool(
461
+ name="text_contains",
462
+ description="Check if text contains a substring",
463
+ tags=["text", "search", "safe"]
464
+ )
465
+ def contains(
466
+ self,
467
+ text: str,
468
+ substring: str,
469
+ case_sensitive: bool = True
470
+ ) -> Dict[str, Any]:
471
+ """
472
+ Check if text contains substring.
473
+
474
+ Args:
475
+ text: Text to search in
476
+ substring: String to find
477
+ case_sensitive: Case sensitive search
478
+
479
+ Returns:
480
+ Dict with result
481
+ """
482
+ try:
483
+ if case_sensitive:
484
+ found = substring in text
485
+ count = text.count(substring)
486
+ else:
487
+ found = substring.lower() in text.lower()
488
+ count = text.lower().count(substring.lower())
489
+
490
+ return {
491
+ "success": True,
492
+ "contains": found,
493
+ "count": count
494
+ }
495
+ except Exception as e:
496
+ return {"success": False, "error": str(e)}
497
+
498
+ @tool(
499
+ name="text_truncate",
500
+ description="Truncate text to a maximum length",
501
+ tags=["text", "truncate", "safe"]
502
+ )
503
+ def truncate(
504
+ self,
505
+ text: str,
506
+ max_length: int,
507
+ suffix: str = "..."
508
+ ) -> Dict[str, Any]:
509
+ """
510
+ Truncate text with suffix.
511
+
512
+ Args:
513
+ text: Text to truncate
514
+ max_length: Maximum length
515
+ suffix: Suffix to add if truncated
516
+
517
+ Returns:
518
+ Dict with result
519
+ """
520
+ try:
521
+ if len(text) <= max_length:
522
+ return {
523
+ "success": True,
524
+ "result": text,
525
+ "truncated": False
526
+ }
527
+
528
+ result = text[:max_length - len(suffix)] + suffix
529
+
530
+ return {
531
+ "success": True,
532
+ "result": result,
533
+ "truncated": True,
534
+ "original_length": len(text)
535
+ }
536
+ except Exception as e:
537
+ return {"success": False, "error": str(e)}
@@ -0,0 +1,175 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Safe Toolkit Factory.
5
+
6
+ Creates pre-configured collections of safe tools for common use cases.
7
+ """
8
+
9
+ from typing import Dict, List, Optional, Any
10
+
11
+ from atr.tools.safe.http_client import HttpClientTool
12
+ from atr.tools.safe.file_reader import FileReaderTool
13
+ from atr.tools.safe.json_parser import JsonParserTool
14
+ from atr.tools.safe.calculator import CalculatorTool
15
+ from atr.tools.safe.datetime_tool import DateTimeTool
16
+ from atr.tools.safe.text_tool import TextTool
17
+
18
+
19
+ def create_safe_toolkit(
20
+ preset: str = "standard",
21
+ config: Optional[Dict[str, Any]] = None
22
+ ) -> Dict[str, Any]:
23
+ """
24
+ Create a pre-configured toolkit of safe tools.
25
+
26
+ Presets:
27
+ - "minimal": Only calculator, datetime, text (no I/O)
28
+ - "standard": All tools with sensible defaults
29
+ - "restricted": Standard but with more restrictions
30
+ - "readonly": File reader + JSON parser + calculator
31
+ - "network": HTTP client only
32
+
33
+ Args:
34
+ preset: Preset name
35
+ config: Optional configuration overrides
36
+
37
+ Returns:
38
+ Dict with tool instances and registry helper
39
+
40
+ Example:
41
+ ```python
42
+ # Create standard toolkit
43
+ toolkit = create_safe_toolkit("standard")
44
+
45
+ # Access tools
46
+ http = toolkit["http"]
47
+ files = toolkit["files"]
48
+
49
+ # Register all tools with ATR
50
+ from atr import ToolRegistry
51
+ registry = ToolRegistry()
52
+ toolkit["register_all"](registry)
53
+ ```
54
+ """
55
+ config = config or {}
56
+
57
+ tools = {}
58
+
59
+ if preset == "minimal":
60
+ # No I/O tools - just processing
61
+ tools["calculator"] = CalculatorTool(
62
+ precision=config.get("precision", 15)
63
+ )
64
+ tools["datetime"] = DateTimeTool(
65
+ default_timezone=config.get("timezone", "UTC")
66
+ )
67
+ tools["text"] = TextTool(
68
+ max_length=config.get("max_text_length", 100000)
69
+ )
70
+
71
+ elif preset == "readonly":
72
+ # File reading and processing
73
+ tools["files"] = FileReaderTool(
74
+ sandbox_paths=config.get("sandbox_paths"),
75
+ allowed_extensions=config.get("allowed_extensions", [
76
+ ".txt", ".json", ".yaml", ".yml", ".md", ".csv", ".xml"
77
+ ]),
78
+ max_file_size=config.get("max_file_size", 1_000_000)
79
+ )
80
+ tools["json"] = JsonParserTool(
81
+ max_size=config.get("max_json_size", 1_000_000)
82
+ )
83
+ tools["calculator"] = CalculatorTool()
84
+ tools["datetime"] = DateTimeTool()
85
+ tools["text"] = TextTool()
86
+
87
+ elif preset == "network":
88
+ # HTTP only
89
+ tools["http"] = HttpClientTool(
90
+ allowed_domains=config.get("allowed_domains"),
91
+ blocked_domains=config.get("blocked_domains"),
92
+ rate_limit=config.get("rate_limit", 60),
93
+ timeout=config.get("timeout", 30),
94
+ max_response_size=config.get("max_response_size", 10_000_000)
95
+ )
96
+
97
+ elif preset == "restricted":
98
+ # All tools with stricter limits
99
+ tools["http"] = HttpClientTool(
100
+ allowed_domains=config.get("allowed_domains", []), # Must specify domains
101
+ rate_limit=config.get("rate_limit", 10), # Lower rate limit
102
+ timeout=config.get("timeout", 10),
103
+ max_response_size=config.get("max_response_size", 1_000_000)
104
+ )
105
+ tools["files"] = FileReaderTool(
106
+ sandbox_paths=config.get("sandbox_paths", []), # Must specify paths
107
+ allowed_extensions=config.get("allowed_extensions", [".txt", ".json"]),
108
+ max_file_size=config.get("max_file_size", 100_000),
109
+ follow_symlinks=False
110
+ )
111
+ tools["json"] = JsonParserTool(
112
+ max_size=config.get("max_json_size", 100_000),
113
+ max_depth=config.get("max_depth", 20)
114
+ )
115
+ tools["calculator"] = CalculatorTool(precision=10)
116
+ tools["datetime"] = DateTimeTool()
117
+ tools["text"] = TextTool(max_length=10000)
118
+
119
+ else: # "standard" or default
120
+ # All tools with sensible defaults
121
+ tools["http"] = HttpClientTool(
122
+ allowed_domains=config.get("allowed_domains"),
123
+ blocked_domains=config.get("blocked_domains"),
124
+ rate_limit=config.get("rate_limit", 60),
125
+ timeout=config.get("timeout", 30),
126
+ max_response_size=config.get("max_response_size", 10_000_000)
127
+ )
128
+ tools["files"] = FileReaderTool(
129
+ sandbox_paths=config.get("sandbox_paths"),
130
+ allowed_extensions=config.get("allowed_extensions"),
131
+ max_file_size=config.get("max_file_size", 10_000_000)
132
+ )
133
+ tools["json"] = JsonParserTool(
134
+ max_size=config.get("max_json_size", 10_000_000)
135
+ )
136
+ tools["calculator"] = CalculatorTool(
137
+ precision=config.get("precision", 15)
138
+ )
139
+ tools["datetime"] = DateTimeTool(
140
+ default_timezone=config.get("timezone", "UTC")
141
+ )
142
+ tools["text"] = TextTool(
143
+ max_length=config.get("max_text_length", 1_000_000)
144
+ )
145
+
146
+ # Helper function to register all tools
147
+ def register_all(registry):
148
+ """Register all tools with an ATR registry."""
149
+ for name, tool in tools.items():
150
+ if name.startswith("_"):
151
+ continue
152
+ # Register tool methods
153
+ for method_name in dir(tool):
154
+ if method_name.startswith("_"):
155
+ continue
156
+ method = getattr(tool, method_name)
157
+ if callable(method) and hasattr(method, "_tool_metadata"):
158
+ registry.register(method)
159
+
160
+ tools["register_all"] = register_all
161
+ tools["_preset"] = preset
162
+ tools["_config"] = config
163
+
164
+ return tools
165
+
166
+
167
+ def list_presets() -> Dict[str, str]:
168
+ """List available toolkit presets."""
169
+ return {
170
+ "minimal": "Calculator, datetime, text - no I/O operations",
171
+ "standard": "All tools with sensible defaults",
172
+ "restricted": "All tools with stricter security limits",
173
+ "readonly": "File reader, JSON parser, calculator - no network",
174
+ "network": "HTTP client only - no file access"
175
+ }