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,60 @@
1
+ """
2
+ 主循环 **上下文治理** 对 Graph 的 **门面服务**(唯一推荐接入点)。
3
+
4
+ 与 Claude Code 对齐:
5
+ - 内部编排 CC 五段(`ContextPipeline` + `stages/*`),与 `query.ts` 中 `messagesForQuery` 顺序一致;
6
+ - `factory` 中 `prune_and_compress` 节点应调用 `default_loop_context_compaction().run(...)`,而非散落模块函数。
7
+
8
+ 设计要点:
9
+ - `LoopContextCompaction` 构造注入 `ContextPipeline`,便于单测替换阶段序列;
10
+ - `default_loop_context_compaction()` 返回进程内单例,避免每轮重复装配。
11
+
12
+ 注意:
13
+ - 不修改 `should_end` / `finish_reason` 等 exit 字段;只产出 `messages` 增量补丁。
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import Any, Dict, Mapping
19
+
20
+ from .pipeline import ContextPipeline
21
+ from .settings import CompactionSettings
22
+ from .types import ContextCompactionContext
23
+
24
+
25
+ class LoopContextCompaction:
26
+ """对 LangGraph state 执行 CC 五段上下文流水线。"""
27
+
28
+ def __init__(self, pipeline: ContextPipeline) -> None:
29
+ self._pipeline = pipeline
30
+
31
+ @classmethod
32
+ def with_default_pipeline(
33
+ cls,
34
+ *,
35
+ settings: CompactionSettings | None = None,
36
+ ) -> LoopContextCompaction:
37
+ """使用 CC 默认五段顺序装配管道。"""
38
+ return cls(ContextPipeline.with_default_stages(settings=settings))
39
+
40
+ def run(self, state: Mapping[str, Any], model: Any | None = None) -> Dict[str, Any]:
41
+ """返回 `Command(update=...)` 可用的补丁(常见为 ``{}`` 或 ``{"messages": ...}``)。"""
42
+ ctx = ContextCompactionContext.from_state(state, model)
43
+ return self._pipeline.run(state, ctx)
44
+
45
+
46
+ _default_loop_context_compaction: LoopContextCompaction | None = None
47
+
48
+
49
+ def default_loop_context_compaction() -> LoopContextCompaction:
50
+ """进程内单例门面(`factory` 节点调用)。"""
51
+ global _default_loop_context_compaction
52
+ if _default_loop_context_compaction is None:
53
+ _default_loop_context_compaction = LoopContextCompaction.with_default_pipeline()
54
+ return _default_loop_context_compaction
55
+
56
+
57
+ __all__ = [
58
+ "LoopContextCompaction",
59
+ "default_loop_context_compaction",
60
+ ]
@@ -0,0 +1,56 @@
1
+ """
2
+ LangChain 消息内容规范化与 token 粗估(供 `stages/*` 复用)。
3
+
4
+ 与 Claude Code 对齐:
5
+ - CC 各紧凑阶段同样基于「字符 / token 近似」做预算;此处集中实现,避免五段内重复逻辑。
6
+
7
+ 设计要点:
8
+ - 仅使用 `BaseMessage.content` 的公开形状(`str`、多模态 `list[dict|str]`),与 LangChain 一致;
9
+ - `estimate_*` 为 **启发式**,与真实 tokenizer 有偏差,仅用于会话级预算与观测。
10
+
11
+ 注意:
12
+ - 不 import 具体阶段;不依赖 LangGraph。
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import Any
18
+
19
+
20
+ def stringify_content(content: Any) -> str:
21
+ """将 LangChain `Message.content` 规范为纯文本(多模态块取 text 段)。"""
22
+ if content is None:
23
+ return ""
24
+ if isinstance(content, str):
25
+ return content
26
+ if isinstance(content, list):
27
+ parts: list[str] = []
28
+ for block in content:
29
+ if isinstance(block, str):
30
+ parts.append(block)
31
+ elif isinstance(block, dict):
32
+ if block.get("type") == "text":
33
+ parts.append(str(block.get("text", "")))
34
+ else:
35
+ parts.append(str(block))
36
+ else:
37
+ parts.append(str(block))
38
+ return "".join(parts)
39
+ return str(content)
40
+
41
+
42
+ def estimate_tokens_from_chars(n_chars: int, num_chars_per_token: int) -> int:
43
+ if num_chars_per_token <= 0:
44
+ return n_chars
45
+ return (max(0, n_chars) + num_chars_per_token - 1) // num_chars_per_token
46
+
47
+
48
+ def estimate_tokens_from_content(content: Any, num_chars_per_token: int) -> int:
49
+ return estimate_tokens_from_chars(len(stringify_content(content)), num_chars_per_token)
50
+
51
+
52
+ def total_estimated_tokens_for_messages(messages: list[Any], num_chars_per_token: int) -> int:
53
+ total_chars = 0
54
+ for m in messages:
55
+ total_chars += len(stringify_content(getattr(m, "content", None)))
56
+ return estimate_tokens_from_chars(total_chars, num_chars_per_token)
@@ -0,0 +1,127 @@
1
+ """
2
+ 上下文压缩流水线编排(`ContextPipeline` + 默认 CC 五段装配)。
3
+
4
+ 与 Claude Code 对齐:
5
+ - **仅** `query.ts` 中 `applyToolResultBudget` → snip → microcompact → collapse → autocompact 五段;
6
+ - `DefaultCompactionStages.build()` 为顺序 SSOT,与 TS 侧 `messagesForQuery` 链一致。
7
+
8
+ 设计要点:
9
+ - `ContextPipeline.run` 仅消费 `CompactionStage`;若最终 `messages` 列表引用被替换则返回补丁;
10
+ - 可选 `settings=` 与自定义五段注入的 `CompactionSettings` 对齐;省略时用 `get_active_compaction_settings()`(与 `DefaultCompactionStages` 默认一致);
11
+ - 阶段 C:可选写入 `compaction_pipeline_meta`(token 告警档、各段 `tokens_freed`),供观测与 event 适配;
12
+ - 门面 `LoopContextCompaction`(见 `compaction_service.py`)负责 `ContextCompactionContext` 装配与调用本类。
13
+
14
+ 注意:
15
+ - 阶段若就地改消息且仍返回同一 list,当前实现**无法**检测变更;实现阶段请返回新 list。
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from typing import Any
21
+
22
+ from ..config.token_estimator import TokenEstimator
23
+ from .blocking_guard import CompactionBlockingGuard
24
+ from .settings import CompactionSettings, get_active_compaction_settings
25
+ from .stages import (
26
+ AutocompactStage,
27
+ ContextCollapseStage,
28
+ MicrocompactStage,
29
+ SnipCompactStage,
30
+ ToolResultBudgetStage,
31
+ )
32
+ from .stages.base import CompactionStage
33
+ from .types import ContextCompactionContext
34
+
35
+
36
+ class DefaultCompactionStages:
37
+ """CC 五段默认装配(顺序即契约)。"""
38
+
39
+ @staticmethod
40
+ def build() -> tuple[CompactionStage, ...]:
41
+ return (
42
+ ToolResultBudgetStage(),
43
+ SnipCompactStage(),
44
+ MicrocompactStage(),
45
+ ContextCollapseStage(),
46
+ AutocompactStage(),
47
+ )
48
+
49
+
50
+ class ContextPipeline:
51
+ """顺序执行 CC 五段。"""
52
+
53
+ def __init__(
54
+ self,
55
+ stages: tuple[CompactionStage, ...],
56
+ *,
57
+ settings: CompactionSettings | None = None,
58
+ ) -> None:
59
+ self._stages = stages
60
+ self._settings_override = settings
61
+
62
+ @classmethod
63
+ def with_default_stages(
64
+ cls,
65
+ settings: CompactionSettings | None = None,
66
+ ) -> ContextPipeline:
67
+ """使用 `DefaultCompactionStages` 构建管道。"""
68
+ return cls(DefaultCompactionStages.build(), settings=settings)
69
+
70
+ @property
71
+ def stages(self) -> tuple[CompactionStage, ...]:
72
+ return self._stages
73
+
74
+ def run(self, state: dict[str, Any], ctx: ContextCompactionContext) -> dict[str, Any]:
75
+ messages = state.get("messages")
76
+ if not isinstance(messages, list) or not messages:
77
+ return {}
78
+
79
+ s = self._settings_override or get_active_compaction_settings()
80
+ estimator = TokenEstimator(num_chars_per_token=s.num_chars_per_token)
81
+ original_input = list(messages)
82
+ current = original_input
83
+ guard = CompactionBlockingGuard(settings=s)
84
+ tw = guard.calculate_token_warning_state(original_input)
85
+ tokens_before = estimator.estimate_messages_tokens(original_input)
86
+ stages_meta: list[dict[str, Any]] = []
87
+ total_freed = 0
88
+
89
+ for stage in self._stages:
90
+ sr = stage.run(current, ctx)
91
+ total_freed += sr.tokens_freed
92
+ stages_meta.append(
93
+ {
94
+ "name": stage.name,
95
+ "tokens_freed": sr.tokens_freed,
96
+ "extra": dict(sr.extra),
97
+ }
98
+ )
99
+ current = sr.messages
100
+ ctx.snip_tokens_freed_carry += sr.tokens_freed
101
+
102
+ patch: dict[str, Any] = {}
103
+ if current is not original_input:
104
+ patch["messages"] = current
105
+
106
+ tokens_after = estimator.estimate_messages_tokens(current)
107
+ if s.emit_compaction_pipeline_meta and (
108
+ "messages" in patch or total_freed > 0
109
+ ):
110
+ patch["compaction_pipeline_meta"] = {
111
+ "estimated_tokens_before": tokens_before,
112
+ "estimated_tokens_after": tokens_after,
113
+ "tokens_freed_sum": total_freed,
114
+ "snip_tokens_freed_carry_final": ctx.snip_tokens_freed_carry,
115
+ "token_warning_level": tw.level,
116
+ "stages": stages_meta,
117
+ }
118
+
119
+ if not patch:
120
+ return {}
121
+ return patch
122
+
123
+
124
+ __all__ = [
125
+ "ContextPipeline",
126
+ "DefaultCompactionStages",
127
+ ]
@@ -0,0 +1,103 @@
1
+ """
2
+ 主循环上下文治理的 **策略与提示词**(单一配置对象,便于替换与单测)。
3
+
4
+ 与 Claude Code 对齐:
5
+ - 数值与 `OPENCODE_*` 环境变量覆盖语义、会话摘要模板与 CC `messagesForQuery` 后续各阶段读取的阈值/提示一致方向;
6
+ - 各 `CompactionStage` 实现应通过 `get_active_compaction_settings()` 读取,避免散落魔法数。
7
+
8
+ 设计要点:
9
+ - `CompactionSettings` 为 `frozen` 值对象;可变绑定为模块级 `active_compaction_settings`,单测可 `set_active_compaction_settings(...)` 整体替换。
10
+
11
+ 注意:
12
+ - 不依赖 LangGraph;与 `tool_runtime` 单条截断分工见设计文档。
13
+ - 紧凑化提示词函数(如 `get_compact_prompt`)在 **`loop/prompt/compact.py`**,不在本模块再导出。
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from dataclasses import dataclass, field
19
+ from typing import FrozenSet
20
+
21
+ from ..prompt.compact import AutocompactLlmPromptKind
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class CompactionSettings:
26
+ """CC 对齐上下文治理的默认策略(提示词 + 数值阈值)。"""
27
+
28
+ num_chars_per_token: int = 4
29
+ prune_protect_tokens: int = 40_000
30
+ prune_minimum_tokens: int = 20_000
31
+ prune_protected_tools: FrozenSet[str] = field(default_factory=lambda: frozenset({"skill"}))
32
+ default_max_context_tokens: int = 120_000
33
+ default_compress_trigger_fraction: float = 0.8
34
+ default_compress_keep_recent_messages: int = 8
35
+ # --- autocompact LLM 提示词(对齐 `compact.ts` + `prompt.ts`)---
36
+ # 摘要插在保留 tail 之前 → 默认 `partial_up_to`;全量折叠无尾窗时用 `full`;与 CC partial pivot「from」一致时用 `partial_from`。
37
+ autocompact_llm_prompt_kind: AutocompactLlmPromptKind = "partial_up_to"
38
+ # 追加到 CC 模板末尾的 Additional Instructions(对齐 hook merge 的 customInstructions)。
39
+ compact_custom_instructions: str | None = None
40
+ # 非 None 时跳过 builder,整段作为 compact 调用的指令(单测 / 完全自定义)。
41
+ autocompact_llm_instructions_override: str | None = None
42
+ # 兼容旧字段:未设置 override 时若此字段非空则优先于 builder(避免破坏只传 compaction_system_prompt 的调用方)。
43
+ compaction_system_prompt: str | None = None
44
+ # --- tool_result_budget(对齐 applyToolResultBudget:单条结果软上限)---
45
+ tool_result_max_chars_per_message: int = 48_000
46
+ tool_result_budget_suffix: str = field(
47
+ default="\n\n[... truncated by tool_result_budget ...]"
48
+ )
49
+ # --- snip_compact(对齐 snip:尾部保留 + 历史大块截断)---
50
+ snip_preserve_tail_messages: int = 10
51
+ snip_max_tool_content_chars: int = 12_288
52
+ snip_suffix: str = field(default="\n\n[... snipped by snip_compact ...]")
53
+ # --- microcompact(轻量规范化;递归压缩时跳过)---
54
+ microcompact_skip_query_sources: FrozenSet[str] = field(
55
+ default_factory=lambda: frozenset({"compact"})
56
+ )
57
+ # --- context_collapse(重复 tool 结果治理)---
58
+ collapse_dedupe_consecutive_same_tool_call_id: bool = True
59
+ # --- autocompact(阈值触发;产品与 CC 对齐默认走 LLM 摘要,无 model 时回退 prune)---
60
+ autocompact_skip_query_sources: FrozenSet[str] = field(
61
+ default_factory=lambda: frozenset({"compact"})
62
+ )
63
+ autocompact_use_llm_when_available: bool = True
64
+ autocompact_llm_excerpt_chars_per_message: int = 20_000
65
+ # --- snip carry → autocompact 阈值放宽(对齐 CC snipTokensFreed 计入阈值)---
66
+ # True:将本流水线已累计的 `snip_tokens_freed_carry` 加到触发阈值上,避免 snip 已释放后仍立刻 autocompact。
67
+ autocompact_snip_carry_threshold_slack: bool = True
68
+ # --- blocking / token 告警(对齐 CC calculateTokenWarningState 分档)---
69
+ compaction_token_warn_fraction: float = 0.85
70
+ compaction_block_compact_llm_fraction: float = 0.98
71
+ # 达到或超过视为「主上下文已顶满」,建议由调用方拦主模型(本守卫 `should_block`)。
72
+ compaction_block_main_model_fraction: float = 1.0
73
+ # True:`model` 节点在 `invoke` 前若 `should_block` 命中则不再调主模型,写入合成 AIMessage 并结束。
74
+ enable_main_model_context_token_block: bool = True
75
+ # --- tool transcript guard(对齐 CC `ensureToolResultPairing`:发往 LLM 前修复 tool 配对)---
76
+ # True:在绑定模型 `invoke/ainvoke` 前对 `messages` 做确定性修复(跨 assistant 去重 tool_call_id、
77
+ # 去重连续 ToolMessage、补 synthetic 等),降低提供商 400 与会话卡死风险;不依赖提示词。
78
+ enable_tool_transcript_repair_before_llm: bool = True
79
+ # --- 观测:写入 state.compaction_pipeline_meta(需 LoopAgentStateFields 含该键)---
80
+ emit_compaction_pipeline_meta: bool = True
81
+
82
+
83
+ active_compaction_settings: CompactionSettings = CompactionSettings()
84
+
85
+
86
+ def get_active_compaction_settings() -> CompactionSettings:
87
+ """返回当前生效的策略(默认即模块级 `active_compaction_settings`)。"""
88
+ return active_compaction_settings
89
+
90
+
91
+ def set_active_compaction_settings(settings: CompactionSettings) -> None:
92
+ """替换全局策略(单测或进程级配置用)。"""
93
+ global active_compaction_settings
94
+ active_compaction_settings = settings
95
+
96
+
97
+ __all__ = [
98
+ "AutocompactLlmPromptKind",
99
+ "CompactionSettings",
100
+ "active_compaction_settings",
101
+ "get_active_compaction_settings",
102
+ "set_active_compaction_settings",
103
+ ]
@@ -0,0 +1,29 @@
1
+ """
2
+ 上下文压缩流水线阶段实现包。
3
+
4
+ 与 Claude Code 对齐:
5
+ - 本子包 **仅含 CC 五段**(`CompactionStage` 子类)及泛型 `NoOpCompactionStage`;Graph 入口见 `default_loop_context_compaction().run`(`compaction_service`)。
6
+
7
+ 注意:
8
+ - 新增 CC 阶段请在此包或子模块实现 `CompactionStage`,并在 `pipeline` 中注册。
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from .autocompact import AutocompactStage
14
+ from .base import CompactionStage
15
+ from .collapse import ContextCollapseStage
16
+ from .microcompact import MicrocompactStage
17
+ from .noop import NoOpCompactionStage
18
+ from .snip import SnipCompactStage
19
+ from .tool_result_budget import ToolResultBudgetStage
20
+
21
+ __all__ = [
22
+ "AutocompactStage",
23
+ "CompactionStage",
24
+ "ContextCollapseStage",
25
+ "MicrocompactStage",
26
+ "NoOpCompactionStage",
27
+ "SnipCompactStage",
28
+ "ToolResultBudgetStage",
29
+ ]
@@ -0,0 +1,140 @@
1
+ """
2
+ Autocompact 阶段:超阈值时折叠前缀消息(对齐 CC `deps.autocompact` 的会话级收紧)。
3
+
4
+ 与 Claude Code 对齐:
5
+ - 当估计 token 超过 `default_max_context_tokens * default_compress_trigger_fraction` 时,保留首部 `SystemMessage` 与尾部 `default_compress_keep_recent_messages` 条,中间以占位或(可选)LLM 摘要替换;
6
+ - `ctx.snip_tokens_freed_carry` 已由 `ContextPipeline` 累加前置阶段释放量,可供后续扩展阈值修正(与 CC `snipTokensFreed` 同向)。
7
+
8
+ 设计要点:
9
+ - 默认 `autocompact_use_llm_when_available=True`:超阈值且 `ctx.model` 可用、且 **未** 命中 `CompactionBlockingGuard.should_block_compact_llm` 时走 LLM;否则或无 model / `invoke` 失败则 **prune**;
10
+ - `autocompact_snip_carry_threshold_slack`:将 `ctx.snip_tokens_freed_carry` 加到触发阈值(进入本阶段前由前四段累加),对齐 CC `snipTokensFreed` 阈值修正。
11
+ - LLM 路径对齐 `compact.ts`:`messages` 先放待摘要摘录,**最后一条** `HumanMessage` 为完整 compact 指令(与 `summaryRequest` 同序);指令由 `build_autocompact_llm_instruction_prompt` 按 `autocompact_llm_prompt_kind` 选择(默认 `partial_up_to`,与「摘要 + 保留 tail」一致),`compact_custom_instructions` 对应 CC `customInstructions`;`autocompact_llm_instructions_override` / 非空的 `compaction_system_prompt` 可覆盖 builder;
12
+ - 模型输出经 `get_compact_user_summary_message(..., recent_messages_preserved=True)`(内部 `format_compact_summary`)写入 state,与 CC 续写包装一致。
13
+
14
+ 注意:
15
+ - `query_source in autocompact_skip_query_sources`(如 `compact`)时跳过,防递归压缩。
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from typing import Any
21
+
22
+ from langchain_core.messages import HumanMessage, SystemMessage
23
+
24
+ from ..blocking_guard import CompactionBlockingGuard
25
+ from ..message_utils import stringify_content, total_estimated_tokens_for_messages
26
+ from ...prompt.compact import build_autocompact_llm_instruction_prompt, get_compact_user_summary_message
27
+ from ..settings import CompactionSettings, get_active_compaction_settings
28
+ from ..types import ContextCompactionContext, StageResult
29
+ from .base import CompactionStage
30
+
31
+
32
+ class AutocompactStage(CompactionStage):
33
+ """CC 阶段 ⑤:autocompact(阈值折叠 / 可选 LLM 摘要)。"""
34
+
35
+ def __init__(self, settings: CompactionSettings | None = None) -> None:
36
+ self._settings_override = settings
37
+
38
+ def _effective_settings(self) -> CompactionSettings:
39
+ return self._settings_override or get_active_compaction_settings()
40
+
41
+ @property
42
+ def name(self) -> str:
43
+ return "autocompact"
44
+
45
+ def run(self, messages: list[Any], ctx: ContextCompactionContext) -> StageResult:
46
+ s = self._effective_settings()
47
+ if ctx.query_source in s.autocompact_skip_query_sources:
48
+ return StageResult(messages=messages, tokens_freed=0, extra={"autocompact_skipped": "query_source"})
49
+ total = total_estimated_tokens_for_messages(messages, s.num_chars_per_token)
50
+ threshold = int(s.default_max_context_tokens * s.default_compress_trigger_fraction)
51
+ slack = ctx.snip_tokens_freed_carry if s.autocompact_snip_carry_threshold_slack else 0
52
+ if total <= threshold + slack:
53
+ return StageResult(messages=messages, tokens_freed=0)
54
+
55
+ leading: list[Any] = []
56
+ idx = 0
57
+ while idx < len(messages) and isinstance(messages[idx], SystemMessage):
58
+ leading.append(messages[idx])
59
+ idx += 1
60
+ rest = messages[idx:]
61
+ k = max(1, s.default_compress_keep_recent_messages)
62
+ if len(rest) <= k:
63
+ return StageResult(messages=messages, tokens_freed=0)
64
+
65
+ head = rest[:-k]
66
+ tail = rest[-k:]
67
+
68
+ block_llm = CompactionBlockingGuard(settings=s).should_block_compact_llm(messages)
69
+ if (
70
+ s.autocompact_use_llm_when_available
71
+ and ctx.model is not None
72
+ and not block_llm
73
+ ):
74
+ summary = _invoke_summary(ctx.model, s, head)
75
+ if summary:
76
+ folded = leading + [
77
+ HumanMessage(
78
+ content=get_compact_user_summary_message(
79
+ summary,
80
+ recent_messages_preserved=True,
81
+ )
82
+ )
83
+ ] + tail
84
+ new_total = total_estimated_tokens_for_messages(folded, s.num_chars_per_token)
85
+ return StageResult(
86
+ messages=folded,
87
+ tokens_freed=max(0, total - new_total),
88
+ extra={"autocompact_mode": "llm", "folded_messages": len(head)},
89
+ )
90
+
91
+ folded = leading + [
92
+ HumanMessage(
93
+ content=(
94
+ f"[Context autocompacted: {len(head)} earlier messages omitted; "
95
+ "details dropped for token budget.]"
96
+ )
97
+ )
98
+ ] + tail
99
+ new_total = total_estimated_tokens_for_messages(folded, s.num_chars_per_token)
100
+ mode = "prune_blocked_llm" if block_llm else "prune"
101
+ return StageResult(
102
+ messages=folded,
103
+ tokens_freed=max(0, total - new_total),
104
+ extra={"autocompact_mode": mode, "folded_messages": len(head)},
105
+ )
106
+
107
+
108
+ def _resolve_autocompact_llm_instruction(s: CompactionSettings) -> str:
109
+ if s.autocompact_llm_instructions_override is not None:
110
+ return s.autocompact_llm_instructions_override
111
+ if s.compaction_system_prompt is not None and s.compaction_system_prompt.strip() != "":
112
+ return s.compaction_system_prompt
113
+ return build_autocompact_llm_instruction_prompt(
114
+ s.autocompact_llm_prompt_kind,
115
+ s.compact_custom_instructions,
116
+ )
117
+
118
+
119
+ def _invoke_summary(model: Any, s: CompactionSettings, head: list[Any]) -> str | None:
120
+ cap = max(256, s.autocompact_llm_excerpt_chars_per_message)
121
+ parts: list[str] = []
122
+ for m in head:
123
+ label = getattr(m, "type", type(m).__name__)
124
+ chunk = stringify_content(getattr(m, "content", None))[:cap]
125
+ parts.append(f"{label}: {chunk}")
126
+ body = "\n\n---\n\n".join(parts)
127
+ excerpt_msg = HumanMessage(
128
+ content=(
129
+ "[Conversation excerpt to summarize — follows above chronology in thread]\n\n" + body
130
+ )
131
+ )
132
+ instruction = _resolve_autocompact_llm_instruction(s)
133
+ instruction_msg = HumanMessage(content=instruction)
134
+ # 与 CC `streamCompactSummary` 一致:history 在前,compact 指令为**最后一条** user 消息。
135
+ try:
136
+ resp = model.invoke([excerpt_msg, instruction_msg])
137
+ except Exception:
138
+ return None
139
+ text = stringify_content(getattr(resp, "content", None))
140
+ return text if text else None
@@ -0,0 +1,32 @@
1
+ """
2
+ 压缩流水线阶段抽象基类(CompactionStage)。
3
+
4
+ 与 Claude Code 对齐:
5
+ - 对应 `query.ts` 中 `messagesForQuery` 上顺序执行的治理步骤;每步为可替换策略单元。
6
+
7
+ 设计要点:
8
+ - 子类实现 `run(messages, ctx) -> StageResult`;CC 五段均在 `stages/` 内实现。
9
+
10
+ 注意:
11
+ - 不依赖 LangGraph;仅处理消息列表与 ContextCompactionContext。
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from abc import ABC, abstractmethod
17
+ from typing import Any
18
+
19
+ from ..types import ContextCompactionContext, StageResult
20
+
21
+
22
+ class CompactionStage(ABC):
23
+ """上下文压缩流水线中的一步(CC queryLoop 阶段 ① 子步骤)。"""
24
+
25
+ @property
26
+ @abstractmethod
27
+ def name(self) -> str:
28
+ """阶段名(观测 / 日志)。"""
29
+
30
+ @abstractmethod
31
+ def run(self, messages: list[Any], ctx: ContextCompactionContext) -> StageResult:
32
+ """输入当前消息列表,返回更新后的列表与可选 tokens_freed。"""
@@ -0,0 +1,76 @@
1
+ """
2
+ Context collapse 阶段:合并连续重复 `tool_call_id` 的 `ToolMessage`(对齐 CC collapse 卫生语义子集)。
3
+
4
+ 与 Claude Code 对齐:
5
+ - 对应 `applyCollapsesIfNeeded` 中「同一调用重复写回」类问题的治理:保留**最后一次**结果,去掉前置重复块,减少无意义重复 token。
6
+
7
+ 设计要点:
8
+ - 仅处理**连续**且 `tool_call_id` 相同的 `ToolMessage`;不跨非 Tool 消息合并;
9
+ - 可通过 `CompactionSettings.collapse_dedupe_consecutive_same_tool_call_id` 关闭。
10
+
11
+ 注意:
12
+ - 不依赖 LangGraph;与 autocompact 分工:本阶段不生成会话摘要,仅结构化去重。
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import Any
18
+
19
+ from langchain_core.messages import ToolMessage
20
+
21
+ from ..message_utils import estimate_tokens_from_chars, stringify_content
22
+ from ..settings import CompactionSettings, get_active_compaction_settings
23
+ from ..types import ContextCompactionContext, StageResult
24
+ from .base import CompactionStage
25
+
26
+
27
+ class ContextCollapseStage(CompactionStage):
28
+ """CC 阶段 ④:context collapse(连续重复 tool 结果折叠)。"""
29
+
30
+ def __init__(self, settings: CompactionSettings | None = None) -> None:
31
+ self._settings_override = settings
32
+
33
+ def _effective_settings(self) -> CompactionSettings:
34
+ return self._settings_override or get_active_compaction_settings()
35
+
36
+ @property
37
+ def name(self) -> str:
38
+ return "context_collapse"
39
+
40
+ def run(self, messages: list[Any], ctx: ContextCompactionContext) -> StageResult:
41
+ _ = ctx
42
+ s = self._effective_settings()
43
+ if not s.collapse_dedupe_consecutive_same_tool_call_id or not messages:
44
+ return StageResult(messages=messages, tokens_freed=0)
45
+ out: list[Any] = []
46
+ i = 0
47
+ freed_chars = 0
48
+ dropped = 0
49
+ while i < len(messages):
50
+ m = messages[i]
51
+ if isinstance(m, ToolMessage):
52
+ j = i
53
+ tid = m.tool_call_id
54
+ while (
55
+ j + 1 < len(messages)
56
+ and isinstance(messages[j + 1], ToolMessage)
57
+ and messages[j + 1].tool_call_id == tid
58
+ ):
59
+ j += 1
60
+ # messages[i : j + 1] 同 id;保留最后一条,丢弃 i..j-1
61
+ if j > i:
62
+ for k in range(i, j):
63
+ freed_chars += len(stringify_content(messages[k].content))
64
+ dropped += 1
65
+ out.append(messages[j])
66
+ i = j + 1
67
+ continue
68
+ out.append(m)
69
+ i += 1
70
+ if len(out) == len(messages):
71
+ return StageResult(messages=messages, tokens_freed=0)
72
+ return StageResult(
73
+ messages=out,
74
+ tokens_freed=estimate_tokens_from_chars(freed_chars, s.num_chars_per_token),
75
+ extra={"collapse_dropped_tool_messages": dropped},
76
+ )