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,169 @@
1
+ """
2
+ trace_cleanup/scheduler.py — TraceCleanupScheduler
3
+
4
+ 职责:
5
+ 定时触发 trace retention 清理(通过 registry 构造 TraceCleanupTask 并 await run)。
6
+
7
+ 链路位置:
8
+ 由上层在 **已运行的 asyncio 事件循环** 内 ``asyncio.create_task(scheduler.start())`` 启动;
9
+ 退出前须 ``scheduler.stop()`` 并 cancel 该 Task(OB-D10 §3.3)。
10
+
11
+ 当前裁剪范围:
12
+ 不修改 BackgroundAiTaskScheduler 基类;可选 **非阻塞文件锁** + **marker 冷却**(多进程节流)。
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import asyncio
18
+ import logging
19
+ import time
20
+ from pathlib import Path
21
+ from typing import Any, IO
22
+
23
+ import portalocker
24
+ from portalocker.exceptions import BaseLockException
25
+
26
+ from langchain_agentx.observability.evaluation.store import EvaluationStore
27
+ from langchain_agentx.observability.trace.sqlite_store import SqliteTraceStore
28
+
29
+ from ..ai_analysis.registry import BackgroundAiTaskRegistry
30
+ from ..ai_analysis.scheduler import BackgroundAiTaskScheduler
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+ _NO_LOCK = object()
35
+
36
+
37
+ class TraceCleanupScheduler(BackgroundAiTaskScheduler):
38
+ """覆盖 ``_poll_once``:按配置触发 ``trace.cleanup`` 任务。"""
39
+
40
+ def __init__(
41
+ self,
42
+ trace_store: SqliteTraceStore,
43
+ evaluation_store: EvaluationStore,
44
+ registry: BackgroundAiTaskRegistry,
45
+ *,
46
+ poll_interval: float = 86400.0,
47
+ cleanup_config: dict[str, Any] | None = None,
48
+ enable_file_lock: bool = True,
49
+ lock_path: Path | str | None = None,
50
+ marker_path: Path | str | None = None,
51
+ marker_cooldown_seconds: float | None = None,
52
+ task_factory_kwargs: dict[str, Any] | None = None,
53
+ ) -> None:
54
+ """
55
+ Args:
56
+ trace_store / evaluation_store: 注入 Store(测试须可传 tmp_path)。
57
+ registry: 须已注册 ``trace.cleanup``(通常随 ai_analysis 包 import 侧载)。
58
+ poll_interval: ``start()`` 循环中 ``asyncio.sleep`` 间隔(秒)。
59
+ cleanup_config: 传入 ``TraceCleanupTask.run`` 的 kwargs(如 failed_days / normal_days)。
60
+ enable_file_lock: 为 True 时用 ``portalocker`` 非阻塞独占锁,避免多进程同时跑 DELETE。
61
+ lock_path / marker_path: 默认与 ``trace_store.db_path`` 同目录派生文件名。
62
+ marker_cooldown_seconds: 非 None 时,marker 存在且距上次成功清理不足该秒数则跳过本轮(多进程节流);**None** 表示不按 marker 跳过(§4.1 Q5 幂等每轮)。
63
+ task_factory_kwargs: 除 ``trace_store`` / ``evaluation_store`` 外传给 ``TraceCleanupTask`` 的 ``graph_factory`` / ``hook_engine`` 等。
64
+ """
65
+ db = Path(trace_store.db_path)
66
+ self._trace_store = trace_store
67
+ self._eval_store = evaluation_store
68
+ merged: dict[str, Any] = {
69
+ "failed_days": 180,
70
+ "normal_days": 30,
71
+ "cleanup_diagnostic_reports": False,
72
+ }
73
+ if cleanup_config:
74
+ merged.update(cleanup_config)
75
+ self._cleanup_config = merged
76
+ self._enable_file_lock = bool(enable_file_lock)
77
+ self._lock_path = Path(lock_path) if lock_path is not None else db.with_suffix(".cleanup.lock")
78
+ self._marker_path = Path(marker_path) if marker_path is not None else db.with_suffix(".cleanup.marker")
79
+ self._marker_cooldown_seconds = marker_cooldown_seconds
80
+
81
+ super().__init__(
82
+ evaluation_store=evaluation_store,
83
+ registry=registry,
84
+ task_factory_kwargs={
85
+ "trace_store": trace_store,
86
+ "evaluation_store": evaluation_store,
87
+ **(task_factory_kwargs or {}),
88
+ },
89
+ poll_interval=poll_interval,
90
+ )
91
+
92
+ def _should_skip_cleanup(self) -> bool:
93
+ """marker 冷却:在 ``marker_cooldown_seconds`` 非空且文件在冷却期内返回 True。"""
94
+ if self._marker_cooldown_seconds is None:
95
+ return False
96
+ path = self._marker_path
97
+ if not path.is_file():
98
+ return False
99
+ try:
100
+ last = float(path.read_text(encoding="utf-8").strip())
101
+ except (OSError, ValueError):
102
+ return False
103
+ return (time.time() - last) < float(self._marker_cooldown_seconds)
104
+
105
+ def _acquire_lock_nonblocking(self) -> Any:
106
+ """非阻塞获取清理锁;失败返回 ``None``。关闭文件锁时返回哨兵 ``_NO_LOCK``。"""
107
+ if not self._enable_file_lock:
108
+ return _NO_LOCK
109
+ self._lock_path.parent.mkdir(parents=True, exist_ok=True)
110
+ handle = open(self._lock_path, "a+b") # noqa: SIM115 — 生命周期由 _release_lock 关闭
111
+ try:
112
+ portalocker.lock(handle, portalocker.LOCK_EX | portalocker.LOCK_NB)
113
+ except (OSError, BaseLockException):
114
+ handle.close()
115
+ return None
116
+ return handle
117
+
118
+ def _release_lock(self, lock_handle: Any) -> None:
119
+ if lock_handle is None or lock_handle is _NO_LOCK:
120
+ return
121
+ handle: IO[bytes] = lock_handle
122
+ try:
123
+ portalocker.unlock(handle)
124
+ except Exception:
125
+ logger.debug("portalocker.unlock 忽略异常", exc_info=True)
126
+ try:
127
+ handle.close()
128
+ except OSError:
129
+ pass
130
+
131
+ def _write_marker_now(self) -> None:
132
+ self._marker_path.parent.mkdir(parents=True, exist_ok=True)
133
+ self._marker_path.write_text(str(time.time()), encoding="utf-8")
134
+
135
+ async def _poll_once(self) -> None:
136
+ if self._should_skip_cleanup():
137
+ logger.debug("TraceCleanupScheduler 跳过本轮(marker 冷却中)")
138
+ return
139
+
140
+ lock_handle = self._acquire_lock_nonblocking()
141
+ if lock_handle is None:
142
+ logger.debug("TraceCleanupScheduler 跳过本轮(未获取到清理锁)")
143
+ return
144
+
145
+ try:
146
+ task_factory = self._registry.get("trace.cleanup")
147
+ if task_factory is None:
148
+ logger.warning("registry 未注册 trace.cleanup,跳过清理")
149
+ return
150
+ try:
151
+ # ``task_factory_kwargs`` 已在 ``__init__`` 中含 trace_store / evaluation_store
152
+ task = task_factory(**self._task_factory_kwargs)
153
+ except Exception:
154
+ logger.exception("TraceCleanupScheduler 构造 TraceCleanupTask 失败")
155
+ return
156
+
157
+ try:
158
+ await task.run(**self._cleanup_config)
159
+ except Exception:
160
+ logger.exception("TraceCleanupScheduler 执行 trace.cleanup 失败")
161
+ return
162
+
163
+ if self._marker_cooldown_seconds is not None:
164
+ self._write_marker_now()
165
+ finally:
166
+ self._release_lock(lock_handle)
167
+
168
+
169
+ __all__ = ["TraceCleanupScheduler"]
@@ -0,0 +1,90 @@
1
+ """
2
+ tool_runtime/ — 工具运行时框架核心包
3
+
4
+ 对外暴露框架的稳定公共接口,外部代码只需从此处导入,
5
+ 不需要直接依赖子模块路径。
6
+ """
7
+
8
+ from .adapter import LangChainAdapter
9
+ from .base import RuntimeTool
10
+ from .errors import (
11
+ ToolInputSchemaError,
12
+ ToolNotFoundError,
13
+ ToolRegistrationError,
14
+ ToolRuntimeError,
15
+ blocked_envelope,
16
+ exception_to_envelope,
17
+ validation_error_envelope,
18
+ )
19
+ from .models import (
20
+ AuthorizationDecision,
21
+ ToolArtifact,
22
+ ToolErrorInfo,
23
+ ToolExecutionContext,
24
+ ToolHint,
25
+ ToolResultEnvelope,
26
+ ValidationResult,
27
+ )
28
+ from .identical_call_cache import IdenticalCallMemo
29
+ from .loader import ToolRuntimeLoader
30
+ from .pipeline import PermissionAskInterrupt, ToolExecutorPipeline, ToolOutputManager
31
+ from .policy import DefaultPolicyEngine, PolicyEngine, ToolPolicyConfig
32
+ from .registry import RuntimeToolRegistry
33
+ from .resolvers import (
34
+ AgentSessionPermissionResolver,
35
+ BackgroundPermissionResolver,
36
+ ConversationPermissionResolver,
37
+ PermissionRequest,
38
+ PermissionResolver,
39
+ WorkflowPermissionResolver,
40
+ )
41
+ from .session_store import AgentSessionStore, FileReadRecord, FileWriteRecord
42
+ from .state_bridge import ToolStateBridge
43
+
44
+ __all__ = [
45
+ # models
46
+ "ToolExecutionContext",
47
+ "ToolResultEnvelope",
48
+ "ValidationResult",
49
+ "AuthorizationDecision",
50
+ "ToolErrorInfo",
51
+ "ToolHint",
52
+ "ToolArtifact",
53
+ # base
54
+ "RuntimeTool",
55
+ # errors
56
+ "ToolRuntimeError",
57
+ "ToolRegistrationError",
58
+ "ToolNotFoundError",
59
+ "ToolInputSchemaError",
60
+ "exception_to_envelope",
61
+ "blocked_envelope",
62
+ "validation_error_envelope",
63
+ # policy
64
+ "ToolPolicyConfig",
65
+ "PolicyEngine",
66
+ "DefaultPolicyEngine",
67
+ # state
68
+ "ToolStateBridge",
69
+ # session
70
+ "AgentSessionStore",
71
+ "FileReadRecord",
72
+ "FileWriteRecord",
73
+ # pipeline
74
+ "ToolOutputManager",
75
+ "ToolExecutorPipeline",
76
+ "PermissionAskInterrupt",
77
+ "IdenticalCallMemo",
78
+ "PermissionResolver",
79
+ "PermissionRequest",
80
+ "AgentSessionPermissionResolver",
81
+ "ConversationPermissionResolver",
82
+ "BackgroundPermissionResolver",
83
+ "WorkflowPermissionResolver",
84
+ # adapter
85
+ "LangChainAdapter",
86
+ # registry
87
+ "RuntimeToolRegistry",
88
+ # loader
89
+ "ToolRuntimeLoader",
90
+ ]
@@ -0,0 +1,365 @@
1
+ """
2
+ runtime/adapter.py — LangChain 集成适配器
3
+
4
+ 职责:
5
+ 框架内部 RuntimeTool 与 LangChain 生态之间的唯一边界层,负责:
6
+ 1. 从 RunnableConfig 构造 ToolExecutionContext(隔离 LangGraph 底层依赖)
7
+ - 同时将 RuntimeTool 的 is_read_only / is_destructive 注入 ctx.tool_flags
8
+ - 允许通过 configurable 透传 permission_mode、sandbox 相关运行时标志
9
+ - PolicyEngine 从 ctx.tool_flags 读取,不污染 input_data
10
+ 2. 将 RuntimeTool 导出为 LangChain StructuredTool(保持模型可见 schema 稳定)
11
+ 3. 将内部 ToolResultEnvelope 映射为 ToolMessage.content 字符串
12
+
13
+ LangChain 工具执行路径:
14
+ ToolNode → StructuredTool.func(**kwargs) → str → ToolMessage.content
15
+
16
+ 同参重复调用短路在 ``ToolExecutorPipeline``(``RuntimeTool.dedupe_identical_calls`` +
17
+ ``IdenticalCallMemo``)完成;本适配器仍只负责 ``pipeline.run/arun`` → 字符串,不单独做去重。
18
+
19
+ 与 CC 对比:
20
+ CC 中工具直接实现 TypeScript 函数,由 ToolNode 调用,没有显式 adapter 层。
21
+ 本框架引入 LangChainAdapter 作为边界,将 LangGraph 底层对象(RunnableConfig、
22
+ state)的消费集中在此处,工具内部只依赖 ToolExecutionContext。
23
+
24
+ envelope_to_tool_output 的输出格式对应 CC 工具返回的 ToolResult.content,
25
+ 模型只收到紧凑文本,meta 不进入模型上下文。
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import threading
31
+ import time
32
+ from typing import Any, Callable, Literal, MutableMapping
33
+
34
+ from langgraph.prebuilt.tool_node import ToolRuntime
35
+ from langchain_core.runnables import RunnableConfig
36
+ from langchain_core.tools import StructuredTool
37
+
38
+ from .models import ToolExecutionContext, ToolResultEnvelope
39
+ from langchain_agentx.observability.replay.store import (
40
+ OBS_SCHEMA_VERSION,
41
+ get_global_trace_store,
42
+ sample_events,
43
+ sanitize_value,
44
+ )
45
+ from .pipeline import ToolExecutorPipeline
46
+
47
+
48
+ class LangChainAdapter:
49
+ """
50
+ LangChain 集成适配器。
51
+
52
+ 职责边界:
53
+ 负责:上下文构造(含 tool_flags 注入)、StructuredTool 导出、envelope → str 映射
54
+ 不负责:业务执行、policy 规则决策、graph 路由
55
+ """
56
+
57
+ def build_tool_execution_context(
58
+ self,
59
+ *,
60
+ tool_name: str,
61
+ raw_input: dict[str, Any],
62
+ state: MutableMapping[str, Any],
63
+ config: RunnableConfig | None = None,
64
+ tool_call_id: str | None = None,
65
+ actor: Literal["agent", "human", "system"] = "agent",
66
+ tool_flags: dict[str, Any] | None = None,
67
+ ) -> ToolExecutionContext:
68
+ """
69
+ 从 RunnableConfig.configurable 构造 ToolExecutionContext。
70
+
71
+ LangGraph 在调用工具时通过 RunnableConfig 传递 thread_id、run_id、
72
+ tool_call_id 等元数据。本方法将这些字段提取并归一化,
73
+ 隔离工具内部对 LangGraph 底层对象的直接依赖。
74
+
75
+ tool_flags:由 build_structured_tool 从 RuntimeTool 类属性提取后传入,
76
+ {"is_read_only": bool, "is_destructive": bool}
77
+ PolicyEngine.authorize() 从 ctx.tool_flags 读取工具读写属性,
78
+ 不污染 input_data。
79
+
80
+ config 参数接受 RunnableConfig 或 None。
81
+ """
82
+ configurable: dict = {}
83
+ if config is not None:
84
+ configurable = config.get("configurable", {}) or {}
85
+
86
+ loop_config = configurable.get("loop_config")
87
+
88
+ model = getattr(loop_config, "model", None)
89
+ available_tools = list(getattr(loop_config, "available_tools", None) or [])
90
+ session_id = getattr(loop_config, "session_id", None)
91
+ conversation_session_id = getattr(loop_config, "conversation_session_id", None) or session_id
92
+ workspace_root = getattr(loop_config, "workspace_root", None)
93
+ agent_home = getattr(loop_config, "agent_home", ".langchain_agentx")
94
+ # cwd 优先级:loop_config.cwd > loop_config.workspace_root > get_cwd()
95
+ # CC 对照:getCwd() = AsyncLocalStorage.getStore() ?? getCwdState()
96
+ from langchain_agentx.utils.cwd import get_cwd
97
+ cwd = getattr(loop_config, "cwd", None) or workspace_root or get_cwd()
98
+ trace_enabled = bool(getattr(loop_config, "trace_enabled", True))
99
+
100
+ _depth_from_loop = getattr(loop_config, "agent_depth", None)
101
+ if _depth_from_loop is not None:
102
+ agent_depth = int(_depth_from_loop)
103
+ elif isinstance(state, dict):
104
+ agent_depth = int(state.get("agent_depth") or 0)
105
+ else:
106
+ agent_depth = 0
107
+
108
+ _agent_id_from_state = state.get("agent_id") if isinstance(state, dict) else None
109
+ agent_id = getattr(loop_config, "agent_id", None) or _agent_id_from_state
110
+
111
+ resolved_tool_call_id = tool_call_id
112
+ if not isinstance(resolved_tool_call_id, str) or not resolved_tool_call_id:
113
+ resolved_tool_call_id = configurable.get("tool_call_id")
114
+ if not isinstance(resolved_tool_call_id, str) or not resolved_tool_call_id:
115
+ raw_tool_call_id = raw_input.get("tool_call_id") or raw_input.get("id")
116
+ if isinstance(raw_tool_call_id, str) and raw_tool_call_id:
117
+ resolved_tool_call_id = raw_tool_call_id
118
+ else:
119
+ resolved_tool_call_id = configurable.get("run_id")
120
+
121
+ cancel_ev = configurable.get("cancel_event")
122
+ if not isinstance(cancel_ev, threading.Event):
123
+ cancel_ev = None
124
+
125
+ return ToolExecutionContext(
126
+ tool_name=tool_name,
127
+ # 回退顺序:显式 tool_call_id -> config.tool_call_id -> input.tool_call_id -> input.id -> run_id
128
+ # 用于尽量保留上游真实 tool_call 语义,避免子 run 关联键缺失。
129
+ tool_call_id=resolved_tool_call_id,
130
+ input_args=raw_input,
131
+ state=state,
132
+ model=model,
133
+ available_tools=available_tools,
134
+ agent_id=agent_id,
135
+ agent_depth=agent_depth,
136
+ now_ts=time.time(),
137
+ thread_id=configurable.get("thread_id"),
138
+ run_id=configurable.get("run_id") or session_id,
139
+ session_id=session_id,
140
+ conversation_session_id=conversation_session_id,
141
+ actor=actor,
142
+ session_store=configurable.get("session_store"),
143
+ tool_loader=configurable.get("tool_loader"),
144
+ tool_registry=configurable.get("tool_registry"),
145
+ tool_flags=tool_flags or {},
146
+ workspace_root=workspace_root,
147
+ agent_home=agent_home,
148
+ cwd=cwd,
149
+ trace_enabled=trace_enabled,
150
+ cancel_event=cancel_ev,
151
+ )
152
+
153
+ def build_structured_tool(
154
+ self,
155
+ *,
156
+ tool: Any, # RuntimeTool,避免循环导入用 Any
157
+ pipeline: ToolExecutorPipeline,
158
+ state_getter: Callable[[], MutableMapping[str, Any]] | None = None,
159
+ ) -> StructuredTool:
160
+ """
161
+ 将 RuntimeTool 导出为 LangChain StructuredTool。
162
+
163
+ args_schema 直接使用 tool.input_model,保证模型可见 schema 稳定。
164
+ 内部 func / coroutine 调用 pipeline.run / arun,
165
+ 通过 LangChain 的 config 参数获取 RunnableConfig。
166
+
167
+ 从 tool 类属性提取 tool_flags,在每次调用时注入 ctx,
168
+ 使 PolicyEngine 可以获取工具的读写属性。
169
+
170
+ state_getter:历史参数,已忽略;LangGraph state 不再注入 ctx.state,
171
+ 控制面请使用 ``config["configurable"]["loop_config"]``(见 ``build_tool_execution_context``)。
172
+ """
173
+ adapter = self
174
+ # 在 StructuredTool 构造时从类属性提取,避免每次调用重复读取
175
+ _tool_flags: dict[str, Any] = {
176
+ "is_read_only": getattr(tool, "is_read_only", False),
177
+ "is_destructive": getattr(tool, "is_destructive", False),
178
+ }
179
+
180
+ def _func(
181
+ config: RunnableConfig,
182
+ tool_runtime: ToolRuntime | None = None,
183
+ **kwargs: Any,
184
+ ) -> str:
185
+ state: MutableMapping[str, Any] = {}
186
+ effective_tool_flags = dict(_tool_flags)
187
+ configurable = config.get("configurable", {}) or {}
188
+ if "permission_mode" in configurable:
189
+ effective_tool_flags["permission_mode"] = configurable["permission_mode"]
190
+ if "allow_dangerously_disable_sandbox" in configurable:
191
+ effective_tool_flags["allow_dangerously_disable_sandbox"] = configurable["allow_dangerously_disable_sandbox"]
192
+ if "sandbox_excluded_commands" in configurable:
193
+ effective_tool_flags["sandbox_excluded_commands"] = configurable["sandbox_excluded_commands"]
194
+ ctx = adapter.build_tool_execution_context(
195
+ tool_name=tool.name,
196
+ raw_input=kwargs,
197
+ state=state,
198
+ config=config,
199
+ tool_call_id=getattr(tool_runtime, "tool_call_id", None),
200
+ tool_flags=effective_tool_flags,
201
+ )
202
+ envelope = pipeline.run(tool=tool, raw_input=kwargs, ctx=ctx)
203
+ return adapter.envelope_to_tool_output(envelope)
204
+
205
+ async def _afunc(
206
+ config: RunnableConfig,
207
+ tool_runtime: ToolRuntime | None = None,
208
+ **kwargs: Any,
209
+ ) -> str:
210
+ state: MutableMapping[str, Any] = {}
211
+ effective_tool_flags = dict(_tool_flags)
212
+ configurable = config.get("configurable", {}) or {}
213
+ if "permission_mode" in configurable:
214
+ effective_tool_flags["permission_mode"] = configurable["permission_mode"]
215
+ if "allow_dangerously_disable_sandbox" in configurable:
216
+ effective_tool_flags["allow_dangerously_disable_sandbox"] = configurable["allow_dangerously_disable_sandbox"]
217
+ if "sandbox_excluded_commands" in configurable:
218
+ effective_tool_flags["sandbox_excluded_commands"] = configurable["sandbox_excluded_commands"]
219
+ ctx = adapter.build_tool_execution_context(
220
+ tool_name=tool.name,
221
+ raw_input=kwargs,
222
+ state=state,
223
+ config=config,
224
+ tool_call_id=getattr(tool_runtime, "tool_call_id", None),
225
+ tool_flags=effective_tool_flags,
226
+ )
227
+ envelope = await pipeline.arun(tool=tool, raw_input=kwargs, ctx=ctx)
228
+ return adapter.envelope_to_tool_output(envelope)
229
+
230
+ return StructuredTool(
231
+ name=tool.name,
232
+ description=tool.description,
233
+ args_schema=tool.input_model,
234
+ func=_func,
235
+ coroutine=_afunc,
236
+ )
237
+
238
+ def envelope_to_tool_output(self, env: ToolResultEnvelope) -> str:
239
+ """
240
+ 将内部 ToolResultEnvelope 映射为 LangChain ToolMessage.content 字符串。
241
+
242
+ 输出格式:
243
+ ok: summary + payload 紧凑文本 + artifacts/hints(若有)
244
+ blocked: "Permission denied: <message>"
245
+ error: summary + payload(若有)
246
+ truncated 时:summary 已包含 overflow_file 提示(由 ToolOutputManager 注入)
247
+
248
+ meta 默认不进入输出(面向系统,不面向模型)。
249
+ """
250
+ self._capture_observability(env)
251
+
252
+ if env.status == "blocked":
253
+ return env.summary # "Permission denied: ..."
254
+
255
+ parts: list[str] = [env.summary]
256
+ payload_text = self._render_payload(env.payload)
257
+ if payload_text:
258
+ parts.append(payload_text)
259
+
260
+ if env.blocks:
261
+ parts.extend(self._render_blocks(env))
262
+
263
+ if env.artifacts:
264
+ parts.extend(self._render_artifacts(env))
265
+
266
+ if env.hints:
267
+ hint_lines = [f"[Hint: {h.message}]" for h in env.hints]
268
+ parts.extend(hint_lines)
269
+
270
+ meta_lines = self._render_selected_meta(env)
271
+ if meta_lines:
272
+ parts.extend(meta_lines)
273
+
274
+ return "\n".join(parts)
275
+
276
+ @staticmethod
277
+ def _render_payload(payload: dict[str, Any] | str | None) -> str | None:
278
+ if payload is None:
279
+ return None
280
+ if isinstance(payload, str):
281
+ return payload
282
+
283
+ import json
284
+
285
+ try:
286
+ return json.dumps(payload, ensure_ascii=False)
287
+ except Exception:
288
+ return str(payload)
289
+
290
+ @staticmethod
291
+ def _render_artifacts(env: ToolResultEnvelope) -> list[str]:
292
+ lines: list[str] = []
293
+ for artifact in env.artifacts or ():
294
+ label = artifact.description or artifact.media_type
295
+ lines.append(f"[Artifact: {label} ({artifact.media_type})]")
296
+ return lines
297
+
298
+ @staticmethod
299
+ def _render_blocks(env: ToolResultEnvelope) -> list[str]:
300
+ lines: list[str] = []
301
+ for block in env.blocks or ():
302
+ if block.type == "text" and block.text:
303
+ lines.append(block.text)
304
+ elif block.type == "status" and block.text:
305
+ lines.append(f"[Status] {block.text}")
306
+ elif block.type == "reference":
307
+ ref = block.uri or ""
308
+ label = block.text or "reference"
309
+ lines.append(f"[Reference: {label}] {ref}")
310
+ elif block.type == "image":
311
+ label = block.media_type or "image/*"
312
+ lines.append(f"[Image block: {label}]")
313
+ return lines
314
+
315
+ @staticmethod
316
+ def _render_selected_meta(env: ToolResultEnvelope) -> list[str]:
317
+ if not env.meta:
318
+ return []
319
+ keys = (
320
+ "task_id",
321
+ "task_mode",
322
+ "task_status",
323
+ "task_exit_code",
324
+ "auto_backgrounded",
325
+ )
326
+ lines: list[str] = []
327
+ for key in keys:
328
+ if key in env.meta and env.meta[key] is not None:
329
+ lines.append(f"[Meta: {key}={env.meta[key]}]")
330
+ obs = env.meta.get("observability")
331
+ if isinstance(obs, dict):
332
+ events = obs.get("events", [])
333
+ lines.append(f"[Meta: observability_schema={obs.get('schema_version', OBS_SCHEMA_VERSION)}]")
334
+ lines.append(f"[Meta: observability_events={len(events) if isinstance(events, list) else 0}]")
335
+ return lines
336
+
337
+ @staticmethod
338
+ def _capture_observability(env: ToolResultEnvelope) -> None:
339
+ if not env.meta:
340
+ return
341
+ obs = env.meta.get("observability")
342
+ cleaned: dict[str, Any] | None = None
343
+ if isinstance(obs, dict):
344
+ cleaned = sanitize_value(obs)
345
+ events = cleaned.get("events")
346
+ if isinstance(events, list):
347
+ cleaned["events"] = sample_events(events, limit=200)
348
+ trace_ctx = (
349
+ cleaned.get("trace_context")
350
+ if isinstance(cleaned, dict) and isinstance(cleaned.get("trace_context"), dict)
351
+ else {}
352
+ )
353
+ tool_call_id = trace_ctx.get("tool_call_id") or env.meta.get("tool_call_id")
354
+ decision_trace = env.meta.get("decision_trace") if isinstance(env.meta.get("decision_trace"), list) else []
355
+ if cleaned is None and not decision_trace:
356
+ return
357
+ record = {
358
+ "schema_version": (cleaned or {}).get("schema_version", OBS_SCHEMA_VERSION),
359
+ "tool_name": env.tool_name,
360
+ "status": env.status,
361
+ "summary": env.summary,
362
+ "observability": cleaned or {},
363
+ "decision_trace": sanitize_value(decision_trace),
364
+ }
365
+ get_global_trace_store().put(tool_call_id, record)