langchain-agentx-python 0.1__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 (354) hide show
  1. langchain_agentx/__init__.py +46 -0
  2. langchain_agentx/command/__init__.py +28 -0
  3. langchain_agentx/command/builtin/__init__.py +25 -0
  4. langchain_agentx/command/builtin/clear.py +33 -0
  5. langchain_agentx/command/builtin/compact.py +33 -0
  6. langchain_agentx/command/builtin/memory.py +37 -0
  7. langchain_agentx/command/builtin/reload_plugins.py +42 -0
  8. langchain_agentx/command/context.py +30 -0
  9. langchain_agentx/command/dispatcher.py +183 -0
  10. langchain_agentx/command/registry.py +110 -0
  11. langchain_agentx/command/result.py +25 -0
  12. langchain_agentx/command/types.py +41 -0
  13. langchain_agentx/config/__init__.py +14 -0
  14. langchain_agentx/loop/__init__.py +47 -0
  15. langchain_agentx/loop/config/__init__.py +20 -0
  16. langchain_agentx/loop/config/agent_config.py +66 -0
  17. langchain_agentx/loop/config/agent_loop_config.py +72 -0
  18. langchain_agentx/loop/config/model_context_resolver.py +105 -0
  19. langchain_agentx/loop/config/runtime_settings.py +50 -0
  20. langchain_agentx/loop/config/token_estimator.py +133 -0
  21. langchain_agentx/loop/context/__init__.py +66 -0
  22. langchain_agentx/loop/context/blocking_guard.py +97 -0
  23. langchain_agentx/loop/context/compaction_service.py +60 -0
  24. langchain_agentx/loop/context/message_utils.py +56 -0
  25. langchain_agentx/loop/context/pipeline.py +127 -0
  26. langchain_agentx/loop/context/settings.py +103 -0
  27. langchain_agentx/loop/context/stages/__init__.py +29 -0
  28. langchain_agentx/loop/context/stages/autocompact.py +140 -0
  29. langchain_agentx/loop/context/stages/base.py +32 -0
  30. langchain_agentx/loop/context/stages/collapse.py +76 -0
  31. langchain_agentx/loop/context/stages/microcompact.py +76 -0
  32. langchain_agentx/loop/context/stages/noop.py +33 -0
  33. langchain_agentx/loop/context/stages/snip.py +71 -0
  34. langchain_agentx/loop/context/stages/tool_result_budget.py +69 -0
  35. langchain_agentx/loop/context/types.py +79 -0
  36. langchain_agentx/loop/exit/__init__.py +1 -0
  37. langchain_agentx/loop/exit/exit_logic.py +320 -0
  38. langchain_agentx/loop/exit/reason_codes.py +39 -0
  39. langchain_agentx/loop/graph/__init__.py +5 -0
  40. langchain_agentx/loop/graph/builtin_loop_control.py +197 -0
  41. langchain_agentx/loop/graph/factory.py +1409 -0
  42. langchain_agentx/loop/graph/graph_edges.py +820 -0
  43. langchain_agentx/loop/hook/__init__.py +48 -0
  44. langchain_agentx/loop/hook/async_hook_runner.py +62 -0
  45. langchain_agentx/loop/hook/config.py +280 -0
  46. langchain_agentx/loop/hook/engine.py +321 -0
  47. langchain_agentx/loop/hook/executors/__init__.py +9 -0
  48. langchain_agentx/loop/hook/executors/agent.py +107 -0
  49. langchain_agentx/loop/hook/executors/command.py +230 -0
  50. langchain_agentx/loop/hook/executors/http.py +114 -0
  51. langchain_agentx/loop/hook/executors/prompt.py +92 -0
  52. langchain_agentx/loop/hook/graph_wiring.py +134 -0
  53. langchain_agentx/loop/hook/registry.py +262 -0
  54. langchain_agentx/loop/hook/trust.py +43 -0
  55. langchain_agentx/loop/hook/types.py +110 -0
  56. langchain_agentx/loop/injection/__init__.py +13 -0
  57. langchain_agentx/loop/injection/dedup.py +74 -0
  58. langchain_agentx/loop/loop_abort.py +36 -0
  59. langchain_agentx/loop/model/__init__.py +1 -0
  60. langchain_agentx/loop/model/model_node.py +648 -0
  61. langchain_agentx/loop/model/model_nodes.py +661 -0
  62. langchain_agentx/loop/model/orphan_tool_results.py +38 -0
  63. langchain_agentx/loop/model/retrier.py +307 -0
  64. langchain_agentx/loop/model/retry_bridge.py +58 -0
  65. langchain_agentx/loop/model/retry_events.py +35 -0
  66. langchain_agentx/loop/model/retry_policy.py +56 -0
  67. langchain_agentx/loop/model/schema_and_format.py +153 -0
  68. langchain_agentx/loop/model/tool_and_model_binding.py +227 -0
  69. langchain_agentx/loop/model/tool_call_degradation_corrector.py +443 -0
  70. langchain_agentx/loop/model/tool_transcript_guard.py +225 -0
  71. langchain_agentx/loop/prompt/__init__.py +95 -0
  72. langchain_agentx/loop/prompt/builder.py +61 -0
  73. langchain_agentx/loop/prompt/builtin.py +218 -0
  74. langchain_agentx/loop/prompt/compact.py +408 -0
  75. langchain_agentx/loop/prompt/sections.py +120 -0
  76. langchain_agentx/loop/runtime/__init__.py +19 -0
  77. langchain_agentx/loop/runtime/context.py +34 -0
  78. langchain_agentx/loop/runtime/context_factory.py +107 -0
  79. langchain_agentx/loop/runtime/subagent_execution_paths.py +68 -0
  80. langchain_agentx/loop/subagent/__init__.py +53 -0
  81. langchain_agentx/loop/subagent/async_runner.py +215 -0
  82. langchain_agentx/loop/subagent/context.py +209 -0
  83. langchain_agentx/loop/subagent/fork_worktree_notice.py +25 -0
  84. langchain_agentx/loop/subagent/graph.py +72 -0
  85. langchain_agentx/loop/subagent/orchestrator.py +391 -0
  86. langchain_agentx/loop/subagent/progress.py +30 -0
  87. langchain_agentx/loop/subagent/prompt.py +52 -0
  88. langchain_agentx/loop/subagent/runner.py +504 -0
  89. langchain_agentx/loop/subagent/transcript.py +172 -0
  90. langchain_agentx/memory/__init__.py +2 -0
  91. langchain_agentx/memory/instruction/__init__.py +12 -0
  92. langchain_agentx/memory/instruction/loader.py +325 -0
  93. langchain_agentx/memory/instruction/resolver.py +24 -0
  94. langchain_agentx/memory/instruction/runtime.py +83 -0
  95. langchain_agentx/memory/instruction/sections.py +83 -0
  96. langchain_agentx/memory/instruction/types.py +59 -0
  97. langchain_agentx/memory/memdir/__init__.py +77 -0
  98. langchain_agentx/memory/memdir/age.py +36 -0
  99. langchain_agentx/memory/memdir/agent_memory.py +380 -0
  100. langchain_agentx/memory/memdir/extractor.py +309 -0
  101. langchain_agentx/memory/memdir/loader.py +187 -0
  102. langchain_agentx/memory/memdir/paths.py +63 -0
  103. langchain_agentx/memory/memdir/recall.py +45 -0
  104. langchain_agentx/memory/memdir/runtime.py +43 -0
  105. langchain_agentx/memory/memdir/scan.py +135 -0
  106. langchain_agentx/memory/memdir/types.py +104 -0
  107. langchain_agentx/memory/session/__init__.py +76 -0
  108. langchain_agentx/memory/session/compact_bridge.py +208 -0
  109. langchain_agentx/memory/session/prompts.py +172 -0
  110. langchain_agentx/memory/session/session_memory.py +282 -0
  111. langchain_agentx/observability/__init__.py +67 -0
  112. langchain_agentx/observability/evaluation/__init__.py +17 -0
  113. langchain_agentx/observability/evaluation/checkers/__init__.py +18 -0
  114. langchain_agentx/observability/evaluation/checkers/base.py +34 -0
  115. langchain_agentx/observability/evaluation/checkers/compaction.py +38 -0
  116. langchain_agentx/observability/evaluation/checkers/degradation.py +50 -0
  117. langchain_agentx/observability/evaluation/checkers/exit_quality.py +42 -0
  118. langchain_agentx/observability/evaluation/checkers/session_memory.py +45 -0
  119. langchain_agentx/observability/evaluation/checkers/tool_behavior.py +53 -0
  120. langchain_agentx/observability/evaluation/retention_scheduler.py +67 -0
  121. langchain_agentx/observability/evaluation/service.py +102 -0
  122. langchain_agentx/observability/evaluation/state.py +32 -0
  123. langchain_agentx/observability/evaluation/store.py +258 -0
  124. langchain_agentx/observability/events/__init__.py +15 -0
  125. langchain_agentx/observability/events/langchain_agentx_event_adapter.py +832 -0
  126. langchain_agentx/observability/logging/__init__.py +15 -0
  127. langchain_agentx/observability/logging/debug_burst.py +95 -0
  128. langchain_agentx/observability/logging/logging_config.py +178 -0
  129. langchain_agentx/observability/logging/logging_contract.py +65 -0
  130. langchain_agentx/observability/replay/__init__.py +35 -0
  131. langchain_agentx/observability/replay/cli.py +91 -0
  132. langchain_agentx/observability/replay/service.py +83 -0
  133. langchain_agentx/observability/replay/store.py +278 -0
  134. langchain_agentx/observability/replay/ui.py +47 -0
  135. langchain_agentx/observability/trace/__init__.py +25 -0
  136. langchain_agentx/observability/trace/collector.py +560 -0
  137. langchain_agentx/observability/trace/event_emitter.py +183 -0
  138. langchain_agentx/observability/trace/hook_event_emitter.py +49 -0
  139. langchain_agentx/observability/trace/models.py +144 -0
  140. langchain_agentx/observability/trace/sqlite_store.py +873 -0
  141. langchain_agentx/observability/trace/trace_callback.py +295 -0
  142. langchain_agentx/observability/trace/trace_lifecycle_collector.py +114 -0
  143. langchain_agentx/plugin/__init__.py +26 -0
  144. langchain_agentx/plugin/builtin.py +53 -0
  145. langchain_agentx/plugin/config.py +113 -0
  146. langchain_agentx/plugin/loader.py +386 -0
  147. langchain_agentx/plugin/manifest.py +154 -0
  148. langchain_agentx/plugin/registries.py +211 -0
  149. langchain_agentx/plugin/types.py +142 -0
  150. langchain_agentx/provider/__init__.py +27 -0
  151. langchain_agentx/provider/anthropic.py +121 -0
  152. langchain_agentx/provider/compatible_chat_openai.py +86 -0
  153. langchain_agentx/provider/env.py +45 -0
  154. langchain_agentx/provider/model_profile.py +156 -0
  155. langchain_agentx/provider/openai.py +89 -0
  156. langchain_agentx/session/__init__.py +17 -0
  157. langchain_agentx/session/agent_session.py +320 -0
  158. langchain_agentx/session/conversation_factory.py +87 -0
  159. langchain_agentx/session/conversation_recovery.py +156 -0
  160. langchain_agentx/session/conversation_session.py +198 -0
  161. langchain_agentx/session/factory.py +143 -0
  162. langchain_agentx/session/protocol.py +25 -0
  163. langchain_agentx/task_runtime/__init__.py +113 -0
  164. langchain_agentx/task_runtime/core/__init__.py +51 -0
  165. langchain_agentx/task_runtime/core/ids.py +33 -0
  166. langchain_agentx/task_runtime/core/interfaces.py +115 -0
  167. langchain_agentx/task_runtime/core/notification_priority.py +19 -0
  168. langchain_agentx/task_runtime/core/types.py +136 -0
  169. langchain_agentx/task_runtime/integrations/__init__.py +33 -0
  170. langchain_agentx/task_runtime/integrations/loop_adapter.py +91 -0
  171. langchain_agentx/task_runtime/integrations/loop_integration.py +61 -0
  172. langchain_agentx/task_runtime/integrations/prefetch_providers.py +108 -0
  173. langchain_agentx/task_runtime/integrations/provider_factory.py +103 -0
  174. langchain_agentx/task_runtime/integrations/queued_command_provider.py +184 -0
  175. langchain_agentx/task_runtime/integrations/sqlite_queued_command_provider.py +338 -0
  176. langchain_agentx/task_runtime/integrations/tool_use_summary_provider.py +254 -0
  177. langchain_agentx/task_runtime/orchestrator/__init__.py +5 -0
  178. langchain_agentx/task_runtime/orchestrator/runtime.py +386 -0
  179. langchain_agentx/task_runtime/output/__init__.py +5 -0
  180. langchain_agentx/task_runtime/output/sink.py +64 -0
  181. langchain_agentx/task_runtime/policy/__init__.py +11 -0
  182. langchain_agentx/task_runtime/policy/withhold_visibility.py +32 -0
  183. langchain_agentx/task_runtime/queue/__init__.py +5 -0
  184. langchain_agentx/task_runtime/queue/in_memory.py +55 -0
  185. langchain_agentx/task_runtime/skill_prefetch/__init__.py +4 -0
  186. langchain_agentx/task_runtime/skill_prefetch/attachments.py +46 -0
  187. langchain_agentx/task_runtime/skill_prefetch/models.py +37 -0
  188. langchain_agentx/task_runtime/skill_prefetch/provider.py +344 -0
  189. langchain_agentx/task_runtime/store/__init__.py +6 -0
  190. langchain_agentx/task_runtime/store/in_memory.py +81 -0
  191. langchain_agentx/task_runtime/store/sqlite_store.py +281 -0
  192. langchain_agentx/task_runtime/tasks/__init__.py +76 -0
  193. langchain_agentx/task_runtime/tasks/ai_analysis/__init__.py +15 -0
  194. langchain_agentx/task_runtime/tasks/ai_analysis/base.py +41 -0
  195. langchain_agentx/task_runtime/tasks/ai_analysis/evaluation.py +67 -0
  196. langchain_agentx/task_runtime/tasks/ai_analysis/registry.py +36 -0
  197. langchain_agentx/task_runtime/tasks/ai_analysis/scheduler.py +70 -0
  198. langchain_agentx/task_runtime/tasks/base/__init__.py +6 -0
  199. langchain_agentx/task_runtime/tasks/base/contracts.py +24 -0
  200. langchain_agentx/task_runtime/tasks/custom/__init__.py +7 -0
  201. langchain_agentx/task_runtime/tasks/custom/executor.py +60 -0
  202. langchain_agentx/task_runtime/tasks/custom/notification.py +7 -0
  203. langchain_agentx/task_runtime/tasks/custom/semantics.py +13 -0
  204. langchain_agentx/task_runtime/tasks/custom/spec.py +33 -0
  205. langchain_agentx/task_runtime/tasks/dream_task/__init__.py +15 -0
  206. langchain_agentx/task_runtime/tasks/dream_task/executor.py +61 -0
  207. langchain_agentx/task_runtime/tasks/dream_task/notification.py +19 -0
  208. langchain_agentx/task_runtime/tasks/dream_task/semantics.py +13 -0
  209. langchain_agentx/task_runtime/tasks/dream_task/spec.py +35 -0
  210. langchain_agentx/task_runtime/tasks/dream_task/state.py +17 -0
  211. langchain_agentx/task_runtime/tasks/in_process_teammate/__init__.py +12 -0
  212. langchain_agentx/task_runtime/tasks/in_process_teammate/executor.py +36 -0
  213. langchain_agentx/task_runtime/tasks/in_process_teammate/notification.py +25 -0
  214. langchain_agentx/task_runtime/tasks/in_process_teammate/semantics.py +13 -0
  215. langchain_agentx/task_runtime/tasks/in_process_teammate/spec.py +63 -0
  216. langchain_agentx/task_runtime/tasks/local_agent/__init__.py +14 -0
  217. langchain_agentx/task_runtime/tasks/local_agent/executor.py +33 -0
  218. langchain_agentx/task_runtime/tasks/local_agent/notification.py +21 -0
  219. langchain_agentx/task_runtime/tasks/local_agent/runner.py +43 -0
  220. langchain_agentx/task_runtime/tasks/local_agent/semantics.py +13 -0
  221. langchain_agentx/task_runtime/tasks/local_agent/spec.py +31 -0
  222. langchain_agentx/task_runtime/tasks/local_bash/__init__.py +13 -0
  223. langchain_agentx/task_runtime/tasks/local_bash/executor.py +95 -0
  224. langchain_agentx/task_runtime/tasks/local_bash/notification.py +22 -0
  225. langchain_agentx/task_runtime/tasks/local_bash/semantics.py +13 -0
  226. langchain_agentx/task_runtime/tasks/local_bash/spec.py +55 -0
  227. langchain_agentx/task_runtime/tasks/remote_agent/__init__.py +19 -0
  228. langchain_agentx/task_runtime/tasks/remote_agent/backend.py +76 -0
  229. langchain_agentx/task_runtime/tasks/remote_agent/executor.py +37 -0
  230. langchain_agentx/task_runtime/tasks/remote_agent/notification.py +22 -0
  231. langchain_agentx/task_runtime/tasks/remote_agent/semantics.py +13 -0
  232. langchain_agentx/task_runtime/tasks/remote_agent/spec.py +34 -0
  233. langchain_agentx/task_runtime/tasks/trace_cleanup/__init__.py +19 -0
  234. langchain_agentx/task_runtime/tasks/trace_cleanup/bootstrap.py +95 -0
  235. langchain_agentx/task_runtime/tasks/trace_cleanup/executor.py +66 -0
  236. langchain_agentx/task_runtime/tasks/trace_cleanup/scheduler.py +169 -0
  237. langchain_agentx/tool_runtime/__init__.py +90 -0
  238. langchain_agentx/tool_runtime/adapter.py +365 -0
  239. langchain_agentx/tool_runtime/base.py +319 -0
  240. langchain_agentx/tool_runtime/errors.py +190 -0
  241. langchain_agentx/tool_runtime/identical_call_cache.py +110 -0
  242. langchain_agentx/tool_runtime/loader.py +195 -0
  243. langchain_agentx/tool_runtime/models.py +260 -0
  244. langchain_agentx/tool_runtime/permission_context.py +78 -0
  245. langchain_agentx/tool_runtime/pipeline.py +621 -0
  246. langchain_agentx/tool_runtime/policy.py +447 -0
  247. langchain_agentx/tool_runtime/registry.py +81 -0
  248. langchain_agentx/tool_runtime/resolvers/__init__.py +27 -0
  249. langchain_agentx/tool_runtime/resolvers/agent_session.py +125 -0
  250. langchain_agentx/tool_runtime/resolvers/background.py +32 -0
  251. langchain_agentx/tool_runtime/resolvers/base.py +20 -0
  252. langchain_agentx/tool_runtime/resolvers/conversation.py +22 -0
  253. langchain_agentx/tool_runtime/resolvers/workflow.py +73 -0
  254. langchain_agentx/tool_runtime/session_store.py +132 -0
  255. langchain_agentx/tool_runtime/smoke_test_runtime.py +294 -0
  256. langchain_agentx/tool_runtime/state_bridge.py +164 -0
  257. langchain_agentx/tools/__init__.py +26 -0
  258. langchain_agentx/tools/agent/__init__.py +9 -0
  259. langchain_agentx/tools/agent/backend.py +53 -0
  260. langchain_agentx/tools/agent/built_in/__init__.py +19 -0
  261. langchain_agentx/tools/agent/built_in/agentx_guide.py +65 -0
  262. langchain_agentx/tools/agent/built_in/explore.py +80 -0
  263. langchain_agentx/tools/agent/built_in/general.py +57 -0
  264. langchain_agentx/tools/agent/built_in/plan.py +89 -0
  265. langchain_agentx/tools/agent/built_in/statusline_setup.py +64 -0
  266. langchain_agentx/tools/agent/built_in/verification.py +120 -0
  267. langchain_agentx/tools/agent/builtin_subagent_loader.py +89 -0
  268. langchain_agentx/tools/agent/cwd_resolution.py +119 -0
  269. langchain_agentx/tools/agent/limits.py +26 -0
  270. langchain_agentx/tools/agent/loader.py +270 -0
  271. langchain_agentx/tools/agent/models.py +85 -0
  272. langchain_agentx/tools/agent/prompt.py +120 -0
  273. langchain_agentx/tools/agent/registry/__init__.py +18 -0
  274. langchain_agentx/tools/agent/registry/config.py +29 -0
  275. langchain_agentx/tools/agent/registry/registry.py +47 -0
  276. langchain_agentx/tools/agent/scope.py +137 -0
  277. langchain_agentx/tools/agent/tool.py +256 -0
  278. langchain_agentx/tools/bash/__init__.py +9 -0
  279. langchain_agentx/tools/bash/ast_security.py +571 -0
  280. langchain_agentx/tools/bash/backend.py +1447 -0
  281. langchain_agentx/tools/bash/bash_hardening.py +734 -0
  282. langchain_agentx/tools/bash/bash_runtime_contract.py +41 -0
  283. langchain_agentx/tools/bash/cwd_reporter.py +95 -0
  284. langchain_agentx/tools/bash/limits.py +71 -0
  285. langchain_agentx/tools/bash/mode_validation.py +282 -0
  286. langchain_agentx/tools/bash/models.py +131 -0
  287. langchain_agentx/tools/bash/observability.py +148 -0
  288. langchain_agentx/tools/bash/output_utils.py +200 -0
  289. langchain_agentx/tools/bash/path_security.py +2429 -0
  290. langchain_agentx/tools/bash/prompt.py +68 -0
  291. langchain_agentx/tools/bash/read_only_validation.py +589 -0
  292. langchain_agentx/tools/bash/result_presenter.py +324 -0
  293. langchain_agentx/tools/bash/sandbox_decision.py +133 -0
  294. langchain_agentx/tools/bash/security.py +311 -0
  295. langchain_agentx/tools/bash/sed_edit_parser.py +243 -0
  296. langchain_agentx/tools/bash/sed_validation.py +163 -0
  297. langchain_agentx/tools/bash/semantics.py +111 -0
  298. langchain_agentx/tools/bash/session_manager.py +205 -0
  299. langchain_agentx/tools/bash/session_runtime.py +290 -0
  300. langchain_agentx/tools/bash/shell_locator.py +191 -0
  301. langchain_agentx/tools/bash/task_runtime.py +91 -0
  302. langchain_agentx/tools/bash/tool.py +939 -0
  303. langchain_agentx/tools/bash/windows_shell_quoting.py +45 -0
  304. langchain_agentx/tools/glob/__init__.py +9 -0
  305. langchain_agentx/tools/glob/models.py +57 -0
  306. langchain_agentx/tools/glob/pagination.py +30 -0
  307. langchain_agentx/tools/glob/prompt.py +24 -0
  308. langchain_agentx/tools/glob/rg_list_backend.py +139 -0
  309. langchain_agentx/tools/glob/rg_pattern.py +44 -0
  310. langchain_agentx/tools/glob/tool.py +327 -0
  311. langchain_agentx/tools/grep/__init__.py +7 -0
  312. langchain_agentx/tools/grep/backend.py +375 -0
  313. langchain_agentx/tools/grep/models.py +127 -0
  314. langchain_agentx/tools/grep/prompt.py +30 -0
  315. langchain_agentx/tools/grep/rg_subprocess_controller.py +114 -0
  316. langchain_agentx/tools/grep/tool.py +475 -0
  317. langchain_agentx/tools/read/__init__.py +9 -0
  318. langchain_agentx/tools/read/backend.py +415 -0
  319. langchain_agentx/tools/read/limits.py +67 -0
  320. langchain_agentx/tools/read/models.py +156 -0
  321. langchain_agentx/tools/read/prompt.py +73 -0
  322. langchain_agentx/tools/read/tool.py +494 -0
  323. langchain_agentx/tools/ripgrep_plugin_exclusions.py +137 -0
  324. langchain_agentx/tools/skill/__init__.py +4 -0
  325. langchain_agentx/tools/skill/argument_substitution.py +80 -0
  326. langchain_agentx/tools/skill/loader.py +196 -0
  327. langchain_agentx/tools/skill/models.py +88 -0
  328. langchain_agentx/tools/skill/policy.py +80 -0
  329. langchain_agentx/tools/skill/prompt.py +35 -0
  330. langchain_agentx/tools/skill/tool.py +222 -0
  331. langchain_agentx/utils/__init__.py +0 -0
  332. langchain_agentx/utils/cwd.py +124 -0
  333. langchain_agentx/utils/host_platform.py +112 -0
  334. langchain_agentx/utils/path_hierarchy.py +48 -0
  335. langchain_agentx/utils/path_user_input.py +66 -0
  336. langchain_agentx/utils/rg_executable.py +18 -0
  337. langchain_agentx/utils/subprocess_text.py +101 -0
  338. langchain_agentx/utils/temp_paths.py +77 -0
  339. langchain_agentx/utils/unc_path.py +25 -0
  340. langchain_agentx/utils/win_reserved_paths.py +51 -0
  341. langchain_agentx/workflow/__init__.py +7 -0
  342. langchain_agentx/workflow/base.py +97 -0
  343. langchain_agentx/workflow/batch.py +55 -0
  344. langchain_agentx/workflow/dag.py +54 -0
  345. langchain_agentx/workspace/__init__.py +13 -0
  346. langchain_agentx/workspace/config.py +140 -0
  347. langchain_agentx/workspace/path_key_normalizer.py +30 -0
  348. langchain_agentx/workspace/resolver.py +74 -0
  349. langchain_agentx/workspace/validators.py +41 -0
  350. langchain_agentx_python-0.1.dist-info/LICENSE +201 -0
  351. langchain_agentx_python-0.1.dist-info/METADATA +513 -0
  352. langchain_agentx_python-0.1.dist-info/RECORD +354 -0
  353. langchain_agentx_python-0.1.dist-info/WHEEL +5 -0
  354. langchain_agentx_python-0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,311 @@
1
+ """
2
+ tools/bash/security.py — BashRuntimeTool 基础安全检查
3
+
4
+ v1 实现"可用性优先"的安全防护,防止常见误操作。
5
+ v2 将引入 AST 级分析(对应 CC bashSecurity.ts 的 2593 行)。
6
+
7
+ 对应 CC:
8
+ destructiveCommandWarning.ts → detect_destructive_patterns()
9
+ BashTool.tsx::validateInput() → detect_sleep_block() / detect_interactive_command()
10
+ bashSecurity.ts (部分) → detect_infinite_output()
11
+
12
+ 安全层次(纵深防御):
13
+ validate_input 层(阻断):sleep 大值、交互式命令、无限输出、空命令
14
+ invoke 层(警告不阻断):危险命令 → 注入 warning hint
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import re
20
+ import shlex
21
+
22
+ from .read_only_validation import BashReadOnlyClassifier
23
+
24
+
25
+ # ---------------------------------------------------------------------------
26
+ # 危险命令模式(对应 CC destructiveCommandWarning.ts::DESTRUCTIVE_PATTERNS)
27
+ # ---------------------------------------------------------------------------
28
+
29
+ # (正则模式, 警告描述)
30
+ _DESTRUCTIVE_PATTERNS: list[tuple[re.Pattern[str], str]] = [
31
+ # rm -rf / 或 rm -rf /path(根目录及其下一级)
32
+ (
33
+ re.compile(r"\brm\b.*-[^-\s]*[rf]{2}.*\s/(?:[^/\s]+)?(?:\s|$)"),
34
+ "Dangerous: rm -rf targeting root or system path",
35
+ ),
36
+ # rm -rf 整体(任意路径,提示风险)
37
+ (
38
+ re.compile(r"\brm\b.*-[^-\s]*[rf]{1,2}"),
39
+ "Potentially destructive: recursive remove with force flag",
40
+ ),
41
+ # chmod 777 或 chmod a+rwx(全权限)
42
+ (
43
+ re.compile(r"\bchmod\b.*(?:0*777|a\+rwx|ugo\+rwx)"),
44
+ "Dangerous: granting full permissions to all users",
45
+ ),
46
+ # dd 直接写入设备
47
+ (
48
+ re.compile(r"\bdd\b.*\bof=/dev/(?!null\b)"),
49
+ "Dangerous: direct device write via dd",
50
+ ),
51
+ # 重定向到非 /dev/null 的设备文件
52
+ (
53
+ re.compile(r">\s*/dev/(?!null\b)"),
54
+ "Dangerous: redirecting output to a device file",
55
+ ),
56
+ # mkfs 格式化
57
+ (
58
+ re.compile(r"\bmkfs\b"),
59
+ "Dangerous: filesystem creation (mkfs)",
60
+ ),
61
+ # fdisk 分区操作
62
+ (
63
+ re.compile(r"\bfdisk\b.*\b/dev/"),
64
+ "Dangerous: disk partition operation (fdisk)",
65
+ ),
66
+ # :(){:|:&};: Fork Bomb
67
+ (
68
+ re.compile(r":\(\)\{.*:\|.*:.*\}"),
69
+ "Dangerous: fork bomb pattern detected",
70
+ ),
71
+ ]
72
+
73
+ # ---------------------------------------------------------------------------
74
+ # 无限输出命令(对应 CC BLOCKED_DEVICE_PATHS 的命令级等价)
75
+ # ---------------------------------------------------------------------------
76
+
77
+ # 命令基础名称 → 会无限输出(无配合 head/timeout 等限制时)
78
+ _INFINITE_OUTPUT_BASE_COMMANDS: frozenset[str] = frozenset({
79
+ "yes",
80
+ "cat", # 仅当目标为特定设备文件时,见 _INFINITE_DEVICE_ARGS
81
+ "tail", # 仅当使用 -f 且目标为设备文件时
82
+ })
83
+
84
+ # 与无限输出相关的设备文件路径
85
+ _INFINITE_DEVICE_PATHS: frozenset[str] = frozenset({
86
+ "/dev/urandom",
87
+ "/dev/random",
88
+ "/dev/zero",
89
+ "/dev/full",
90
+ })
91
+
92
+ # 需要 TTY 的交互式命令(对应 CC 中用户体验层面的保护)
93
+ _INTERACTIVE_COMMANDS: frozenset[str] = frozenset({
94
+ "vim", "vi", "nvim", "nano", "emacs", "pico",
95
+ "top", "htop", "btop", "iotop", "atop",
96
+ "less", "more",
97
+ "ipython", "bpython",
98
+ "node", # 无参数时进入 REPL
99
+ "irb", "pry",
100
+ "mysql", "psql", "sqlite3", "redis-cli", "mongo",
101
+ "python", "python3", # 无参数时进入 REPL
102
+ "gdb", "lldb",
103
+ "ftp", "sftp",
104
+ })
105
+
106
+
107
+ # ---------------------------------------------------------------------------
108
+ # 公开 API
109
+ # ---------------------------------------------------------------------------
110
+
111
+
112
+ def detect_destructive_patterns(command: str) -> str | None:
113
+ """
114
+ 检测命令中的危险操作模式。
115
+
116
+ 返回:
117
+ str — 警告文本(不阻断执行,在 present() 中作为 hint 展示)
118
+ None — 无危险模式
119
+ """
120
+ for pattern, warning in _DESTRUCTIVE_PATTERNS:
121
+ if pattern.search(command):
122
+ return warning
123
+ return None
124
+
125
+
126
+ def detect_infinite_output(command: str) -> bool:
127
+ """
128
+ 检测命令是否会产生无限输出,导致进程挂起。
129
+
130
+ 策略:
131
+ - `yes` 单独或作为管道起点(无 head/timeout 等限制时)
132
+ - `cat /dev/urandom` 等读取无限设备文件
133
+ - `tail -f /dev/urandom` 等
134
+
135
+ 注意:`yes | head -n 5` 是合法的(有 head 截断),不阻断。
136
+ """
137
+ stripped = command.strip()
138
+
139
+ # 如果有管道,提取第一个命令检查
140
+ # 有管道终结命令(head/timeout/take)时不阻断
141
+ parts = _split_pipeline(stripped)
142
+
143
+ # 检查是否有 head / timeout / wc 等截断命令
144
+ has_limiter = any(
145
+ _get_base_cmd(p) in {"head", "timeout", "wc", "tail", "tee", "take"}
146
+ for p in parts[1:] # 第二个之后的管道部分
147
+ )
148
+
149
+ first_cmd = parts[0] if parts else stripped
150
+ base = _get_base_cmd(first_cmd)
151
+
152
+ # yes 命令:单独使用时无限输出
153
+ if base == "yes" and not has_limiter:
154
+ return True
155
+
156
+ # cat/tail 读取设备文件
157
+ if base in {"cat", "tail"}:
158
+ for dev_path in _INFINITE_DEVICE_PATHS:
159
+ if dev_path in first_cmd:
160
+ return True
161
+
162
+ return False
163
+
164
+
165
+ def detect_sleep_block(command: str, threshold_sec: int) -> int | None:
166
+ """
167
+ 检测 sleep N 中 N >= threshold_sec 的情况。
168
+
169
+ 返回:
170
+ int — 检测到的 sleep 时长(秒),提示模型使用 run_in_background
171
+ None — 未检测到阻塞性 sleep
172
+
173
+ 对应 CC BashTool.tsx::detectBlockedSleepPattern()。
174
+
175
+ 注意:
176
+ - `sleep 1.5` 等浮点数秒数 < threshold 时不阻断
177
+ - `sleep 5 && cmd` 如果 sleep_sec < threshold 则不阻断
178
+ - 支持 `sleep 5m`、`sleep 2h` 等带单位格式
179
+ """
180
+ # 匹配 sleep N(支持 s/m/h/d 单位)
181
+ m = re.search(r"\bsleep\s+(\d+(?:\.\d+)?)([smhd]?)\b", command)
182
+ if not m:
183
+ return None
184
+
185
+ val = float(m.group(1))
186
+ unit = m.group(2)
187
+
188
+ # 转换为秒
189
+ unit_multipliers = {"": 1, "s": 1, "m": 60, "h": 3600, "d": 86400}
190
+ total_sec = val * unit_multipliers.get(unit, 1)
191
+
192
+ if total_sec >= threshold_sec:
193
+ return int(total_sec)
194
+ return None
195
+
196
+
197
+ def detect_interactive_command(command: str) -> str | None:
198
+ """
199
+ 检测命令是否需要 TTY(交互式命令)。
200
+
201
+ 返回:
202
+ str — 交互式命令名称(用于错误消息)
203
+ None — 无交互式命令
204
+
205
+ 策略:
206
+ - 仅检测命令基础名(无参数的 python / node 等进入 REPL)
207
+ - `python script.py` 不阻断(有参数,非 REPL)
208
+ - `vim` / `nano` 等编辑器无论参数均阻断
209
+ """
210
+ stripped = command.strip()
211
+
212
+ # 尝试解析命令的第一个 token
213
+ base = _get_base_cmd(stripped)
214
+
215
+ # 文本编辑器:始终阻断
216
+ _always_block = {"vim", "vi", "nvim", "nano", "emacs", "pico", "less", "more"}
217
+ # 系统监控:始终阻断
218
+ _always_block |= {"top", "htop", "btop", "iotop", "atop"}
219
+ # DB 客户端:始终阻断
220
+ _always_block |= {"mysql", "psql", "sqlite3", "redis-cli", "mongo"}
221
+
222
+ if base in _always_block:
223
+ return base
224
+
225
+ # REPL 命令:只有无参数或仅 flag 参数时阻断
226
+ _repl_commands = {"python", "python3", "python2", "node", "ipython", "bpython",
227
+ "irb", "pry", "gdb", "lldb", "ftp", "sftp"}
228
+ if base in _repl_commands:
229
+ # 检查是否有非 flag 的参数(如文件名、脚本)
230
+ try:
231
+ tokens = shlex.split(stripped)
232
+ except ValueError:
233
+ tokens = stripped.split()
234
+
235
+ # 跳过命令名,检查其余 token
236
+ args = [t for t in tokens[1:] if not t.startswith("-")]
237
+ if not args:
238
+ # 没有非 flag 参数 → 进入 REPL 模式
239
+ return base
240
+
241
+ return None
242
+
243
+
244
+ def is_read_only_command(command: str) -> bool:
245
+ """
246
+ 检测命令是否为只读操作(无副作用)。
247
+
248
+ 用于工具的 is_read_only 动态判断。
249
+ 对应 CC checkReadOnlyConstraints() 的简化版本(v1)。
250
+ """
251
+ classifier = BashReadOnlyClassifier()
252
+ return classifier.classify(command).is_read_only
253
+
254
+
255
+ # ---------------------------------------------------------------------------
256
+ # 内部辅助
257
+ # ---------------------------------------------------------------------------
258
+
259
+
260
+ def _get_base_cmd(command: str) -> str:
261
+ """提取命令的基础名(去掉路径前缀和参数)。"""
262
+ stripped = command.strip()
263
+ if not stripped:
264
+ return ""
265
+ # 取第一个 token
266
+ try:
267
+ tokens = shlex.split(stripped)
268
+ except ValueError:
269
+ tokens = stripped.split()
270
+ if not tokens:
271
+ return ""
272
+ cmd = tokens[0]
273
+ # 去掉路径前缀(/usr/bin/python → python)
274
+ return cmd.rsplit("/", 1)[-1]
275
+
276
+
277
+ def _split_pipeline(command: str) -> list[str]:
278
+ """
279
+ 简单分割管道命令(仅按 | 分割,不做完整 AST 解析)。
280
+ v2 改为 AST 分析。
281
+ """
282
+ # 简单字符串分割,不处理引号内的 |
283
+ parts = []
284
+ current = []
285
+ in_single = False
286
+ in_double = False
287
+ i = 0
288
+ while i < len(command):
289
+ c = command[i]
290
+ if c == "'" and not in_double:
291
+ in_single = not in_single
292
+ current.append(c)
293
+ elif c == '"' and not in_single:
294
+ in_double = not in_double
295
+ current.append(c)
296
+ elif c == "|" and not in_single and not in_double:
297
+ # 跳过 || (逻辑 OR)
298
+ if i + 1 < len(command) and command[i + 1] == "|":
299
+ current.append("||")
300
+ i += 2
301
+ continue
302
+ parts.append("".join(current).strip())
303
+ current = []
304
+ else:
305
+ current.append(c)
306
+ i += 1
307
+
308
+ if current:
309
+ parts.append("".join(current).strip())
310
+
311
+ return [p for p in parts if p]
@@ -0,0 +1,243 @@
1
+ """
2
+ tools/bash/sed_edit_parser.py — BashRuntimeTool 模块2:sed 编辑解析与预览
3
+
4
+ 职责:
5
+ 为 `sed -i` 可控编辑工作流提供“可确定 preview”的解析能力,包括:
6
+ 1. 识别 simple in-place substitution 命令
7
+ 2. 提取 file / pattern / replacement / flags / extended-regex 信息
8
+ 3. 在内存中模拟 sed substitution,生成 preview diff
9
+
10
+ 设计边界:
11
+ - 只处理 simple `sed -i 's/from/to/flags' file` 这类可稳定预览的场景
12
+ - 遇到多文件、未知 flag、无法稳定解析的表达式时返回 None
13
+ - 不负责权限决策;是否进入 preview/apply 流由 `sed_validation.py` 决定
14
+
15
+ 与 CC 对照:
16
+ 对应 CC `sedEditParser.ts` 的 Python 化版本,但保持当前阶段需要的最小可用
17
+ 范围,不复制其全部兼容细节。
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import difflib
23
+ import re
24
+ import shlex
25
+ from dataclasses import dataclass
26
+
27
+
28
+ _BACKSLASH_PLACEHOLDER = "\x00BACKSLASH\x00"
29
+ _PLUS_PLACEHOLDER = "\x00PLUS\x00"
30
+ _QUESTION_PLACEHOLDER = "\x00QUESTION\x00"
31
+ _PIPE_PLACEHOLDER = "\x00PIPE\x00"
32
+ _LPAREN_PLACEHOLDER = "\x00LPAREN\x00"
33
+ _RPAREN_PLACEHOLDER = "\x00RPAREN\x00"
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class SedEditInfo:
38
+ file_path: str
39
+ pattern: str
40
+ replacement: str
41
+ flags: str
42
+ extended_regex: bool
43
+
44
+
45
+ class BashSedEditParser:
46
+ """解析 simple `sed -i 's/a/b/' file` 命令,并生成 preview。"""
47
+
48
+ _VALID_FLAGS = re.compile(r"^[gpimIM1-9]*$")
49
+
50
+ def parse(self, command: str) -> SedEditInfo | None:
51
+ trimmed = command.strip()
52
+ if not re.match(r"^\s*sed\s+", trimmed):
53
+ return None
54
+
55
+ try:
56
+ tokens = shlex.split(trimmed)
57
+ except ValueError:
58
+ return None
59
+
60
+ if not tokens or tokens[0] != "sed":
61
+ return None
62
+
63
+ has_in_place = False
64
+ extended_regex = False
65
+ expression: str | None = None
66
+ file_path: str | None = None
67
+
68
+ i = 1
69
+ while i < len(tokens):
70
+ arg = tokens[i]
71
+ if arg in {"-i", "--in-place"}:
72
+ has_in_place = True
73
+ i += 1
74
+ if i < len(tokens):
75
+ next_arg = tokens[i]
76
+ if next_arg == "" or (not next_arg.startswith("-") and next_arg.startswith(".")):
77
+ i += 1
78
+ continue
79
+ if arg.startswith("-i"):
80
+ has_in_place = True
81
+ i += 1
82
+ continue
83
+ if arg in {"-E", "-r", "--regexp-extended"}:
84
+ extended_regex = True
85
+ i += 1
86
+ continue
87
+ if arg in {"-e", "--expression"}:
88
+ if i + 1 >= len(tokens) or expression is not None:
89
+ return None
90
+ expression = tokens[i + 1]
91
+ i += 2
92
+ continue
93
+ if arg.startswith("--expression="):
94
+ if expression is not None:
95
+ return None
96
+ expression = arg.split("=", 1)[1]
97
+ i += 1
98
+ continue
99
+ if arg.startswith("-"):
100
+ return None
101
+ if expression is None:
102
+ expression = arg
103
+ elif file_path is None:
104
+ file_path = arg
105
+ else:
106
+ return None
107
+ i += 1
108
+
109
+ if not has_in_place or not expression or not file_path:
110
+ return None
111
+ if not expression.startswith("s/"):
112
+ return None
113
+
114
+ parsed_expression = self._parse_substitution_expression(
115
+ expression=expression,
116
+ file_path=file_path,
117
+ extended_regex=extended_regex,
118
+ )
119
+ return parsed_expression
120
+
121
+ def apply_substitution(self, content: str, sed_info: SedEditInfo) -> str:
122
+ regex_flags = ""
123
+ if "g" in sed_info.flags:
124
+ regex_flags += "g"
125
+ if "i" in sed_info.flags or "I" in sed_info.flags:
126
+ regex_flags += "i"
127
+ if "m" in sed_info.flags or "M" in sed_info.flags:
128
+ regex_flags += "m"
129
+
130
+ js_pattern = sed_info.pattern.replace(r"\/", "/")
131
+ if not sed_info.extended_regex:
132
+ js_pattern = (
133
+ js_pattern
134
+ .replace(r"\\", _BACKSLASH_PLACEHOLDER)
135
+ .replace(r"\+", _PLUS_PLACEHOLDER)
136
+ .replace(r"\?", _QUESTION_PLACEHOLDER)
137
+ .replace(r"\|", _PIPE_PLACEHOLDER)
138
+ .replace(r"\(", _LPAREN_PLACEHOLDER)
139
+ .replace(r"\)", _RPAREN_PLACEHOLDER)
140
+ .replace("+", r"\+")
141
+ .replace("?", r"\?")
142
+ .replace("|", r"\|")
143
+ .replace("(", r"\(")
144
+ .replace(")", r"\)")
145
+ .replace(_BACKSLASH_PLACEHOLDER, r"\\")
146
+ .replace(_PLUS_PLACEHOLDER, "+")
147
+ .replace(_QUESTION_PLACEHOLDER, "?")
148
+ .replace(_PIPE_PLACEHOLDER, "|")
149
+ .replace(_LPAREN_PLACEHOLDER, "(")
150
+ .replace(_RPAREN_PLACEHOLDER, ")")
151
+ )
152
+
153
+ replacement = (
154
+ sed_info.replacement
155
+ .replace(r"\/", "/")
156
+ .replace(r"\&", "__ESCAPED_AMP__")
157
+ .replace("&", "$$&")
158
+ .replace("__ESCAPED_AMP__", "&")
159
+ )
160
+
161
+ try:
162
+ regex = re.compile(js_pattern, flags=self._to_python_flags(regex_flags))
163
+ except re.error:
164
+ return content
165
+ return regex.sub(replacement, content)
166
+
167
+ def build_preview(self, *, original: str, updated: str, file_path: str) -> str:
168
+ diff = list(
169
+ difflib.unified_diff(
170
+ original.splitlines(),
171
+ updated.splitlines(),
172
+ fromfile=f"{file_path} (before)",
173
+ tofile=f"{file_path} (after)",
174
+ lineterm="",
175
+ n=3,
176
+ )
177
+ )
178
+ if not diff:
179
+ return f"Preview for {file_path}: no textual changes."
180
+ preview = "\n".join(diff[:60])
181
+ return f"Preview for {file_path}:\n```diff\n{preview}\n```"
182
+
183
+ def _parse_substitution_expression(
184
+ self,
185
+ *,
186
+ expression: str,
187
+ file_path: str,
188
+ extended_regex: bool,
189
+ ) -> SedEditInfo | None:
190
+ rest = expression[2:]
191
+ pattern = ""
192
+ replacement = ""
193
+ flags = ""
194
+ state = "pattern"
195
+ i = 0
196
+ while i < len(rest):
197
+ char = rest[i]
198
+ if char == "\\" and i + 1 < len(rest):
199
+ value = char + rest[i + 1]
200
+ if state == "pattern":
201
+ pattern += value
202
+ elif state == "replacement":
203
+ replacement += value
204
+ else:
205
+ flags += value
206
+ i += 2
207
+ continue
208
+ if char == "/":
209
+ if state == "pattern":
210
+ state = "replacement"
211
+ elif state == "replacement":
212
+ state = "flags"
213
+ else:
214
+ return None
215
+ i += 1
216
+ continue
217
+ if state == "pattern":
218
+ pattern += char
219
+ elif state == "replacement":
220
+ replacement += char
221
+ else:
222
+ flags += char
223
+ i += 1
224
+ if state != "flags":
225
+ return None
226
+ if not self._VALID_FLAGS.match(flags):
227
+ return None
228
+ return SedEditInfo(
229
+ file_path=file_path,
230
+ pattern=pattern,
231
+ replacement=replacement,
232
+ flags=flags,
233
+ extended_regex=extended_regex,
234
+ )
235
+
236
+ @staticmethod
237
+ def _to_python_flags(flag_text: str) -> int:
238
+ value = 0
239
+ if "i" in flag_text:
240
+ value |= re.IGNORECASE
241
+ if "m" in flag_text:
242
+ value |= re.MULTILINE
243
+ return value