aip-agents-binary 0.0.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 (447) hide show
  1. aip_agents/__init__.py +66 -0
  2. aip_agents/a2a/__init__.py +19 -0
  3. aip_agents/a2a/server/__init__.py +10 -0
  4. aip_agents/a2a/server/base_executor.py +1125 -0
  5. aip_agents/a2a/server/google_adk_executor.py +198 -0
  6. aip_agents/a2a/server/langflow_executor.py +180 -0
  7. aip_agents/a2a/server/langgraph_executor.py +270 -0
  8. aip_agents/a2a/types.py +232 -0
  9. aip_agents/a2ui/__init__.py +26 -0
  10. aip_agents/a2ui/catalog_provider.py +109 -0
  11. aip_agents/a2ui/examples/0.8/booking_form.json +1 -0
  12. aip_agents/a2ui/examples/0.8/confirmation.json +1 -0
  13. aip_agents/a2ui/examples/0.8/single_column_list.json +1 -0
  14. aip_agents/a2ui/examples/0.9/booking_form.json +1 -0
  15. aip_agents/a2ui/examples/0.9/confirmation.json +1 -0
  16. aip_agents/a2ui/examples/0.9/single_column_list.json +1 -0
  17. aip_agents/a2ui/middleware.py +754 -0
  18. aip_agents/a2ui/models.py +153 -0
  19. aip_agents/a2ui/public_url.py +138 -0
  20. aip_agents/a2ui/stream_filter.py +67 -0
  21. aip_agents/agent/__init__.py +72 -0
  22. aip_agents/agent/base_agent.py +1437 -0
  23. aip_agents/agent/base_langgraph_agent.py +4048 -0
  24. aip_agents/agent/google_adk_agent.py +932 -0
  25. aip_agents/agent/google_adk_constants.py +11 -0
  26. aip_agents/agent/graph_runtime.py +185 -0
  27. aip_agents/agent/hitl/__init__.py +24 -0
  28. aip_agents/agent/hitl/config.py +28 -0
  29. aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
  30. aip_agents/agent/hitl/manager.py +532 -0
  31. aip_agents/agent/hitl/models.py +18 -0
  32. aip_agents/agent/hitl/prompt/__init__.py +9 -0
  33. aip_agents/agent/hitl/prompt/base.py +42 -0
  34. aip_agents/agent/hitl/prompt/deferred.py +73 -0
  35. aip_agents/agent/hitl/registry.py +149 -0
  36. aip_agents/agent/interface.py +138 -0
  37. aip_agents/agent/interfaces.py +65 -0
  38. aip_agents/agent/langflow_agent.py +464 -0
  39. aip_agents/agent/langgraph_react_agent.py +3741 -0
  40. aip_agents/agent/model_invocation_adapter.py +346 -0
  41. aip_agents/agent/system_instruction_context.py +34 -0
  42. aip_agents/agent/tool_execution_engine.py +587 -0
  43. aip_agents/audio_interface/__init__.py +30 -0
  44. aip_agents/audio_interface/audio_agent_adapter.py +254 -0
  45. aip_agents/audio_interface/config.py +146 -0
  46. aip_agents/audio_interface/errors.py +15 -0
  47. aip_agents/audio_interface/livekit_audio_session.py +504 -0
  48. aip_agents/audio_interface/livekit_realtime_audio_session.py +142 -0
  49. aip_agents/audio_interface/session_factory.py +40 -0
  50. aip_agents/clients/__init__.py +10 -0
  51. aip_agents/clients/langflow/__init__.py +10 -0
  52. aip_agents/clients/langflow/client.py +477 -0
  53. aip_agents/clients/langflow/types.py +18 -0
  54. aip_agents/common/__init__.py +5 -0
  55. aip_agents/common/callable_introspection.py +17 -0
  56. aip_agents/constants.py +23 -0
  57. aip_agents/context/__init__.py +5 -0
  58. aip_agents/context/budget.py +237 -0
  59. aip_agents/context/compaction.py +339 -0
  60. aip_agents/context/metadata.py +169 -0
  61. aip_agents/context/offload.py +449 -0
  62. aip_agents/context/preparation.py +196 -0
  63. aip_agents/context/prompt.py +61 -0
  64. aip_agents/context/runtime.py +149 -0
  65. aip_agents/context/session_recall.py +69 -0
  66. aip_agents/context/state.py +48 -0
  67. aip_agents/context/utils.py +44 -0
  68. aip_agents/credentials/manager.py +132 -0
  69. aip_agents/examples/__init__.py +5 -0
  70. aip_agents/examples/audio_livekit_session.py +116 -0
  71. aip_agents/examples/compare_streaming_client.py +783 -0
  72. aip_agents/examples/compare_streaming_server.py +141 -0
  73. aip_agents/examples/gl_connectors_smoke.py +148 -0
  74. aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
  75. aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
  76. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
  77. aip_agents/examples/hello_world_a2a_google_adk_server.py +78 -0
  78. aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
  79. aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
  80. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
  81. aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
  82. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
  83. aip_agents/examples/hello_world_a2a_langchain_reference_server.py +104 -0
  84. aip_agents/examples/hello_world_a2a_langchain_server.py +78 -0
  85. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +77 -0
  86. aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
  87. aip_agents/examples/hello_world_a2a_langflow_server.py +80 -0
  88. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
  89. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
  90. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +91 -0
  91. aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
  92. aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
  93. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
  94. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
  95. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
  96. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
  97. aip_agents/examples/hello_world_a2a_langgraph_server.py +83 -0
  98. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +78 -0
  99. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +131 -0
  100. aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
  101. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
  102. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +250 -0
  103. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
  104. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +79 -0
  105. aip_agents/examples/hello_world_filesystem.py +162 -0
  106. aip_agents/examples/hello_world_google_adk.py +41 -0
  107. aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
  108. aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
  109. aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
  110. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
  111. aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
  112. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
  113. aip_agents/examples/hello_world_google_adk_stream.py +44 -0
  114. aip_agents/examples/hello_world_langchain.py +28 -0
  115. aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
  116. aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
  117. aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
  118. aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
  119. aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
  120. aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
  121. aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
  122. aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
  123. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
  124. aip_agents/examples/hello_world_langchain_stream.py +36 -0
  125. aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
  126. aip_agents/examples/hello_world_langflow_agent.py +163 -0
  127. aip_agents/examples/hello_world_langgraph.py +39 -0
  128. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.py +44 -0
  129. aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
  130. aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
  131. aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
  132. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
  133. aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
  134. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
  135. aip_agents/examples/hello_world_langgraph_skills.py +62 -0
  136. aip_agents/examples/hello_world_langgraph_stream.py +43 -0
  137. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
  138. aip_agents/examples/hello_world_model_switch_cli.py +210 -0
  139. aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
  140. aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
  141. aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
  142. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
  143. aip_agents/examples/hello_world_multi_agent_tool_output_client.py +57 -0
  144. aip_agents/examples/hello_world_multi_agent_tool_output_server.py +131 -0
  145. aip_agents/examples/hello_world_pii_logger.py +21 -0
  146. aip_agents/examples/hello_world_ptc.py +49 -0
  147. aip_agents/examples/hello_world_ptc_custom_tools.py +83 -0
  148. aip_agents/examples/hello_world_sentry.py +133 -0
  149. aip_agents/examples/hello_world_step_limits.py +273 -0
  150. aip_agents/examples/hello_world_stock_a2a_server.py +100 -0
  151. aip_agents/examples/hello_world_tool_output_client.py +55 -0
  152. aip_agents/examples/hello_world_tool_output_server.py +113 -0
  153. aip_agents/examples/hitl_demo.py +724 -0
  154. aip_agents/examples/mcp_configs/configs.py +63 -0
  155. aip_agents/examples/mcp_servers/common.py +76 -0
  156. aip_agents/examples/mcp_servers/mcp_name.py +29 -0
  157. aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
  158. aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
  159. aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
  160. aip_agents/examples/mcp_servers/mcp_time.py +10 -0
  161. aip_agents/examples/pii_demo_langgraph_client.py +69 -0
  162. aip_agents/examples/pii_demo_langgraph_server.py +125 -0
  163. aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
  164. aip_agents/examples/pii_demo_multi_agent_server.py +246 -0
  165. aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
  166. aip_agents/examples/todolist_planning_a2a_langgraph_server.py +87 -0
  167. aip_agents/examples/tools/__init__.py +27 -0
  168. aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
  169. aip_agents/examples/tools/adk_weather_tool.py +60 -0
  170. aip_agents/examples/tools/data_generator_tool.py +103 -0
  171. aip_agents/examples/tools/data_visualization_tool.py +312 -0
  172. aip_agents/examples/tools/image_artifact_tool.py +136 -0
  173. aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
  174. aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
  175. aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
  176. aip_agents/examples/tools/langchain_weather_tool.py +48 -0
  177. aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
  178. aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
  179. aip_agents/examples/tools/multiply_tool.py +43 -0
  180. aip_agents/examples/tools/pii_demo_tools.py +189 -0
  181. aip_agents/examples/tools/random_chart_tool.py +142 -0
  182. aip_agents/examples/tools/serper_tool.py +202 -0
  183. aip_agents/examples/tools/stock_tools.py +82 -0
  184. aip_agents/examples/tools/table_generator_tool.py +167 -0
  185. aip_agents/examples/tools/time_tool.py +82 -0
  186. aip_agents/examples/tools/weather_forecast_tool.py +38 -0
  187. aip_agents/guardrails/__init__.py +83 -0
  188. aip_agents/guardrails/engines/__init__.py +69 -0
  189. aip_agents/guardrails/engines/base.py +90 -0
  190. aip_agents/guardrails/engines/nemo.py +101 -0
  191. aip_agents/guardrails/engines/phrase_matcher.py +113 -0
  192. aip_agents/guardrails/exceptions.py +39 -0
  193. aip_agents/guardrails/manager.py +163 -0
  194. aip_agents/guardrails/middleware.py +199 -0
  195. aip_agents/guardrails/schemas.py +63 -0
  196. aip_agents/guardrails/utils.py +45 -0
  197. aip_agents/integration/__init__.py +43 -0
  198. aip_agents/integration/agent.py +58 -0
  199. aip_agents/integration/guardrails.py +52 -0
  200. aip_agents/integration/hitl.py +71 -0
  201. aip_agents/integration/ptc.py +95 -0
  202. aip_agents/integration/skills.py +37 -0
  203. aip_agents/integration/storage.py +81 -0
  204. aip_agents/integration/version.py +103 -0
  205. aip_agents/mcp/__init__.py +1 -0
  206. aip_agents/mcp/client/__init__.py +60 -0
  207. aip_agents/mcp/client/base_mcp_client.py +377 -0
  208. aip_agents/mcp/client/connection_manager.py +231 -0
  209. aip_agents/mcp/client/errors.py +119 -0
  210. aip_agents/mcp/client/google_adk/__init__.py +45 -0
  211. aip_agents/mcp/client/google_adk/client.py +383 -0
  212. aip_agents/mcp/client/langchain/__init__.py +11 -0
  213. aip_agents/mcp/client/langchain/client.py +265 -0
  214. aip_agents/mcp/client/persistent_session.py +704 -0
  215. aip_agents/mcp/client/session_pool.py +358 -0
  216. aip_agents/mcp/client/transports.py +288 -0
  217. aip_agents/mcp/utils/__init__.py +7 -0
  218. aip_agents/mcp/utils/config_validator.py +139 -0
  219. aip_agents/memory/__init__.py +14 -0
  220. aip_agents/memory/adapters/__init__.py +10 -0
  221. aip_agents/memory/adapters/base_adapter.py +815 -0
  222. aip_agents/memory/adapters/mem0.py +286 -0
  223. aip_agents/memory/base.py +84 -0
  224. aip_agents/memory/constants.py +49 -0
  225. aip_agents/memory/factory.py +86 -0
  226. aip_agents/memory/guidance.py +20 -0
  227. aip_agents/memory/simple_memory.py +47 -0
  228. aip_agents/middleware/__init__.py +65 -0
  229. aip_agents/middleware/backends/__init__.py +72 -0
  230. aip_agents/middleware/backends/in_memory.py +578 -0
  231. aip_agents/middleware/backends/local_disk.py +1203 -0
  232. aip_agents/middleware/backends/protocol.py +353 -0
  233. aip_agents/middleware/backends/sandbox.py +1431 -0
  234. aip_agents/middleware/backends/sandbox_files.py +113 -0
  235. aip_agents/middleware/backends/utils.py +293 -0
  236. aip_agents/middleware/base.py +239 -0
  237. aip_agents/middleware/filesystem.py +690 -0
  238. aip_agents/middleware/manager.py +291 -0
  239. aip_agents/middleware/memory.py +532 -0
  240. aip_agents/middleware/pii_middleware.py +192 -0
  241. aip_agents/middleware/schema.py +19 -0
  242. aip_agents/middleware/skills.py +872 -0
  243. aip_agents/middleware/task_delegation.py +1656 -0
  244. aip_agents/middleware/todolist.py +276 -0
  245. aip_agents/middleware/tools/__init__.py +23 -0
  246. aip_agents/middleware/tools/edit_file.py +70 -0
  247. aip_agents/middleware/tools/execute.py +428 -0
  248. aip_agents/middleware/tools/grep_file.py +79 -0
  249. aip_agents/middleware/tools/ls.py +71 -0
  250. aip_agents/middleware/tools/read_file.py +239 -0
  251. aip_agents/middleware/tools/skill_load.py +193 -0
  252. aip_agents/middleware/tools/skill_resource.py +338 -0
  253. aip_agents/middleware/tools/write_file.py +114 -0
  254. aip_agents/middleware/vision.py +322 -0
  255. aip_agents/ptc/__init__.py +87 -0
  256. aip_agents/ptc/custom_tools.py +511 -0
  257. aip_agents/ptc/custom_tools_payload.py +499 -0
  258. aip_agents/ptc/custom_tools_templates/__init__.py +1 -0
  259. aip_agents/ptc/custom_tools_templates/custom_build_function.py.template +24 -0
  260. aip_agents/ptc/custom_tools_templates/custom_init.py.template +26 -0
  261. aip_agents/ptc/custom_tools_templates/custom_invoke.py.template +149 -0
  262. aip_agents/ptc/custom_tools_templates/custom_registry.py.template +112 -0
  263. aip_agents/ptc/custom_tools_templates/custom_sources_init.py.template +7 -0
  264. aip_agents/ptc/custom_tools_templates/custom_wrapper.py.template +87 -0
  265. aip_agents/ptc/doc_gen.py +122 -0
  266. aip_agents/ptc/exceptions.py +57 -0
  267. aip_agents/ptc/executor.py +627 -0
  268. aip_agents/ptc/mcp/__init__.py +45 -0
  269. aip_agents/ptc/mcp/sandbox_bridge.py +679 -0
  270. aip_agents/ptc/mcp/templates/__init__.py +1 -0
  271. aip_agents/ptc/mcp/templates/mcp_client.py.template +240 -0
  272. aip_agents/ptc/naming.py +430 -0
  273. aip_agents/ptc/payload.py +26 -0
  274. aip_agents/ptc/prompt_builder.py +674 -0
  275. aip_agents/ptc/ptc_helper.py +16 -0
  276. aip_agents/ptc/sandbox_bridge.py +297 -0
  277. aip_agents/ptc/template_utils.py +33 -0
  278. aip_agents/ptc/templates/__init__.py +1 -0
  279. aip_agents/ptc/templates/ptc_helper.py.template +134 -0
  280. aip_agents/ptc/tool_def_helpers.py +101 -0
  281. aip_agents/ptc/tool_enrichment.py +163 -0
  282. aip_agents/resilience/__init__.py +10 -0
  283. aip_agents/resilience/base.py +46 -0
  284. aip_agents/resilience/tool.py +1661 -0
  285. aip_agents/sandbox/__init__.py +62 -0
  286. aip_agents/sandbox/defaults.py +220 -0
  287. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-1-datasaur.Dockerfile +120 -0
  288. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-1.Dockerfile +100 -0
  289. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-2-datasaur.Dockerfile +143 -0
  290. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-2.Dockerfile +119 -0
  291. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-3-datasaur.Dockerfile +173 -0
  292. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-3.Dockerfile +130 -0
  293. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.Dockerfile +164 -0
  294. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.patch_js_context.py +41 -0
  295. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.requirements.txt +29 -0
  296. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2.Dockerfile +87 -0
  297. aip_agents/sandbox/e2b_runtime.py +27 -0
  298. aip_agents/sandbox/image_registry.py +98 -0
  299. aip_agents/sandbox/providers/__init__.py +76 -0
  300. aip_agents/sandbox/providers/base.py +392 -0
  301. aip_agents/sandbox/providers/bedrock.py +28 -0
  302. aip_agents/sandbox/providers/e2b.py +230 -0
  303. aip_agents/sandbox/providers/opensandbox.py +163 -0
  304. aip_agents/sandbox/sandbox_runtime.py +967 -0
  305. aip_agents/sandbox/template_builder.py +240 -0
  306. aip_agents/sandbox/types.py +141 -0
  307. aip_agents/sandbox/validation.py +50 -0
  308. aip_agents/schema/__init__.py +106 -0
  309. aip_agents/schema/a2a.py +58 -0
  310. aip_agents/schema/agent.py +117 -0
  311. aip_agents/schema/artifacts.py +75 -0
  312. aip_agents/schema/attachments.py +425 -0
  313. aip_agents/schema/hitl.py +157 -0
  314. aip_agents/schema/langgraph.py +47 -0
  315. aip_agents/schema/model_id.py +103 -0
  316. aip_agents/schema/model_usage.py +50 -0
  317. aip_agents/schema/runtime_metadata.py +78 -0
  318. aip_agents/schema/step_limit.py +108 -0
  319. aip_agents/schema/storage.py +40 -0
  320. aip_agents/schema/tool_payloads.py +68 -0
  321. aip_agents/sentry/__init__.py +11 -0
  322. aip_agents/sentry/sentry.py +267 -0
  323. aip_agents/skills/__init__.py +30 -0
  324. aip_agents/skills/errors.py +110 -0
  325. aip_agents/skills/installer.py +479 -0
  326. aip_agents/skills/models.py +769 -0
  327. aip_agents/skills/stager.py +224 -0
  328. aip_agents/skills/validation.py +53 -0
  329. aip_agents/storage/__init__.py +41 -0
  330. aip_agents/storage/base.py +85 -0
  331. aip_agents/storage/clients/__init__.py +12 -0
  332. aip_agents/storage/clients/minio_client.py +318 -0
  333. aip_agents/storage/config.py +62 -0
  334. aip_agents/storage/providers/__init__.py +15 -0
  335. aip_agents/storage/providers/base.py +106 -0
  336. aip_agents/storage/providers/memory.py +114 -0
  337. aip_agents/storage/providers/object_storage.py +214 -0
  338. aip_agents/tools/__init__.py +65 -0
  339. aip_agents/tools/agent_schedule_tool.py +392 -0
  340. aip_agents/tools/browser_use/__init__.py +83 -0
  341. aip_agents/tools/browser_use/action_parser.py +104 -0
  342. aip_agents/tools/browser_use/browser_use_tool.py +1118 -0
  343. aip_agents/tools/browser_use/llm_config.py +120 -0
  344. aip_agents/tools/browser_use/minio_storage.py +203 -0
  345. aip_agents/tools/browser_use/schemas.py +121 -0
  346. aip_agents/tools/browser_use/session.py +13 -0
  347. aip_agents/tools/browser_use/session_errors.py +132 -0
  348. aip_agents/tools/browser_use/steel_session_recording.py +321 -0
  349. aip_agents/tools/browser_use/streaming.py +263 -0
  350. aip_agents/tools/browser_use/structured_data_parser.py +257 -0
  351. aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
  352. aip_agents/tools/browser_use/types.py +37 -0
  353. aip_agents/tools/code_sandbox/__init__.py +48 -0
  354. aip_agents/tools/code_sandbox/code_sandbox_tool.py +350 -0
  355. aip_agents/tools/code_sandbox/constant.py +13 -0
  356. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +346 -0
  357. aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +65 -0
  358. aip_agents/tools/computer_use/__init__.py +75 -0
  359. aip_agents/tools/computer_use/computer_use_tool.py +910 -0
  360. aip_agents/tools/computer_use/schemas.py +78 -0
  361. aip_agents/tools/computer_use/streaming.py +198 -0
  362. aip_agents/tools/computer_use/types.py +32 -0
  363. aip_agents/tools/constants.py +177 -0
  364. aip_agents/tools/date_range_tool.py +554 -0
  365. aip_agents/tools/document_loader/__init__.py +44 -0
  366. aip_agents/tools/document_loader/base_reader.py +302 -0
  367. aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
  368. aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
  369. aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
  370. aip_agents/tools/document_loader/pdf_splitter.py +169 -0
  371. aip_agents/tools/execute_ptc_code.py +582 -0
  372. aip_agents/tools/gl_connector/__init__.py +5 -0
  373. aip_agents/tools/gl_connector/tool.py +518 -0
  374. aip_agents/tools/gl_connector_tools.py +170 -0
  375. aip_agents/tools/memory_search/__init__.py +29 -0
  376. aip_agents/tools/memory_search/base.py +213 -0
  377. aip_agents/tools/memory_search/mem0.py +424 -0
  378. aip_agents/tools/memory_search/schema.py +81 -0
  379. aip_agents/tools/memory_search_tool.py +34 -0
  380. aip_agents/tools/streaming_common.py +577 -0
  381. aip_agents/tools/time_tool.py +117 -0
  382. aip_agents/tools/tool_config_injector.py +300 -0
  383. aip_agents/tools/web_search/__init__.py +15 -0
  384. aip_agents/tools/web_search/serper_tool.py +187 -0
  385. aip_agents/types/__init__.py +73 -0
  386. aip_agents/types/a2a_events.py +13 -0
  387. aip_agents/utils/__init__.py +91 -0
  388. aip_agents/utils/a2a_connector.py +1758 -0
  389. aip_agents/utils/artifact_helpers.py +542 -0
  390. aip_agents/utils/artifacts/__init__.py +20 -0
  391. aip_agents/utils/artifacts/tracker.py +367 -0
  392. aip_agents/utils/attachment_helpers.py +178 -0
  393. aip_agents/utils/constants.py +22 -0
  394. aip_agents/utils/datetime/__init__.py +34 -0
  395. aip_agents/utils/datetime/normalization.py +231 -0
  396. aip_agents/utils/datetime/timezone.py +206 -0
  397. aip_agents/utils/env_loader.py +27 -0
  398. aip_agents/utils/event_handler_registry.py +58 -0
  399. aip_agents/utils/file_prompt_utils.py +276 -0
  400. aip_agents/utils/file_watchers/__init__.py +33 -0
  401. aip_agents/utils/file_watchers/base.py +46 -0
  402. aip_agents/utils/file_watchers/e2b.py +128 -0
  403. aip_agents/utils/file_watchers/factory.py +39 -0
  404. aip_agents/utils/file_watchers/ls_diff.py +88 -0
  405. aip_agents/utils/file_watchers/sandbox.py +66 -0
  406. aip_agents/utils/file_watchers/watchdog.py +97 -0
  407. aip_agents/utils/final_response_builder.py +211 -0
  408. aip_agents/utils/formatter_llm_client.py +258 -0
  409. aip_agents/utils/langgraph/__init__.py +19 -0
  410. aip_agents/utils/langgraph/converter.py +399 -0
  411. aip_agents/utils/langgraph/delegation_context.py +22 -0
  412. aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
  413. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
  414. aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
  415. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1329 -0
  416. aip_agents/utils/langgraph/tool_output_management.py +1047 -0
  417. aip_agents/utils/logger.py +195 -0
  418. aip_agents/utils/metadata/__init__.py +27 -0
  419. aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
  420. aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
  421. aip_agents/utils/metadata/activity_narrative/builder.py +822 -0
  422. aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
  423. aip_agents/utils/metadata/activity_narrative/context.py +49 -0
  424. aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
  425. aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
  426. aip_agents/utils/metadata/schemas/__init__.py +16 -0
  427. aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
  428. aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
  429. aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
  430. aip_agents/utils/metadata_helper.py +363 -0
  431. aip_agents/utils/name_preprocessor/__init__.py +17 -0
  432. aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
  433. aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
  434. aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
  435. aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
  436. aip_agents/utils/pii/__init__.py +29 -0
  437. aip_agents/utils/pii/pii_handler.py +555 -0
  438. aip_agents/utils/pii/pii_helper.py +225 -0
  439. aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
  440. aip_agents/utils/reference_helper.py +273 -0
  441. aip_agents/utils/sse_chunk_transformer.py +836 -0
  442. aip_agents/utils/step_limit_manager.py +265 -0
  443. aip_agents/utils/token_usage_helper.py +531 -0
  444. aip_agents_binary-0.0.0.dist-info/METADATA +825 -0
  445. aip_agents_binary-0.0.0.dist-info/RECORD +447 -0
  446. aip_agents_binary-0.0.0.dist-info/WHEEL +5 -0
  447. aip_agents_binary-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,338 @@
1
+ """Skill-attributed resource discovery and loading tool."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import posixpath
8
+ import re
9
+ from typing import Annotated, Any, Final, TypedDict
10
+
11
+ from langchain_core.tools import BaseTool
12
+ from pydantic import BaseModel, Field, SkipValidation
13
+
14
+ from aip_agents.middleware.schema import MiddlewareToolConfig
15
+ from aip_agents.skills.errors import SkillValidationError
16
+ from aip_agents.skills.validation import validate_skill_name
17
+
18
+ MAX_SKILL_RESOURCE_CHARS: Final[int] = 20_000
19
+ _TOKEN_RE = re.compile(r"[a-z0-9]+")
20
+
21
+ __all__ = [
22
+ "MAX_SKILL_RESOURCE_CHARS",
23
+ "SkillResourceInput",
24
+ "SkillResourceResult",
25
+ "SkillResourceSummary",
26
+ "SkillResourceTool",
27
+ ]
28
+
29
+
30
+ class SkillResourceInput(BaseModel):
31
+ """Input schema for skill_resource."""
32
+
33
+ name: str = Field(description="Skill name that owns the resource")
34
+ path: str | None = Field(default=None, description="Resource backend path or skill-relative path")
35
+ query: str | None = Field(
36
+ default=None, description="Optional keyword query used to pick a resource from the manifest"
37
+ )
38
+ max_chars: int | None = Field(default=None, description="Maximum characters to return for text resources")
39
+
40
+
41
+ class SkillResourceSummary(TypedDict, total=False):
42
+ """Manifest-backed resource metadata."""
43
+
44
+ backend_path: str
45
+ relative_path: str
46
+ resource_type: str
47
+ size_bytes: int
48
+ is_binary: bool
49
+ summary: str | None
50
+ score: int
51
+
52
+
53
+ class SkillResourceResult(TypedDict, total=False):
54
+ """Structured output returned by `skill_resource`."""
55
+
56
+ name: str
57
+ backend_root: str | None
58
+ resource_index_path: str | None
59
+ resource_path: str | None
60
+ relative_path: str | None
61
+ resource_type: str | None
62
+ size_bytes: int | None
63
+ is_binary: bool | None
64
+ summary: str | None
65
+ content: str
66
+ content_hash: str
67
+ budget_truncated: bool
68
+ warnings: list[str]
69
+ matches: list[SkillResourceSummary]
70
+ error: str
71
+
72
+
73
+ class SkillResourceTool(BaseTool):
74
+ """Tool for skill-attributed support-resource discovery and loading."""
75
+
76
+ name: str = "skill_resource"
77
+ description: str = (
78
+ "Discover or load a staged skill support resource through the skill manifest. "
79
+ "Use this for references, examples, scripts, and assets after skill_load so resource use is skill-attributed."
80
+ )
81
+ args_schema: type[BaseModel] = SkillResourceInput # type: ignore[assignment]
82
+ tool_config_schema: type[BaseModel] = MiddlewareToolConfig
83
+ backend: Annotated[Any, SkipValidation()]
84
+ destination_root: str = "/skills"
85
+ skill_inventory: Annotated[Any, SkipValidation()]
86
+
87
+ def _run(
88
+ self,
89
+ name: str,
90
+ path: str | None = None,
91
+ query: str | None = None,
92
+ max_chars: int | None = None,
93
+ ) -> SkillResourceResult:
94
+ requested_name = name.strip()
95
+ try:
96
+ validate_skill_name(requested_name)
97
+ except SkillValidationError as exc:
98
+ return self._error_result(requested_name, f"Invalid skill name: {exc}")
99
+
100
+ skill_names = {skill.name for skill in self.skill_inventory() or []}
101
+ if requested_name not in skill_names:
102
+ return self._error_result(requested_name, f"Skill not found: {requested_name}")
103
+
104
+ backend_root = posixpath.join(self.destination_root, requested_name)
105
+ resource_index_path = posixpath.join(backend_root, ".aip-skill-index.json")
106
+ resources_result = self._load_manifest(requested_name, backend_root, resource_index_path)
107
+ if "error" in resources_result:
108
+ return resources_result
109
+
110
+ resources = resources_result["matches"]
111
+ selected = self._select_resource(resources, backend_root=backend_root, path=path, query=query)
112
+ if selected is None:
113
+ if path:
114
+ return self._error_result(requested_name, f"Resource not found in skill manifest: {path}")
115
+ return {
116
+ "name": requested_name,
117
+ "backend_root": backend_root,
118
+ "resource_index_path": resource_index_path,
119
+ "resource_path": None,
120
+ "relative_path": None,
121
+ "resource_type": None,
122
+ "size_bytes": None,
123
+ "is_binary": None,
124
+ "summary": None,
125
+ "content": "",
126
+ "content_hash": "",
127
+ "budget_truncated": False,
128
+ "warnings": ["No resource selected; inspect matches or provide path/query."],
129
+ "matches": self._rank_resources(resources, query),
130
+ }
131
+
132
+ return self._load_resource(requested_name, backend_root, resource_index_path, selected, max_chars=max_chars)
133
+
134
+ def _load_manifest(self, name: str, backend_root: str, resource_index_path: str) -> SkillResourceResult:
135
+ try:
136
+ manifest = json.loads(self.backend.read_bytes(resource_index_path).decode("utf-8"))
137
+ except Exception as exc: # pragma: no cover
138
+ return self._error_result(
139
+ name, f"Unable to read resource manifest at {resource_index_path}: {exc}"
140
+ ) # pragma: no cover
141
+
142
+ resources = manifest.get("resources", [])
143
+ if not isinstance(resources, list):
144
+ return self._error_result(
145
+ name, f"Invalid resource manifest at {resource_index_path}: resources must be a list"
146
+ )
147
+
148
+ summaries: list[SkillResourceSummary] = []
149
+ for item in resources:
150
+ if not isinstance(item, dict):
151
+ continue
152
+ relative_path = self._normalize_manifest_relative_path(str(item.get("relative_path", "")))
153
+ backend_path = self._normalize_manifest_backend_path(
154
+ str(item.get("backend_path", "")), backend_root=backend_root, relative_path=relative_path
155
+ )
156
+ size_bytes = self._coerce_size_bytes(item.get("size_bytes", 0))
157
+ if backend_path is None or relative_path is None:
158
+ continue
159
+ summaries.append(
160
+ {
161
+ "backend_path": backend_path,
162
+ "relative_path": relative_path,
163
+ "resource_type": str(item.get("resource_type", "asset")),
164
+ "size_bytes": size_bytes,
165
+ "is_binary": bool(item.get("is_binary", False)),
166
+ "summary": item.get("summary") if item.get("summary") is None else str(item.get("summary")),
167
+ }
168
+ )
169
+ return {
170
+ "name": name,
171
+ "backend_root": backend_root,
172
+ "resource_index_path": resource_index_path,
173
+ "matches": summaries,
174
+ }
175
+
176
+ def _select_resource(
177
+ self,
178
+ resources: list[SkillResourceSummary],
179
+ *,
180
+ backend_root: str,
181
+ path: str | None,
182
+ query: str | None,
183
+ ) -> SkillResourceSummary | None:
184
+ normalized_path = self._normalize_requested_path(path, backend_root=backend_root) if path else None
185
+ if normalized_path:
186
+ for resource in resources:
187
+ if normalized_path in {resource["relative_path"], resource["backend_path"]}:
188
+ return resource
189
+ return None
190
+ ranked = self._rank_resources(resources, query)
191
+ if query and ranked and ranked[0].get("score", 0) > 0:
192
+ return ranked[0]
193
+ return None
194
+
195
+ def _load_resource(
196
+ self,
197
+ name: str,
198
+ backend_root: str,
199
+ resource_index_path: str,
200
+ resource: SkillResourceSummary,
201
+ *,
202
+ max_chars: int | None,
203
+ ) -> SkillResourceResult:
204
+ resource_path = resource["backend_path"]
205
+ try:
206
+ raw_content = self.backend.read_bytes(resource_path)
207
+ except Exception as exc:
208
+ return self._resource_error_result(
209
+ name, backend_root, resource_index_path, resource, f"Unable to read resource at {resource_path}: {exc}"
210
+ )
211
+ content_hash = f"sha256:{hashlib.sha256(raw_content).hexdigest()}"
212
+ warnings: list[str] = []
213
+ content = ""
214
+ budget_truncated = False
215
+ if resource["is_binary"]:
216
+ warnings.append("Binary resource metadata returned without raw content.")
217
+ else:
218
+ decoded = raw_content.decode("utf-8", errors="replace")
219
+ limit = max(1, min(max_chars or MAX_SKILL_RESOURCE_CHARS, MAX_SKILL_RESOURCE_CHARS))
220
+ budget_truncated = len(decoded) > limit
221
+ content = decoded[:limit] if budget_truncated else decoded
222
+ if budget_truncated:
223
+ warnings.append(f"Resource content truncated to {limit} characters.")
224
+
225
+ return {
226
+ "name": name,
227
+ "backend_root": backend_root,
228
+ "resource_index_path": resource_index_path,
229
+ "resource_path": resource_path,
230
+ "relative_path": resource["relative_path"],
231
+ "resource_type": resource["resource_type"],
232
+ "size_bytes": resource["size_bytes"],
233
+ "is_binary": resource["is_binary"],
234
+ "summary": resource.get("summary"),
235
+ "content": content,
236
+ "content_hash": content_hash,
237
+ "budget_truncated": budget_truncated,
238
+ "warnings": warnings,
239
+ "matches": [resource],
240
+ }
241
+
242
+ def _rank_resources(self, resources: list[SkillResourceSummary], query: str | None) -> list[SkillResourceSummary]:
243
+ if not query:
244
+ return sorted(resources, key=lambda item: item["relative_path"])
245
+ query_tokens = set(_TOKEN_RE.findall(query.lower()))
246
+ ranked: list[SkillResourceSummary] = []
247
+ for resource in resources:
248
+ haystack = " ".join(
249
+ [resource["relative_path"], resource["resource_type"], str(resource.get("summary") or "")]
250
+ ).lower()
251
+ score = sum(1 for token in query_tokens if token in haystack)
252
+ ranked.append({**resource, "score": score})
253
+ return sorted(ranked, key=lambda item: (-int(item.get("score", 0)), item["relative_path"]))
254
+
255
+ def _normalize_requested_path(self, path: str | None, *, backend_root: str) -> str | None:
256
+ if path is None:
257
+ return None # pragma: no cover
258
+ normalized = posixpath.normpath(path.strip().replace("\\", "/"))
259
+ if normalized in {"", "."}:
260
+ return None
261
+ if normalized.startswith(f"{backend_root}/"):
262
+ return normalized
263
+ if normalized.startswith("/"):
264
+ return normalized # pragma: no cover
265
+ return normalized.lstrip("/")
266
+
267
+ def _normalize_manifest_relative_path(self, relative_path: str) -> str | None:
268
+ normalized = relative_path.strip().replace("\\", "/")
269
+ if not normalized or normalized.startswith("/") or "\x00" in normalized:
270
+ return None
271
+ if any(part == ".." for part in normalized.split("/")):
272
+ return None
273
+ normalized = posixpath.normpath(normalized)
274
+ if normalized in {"", "."}:
275
+ return None # pragma: no cover
276
+ return normalized
277
+
278
+ def _normalize_manifest_backend_path(
279
+ self, backend_path: str, *, backend_root: str, relative_path: str | None
280
+ ) -> str | None:
281
+ if relative_path is None:
282
+ return None
283
+ normalized = posixpath.normpath(backend_path.strip().replace("\\", "/"))
284
+ expected = posixpath.normpath(posixpath.join(backend_root, relative_path))
285
+ if normalized != expected or not normalized.startswith(f"{backend_root}/"):
286
+ return None
287
+ return normalized
288
+
289
+ def _coerce_size_bytes(self, value: Any) -> int:
290
+ try:
291
+ return max(0, int(value))
292
+ except (TypeError, ValueError):
293
+ return 0
294
+
295
+ def _resource_error_result(
296
+ self,
297
+ name: str,
298
+ backend_root: str,
299
+ resource_index_path: str,
300
+ resource: SkillResourceSummary,
301
+ error: str,
302
+ ) -> SkillResourceResult:
303
+ return {
304
+ "name": name,
305
+ "backend_root": backend_root,
306
+ "resource_index_path": resource_index_path,
307
+ "resource_path": resource["backend_path"],
308
+ "relative_path": resource["relative_path"],
309
+ "resource_type": resource["resource_type"],
310
+ "size_bytes": resource["size_bytes"],
311
+ "is_binary": resource["is_binary"],
312
+ "summary": resource.get("summary"),
313
+ "content": "",
314
+ "content_hash": "",
315
+ "budget_truncated": False,
316
+ "warnings": [error],
317
+ "matches": [resource],
318
+ "error": error,
319
+ }
320
+
321
+ def _error_result(self, name: str, error: str) -> SkillResourceResult:
322
+ return {
323
+ "name": name,
324
+ "backend_root": None,
325
+ "resource_index_path": None,
326
+ "resource_path": None,
327
+ "relative_path": None,
328
+ "resource_type": None,
329
+ "size_bytes": None,
330
+ "is_binary": None,
331
+ "summary": None,
332
+ "content": "",
333
+ "content_hash": "",
334
+ "budget_truncated": False,
335
+ "warnings": [error],
336
+ "matches": [],
337
+ "error": error,
338
+ }
@@ -0,0 +1,114 @@
1
+ """Write file tool for agents.
2
+
3
+ Authors:
4
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
5
+
6
+ TODO: When migrating to deepagents, replace this entire file with:
7
+ from deepagents.middleware.tools.write_file import WriteFileTool, WriteFileInput
8
+
9
+ Note: deepagents WriteFileTool uses ToolRuntime pattern instead of BaseTool.
10
+ We'll need to adapt our middleware to use deepagents' tool registration.
11
+ """
12
+
13
+ from typing import Annotated, Any
14
+
15
+ from langchain_core.tools import BaseTool
16
+ from pydantic import BaseModel, Field, SkipValidation
17
+
18
+ from aip_agents.middleware.schema import MiddlewareToolConfig
19
+
20
+ WRITE_FILE_TOOL_DESCRIPTION = """Writes to a new file in the filesystem.
21
+
22
+ Usage:
23
+ - The write_file tool will create the a new file.
24
+ - Prefer to edit existing files (with the edit_file tool) over creating new ones when possible.
25
+ """
26
+
27
+
28
+ class WriteFileInput(BaseModel):
29
+ """Input schema for ``write_file``.
30
+
31
+ Attributes:
32
+ path (str): Absolute destination path for the new file.
33
+ content (str): UTF-8 text content to write.
34
+ """
35
+
36
+ path: str = Field(description="Absolute path to the file to write. Must be absolute, not relative.")
37
+ content: str = Field(description="Content to write to the file.")
38
+
39
+
40
+ class WriteFileTool(BaseTool):
41
+ """Create a new file in the configured filesystem backend.
42
+
43
+ This tool enforces create-only semantics: if the target path already exists,
44
+ it returns an error instead of overwriting the file.
45
+
46
+ Attributes:
47
+ name (str): Tool identifier exposed to the model.
48
+ description (str): Human-readable usage guidance for the model.
49
+ args_schema (type[BaseModel]): Pydantic schema for tool arguments.
50
+ backend (Any): Filesystem backend implementing ``read`` and ``write``.
51
+ """
52
+
53
+ name: str = "write_file"
54
+ description: str = WRITE_FILE_TOOL_DESCRIPTION
55
+ args_schema: type[BaseModel] = WriteFileInput
56
+ tool_config_schema: type[BaseModel] = MiddlewareToolConfig
57
+ backend: Annotated[Any, SkipValidation()] # BackendProtocol - skip validation for Protocol type
58
+
59
+ def _file_exists(self, path: str) -> bool:
60
+ """Check whether a filesystem entry already exists at ``path``.
61
+
62
+ Args:
63
+ path (str): Absolute path to inspect.
64
+
65
+ Returns:
66
+ bool: ``True`` when a file or directory exists at ``path``;
67
+ ``False`` when the path does not exist.
68
+ """
69
+ try:
70
+ self.backend.read(path, line_offset=0, limit=1)
71
+ return True
72
+ except FileNotFoundError:
73
+ return False
74
+ except IsADirectoryError:
75
+ return True
76
+
77
+ def _run(self, path: str, content: str) -> str:
78
+ """Create a new file with the provided content.
79
+
80
+ Args:
81
+ path (str): Absolute destination path for the new file.
82
+ content (str): Full file content to persist.
83
+
84
+ Returns:
85
+ str: Success or error status message. Returns
86
+ ``"Successfully wrote {n} characters to {path}"`` on success,
87
+ or ``"Error: ..."`` when creation fails.
88
+
89
+ Notes:
90
+ This method catches runtime exceptions and converts them to
91
+ user-facing error messages; it does not raise exceptions.
92
+
93
+ Examples:
94
+ >>> tool._run(path="/reports/summary.md", content="# Summary")
95
+ 'Successfully wrote 9 characters to /reports/summary.md'
96
+ >>> tool._run(path="/reports/summary.md", content="# New")
97
+ 'Error: File already exists: /reports/summary.md'
98
+ """
99
+ try:
100
+ if self._file_exists(path):
101
+ return f"Error: File already exists: {path}"
102
+
103
+ if hasattr(self.backend, "ensure_parent_dir"):
104
+ self.backend.ensure_parent_dir(path)
105
+
106
+ result = self.backend.write(path, content)
107
+ if result.success:
108
+ return f"Successfully wrote {len(content)} characters to {path}"
109
+ else:
110
+ return f"Error: {result.error or 'Unknown error'}"
111
+ except PermissionError:
112
+ return f"Error: Permission denied: {path}"
113
+ except Exception as e:
114
+ return f"Error: {str(e)}"