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,103 @@
1
+ """
2
+ task_runtime/integrations/provider_factory.py — queued provider 统一配置入口
3
+
4
+ 职责:
5
+ 为 task 侧提供一个统一的 provider 装配工厂,减少调用方手工拼装成本。
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+ from pathlib import Path
11
+ from typing import Literal
12
+
13
+ from langchain_agentx.workspace import resolve_agent_workspace_config
14
+
15
+ from .prefetch_providers import MemoryPrefetchProvider
16
+ from ..skill_prefetch import SkillPrefetchProvider
17
+ from .queued_command_provider import InMemoryQueuedCommandProvider
18
+ from .sqlite_queued_command_provider import SqliteQueuedCommandProvider
19
+ from .tool_use_summary_provider import ToolUseSummaryProvider
20
+
21
+
22
+ BackendType = Literal["in_memory", "sqlite"]
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class ProviderFactoryConfig:
27
+ backend_type: BackendType = "in_memory"
28
+ sqlite_db_path: str | None = None
29
+ workspace_root: str | None = None
30
+ agent_home: str = ".langchain_agentx"
31
+ max_queue_size: int | None = None
32
+ ttl_sec: float | None = None
33
+ enable_memory_prefetch: bool = True
34
+ enable_skill_prefetch: bool = True
35
+ enable_tool_summary: bool = True
36
+ summary_error_ratio_now_threshold: float = 0.5
37
+ summary_avg_latency_now_threshold_ms: int = 2000
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class ProviderBundle:
42
+ memory_prefetch: MemoryPrefetchProvider | None
43
+ skill_prefetch: SkillPrefetchProvider | None
44
+ tool_summary: ToolUseSummaryProvider | None
45
+
46
+ def as_list(self) -> list:
47
+ return [item for item in [self.memory_prefetch, self.skill_prefetch, self.tool_summary] if item is not None]
48
+
49
+
50
+ def build_provider_bundle(config: ProviderFactoryConfig) -> ProviderBundle:
51
+ workspace_cfg = resolve_agent_workspace_config(
52
+ workspace_root=config.workspace_root or Path.cwd(),
53
+ agent_home=config.agent_home,
54
+ )
55
+ backend = _build_backend(config)
56
+ memory_prefetch = (
57
+ MemoryPrefetchProvider(backend=backend)
58
+ if config.enable_memory_prefetch
59
+ else None
60
+ )
61
+ skill_prefetch = (
62
+ SkillPrefetchProvider(
63
+ workspace_root=workspace_cfg.workspace_root,
64
+ agent_home=workspace_cfg.agent_home,
65
+ )
66
+ if config.enable_skill_prefetch
67
+ else None
68
+ )
69
+ tool_summary = (
70
+ ToolUseSummaryProvider(
71
+ backend=backend,
72
+ error_ratio_now_threshold=config.summary_error_ratio_now_threshold,
73
+ avg_latency_now_threshold_ms=config.summary_avg_latency_now_threshold_ms,
74
+ )
75
+ if config.enable_tool_summary
76
+ else None
77
+ )
78
+ return ProviderBundle(
79
+ memory_prefetch=memory_prefetch,
80
+ skill_prefetch=skill_prefetch,
81
+ tool_summary=tool_summary,
82
+ )
83
+
84
+
85
+ def _build_backend(config: ProviderFactoryConfig):
86
+ if config.backend_type == "sqlite":
87
+ workspace_cfg = resolve_agent_workspace_config(
88
+ workspace_root=config.workspace_root or Path.cwd(),
89
+ agent_home=config.agent_home,
90
+ )
91
+ db_path = config.sqlite_db_path or str(workspace_cfg.task_runtime_db_path)
92
+ Path(db_path).expanduser().resolve().parent.mkdir(parents=True, exist_ok=True)
93
+ return SqliteQueuedCommandProvider(
94
+ db_path,
95
+ max_queue_size=config.max_queue_size,
96
+ ttl_sec=config.ttl_sec,
97
+ recover_reserved_on_startup=True,
98
+ )
99
+ return InMemoryQueuedCommandProvider(
100
+ max_queue_size=config.max_queue_size,
101
+ ttl_sec=config.ttl_sec,
102
+ )
103
+
@@ -0,0 +1,184 @@
1
+ """
2
+ task_runtime/integrations/queued_command_provider.py — 通用 queued-command provider
3
+
4
+ 职责:
5
+ 为 memory/skill prefetch 等“非 task_runtime 来源”提供统一的队列能力:
6
+ 1. enqueue:写入待注入命令
7
+ 2. build_batch:按 scope/priority reserve 批次
8
+ 3. ack_batch / nack_batch:两阶段确认
9
+ 4. has_pending:供 loop_controller 退出守卫探测
10
+
11
+ 边界:
12
+ - 仅负责 queued-command 的队列与批次确认
13
+ - 不依赖 TaskRuntime,不参与任务生命周期状态机
14
+ """
15
+ from __future__ import annotations
16
+
17
+ from typing import Any
18
+
19
+ from dataclasses import dataclass, field
20
+ from threading import RLock
21
+ from uuid import uuid4
22
+ import time
23
+
24
+ from ..core.types import QueuePriority, TaskScope
25
+ from .loop_adapter import LoopInjectionBatch
26
+
27
+
28
+ _PRIORITY_ORDER = {
29
+ QueuePriority.NOW: 0,
30
+ QueuePriority.NEXT: 1,
31
+ QueuePriority.LATER: 2,
32
+ }
33
+
34
+
35
+ @dataclass(frozen=True)
36
+ class QueuedCommandEnvelope:
37
+ command_id: str
38
+ source: str
39
+ summary: str
40
+ scope: TaskScope
41
+ priority: QueuePriority = QueuePriority.NEXT
42
+ payload: dict = field(default_factory=dict)
43
+ dedup_key: str | None = None
44
+ created_at: float = field(default_factory=time.time)
45
+
46
+
47
+ class InMemoryQueuedCommandProvider:
48
+ """最小可用 queued-command provider(内存版)。"""
49
+
50
+ def __init__(
51
+ self,
52
+ *,
53
+ max_queue_size: int | None = None,
54
+ ttl_sec: float | None = None,
55
+ ) -> None:
56
+ self._lock = RLock()
57
+ self._queue: list[QueuedCommandEnvelope] = []
58
+ self._reserved_batches: dict[str, list[QueuedCommandEnvelope]] = {}
59
+ self._max_queue_size = max_queue_size
60
+ self._ttl_sec = ttl_sec
61
+
62
+ def enqueue(self, env: QueuedCommandEnvelope) -> None:
63
+ with self._lock:
64
+ self._evict_expired_locked(now_ts=time.time())
65
+ # 可选去重:同 scope + dedup_key 仅保留最新一条
66
+ if env.dedup_key:
67
+ self._queue = [
68
+ item
69
+ for item in self._queue
70
+ if not (
71
+ item.scope.agent_id == env.scope.agent_id
72
+ and item.dedup_key == env.dedup_key
73
+ )
74
+ ]
75
+ self._queue.append(env)
76
+ self._evict_over_capacity_locked()
77
+
78
+ def build_batch(
79
+ self,
80
+ scope: TaskScope,
81
+ max_priority: QueuePriority = QueuePriority.NEXT,
82
+ *,
83
+ limit: int = 8,
84
+ ) -> LoopInjectionBatch:
85
+ if limit <= 0:
86
+ return LoopInjectionBatch(batch_id="", command_ids=[], attachment_messages=[])
87
+ threshold = _PRIORITY_ORDER[max_priority]
88
+ with self._lock:
89
+ self._evict_expired_locked(now_ts=time.time())
90
+ candidates = [
91
+ item
92
+ for item in self._queue
93
+ if item.scope.agent_id == scope.agent_id
94
+ and _PRIORITY_ORDER[item.priority] <= threshold
95
+ ]
96
+ candidates.sort(key=lambda item: _PRIORITY_ORDER[item.priority])
97
+ selected = candidates[:limit]
98
+ if not selected:
99
+ return LoopInjectionBatch(batch_id="", command_ids=[], attachment_messages=[])
100
+
101
+ selected_ids = {item.command_id for item in selected}
102
+ self._queue = [item for item in self._queue if item.command_id not in selected_ids]
103
+ batch_id = f"qbatch_{uuid4().hex[:10]}"
104
+ self._reserved_batches[batch_id] = selected
105
+ return LoopInjectionBatch(
106
+ batch_id=batch_id,
107
+ command_ids=[item.command_id for item in selected],
108
+ attachment_messages=[_to_attachment_message(item) for item in selected],
109
+ )
110
+
111
+ def ack_batch(self, batch: LoopInjectionBatch) -> None:
112
+ if not batch.batch_id:
113
+ return
114
+ with self._lock:
115
+ self._reserved_batches.pop(batch.batch_id, None)
116
+
117
+ def nack_batch(self, batch: LoopInjectionBatch, *, requeue: bool = True) -> None:
118
+ if not batch.batch_id:
119
+ return
120
+ with self._lock:
121
+ reserved = self._reserved_batches.pop(batch.batch_id, None)
122
+ if reserved and requeue:
123
+ self._queue.extend(reserved)
124
+
125
+ def has_pending(
126
+ self, scope: TaskScope, max_priority: QueuePriority = QueuePriority.NEXT
127
+ ) -> bool:
128
+ threshold = _PRIORITY_ORDER[max_priority]
129
+ with self._lock:
130
+ self._evict_expired_locked(now_ts=time.time())
131
+ return any(
132
+ item.scope.agent_id == scope.agent_id
133
+ and _PRIORITY_ORDER[item.priority] <= threshold
134
+ for item in self._queue
135
+ )
136
+
137
+ def ingest_from_messages(self, *, scope: TaskScope, messages: list[Any]) -> int:
138
+ """默认无 loop messages 桥接;子类或包装 provider 可覆盖。"""
139
+ return 0
140
+
141
+ def evict_expired(self, *, now_ts: float | None = None) -> int:
142
+ with self._lock:
143
+ return self._evict_expired_locked(now_ts=time.time() if now_ts is None else now_ts)
144
+
145
+ def _evict_over_capacity_locked(self) -> None:
146
+ if self._max_queue_size is None:
147
+ return
148
+ if self._max_queue_size < 0:
149
+ return
150
+ overflow = len(self._queue) - self._max_queue_size
151
+ if overflow <= 0:
152
+ return
153
+ # 保留高优先级:按优先级+创建时间排序,淘汰最不重要且最老的记录
154
+ self._queue.sort(
155
+ key=lambda item: (_PRIORITY_ORDER[item.priority], item.created_at)
156
+ )
157
+ # 末尾是低优先级;从末尾逆向淘汰 overflow 个
158
+ self._queue = self._queue[: self._max_queue_size]
159
+
160
+ def _evict_expired_locked(self, *, now_ts: float) -> int:
161
+ if self._ttl_sec is None or self._ttl_sec <= 0:
162
+ return 0
163
+ before = len(self._queue)
164
+ self._queue = [
165
+ item for item in self._queue if (now_ts - float(item.created_at)) < self._ttl_sec
166
+ ]
167
+ return before - len(self._queue)
168
+
169
+
170
+ def _to_attachment_message(env: QueuedCommandEnvelope) -> dict:
171
+ text = f"[queued_command][{env.source}] {env.summary}"
172
+ return {
173
+ "role": "user",
174
+ "content": [{"type": "text", "text": text}],
175
+ "metadata": {
176
+ "queued_command": {
177
+ "command_id": env.command_id,
178
+ "source": env.source,
179
+ "priority": env.priority.value,
180
+ "payload": env.payload,
181
+ }
182
+ },
183
+ }
184
+
@@ -0,0 +1,338 @@
1
+ """
2
+ task_runtime/integrations/sqlite_queued_command_provider.py — 持久化 queued-command provider
3
+
4
+ 职责:
5
+ 提供 queued command 的 SQLite 持久化实现,支持:
6
+ 1. enqueue/build_batch/ack/nack/has_pending
7
+ 2. TTL 回收与容量回收
8
+ 3. 进程重启后的 reserved 恢复(crash-recovery)
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import sqlite3
14
+ import time
15
+ from threading import RLock
16
+ from typing import Any
17
+ from uuid import uuid4
18
+
19
+ from ..core.types import QueuePriority, TaskScope
20
+ from .loop_adapter import LoopInjectionBatch
21
+ from .queued_command_provider import QueuedCommandEnvelope
22
+
23
+
24
+ _PRIORITY_ORDER = {
25
+ QueuePriority.NOW: 0,
26
+ QueuePriority.NEXT: 1,
27
+ QueuePriority.LATER: 2,
28
+ }
29
+
30
+
31
+ class SqliteQueuedCommandProvider:
32
+ def __init__(
33
+ self,
34
+ db_path: str,
35
+ *,
36
+ max_queue_size: int | None = None,
37
+ ttl_sec: float | None = None,
38
+ recover_reserved_on_startup: bool = True,
39
+ ) -> None:
40
+ self._lock = RLock()
41
+ self._conn = sqlite3.connect(db_path, check_same_thread=False)
42
+ self._conn.row_factory = sqlite3.Row
43
+ self._max_queue_size = max_queue_size
44
+ self._ttl_sec = ttl_sec
45
+ self._init_schema()
46
+ if recover_reserved_on_startup:
47
+ self._recover_reserved()
48
+
49
+ def _init_schema(self) -> None:
50
+ with self._conn:
51
+ self._conn.execute(
52
+ """
53
+ CREATE TABLE IF NOT EXISTS queued_commands (
54
+ command_id TEXT PRIMARY KEY,
55
+ source TEXT NOT NULL,
56
+ summary TEXT NOT NULL,
57
+ scope_agent_id TEXT,
58
+ priority TEXT NOT NULL,
59
+ payload_json TEXT NOT NULL,
60
+ dedup_key TEXT,
61
+ created_at REAL NOT NULL
62
+ )
63
+ """
64
+ )
65
+ self._conn.execute(
66
+ """
67
+ CREATE TABLE IF NOT EXISTS reserved_commands (
68
+ batch_id TEXT NOT NULL,
69
+ command_id TEXT NOT NULL,
70
+ source TEXT NOT NULL,
71
+ summary TEXT NOT NULL,
72
+ scope_agent_id TEXT,
73
+ priority TEXT NOT NULL,
74
+ payload_json TEXT NOT NULL,
75
+ dedup_key TEXT,
76
+ created_at REAL NOT NULL,
77
+ PRIMARY KEY (batch_id, command_id)
78
+ )
79
+ """
80
+ )
81
+
82
+ def enqueue(self, env: QueuedCommandEnvelope) -> None:
83
+ with self._lock, self._conn:
84
+ self._evict_expired_locked(now_ts=time.time())
85
+ if env.dedup_key:
86
+ self._conn.execute(
87
+ "DELETE FROM queued_commands WHERE scope_agent_id IS ? AND dedup_key = ?",
88
+ (env.scope.agent_id, env.dedup_key),
89
+ )
90
+ self._conn.execute(
91
+ """
92
+ INSERT OR REPLACE INTO queued_commands(
93
+ command_id, source, summary, scope_agent_id, priority, payload_json, dedup_key, created_at
94
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
95
+ """,
96
+ (
97
+ env.command_id,
98
+ env.source,
99
+ env.summary,
100
+ env.scope.agent_id,
101
+ env.priority.value,
102
+ json.dumps(env.payload, ensure_ascii=True),
103
+ env.dedup_key,
104
+ float(env.created_at),
105
+ ),
106
+ )
107
+ self._evict_over_capacity_locked()
108
+
109
+ def build_batch(
110
+ self,
111
+ scope: TaskScope,
112
+ max_priority: QueuePriority = QueuePriority.NEXT,
113
+ *,
114
+ limit: int = 8,
115
+ ) -> LoopInjectionBatch:
116
+ if limit <= 0:
117
+ return LoopInjectionBatch(batch_id="", command_ids=[], attachment_messages=[])
118
+ threshold = _PRIORITY_ORDER[max_priority]
119
+ with self._lock, self._conn:
120
+ self._evict_expired_locked(now_ts=time.time())
121
+ rows = self._conn.execute(
122
+ """
123
+ SELECT * FROM queued_commands
124
+ WHERE scope_agent_id IS ?
125
+ ORDER BY
126
+ CASE priority
127
+ WHEN 'now' THEN 0
128
+ WHEN 'next' THEN 1
129
+ ELSE 2
130
+ END ASC,
131
+ created_at ASC
132
+ """,
133
+ (scope.agent_id,),
134
+ ).fetchall()
135
+ selected = [
136
+ row
137
+ for row in rows
138
+ if _PRIORITY_ORDER[QueuePriority(str(row["priority"]))] <= threshold
139
+ ][:limit]
140
+ if not selected:
141
+ return LoopInjectionBatch(batch_id="", command_ids=[], attachment_messages=[])
142
+
143
+ batch_id = f"sqbatch_{uuid4().hex[:10]}"
144
+ command_ids = [str(item["command_id"]) for item in selected]
145
+ for row in selected:
146
+ self._conn.execute(
147
+ """
148
+ INSERT OR REPLACE INTO reserved_commands(
149
+ batch_id, command_id, source, summary, scope_agent_id, priority, payload_json, dedup_key, created_at
150
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
151
+ """,
152
+ (
153
+ batch_id,
154
+ row["command_id"],
155
+ row["source"],
156
+ row["summary"],
157
+ row["scope_agent_id"],
158
+ row["priority"],
159
+ row["payload_json"],
160
+ row["dedup_key"],
161
+ row["created_at"],
162
+ ),
163
+ )
164
+ placeholders = ",".join("?" for _ in command_ids)
165
+ self._conn.execute(
166
+ f"DELETE FROM queued_commands WHERE command_id IN ({placeholders})",
167
+ command_ids,
168
+ )
169
+ messages = [
170
+ _to_attachment_message(_row_to_env(row))
171
+ for row in selected
172
+ ]
173
+ return LoopInjectionBatch(
174
+ batch_id=batch_id,
175
+ command_ids=command_ids,
176
+ attachment_messages=messages,
177
+ )
178
+
179
+ def ack_batch(self, batch: LoopInjectionBatch) -> None:
180
+ if not batch.batch_id:
181
+ return
182
+ with self._lock, self._conn:
183
+ self._conn.execute(
184
+ "DELETE FROM reserved_commands WHERE batch_id = ?",
185
+ (batch.batch_id,),
186
+ )
187
+
188
+ def nack_batch(self, batch: LoopInjectionBatch, *, requeue: bool = True) -> None:
189
+ if not batch.batch_id:
190
+ return
191
+ with self._lock, self._conn:
192
+ rows = self._conn.execute(
193
+ "SELECT * FROM reserved_commands WHERE batch_id = ?",
194
+ (batch.batch_id,),
195
+ ).fetchall()
196
+ if requeue:
197
+ for row in rows:
198
+ self._conn.execute(
199
+ """
200
+ INSERT OR REPLACE INTO queued_commands(
201
+ command_id, source, summary, scope_agent_id, priority, payload_json, dedup_key, created_at
202
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
203
+ """,
204
+ (
205
+ row["command_id"],
206
+ row["source"],
207
+ row["summary"],
208
+ row["scope_agent_id"],
209
+ row["priority"],
210
+ row["payload_json"],
211
+ row["dedup_key"],
212
+ row["created_at"],
213
+ ),
214
+ )
215
+ self._conn.execute(
216
+ "DELETE FROM reserved_commands WHERE batch_id = ?",
217
+ (batch.batch_id,),
218
+ )
219
+
220
+ def has_pending(
221
+ self, scope: TaskScope, max_priority: QueuePriority = QueuePriority.NEXT
222
+ ) -> bool:
223
+ threshold = _PRIORITY_ORDER[max_priority]
224
+ with self._lock:
225
+ self._evict_expired_locked(now_ts=time.time())
226
+ rows = self._conn.execute(
227
+ "SELECT priority FROM queued_commands WHERE scope_agent_id IS ?",
228
+ (scope.agent_id,),
229
+ ).fetchall()
230
+ return any(
231
+ _PRIORITY_ORDER[QueuePriority(str(item["priority"]))] <= threshold
232
+ for item in rows
233
+ )
234
+
235
+ def ingest_from_messages(self, *, scope: TaskScope, messages: list[Any]) -> int:
236
+ return 0
237
+
238
+ def evict_expired(self, *, now_ts: float | None = None) -> int:
239
+ with self._lock:
240
+ return self._evict_expired_locked(now_ts=time.time() if now_ts is None else now_ts)
241
+
242
+ def close(self) -> None:
243
+ with self._lock:
244
+ self._conn.close()
245
+
246
+ def _recover_reserved(self) -> None:
247
+ with self._lock, self._conn:
248
+ rows = self._conn.execute("SELECT * FROM reserved_commands").fetchall()
249
+ for row in rows:
250
+ self._conn.execute(
251
+ """
252
+ INSERT OR REPLACE INTO queued_commands(
253
+ command_id, source, summary, scope_agent_id, priority, payload_json, dedup_key, created_at
254
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
255
+ """,
256
+ (
257
+ row["command_id"],
258
+ row["source"],
259
+ row["summary"],
260
+ row["scope_agent_id"],
261
+ row["priority"],
262
+ row["payload_json"],
263
+ row["dedup_key"],
264
+ row["created_at"],
265
+ ),
266
+ )
267
+ self._conn.execute("DELETE FROM reserved_commands")
268
+
269
+ def _evict_expired_locked(self, *, now_ts: float) -> int:
270
+ if self._ttl_sec is None or self._ttl_sec <= 0:
271
+ return 0
272
+ cutoff = float(now_ts) - float(self._ttl_sec)
273
+ with self._conn:
274
+ cur = self._conn.execute(
275
+ "DELETE FROM queued_commands WHERE created_at <= ?",
276
+ (cutoff,),
277
+ )
278
+ return int(cur.rowcount or 0)
279
+
280
+ def _evict_over_capacity_locked(self) -> None:
281
+ if self._max_queue_size is None or self._max_queue_size < 0:
282
+ return
283
+ total = int(self._conn.execute("SELECT COUNT(*) FROM queued_commands").fetchone()[0])
284
+ overflow = total - self._max_queue_size
285
+ if overflow <= 0:
286
+ return
287
+ rows = self._conn.execute(
288
+ """
289
+ SELECT command_id
290
+ FROM queued_commands
291
+ ORDER BY
292
+ CASE priority
293
+ WHEN 'now' THEN 0
294
+ WHEN 'next' THEN 1
295
+ ELSE 2
296
+ END ASC,
297
+ created_at ASC
298
+ """
299
+ ).fetchall()
300
+ keep_ids = [str(item["command_id"]) for item in rows[: self._max_queue_size]]
301
+ if not keep_ids:
302
+ self._conn.execute("DELETE FROM queued_commands")
303
+ return
304
+ placeholders = ",".join("?" for _ in keep_ids)
305
+ self._conn.execute(
306
+ f"DELETE FROM queued_commands WHERE command_id NOT IN ({placeholders})",
307
+ keep_ids,
308
+ )
309
+
310
+
311
+ def _row_to_env(row: sqlite3.Row) -> QueuedCommandEnvelope:
312
+ return QueuedCommandEnvelope(
313
+ command_id=str(row["command_id"]),
314
+ source=str(row["source"]),
315
+ summary=str(row["summary"]),
316
+ scope=TaskScope(agent_id=row["scope_agent_id"]),
317
+ priority=QueuePriority(str(row["priority"])),
318
+ payload=json.loads(row["payload_json"] or "{}"),
319
+ dedup_key=row["dedup_key"],
320
+ created_at=float(row["created_at"]),
321
+ )
322
+
323
+
324
+ def _to_attachment_message(env: QueuedCommandEnvelope) -> dict:
325
+ text = f"[queued_command][{env.source}] {env.summary}"
326
+ return {
327
+ "role": "user",
328
+ "content": [{"type": "text", "text": text}],
329
+ "metadata": {
330
+ "queued_command": {
331
+ "command_id": env.command_id,
332
+ "source": env.source,
333
+ "priority": env.priority.value,
334
+ "payload": env.payload,
335
+ }
336
+ },
337
+ }
338
+