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,77 @@
1
+ """utils/temp_paths.py — 跨平台系统临时目录路径(测试与示例用)。
2
+
3
+ 职责:
4
+ 提供「与 OS 一致的可写临时区」下的 `Path` / `str`,避免在单测与演示中写死 POSIX ``/tmp``,
5
+ 以便在 Windows(通常为 ``%TEMP%``)与 Linux/macOS 上同一套断言与逻辑路径。
6
+ 核心逻辑在 SystemTempLocations;模块级函数为薄封装。
7
+
8
+ 链路位置:
9
+ 单测、examples 中仅需「绝对路径字符串」且不便注入 ``tmp_path`` 时使用。
10
+
11
+ 当前裁剪范围:
12
+ 不负责创建目录或清理文件;调用方按需 ``mkdir`` / 使用 ``tmp_path``。
13
+ 生产业务路径仍应来自 ``AgentWorkspaceConfig`` 等配置层,而非本模块。
14
+
15
+ 工程化约定(与 Claude Code 等 Node 工程对齐思路):
16
+ - **命名空间**:首段路径分量使用项目前缀(如 ``langchain_agentx_tool_runtime_test/...``),等价于
17
+ CC 中 ``join(tmpdir(), 'claude', ...)`` 在系统临时目录下再分一层,减少与其它软件冲突。
18
+ - **环境变量**:``tempfile.gettempdir()`` 会尊重常见宿主配置(如 Unix ``TMPDIR``、Windows ``TEMP``/``TMP``),
19
+ 不在此模块重复实现;CI 若需固定沙箱目录,应通过设置上述环境变量而非改库代码。
20
+ - **非安全边界**:本模块**不做** CC ``memdir/paths`` 类对 UNC、盘符根、过短路径的拒绝策略;若路径来自
21
+ 不可信输入,须在策略层校验,勿仅依赖本工具返回的路径。
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import tempfile
27
+ from pathlib import Path
28
+
29
+
30
+ class SystemTempLocations:
31
+ """封装 ``tempfile.gettempdir()`` 下的品牌子路径拼接。"""
32
+
33
+ def system_temp_dir(self) -> Path:
34
+ """返回当前进程可见的系统临时目录(``tempfile.gettempdir()``)。"""
35
+ return Path(tempfile.gettempdir())
36
+
37
+ def under_system_temp(self, *relative_parts: str) -> Path:
38
+ """``system_temp_dir()`` 下拼接相对路径(至少一段,避免误用根)。"""
39
+ if not relative_parts:
40
+ raise ValueError("under_system_temp requires at least one path component")
41
+ return self.system_temp_dir().joinpath(*relative_parts)
42
+
43
+ def under_system_temp_str(self, *relative_parts: str) -> str:
44
+ """与 :meth:`under_system_temp` 相同,返回 ``str``(便于存入 session_store 等)。"""
45
+ return str(self.under_system_temp(*relative_parts))
46
+
47
+
48
+ _default_locations = SystemTempLocations()
49
+
50
+
51
+ def default_system_temp_locations() -> SystemTempLocations:
52
+ """返回进程内共享的默认实例。"""
53
+ return _default_locations
54
+
55
+
56
+ def system_temp_dir() -> Path:
57
+ """返回当前进程可见的系统临时目录(``tempfile.gettempdir()``)。"""
58
+ return _default_locations.system_temp_dir()
59
+
60
+
61
+ def under_system_temp(*relative_parts: str) -> Path:
62
+ """``system_temp_dir()`` 下拼接相对路径(至少一段,避免误用根)。"""
63
+ return _default_locations.under_system_temp(*relative_parts)
64
+
65
+
66
+ def under_system_temp_str(*relative_parts: str) -> str:
67
+ """与 :func:`under_system_temp` 相同,返回 ``str``(便于存入 session_store 等)。"""
68
+ return _default_locations.under_system_temp_str(*relative_parts)
69
+
70
+
71
+ __all__ = [
72
+ "SystemTempLocations",
73
+ "default_system_temp_locations",
74
+ "system_temp_dir",
75
+ "under_system_temp",
76
+ "under_system_temp_str",
77
+ ]
@@ -0,0 +1,25 @@
1
+ """
2
+ utils/unc_path.py — UNC / 双斜线网径前缀检测
3
+
4
+ 职责:为 Grep/Glob(及未来 Read 等)的 validate_input 提供与 CC 一致的 stat 前短路判断,
5
+ 避免对 UNC 路径做本地 stat 触发不必要的远程/凭据面(CC GrepTool/GlobTool validateInput)。
6
+
7
+ CC 对照:src/tools/GrepTool/GrepTool.ts、src/tools/GlobTool/GlobTool.ts(absolutePath.startsWith('\\\\') || startsWith('//'))。
8
+
9
+ 当前裁剪:仅前缀判定;不解析 \\\\?\\UNC\\ 等扩展设备语法(与 CC 当前检查范围一致)。
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+
15
+ def is_unc_path_skip_local_stat(path: str) -> bool:
16
+ """
17
+ 若路径在展开后仍表现为 UNC / SMB 风格根前缀,则不在本地做 exists/stat 校验。
18
+
19
+ - 反斜杠 UNC:`\\\\server\\share\\...`
20
+ - 正斜杠根:`//server/share/...`(含部分 POSIX 下 ``//`` 绝对路径形态)
21
+ """
22
+ s = path.strip()
23
+ if not s:
24
+ return False
25
+ return s.startswith("\\\\") or s.startswith("//")
@@ -0,0 +1,51 @@
1
+ """
2
+ utils/win_reserved_paths.py — Windows 保留设备名路径检测
3
+
4
+ 职责:判断路径中是否出现 Windows 保留名(CON/PRN/AUX/NUL/COM1-9/LPT1-9),
5
+ 供 Read 等工具在本地 I/O 前拒绝误读设备(与 Unix /dev 阻断互补)。
6
+
7
+ CC 对照:FileReadTool 仍以 Unix 设备表为主;本工程在 win32 上补一层路径段检测。
8
+
9
+ 当前裁剪:仅按 Windows 规则拆分路径段并检查各段 stem(与 COM1.txt 等扩展名规则一致);
10
+ 非 win32 恒为 False。拆分固定使用 PureWindowsPath,避免在 Linux CI 上 Path 仍为
11
+ PosixPath 时反斜杠路径被误解析(单段 + stem 误判)。
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import sys
17
+ from pathlib import PureWindowsPath
18
+
19
+ _WIN_RESERVED_STEMS: frozenset[str] = frozenset(
20
+ {"CON", "PRN", "AUX", "NUL", *(f"COM{i}" for i in range(1, 10)), *(f"LPT{i}" for i in range(1, 10))}
21
+ )
22
+
23
+
24
+ def path_uses_windows_reserved_name(path: str) -> bool:
25
+ """
26
+ 若当前平台为 Windows,且路径任一段(按 Windows 路径规则拆分)的 stem 为保留设备名,则返回 True。
27
+
28
+ 例如 ``C:\\temp\\NUL``、``C:\\share\\COM1.dat``、末段为 ``CON`` 的路径。
29
+ """
30
+ if sys.platform != "win32":
31
+ return False
32
+ if not path or not path.strip():
33
+ return False
34
+ try:
35
+ parts = PureWindowsPath(path).parts
36
+ except (ValueError, OSError):
37
+ return False
38
+ for part in parts:
39
+ seg = part.rstrip("/\\")
40
+ if not seg:
41
+ continue
42
+ # 盘符段如 ``C:`` 无 stem 意义
43
+ if len(seg) == 2 and seg[1] == ":":
44
+ continue
45
+ stem = PureWindowsPath(seg).stem.upper()
46
+ if stem in _WIN_RESERVED_STEMS:
47
+ return True
48
+ return False
49
+
50
+
51
+ __all__ = ["path_uses_windows_reserved_name"]
@@ -0,0 +1,7 @@
1
+ """Workflow 容器导出。"""
2
+
3
+ from .base import BaseWorkflow
4
+ from .batch import BatchWorkflow
5
+ from .dag import DagWorkflow
6
+
7
+ __all__ = ["BaseWorkflow", "DagWorkflow", "BatchWorkflow"]
@@ -0,0 +1,97 @@
1
+ """
2
+ workflow/base.py — Workflow 编排基类。
3
+
4
+ 职责:
5
+ 封装任务级容器生命周期(start/invoke/end)、并发控制与单次 agent invoke 入口。
6
+
7
+ 链路位置:
8
+ 作为场景化 Workflow 的父类,复用 Agent Loop 并对齐 LoopContainer 生命周期协议。
9
+
10
+ 当前裁剪范围:
11
+ 仅提供 BaseWorkflow 最小契约;Dag/Batch 骨架在独立文件占位。
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import asyncio
17
+ import inspect
18
+ from abc import ABC, abstractmethod
19
+ from typing import Any, Callable, Literal
20
+ from uuid import uuid4
21
+
22
+ from ..loop.hook.types import HookContext, HookEvent
23
+ from ..memory.memdir.agent_memory import drain_pending_agent_memory_extraction
24
+
25
+
26
+ class BaseWorkflow(ABC):
27
+ """场景编排基类。"""
28
+
29
+ container_type: str = "workflow_task"
30
+
31
+ def __init__(
32
+ self,
33
+ graph_factory: Callable[..., Any],
34
+ hook_engine: Any,
35
+ max_concurrency: int = 4,
36
+ error_policy: Literal["fail_fast", "best_effort"] = "best_effort",
37
+ ) -> None:
38
+ self._graph_factory = graph_factory
39
+ self._hook_engine = hook_engine
40
+ self._semaphore = asyncio.Semaphore(max_concurrency)
41
+ self._error_policy = error_policy
42
+ self._workflow_id = uuid4().hex[:8]
43
+
44
+ async def __aenter__(self) -> "BaseWorkflow":
45
+ await self._on_workflow_start()
46
+ return self
47
+
48
+ async def __aexit__(self, *_) -> None:
49
+ await self._on_workflow_end()
50
+
51
+ async def run(self, **kwargs: Any) -> Any:
52
+ async with self:
53
+ return await self._execute(**kwargs)
54
+
55
+ @abstractmethod
56
+ async def _execute(self, **kwargs: Any) -> Any:
57
+ """子类实现具体编排逻辑。"""
58
+
59
+ async def _invoke_agent(
60
+ self,
61
+ messages: list[Any],
62
+ task_key: str,
63
+ parent_session_id: str | None = None,
64
+ ) -> dict[str, Any]:
65
+ session_id = f"{self._workflow_id}-{task_key}"
66
+ async with self._semaphore:
67
+ graph = self._graph_factory(
68
+ session_id=session_id,
69
+ parent_conversation_session_id=parent_session_id,
70
+ is_subagent=parent_session_id is not None,
71
+ container_type=self.container_type,
72
+ )
73
+ result = await graph.ainvoke({"messages": messages})
74
+ return dict(result) if isinstance(result, dict) else {"result": result}
75
+
76
+ async def _on_workflow_start(self) -> None:
77
+ await self._execute_hook(HookEvent.SESSION_START)
78
+
79
+ async def _on_workflow_end(self) -> None:
80
+ await drain_pending_agent_memory_extraction(timeout_ms=60_000)
81
+ await self._execute_hook(HookEvent.SESSION_END)
82
+
83
+ async def _execute_hook(self, event: HookEvent) -> None:
84
+ ctx = HookContext(event=event, state={}, session_id=self._workflow_id)
85
+ execute_async = getattr(self._hook_engine, "execute_async", None)
86
+ if callable(execute_async):
87
+ async_out = execute_async(ctx)
88
+ if inspect.isawaitable(async_out):
89
+ await async_out
90
+ return
91
+ execute = getattr(self._hook_engine, "execute")
92
+ out = execute(ctx)
93
+ if inspect.isawaitable(out):
94
+ await out
95
+
96
+
97
+ __all__ = ["BaseWorkflow"]
@@ -0,0 +1,55 @@
1
+ """
2
+ workflow/batch.py — Batch 编排骨架。
3
+
4
+ 职责:
5
+ 为同构批处理任务提供标准执行模板(items -> process x N -> merge),
6
+ 子类实现 _process_item() 和 merge_results() 即可。
7
+
8
+ 链路位置:
9
+ 继承 BaseWorkflow,为批处理型业务 Workflow 提供标准入口。
10
+
11
+ 当前裁剪范围:
12
+ 并发由 BaseWorkflow._semaphore 控制;错误策略由 error_policy 决定。
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import asyncio
18
+ from abc import abstractmethod
19
+ from typing import Any
20
+
21
+ from .base import BaseWorkflow
22
+
23
+
24
+ class BatchWorkflow(BaseWorkflow):
25
+ """批处理编排骨架。"""
26
+
27
+ async def process_all(self, items: list[Any]) -> Any:
28
+ """
29
+ 并发处理所有 items,结果交给 merge_results() 聚合。
30
+
31
+ best_effort:单个 item 失败记为 None,其余继续。
32
+ fail_fast:任意 item 失败立即抛异常。
33
+ """
34
+ tasks = [self._process_item(item, idx) for idx, item in enumerate(items)]
35
+ if self._error_policy == "fail_fast":
36
+ results = await asyncio.gather(*tasks)
37
+ else:
38
+ raw_results = await asyncio.gather(*tasks, return_exceptions=True)
39
+ results = [None if isinstance(item, BaseException) else item for item in raw_results]
40
+ return self.merge_results(results)
41
+
42
+ @abstractmethod
43
+ async def _process_item(self, item: Any, index: int) -> Any:
44
+ """子类实现单个 item 的处理逻辑。"""
45
+
46
+ @abstractmethod
47
+ def merge_results(self, results: list[Any]) -> Any:
48
+ """子类实现多个 item 结果的聚合逻辑。"""
49
+
50
+ @abstractmethod
51
+ async def _execute(self, **kwargs: Any) -> Any:
52
+ """子类实现入口,通常直接调用 process_all(items)。"""
53
+
54
+
55
+ __all__ = ["BatchWorkflow"]
@@ -0,0 +1,54 @@
1
+ """
2
+ workflow/dag.py — DAG 编排骨架。
3
+
4
+ 职责:
5
+ 为有向无环图编排场景提供步骤辅助入口(_run_step),
6
+ 子类通过普通 Python 代码声明步骤依赖与并发关系。
7
+
8
+ 链路位置:
9
+ 继承 BaseWorkflow,为具体业务 Workflow(如 WikiWorkflow)提供结构化骨架。
10
+
11
+ 当前裁剪范围:
12
+ 不做自动 DAG 拓扑调度,步骤顺序与并发由子类 _execute() 控制。
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from abc import abstractmethod
18
+ from typing import Any
19
+
20
+ from .base import BaseWorkflow
21
+
22
+
23
+ class DagWorkflow(BaseWorkflow):
24
+ """
25
+ DAG 编排骨架。
26
+
27
+ 子类通过 _run_step() 与 Python 控制流表达依赖与并发。
28
+ """
29
+
30
+ async def _run_step(
31
+ self,
32
+ step_name: str,
33
+ messages: list[Any],
34
+ *,
35
+ parent_session_id: str | None = None,
36
+ ) -> dict[str, Any]:
37
+ """
38
+ 执行单个 DAG 步骤。
39
+
40
+ step_name 作为 task_key,最终生成 session_id = f"{workflow_id}-{step_name}",
41
+ trace 可按步骤名检索执行链路。
42
+ """
43
+ return await self._invoke_agent(
44
+ messages=messages,
45
+ task_key=step_name,
46
+ parent_session_id=parent_session_id,
47
+ )
48
+
49
+ @abstractmethod
50
+ async def _execute(self, **kwargs: Any) -> Any:
51
+ """子类实现具体步骤编排逻辑。"""
52
+
53
+
54
+ __all__ = ["DagWorkflow"]
@@ -0,0 +1,13 @@
1
+ from .config import AgentWorkspaceConfig
2
+ from .path_key_normalizer import PathKeyNormalizer, normalize_path_key
3
+ from .resolver import AgentWorkspaceResolver, resolve_agent_workspace_config
4
+ from .validators import AgentWorkspacePathValidator
5
+
6
+ __all__ = [
7
+ "AgentWorkspaceConfig",
8
+ "AgentWorkspaceResolver",
9
+ "AgentWorkspacePathValidator",
10
+ "PathKeyNormalizer",
11
+ "normalize_path_key",
12
+ "resolve_agent_workspace_config",
13
+ ]
@@ -0,0 +1,140 @@
1
+ """
2
+ workspace/config.py — AgentWorkspace 路径配置模型
3
+
4
+ 职责:
5
+ 提供 `workspace_root + agent_home` 的派生目录视图(skills/worktrees/tasks/data/db),
6
+ 作为跨模块目录语义的数据模型。
7
+
8
+ 在整体链路中的位置:
9
+ AgentWorkspaceResolver.resolve()
10
+ -> AgentWorkspaceConfig
11
+ -> 各入口按属性读取目标路径。
12
+
13
+ 当前裁剪范围:
14
+ 仅路径派生,不做校验与副作用创建。
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from dataclasses import dataclass
20
+ from pathlib import Path
21
+ import re
22
+
23
+ from .path_key_normalizer import normalize_path_key
24
+
25
+
26
+ def _sanitize_agent_type(agent_type: str) -> str:
27
+ """安全化 agent_type,避免路径注入;键串先 NFC 与 session_id 同策略。"""
28
+ normalized = normalize_path_key(str(agent_type or "").strip())
29
+ normalized = normalized.replace(":", "-")
30
+ normalized = re.sub(r"[/\\]", "-", normalized)
31
+ normalized = normalized.replace("..", "-")
32
+ normalized = normalized.replace("\x00", "")
33
+ normalized = normalized.strip("-").strip()
34
+ if not normalized:
35
+ raise ValueError(f"agent_type {agent_type!r} sanitizes to empty string")
36
+ return normalized
37
+
38
+
39
+ def _normalize_memory_scope(scope: str) -> str:
40
+ """单路径段 scope(project/user/local 等)做 NFC,并拒绝路径分隔与穿越片段。"""
41
+ s = normalize_path_key(str(scope or "").strip())
42
+ if not s:
43
+ raise ValueError("scope cannot be empty")
44
+ if re.search(r"[/\\]", s) or ".." in s:
45
+ raise ValueError(f"invalid scope for agent_memory_dir: {scope!r}")
46
+ return s
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ class AgentWorkspaceConfig:
51
+ workspace_root: Path
52
+ agent_home: str = ".langchain_agentx"
53
+ plugins_enabled: bool = True
54
+ workspace_trust_accepted: bool = False
55
+
56
+ @property
57
+ def agent_home_dir(self) -> Path:
58
+ return (self.workspace_root / self.agent_home).resolve()
59
+
60
+ @property
61
+ def skills_dir(self) -> Path:
62
+ return self.agent_home_dir / "skills"
63
+
64
+ @property
65
+ def worktrees_dir(self) -> Path:
66
+ return self.agent_home_dir / "worktrees"
67
+
68
+ @property
69
+ def tasks_dir(self) -> Path:
70
+ return self.agent_home_dir / "tasks"
71
+
72
+ @property
73
+ def data_dir(self) -> Path:
74
+ return self.agent_home_dir / "data"
75
+
76
+ @property
77
+ def db_dir(self) -> Path:
78
+ return self.data_dir / "db"
79
+
80
+ @property
81
+ def traces_db_path(self) -> Path:
82
+ return self.db_dir / "traces.db"
83
+
84
+ @property
85
+ def task_runtime_db_path(self) -> Path:
86
+ return self.db_dir / "task_runtime.db"
87
+
88
+ @property
89
+ def evaluation_db_path(self) -> Path:
90
+ return self.db_dir / "evaluation.db"
91
+
92
+ @property
93
+ def memory_dir(self) -> Path:
94
+ return self.agent_home_dir / "memory"
95
+
96
+ @property
97
+ def memory_entrypoint(self) -> Path:
98
+ return self.memory_dir / "MEMORY.md"
99
+
100
+ @property
101
+ def sessions_dir(self) -> Path:
102
+ return self.agent_home_dir / "sessions"
103
+
104
+ @property
105
+ def transcripts_dir(self) -> Path:
106
+ return self.agent_home_dir / "transcripts"
107
+
108
+ @property
109
+ def workspace_config_path(self) -> Path:
110
+ return self.agent_home_dir / "workspace_config.json"
111
+
112
+ @property
113
+ def plugins_dir(self) -> Path:
114
+ return self.agent_home_dir / "plugins"
115
+
116
+ @property
117
+ def plugin_config_path(self) -> Path:
118
+ return self.agent_home_dir / "plugin_config.json"
119
+
120
+ @property
121
+ def plugin_cache_dir(self) -> Path:
122
+ return self.agent_home_dir / "plugin_cache"
123
+
124
+ def session_memory_path(self, session_id: str) -> Path:
125
+ sid = normalize_path_key(str(session_id).strip())
126
+ if not sid:
127
+ raise ValueError("session_id cannot be empty")
128
+ return self.sessions_dir / f"{sid}.md"
129
+
130
+ @property
131
+ def session_memory_config_dir(self) -> Path:
132
+ return self.agent_home_dir / "session-memory" / "config"
133
+
134
+ def agent_memory_dir(self, agent_type: str, scope: str = "project") -> Path:
135
+ sanitized = _sanitize_agent_type(agent_type)
136
+ sc = _normalize_memory_scope(scope)
137
+ return self.memory_dir / "agents" / sanitized / sc
138
+
139
+ def agent_memory_entrypoint(self, agent_type: str, scope: str = "project") -> Path:
140
+ return self.agent_memory_dir(agent_type, scope) / "MEMORY.md"
@@ -0,0 +1,30 @@
1
+ """
2
+ workspace/path_key_normalizer.py — 路径字符串键 NFC 稳定化
3
+
4
+ 职责:对作为持久化键 / 文件名片段的路径相关字符串做 Unicode NFC,与 CC `normalize('NFC')` 同向。
5
+ 在整体链路中的位置:AgentWorkspaceConfig 派生会话文件路径等边界;不在 expand_path 默认链上调用。
6
+ 当前裁剪范围:仅 NFC;不 resolve、不替代 workspace 越界校验(见 validators)。调用方:``session_memory_path``、``_sanitize_agent_type``、``_normalize_memory_scope``(``agent_memory_dir``)。
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import unicodedata
12
+ from dataclasses import dataclass
13
+
14
+
15
+ @dataclass(frozen=True, slots=True)
16
+ class PathKeyNormalizer:
17
+ """无状态键稳定化;多线程只读安全。"""
18
+
19
+ form: str = "NFC"
20
+
21
+ def normalize(self, path: str) -> str:
22
+ return unicodedata.normalize(self.form, path)
23
+
24
+
25
+ _DEFAULT_NORMALIZER = PathKeyNormalizer()
26
+
27
+
28
+ def normalize_path_key(path: str) -> str:
29
+ """模块级薄封装:会话 id、未来其它「键串」边界的默认 NFC。"""
30
+ return _DEFAULT_NORMALIZER.normalize(path)
@@ -0,0 +1,74 @@
1
+ """
2
+ workspace/resolver.py — AgentWorkspace 配置解析协作者
3
+
4
+ 职责:
5
+ 按统一优先级解析 workspace_root 与 agent_home,并产出
6
+ AgentWorkspaceConfig,作为全局目录语义单一来源。
7
+
8
+ 在整体链路中的位置:
9
+ 各入口(tool_runtime / task_runtime / loop / observability)
10
+ -> resolve_agent_workspace_config()
11
+ -> AgentWorkspaceResolver.resolve()
12
+
13
+ 当前裁剪范围:
14
+ 解析与校验;不直接做业务目录写入。
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import os
20
+ from pathlib import Path
21
+
22
+ from .config import AgentWorkspaceConfig
23
+ from .validators import AgentWorkspacePathValidator
24
+
25
+ DEFAULT_AGENT_HOME = ".langchain_agentx"
26
+ ENV_AGENT_HOME = "LANGCHAIN_AGENTX_AGENT_HOME"
27
+ ENV_WORKSPACE_ROOT = "LANGCHAIN_AGENTX_WORKSPACE_ROOT"
28
+
29
+
30
+ class AgentWorkspaceResolver:
31
+ """Agent workspace 配置解析器。"""
32
+
33
+ def __init__(
34
+ self,
35
+ *,
36
+ default_agent_home: str = DEFAULT_AGENT_HOME,
37
+ env_agent_home: str = ENV_AGENT_HOME,
38
+ env_workspace_root: str = ENV_WORKSPACE_ROOT,
39
+ path_validator: AgentWorkspacePathValidator | None = None,
40
+ ) -> None:
41
+ self._default_agent_home = default_agent_home
42
+ self._env_agent_home = env_agent_home
43
+ self._env_workspace_root = env_workspace_root
44
+ self._path_validator = path_validator or AgentWorkspacePathValidator()
45
+
46
+ def resolve(
47
+ self,
48
+ *,
49
+ workspace_root: str | Path | None = None,
50
+ agent_home: str | None = None,
51
+ ) -> AgentWorkspaceConfig:
52
+ root_raw = workspace_root or os.getenv(self._env_workspace_root) or os.getcwd()
53
+ root = Path(root_raw).expanduser().resolve()
54
+ home_raw = (
55
+ agent_home
56
+ if agent_home is not None
57
+ else os.getenv(self._env_agent_home, self._default_agent_home)
58
+ )
59
+ home = self._path_validator.validate_agent_home(home_raw)
60
+ cfg = AgentWorkspaceConfig(workspace_root=root, agent_home=home)
61
+ self._path_validator.ensure_within_workspace(cfg.workspace_root, cfg.agent_home_dir)
62
+ return cfg
63
+
64
+
65
+ _DEFAULT_RESOLVER = AgentWorkspaceResolver()
66
+
67
+
68
+ def resolve_agent_workspace_config(
69
+ *,
70
+ workspace_root: str | Path | None = None,
71
+ agent_home: str | None = None,
72
+ ) -> AgentWorkspaceConfig:
73
+ """兼容入口:调用默认 AgentWorkspaceResolver。"""
74
+ return _DEFAULT_RESOLVER.resolve(workspace_root=workspace_root, agent_home=agent_home)
@@ -0,0 +1,41 @@
1
+ """
2
+ workspace/validators.py — AgentWorkspace 路径校验协作者
3
+
4
+ 职责:
5
+ 统一封装 workspace 目录相关校验规则(agent_home 合法性、越界检查),
6
+ 作为 resolver 的纯校验依赖,避免调用方散落 if/else 逻辑。
7
+
8
+ 在整体链路中的位置:
9
+ AgentWorkspaceResolver.resolve()
10
+ -> AgentWorkspacePathValidator.validate_agent_home()
11
+ -> AgentWorkspacePathValidator.ensure_within_workspace()
12
+
13
+ 当前裁剪范围:
14
+ 仅覆盖路径规范与越界校验;不承载目录创建与业务策略。
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from pathlib import Path
20
+
21
+
22
+ class AgentWorkspacePathValidator:
23
+ """Agent workspace 路径规则校验器。"""
24
+
25
+ def validate_agent_home(self, agent_home: str) -> str:
26
+ text = (agent_home or "").strip()
27
+ if not text:
28
+ raise ValueError("agent_home cannot be empty")
29
+ path = Path(text)
30
+ if path.is_absolute():
31
+ raise ValueError("agent_home must be a relative path")
32
+ if ".." in path.parts:
33
+ raise ValueError("agent_home cannot contain '..'")
34
+ return text
35
+
36
+ def ensure_within_workspace(self, workspace_root: Path, candidate: Path) -> Path:
37
+ base = workspace_root.resolve()
38
+ resolved = candidate.resolve()
39
+ if not str(resolved).startswith(str(base)):
40
+ raise ValueError("Resolved path escapes workspace_root")
41
+ return resolved