sgar 0.1.3__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 (393) hide show
  1. core/__init__.py +7 -0
  2. core/cc/__init__.py +119 -0
  3. core/cc/agents/__init__.py +10 -0
  4. core/cc/agents/agent_tool.py +320 -0
  5. core/cc/agents/backends/__init__.py +11 -0
  6. core/cc/agents/backends/base.py +62 -0
  7. core/cc/agents/backends/in_process.py +126 -0
  8. core/cc/agents/backends/local_subprocess.py +332 -0
  9. core/cc/agents/definitions.py +25 -0
  10. core/cc/agents/runtime.py +208 -0
  11. core/cc/agents/runtime_registry.py +88 -0
  12. core/cc/agents/runtime_transport.py +66 -0
  13. core/cc/agents/swarm/__init__.py +3 -0
  14. core/cc/agents/swarm/mailbox.py +310 -0
  15. core/cc/agents/task_manager.py +101 -0
  16. core/cc/agents/task_model.py +106 -0
  17. core/cc/agents/task_state_store.py +149 -0
  18. core/cc/api.py +413 -0
  19. core/cc/artifact_state.py +156 -0
  20. core/cc/audit.py +255 -0
  21. core/cc/command_runner.py +331 -0
  22. core/cc/config.py +830 -0
  23. core/cc/conversation/__init__.py +47 -0
  24. core/cc/conversation/agent_mode_strategy.py +129 -0
  25. core/cc/conversation/compact.py +69 -0
  26. core/cc/conversation/context_assembler.py +142 -0
  27. core/cc/conversation/llm_adapter.py +410 -0
  28. core/cc/conversation/message_store.py +98 -0
  29. core/cc/conversation/middleware.py +288 -0
  30. core/cc/conversation/mode_strategy.py +280 -0
  31. core/cc/conversation/models.py +73 -0
  32. core/cc/conversation/prompt_builder.py +162 -0
  33. core/cc/conversation/prompt_catalog.py +98 -0
  34. core/cc/conversation/protocol.py +28 -0
  35. core/cc/conversation/query_engine.py +483 -0
  36. core/cc/conversation/query_loop.py +1173 -0
  37. core/cc/conversation/query_loop_followup.py +105 -0
  38. core/cc/conversation/query_loop_implementation_sync.py +95 -0
  39. core/cc/conversation/query_loop_mode_transitions.py +127 -0
  40. core/cc/conversation/query_loop_tool_events.py +94 -0
  41. core/cc/conversation/session.py +119 -0
  42. core/cc/conversation/session_journal.py +70 -0
  43. core/cc/conversation/strategy_common.py +93 -0
  44. core/cc/conversation/tool_ledger.py +179 -0
  45. core/cc/conversation/turn_pipeline.py +315 -0
  46. core/cc/editing/__init__.py +16 -0
  47. core/cc/editing/facade.py +288 -0
  48. core/cc/editing/file_state.py +61 -0
  49. core/cc/editing/requests.py +76 -0
  50. core/cc/editing/rollback.py +150 -0
  51. core/cc/editing/validator.py +107 -0
  52. core/cc/engine_factory.py +116 -0
  53. core/cc/errors.py +46 -0
  54. core/cc/jsonl.py +105 -0
  55. core/cc/llm.py +112 -0
  56. core/cc/memory/__init__.py +26 -0
  57. core/cc/memory/base.py +40 -0
  58. core/cc/memory/extractor.py +257 -0
  59. core/cc/memory/models.py +133 -0
  60. core/cc/memory/noop_provider.py +51 -0
  61. core/cc/memory/policy.py +70 -0
  62. core/cc/memory/registry.py +30 -0
  63. core/cc/memory/runtime.py +289 -0
  64. core/cc/memory/serializer.py +53 -0
  65. core/cc/observability.py +54 -0
  66. core/cc/plan.py +99 -0
  67. core/cc/prompt_cc_review.md +100 -0
  68. core/cc/prompts/agents/worker.en.md +3 -0
  69. core/cc/prompts/agents/worker.zh.md +3 -0
  70. core/cc/prompts/system/agent_mode.en.md +34 -0
  71. core/cc/prompts/system/agent_mode.zh.md +34 -0
  72. core/cc/prompts/system/agent_mode_followup.en.md +8 -0
  73. core/cc/prompts/system/agent_mode_followup.zh.md +8 -0
  74. core/cc/prompts/system/ask_mode.en.md +30 -0
  75. core/cc/prompts/system/ask_mode.zh.md +30 -0
  76. core/cc/prompts/system/code_build.en.md +36 -0
  77. core/cc/prompts/system/code_build.zh.md +36 -0
  78. core/cc/prompts/system/coordinator.en.md +3 -0
  79. core/cc/prompts/system/coordinator.zh.md +3 -0
  80. core/cc/prompts/system/default.en.md +36 -0
  81. core/cc/prompts/system/default.zh.md +36 -0
  82. core/cc/prompts/system/doc_mode.en.md +37 -0
  83. core/cc/prompts/system/doc_mode.zh.md +37 -0
  84. core/cc/prompts/system/implementation_followup.en.md +6 -0
  85. core/cc/prompts/system/implementation_followup.zh.md +6 -0
  86. core/cc/prompts/system/implementation_task_sync_followup.en.md +8 -0
  87. core/cc/prompts/system/implementation_task_sync_followup.zh.md +8 -0
  88. core/cc/prompts/system/plan_implementation.en.md +32 -0
  89. core/cc/prompts/system/plan_implementation.zh.md +32 -0
  90. core/cc/prompts/system/plan_mode.en.md +52 -0
  91. core/cc/prompts/system/plan_mode.zh.md +52 -0
  92. core/cc/prompts/system/query_continue.en.md +1 -0
  93. core/cc/prompts/system/query_continue.zh.md +1 -0
  94. core/cc/prompts/system/render_followup.en.md +5 -0
  95. core/cc/prompts/system/render_followup.zh.md +5 -0
  96. core/cc/prompts/system/spec_mode.en.md +41 -0
  97. core/cc/prompts/system/spec_mode.zh.md +41 -0
  98. core/cc/prompts/system/spec_render.en.md +35 -0
  99. core/cc/prompts/system/spec_render.zh.md +35 -0
  100. core/cc/prompts/system/swarm_planner.en.md +35 -0
  101. core/cc/prompts/system/swarm_planner.zh.md +35 -0
  102. core/cc/prompts/system/tool_followup.en.md +1 -0
  103. core/cc/prompts/system/tool_followup.zh.md +1 -0
  104. core/cc/prompts/tools/file_edit.en.md +3 -0
  105. core/cc/prompts/tools/file_edit.zh.md +3 -0
  106. core/cc/providers.py +215 -0
  107. core/cc/runtime.py +29 -0
  108. core/cc/safety/__init__.py +11 -0
  109. core/cc/safety/classifier.py +82 -0
  110. core/cc/safety/command_rules.py +170 -0
  111. core/cc/safety/decision.py +17 -0
  112. core/cc/safety/file_rules.py +32 -0
  113. core/cc/safety/permission_mode.py +35 -0
  114. core/cc/specs.py +99 -0
  115. core/cc/structured_flow.py +548 -0
  116. core/cc/tools/__init__.py +59 -0
  117. core/cc/tools/base.py +94 -0
  118. core/cc/tools/builtin.py +80 -0
  119. core/cc/tools/context.py +73 -0
  120. core/cc/tools/enter_plan_mode.py +68 -0
  121. core/cc/tools/enter_spec_mode.py +68 -0
  122. core/cc/tools/executor.py +107 -0
  123. core/cc/tools/exit_plan_mode.py +95 -0
  124. core/cc/tools/exit_spec_mode.py +95 -0
  125. core/cc/tools/file_edit.py +124 -0
  126. core/cc/tools/file_read.py +139 -0
  127. core/cc/tools/file_write.py +105 -0
  128. core/cc/tools/glob_tool.py +79 -0
  129. core/cc/tools/grep_tool.py +453 -0
  130. core/cc/tools/memory.py +145 -0
  131. core/cc/tools/memory_fact.py +114 -0
  132. core/cc/tools/memory_search.py +62 -0
  133. core/cc/tools/memory_status.py +49 -0
  134. core/cc/tools/memory_store.py +72 -0
  135. core/cc/tools/orchestrator.py +249 -0
  136. core/cc/tools/plan_artifact_write.py +210 -0
  137. core/cc/tools/powershell.py +65 -0
  138. core/cc/tools/registry.py +29 -0
  139. core/cc/tools/result_mapper.py +47 -0
  140. core/cc/tools/run_tests.py +118 -0
  141. core/cc/tools/send_message.py +87 -0
  142. core/cc/tools/shell.py +113 -0
  143. core/cc/tools/spec_artifact_write.py +159 -0
  144. core/cc/tools/task_stop.py +77 -0
  145. core/cc/tools/todo_write.py +84 -0
  146. core/cc/tools/worktree.py +152 -0
  147. core/cc/usage.md +370 -0
  148. core/ccx/__init__.py +46 -0
  149. core/ccx/agents/__init__.py +17 -0
  150. core/ccx/agents/cc_agent.py +1149 -0
  151. core/ccx/agents/ccx_research_tool.py +304 -0
  152. core/ccx/agents/ccx_sgar_tool.py +278 -0
  153. core/ccx/agents/ccx_spawn_tool.py +357 -0
  154. core/ccx/agents/ccx_tool.py +571 -0
  155. core/ccx/agents/ctx_search_tool.py +372 -0
  156. core/ccx/agents/event_bridge.py +305 -0
  157. core/ccx/agents/goal_prompts.py +260 -0
  158. core/ccx/agents/governed_goal.py +1388 -0
  159. core/ccx/agents/governed_run.py +444 -0
  160. core/ccx/agents/governed_spawn.py +470 -0
  161. core/ccx/agents/metadata_inheritance.py +143 -0
  162. core/ccx/agents/read_only_runner.py +178 -0
  163. core/ccx/agents/research_runner.py +340 -0
  164. core/ccx/agents/subagent.py +216 -0
  165. core/ccx/agents/swarm/__init__.py +18 -0
  166. core/ccx/agents/swarm/coordinator.py +379 -0
  167. core/ccx/agents/swarm/mailbox_bridge.py +155 -0
  168. core/ccx/agents/swarm/team_runtime.py +365 -0
  169. core/ccx/agents/task_manager.py +402 -0
  170. core/ccx/api.py +2346 -0
  171. core/ccx/audit/__init__.py +95 -0
  172. core/ccx/audit/check_template.py +61 -0
  173. core/ccx/audit/code_task.py +320 -0
  174. core/ccx/audit/contract.py +95 -0
  175. core/ccx/audit/finding_ledger.py +143 -0
  176. core/ccx/audit/gitdiff.py +208 -0
  177. core/ccx/audit/ledger.py +92 -0
  178. core/ccx/audit/mutation.py +258 -0
  179. core/ccx/audit/regression_capture.py +276 -0
  180. core/ccx/audit/wiring.py +119 -0
  181. core/ccx/conversation/__init__.py +15 -0
  182. core/ccx/llm_monitor.py +1002 -0
  183. core/ccx/memory/__init__.py +36 -0
  184. core/ccx/memory/inject.py +32 -0
  185. core/ccx/memory/models.py +292 -0
  186. core/ccx/memory/recall.py +146 -0
  187. core/ccx/memory/store.py +272 -0
  188. core/ccx/memory/summarizer.py +174 -0
  189. core/ccx/modes/__init__.py +50 -0
  190. core/ccx/modes/_goal.py +19 -0
  191. core/ccx/modes/_paths.py +14 -0
  192. core/ccx/modes/_sgar_command_helpers.py +611 -0
  193. core/ccx/modes/_text_masking.py +83 -0
  194. core/ccx/modes/agent.py +245 -0
  195. core/ccx/modes/artifacts.py +189 -0
  196. core/ccx/modes/ask.py +256 -0
  197. core/ccx/modes/blueprint.py +82 -0
  198. core/ccx/modes/diagnostics.py +259 -0
  199. core/ccx/modes/doc.py +4825 -0
  200. core/ccx/modes/llm_client.py +223 -0
  201. core/ccx/modes/parsing.py +216 -0
  202. core/ccx/modes/plan.py +550 -0
  203. core/ccx/modes/prompts/__init__.py +234 -0
  204. core/ccx/modes/prompts/agent.toml +13 -0
  205. core/ccx/modes/prompts/doc_decompose.toml +71 -0
  206. core/ccx/modes/prompts/doc_investigator.toml +293 -0
  207. core/ccx/modes/prompts/doc_prose_to_json.toml +85 -0
  208. core/ccx/modes/prompts/doc_surveyor.toml +103 -0
  209. core/ccx/modes/prompts/plan.toml +40 -0
  210. core/ccx/modes/prompts/spec.toml +28 -0
  211. core/ccx/modes/prompts/watch_analyzer.toml +179 -0
  212. core/ccx/modes/prompts/watch_fixer.toml +74 -0
  213. core/ccx/modes/sgarx.py +83 -0
  214. core/ccx/modes/spec.py +375 -0
  215. core/ccx/modes/watch.py +2220 -0
  216. core/ccx/modes/watch_checks.py +786 -0
  217. core/ccx/mypy.ini +32 -0
  218. core/ccx/prompt_ccx_self_review.md +103 -0
  219. core/ccx/prompts.py +42 -0
  220. core/ccx/report.py +950 -0
  221. core/ccx/runtime.py +1015 -0
  222. core/ccx/services/__init__.py +27 -0
  223. core/ccx/services/cost_events.py +60 -0
  224. core/ccx/services/findings_collector.py +83 -0
  225. core/ccx/services/governance_events.py +132 -0
  226. core/ccx/services/repository_outline.py +152 -0
  227. core/ccx/services/steer_inbox.py +140 -0
  228. core/ccx/sgar/__init__.py +53 -0
  229. core/ccx/sgar/__main__.py +5 -0
  230. core/ccx/sgar/autobuild.py +189 -0
  231. core/ccx/sgar/checks.py +302 -0
  232. core/ccx/sgar/cli.py +325 -0
  233. core/ccx/sgar/missions.py +474 -0
  234. core/ccx/sgar/models.py +208 -0
  235. core/ccx/sgar/runtime.py +906 -0
  236. core/ccx/sgar/store.py +381 -0
  237. core/ccx/sgar/tracing.py +212 -0
  238. core/ccx/sgar/validation.py +247 -0
  239. core/ccx/sgarx/__init__.py +13 -0
  240. core/ccx/sgarx/runtime.py +661 -0
  241. core/ccx/sgarx/store.py +26 -0
  242. core/ccx/structured_flow.py +346 -0
  243. core/ccx/templates/report.html.j2 +547 -0
  244. core/ccx/watch.py +1326 -0
  245. core/deepstack_v5/__init__.py +66 -0
  246. core/deepstack_v5/config.py +97 -0
  247. core/deepstack_v5/control/__init__.py +0 -0
  248. core/deepstack_v5/control/budget.py +141 -0
  249. core/deepstack_v5/control/controller.py +159 -0
  250. core/deepstack_v5/control/escalation.py +109 -0
  251. core/deepstack_v5/engine.py +1530 -0
  252. core/deepstack_v5/events.py +164 -0
  253. core/deepstack_v5/execution/__init__.py +0 -0
  254. core/deepstack_v5/execution/assignment.py +121 -0
  255. core/deepstack_v5/execution/dispatch_context.py +94 -0
  256. core/deepstack_v5/execution/dispatcher.py +764 -0
  257. core/deepstack_v5/execution/graph.py +346 -0
  258. core/deepstack_v5/execution/node.py +214 -0
  259. core/deepstack_v5/execution/toolcall.py +152 -0
  260. core/deepstack_v5/knowledge/__init__.py +0 -0
  261. core/deepstack_v5/knowledge/claims.py +254 -0
  262. core/deepstack_v5/knowledge/compaction.py +209 -0
  263. core/deepstack_v5/memory/__init__.py +48 -0
  264. core/deepstack_v5/memory/content_store.py +816 -0
  265. core/deepstack_v5/memory/priority.py +122 -0
  266. core/deepstack_v5/memory/resume.py +128 -0
  267. core/deepstack_v5/memory/snapshot.py +262 -0
  268. core/deepstack_v5/persistence/__init__.py +31 -0
  269. core/deepstack_v5/persistence/db.py +436 -0
  270. core/deepstack_v5/persistence/file_backend.py +512 -0
  271. core/deepstack_v5/persistence/outbox.py +98 -0
  272. core/deepstack_v5/persistence/stores.py +1094 -0
  273. core/deepstack_v5/runtime.py +210 -0
  274. core/deepstack_v5/types.py +454 -0
  275. core/llms/__init__.py +1 -0
  276. core/llms/_deepseek_stream.py +400 -0
  277. core/llms/_llm_api_client.py +600 -0
  278. core/llms/ali_deep_seek_client.py +16 -0
  279. core/llms/ali_deep_seek_r1_client.py +16 -0
  280. core/llms/ark_client.py +16 -0
  281. core/llms/aws_deepseek_client.py +18 -0
  282. core/llms/aws_deepseek_r1_client.py +19 -0
  283. core/llms/azure_deep_seek_client.py +132 -0
  284. core/llms/baichuan_client.py +129 -0
  285. core/llms/bce_deep_seek_client.py +20 -0
  286. core/llms/bce_deep_seek_r1_client.py +20 -0
  287. core/llms/claude_aws_client.py +676 -0
  288. core/llms/claude_client.py +365 -0
  289. core/llms/deepbricks_client.py +16 -0
  290. core/llms/doubao_client.py +238 -0
  291. core/llms/dummy_client.py +32 -0
  292. core/llms/ernie_client.py +277 -0
  293. core/llms/fallback_client.py +181 -0
  294. core/llms/gemini2_client.py +385 -0
  295. core/llms/gemini_client.py +596 -0
  296. core/llms/gemini_pro_client.py +18 -0
  297. core/llms/glm_client.py +469 -0
  298. core/llms/glm_free_client.py +14 -0
  299. core/llms/glm_openai_client.py +19 -0
  300. core/llms/healer_alpha_client.py +16 -0
  301. core/llms/hunter_alpha_client.py +16 -0
  302. core/llms/hunyuan_client.py +266 -0
  303. core/llms/infini_deep_seek_client.py +23 -0
  304. core/llms/infini_deep_seek_r1_client.py +19 -0
  305. core/llms/kimi_client.py +21 -0
  306. core/llms/llm_factory.py +98 -0
  307. core/llms/mi_client.py +21 -0
  308. core/llms/mimo_client.py +16 -0
  309. core/llms/mini_max_client.py +308 -0
  310. core/llms/mini_max_pro.py +174 -0
  311. core/llms/mini_max_text_client.py +11 -0
  312. core/llms/minimax_r1_client.py +19 -0
  313. core/llms/minmax_m2_client.py +21 -0
  314. core/llms/moonshot_client.py +359 -0
  315. core/llms/multi_claude.py +456 -0
  316. core/llms/ollama_ds32b_client.py +21 -0
  317. core/llms/open_router_client.py +16 -0
  318. core/llms/openai_client.py +555 -0
  319. core/llms/openai_http_client.py +806 -0
  320. core/llms/ppio_claude_client.py +19 -0
  321. core/llms/ppio_claude_sonnet_client.py +19 -0
  322. core/llms/ppio_client.py +18 -0
  323. core/llms/ppio_cluade_opus_client.py +19 -0
  324. core/llms/ppio_deep_seek_client.py +18 -0
  325. core/llms/ppio_deep_seek_r1_cleint.py +19 -0
  326. core/llms/ppio_gemini_pro_client.py +19 -0
  327. core/llms/ppio_openai_client.py +19 -0
  328. core/llms/ppio_r_client.py +19 -0
  329. core/llms/px_client.py +442 -0
  330. core/llms/qianwen14_client.py +15 -0
  331. core/llms/qianwen32_client.py +15 -0
  332. core/llms/qianwen_client.py +442 -0
  333. core/llms/qianwen_coder_client.py +16 -0
  334. core/llms/qianwen_coder_plus_client.py +15 -0
  335. core/llms/qianwen_coder_turbo_client.py +15 -0
  336. core/llms/qianwen_plus.py +17 -0
  337. core/llms/qianwen_qwq_client.py +15 -0
  338. core/llms/qianwen_turbo.py +26 -0
  339. core/llms/qiniu_deep_seek_client.py +27 -0
  340. core/llms/qiniu_deep_seek_r1_client.py +23 -0
  341. core/llms/qwq_client.py +18 -0
  342. core/llms/sense_deep_seek_client.py +20 -0
  343. core/llms/sense_deep_seek_r1_client.py +24 -0
  344. core/llms/silicon_deep_seek_client.py +18 -0
  345. core/llms/silicon_deep_seek_r1_client.py +19 -0
  346. core/llms/simple_azure.py +336 -0
  347. core/llms/simple_claude.py +389 -0
  348. core/llms/simple_deep_seek_client.py +846 -0
  349. core/llms/simple_deep_seek_client_chat.py +116 -0
  350. core/llms/simple_deep_seek_client_reasoning.py +88 -0
  351. core/llms/simple_deep_seek_client_speciale.py +20 -0
  352. core/llms/simple_doubao_client.py +241 -0
  353. core/llms/spark_client.py +16 -0
  354. core/llms/tencent_deep_seek_client.py +21 -0
  355. core/llms/tencent_deep_seek_r1_client.py +23 -0
  356. core/llms/volc_deep_seek_client.py +19 -0
  357. core/llms/volc_deep_seek_r1_client.py +19 -0
  358. core/llms/zero1_improver_client.py +17 -0
  359. core/utils/CODE_MODIFICATION_TOOLS.md +628 -0
  360. core/utils/CODE_UPDATE_QUICKSTART.md +409 -0
  361. core/utils/FEE_READER_USAGE.md +270 -0
  362. core/utils/FIX_LLM_CLIENT_CHAT_METHOD.md +257 -0
  363. core/utils/UNIFIED_LOGGER_README.md +278 -0
  364. core/utils/__init__.py +43 -0
  365. core/utils/_legacy/llm_ast_editor.md +943 -0
  366. core/utils/autonomous_code_agent.md +558 -0
  367. core/utils/code_editor.md +340 -0
  368. core/utils/common.py +47 -0
  369. core/utils/config_setting.py +52 -0
  370. core/utils/editor_fallback.py +84 -0
  371. core/utils/error_classifier_helper.py +353 -0
  372. core/utils/handle_max_tokens.py +63 -0
  373. core/utils/how_to_use_code_editor.md +792 -0
  374. core/utils/json_from_text.py +694 -0
  375. core/utils/llm_block_editor.py +3204 -0
  376. core/utils/llm_block_editor_lnfree.py +1096 -0
  377. core/utils/llm_code_editor.py +1447 -0
  378. core/utils/log.py +250 -0
  379. core/utils/prompt_language.py +81 -0
  380. core/utils/rate_limit.py +35 -0
  381. core/utils/retry.py +19 -0
  382. core/utils/robust_llm_editor.py +1009 -0
  383. core/utils/single_ton.py +25 -0
  384. core/utils/smart_llm_editor_v2.py +1565 -0
  385. core/utils/source_code_manager.py +363 -0
  386. sgar/__init__.py +3 -0
  387. sgar/__main__.py +5 -0
  388. sgar-0.1.3.dist-info/METADATA +90 -0
  389. sgar-0.1.3.dist-info/RECORD +393 -0
  390. sgar-0.1.3.dist-info/WHEEL +5 -0
  391. sgar-0.1.3.dist-info/entry_points.txt +2 -0
  392. sgar-0.1.3.dist-info/licenses/LICENSE +6 -0
  393. sgar-0.1.3.dist-info/top_level.txt +2 -0
core/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ def str_to_obj(*args, **kwargs):
2
+ from .utils.common import str_to_obj as _str_to_obj
3
+
4
+ return _str_to_obj(*args, **kwargs)
5
+
6
+
7
+ __all__ = ["str_to_obj"]
core/cc/__init__.py ADDED
@@ -0,0 +1,119 @@
1
+ # ---------------------------------------------------------------------------
2
+ # Public API (stable, recommended for external integrations)
3
+ # ---------------------------------------------------------------------------
4
+ from .audit import (
5
+ RuntimeAuditQuery,
6
+ RuntimeAuditSnapshot,
7
+ RuntimeAuditSummary,
8
+ query_runtime_audit,
9
+ read_runtime_audit,
10
+ summarize_runtime_audit,
11
+ )
12
+ from .api import (
13
+ AgentRunRequest,
14
+ AgentRunResult,
15
+ CodeAgent,
16
+ CodeBuildRequest,
17
+ build_code_with_agent,
18
+ run_code_agent,
19
+ )
20
+ from .config import CCConfig, load_cc_config
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Advanced API (stable but lower-level; for fine-grained session control)
24
+ # ---------------------------------------------------------------------------
25
+ from .llm import DefaultLLMClientProvider, LLMClientProvider
26
+ from .runtime import build_default_query_engine
27
+ from .conversation.query_engine import QueryEngine
28
+ from .conversation.session import QuerySession, SessionFactory
29
+ from .conversation.models import SessionEvent, SessionMessage
30
+
31
+ # ---------------------------------------------------------------------------
32
+ # Middleware pipeline (composable cross-cutting concerns)
33
+ # ---------------------------------------------------------------------------
34
+ from .conversation.middleware import (
35
+ RetryPolicy,
36
+ TurnHooks,
37
+ TurnMiddleware,
38
+ TurnRunner,
39
+ apply as apply_middleware,
40
+ pipe as pipe_middleware,
41
+ with_compaction,
42
+ with_hooks,
43
+ with_persistence,
44
+ with_retry,
45
+ with_turn_tracking,
46
+ )
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # Environment providers (injectable filesystem / shell abstractions)
50
+ # ---------------------------------------------------------------------------
51
+ from .providers import (
52
+ CommandProvider,
53
+ Environment,
54
+ FileSystemProvider,
55
+ LocalCommandProvider,
56
+ LocalFileSystemProvider,
57
+ default_environment,
58
+ )
59
+
60
+ # ---------------------------------------------------------------------------
61
+ # Errors
62
+ # ---------------------------------------------------------------------------
63
+ from .errors import (
64
+ CCError,
65
+ ConfigError,
66
+ ToolExecutionError,
67
+ ToolValidationError,
68
+ )
69
+
70
+ __all__ = [
71
+ # -- Public API --
72
+ "AgentRunRequest",
73
+ "AgentRunResult",
74
+ "CCConfig",
75
+ "CodeAgent",
76
+ "CodeBuildRequest",
77
+ "RuntimeAuditQuery",
78
+ "RuntimeAuditSnapshot",
79
+ "RuntimeAuditSummary",
80
+ "build_code_with_agent",
81
+ "load_cc_config",
82
+ "query_runtime_audit",
83
+ "read_runtime_audit",
84
+ "run_code_agent",
85
+ "summarize_runtime_audit",
86
+ # -- Advanced API --
87
+ "DefaultLLMClientProvider",
88
+ "LLMClientProvider",
89
+ "QueryEngine",
90
+ "QuerySession",
91
+ "SessionEvent",
92
+ "SessionFactory",
93
+ "SessionMessage",
94
+ "build_default_query_engine",
95
+ # -- Middleware pipeline --
96
+ "RetryPolicy",
97
+ "TurnHooks",
98
+ "TurnMiddleware",
99
+ "TurnRunner",
100
+ "apply_middleware",
101
+ "pipe_middleware",
102
+ "with_compaction",
103
+ "with_hooks",
104
+ "with_persistence",
105
+ "with_retry",
106
+ "with_turn_tracking",
107
+ # -- Environment providers --
108
+ "CommandProvider",
109
+ "Environment",
110
+ "FileSystemProvider",
111
+ "LocalCommandProvider",
112
+ "LocalFileSystemProvider",
113
+ "default_environment",
114
+ # -- Errors --
115
+ "CCError",
116
+ "ConfigError",
117
+ "ToolExecutionError",
118
+ "ToolValidationError",
119
+ ]
@@ -0,0 +1,10 @@
1
+ from .definitions import AgentDefinition
2
+ from .task_manager import TaskManager
3
+ from .task_model import AgentTask, AgentTaskStatus
4
+
5
+ __all__ = [
6
+ "AgentDefinition",
7
+ "AgentTask",
8
+ "AgentTaskStatus",
9
+ "TaskManager",
10
+ ]
@@ -0,0 +1,320 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ import logging
5
+ import os
6
+ from typing import Any
7
+ import asyncio
8
+
9
+ from ..agents.backends import InProcessBackend, LocalSubprocessBackend, RuntimeBackend
10
+ from ..config import CCConfig
11
+ from ..conversation.session import QuerySession, SessionFactory
12
+ from ..llm import DefaultLLMClientProvider, LLMClientProvider
13
+ from ..observability import EventRecord, JsonlAuditLogger
14
+ from ..tools.base import BaseTool, ToolCall, ToolResult, ToolSpec, ValidationResult
15
+ from ..tools.context import ToolUseContext
16
+ from .definitions import AgentDefinition
17
+ from .runtime import AgentRuntime
18
+ from .runtime_registry import InProcessRuntimeRegistry, get_in_process_runtime_registry
19
+ from .task_manager import TaskManager
20
+ from .task_model import AgentTask, AgentTaskStatus
21
+
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ # Maximum recursion depth for the ``agent`` tool. With cc_query_loop as the
27
+ # default ccx runner kind, every agent node can call ``agent`` to spawn a
28
+ # child runtime, which itself can call ``agent``, and so on. Each level
29
+ # multiplies the cost by the per-node fan-out, so an unconstrained recursion
30
+ # of depth 4 with fan-out 3 is already 81 leaves. The cap here is the only
31
+ # mechanical (non-prompt) backstop — LLMs can't reliably stay within a
32
+ # nominal budget via prompt instructions, but the runtime can refuse the
33
+ # call.
34
+ #
35
+ # Depth 0 = the top-level cc engine driven by ccx's CcAgentRunner; depth 1
36
+ # = its first helper; depth 2 = a helper-of-helper. The default of 3 keeps
37
+ # room for a real "lead → researcher → reviewer" pattern without permitting
38
+ # unbounded chains. Override via the ``CC_MAX_AGENT_RECURSION_DEPTH``
39
+ # environment variable for unusual workloads.
40
+ _DEFAULT_MAX_AGENT_RECURSION_DEPTH = 3
41
+ _RECURSION_DEPTH_STATE_KEY = "agent_recursion_depth"
42
+ _AGENT_SPAWN_REFUSED_ERROR_CODE = "AT1100"
43
+
44
+
45
+ def _resolve_max_recursion_depth() -> int:
46
+ raw = os.environ.get("CC_MAX_AGENT_RECURSION_DEPTH")
47
+ if not raw:
48
+ return _DEFAULT_MAX_AGENT_RECURSION_DEPTH
49
+ try:
50
+ value = int(raw)
51
+ except ValueError:
52
+ logger.warning(
53
+ "CC_MAX_AGENT_RECURSION_DEPTH=%r is not an int; using default %d",
54
+ raw, _DEFAULT_MAX_AGENT_RECURSION_DEPTH,
55
+ )
56
+ return _DEFAULT_MAX_AGENT_RECURSION_DEPTH
57
+ if value < 0:
58
+ logger.warning(
59
+ "CC_MAX_AGENT_RECURSION_DEPTH=%d is negative; using default %d",
60
+ value, _DEFAULT_MAX_AGENT_RECURSION_DEPTH,
61
+ )
62
+ return _DEFAULT_MAX_AGENT_RECURSION_DEPTH
63
+ return value
64
+
65
+
66
+ @dataclass(slots=True)
67
+ class AgentToolRequest:
68
+ description: str
69
+ prompt: str
70
+ subagent_type: str | None = None
71
+ backend: str | None = None
72
+ model: str | None = None
73
+ run_in_background: bool = False
74
+ name: str | None = None
75
+ team_name: str | None = None
76
+ mode: str | None = None
77
+ isolation: str | None = None
78
+ cwd: str | None = None
79
+
80
+
81
+ class AgentTool(BaseTool):
82
+ def __init__(
83
+ self,
84
+ llm_client_provider: LLMClientProvider | None = None,
85
+ runtime_registry: InProcessRuntimeRegistry | None = None,
86
+ ) -> None:
87
+ super().__init__(
88
+ ToolSpec(
89
+ name="agent",
90
+ description="Spawn a child agent to work on a sub-task and return its result.",
91
+ input_schema={
92
+ "type": "object",
93
+ "properties": {
94
+ "description": {"type": "string"},
95
+ "prompt": {"type": "string"},
96
+ "subagent_type": {"type": "string"},
97
+ "backend": {"type": "string"},
98
+ "run_in_background": {"type": "boolean"},
99
+ "cwd": {"type": "string"},
100
+ },
101
+ "required": ["description", "prompt"],
102
+ },
103
+ )
104
+ )
105
+ self.llm_client_provider = llm_client_provider or DefaultLLMClientProvider()
106
+ self.runtime_registry = runtime_registry
107
+
108
+ def validate_input(self, arguments: dict[str, Any]) -> ValidationResult:
109
+ if not arguments.get("description"):
110
+ return ValidationResult(ok=False, message="description is required.")
111
+ if not arguments.get("prompt"):
112
+ return ValidationResult(ok=False, message="prompt is required.")
113
+ backend = arguments.get("backend")
114
+ if backend and str(backend) not in {"in_process", "local_subprocess"}:
115
+ return ValidationResult(ok=False, message=f"Unsupported backend: {backend}")
116
+ return ValidationResult(ok=True)
117
+
118
+ async def execute(self, tool_call: ToolCall, ctx: ToolUseContext) -> ToolResult:
119
+ request = AgentToolRequest(
120
+ description=str(tool_call.arguments["description"]),
121
+ prompt=str(tool_call.arguments["prompt"]),
122
+ subagent_type=tool_call.arguments.get("subagent_type"),
123
+ backend=tool_call.arguments.get("backend"),
124
+ run_in_background=bool(tool_call.arguments.get("run_in_background", False)),
125
+ cwd=tool_call.arguments.get("cwd"),
126
+ )
127
+ # Recursion depth check (Tier 1 backstop against runaway sub-agent
128
+ # spawning when cc_query_loop is the default ccx runner). Read the
129
+ # parent's depth from session metadata.state, refuse if we'd exceed
130
+ # the cap, and emit an audit event either way so deep chains are
131
+ # debuggable post-hoc.
132
+ parent_session = ctx.metadata.get("session")
133
+ parent_depth = 0
134
+ if parent_session is not None:
135
+ parent_depth = int(
136
+ (parent_session.metadata.state or {}).get(
137
+ _RECURSION_DEPTH_STATE_KEY, 0,
138
+ ) or 0
139
+ )
140
+ max_depth = _resolve_max_recursion_depth()
141
+ runtime_root_for_audit = ctx.config.runtime_root_path(ctx.cwd)
142
+ agent_audit_logger = JsonlAuditLogger(
143
+ runtime_root_for_audit / "audit" / "agent_events.jsonl"
144
+ )
145
+ prospective_child_depth = parent_depth + 1
146
+ if prospective_child_depth > max_depth:
147
+ agent_audit_logger.append(EventRecord(
148
+ event_type="agent_spawn_refused_recursion_cap",
149
+ session_id=getattr(parent_session, "session_id", None),
150
+ tool_name="agent",
151
+ success=False,
152
+ error_code=_AGENT_SPAWN_REFUSED_ERROR_CODE,
153
+ details={
154
+ "parent_depth": parent_depth,
155
+ "prospective_child_depth": prospective_child_depth,
156
+ "max_depth": max_depth,
157
+ "description": (request.description or "")[:200],
158
+ "tool_use_id": tool_call.tool_use_id,
159
+ },
160
+ ))
161
+ logger.warning(
162
+ "agent tool refused: parent_depth=%d would exceed max_depth=%d "
163
+ "(description=%r). Set CC_MAX_AGENT_RECURSION_DEPTH to raise the cap.",
164
+ parent_depth, max_depth, (request.description or "")[:120],
165
+ )
166
+ return ToolResult(
167
+ tool_use_id=tool_call.tool_use_id,
168
+ tool_name=tool_call.tool_name,
169
+ success=False,
170
+ content=(
171
+ f"agent spawn refused: recursion depth {prospective_child_depth} "
172
+ f"would exceed cap {max_depth}. Complete this task with the "
173
+ f"context you already have, or finalize and return."
174
+ ),
175
+ error_code=_AGENT_SPAWN_REFUSED_ERROR_CODE,
176
+ data={
177
+ "parent_depth": parent_depth,
178
+ "max_depth": max_depth,
179
+ "refused": True,
180
+ },
181
+ )
182
+ # Approaching-cap warning (one less than max). Distinct event type
183
+ # so dashboards can flag chains that get close without firing on
184
+ # every spawn.
185
+ if prospective_child_depth == max_depth:
186
+ agent_audit_logger.append(EventRecord(
187
+ event_type="agent_collaboration_depth_warning",
188
+ session_id=getattr(parent_session, "session_id", None),
189
+ tool_name="agent",
190
+ success=None,
191
+ details={
192
+ "parent_depth": parent_depth,
193
+ "child_depth": prospective_child_depth,
194
+ "max_depth": max_depth,
195
+ "description": (request.description or "")[:200],
196
+ "tool_use_id": tool_call.tool_use_id,
197
+ },
198
+ ))
199
+
200
+ definition = self.resolve_agent_definition(request)
201
+ backend_name = self.resolve_backend_name(request, ctx)
202
+ backend = self.resolve_backend(backend_name)
203
+ runtime_root = ctx.config.runtime_root_path(ctx.cwd)
204
+ runtime_registry = self.runtime_registry or get_in_process_runtime_registry(runtime_root)
205
+ task_manager = TaskManager(runtime_root / "tasks")
206
+ task = task_manager.create_task(
207
+ AgentTask.create(
208
+ agent_type=definition.agent_id,
209
+ backend=backend_name,
210
+ prompt_language=ctx.prompt_language,
211
+ title=request.description,
212
+ input_payload={
213
+ "description": request.description,
214
+ "prompt": request.prompt,
215
+ },
216
+ )
217
+ )
218
+ child_session = self.build_child_session(
219
+ parent_session=ctx.metadata["session"],
220
+ task=task,
221
+ agent_definition=definition,
222
+ cwd=request.cwd or ctx.cwd,
223
+ )
224
+ # Stamp the child's depth so its own ``agent`` tool calls see a
225
+ # bumped value and the cap composes across levels.
226
+ child_session.metadata.state[_RECURSION_DEPTH_STATE_KEY] = prospective_child_depth
227
+ from ..runtime import build_default_query_engine
228
+
229
+ runtime = AgentRuntime(
230
+ definition=definition,
231
+ task=task,
232
+ query_engine=build_default_query_engine(
233
+ cwd=child_session.cwd,
234
+ config=child_session.config,
235
+ llm_client_provider=self.llm_client_provider,
236
+ session=child_session,
237
+ ),
238
+ task_manager=task_manager,
239
+ )
240
+ controller = await backend.create_controller(
241
+ runtime=runtime,
242
+ run_in_background=request.run_in_background,
243
+ runtime_root=runtime_root,
244
+ )
245
+ runtime_registry.register(controller)
246
+ if request.run_in_background:
247
+ if backend_name == "in_process":
248
+ task_manager.update_task_status(task.task_id, AgentTaskStatus.RUNNING)
249
+ background_task = asyncio.create_task(controller.start(request.prompt))
250
+ runtime_registry.register_background_task(task.runtime_id, background_task)
251
+ launch_result = {
252
+ "task_id": task.task_id,
253
+ "runtime_id": task.runtime_id,
254
+ "status": task.status.value,
255
+ "background": True,
256
+ "backend": backend_name,
257
+ }
258
+ else:
259
+ launch_result = await controller.start(request.prompt)
260
+ return ToolResult(
261
+ tool_use_id=tool_call.tool_use_id,
262
+ tool_name=tool_call.tool_name,
263
+ success=True,
264
+ content="Agent launched in background mode.",
265
+ data=launch_result,
266
+ )
267
+ result = await controller.start(request.prompt)
268
+ return ToolResult(
269
+ tool_use_id=tool_call.tool_use_id,
270
+ tool_name=tool_call.tool_name,
271
+ success=True,
272
+ content=result["final_text"],
273
+ data=result,
274
+ )
275
+
276
+ def resolve_backend_name(self, request: AgentToolRequest, ctx: ToolUseContext) -> str:
277
+ return str(request.backend or request.mode or ctx.config.default_backend or "in_process")
278
+
279
+ def resolve_backend(self, backend_name: str) -> RuntimeBackend:
280
+ if backend_name == "in_process":
281
+ return InProcessBackend()
282
+ if backend_name == "local_subprocess":
283
+ return LocalSubprocessBackend()
284
+ raise ValueError(f"Unsupported backend: {backend_name}")
285
+
286
+ def resolve_agent_definition(self, request: AgentToolRequest) -> AgentDefinition:
287
+ agent_id = request.subagent_type or "worker"
288
+ return AgentDefinition(
289
+ agent_id=agent_id,
290
+ name=request.name or agent_id,
291
+ description=request.description,
292
+ prompt_key="agents.worker",
293
+ )
294
+
295
+ def build_child_session(
296
+ self,
297
+ *,
298
+ parent_session: QuerySession,
299
+ task: AgentTask,
300
+ agent_definition: AgentDefinition,
301
+ cwd: str,
302
+ ) -> QuerySession:
303
+ child_config = CCConfig.from_mapping(
304
+ {
305
+ **parent_session.config.to_dict(),
306
+ "agent_mode": "",
307
+ }
308
+ )
309
+ child_factory = SessionFactory(child_config)
310
+ child_session = child_factory.create(
311
+ cwd=cwd,
312
+ model_name=parent_session.model_name,
313
+ agent_id=agent_definition.agent_id,
314
+ parent_task_id=task.task_id,
315
+ )
316
+ child_session.prompt_language = parent_session.prompt_language
317
+ child_session.permission_mode = parent_session.permission_mode
318
+ child_session.agent_mode = ""
319
+ child_session.metadata.state["spawned_by_agent_mode"] = parent_session.agent_mode
320
+ return child_session
@@ -0,0 +1,11 @@
1
+ from .base import BackendHandle, RuntimeBackend, RuntimeController
2
+ from .in_process import InProcessBackend
3
+ from .local_subprocess import LocalSubprocessBackend
4
+
5
+ __all__ = [
6
+ "BackendHandle",
7
+ "InProcessBackend",
8
+ "LocalSubprocessBackend",
9
+ "RuntimeBackend",
10
+ "RuntimeController",
11
+ ]
@@ -0,0 +1,62 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from typing import Any, Protocol, TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from ..runtime import AgentMessage, AgentRuntime
9
+ from ..task_model import AgentTask
10
+
11
+
12
+ @dataclass(slots=True)
13
+ class BackendHandle:
14
+ runtime_id: str
15
+ backend_name: str
16
+ process_id: int | None = None
17
+ endpoint: str | None = None
18
+ output_path: str | None = None
19
+
20
+
21
+ class RuntimeController(Protocol):
22
+ task: "AgentTask"
23
+ handle: BackendHandle
24
+
25
+ async def start(self, prompt: str) -> dict[str, Any]:
26
+ ...
27
+
28
+ async def send_message(
29
+ self,
30
+ message: "AgentMessage",
31
+ *,
32
+ timeout_seconds: float | None = None,
33
+ ) -> dict[str, Any]:
34
+ ...
35
+
36
+ async def stop(self, reason: str) -> None:
37
+ ...
38
+
39
+ async def collect_status(self) -> dict[str, Any]:
40
+ ...
41
+
42
+ async def apply_shared_state(
43
+ self,
44
+ *,
45
+ shared_context: dict[str, Any],
46
+ shared_allowed_paths: list[str],
47
+ timeout_seconds: float | None = None,
48
+ ) -> dict[str, Any]:
49
+ ...
50
+
51
+
52
+ class RuntimeBackend(Protocol):
53
+ name: str
54
+
55
+ async def create_controller(
56
+ self,
57
+ *,
58
+ runtime: "AgentRuntime",
59
+ run_in_background: bool,
60
+ runtime_root: Path,
61
+ ) -> RuntimeController:
62
+ ...
@@ -0,0 +1,126 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from pathlib import Path
5
+ import time
6
+
7
+ from ..swarm.mailbox import MailboxEnvelope, MailboxStore
8
+ from .base import BackendHandle, RuntimeBackend, RuntimeController
9
+
10
+
11
+ class InProcessController:
12
+ def __init__(self, runtime, runtime_root: Path, *, keep_alive: bool) -> None:
13
+ self.runtime = runtime
14
+ self.task = runtime.task
15
+ self.task_manager = runtime.task_manager
16
+ self.keep_alive = keep_alive
17
+ session = runtime.query_engine.session
18
+ team_id = session.metadata.team_id
19
+ lead_runtime_id = session.metadata.state.get("team_lead_runtime_id")
20
+ self.team_mailbox = (
21
+ MailboxStore(runtime_root / "teams" / str(team_id) / "mailbox")
22
+ if team_id is not None and lead_runtime_id
23
+ else None
24
+ )
25
+ self.lead_runtime_id = str(lead_runtime_id) if lead_runtime_id else None
26
+ self.handle = BackendHandle(
27
+ runtime_id=runtime.task.runtime_id,
28
+ backend_name="in_process",
29
+ process_id=None,
30
+ output_path=None,
31
+ )
32
+
33
+ async def start(self, prompt: str) -> dict[str, object]:
34
+ return await self.runtime.start(prompt, keep_alive=self.keep_alive)
35
+
36
+ async def send_message(self, message, *, timeout_seconds: float | None = None) -> dict[str, object]:
37
+ del timeout_seconds
38
+ self._publish_team_event(
39
+ "status_update",
40
+ {
41
+ "message_id": message.message_id,
42
+ "status": "running",
43
+ "description": message.metadata.get("description"),
44
+ },
45
+ )
46
+ self._publish_team_event(
47
+ "partial_result",
48
+ {
49
+ "message_id": message.message_id,
50
+ "partial_text": f"Worker {self.task.runtime_id} started task {message.message_id}",
51
+ "description": message.metadata.get("description"),
52
+ },
53
+ )
54
+ result = await self.runtime.send_message(message)
55
+ self._publish_team_event(
56
+ "final_result",
57
+ {
58
+ "message_id": message.message_id,
59
+ "description": message.metadata.get("description"),
60
+ "final_text": result.get("final_text", ""),
61
+ "status": result.get("status"),
62
+ },
63
+ )
64
+ self._publish_team_event(
65
+ "assignment_completed",
66
+ {
67
+ "message_id": message.message_id,
68
+ "description": message.metadata.get("description"),
69
+ "final_text": result.get("final_text", ""),
70
+ "status": result.get("status"),
71
+ },
72
+ )
73
+ return result
74
+
75
+ async def stop(self, reason: str) -> None:
76
+ await self.runtime.stop(reason)
77
+
78
+ async def collect_status(self) -> dict[str, object]:
79
+ return await self.runtime.collect_status()
80
+
81
+ async def apply_shared_state(
82
+ self,
83
+ *,
84
+ shared_context: dict[str, object],
85
+ shared_allowed_paths: list[str],
86
+ timeout_seconds: float | None = None,
87
+ ) -> dict[str, object]:
88
+ del timeout_seconds
89
+ session = self.runtime.query_engine.session
90
+ session.metadata.state["team_shared_context"] = dict(shared_context)
91
+ session.metadata.state["team_shared_allowed_paths"] = list(shared_allowed_paths)
92
+ session.metadata.state["allowed_paths"] = list(shared_allowed_paths)
93
+ return {
94
+ "runtime_id": self.task.runtime_id,
95
+ "backend": self.handle.backend_name,
96
+ "shared_context": dict(shared_context),
97
+ "shared_allowed_paths": list(shared_allowed_paths),
98
+ }
99
+
100
+ def _publish_team_event(self, message_type: str, payload: dict[str, object]) -> None:
101
+ if self.team_mailbox is None or self.lead_runtime_id is None:
102
+ return
103
+ message_token = str(payload.get("message_id") or "evt")
104
+ self.team_mailbox.enqueue(
105
+ MailboxEnvelope(
106
+ envelope_id=f"env_{message_type}_{self.task.runtime_id}_{message_token}_{int(time.time() * 1000)}",
107
+ team_id=self.runtime.query_engine.session.metadata.team_id,
108
+ from_runtime_id=self.task.runtime_id,
109
+ to_runtime_id=self.lead_runtime_id,
110
+ message_type=message_type,
111
+ payload=dict(payload),
112
+ )
113
+ )
114
+
115
+
116
+ class InProcessBackend(RuntimeBackend):
117
+ name = "in_process"
118
+
119
+ async def create_controller(
120
+ self,
121
+ *,
122
+ runtime,
123
+ run_in_background: bool,
124
+ runtime_root: Path,
125
+ ) -> RuntimeController:
126
+ return InProcessController(runtime, runtime_root, keep_alive=run_in_background)