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,475 @@
1
+ """
2
+ tools/grep/tool.py — GrepRuntimeTool
3
+
4
+ 职责:
5
+ RuntimeTool 生命周期编排:normalize / validate / permissions / invoke / present。
6
+
7
+ 对应 CC:GrepTool.ts(call + mapToolResultToToolResultBlockParam 语义由 present 承担)。
8
+ validate_input 对 UNC / ``//`` 前缀跳过本地 exists,与 CC validateInput 一致(utils.unc_path)。
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import os
14
+ import time
15
+ from typing import Any
16
+
17
+ from langchain_agentx.tool_runtime.base import RuntimeTool
18
+ from langchain_agentx.utils.path_user_input import UserPathInputNormalizer
19
+ from langchain_agentx.tool_runtime.models import (
20
+ AuthorizationDecision,
21
+ ToolExecutionContext,
22
+ ToolHint,
23
+ ToolResultEnvelope,
24
+ ValidationResult,
25
+ )
26
+
27
+ from langchain_agentx.tools.ripgrep_plugin_exclusions import PluginCacheGlobExclusions
28
+ from langchain_agentx.utils.rg_executable import default_rg_executable
29
+ from langchain_agentx.utils.unc_path import is_unc_path_skip_local_stat
30
+
31
+ from .backend import RipgrepBackend
32
+ from .models import DEFAULT_HEAD_LIMIT, GrepOutputMode, GrepToolInput, GrepToolOutput
33
+ from .prompt import DESCRIPTION, TOOL_NAME
34
+
35
+
36
+ def _apply_head_limit(
37
+ items: list[Any],
38
+ head_limit: int,
39
+ offset: int,
40
+ ) -> tuple[list[Any], bool, int | None, int | None]:
41
+ """对齐 CC applyHeadLimit:head_limit=0 无限制;截断时才设置 applied_limit。"""
42
+ if head_limit == 0:
43
+ sliced = items[offset:]
44
+ return sliced, False, None, offset if offset > 0 else None
45
+ sliced = items[offset : offset + head_limit]
46
+ truncated = len(items) - offset > head_limit
47
+ applied_limit = head_limit if truncated else None
48
+ applied_offset = offset if offset > 0 else None
49
+ return sliced, truncated, applied_limit, applied_offset
50
+
51
+
52
+ def _resolve_hit_path(line: str, search_root: str) -> str:
53
+ line = line.strip()
54
+ if not line:
55
+ return line
56
+ if os.path.isabs(line):
57
+ return os.path.normpath(line)
58
+ base = search_root if os.path.isdir(search_root) else os.path.dirname(search_root)
59
+ return os.path.normpath(os.path.join(base, line))
60
+
61
+
62
+ def _plural(n: int, one: str, many: str) -> str:
63
+ return one if n == 1 else many
64
+
65
+
66
+ class GrepRuntimeTool(RuntimeTool):
67
+ name: str = TOOL_NAME
68
+ description: str = DESCRIPTION
69
+ input_model = GrepToolInput
70
+ output_model = GrepToolOutput
71
+ is_read_only: bool = True
72
+ is_concurrency_safe: bool = True
73
+ never_truncate: bool = False
74
+ dedupe_identical_calls: bool = True
75
+ max_result_size_chars: int = 20_000
76
+
77
+ def __init__(
78
+ self,
79
+ *,
80
+ workspace_root: str,
81
+ policy: Any | None = None,
82
+ state_bridge: Any | None = None,
83
+ backend: RipgrepBackend | None = None,
84
+ rg_path: str | None = None,
85
+ rg_timeout: int = RipgrepBackend.DEFAULT_TIMEOUT,
86
+ ignore_patterns: list[str] | None = None,
87
+ path_normalizer: UserPathInputNormalizer | None = None,
88
+ plugin_cache_glob_exclusions: PluginCacheGlobExclusions | None = None,
89
+ ) -> None:
90
+ super().__init__(policy=policy, state_bridge=state_bridge)
91
+ self._workspace_root = os.path.realpath(os.path.expanduser(workspace_root))
92
+ self._ignore_patterns = [p for p in (ignore_patterns or []) if str(p).strip()]
93
+ self._path_normalizer = path_normalizer or UserPathInputNormalizer()
94
+ self._plugin_cache_glob_exclusions = plugin_cache_glob_exclusions
95
+ resolved_rg = rg_path if rg_path is not None else default_rg_executable()
96
+ self._backend = backend or RipgrepBackend(rg_path=resolved_rg, timeout=rg_timeout)
97
+
98
+ def normalize_input(
99
+ self, raw: dict[str, Any], ctx: ToolExecutionContext
100
+ ) -> dict[str, Any]:
101
+ data = dict(raw)
102
+ path = data.get("path")
103
+ from langchain_agentx.utils.cwd import get_cwd
104
+
105
+ if path is None or (isinstance(path, str) and not path.strip()):
106
+ data["path"] = ctx.cwd or get_cwd()
107
+ elif isinstance(path, str):
108
+ data["path"] = self._path_normalizer.normalize_and_expand(
109
+ path, base_dir=ctx.cwd
110
+ )
111
+ pat = data.get("pattern")
112
+ if isinstance(pat, str):
113
+ data["pattern"] = pat.strip()
114
+ return data
115
+
116
+ def validate_input(
117
+ self, data: dict[str, Any], ctx: ToolExecutionContext
118
+ ) -> ValidationResult:
119
+ pattern = (data.get("pattern") or "").strip()
120
+ if not pattern:
121
+ return ValidationResult(
122
+ ok=False,
123
+ message="Pattern cannot be empty",
124
+ code="EMPTY_PATTERN",
125
+ )
126
+ search_path = data.get("path") or ""
127
+ # CC validateInput:UNC / // 前缀跳过本地 stat(防 NTLM 等凭据面)
128
+ if is_unc_path_skip_local_stat(search_path):
129
+ return ValidationResult(ok=True)
130
+ if not os.path.exists(search_path):
131
+ return ValidationResult(
132
+ ok=False,
133
+ message=(
134
+ f"Path does not exist: {search_path}. "
135
+ f"Current workspace: {self._workspace_root}."
136
+ ),
137
+ code="PATH_NOT_FOUND",
138
+ )
139
+ return ValidationResult(ok=True)
140
+
141
+ def check_permissions(
142
+ self, data: dict[str, Any], ctx: ToolExecutionContext
143
+ ) -> AuthorizationDecision:
144
+ """
145
+ 显式授权入口:与 RuntimeTool 基类行为一致,委托 PolicyEngine。
146
+ 保留该 hook 便于审计与后续扩展 grep 特有权限策略。
147
+ """
148
+ return super().check_permissions(data, ctx)
149
+
150
+ def invoke(self, data: dict[str, Any], ctx: ToolExecutionContext) -> GrepToolOutput:
151
+ t0 = time.perf_counter()
152
+ pattern = str(data["pattern"]).strip()
153
+ search_root = str(data["path"])
154
+ mode = GrepOutputMode(data.get("output_mode", GrepOutputMode.files_with_matches))
155
+ head_limit = int(data.get("head_limit", DEFAULT_HEAD_LIMIT))
156
+ offset = int(data.get("offset", 0))
157
+
158
+ glob = data.get("glob")
159
+ glob_s = str(glob).strip() if glob else None
160
+ if glob_s == "":
161
+ glob_s = None
162
+
163
+ ft = data.get("file_type")
164
+ ft_s = str(ft).strip() if ft else None
165
+ if ft_s == "":
166
+ ft_s = None
167
+
168
+ ctx_before = data.get("context_before")
169
+ ctx_after = data.get("context_after")
170
+ ctx_unified = data.get("context")
171
+ show_ln = bool(data.get("show_line_numbers", True))
172
+ case_i = bool(data.get("case_insensitive", False))
173
+ multiline = bool(data.get("multiline", False))
174
+
175
+ plugin_ex: list[str] | None = None
176
+ if self._plugin_cache_glob_exclusions is not None:
177
+ plugin_ex = self._plugin_cache_glob_exclusions.get_exclusions_for_search(
178
+ search_root
179
+ )
180
+ if not plugin_ex:
181
+ plugin_ex = None
182
+
183
+ cancel_ev = ctx.cancel_event
184
+
185
+ # CC 在 content/count 模式下会先 applyHeadLimit 再做相对化,避免无界输出。
186
+ # 这里实现“准流式提前截止”:当 head_limit!=0 时,仅收集 offset+head_limit 行。
187
+ if mode in (GrepOutputMode.content, GrepOutputMode.count) and head_limit != 0:
188
+ raw_lines = self._backend.search_stream_limited(
189
+ pattern,
190
+ search_root,
191
+ glob=glob_s,
192
+ ignore_patterns=self._ignore_patterns,
193
+ file_type=ft_s,
194
+ output_mode=mode,
195
+ context_before=ctx_before if mode == GrepOutputMode.content else None,
196
+ context_after=ctx_after if mode == GrepOutputMode.content else None,
197
+ context=ctx_unified if mode == GrepOutputMode.content else None,
198
+ show_line_numbers=show_ln if mode == GrepOutputMode.content else True,
199
+ case_insensitive=case_i,
200
+ multiline=multiline,
201
+ max_lines=offset + head_limit,
202
+ plugin_glob_exclusions=plugin_ex,
203
+ cancel_event=cancel_ev,
204
+ )
205
+ else:
206
+ raw_lines = self._backend.search(
207
+ pattern,
208
+ search_root,
209
+ glob=glob_s,
210
+ ignore_patterns=self._ignore_patterns,
211
+ file_type=ft_s,
212
+ output_mode=mode,
213
+ context_before=ctx_before if mode == GrepOutputMode.content else None,
214
+ context_after=ctx_after if mode == GrepOutputMode.content else None,
215
+ context=ctx_unified if mode == GrepOutputMode.content else None,
216
+ show_line_numbers=show_ln if mode == GrepOutputMode.content else True,
217
+ case_insensitive=case_i,
218
+ multiline=multiline,
219
+ plugin_glob_exclusions=plugin_ex,
220
+ cancel_event=cancel_ev,
221
+ )
222
+
223
+ duration_ms = (time.perf_counter() - t0) * 1000.0
224
+
225
+ if mode == GrepOutputMode.content:
226
+ return self._finish_content(
227
+ raw_lines, pattern, search_root, head_limit, offset, duration_ms
228
+ )
229
+ if mode == GrepOutputMode.count:
230
+ return self._finish_count(
231
+ raw_lines, pattern, search_root, head_limit, offset, duration_ms
232
+ )
233
+ return self._finish_files(
234
+ raw_lines, pattern, search_root, head_limit, offset, duration_ms
235
+ )
236
+
237
+ def _finish_files(
238
+ self,
239
+ raw_lines: list[str],
240
+ pattern: str,
241
+ search_root: str,
242
+ head_limit: int,
243
+ offset: int,
244
+ duration_ms: float,
245
+ ) -> GrepToolOutput:
246
+ paths = [_resolve_hit_path(ln, search_root) for ln in raw_lines if ln.strip()]
247
+
248
+ def _mtime_or_zero(p: str) -> float:
249
+ try:
250
+ return os.path.getmtime(p)
251
+ except OSError:
252
+ return 0.0
253
+
254
+ sorted_paths = sorted(paths, key=_mtime_or_zero, reverse=True)
255
+ sliced, truncated, applied_limit, applied_offset = _apply_head_limit(
256
+ sorted_paths, head_limit, offset
257
+ )
258
+ rels = [self._to_relative(p) for p in sliced]
259
+ return GrepToolOutput(
260
+ mode=GrepOutputMode.files_with_matches,
261
+ pattern=pattern,
262
+ search_root=search_root,
263
+ filenames=rels,
264
+ num_files=len(rels),
265
+ truncated=truncated,
266
+ applied_limit=applied_limit,
267
+ applied_offset=applied_offset,
268
+ duration_ms=duration_ms,
269
+ )
270
+
271
+ def _finish_content(
272
+ self,
273
+ raw_lines: list[str],
274
+ pattern: str,
275
+ search_root: str,
276
+ head_limit: int,
277
+ offset: int,
278
+ duration_ms: float,
279
+ ) -> GrepToolOutput:
280
+ sliced, truncated, applied_limit, applied_offset = _apply_head_limit(
281
+ raw_lines, head_limit, offset
282
+ )
283
+ final_lines = [self._relativize_content_line(ln, search_root) for ln in sliced]
284
+ body = "\n".join(final_lines)
285
+ return GrepToolOutput(
286
+ mode=GrepOutputMode.content,
287
+ pattern=pattern,
288
+ search_root=search_root,
289
+ content=body,
290
+ num_lines=len(final_lines),
291
+ truncated=truncated,
292
+ applied_limit=applied_limit,
293
+ applied_offset=applied_offset,
294
+ duration_ms=duration_ms,
295
+ )
296
+
297
+ def _finish_count(
298
+ self,
299
+ raw_lines: list[str],
300
+ pattern: str,
301
+ search_root: str,
302
+ head_limit: int,
303
+ offset: int,
304
+ duration_ms: float,
305
+ ) -> GrepToolOutput:
306
+ sliced, truncated, applied_limit, applied_offset = _apply_head_limit(
307
+ raw_lines, head_limit, offset
308
+ )
309
+ final_lines: list[str] = []
310
+ total_matches = 0
311
+ file_count = 0
312
+ for ln in sliced:
313
+ idx = ln.rfind(":")
314
+ if idx <= 0:
315
+ final_lines.append(ln)
316
+ continue
317
+ file_path = ln[:idx]
318
+ count_str = ln[idx + 1 :].strip()
319
+ try:
320
+ cnt = int(count_str)
321
+ except ValueError:
322
+ cnt = 0
323
+ total_matches += cnt
324
+ file_count += 1
325
+ final_lines.append(self._to_relative(_resolve_hit_path(file_path, search_root)) + ":" + count_str)
326
+
327
+ return GrepToolOutput(
328
+ mode=GrepOutputMode.count,
329
+ pattern=pattern,
330
+ search_root=search_root,
331
+ count_content="\n".join(final_lines),
332
+ num_files=file_count,
333
+ num_matches=total_matches,
334
+ truncated=truncated,
335
+ applied_limit=applied_limit,
336
+ applied_offset=applied_offset,
337
+ duration_ms=duration_ms,
338
+ )
339
+
340
+ def _to_relative(self, abs_path: str) -> str:
341
+ try:
342
+ return os.path.relpath(abs_path, self._workspace_root)
343
+ except ValueError:
344
+ return abs_path
345
+
346
+ def _relativize_content_line(self, line: str, search_root: str) -> str:
347
+ idx = line.find(":")
348
+ if idx <= 0:
349
+ return line
350
+ file_path = line[:idx]
351
+ rest = line[idx:]
352
+ resolved = _resolve_hit_path(file_path, search_root)
353
+ return self._to_relative(resolved) + rest
354
+
355
+ def _format_pagination(self, applied_limit: int | None, applied_offset: int | None) -> str:
356
+ parts: list[str] = []
357
+ if applied_limit is not None:
358
+ parts.append(f"limit: {applied_limit}")
359
+ if applied_offset:
360
+ parts.append(f"offset: {applied_offset}")
361
+ return ", ".join(parts)
362
+
363
+ def present(
364
+ self, data: dict[str, Any], result: Any, ctx: ToolExecutionContext
365
+ ) -> ToolResultEnvelope:
366
+ if not isinstance(result, GrepToolOutput):
367
+ return ToolResultEnvelope(
368
+ status="ok",
369
+ tool_name=self.name,
370
+ summary=str(result),
371
+ payload=str(result),
372
+ )
373
+
374
+ r = result
375
+ meta = {
376
+ "mode": r.mode.value,
377
+ "duration_ms": round(r.duration_ms, 2),
378
+ "pattern": r.pattern,
379
+ }
380
+
381
+ if r.mode == GrepOutputMode.files_with_matches:
382
+ if r.num_files == 0:
383
+ return ToolResultEnvelope(
384
+ status="ok",
385
+ tool_name=self.name,
386
+ summary="No files found",
387
+ payload="No files found",
388
+ meta=meta,
389
+ )
390
+ lim = self._format_pagination(r.applied_limit, r.applied_offset)
391
+ summary = (
392
+ f"Found {r.num_files} {_plural(r.num_files, 'file', 'files')}"
393
+ + (f" {lim}" if lim else "")
394
+ )
395
+ body = "\n".join(r.filenames)
396
+ hints: list[ToolHint] | None = None
397
+ if r.truncated and r.applied_limit is not None:
398
+ off = r.applied_offset or 0
399
+ hints = [
400
+ ToolHint(
401
+ message=(
402
+ f"Results truncated at {r.applied_limit}. "
403
+ f"Use offset={off + r.applied_limit} to fetch more, "
404
+ "or narrow pattern/path/glob."
405
+ )
406
+ )
407
+ ]
408
+ return ToolResultEnvelope(
409
+ status="ok",
410
+ tool_name=self.name,
411
+ summary=summary,
412
+ payload=body,
413
+ hints=hints,
414
+ meta=meta,
415
+ )
416
+
417
+ if r.mode == GrepOutputMode.content:
418
+ content = r.content if r.content else "No matches found"
419
+ lim = self._format_pagination(r.applied_limit, r.applied_offset)
420
+ if lim:
421
+ content = f"{content}\n\n[Showing results with pagination = {lim}]"
422
+ summary = f"Found {r.num_lines} matching {_plural(r.num_lines, 'line', 'lines')}"
423
+ if r.truncated and r.applied_limit is not None:
424
+ summary += f" (truncated at {r.applied_limit})"
425
+ hints = None
426
+ if r.truncated and r.applied_limit is not None:
427
+ off = r.applied_offset or 0
428
+ hints = [
429
+ ToolHint(
430
+ message=(
431
+ f"Use offset={off + r.applied_limit} for more lines, "
432
+ "or narrow the search."
433
+ )
434
+ )
435
+ ]
436
+ return ToolResultEnvelope(
437
+ status="ok",
438
+ tool_name=self.name,
439
+ summary=summary,
440
+ payload=content,
441
+ hints=hints,
442
+ meta=meta,
443
+ )
444
+
445
+ # count
446
+ raw_lines = r.count_content or "No matches found"
447
+ lim = self._format_pagination(r.applied_limit, r.applied_offset)
448
+ occ = _plural(r.num_matches, "occurrence", "occurrences")
449
+ fword = _plural(r.num_files, "file", "files")
450
+ summary = f"Found {r.num_matches} total {occ} across {r.num_files} {fword}."
451
+ if lim:
452
+ summary += f" (pagination: {lim})"
453
+ tail = f"Found {r.num_matches} total {occ} across {r.num_files} {fword}."
454
+ if lim:
455
+ tail += f" with pagination = {lim}"
456
+ body = raw_lines + "\n\n" + tail
457
+ hints = None
458
+ if r.truncated and r.applied_limit is not None:
459
+ off = r.applied_offset or 0
460
+ hints = [
461
+ ToolHint(
462
+ message=(
463
+ f"Use offset={off + r.applied_limit} for more entries, "
464
+ "or narrow the search."
465
+ )
466
+ )
467
+ ]
468
+ return ToolResultEnvelope(
469
+ status="ok",
470
+ tool_name=self.name,
471
+ summary=summary,
472
+ payload=body,
473
+ hints=hints,
474
+ meta=meta,
475
+ )
@@ -0,0 +1,9 @@
1
+ """
2
+ tools/read — ReadRuntimeTool 包
3
+
4
+ ReadRuntimeTool 是工具体系 P1 首个工具,支持文本、图片、PDF、Jupyter Notebook 读取。
5
+ """
6
+
7
+ from .tool import ReadRuntimeTool
8
+
9
+ __all__ = ["ReadRuntimeTool"]