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,504 @@
1
+ """
2
+ loop/subagent/runner.py — 子 Agent 同步执行路径
3
+
4
+ 职责:run_subagent(),plain/fork 模式同步阻塞执行,astream_events 流式处理
5
+ 在整体链路中的位置:AgentRuntimeTool.invoke() → run_subagent() → SubagentResult
6
+ CC 对照:runAgent.ts runAgent();onProgress 回调逻辑
7
+ 当前裁剪范围:v1 plain/fork 同步路径;coordinate 异步路径见 async_runner.py
8
+
9
+ 执行流程(run_subagent):
10
+ 1. 校验:is_async=True 时抛 RuntimeError,model=None 时抛 ValueError
11
+ 2. 构造 SubagentTranscriptManager(InMemoryTranscriptStore)
12
+ 3. create_subagent_graph(ctx, tools, model) 创建子图
13
+ 4. 将 ctx.initial_messages 转为 LangChain HumanMessage 列表作为图输入
14
+ 5. astream_events 循环(单线收集,CC agentMessages 累积模式):
15
+ on_tool_start → on_progress(tool_use)
16
+ on_tool_end → on_progress(tool_result) + transcript.append()
17
+ on_chat_model_end → 累积 assistant 消息 + total_steps 计数 + on_progress(model_step)
18
+ on_chain_end(LangGraph) → 仅提取 terminal_reason(不再取 messages)
19
+ 6. 从累积的 assistant 消息中提取最终内容(CC finalizeAgentTool 语义)
20
+ 7. on_progress(completed),返回 SubagentResult
21
+ try/finally:确保 transcript.cleanup() + cleanup_subagent_context() 必然执行
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import asyncio
27
+ import logging
28
+ import threading
29
+ from concurrent.futures import Future
30
+ from dataclasses import dataclass
31
+ from typing import Any, Callable
32
+
33
+ from langchain_core.messages import HumanMessage
34
+
35
+ from langchain_agentx.loop.subagent.context import (
36
+ SubagentContext,
37
+ SubagentResult,
38
+ cleanup_subagent_context,
39
+ )
40
+ from langchain_agentx.loop.subagent.graph import create_subagent_graph
41
+ from langchain_agentx.loop.subagent.progress import SubagentProgressEvent
42
+ from langchain_agentx.loop.subagent.transcript import (
43
+ InMemoryTranscriptStore,
44
+ SubagentTranscriptManager,
45
+ )
46
+
47
+ logger = logging.getLogger(__name__)
48
+
49
+
50
+ def _run_coroutine_sync(coro: Any) -> Any:
51
+ """在同步上下文运行协程;若当前已有事件循环则转到子线程执行。"""
52
+ try:
53
+ asyncio.get_running_loop()
54
+ except RuntimeError:
55
+ return asyncio.run(coro)
56
+
57
+ fut: Future[Any] = Future()
58
+
59
+ def _runner() -> None:
60
+ try:
61
+ fut.set_result(asyncio.run(coro))
62
+ except Exception as exc:
63
+ fut.set_exception(exc)
64
+
65
+ thread = threading.Thread(target=_runner, daemon=True)
66
+ thread.start()
67
+ thread.join()
68
+ return fut.result()
69
+
70
+
71
+ @dataclass
72
+ class BackgroundLaunchedResult:
73
+ """异步路径立即返回的凭证(coordinate 模式)。"""
74
+ status: str # always "async_launched"
75
+ agent_id: str
76
+ task_id: str
77
+
78
+
79
+ @dataclass
80
+ class _EventCollectionResult:
81
+ """子图事件收集结果。"""
82
+ collected_assistant_messages: list[Any]
83
+ terminal_reason: str
84
+ total_steps: int
85
+ error_message: str
86
+ last_tool_name: str
87
+ child_run_id: str | None
88
+ tool_step: int
89
+
90
+
91
+ def _to_message_dict(msg: Any) -> dict[str, Any]:
92
+ """将 LangChain message 对象转为 dict,已是 dict 则直接返回。"""
93
+ if isinstance(msg, dict):
94
+ return msg
95
+ role = getattr(msg, "type", None) or getattr(msg, "role", "unknown")
96
+ # LangChain message type → role 映射
97
+ role_map = {"human": "user", "ai": "assistant", "system": "system", "tool": "tool"}
98
+ role = role_map.get(role, role)
99
+ content = getattr(msg, "content", "")
100
+ return {"role": role, "content": content}
101
+
102
+
103
+ def _extract_final_content(messages: list[Any], allowed_tool_names: set[str] | None = None) -> str:
104
+ """从累积的 assistant 消息中提取最终文本内容。
105
+
106
+ 策略(对齐 CC finalizeAgentTool):
107
+ 1. 从后往前找第一条有文本且不是纯工具调度消息的 AIMessage。
108
+ 2. 若所有 AIMessage 都是纯工具调度(有 tool_calls 无文本),返回空串,
109
+ 由调用方附诊断信息。
110
+
111
+ "纯工具调度消息"定义:tool_calls 非空,或内容含退化格式工具调用文本。
112
+ on_chat_model_end 在 ToolCallDegradationCorrector 处理前触发,因此
113
+ collected_assistant_messages 里的退化消息尚未被纠偏,需在此处识别并跳过。
114
+ """
115
+ def _text_from_msg(msg: Any) -> str:
116
+ if isinstance(msg, dict):
117
+ if msg.get("role") in ("assistant", "ai"):
118
+ return str(msg.get("content", ""))
119
+ else:
120
+ if getattr(msg, "type", None) == "ai":
121
+ content = getattr(msg, "content", "")
122
+ if isinstance(content, str):
123
+ return content
124
+ if isinstance(content, list):
125
+ parts = [
126
+ item.get("text", "")
127
+ for item in content
128
+ if isinstance(item, dict) and item.get("type") == "text"
129
+ ]
130
+ return "".join(parts)
131
+ return ""
132
+
133
+ def _has_tool_calls(msg: Any) -> bool:
134
+ # on_chat_model_end fires before ToolCallDegradationCorrector processes the message,
135
+ # so raw degraded messages have no tool_calls but contain degradation text patterns.
136
+ # Treat those as tool dispatch messages too, so they are skipped when extracting
137
+ # the final summary content.
138
+ if isinstance(msg, dict):
139
+ if bool(msg.get("tool_calls")):
140
+ return True
141
+ content = str(msg.get("content", ""))
142
+ else:
143
+ if bool(getattr(msg, "tool_calls", None)):
144
+ return True
145
+ content = getattr(msg, "content", "")
146
+ if not isinstance(content, str):
147
+ return False
148
+ # JSON-tag / prefix degradation formats
149
+ if "<tool_call>" in content or "CALL_TOOL:" in content or "TOOL_CALL:" in content:
150
+ return True
151
+ # XML child-element / self-closing formats: <ToolName>...</ToolName> or <ToolName ... />
152
+ # Only match tags whose names are in the known tool whitelist to avoid false positives
153
+ # on <env>, <tool_code>, or arbitrary HTML in model output.
154
+ if allowed_tool_names:
155
+ for name in allowed_tool_names:
156
+ if f"<{name}>" in content or f"<{name} " in content or f"<{name}/>" in content:
157
+ return True
158
+ return False
159
+
160
+ for msg in reversed(messages):
161
+ text = _text_from_msg(msg)
162
+ if text and not _has_tool_calls(msg):
163
+ # 有文本且不是工具调度消息 — 这是真正的最终回复
164
+ return text
165
+
166
+ # 所有消息都是纯工具调度(或无文本),返回空串
167
+ return ""
168
+
169
+
170
+ def _resolve_subagent_model(ctx: SubagentContext, model: Any | None) -> Any:
171
+ if ctx.is_async:
172
+ raise RuntimeError(
173
+ "异步路径请调用 run_async_agent_lifecycle(),不要通过 run_subagent() 执行"
174
+ )
175
+ resolved_model = model if model is not None else ctx.model
176
+ if resolved_model is None:
177
+ raise ValueError("model is required for run_subagent")
178
+ return resolved_model
179
+
180
+
181
+ def _build_graph_input_messages(initial_messages: list[Any]) -> list[Any]:
182
+ return [
183
+ HumanMessage(content=m["content"])
184
+ if isinstance(m, dict) and m.get("role") == "user"
185
+ else m
186
+ for m in initial_messages
187
+ ]
188
+
189
+
190
+ def _emit_progress(
191
+ on_progress: Callable[[SubagentProgressEvent], None] | None,
192
+ event: SubagentProgressEvent,
193
+ ) -> None:
194
+ if on_progress:
195
+ on_progress(event)
196
+
197
+
198
+ def _handle_tool_start_event(
199
+ *,
200
+ ctx: SubagentContext,
201
+ event: dict[str, Any],
202
+ on_progress: Callable[[SubagentProgressEvent], None] | None,
203
+ current_step: int,
204
+ ) -> tuple[int, str]:
205
+ next_step = current_step + 1
206
+ tool_name = event.get("name", "")
207
+ _emit_progress(
208
+ on_progress,
209
+ SubagentProgressEvent(
210
+ type="tool_use",
211
+ subagent_type=ctx.subagent_type,
212
+ agent_id=ctx.agent_id,
213
+ step=next_step,
214
+ tool_name=tool_name,
215
+ summary=f"calling {tool_name}",
216
+ ),
217
+ )
218
+ return next_step, tool_name
219
+
220
+
221
+ def _handle_tool_end_event(
222
+ *,
223
+ ctx: SubagentContext,
224
+ event: dict[str, Any],
225
+ data: dict[str, Any],
226
+ transcript: SubagentTranscriptManager,
227
+ on_progress: Callable[[SubagentProgressEvent], None] | None,
228
+ step: int,
229
+ ) -> None:
230
+ tool_name = event.get("name", "")
231
+ output = data.get("output")
232
+ output_str = str(output)[:500] if output is not None else ""
233
+ msg_dict = {"role": "tool", "tool_name": tool_name, "content": output_str}
234
+ transcript.append(msg_dict)
235
+ _emit_progress(
236
+ on_progress,
237
+ SubagentProgressEvent(
238
+ type="tool_result",
239
+ subagent_type=ctx.subagent_type,
240
+ agent_id=ctx.agent_id,
241
+ step=step,
242
+ tool_name=tool_name,
243
+ summary=output_str[:200],
244
+ ),
245
+ )
246
+
247
+
248
+ def _handle_chat_model_end_event(
249
+ *,
250
+ ctx: SubagentContext,
251
+ data: dict[str, Any],
252
+ on_progress: Callable[[SubagentProgressEvent], None] | None,
253
+ collected_assistant_messages: list[Any],
254
+ total_steps: int,
255
+ ) -> int:
256
+ output = data.get("output")
257
+ if output is not None:
258
+ collected_assistant_messages.append(output)
259
+ has_tool_calls = bool(output is not None and getattr(output, "tool_calls", None))
260
+ if not has_tool_calls:
261
+ total_steps += 1
262
+ _emit_progress(
263
+ on_progress,
264
+ SubagentProgressEvent(
265
+ type="model_step",
266
+ subagent_type=ctx.subagent_type,
267
+ agent_id=ctx.agent_id,
268
+ step=total_steps,
269
+ summary="model responded",
270
+ ),
271
+ )
272
+ return total_steps
273
+
274
+
275
+ def _handle_langgraph_end_event(
276
+ *,
277
+ data: dict[str, Any],
278
+ terminal_reason: str,
279
+ child_run_id: str | None,
280
+ ) -> tuple[str, str | None]:
281
+ output = data.get("output") or {}
282
+ if not isinstance(output, dict):
283
+ return terminal_reason, child_run_id
284
+ tr = output.get("terminal_reason")
285
+ if tr:
286
+ terminal_reason = str(tr)
287
+ if child_run_id is None:
288
+ child_run_id = output.get("_run_id")
289
+ return terminal_reason, child_run_id
290
+
291
+
292
+ async def _collect_subagent_events(
293
+ *,
294
+ ctx: SubagentContext,
295
+ graph: Any,
296
+ graph_input: dict[str, Any],
297
+ run_tags: list[str],
298
+ transcript: SubagentTranscriptManager,
299
+ on_progress: Callable[[SubagentProgressEvent], None] | None,
300
+ ) -> _EventCollectionResult:
301
+ collected_assistant_messages: list[Any] = []
302
+ terminal_reason = "completed"
303
+ total_steps = 0
304
+ error_message = ""
305
+ last_tool_name = ""
306
+ child_run_id: str | None = None
307
+ tool_step = 0
308
+ try:
309
+ async for event in graph.astream_events(
310
+ graph_input,
311
+ config={"tags": run_tags, "recursion_limit": 9999},
312
+ version="v2",
313
+ ):
314
+ event_name = event.get("event", "")
315
+ data = event.get("data") or {}
316
+ if event_name == "on_tool_start":
317
+ tool_step, last_tool_name = _handle_tool_start_event(
318
+ ctx=ctx,
319
+ event=event,
320
+ on_progress=on_progress,
321
+ current_step=tool_step,
322
+ )
323
+ elif event_name == "on_tool_end":
324
+ _handle_tool_end_event(
325
+ ctx=ctx,
326
+ event=event,
327
+ data=data,
328
+ transcript=transcript,
329
+ on_progress=on_progress,
330
+ step=tool_step,
331
+ )
332
+ elif event_name == "on_chat_model_end":
333
+ total_steps = _handle_chat_model_end_event(
334
+ ctx=ctx,
335
+ data=data,
336
+ on_progress=on_progress,
337
+ collected_assistant_messages=collected_assistant_messages,
338
+ total_steps=total_steps,
339
+ )
340
+ elif event_name == "on_chain_end" and event.get("name") == "LangGraph":
341
+ terminal_reason, child_run_id = _handle_langgraph_end_event(
342
+ data=data,
343
+ terminal_reason=terminal_reason,
344
+ child_run_id=child_run_id,
345
+ )
346
+ except Exception as exc:
347
+ logger.warning(
348
+ "subagent astream_events error agent_id=%s: %s",
349
+ ctx.agent_id,
350
+ exc,
351
+ exc_info=True,
352
+ )
353
+ error_message = str(exc)
354
+ terminal_reason = "error"
355
+ return _EventCollectionResult(
356
+ collected_assistant_messages=collected_assistant_messages,
357
+ terminal_reason=terminal_reason,
358
+ total_steps=total_steps,
359
+ error_message=error_message,
360
+ last_tool_name=last_tool_name,
361
+ child_run_id=child_run_id,
362
+ tool_step=tool_step,
363
+ )
364
+
365
+
366
+ def _build_subagent_result(
367
+ *,
368
+ ctx: SubagentContext,
369
+ collected: _EventCollectionResult,
370
+ transcript: SubagentTranscriptManager,
371
+ allowed_tool_names: set[str] | None,
372
+ on_progress: Callable[[SubagentProgressEvent], None] | None,
373
+ ) -> SubagentResult:
374
+ content = _extract_final_content(collected.collected_assistant_messages, allowed_tool_names)
375
+ status = "error" if collected.terminal_reason == "error" else "completed"
376
+ transcript.flush([_to_message_dict(m) for m in collected.collected_assistant_messages])
377
+ if not content and status == "completed":
378
+ diag = f"total_steps={collected.total_steps}"
379
+ if collected.last_tool_name:
380
+ diag += f" last_tool={collected.last_tool_name}"
381
+ diag += f" terminal_reason={collected.terminal_reason}"
382
+ logger.warning(
383
+ "subagent completed with no content agent_id=%s %s",
384
+ ctx.agent_id, diag,
385
+ )
386
+ _emit_progress(
387
+ on_progress,
388
+ SubagentProgressEvent(
389
+ type="completed",
390
+ subagent_type=ctx.subagent_type,
391
+ agent_id=ctx.agent_id,
392
+ step=collected.total_steps,
393
+ summary=content[:200] if content else "done",
394
+ ),
395
+ )
396
+ return SubagentResult(
397
+ agent_id=ctx.agent_id,
398
+ content=content,
399
+ status=status,
400
+ terminal_reason=collected.terminal_reason,
401
+ total_steps=collected.total_steps,
402
+ error_message=collected.error_message,
403
+ child_run_id=collected.child_run_id,
404
+ )
405
+
406
+
407
+ async def run_subagent(
408
+ ctx: SubagentContext,
409
+ loader: Any,
410
+ model: Any | None = None,
411
+ on_progress: Callable[[SubagentProgressEvent], None] | None = None,
412
+ allowed_tool_names: set[str] | None = None,
413
+ ) -> SubagentResult:
414
+ """运行子 Agent(plain/fork 同步路径)。
415
+
416
+ Args:
417
+ ctx: 子 Agent 执行上下文
418
+ loader: 子会话 ToolRuntimeLoader
419
+ model: 语言模型(str 或 BaseChatModel),None 时继承 ctx.model
420
+ on_progress: 进度回调(Channel B),每个工具调用步骤触发
421
+ allowed_tool_names: 工具白名单,用于识别 XML 退化格式(如 <Read>...</Read>)
422
+
423
+ Returns:
424
+ SubagentResult
425
+
426
+ Raises:
427
+ RuntimeError: ctx.is_async=True 时(异步路径请调用 run_async_agent_lifecycle)
428
+ ValueError: model 与 ctx.model 均为 None 时
429
+
430
+ 不变量:无论成功/失败,try/finally 确保 transcript.cleanup() 和
431
+ cleanup_subagent_context() 被调用。
432
+ """
433
+ resolved_model = _resolve_subagent_model(ctx, model)
434
+
435
+ store = InMemoryTranscriptStore()
436
+ transcript = SubagentTranscriptManager(
437
+ store=store,
438
+ agent_id=ctx.agent_id,
439
+ parent_agent_id=ctx.parent_agent_id,
440
+ )
441
+
442
+ try:
443
+ graph = create_subagent_graph(ctx=ctx, loader=loader, model=resolved_model)
444
+ input_messages = _build_graph_input_messages(ctx.initial_messages)
445
+ graph_input = {"messages": input_messages}
446
+ run_tags = [f"subagent:{ctx.subagent_type}", f"agent_id:{ctx.agent_id}"]
447
+ collected = await _collect_subagent_events(
448
+ ctx=ctx,
449
+ graph=graph,
450
+ graph_input=graph_input,
451
+ run_tags=run_tags,
452
+ transcript=transcript,
453
+ on_progress=on_progress,
454
+ )
455
+ return _build_subagent_result(
456
+ ctx=ctx,
457
+ collected=collected,
458
+ transcript=transcript,
459
+ allowed_tool_names=allowed_tool_names,
460
+ on_progress=on_progress,
461
+ )
462
+
463
+ finally:
464
+ transcript.cleanup()
465
+ cleanup_subagent_context(ctx)
466
+
467
+
468
+ async def run_subagent_with_background_signal(
469
+ ctx: SubagentContext,
470
+ loader: Any,
471
+ model: Any | None = None,
472
+ on_progress: Callable[[SubagentProgressEvent], None] | None = None,
473
+ background_signal: asyncio.Event | None = None,
474
+ on_background_request: Callable[[SubagentContext], str] | None = None,
475
+ allowed_tool_names: set[str] | None = None,
476
+ ) -> SubagentResult | BackgroundLaunchedResult:
477
+ """同步执行中监听后台化信号(CC Promise.race 语义)。"""
478
+ agent_task = asyncio.create_task(
479
+ run_subagent(ctx=ctx, loader=loader, model=model, on_progress=on_progress, allowed_tool_names=allowed_tool_names)
480
+ )
481
+ if background_signal is None:
482
+ return await agent_task
483
+
484
+ signal_task = asyncio.create_task(background_signal.wait())
485
+ done, pending = await asyncio.wait(
486
+ {agent_task, signal_task},
487
+ return_when=asyncio.FIRST_COMPLETED,
488
+ )
489
+
490
+ if signal_task in done and signal_task.result():
491
+ agent_task.cancel()
492
+ for task in pending:
493
+ task.cancel()
494
+ if on_background_request is None:
495
+ raise RuntimeError("background signal received but no handler configured")
496
+ task_id = on_background_request(ctx)
497
+ return BackgroundLaunchedResult(
498
+ status="async_launched",
499
+ agent_id=ctx.agent_id,
500
+ task_id=task_id,
501
+ )
502
+
503
+ signal_task.cancel()
504
+ return await agent_task
@@ -0,0 +1,172 @@
1
+ """Subagent Transcript 存储与管理
2
+
3
+ 职责:管理单个子会话 transcript 生命周期
4
+ 在整体链路中的位置:runner.py 构造 SubagentTranscriptManager → append/flush/cleanup
5
+ CC 对照:src/agent/subagent/transcript.ts TranscriptStore 接口
6
+ 当前裁剪范围:v1 Protocol + 内存实现 + JSONL 文件后端
7
+ """
8
+
9
+ import json
10
+ from dataclasses import dataclass, field
11
+ from pathlib import Path
12
+ from typing import Any, Protocol, runtime_checkable
13
+
14
+
15
+ @runtime_checkable
16
+ class SubagentTranscriptStore(Protocol):
17
+ """Transcript 存储后端契约(可换实现:replay/内存/日志)"""
18
+
19
+ def write_transcript(
20
+ self,
21
+ agent_id: str,
22
+ messages: list[dict[str, Any]],
23
+ metadata: dict[str, Any] | None = None,
24
+ ) -> None:
25
+ """写入完整 transcript"""
26
+ ...
27
+
28
+ def append_message(self, agent_id: str, message: dict[str, Any]) -> None:
29
+ """追加单条消息"""
30
+ ...
31
+
32
+ def read_transcript(self, agent_id: str) -> list[dict[str, Any]]:
33
+ """读取 transcript"""
34
+ ...
35
+
36
+ def cleanup_transcript(self, agent_id: str) -> None:
37
+ """清理 transcript"""
38
+ ...
39
+
40
+
41
+ @dataclass
42
+ class SubagentTranscriptManager:
43
+ """管理单个子会话 transcript 生命周期
44
+
45
+ 协作者:依赖 SubagentTranscriptStore(注入)
46
+ 不变量:flush() 必须在 cleanup() 之前调用
47
+ """
48
+ store: SubagentTranscriptStore
49
+ agent_id: str
50
+ parent_agent_id: str | None = None
51
+ _buffer: list[dict[str, Any]] = field(default_factory=list)
52
+
53
+ def append(self, message: dict[str, Any]) -> None:
54
+ """追加消息到缓冲区并写入 store"""
55
+ self._buffer.append(message)
56
+ self.store.append_message(self.agent_id, message)
57
+
58
+ def flush(self, final_messages: list[dict[str, Any]]) -> None:
59
+ """写入完整 transcript(含元数据)"""
60
+ metadata = {"parent_agent_id": self.parent_agent_id} if self.parent_agent_id else {}
61
+ self.store.write_transcript(self.agent_id, final_messages, metadata)
62
+
63
+ def cleanup(self) -> None:
64
+ """清理缓冲区与 store"""
65
+ self._buffer.clear()
66
+ self.store.cleanup_transcript(self.agent_id)
67
+
68
+
69
+ class InMemoryTranscriptStore:
70
+ """内存实现(供单测与 stub 使用)"""
71
+
72
+ def __init__(self) -> None:
73
+ self._storage: dict[str, list[dict[str, Any]]] = {}
74
+
75
+ def write_transcript(
76
+ self,
77
+ agent_id: str,
78
+ messages: list[dict[str, Any]],
79
+ metadata: dict[str, Any] | None = None,
80
+ ) -> None:
81
+ self._storage[agent_id] = messages.copy()
82
+
83
+ def append_message(self, agent_id: str, message: dict[str, Any]) -> None:
84
+ if agent_id not in self._storage:
85
+ self._storage[agent_id] = []
86
+ self._storage[agent_id].append(message)
87
+
88
+ def read_transcript(self, agent_id: str) -> list[dict[str, Any]]:
89
+ return self._storage.get(agent_id, [])
90
+
91
+ def cleanup_transcript(self, agent_id: str) -> None:
92
+ self._storage.pop(agent_id, None)
93
+
94
+
95
+ class JsonlTranscriptStore:
96
+ """JSONL 文件实现(按 session/agent 隔离 transcript)。"""
97
+
98
+ def __init__(self, transcripts_dir: str | Path, session_id: str) -> None:
99
+ self._transcripts_dir = Path(transcripts_dir)
100
+ self._session_id = session_id
101
+
102
+ def write_transcript(
103
+ self,
104
+ agent_id: str,
105
+ messages: list[dict[str, Any]],
106
+ metadata: dict[str, Any] | None = None,
107
+ ) -> None:
108
+ path = self._resolve_path(agent_id)
109
+ path.parent.mkdir(parents=True, exist_ok=True)
110
+ with path.open("w", encoding="utf-8") as handle:
111
+ for message in messages:
112
+ handle.write(self._encode_line(message))
113
+ if metadata:
114
+ handle.write(self._encode_line({"type": "metadata", **metadata}))
115
+
116
+ def append_message(self, agent_id: str, message: dict[str, Any]) -> None:
117
+ path = self._resolve_path(agent_id)
118
+ path.parent.mkdir(parents=True, exist_ok=True)
119
+ with path.open("a", encoding="utf-8") as handle:
120
+ handle.write(self._encode_line(message))
121
+
122
+ def read_transcript(self, agent_id: str) -> list[dict[str, Any]]:
123
+ path = self._resolve_path(agent_id)
124
+ if not path.exists():
125
+ return []
126
+ messages: list[dict[str, Any]] = []
127
+ with path.open("r", encoding="utf-8") as handle:
128
+ for raw_line in handle:
129
+ decoded = self._decode_line(raw_line)
130
+ if decoded is None:
131
+ continue
132
+ if decoded.get("type") == "metadata":
133
+ continue
134
+ messages.append(decoded)
135
+ return messages
136
+
137
+ def read_transcript_after_last_boundary(self, agent_id: str) -> list[dict[str, Any]]:
138
+ """读取最后一个 compact_boundary 之后的消息(边界本身不返回)。"""
139
+ messages = self.read_transcript(agent_id)
140
+ last_boundary_index = -1
141
+ for idx, item in enumerate(messages):
142
+ if item.get("type") == "compact_boundary":
143
+ last_boundary_index = idx
144
+ if last_boundary_index < 0:
145
+ return messages
146
+ return messages[last_boundary_index + 1 :]
147
+
148
+ def cleanup_transcript(self, agent_id: str) -> None:
149
+ path = self._resolve_path(agent_id)
150
+ if path.exists():
151
+ path.unlink()
152
+ session_dir = path.parent
153
+ if session_dir.exists() and not any(session_dir.iterdir()):
154
+ session_dir.rmdir()
155
+
156
+ def _resolve_path(self, agent_id: str) -> Path:
157
+ return self._transcripts_dir / self._session_id / f"{agent_id}.jsonl"
158
+
159
+ def _encode_line(self, payload: dict[str, Any]) -> str:
160
+ return json.dumps(payload, ensure_ascii=False) + "\n"
161
+
162
+ def _decode_line(self, line: str) -> dict[str, Any] | None:
163
+ stripped = line.strip()
164
+ if stripped == "":
165
+ return None
166
+ try:
167
+ decoded = json.loads(stripped)
168
+ except json.JSONDecodeError:
169
+ return None
170
+ if not isinstance(decoded, dict):
171
+ return None
172
+ return decoded
@@ -0,0 +1,2 @@
1
+ """memory — Agent 记忆能力包入口。"""
2
+
@@ -0,0 +1,12 @@
1
+ """instruction memory — Layer 1 规则加载与提示词注入。"""
2
+
3
+ from .types import ConditionalRule, InstructionMemoryConfig, LoadedRuleSet, RuleFile, RuleLayer
4
+
5
+ __all__ = [
6
+ "RuleLayer",
7
+ "RuleFile",
8
+ "ConditionalRule",
9
+ "LoadedRuleSet",
10
+ "InstructionMemoryConfig",
11
+ ]
12
+