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,32 @@
1
+ """
2
+ tool_runtime/resolvers/background.py — 后台任务权限 resolver
3
+
4
+ 职责:
5
+ 后台/批处理场景中拒绝 ask 请求并记录审计日志。
6
+
7
+ 链路位置:
8
+ ToolExecutorPipeline ask 分支调用,避免任务挂起等待人工输入。
9
+
10
+ 当前裁剪范围:
11
+ 仅记录基础 warning 日志,不接入统一审计事件总线。
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+
18
+
19
+ class BackgroundPermissionResolver:
20
+ def __init__(self, logger: logging.Logger | None = None) -> None:
21
+ self._log = logger or logging.getLogger(__name__)
22
+
23
+ async def __call__(self, tool_name: str, ask_prompt: str | None) -> bool:
24
+ self._log.warning(
25
+ "background_permission_auto_deny tool=%s prompt=%s",
26
+ tool_name,
27
+ ask_prompt,
28
+ )
29
+ return False
30
+
31
+
32
+ __all__ = ["BackgroundPermissionResolver"]
@@ -0,0 +1,20 @@
1
+ """
2
+ tool_runtime/resolvers/base.py — PermissionResolver 协议定义
3
+
4
+ 职责:
5
+ 提供 ask 分支统一回调协议,隔离 ToolExecutorPipeline 与 UI/会话实现细节。
6
+
7
+ 链路位置:
8
+ ToolExecutorPipeline 在 auth.behavior=="ask" 时 await PermissionResolver。
9
+
10
+ 当前裁剪范围:
11
+ 仅定义类型协议,不引入具体会话/交互实现。
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from collections.abc import Awaitable, Callable
17
+
18
+ PermissionResolver = Callable[[str, str | None], Awaitable[bool]]
19
+
20
+ __all__ = ["PermissionResolver"]
@@ -0,0 +1,22 @@
1
+ """
2
+ tool_runtime/resolvers/conversation.py — 对话场景权限 resolver
3
+
4
+ 职责:
5
+ 对话(Web/IM)场景下不阻塞当前轮,ask 分支统一返回拒绝。
6
+
7
+ 链路位置:
8
+ ToolExecutorPipeline ask 分支调用;拒绝后由模型在下一轮解释并重试。
9
+
10
+ 当前裁剪范围:
11
+ 仅返回 False,不包含外部通知/消息回传能力。
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+
17
+ class ConversationPermissionResolver:
18
+ async def __call__(self, tool_name: str, ask_prompt: str | None) -> bool:
19
+ return False
20
+
21
+
22
+ __all__ = ["ConversationPermissionResolver"]
@@ -0,0 +1,73 @@
1
+ """
2
+ tool_runtime/resolvers/workflow.py — 工作流场景权限 resolver
3
+
4
+ 职责:
5
+ 为 workflow 节点提供同进程可批准/拒绝的等待点(Future 协调)。
6
+
7
+ 链路位置:
8
+ ToolExecutorPipeline ask 分支 await;审批侧通过 approve/reject 回填。
9
+
10
+ 当前裁剪范围:
11
+ 仅支持基于 tool_name 的单键等待,不含跨会话持久化审批。
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import asyncio
17
+ from collections import deque
18
+
19
+
20
+ class WorkflowPermissionResolver:
21
+ def __init__(self, *, timeout: float = 60.0) -> None:
22
+ self._pending: dict[str, deque[asyncio.Future[bool]]] = {}
23
+ self._timeout = timeout
24
+ self._lock = asyncio.Lock()
25
+
26
+ async def __call__(self, tool_name: str, ask_prompt: str | None) -> bool:
27
+ _ = ask_prompt
28
+ loop = asyncio.get_running_loop()
29
+ future: asyncio.Future[bool] = loop.create_future()
30
+ async with self._lock:
31
+ queue = self._pending.setdefault(tool_name, deque())
32
+ queue.append(future)
33
+ try:
34
+ return await asyncio.wait_for(future, timeout=self._timeout)
35
+ except asyncio.TimeoutError:
36
+ return False
37
+ finally:
38
+ async with self._lock:
39
+ queue = self._pending.get(tool_name)
40
+ if queue is not None:
41
+ try:
42
+ queue.remove(future)
43
+ except ValueError:
44
+ pass
45
+ if not queue:
46
+ self._pending.pop(tool_name, None)
47
+
48
+ async def approve(self, tool_name: str) -> bool:
49
+ future = await self._pop_next_pending(tool_name)
50
+ if future and not future.done():
51
+ future.set_result(True)
52
+ return True
53
+ return False
54
+
55
+ async def reject(self, tool_name: str) -> bool:
56
+ future = await self._pop_next_pending(tool_name)
57
+ if future and not future.done():
58
+ future.set_result(False)
59
+ return True
60
+ return False
61
+
62
+ async def _pop_next_pending(self, tool_name: str) -> asyncio.Future[bool] | None:
63
+ async with self._lock:
64
+ queue = self._pending.get(tool_name)
65
+ if not queue:
66
+ return None
67
+ future = queue.popleft()
68
+ if not queue:
69
+ self._pending.pop(tool_name, None)
70
+ return future
71
+
72
+
73
+ __all__ = ["WorkflowPermissionResolver"]
@@ -0,0 +1,132 @@
1
+ """
2
+ runtime/session_store.py — 会话级状态存储
3
+
4
+ 职责:
5
+ AgentSessionStore 是 session 级别的状态对象,生命周期与一次 Agent 会话对齐。
6
+ 它独立于 LangGraph graph state(turn 级)和 ToolExecutionContext(call 级),
7
+ 负责跨 turn 持久化的会话状态:
8
+
9
+ file_read_state — 文件读取记录(为 read-before-write 约束服务)
10
+ file_history — 文件写入/编辑历史
11
+ audit_log — 工具调用审计日志
12
+ permissions_cache — 权限决策缓存(避免重复询问)
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import time
18
+ import uuid
19
+ from dataclasses import dataclass
20
+ from typing import Any
21
+
22
+
23
+ @dataclass
24
+ class FileReadRecord:
25
+ path: str
26
+ tool_call_id: str | None
27
+ ts: float
28
+ line_start: int | None = None
29
+ line_end: int | None = None
30
+
31
+
32
+ @dataclass
33
+ class FileWriteRecord:
34
+ path: str
35
+ operation: str
36
+ tool_call_id: str | None
37
+ ts: float
38
+
39
+
40
+ class AgentSessionStore:
41
+ CONFIGURABLE_KEY = "session_store"
42
+
43
+ def __init__(self, session_id: str | None = None) -> None:
44
+ self.session_id: str = session_id or str(uuid.uuid4())
45
+ self._created_at: float = time.time()
46
+ self._file_read_state: dict[str, FileReadRecord] = {}
47
+ self._file_history: list[FileWriteRecord] = []
48
+ self._audit_log: list[dict[str, Any]] = []
49
+ self._permissions_cache: dict[str, Any] = {}
50
+
51
+ def record_file_read(
52
+ self,
53
+ path: str,
54
+ *,
55
+ tool_call_id: str | None = None,
56
+ ts: float | None = None,
57
+ line_start: int | None = None,
58
+ line_end: int | None = None,
59
+ ) -> None:
60
+ self._file_read_state[path] = FileReadRecord(
61
+ path=path,
62
+ tool_call_id=tool_call_id,
63
+ ts=ts if ts is not None else time.time(),
64
+ line_start=line_start,
65
+ line_end=line_end,
66
+ )
67
+
68
+ def has_been_read(self, path: str) -> bool:
69
+ return path in self._file_read_state
70
+
71
+ def get_file_read_record(self, path: str) -> FileReadRecord | None:
72
+ return self._file_read_state.get(path)
73
+
74
+ def get_all_read_paths(self) -> list[str]:
75
+ return list(self._file_read_state.keys())
76
+
77
+ def record_file_write(
78
+ self,
79
+ path: str,
80
+ operation: str,
81
+ *,
82
+ tool_call_id: str | None = None,
83
+ ts: float | None = None,
84
+ ) -> None:
85
+ self._file_history.append(
86
+ FileWriteRecord(
87
+ path=path,
88
+ operation=operation,
89
+ tool_call_id=tool_call_id,
90
+ ts=ts if ts is not None else time.time(),
91
+ )
92
+ )
93
+
94
+ def get_file_history(self) -> list[FileWriteRecord]:
95
+ return list(self._file_history)
96
+
97
+ def append_audit(self, record: dict[str, Any]) -> None:
98
+ self._audit_log.append(record)
99
+
100
+ def get_audit_log(self) -> list[dict[str, Any]]:
101
+ return list(self._audit_log)
102
+
103
+ def export_audit_log(self) -> list[dict[str, Any]]:
104
+ """语义化导出出口;等价于 get_audit_log()."""
105
+ return self.get_audit_log()
106
+
107
+ def cache_permission(self, key: str, decision: Any) -> None:
108
+ self._permissions_cache[key] = decision
109
+
110
+ def get_cached_permission(self, key: str) -> Any | None:
111
+ return self._permissions_cache.get(key)
112
+
113
+ @property
114
+ def created_at(self) -> float:
115
+ return self._created_at
116
+
117
+ def summary(self) -> dict[str, Any]:
118
+ return {
119
+ "session_id": self.session_id,
120
+ "created_at": self._created_at,
121
+ "files_read": len(self._file_read_state),
122
+ "file_writes": len(self._file_history),
123
+ "audit_entries": len(self._audit_log),
124
+ "cached_permissions": len(self._permissions_cache),
125
+ }
126
+
127
+ def __repr__(self) -> str:
128
+ return (
129
+ f"<AgentSessionStore session_id={self.session_id!r} "
130
+ f"files_read={len(self._file_read_state)} "
131
+ f"audit_entries={len(self._audit_log)}>"
132
+ )
@@ -0,0 +1,294 @@
1
+ """
2
+ runtime 框架端到端冒烟测试(smoke_test_runtime.py)
3
+
4
+ 用途:
5
+ 快速验证 runtime 框架的完整执行路径,无需完整环境,
6
+ 可直接 `python smoke_test_runtime.py` 运行(不依赖 pytest)。
7
+
8
+ 覆盖场景:
9
+ 1. 正常工具流转(EchoTool 10 步 pipeline)
10
+ 2. adapter.envelope_to_tool_output 输出格式
11
+ 3. validate_input 失败 → error envelope 短路
12
+ 4. check_permissions deny → blocked envelope 短路
13
+ 5. ToolOutputManager 超限截断 + overflow_file
14
+ 6. registry + DefaultPolicyEngine 无路径字段放行
15
+ 7. StateBridge record_read / has_been_read / append_audit
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import os
21
+ import sys
22
+ import tempfile
23
+ import types
24
+ import pathlib
25
+ import importlib.util
26
+
27
+ # ---------------------------------------------------------------------------
28
+ # 绕过顶层 langchain_agentx/__init__.py(该文件依赖尚未安装的包)
29
+ # 手动注册父包 stub,直接加载 runtime 子模块
30
+ # ---------------------------------------------------------------------------
31
+
32
+ def _stub_pkg(name: str, path_hint: str) -> None:
33
+ m = types.ModuleType(name)
34
+ m.__path__ = [path_hint]
35
+ m.__package__ = name
36
+ sys.modules[name] = m
37
+
38
+
39
+ # 本文件位于 langchain_agentx/tool_runtime/,parents[2] = 仓库根(含 langchain_agentx 包目录)
40
+ _ROOT = pathlib.Path(__file__).resolve().parents[2]
41
+
42
+ _stub_pkg("langchain_agentx", str(_ROOT / "langchain_agentx"))
43
+ _stub_pkg("langchain_agentx.tool_runtime", str(_ROOT / "langchain_agentx/tool_runtime"))
44
+
45
+
46
+ def _load_module(sub: str) -> None:
47
+ mod_name = f"langchain_agentx.tool_runtime.{sub}"
48
+ path = _ROOT / "langchain_agentx/tool_runtime" / f"{sub}.py"
49
+ spec = importlib.util.spec_from_file_location(mod_name, path)
50
+ mod = importlib.util.module_from_spec(spec)
51
+ sys.modules[mod_name] = mod
52
+ spec.loader.exec_module(mod)
53
+
54
+
55
+ for _sub in ["models", "errors", "base", "policy", "state_bridge", "pipeline", "adapter", "registry"]:
56
+ _load_module(_sub)
57
+
58
+ # ---------------------------------------------------------------------------
59
+ # 导入
60
+ # ---------------------------------------------------------------------------
61
+
62
+ from langchain_agentx.tool_runtime.models import ToolExecutionContext, ToolResultEnvelope, ValidationResult, AuthorizationDecision # noqa: E402
63
+ from langchain_agentx.tool_runtime.base import RuntimeTool # noqa: E402
64
+ from langchain_agentx.tool_runtime.pipeline import ToolExecutorPipeline # noqa: E402
65
+ from langchain_agentx.tool_runtime.adapter import LangChainAdapter # noqa: E402
66
+ from langchain_agentx.tool_runtime.registry import RuntimeToolRegistry # noqa: E402
67
+ from langchain_agentx.tool_runtime.policy import ToolPolicyConfig, DefaultPolicyEngine # noqa: E402
68
+ from langchain_agentx.tool_runtime.state_bridge import ToolStateBridge # noqa: E402
69
+ from pydantic import BaseModel # noqa: E402
70
+
71
+ # ---------------------------------------------------------------------------
72
+ # 最小工具定义(所有测试共用)
73
+ # ---------------------------------------------------------------------------
74
+
75
+ class EchoInput(BaseModel):
76
+ message: str
77
+
78
+
79
+ class EchoTool(RuntimeTool):
80
+ name = "echo"
81
+ description = "回显输入消息"
82
+ input_model = EchoInput
83
+ is_read_only = True
84
+
85
+ def invoke(self, data, ctx):
86
+ return data["message"]
87
+
88
+ def present(self, data, result, ctx):
89
+ return ToolResultEnvelope(
90
+ status="ok",
91
+ tool_name=self.name,
92
+ summary=f"Echo: {result}",
93
+ payload=result,
94
+ )
95
+
96
+
97
+ # ---------------------------------------------------------------------------
98
+ # 辅助
99
+ # ---------------------------------------------------------------------------
100
+
101
+ def _make_ctx(state=None) -> ToolExecutionContext:
102
+ return ToolExecutionContext(
103
+ tool_name="echo",
104
+ tool_call_id="call_001",
105
+ input_args={"message": "hello"},
106
+ state=state if state is not None else {},
107
+ )
108
+
109
+
110
+ def _pass(label: str) -> None:
111
+ print(f" ✅ {label}")
112
+
113
+
114
+ def _section(title: str) -> None:
115
+ print(f"\n=== {title} ===")
116
+
117
+
118
+ # ---------------------------------------------------------------------------
119
+ # Smoke tests
120
+ # ---------------------------------------------------------------------------
121
+
122
+ def test_normal_flow() -> None:
123
+ _section("Test 1: 正常工具流转")
124
+ pipeline = ToolExecutorPipeline()
125
+ ctx = _make_ctx()
126
+ env = pipeline.run(tool=EchoTool(), raw_input={"message": "hello runtime"}, ctx=ctx)
127
+ assert env.status == "ok", f"got {env.status}"
128
+ assert "hello runtime" in env.summary
129
+ _pass(f"status=ok, summary={env.summary!r}")
130
+
131
+
132
+ def test_adapter_output() -> None:
133
+ _section("Test 2: adapter.envelope_to_tool_output")
134
+ pipeline = ToolExecutorPipeline()
135
+ adapter = LangChainAdapter()
136
+ ctx = _make_ctx()
137
+ env = pipeline.run(tool=EchoTool(), raw_input={"message": "hello"}, ctx=ctx)
138
+ output = adapter.envelope_to_tool_output(env)
139
+ assert "hello" in output
140
+ _pass(f"output={output!r}")
141
+
142
+
143
+ def test_validate_input_short_circuit() -> None:
144
+ _section("Test 3: validate_input 失败 → error 短路")
145
+
146
+ class StrictEcho(EchoTool):
147
+ name = "strict_echo"
148
+ def validate_input(self, data, ctx):
149
+ if len(data["message"]) > 5:
150
+ return ValidationResult(ok=False, message="message too long")
151
+ return ValidationResult(ok=True)
152
+
153
+ pipeline = ToolExecutorPipeline()
154
+ ctx = _make_ctx()
155
+ env = pipeline.run(tool=StrictEcho(), raw_input={"message": "too long message"}, ctx=ctx)
156
+ assert env.status == "error"
157
+ assert "too long" in env.summary
158
+ _pass(f"status=error, summary={env.summary!r}")
159
+
160
+
161
+ def test_check_permissions_blocked() -> None:
162
+ _section("Test 4: check_permissions deny → blocked 短路")
163
+
164
+ class DeniedEcho(EchoTool):
165
+ name = "denied_echo"
166
+ def check_permissions(self, data, ctx):
167
+ return AuthorizationDecision(behavior="deny", message="not allowed here")
168
+
169
+ pipeline = ToolExecutorPipeline()
170
+ ctx = _make_ctx()
171
+ env = pipeline.run(tool=DeniedEcho(), raw_input={"message": "hi"}, ctx=ctx)
172
+ assert env.status == "blocked"
173
+ assert "not allowed" in env.summary
174
+ _pass(f"status=blocked, summary={env.summary!r}")
175
+
176
+
177
+ def test_output_manager_truncation() -> None:
178
+ _section("Test 5: ToolOutputManager 超限截断")
179
+
180
+ class TinyEcho(EchoTool):
181
+ name = "tiny_echo"
182
+ max_result_size_chars = 5
183
+
184
+ pipeline = ToolExecutorPipeline()
185
+ ctx = _make_ctx()
186
+ env = pipeline.run(tool=TinyEcho(), raw_input={"message": "hello runtime 1234"}, ctx=ctx)
187
+ assert env.truncated is True
188
+ assert env.overflow_file is not None
189
+ assert os.path.exists(env.overflow_file)
190
+ _pass(f"truncated=True, overflow_file={env.overflow_file}")
191
+
192
+
193
+ def test_registry_and_policy_no_path_field() -> None:
194
+ _section("Test 6: registry + DefaultPolicyEngine(无路径字段 → allow)")
195
+ tmpdir = tempfile.mkdtemp()
196
+ policy = DefaultPolicyEngine(
197
+ ToolPolicyConfig(read_roots=[tmpdir], write_roots=[tmpdir])
198
+ )
199
+ pipeline = ToolExecutorPipeline()
200
+ adapter = LangChainAdapter()
201
+ registry = RuntimeToolRegistry(pipeline=pipeline, adapter=adapter)
202
+ registry.register(EchoTool(policy=policy))
203
+
204
+ assert "echo" in registry
205
+ lc_tools = registry.to_langchain_tools()
206
+ assert len(lc_tools) == 1
207
+ assert lc_tools[0].name == "echo"
208
+
209
+ ctx = _make_ctx()
210
+ env = pipeline.run(tool=registry.get("echo"), raw_input={"message": "ok"}, ctx=ctx)
211
+ assert env.status == "ok"
212
+ _pass("registry.register + to_langchain_tools OK, policy allow (no path field)")
213
+
214
+
215
+ def test_state_bridge() -> None:
216
+ _section("Test 7: ToolStateBridge record_read / has_been_read / append_audit")
217
+ bridge = ToolStateBridge()
218
+ state = {}
219
+ ctx = ToolExecutionContext(
220
+ tool_name="echo", tool_call_id="c1", input_args={}, state=state
221
+ )
222
+
223
+ _td = pathlib.Path(tempfile.gettempdir())
224
+ foo = str(_td / "langchain_agentx_smoke" / "foo.txt")
225
+ bar = str(_td / "langchain_agentx_smoke" / "bar.txt")
226
+ bridge.record_read(ctx, foo, line_start=1, line_end=10)
227
+ assert bridge.has_been_read(ctx, foo)
228
+ assert not bridge.has_been_read(ctx, bar)
229
+
230
+ bridge.append_audit(ctx, {"tool": "echo", "ts": 9999})
231
+ log = bridge.get_audit_log(ctx)
232
+ assert len(log) == 1
233
+ assert log[0]["tool"] == "echo"
234
+ _pass("record_read / has_been_read / audit OK")
235
+
236
+
237
+ def test_exception_captured_as_error_envelope() -> None:
238
+ _section("Test 8: invoke 抛异常 → pipeline 捕获为 error envelope")
239
+
240
+ class BrokenEcho(EchoTool):
241
+ name = "broken_echo"
242
+ def invoke(self, data, ctx):
243
+ raise ValueError("something went wrong")
244
+
245
+ pipeline = ToolExecutorPipeline()
246
+ ctx = _make_ctx()
247
+ env = pipeline.run(tool=BrokenEcho(), raw_input={"message": "hi"}, ctx=ctx)
248
+ assert env.status == "error"
249
+ assert "went wrong" in env.summary
250
+ assert env.meta is not None and "traceback" in env.meta
251
+ _pass(f"status=error, summary={env.summary!r}, traceback in meta")
252
+
253
+
254
+ def test_schema_parse_failure() -> None:
255
+ _section("Test 9: Pydantic schema 解析失败 → error 短路")
256
+ pipeline = ToolExecutorPipeline()
257
+ ctx = _make_ctx()
258
+ # 传入错误类型(缺少必填 message 字段)
259
+ env = pipeline.run(tool=EchoTool(), raw_input={}, ctx=ctx)
260
+ assert env.status == "error"
261
+ assert "schema" in env.summary.lower() or "message" in env.summary.lower()
262
+ _pass(f"status=error on missing field: {env.summary!r}")
263
+
264
+
265
+ # ---------------------------------------------------------------------------
266
+ # Main
267
+ # ---------------------------------------------------------------------------
268
+
269
+ if __name__ == "__main__":
270
+ tests = [
271
+ test_normal_flow,
272
+ test_adapter_output,
273
+ test_validate_input_short_circuit,
274
+ test_check_permissions_blocked,
275
+ test_output_manager_truncation,
276
+ test_registry_and_policy_no_path_field,
277
+ test_state_bridge,
278
+ test_exception_captured_as_error_envelope,
279
+ test_schema_parse_failure,
280
+ ]
281
+ failed = []
282
+ for fn in tests:
283
+ try:
284
+ fn()
285
+ except Exception as exc:
286
+ print(f" ❌ FAILED: {exc}")
287
+ failed.append(fn.__name__)
288
+
289
+ print()
290
+ if failed:
291
+ print(f"❌ {len(failed)} test(s) failed: {failed}")
292
+ sys.exit(1)
293
+ else:
294
+ print(f"✅ All {len(tests)} smoke tests passed")