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,290 @@
1
+ """
2
+ tools/bash/session_runtime.py — BashRuntimeTool v3:长期会话执行运行时
3
+
4
+ 职责:
5
+ 提供单个长期存活 bash 会话的生命周期与命令执行能力,支持:
6
+ 1. 同一会话内连续命令状态保持(cwd/env/shell context)
7
+ 2. 命令级 begin/end/cwd 标记分帧
8
+ 3. 超时中断与会话重建
9
+
10
+ 在整体链路中的位置:
11
+ - 由 `session_manager.py` 统一管理实例
12
+ - 由 `backend.py` 在非沙箱执行路径按配置选择调用
13
+
14
+ 与 CC 对照:
15
+ 对应 CC `Shell.ts` / `LocalShellTask` 的长期 shell 会话语义基础能力。
16
+ 当前是 v3 第一版:优先保证会话持久化与可靠分帧,不包含流式 UI 事件。
17
+
18
+ **Windows 上 `BashBackend(use_persistent_session=True)` 前置条件**(exec-plan Phase 4):
19
+ - 须有可用的 **POSIX shell**(`resolve_posix_shell` 或 **`posix_shell_executable=`** 注入);
20
+ - 默认 **`use_persistent_session` 在 win32 为 false**(见 `bash_runtime_contract` / `BashBackend.__init__`),显式开启前请确认 **Git Bash/MSYS** 下 stdin 多行与换行稳定;
21
+ - 与 `BashBackend` 一致:子进程 **`SHELL`** 在未设置时指向解析到的 shell;**`AGENTX_BASH_CREATE_NO_WINDOW=1`** 时可传 **`CREATE_NO_WINDOW`**。
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import os
27
+ import queue
28
+ import re
29
+ import signal
30
+ import subprocess
31
+ import sys
32
+ import threading
33
+ import time
34
+ import uuid
35
+ from collections.abc import Iterator
36
+ from dataclasses import dataclass
37
+
38
+ from .shell_locator import resolve_posix_shell
39
+ from .windows_shell_quoting import rewrite_windows_null_redirect
40
+
41
+
42
+ _END_RE = re.compile(r"^__AGENTX_END_([A-Za-z0-9_]+)__:(-?\d+)$")
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class BashSessionExecResult:
47
+ stdout: str
48
+ exit_code: int
49
+ interrupted: bool
50
+ cwd_after: str | None = None
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class BashSessionStreamEvent:
55
+ kind: str
56
+ chunk: str | None = None
57
+ exit_code: int | None = None
58
+ interrupted: bool | None = None
59
+ cwd_after: str | None = None
60
+
61
+
62
+ class BashSessionRuntime:
63
+ """单个长期 bash 会话运行时。"""
64
+
65
+ def __init__(
66
+ self,
67
+ *,
68
+ initial_cwd: str | None = None,
69
+ base_env: dict[str, str] | None = None,
70
+ posix_shell_executable: str | None = None,
71
+ ) -> None:
72
+ self._initial_cwd = initial_cwd
73
+ self._base_env = dict(base_env or os.environ.copy())
74
+ self._posix_shell_override = posix_shell_executable
75
+ self._proc: subprocess.Popen[str] | None = None
76
+ self._reader_thread: threading.Thread | None = None
77
+ self._line_queue: queue.Queue[str] = queue.Queue()
78
+ self._lock = threading.Lock()
79
+ self._closed = False
80
+ self._shell_exe: str | None = None
81
+ self._start()
82
+
83
+ def execute(self, command: str, timeout_sec: int) -> BashSessionExecResult:
84
+ events = self.execute_stream(command=command, timeout_sec=timeout_sec)
85
+ stdout_parts: list[str] = []
86
+ final_exit_code = -1
87
+ final_interrupted = False
88
+ final_cwd_after: str | None = None
89
+ for ev in events:
90
+ if ev.kind == "chunk" and ev.chunk:
91
+ stdout_parts.append(ev.chunk)
92
+ if ev.kind == "final":
93
+ final_exit_code = ev.exit_code if ev.exit_code is not None else -1
94
+ final_interrupted = bool(ev.interrupted)
95
+ final_cwd_after = ev.cwd_after
96
+ return BashSessionExecResult(
97
+ stdout="".join(stdout_parts),
98
+ exit_code=final_exit_code,
99
+ interrupted=final_interrupted,
100
+ cwd_after=final_cwd_after,
101
+ )
102
+
103
+ def execute_stream(self, command: str, timeout_sec: int) -> list[BashSessionStreamEvent]:
104
+ return list(self.execute_stream_iter(command=command, timeout_sec=timeout_sec))
105
+
106
+ def execute_stream_iter(
107
+ self,
108
+ *,
109
+ command: str,
110
+ timeout_sec: int,
111
+ cancel_event: threading.Event | None = None,
112
+ ) -> Iterator[BashSessionStreamEvent]:
113
+ with self._lock:
114
+ if self._closed:
115
+ raise RuntimeError("Bash session runtime is closed")
116
+ self._ensure_alive()
117
+ assert self._proc is not None
118
+ token = uuid.uuid4().hex[:12]
119
+ begin_marker = f"__AGENTX_BEGIN_{token}__"
120
+ cwd_marker_prefix = f"__AGENTX_CWD_{token}__:"
121
+ end_marker_prefix = f"__AGENTX_END_{token}__:"
122
+ command = rewrite_windows_null_redirect(command)
123
+ wrapped = (
124
+ f"printf '%s\\n' '{begin_marker}'\n"
125
+ f"{command}\n"
126
+ f"__AGENTX_EC=$?\n"
127
+ f"printf '%s%s\\n' '{cwd_marker_prefix}' \"$PWD\"\n"
128
+ f"printf '%s%s\\n' '{end_marker_prefix}' \"$__AGENTX_EC\"\n"
129
+ )
130
+ assert self._proc.stdin is not None
131
+ self._proc.stdin.write(wrapped)
132
+ self._proc.stdin.flush()
133
+
134
+ collecting = False
135
+ cwd_after: str | None = None
136
+ deadline = time.monotonic() + max(1, timeout_sec)
137
+ while True:
138
+ if cancel_event is not None and cancel_event.is_set():
139
+ self._interrupt_and_restart()
140
+ yield BashSessionStreamEvent(
141
+ kind="final",
142
+ exit_code=-1,
143
+ interrupted=True,
144
+ cwd_after=cwd_after,
145
+ )
146
+ return
147
+ remaining = deadline - time.monotonic()
148
+ if remaining <= 0:
149
+ self._interrupt_and_restart()
150
+ yield BashSessionStreamEvent(
151
+ kind="final",
152
+ exit_code=-1,
153
+ interrupted=True,
154
+ cwd_after=cwd_after,
155
+ )
156
+ return
157
+ try:
158
+ line = self._line_queue.get(timeout=remaining)
159
+ except queue.Empty:
160
+ self._interrupt_and_restart()
161
+ yield BashSessionStreamEvent(
162
+ kind="final",
163
+ exit_code=-1,
164
+ interrupted=True,
165
+ cwd_after=cwd_after,
166
+ )
167
+ return
168
+
169
+ if not collecting:
170
+ if line.rstrip("\n") == begin_marker:
171
+ collecting = True
172
+ continue
173
+
174
+ stripped = line.rstrip("\n")
175
+ if stripped.startswith(cwd_marker_prefix):
176
+ cwd_after = stripped[len(cwd_marker_prefix):]
177
+ continue
178
+ match = _END_RE.match(stripped)
179
+ if match and stripped.startswith(end_marker_prefix):
180
+ exit_code = int(match.group(2))
181
+ yield BashSessionStreamEvent(
182
+ kind="final",
183
+ exit_code=exit_code,
184
+ interrupted=False,
185
+ cwd_after=cwd_after,
186
+ )
187
+ return
188
+ yield BashSessionStreamEvent(kind="chunk", chunk=line)
189
+
190
+ def close(self) -> None:
191
+ with self._lock:
192
+ self._closed = True
193
+ self._terminate_process()
194
+
195
+ def _resolve_shell_exe(self) -> str:
196
+ if self._posix_shell_override is not None:
197
+ return os.path.normpath(
198
+ os.path.realpath(os.path.expanduser(self._posix_shell_override))
199
+ )
200
+ return resolve_posix_shell(environ=self._base_env)
201
+
202
+ def _spawn_env(self, shell_exe: str) -> dict[str, str]:
203
+ out = dict(self._base_env)
204
+ out.setdefault("SHELL", shell_exe)
205
+ return out
206
+
207
+ @staticmethod
208
+ def _win32_creationflags() -> int:
209
+ if sys.platform != "win32":
210
+ return 0
211
+ if os.environ.get("AGENTX_BASH_CREATE_NO_WINDOW") != "1":
212
+ return 0
213
+ return int(getattr(subprocess, "CREATE_NO_WINDOW", 0))
214
+
215
+ def _start(self) -> None:
216
+ self._line_queue = queue.Queue()
217
+ if self._shell_exe is None:
218
+ self._shell_exe = self._resolve_shell_exe()
219
+ shell_exe = self._shell_exe
220
+ assert shell_exe is not None
221
+ spawn_env = self._spawn_env(shell_exe)
222
+ _cf = self._win32_creationflags()
223
+ if _cf:
224
+ self._proc = subprocess.Popen(
225
+ [shell_exe, "--noprofile", "--norc"],
226
+ stdin=subprocess.PIPE,
227
+ stdout=subprocess.PIPE,
228
+ stderr=subprocess.STDOUT,
229
+ cwd=self._initial_cwd,
230
+ env=spawn_env,
231
+ start_new_session=True,
232
+ text=True,
233
+ bufsize=1,
234
+ creationflags=_cf,
235
+ )
236
+ else:
237
+ self._proc = subprocess.Popen(
238
+ [shell_exe, "--noprofile", "--norc"],
239
+ stdin=subprocess.PIPE,
240
+ stdout=subprocess.PIPE,
241
+ stderr=subprocess.STDOUT,
242
+ cwd=self._initial_cwd,
243
+ env=spawn_env,
244
+ start_new_session=True,
245
+ text=True,
246
+ bufsize=1,
247
+ )
248
+ self._reader_thread = threading.Thread(
249
+ target=self._reader_loop,
250
+ name="bash-session-reader",
251
+ daemon=True,
252
+ )
253
+ self._reader_thread.start()
254
+
255
+ def _reader_loop(self) -> None:
256
+ proc = self._proc
257
+ if proc is None or proc.stdout is None:
258
+ return
259
+ while True:
260
+ line = proc.stdout.readline()
261
+ if line == "":
262
+ break
263
+ self._line_queue.put(line)
264
+
265
+ def _ensure_alive(self) -> None:
266
+ if self._proc is None or self._proc.poll() is not None:
267
+ self._start()
268
+
269
+ def _interrupt_and_restart(self) -> None:
270
+ self._terminate_process(send_interrupt=True)
271
+ self._start()
272
+
273
+ def _terminate_process(self, send_interrupt: bool = False) -> None:
274
+ proc = self._proc
275
+ self._proc = None
276
+ if proc is None:
277
+ return
278
+ try:
279
+ if proc.poll() is None:
280
+ if send_interrupt:
281
+ os.killpg(proc.pid, signal.SIGINT)
282
+ time.sleep(0.1)
283
+ if proc.poll() is None:
284
+ os.killpg(proc.pid, signal.SIGTERM)
285
+ time.sleep(0.1)
286
+ if proc.poll() is None:
287
+ os.killpg(proc.pid, signal.SIGKILL)
288
+ proc.wait(timeout=1)
289
+ except Exception:
290
+ pass
@@ -0,0 +1,191 @@
1
+ """
2
+ shell_locator.py — POSIX shell(bash/zsh)可执行文件解析
3
+
4
+ 职责:
5
+ 按冻结顺序解析 **绝对路径**:环境变量覆盖 → PATH 上 `where`/`which` → 固定回退路径;
6
+ 并对候选做 **basename 含 bash|zsh**、普通文件、可执行性校验(对齐 CC `findSuitableShell` 思路)。
7
+
8
+ 链路位置:
9
+ `BashBackend` / `BashSessionRuntime` 在 `Popen` / `create_subprocess_exec` 前取 argv[0];
10
+ 顺序常量与 `bash_runtime_contract.SHELL_ENV_PRECEDENCE` 一致。
11
+
12
+ 当前裁剪范围:
13
+ 不重写用户命令;WSL 包装命令非默认解析链。Windows cwd 文件回传见 **`cwd_reporter.py`**。
14
+ Windows 使用 **`where.exe`** 遍历 PATH(与 CC `utils/which.ts` 语义对齐);**不**将
15
+ `shutil.which("bash")` 作为 Windows 主路径来源——因 `shutil.which` 与 `where.exe` 在
16
+ PATHEXT、遍历顺序上可能不一致(若未来改为 which 主路径,须在模块注释与单测
17
+ 中写明与 CC 的差异及原因)。
18
+
19
+ CC 对照(src/ 相对 CC 检出根):
20
+ `utils/Shell.ts` — `findSuitableShell`
21
+ `utils/which.ts` — `where.exe` 与首行语义
22
+ `utils/windowsPaths.ts` — cwd 下同名 exe 过滤(本实现 `_skip_if_in_cwd`)
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import os
28
+ import shutil
29
+ import subprocess
30
+ import sys
31
+ from collections.abc import Mapping
32
+
33
+ from .bash_runtime_contract import SHELL_ENV_PRECEDENCE
34
+
35
+ _RUNTIME_ERROR_LEAD = (
36
+ "BashRuntimeTool:未找到可用的 POSIX shell(bash / zsh)。\n\n"
37
+ "推荐:\n"
38
+ " 1. 安装 Git for Windows:https://git-scm.com/download/win\n"
39
+ " 2. 安装后在 CMD 中执行:where bash\n"
40
+ " 应出现类似:C:\\Program Files\\Git\\bin\\bash.exe\n\n"
41
+ "临时指定(本工程优先于 CC 变量):\n"
42
+ " set LANGCHAIN_AGENTX_SHELL=C:\\path\\to\\bash.exe\n\n"
43
+ "兼容 CC:\n"
44
+ " set CLAUDE_CODE_SHELL=C:\\path\\to\\bash.exe\n\n"
45
+ "当前已尝试:\n"
46
+ " LANGCHAIN_AGENTX_SHELL → CLAUDE_CODE_SHELL → SHELL → where/which → 常见路径回退\n"
47
+ )
48
+
49
+ _WIN_GIT_BASH_FALLBACKS: tuple[str, ...] = (
50
+ r"C:\Program Files\Git\bin\bash.exe",
51
+ r"C:\Program Files (x86)\Git\bin\bash.exe",
52
+ )
53
+
54
+ _POSIX_BASH_FALLBACKS: tuple[str, ...] = (
55
+ "/bin/bash",
56
+ "/usr/bin/bash",
57
+ "/usr/local/bin/bash",
58
+ "/opt/homebrew/bin/bash",
59
+ )
60
+
61
+ _POSIX_ZSH_FALLBACKS: tuple[str, ...] = (
62
+ "/bin/zsh",
63
+ "/usr/bin/zsh",
64
+ "/usr/local/bin/zsh",
65
+ "/opt/homebrew/bin/zsh",
66
+ )
67
+
68
+
69
+ def _norm_exe(path: str) -> str:
70
+ return os.path.normpath(os.path.realpath(os.path.expanduser(path.strip().strip('"'))))
71
+
72
+
73
+ def _basename_ok(path: str) -> bool:
74
+ base = os.path.basename(path).lower()
75
+ return "bash" in base or "zsh" in base
76
+
77
+
78
+ def _skip_if_in_cwd(path: str) -> bool:
79
+ """与 CC cwd 投毒防护类似:跳过「解析后位于当前工作目录」的候选。"""
80
+ try:
81
+ d = os.path.realpath(os.path.dirname(os.path.abspath(path)))
82
+ c = os.path.realpath(os.getcwd())
83
+ return d == c
84
+ except (OSError, ValueError):
85
+ return False
86
+
87
+
88
+ def _is_executable_file(path: str) -> bool:
89
+ if not os.path.isfile(path):
90
+ return False
91
+ if os.access(path, os.X_OK):
92
+ return True
93
+ try:
94
+ r = subprocess.run(
95
+ [path, "--version"],
96
+ capture_output=True,
97
+ timeout=5,
98
+ env=os.environ,
99
+ )
100
+ return r.returncode == 0
101
+ except (OSError, subprocess.TimeoutExpired):
102
+ return False
103
+
104
+
105
+ def _validate_shell(path: str, *, allow_in_cwd: bool = False) -> str | None:
106
+ p = _norm_exe(path)
107
+ if not _basename_ok(p):
108
+ return None
109
+ if not allow_in_cwd and _skip_if_in_cwd(p):
110
+ return None
111
+ if not _is_executable_file(p):
112
+ return None
113
+ return p
114
+
115
+
116
+ def _where_win(names: tuple[str, ...], env: dict[str, str]) -> list[str]:
117
+ out: list[str] = []
118
+ for name in names:
119
+ try:
120
+ cp = subprocess.run(
121
+ ["where.exe", name],
122
+ capture_output=True,
123
+ text=True,
124
+ timeout=30,
125
+ env=env,
126
+ )
127
+ except (OSError, subprocess.TimeoutExpired):
128
+ continue
129
+ if cp.returncode != 0 or not (cp.stdout or "").strip():
130
+ continue
131
+ for line in cp.stdout.splitlines():
132
+ s = line.strip().strip('"')
133
+ if s:
134
+ out.append(s)
135
+ return out
136
+
137
+
138
+ def _which_posix(cmd: str, env: dict[str, str]) -> str | None:
139
+ path = env.get("PATH")
140
+ if path:
141
+ return shutil.which(cmd, path=path)
142
+ return shutil.which(cmd)
143
+
144
+
145
+ class PosixShellLocator:
146
+ """从环境中解析 POSIX shell 绝对路径。"""
147
+
148
+ __slots__ = ("_environ",)
149
+
150
+ def __init__(self, environ: Mapping[str, str] | None = None) -> None:
151
+ self._environ = dict(os.environ if environ is None else environ)
152
+
153
+ def resolve(self) -> str:
154
+ merged = self._environ
155
+
156
+ for key in SHELL_ENV_PRECEDENCE:
157
+ raw = merged.get(key)
158
+ if raw is None or not str(raw).strip():
159
+ continue
160
+ candidate = _norm_exe(str(raw))
161
+ ok = _validate_shell(candidate, allow_in_cwd=True)
162
+ if ok:
163
+ return ok
164
+
165
+ if sys.platform == "win32":
166
+ for line in _where_win(("bash", "zsh"), merged):
167
+ ok = _validate_shell(line, allow_in_cwd=False)
168
+ if ok:
169
+ return ok
170
+ for fb in _WIN_GIT_BASH_FALLBACKS:
171
+ ok = _validate_shell(fb, allow_in_cwd=False)
172
+ if ok:
173
+ return ok
174
+ else:
175
+ for cmd in ("bash", "zsh"):
176
+ w = _which_posix(cmd, merged)
177
+ if w:
178
+ ok = _validate_shell(w, allow_in_cwd=False)
179
+ if ok:
180
+ return ok
181
+ for fb in _POSIX_BASH_FALLBACKS + _POSIX_ZSH_FALLBACKS:
182
+ ok = _validate_shell(fb, allow_in_cwd=False)
183
+ if ok:
184
+ return ok
185
+
186
+ raise RuntimeError(_RUNTIME_ERROR_LEAD)
187
+
188
+
189
+ def resolve_posix_shell(environ: Mapping[str, str] | None = None) -> str:
190
+ """解析并返回 POSIX shell 绝对路径(薄封装)。"""
191
+ return PosixShellLocator(environ=environ).resolve()
@@ -0,0 +1,91 @@
1
+ """
2
+ tools/bash/task_runtime.py — P1 任务编排统一状态机协议
3
+
4
+ 职责:
5
+ 提供前后台统一任务生命周期状态机与快照模型:
6
+ created -> running -> (completed|failed|interrupted|cancelled)
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass, field
12
+ from typing import Literal
13
+
14
+
15
+ TaskMode = Literal["foreground", "background"]
16
+ TaskStatus = Literal[
17
+ "created",
18
+ "running",
19
+ "completed",
20
+ "failed",
21
+ "interrupted",
22
+ "cancelled",
23
+ ]
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class BashTaskSnapshot:
28
+ task_id: str
29
+ task_mode: TaskMode
30
+ task_status: TaskStatus
31
+ exit_code: int | None
32
+ running: bool
33
+ created_ts: float | None = None
34
+ started_ts: float | None = None
35
+ ended_ts: float | None = None
36
+ output_path: str | None = None
37
+ pid: int | None = None
38
+ output_size: int | None = None
39
+
40
+
41
+ @dataclass
42
+ class BashTaskStateMachine:
43
+ """任务状态机:显式限制状态转移。"""
44
+
45
+ task_id: str
46
+ task_mode: TaskMode
47
+ status: TaskStatus = "created"
48
+ exit_code: int | None = None
49
+ created_ts: float | None = None
50
+ started_ts: float | None = None
51
+ ended_ts: float | None = None
52
+ output_path: str | None = None
53
+ pid: int | None = None
54
+ output_size: int | None = None
55
+ _history: list[TaskStatus] = field(default_factory=lambda: ["created"])
56
+
57
+ _TRANSITIONS: dict[TaskStatus, set[TaskStatus]] = field(
58
+ init=False,
59
+ default_factory=lambda: {
60
+ "created": {"running", "cancelled"},
61
+ "running": {"completed", "failed", "interrupted", "cancelled"},
62
+ "completed": set(),
63
+ "failed": set(),
64
+ "interrupted": set(),
65
+ "cancelled": set(),
66
+ },
67
+ )
68
+
69
+ def transition_to(self, status: TaskStatus) -> None:
70
+ allowed = self._TRANSITIONS.get(self.status, set())
71
+ if status not in allowed:
72
+ raise ValueError(f"Invalid task state transition: {self.status} -> {status}")
73
+ self.status = status
74
+ self._history.append(status)
75
+
76
+ def to_snapshot(self) -> BashTaskSnapshot:
77
+ # 仅 running 表示仍在执行;终态均为 running=False
78
+ running = self.status == "running"
79
+ return BashTaskSnapshot(
80
+ task_id=self.task_id,
81
+ task_mode=self.task_mode,
82
+ task_status=self.status,
83
+ exit_code=self.exit_code,
84
+ running=running,
85
+ created_ts=self.created_ts,
86
+ started_ts=self.started_ts,
87
+ ended_ts=self.ended_ts,
88
+ output_path=self.output_path,
89
+ pid=self.pid,
90
+ output_size=self.output_size,
91
+ )