loopx 0.4.8__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 (811) hide show
  1. loopx/__init__.py +5 -0
  2. loopx/agent_onboarding.py +654 -0
  3. loopx/agent_registry.py +112 -0
  4. loopx/ark_managed_agent_host.py +59 -0
  5. loopx/authority.py +805 -0
  6. loopx/benchmark.py +2875 -0
  7. loopx/benchmark_adapters/__init__.py +1 -0
  8. loopx/benchmark_adapters/agentissue.py +2644 -0
  9. loopx/benchmark_adapters/agents_last_exam.py +3998 -0
  10. loopx/benchmark_adapters/edgebench.py +322 -0
  11. loopx/benchmark_adapters/skillsbench.py +5978 -0
  12. loopx/benchmark_adapters/skillsbench_acp_failure_policy.py +143 -0
  13. loopx/benchmark_adapters/skillsbench_acp_process.py +31 -0
  14. loopx/benchmark_adapters/skillsbench_acp_relay.py +4832 -0
  15. loopx/benchmark_adapters/skillsbench_batch.py +124 -0
  16. loopx/benchmark_adapters/skillsbench_bridge_guard.py +209 -0
  17. loopx/benchmark_adapters/skillsbench_bridge_summary.py +203 -0
  18. loopx/benchmark_adapters/skillsbench_codex_goal_recovery.py +271 -0
  19. loopx/benchmark_adapters/skillsbench_codex_goal_trace.py +81 -0
  20. loopx/benchmark_adapters/skillsbench_codex_runtime.py +339 -0
  21. loopx/benchmark_adapters/skillsbench_dockerfile_runtime.py +467 -0
  22. loopx/benchmark_adapters/skillsbench_failure_signals.py +652 -0
  23. loopx/benchmark_adapters/skillsbench_proxy_runtime.py +327 -0
  24. loopx/benchmark_adapters/skillsbench_remote_bridge.py +402 -0
  25. loopx/benchmark_adapters/skillsbench_result_discovery.py +143 -0
  26. loopx/benchmark_adapters/skillsbench_runner_profile.py +436 -0
  27. loopx/benchmark_adapters/skillsbench_runner_source.py +99 -0
  28. loopx/benchmark_adapters/skillsbench_setup_preflight.py +771 -0
  29. loopx/benchmark_adapters/skillsbench_signals.py +15 -0
  30. loopx/benchmark_adapters/skillsbench_task_source.py +141 -0
  31. loopx/benchmark_adapters/skillsbench_turn_route.py +723 -0
  32. loopx/benchmark_adapters/skillsbench_turn_runtime.py +1069 -0
  33. loopx/benchmark_adapters/skillsbench_typed_repair.py +689 -0
  34. loopx/benchmark_adapters/skillsbench_uv_cache.py +111 -0
  35. loopx/benchmark_adapters/skillsbench_verifier_bootstrap.py +227 -0
  36. loopx/benchmark_adapters/skillsbench_verifier_cache.py +138 -0
  37. loopx/benchmark_adapters/terminal_bench.py +10078 -0
  38. loopx/benchmark_case_analysis.py +1276 -0
  39. loopx/benchmark_case_state.py +1079 -0
  40. loopx/benchmark_core/__init__.py +239 -0
  41. loopx/benchmark_core/adapter.py +84 -0
  42. loopx/benchmark_core/artifacts.py +517 -0
  43. loopx/benchmark_core/attempts.py +199 -0
  44. loopx/benchmark_core/container_exec.py +216 -0
  45. loopx/benchmark_core/io.py +68 -0
  46. loopx/benchmark_core/lifecycle.py +211 -0
  47. loopx/benchmark_core/loop_protocol.py +689 -0
  48. loopx/benchmark_core/observable_handles.py +348 -0
  49. loopx/benchmark_core/parity.py +256 -0
  50. loopx/benchmark_core/remote_closeout.py +482 -0
  51. loopx/benchmark_core/rounds.py +215 -0
  52. loopx/benchmark_core/route_profile.py +509 -0
  53. loopx/benchmark_core/run_permissions.py +206 -0
  54. loopx/benchmark_core/split_control.py +925 -0
  55. loopx/benchmark_core/turn_fidelity.py +326 -0
  56. loopx/benchmark_ledger.py +3793 -0
  57. loopx/benchmark_ledger_countability.py +372 -0
  58. loopx/benchmark_ledger_current.py +724 -0
  59. loopx/benchmark_trajectory.py +405 -0
  60. loopx/benchmarks/__init__.py +1 -0
  61. loopx/benchmarks/qualification/__init__.py +1 -0
  62. loopx/benchmarks/qualification/release_outcome_baseline.py +360 -0
  63. loopx/benchmarks/read_models/__init__.py +1 -0
  64. loopx/benchmarks/read_models/benchmark_attempt_accounting.py +53 -0
  65. loopx/benchmarks/read_models/benchmark_comparison.py +414 -0
  66. loopx/benchmarks/read_models/benchmark_event_timeline.py +113 -0
  67. loopx/benchmarks/read_models/benchmark_experiment_report.py +475 -0
  68. loopx/benchmarks/read_models/benchmark_learning_ledger.py +137 -0
  69. loopx/benchmarks/read_models/benchmark_lifecycle_contracts.py +228 -0
  70. loopx/benchmarks/read_models/benchmark_projection.py +723 -0
  71. loopx/benchmarks/read_models/benchmark_result.py +146 -0
  72. loopx/benchmarks/read_models/benchmark_run_execution_contract.py +116 -0
  73. loopx/benchmarks/read_models/benchmark_run_failure.py +157 -0
  74. loopx/benchmarks/read_models/benchmark_run_metrics.py +213 -0
  75. loopx/benchmarks/read_models/benchmark_run_post_execution.py +635 -0
  76. loopx/benchmarks/read_models/benchmark_run_pre_execution.py +541 -0
  77. loopx/benchmarks/read_models/benchmark_status_compaction.py +1255 -0
  78. loopx/benchmarks/read_models/benchmark_status_runner.py +780 -0
  79. loopx/benchmarks/read_models/goal_start_control_score.py +857 -0
  80. loopx/benchmarks/read_models/skillsbench_post_run_debug.py +746 -0
  81. loopx/benchmarks/read_models/skillsbench_verifier_attribution.py +269 -0
  82. loopx/bootstrap.py +1116 -0
  83. loopx/bootstrap_command_pack.py +2167 -0
  84. loopx/boundary_authority.py +199 -0
  85. loopx/canary/__init__.py +1 -0
  86. loopx/canary/maintainability_ratchet.py +800 -0
  87. loopx/canary/planner.py +1984 -0
  88. loopx/canary/premerge.py +1130 -0
  89. loopx/canary/qualification_profiles.py +309 -0
  90. loopx/canary/quality_surface_catalog.py +838 -0
  91. loopx/canary/release_profiles.py +51 -0
  92. loopx/canary/runner.py +1107 -0
  93. loopx/canary/smoke_health.py +581 -0
  94. loopx/canary/smoke_profiles.py +212 -0
  95. loopx/capabilities/__init__.py +0 -0
  96. loopx/capabilities/agent_turn_recall/__init__.py +17 -0
  97. loopx/capabilities/agent_turn_recall/cli.py +369 -0
  98. loopx/capabilities/agent_turn_recall/core.py +296 -0
  99. loopx/capabilities/auto_research/__init__.py +16 -0
  100. loopx/capabilities/auto_research/bootstrap_contract.py +157 -0
  101. loopx/capabilities/auto_research/cli.py +1468 -0
  102. loopx/capabilities/auto_research/core.py +11 -0
  103. loopx/capabilities/auto_research/defaults.py +79 -0
  104. loopx/capabilities/auto_research/demo_e2e.py +1848 -0
  105. loopx/capabilities/auto_research/demo_supervisor.py +186 -0
  106. loopx/capabilities/auto_research/evidence_packet.py +767 -0
  107. loopx/capabilities/auto_research/human_view.py +794 -0
  108. loopx/capabilities/auto_research/kernel.py +191 -0
  109. loopx/capabilities/auto_research/knn_demo_workspace.py +322 -0
  110. loopx/capabilities/auto_research/live_evidence.py +248 -0
  111. loopx/capabilities/auto_research/preset.py +176 -0
  112. loopx/capabilities/auto_research/research_state.py +1085 -0
  113. loopx/capabilities/auto_research/role_profiles.py +394 -0
  114. loopx/capabilities/auto_research/rollout_append.py +97 -0
  115. loopx/capabilities/auto_research/terminal_result_contract.py +422 -0
  116. loopx/capabilities/auto_research/terminal_result_projection.py +171 -0
  117. loopx/capabilities/auto_research/terminal_result_query.py +233 -0
  118. loopx/capabilities/auto_research/terminal_results.py +349 -0
  119. loopx/capabilities/auto_research/user_contract.py +190 -0
  120. loopx/capabilities/auto_research/worker_loop.py +163 -0
  121. loopx/capabilities/auto_research/worker_runtime.py +777 -0
  122. loopx/capabilities/auto_research/worker_skill/SKILL.md +343 -0
  123. loopx/capabilities/benchmark_toolkit/__init__.py +19 -0
  124. loopx/capabilities/benchmark_toolkit/integrity.py +387 -0
  125. loopx/capabilities/catalog.py +1875 -0
  126. loopx/capabilities/change_quality/__init__.py +19 -0
  127. loopx/capabilities/change_quality/cli.py +171 -0
  128. loopx/capabilities/change_quality/context.py +156 -0
  129. loopx/capabilities/change_quality/oracles.py +269 -0
  130. loopx/capabilities/change_quality/policy.py +34 -0
  131. loopx/capabilities/change_quality/receipt.py +482 -0
  132. loopx/capabilities/change_quality/result.py +493 -0
  133. loopx/capabilities/change_quality/scope.py +171 -0
  134. loopx/capabilities/change_quality/shadow.py +680 -0
  135. loopx/capabilities/content_ops/__init__.py +0 -0
  136. loopx/capabilities/content_ops/cli.py +649 -0
  137. loopx/capabilities/content_ops/connector_packets.py +164 -0
  138. loopx/capabilities/content_ops/item_lifecycle.py +1000 -0
  139. loopx/capabilities/content_ops/layout.py +451 -0
  140. loopx/capabilities/content_ops/markdown.py +456 -0
  141. loopx/capabilities/content_ops/schemas.py +51 -0
  142. loopx/capabilities/content_ops/social_browser_x.py +107 -0
  143. loopx/capabilities/content_ops/surface.py +1956 -0
  144. loopx/capabilities/content_ops/templates/layout-catalog-v0.json +72 -0
  145. loopx/capabilities/context_providers/__init__.py +36 -0
  146. loopx/capabilities/context_providers/base.py +189 -0
  147. loopx/capabilities/context_providers/factory.py +32 -0
  148. loopx/capabilities/context_providers/openviking.py +702 -0
  149. loopx/capabilities/context_providers/service_ownership.py +185 -0
  150. loopx/capabilities/decision_context/__init__.py +129 -0
  151. loopx/capabilities/decision_context/architecture.py +83 -0
  152. loopx/capabilities/decision_context/assembler.py +849 -0
  153. loopx/capabilities/decision_context/catalog_entry.py +195 -0
  154. loopx/capabilities/decision_context/cli.py +310 -0
  155. loopx/capabilities/decision_context/cursor_commit.py +535 -0
  156. loopx/capabilities/decision_context/outcome_feedback.py +352 -0
  157. loopx/capabilities/decision_context/packets.py +654 -0
  158. loopx/capabilities/decision_context/private_state.py +189 -0
  159. loopx/capabilities/decision_context/profile.py +453 -0
  160. loopx/capabilities/decision_context/providers.py +228 -0
  161. loopx/capabilities/decision_context/review_settlement.py +136 -0
  162. loopx/capabilities/decision_context/runtime.py +273 -0
  163. loopx/capabilities/decision_context/sources.py +415 -0
  164. loopx/capabilities/explore/__init__.py +1 -0
  165. loopx/capabilities/explore/activation.py +198 -0
  166. loopx/capabilities/explore/adaptive_replay_planner.py +221 -0
  167. loopx/capabilities/explore/child_replay_runtime.py +463 -0
  168. loopx/capabilities/explore/composition_frontier.py +291 -0
  169. loopx/capabilities/explore/counterfactual_runtime.py +578 -0
  170. loopx/capabilities/explore/episode_runtime.py +647 -0
  171. loopx/capabilities/explore/harness_checkpoint.py +171 -0
  172. loopx/capabilities/explore/harness_gate.py +115 -0
  173. loopx/capabilities/explore/harness_runtime.py +1124 -0
  174. loopx/capabilities/explore/replay_metrics.py +206 -0
  175. loopx/capabilities/explore/replay_runtime.py +1271 -0
  176. loopx/capabilities/explore/resource_portfolio.py +173 -0
  177. loopx/capabilities/explore/result_log.py +974 -0
  178. loopx/capabilities/explore/router_state.py +432 -0
  179. loopx/capabilities/explore/source_history_reconcile.py +255 -0
  180. loopx/capabilities/explore/speculative_scheduler.py +498 -0
  181. loopx/capabilities/explore/todo_branch_plan.py +650 -0
  182. loopx/capabilities/explore/todo_evidence.py +141 -0
  183. loopx/capabilities/explore/trace_runtime.py +284 -0
  184. loopx/capabilities/explore/worker_branch_plan.py +1257 -0
  185. loopx/capabilities/integration_branch/__init__.py +13 -0
  186. loopx/capabilities/integration_branch/cli.py +148 -0
  187. loopx/capabilities/integration_branch/core.py +916 -0
  188. loopx/capabilities/issue_fix/__init__.py +19 -0
  189. loopx/capabilities/issue_fix/acceptance_loop.py +1050 -0
  190. loopx/capabilities/issue_fix/candidate_evidence.py +503 -0
  191. loopx/capabilities/issue_fix/candidate_preflight.py +676 -0
  192. loopx/capabilities/issue_fix/cli.py +1822 -0
  193. loopx/capabilities/issue_fix/cli_input.py +87 -0
  194. loopx/capabilities/issue_fix/content_ops_cli.py +148 -0
  195. loopx/capabilities/issue_fix/discovered_issue_promotion.py +947 -0
  196. loopx/capabilities/issue_fix/explore_projection.py +710 -0
  197. loopx/capabilities/issue_fix/feasibility.py +542 -0
  198. loopx/capabilities/issue_fix/github_public.py +661 -0
  199. loopx/capabilities/issue_fix/intake_surface.py +832 -0
  200. loopx/capabilities/issue_fix/metadata_preview.py +218 -0
  201. loopx/capabilities/issue_fix/metrics_projection.py +1340 -0
  202. loopx/capabilities/issue_fix/metrics_supplement.py +634 -0
  203. loopx/capabilities/issue_fix/metrics_supplement_cli.py +127 -0
  204. loopx/capabilities/issue_fix/outcome_projection.py +1235 -0
  205. loopx/capabilities/issue_fix/periodic_report.py +189 -0
  206. loopx/capabilities/issue_fix/pr_description.py +418 -0
  207. loopx/capabilities/issue_fix/pr_gate_reconcile.py +496 -0
  208. loopx/capabilities/issue_fix/pr_gate_reconcile_cli.py +464 -0
  209. loopx/capabilities/issue_fix/pr_lifecycle.py +1327 -0
  210. loopx/capabilities/issue_fix/pr_lifecycle_rollout.py +85 -0
  211. loopx/capabilities/issue_fix/pr_monitor_materialization.py +257 -0
  212. loopx/capabilities/issue_fix/pr_review_ack.py +439 -0
  213. loopx/capabilities/issue_fix/provider_hooks.py +24 -0
  214. loopx/capabilities/issue_fix/repository_commit_evidence.py +186 -0
  215. loopx/capabilities/issue_fix/repository_context.py +457 -0
  216. loopx/capabilities/issue_fix/repository_memory.py +459 -0
  217. loopx/capabilities/issue_fix/repository_memory_provider.py +1454 -0
  218. loopx/capabilities/issue_fix/repository_snapshot.py +454 -0
  219. loopx/capabilities/issue_fix/reviewer_cli.py +917 -0
  220. loopx/capabilities/issue_fix/reviewer_notification.py +882 -0
  221. loopx/capabilities/issue_fix/reviewer_notification_drain.py +942 -0
  222. loopx/capabilities/issue_fix/reviewer_recommendation.py +1057 -0
  223. loopx/capabilities/issue_fix/reviewer_request.py +1282 -0
  224. loopx/capabilities/issue_fix/reward_memory.py +879 -0
  225. loopx/capabilities/issue_fix/workflow_plan.py +1286 -0
  226. loopx/capabilities/material_lifecycle/__init__.py +161 -0
  227. loopx/capabilities/material_lifecycle/_validation.py +183 -0
  228. loopx/capabilities/material_lifecycle/apply.py +672 -0
  229. loopx/capabilities/material_lifecycle/architecture.py +122 -0
  230. loopx/capabilities/material_lifecycle/cli.py +161 -0
  231. loopx/capabilities/material_lifecycle/decision_planning.py +470 -0
  232. loopx/capabilities/material_lifecycle/explore_execution.py +306 -0
  233. loopx/capabilities/material_lifecycle/intake.py +869 -0
  234. loopx/capabilities/material_lifecycle/inventory.py +147 -0
  235. loopx/capabilities/material_lifecycle/lifecycle.py +98 -0
  236. loopx/capabilities/material_lifecycle/preparation.py +147 -0
  237. loopx/capabilities/material_lifecycle/project_skill.py +83 -0
  238. loopx/capabilities/material_lifecycle/ranking.py +267 -0
  239. loopx/capabilities/material_lifecycle/readable_projection.py +500 -0
  240. loopx/capabilities/material_lifecycle/rebuild.py +480 -0
  241. loopx/capabilities/material_lifecycle/settlement.py +238 -0
  242. loopx/capabilities/periodic_report/__init__.py +71 -0
  243. loopx/capabilities/periodic_report/adapters.py +939 -0
  244. loopx/capabilities/periodic_report/archive.py +422 -0
  245. loopx/capabilities/periodic_report/bindings.py +705 -0
  246. loopx/capabilities/periodic_report/cli.py +277 -0
  247. loopx/capabilities/periodic_report/core.py +691 -0
  248. loopx/capabilities/periodic_report/extension_envelope.py +66 -0
  249. loopx/capabilities/periodic_report/presets.py +103 -0
  250. loopx/capabilities/periodic_report/profile.py +235 -0
  251. loopx/capabilities/periodic_report/project_progress.py +179 -0
  252. loopx/capabilities/periodic_report/triggers.py +452 -0
  253. loopx/capabilities/pr_review_queue/__init__.py +17 -0
  254. loopx/capabilities/pr_review_queue/core.py +506 -0
  255. loopx/capabilities/pr_review_queue/review_contract.py +506 -0
  256. loopx/capabilities/registry.py +192 -0
  257. loopx/capabilities/reward_memory/__init__.py +75 -0
  258. loopx/capabilities/reward_memory/application.py +819 -0
  259. loopx/capabilities/reward_memory/architecture.py +572 -0
  260. loopx/capabilities/reward_memory/candidate_review.py +511 -0
  261. loopx/capabilities/reward_memory/cli.py +469 -0
  262. loopx/capabilities/reward_memory/dogfood.py +574 -0
  263. loopx/capabilities/reward_memory/evaluation.py +296 -0
  264. loopx/capabilities/reward_memory/evaluation_fixtures.py +362 -0
  265. loopx/capabilities/reward_memory/experiment.py +567 -0
  266. loopx/capabilities/reward_memory/health.py +222 -0
  267. loopx/capabilities/reward_memory/ingestion.py +519 -0
  268. loopx/capabilities/reward_memory/registry.py +600 -0
  269. loopx/capabilities/reward_memory/runtime_hooks.py +312 -0
  270. loopx/capabilities/reward_memory/scoped_feedback.py +173 -0
  271. loopx/capabilities/semantic_preference/__init__.py +12 -0
  272. loopx/capabilities/semantic_preference/cli.py +189 -0
  273. loopx/capabilities/semantic_preference/contract.py +592 -0
  274. loopx/capabilities/semantic_preference/reward_memory.py +62 -0
  275. loopx/capabilities/value_connectors/__init__.py +1 -0
  276. loopx/capabilities/value_connectors/cli.py +401 -0
  277. loopx/capabilities/value_connectors/finance_extension_migration.py +108 -0
  278. loopx/capabilities/value_connectors/install_check.py +147 -0
  279. loopx/capabilities/value_connectors/planner.py +733 -0
  280. loopx/capabilities/value_connectors/source_map.py +446 -0
  281. loopx/claude_goal_baseline.py +138 -0
  282. loopx/claude_goal_mode/__init__.py +23 -0
  283. loopx/claude_goal_mode/hooks/goal_policy.py +212 -0
  284. loopx/claude_goal_mode/hooks/goal_state.py +139 -0
  285. loopx/claude_goal_mode/mcp/loopx_mcp.py +167 -0
  286. loopx/claude_goal_mode/scripts/connect.py +103 -0
  287. loopx/claude_goal_mode/scripts/goalmode_cmd.py +241 -0
  288. loopx/claude_goal_mode/scripts/install.py +328 -0
  289. loopx/claude_goal_mode/statusline/goal_status.py +97 -0
  290. loopx/cli.py +836 -0
  291. loopx/cli_commands/__init__.py +334 -0
  292. loopx/cli_commands/_host_thread.py +13 -0
  293. loopx/cli_commands/agentissue_runner_flow.py +447 -0
  294. loopx/cli_commands/agents_last_exam.py +160 -0
  295. loopx/cli_commands/agents_last_exam_baked_input.py +302 -0
  296. loopx/cli_commands/agents_last_exam_host_codex.py +374 -0
  297. loopx/cli_commands/agents_last_exam_launch_dry_run.py +372 -0
  298. loopx/cli_commands/agents_last_exam_local_plan.py +322 -0
  299. loopx/cli_commands/agents_last_exam_runner_source.py +352 -0
  300. loopx/cli_commands/agents_last_exam_task_material.py +335 -0
  301. loopx/cli_commands/agents_last_exam_validation_gate.py +236 -0
  302. loopx/cli_commands/benchmark_boundary.py +499 -0
  303. loopx/cli_commands/benchmark_dispatch.py +161 -0
  304. loopx/cli_commands/benchmark_release_outcome.py +123 -0
  305. loopx/cli_commands/benchmark_review_lifecycle.py +1275 -0
  306. loopx/cli_commands/benchmark_run_ledger.py +763 -0
  307. loopx/cli_commands/benchmark_run_ledger_case_analysis.py +249 -0
  308. loopx/cli_commands/benchmark_run_ledger_classification.py +45 -0
  309. loopx/cli_commands/benchmark_run_ledger_maintenance.py +486 -0
  310. loopx/cli_commands/benchmark_run_ledger_maintenance_registration.py +342 -0
  311. loopx/cli_commands/benchmark_run_ledger_maintenance_rendering.py +233 -0
  312. loopx/cli_commands/benchmark_run_ledger_parity.py +92 -0
  313. loopx/cli_commands/bootstrap_connect.py +238 -0
  314. loopx/cli_commands/canary.py +707 -0
  315. loopx/cli_commands/canary_release_qualification.py +79 -0
  316. loopx/cli_commands/capability.py +96 -0
  317. loopx/cli_commands/doctor.py +43 -0
  318. loopx/cli_commands/dreaming.py +143 -0
  319. loopx/cli_commands/edgebench.py +205 -0
  320. loopx/cli_commands/evidence_log.py +275 -0
  321. loopx/cli_commands/explore.py +989 -0
  322. loopx/cli_commands/explore_planning_commands.py +157 -0
  323. loopx/cli_commands/extension.py +271 -0
  324. loopx/cli_commands/first_run_report.py +73 -0
  325. loopx/cli_commands/goal_channel.py +656 -0
  326. loopx/cli_commands/handoff_mode.py +158 -0
  327. loopx/cli_commands/history.py +622 -0
  328. loopx/cli_commands/host_mode_plan.py +113 -0
  329. loopx/cli_commands/lark_inbox.py +431 -0
  330. loopx/cli_commands/lark_kanban.py +629 -0
  331. loopx/cli_commands/ml_experiment.py +321 -0
  332. loopx/cli_commands/multi_agent.py +211 -0
  333. loopx/cli_commands/opencode2_goal_worker.py +217 -0
  334. loopx/cli_commands/pr_review.py +167 -0
  335. loopx/cli_commands/presentation.py +218 -0
  336. loopx/cli_commands/preset.py +96 -0
  337. loopx/cli_commands/project.py +150 -0
  338. loopx/cli_commands/project_lifecycle.py +915 -0
  339. loopx/cli_commands/quota.py +859 -0
  340. loopx/cli_commands/quota_registration.py +241 -0
  341. loopx/cli_commands/quota_request.py +113 -0
  342. loopx/cli_commands/ready_score.py +110 -0
  343. loopx/cli_commands/registry_admin.py +975 -0
  344. loopx/cli_commands/registry_admin_configure.py +344 -0
  345. loopx/cli_commands/registry_admin_peer.py +84 -0
  346. loopx/cli_commands/registry_authority.py +218 -0
  347. loopx/cli_commands/review_batch.py +146 -0
  348. loopx/cli_commands/slash_commands.py +145 -0
  349. loopx/cli_commands/start_goal.py +251 -0
  350. loopx/cli_commands/starter.py +175 -0
  351. loopx/cli_commands/starter_bootstrap.py +179 -0
  352. loopx/cli_commands/starter_bootstrap_registration.py +198 -0
  353. loopx/cli_commands/starter_runtime_idle.py +107 -0
  354. loopx/cli_commands/starter_scheduler.py +207 -0
  355. loopx/cli_commands/starter_session_runtime.py +152 -0
  356. loopx/cli_commands/starter_visible_common.py +54 -0
  357. loopx/cli_commands/starter_visible_driver.py +161 -0
  358. loopx/cli_commands/starter_visible_pilot.py +278 -0
  359. loopx/cli_commands/status.py +867 -0
  360. loopx/cli_commands/status_registration.py +239 -0
  361. loopx/cli_commands/summary_all.py +222 -0
  362. loopx/cli_commands/support_control.py +809 -0
  363. loopx/cli_commands/support_control_registry.py +68 -0
  364. loopx/cli_commands/support_control_supervisor.py +289 -0
  365. loopx/cli_commands/task_lease.py +306 -0
  366. loopx/cli_commands/terminal_bench_adapter.py +717 -0
  367. loopx/cli_commands/terminal_bench_environment_result.py +1246 -0
  368. loopx/cli_commands/todo.py +940 -0
  369. loopx/cli_commands/todo_argument_validation.py +572 -0
  370. loopx/cli_commands/todo_event.py +114 -0
  371. loopx/cli_commands/turn.py +804 -0
  372. loopx/cli_commands/version.py +46 -0
  373. loopx/cli_commands/worker_bridge.py +659 -0
  374. loopx/cli_rollout.py +314 -0
  375. loopx/codex_cli_goal_tui.py +672 -0
  376. loopx/codex_cli_probe.py +1530 -0
  377. loopx/codex_cli_probe_markdown.py +935 -0
  378. loopx/codex_cli_runtime_probe.py +733 -0
  379. loopx/codex_cli_scheduler.py +564 -0
  380. loopx/codex_goal_baseline.py +620 -0
  381. loopx/configuration_catalog.py +617 -0
  382. loopx/configure_goal.py +1375 -0
  383. loopx/contract.py +996 -0
  384. loopx/control_plane/__init__.py +71 -0
  385. loopx/control_plane/agents/__init__.py +1 -0
  386. loopx/control_plane/agents/agent_lane_recommendation.py +516 -0
  387. loopx/control_plane/agents/agent_scope.py +1578 -0
  388. loopx/control_plane/agents/agent_scope_frontier.py +60 -0
  389. loopx/control_plane/agents/capability_gate.py +531 -0
  390. loopx/control_plane/agents/identity.py +140 -0
  391. loopx/control_plane/agents/legacy_migration.py +169 -0
  392. loopx/control_plane/agents/management_projection.py +658 -0
  393. loopx/control_plane/agents/material_frontier.py +608 -0
  394. loopx/control_plane/agents/material_handoff.py +156 -0
  395. loopx/control_plane/agents/multi_agent/__init__.py +1 -0
  396. loopx/control_plane/agents/multi_agent/codex_executable.py +207 -0
  397. loopx/control_plane/agents/multi_agent/collective_round_ledger.py +387 -0
  398. loopx/control_plane/agents/multi_agent/contract.py +474 -0
  399. loopx/control_plane/agents/multi_agent/recipe.py +110 -0
  400. loopx/control_plane/agents/multi_agent/role_successor.py +297 -0
  401. loopx/control_plane/agents/multi_agent/runtime_scripts.py +426 -0
  402. loopx/control_plane/agents/multi_agent/visible_launch_policy.py +149 -0
  403. loopx/control_plane/agents/multi_agent/visible_wake_scheduler.py +392 -0
  404. loopx/control_plane/agents/profile.py +216 -0
  405. loopx/control_plane/agents/runtime_model.py +73 -0
  406. loopx/control_plane/agents/subagent_activity.py +164 -0
  407. loopx/control_plane/agents/supervisor.py +544 -0
  408. loopx/control_plane/agents/supervisor_events.py +462 -0
  409. loopx/control_plane/agents/supervisor_inject.py +204 -0
  410. loopx/control_plane/agents/work_mode.py +56 -0
  411. loopx/control_plane/agents/workspace_guard.py +364 -0
  412. loopx/control_plane/effect_program.py +644 -0
  413. loopx/control_plane/goals/__init__.py +1 -0
  414. loopx/control_plane/goals/active_state_event_projection.py +103 -0
  415. loopx/control_plane/goals/active_state_metadata.py +47 -0
  416. loopx/control_plane/goals/active_state_sections.py +58 -0
  417. loopx/control_plane/goals/configure_goal_service.py +354 -0
  418. loopx/control_plane/goals/contract_health.py +132 -0
  419. loopx/control_plane/goals/dreaming.py +152 -0
  420. loopx/control_plane/goals/global_registry_health.py +199 -0
  421. loopx/control_plane/goals/global_registry_shadow.py +33 -0
  422. loopx/control_plane/goals/goal_channel.py +34 -0
  423. loopx/control_plane/goals/goal_channel_projection.py +560 -0
  424. loopx/control_plane/goals/goal_frontier/__init__.py +1917 -0
  425. loopx/control_plane/goals/goal_frontier/ack_policy.py +149 -0
  426. loopx/control_plane/goals/goal_frontier/outcome_continuity.py +437 -0
  427. loopx/control_plane/goals/goal_frontier/replan_rules.py +210 -0
  428. loopx/control_plane/goals/goal_frontier/semantic_history.py +314 -0
  429. loopx/control_plane/goals/goal_frontier/terminal.py +180 -0
  430. loopx/control_plane/goals/goal_vision.py +443 -0
  431. loopx/control_plane/goals/goal_vision_policy.py +36 -0
  432. loopx/control_plane/goals/goal_vision_state.py +62 -0
  433. loopx/control_plane/goals/goal_vision_wait.py +290 -0
  434. loopx/control_plane/goals/path_resolution.py +20 -0
  435. loopx/control_plane/goals/start_contract.py +206 -0
  436. loopx/control_plane/goals/vision_checkpoint.py +92 -0
  437. loopx/control_plane/handoff/__init__.py +1 -0
  438. loopx/control_plane/handoff/cross_runtime_impl_review.py +311 -0
  439. loopx/control_plane/handoff/delivery_contract.py +161 -0
  440. loopx/control_plane/handoff/handoff_runs.py +71 -0
  441. loopx/control_plane/handoff/project_handoff.py +155 -0
  442. loopx/control_plane/handoff/review_batch.py +463 -0
  443. loopx/control_plane/handoff/review_packet_context.py +216 -0
  444. loopx/control_plane/heartbeat/agent.py +173 -0
  445. loopx/control_plane/heartbeat/budget.py +66 -0
  446. loopx/control_plane/heartbeat/builder.py +501 -0
  447. loopx/control_plane/heartbeat/host.py +64 -0
  448. loopx/control_plane/heartbeat/rules.py +68 -0
  449. loopx/control_plane/heartbeat/task_body.py +759 -0
  450. loopx/control_plane/heartbeat/visible_goal.py +86 -0
  451. loopx/control_plane/projects/__init__.py +1 -0
  452. loopx/control_plane/projects/contract.py +25 -0
  453. loopx/control_plane/projects/registry.py +663 -0
  454. loopx/control_plane/quota/__init__.py +1 -0
  455. loopx/control_plane/quota/cli_projection.py +704 -0
  456. loopx/control_plane/quota/decision_summary.py +431 -0
  457. loopx/control_plane/quota/effect_program.py +152 -0
  458. loopx/control_plane/quota/error_codes.py +19 -0
  459. loopx/control_plane/quota/goal_boundary.py +464 -0
  460. loopx/control_plane/quota/heartbeat_receipt.py +277 -0
  461. loopx/control_plane/quota/heartbeat_recommendation.py +718 -0
  462. loopx/control_plane/quota/host_poll_receipts.py +162 -0
  463. loopx/control_plane/quota/live_decision.py +142 -0
  464. loopx/control_plane/quota/monitor_poll.py +786 -0
  465. loopx/control_plane/quota/policy_constants.py +40 -0
  466. loopx/control_plane/quota/projection_repair.py +262 -0
  467. loopx/control_plane/quota/recent_runs.py +210 -0
  468. loopx/control_plane/quota/scheduler_ack.py +490 -0
  469. loopx/control_plane/quota/selected_todo_projection.py +139 -0
  470. loopx/control_plane/quota/settlement.py +437 -0
  471. loopx/control_plane/quota/settlement_cli.py +246 -0
  472. loopx/control_plane/quota/settlement_validation.py +64 -0
  473. loopx/control_plane/quota/settlement_workspace_causality.py +180 -0
  474. loopx/control_plane/quota/should_run.py +249 -0
  475. loopx/control_plane/quota/should_run_packet.py +1165 -0
  476. loopx/control_plane/quota/should_run_prepare.py +675 -0
  477. loopx/control_plane/quota/slot_accounting.py +1123 -0
  478. loopx/control_plane/quota/spend_sources.py +11 -0
  479. loopx/control_plane/quota/stall_repair.py +397 -0
  480. loopx/control_plane/quota/states.py +29 -0
  481. loopx/control_plane/quota/task_orchestration.py +448 -0
  482. loopx/control_plane/quota/task_orchestration_admission.py +497 -0
  483. loopx/control_plane/quota/turn_envelope.py +889 -0
  484. loopx/control_plane/quota/usage_summary.py +140 -0
  485. loopx/control_plane/reward_memory.py +43 -0
  486. loopx/control_plane/runtime/__init__.py +2 -0
  487. loopx/control_plane/runtime/active_user_assisted_pilot.py +275 -0
  488. loopx/control_plane/runtime/agent_scoped_evidence_log.py +435 -0
  489. loopx/control_plane/runtime/decision_freshness.py +203 -0
  490. loopx/control_plane/runtime/event_ledger.py +197 -0
  491. loopx/control_plane/runtime/event_store_migration_bridge.py +196 -0
  492. loopx/control_plane/runtime/goal_project_route.py +70 -0
  493. loopx/control_plane/runtime/local_state_write_correctness.py +242 -0
  494. loopx/control_plane/runtime/promotion_readiness.py +152 -0
  495. loopx/control_plane/runtime/public_safety.py +120 -0
  496. loopx/control_plane/runtime/run_artifacts.py +78 -0
  497. loopx/control_plane/runtime/run_compaction.py +397 -0
  498. loopx/control_plane/runtime/run_context_retention.py +241 -0
  499. loopx/control_plane/runtime/run_history.py +132 -0
  500. loopx/control_plane/runtime/run_index_duplicates.py +205 -0
  501. loopx/control_plane/runtime/run_index_rebuild.py +263 -0
  502. loopx/control_plane/runtime/run_ingest_health.py +336 -0
  503. loopx/control_plane/runtime/runtime_projection_route.py +624 -0
  504. loopx/control_plane/runtime/runtime_projection_writer.py +98 -0
  505. loopx/control_plane/runtime/session_runtime.py +339 -0
  506. loopx/control_plane/runtime/shared_runtime_material_projection.py +332 -0
  507. loopx/control_plane/runtime/shared_runtime_refresh_projection.py +183 -0
  508. loopx/control_plane/runtime/stale_latest_run.py +90 -0
  509. loopx/control_plane/runtime/status_classifications.py +49 -0
  510. loopx/control_plane/runtime/status_projection_cache.py +235 -0
  511. loopx/control_plane/runtime/stride_observation.py +144 -0
  512. loopx/control_plane/runtime/time.py +39 -0
  513. loopx/control_plane/runtime/trajectory_hygiene.py +149 -0
  514. loopx/control_plane/runtime/validation_command.py +69 -0
  515. loopx/control_plane/scheduler/__init__.py +1 -0
  516. loopx/control_plane/scheduler/ack.py +329 -0
  517. loopx/control_plane/scheduler/arbitration.py +188 -0
  518. loopx/control_plane/scheduler/automation_liveness.py +183 -0
  519. loopx/control_plane/scheduler/execution_context.py +555 -0
  520. loopx/control_plane/scheduler/external_evidence_observation.py +428 -0
  521. loopx/control_plane/scheduler/monitor_display.py +143 -0
  522. loopx/control_plane/scheduler/monitor_poll_policy.py +161 -0
  523. loopx/control_plane/scheduler/monitor_poll_writeback.py +351 -0
  524. loopx/control_plane/scheduler/monitor_target.py +64 -0
  525. loopx/control_plane/scheduler/monitor_todo.py +146 -0
  526. loopx/control_plane/scheduler/monitor_wait.py +237 -0
  527. loopx/control_plane/scheduler/scheduler_hint.py +1284 -0
  528. loopx/control_plane/scheduler/state.py +354 -0
  529. loopx/control_plane/scheduler/state_transition_rules.py +179 -0
  530. loopx/control_plane/scheduler/time.py +10 -0
  531. loopx/control_plane/settlement_driver.py +293 -0
  532. loopx/control_plane/status/__init__.py +6 -0
  533. loopx/control_plane/status/active_state_projection.py +105 -0
  534. loopx/control_plane/status/agent_lane_projection.py +375 -0
  535. loopx/control_plane/status/attention_projection.py +74 -0
  536. loopx/control_plane/status/autonomous_replan_projection.py +103 -0
  537. loopx/control_plane/status/collection.py +140 -0
  538. loopx/control_plane/status/contract_projection.py +31 -0
  539. loopx/control_plane/status/dreaming_projection.py +52 -0
  540. loopx/control_plane/status/goal_attention_projection.py +157 -0
  541. loopx/control_plane/status/lifecycle_projection.py +110 -0
  542. loopx/control_plane/status/monitor_display_projection.py +69 -0
  543. loopx/control_plane/status/registry_health_projection.py +75 -0
  544. loopx/control_plane/status/run_projection.py +70 -0
  545. loopx/control_plane/status/runtime_summaries.py +161 -0
  546. loopx/control_plane/testing/__init__.py +1 -0
  547. loopx/control_plane/testing/actual_default_model_behavior_portfolio.py +1371 -0
  548. loopx/control_plane/testing/canary_harness.py +182 -0
  549. loopx/control_plane/testing/capability_monitor_repair_tool_behavior.py +674 -0
  550. loopx/control_plane/testing/cli_output_budget.py +807 -0
  551. loopx/control_plane/testing/cli_output_differential.py +250 -0
  552. loopx/control_plane/testing/cli_output_semantics.py +87 -0
  553. loopx/control_plane/testing/control_plane_composition_scenarios.py +225 -0
  554. loopx/control_plane/testing/decision_replay.py +268 -0
  555. loopx/control_plane/testing/doubao_model_behavior_actor.py +559 -0
  556. loopx/control_plane/testing/model_behavior_corpus.py +344 -0
  557. loopx/control_plane/testing/model_behavior_qualification.py +769 -0
  558. loopx/control_plane/testing/model_behavior_retained_cases.py +235 -0
  559. loopx/control_plane/testing/model_tool_behavior.py +536 -0
  560. loopx/control_plane/testing/onboarding_model_behavior_qualification.py +642 -0
  561. loopx/control_plane/testing/quota_fixtures.py +208 -0
  562. loopx/control_plane/testing/quota_should_run_parity.py +57 -0
  563. loopx/control_plane/testing/release_commit_qualification.py +671 -0
  564. loopx/control_plane/testing/replan_semantic_action_behavior.py +1302 -0
  565. loopx/control_plane/testing/scoped_gate_successor_tool_behavior.py +527 -0
  566. loopx/control_plane/testing/selected_todo_tool_behavior.py +1002 -0
  567. loopx/control_plane/testing/terminal_settlement_tool_behavior.py +656 -0
  568. loopx/control_plane/todos/__init__.py +1 -0
  569. loopx/control_plane/todos/active_state_editing.py +296 -0
  570. loopx/control_plane/todos/active_state_todo_parser.py +138 -0
  571. loopx/control_plane/todos/active_state_todos.py +175 -0
  572. loopx/control_plane/todos/addition.py +103 -0
  573. loopx/control_plane/todos/claim_visibility.py +253 -0
  574. loopx/control_plane/todos/completed_archive.py +139 -0
  575. loopx/control_plane/todos/completion_fence.py +49 -0
  576. loopx/control_plane/todos/completion_policy.py +153 -0
  577. loopx/control_plane/todos/completion_validation.py +248 -0
  578. loopx/control_plane/todos/completion_validation_accountability.py +27 -0
  579. loopx/control_plane/todos/completion_validation_projection.py +57 -0
  580. loopx/control_plane/todos/contract.py +1476 -0
  581. loopx/control_plane/todos/decision_scope.py +554 -0
  582. loopx/control_plane/todos/deferred_resume.py +546 -0
  583. loopx/control_plane/todos/durable_completion.py +201 -0
  584. loopx/control_plane/todos/event_writeback.py +484 -0
  585. loopx/control_plane/todos/frontier_deadline.py +132 -0
  586. loopx/control_plane/todos/handoff_gate.py +283 -0
  587. loopx/control_plane/todos/handoff_mode.py +444 -0
  588. loopx/control_plane/todos/handoff_note.py +202 -0
  589. loopx/control_plane/todos/line_update.py +361 -0
  590. loopx/control_plane/todos/list_projection.py +205 -0
  591. loopx/control_plane/todos/markdown.py +199 -0
  592. loopx/control_plane/todos/monitor_metadata.py +88 -0
  593. loopx/control_plane/todos/mutation_authority.py +299 -0
  594. loopx/control_plane/todos/projection.py +655 -0
  595. loopx/control_plane/todos/quota_summary.py +1138 -0
  596. loopx/control_plane/todos/route_continuation.py +267 -0
  597. loopx/control_plane/todos/succession_warning.py +174 -0
  598. loopx/control_plane/todos/summary_item.py +223 -0
  599. loopx/control_plane/todos/text.py +30 -0
  600. loopx/control_plane/todos/todo_index.py +226 -0
  601. loopx/control_plane/todos/todo_summary.py +1458 -0
  602. loopx/control_plane/todos/unblock_resume.py +326 -0
  603. loopx/control_plane/todos/user_gate.py +263 -0
  604. loopx/control_plane/todos/write_hint.py +63 -0
  605. loopx/control_plane/todos/write_policy.py +135 -0
  606. loopx/control_plane/turn_driver/__init__.py +85 -0
  607. loopx/control_plane/turn_driver/codex_cli.py +502 -0
  608. loopx/control_plane/turn_driver/driver.py +355 -0
  609. loopx/control_plane/turn_driver/executor.py +1468 -0
  610. loopx/control_plane/turn_driver/loop_controller.py +669 -0
  611. loopx/control_plane/turn_driver/settlement.py +318 -0
  612. loopx/control_plane/turn_driver/transaction.py +375 -0
  613. loopx/control_plane/work_items/__init__.py +1 -0
  614. loopx/control_plane/work_items/attention_fields.py +56 -0
  615. loopx/control_plane/work_items/attention_item.py +77 -0
  616. loopx/control_plane/work_items/attention_queue.py +322 -0
  617. loopx/control_plane/work_items/attention_routing.py +213 -0
  618. loopx/control_plane/work_items/autonomous_candidates.py +135 -0
  619. loopx/control_plane/work_items/autonomous_replan_ack.py +276 -0
  620. loopx/control_plane/work_items/autonomous_replan_obligation.py +786 -0
  621. loopx/control_plane/work_items/backlog_hygiene.py +59 -0
  622. loopx/control_plane/work_items/capability_monitor_fallback.py +221 -0
  623. loopx/control_plane/work_items/delivery_batch_scale.py +66 -0
  624. loopx/control_plane/work_items/delivery_outcome.py +152 -0
  625. loopx/control_plane/work_items/delivery_signals.py +113 -0
  626. loopx/control_plane/work_items/execution_obligation.py +235 -0
  627. loopx/control_plane/work_items/goal_route_hint.py +320 -0
  628. loopx/control_plane/work_items/interaction_contract.py +1540 -0
  629. loopx/control_plane/work_items/issue_meta_surface.py +159 -0
  630. loopx/control_plane/work_items/lifecycle.py +139 -0
  631. loopx/control_plane/work_items/operator_inbox.py +266 -0
  632. loopx/control_plane/work_items/outcome_followthrough.py +69 -0
  633. loopx/control_plane/work_items/primary_action.py +326 -0
  634. loopx/control_plane/work_items/progress_observation.py +630 -0
  635. loopx/control_plane/work_items/project_asset.py +675 -0
  636. loopx/control_plane/work_items/repair_delta.py +693 -0
  637. loopx/control_plane/work_items/runtime_capability_reentry.py +168 -0
  638. loopx/control_plane/work_items/semantic_replan_writeback.py +177 -0
  639. loopx/control_plane/work_items/status_contract.py +49 -0
  640. loopx/control_plane/work_items/task_graph.py +1046 -0
  641. loopx/control_plane/work_items/task_lease.py +1254 -0
  642. loopx/control_plane/work_items/task_lease_settlement.py +422 -0
  643. loopx/control_plane/work_items/work_lane.py +510 -0
  644. loopx/control_plane/work_items/work_lane_context.py +161 -0
  645. loopx/demo.py +247 -0
  646. loopx/diagnose.py +633 -0
  647. loopx/doctor.py +1251 -0
  648. loopx/domain_packs/__init__.py +1 -0
  649. loopx/domain_packs/issue_fix.py +571 -0
  650. loopx/domain_packs/ml_experiment.py +854 -0
  651. loopx/domain_state.py +137 -0
  652. loopx/dreaming.py +706 -0
  653. loopx/entrypoint.py +16 -0
  654. loopx/event_sourced_state.py +981 -0
  655. loopx/execution_profile.py +286 -0
  656. loopx/experiments/__init__.py +1 -0
  657. loopx/experiments/planner_worker/__init__.py +1 -0
  658. loopx/experiments/planner_worker/contract.py +523 -0
  659. loopx/experiments/planner_worker/runtime.py +391 -0
  660. loopx/experiments/planner_worker/traex.py +461 -0
  661. loopx/explore_graph.py +11 -0
  662. loopx/extensions/__init__.py +1 -0
  663. loopx/extensions/bundled.py +28 -0
  664. loopx/extensions/execution_envelope.py +126 -0
  665. loopx/extensions/lark/__init__.py +11 -0
  666. loopx/extensions/lark/event_collector.py +478 -0
  667. loopx/extensions/lark/event_collector_runtime.py +506 -0
  668. loopx/extensions/lark/event_inbox.py +454 -0
  669. loopx/extensions/lark/extension.toml +88 -0
  670. loopx/extensions/lark/goal_channel.py +44 -0
  671. loopx/extensions/lark/goal_channel_contracts.py +388 -0
  672. loopx/extensions/lark/goal_channel_lifecycle.py +218 -0
  673. loopx/extensions/lark/goal_channel_runtime.py +792 -0
  674. loopx/extensions/lark/goal_channel_setup.py +805 -0
  675. loopx/extensions/lark/goal_channel_targets.py +215 -0
  676. loopx/extensions/lark/goal_channel_transport.py +281 -0
  677. loopx/extensions/lark/inbox_reactions.py +650 -0
  678. loopx/extensions/lark/inbox_reply.py +430 -0
  679. loopx/extensions/lark/presentation/__init__.py +11 -0
  680. loopx/extensions/lark/presentation/explore_results.py +2276 -0
  681. loopx/extensions/lark/presentation/explore_singleflight.py +127 -0
  682. loopx/extensions/lark/presentation/explore_source_guard.py +121 -0
  683. loopx/extensions/lark/presentation/explore_stage_document.py +703 -0
  684. loopx/extensions/lark/presentation/explore_visual_integrity.py +122 -0
  685. loopx/extensions/lark/presentation/explore_visual_readback.py +452 -0
  686. loopx/extensions/lark/presentation/explore_visual_styles.py +156 -0
  687. loopx/extensions/lark/presentation/issue_fix_surface.py +612 -0
  688. loopx/extensions/lark/presentation/kanban.py +2791 -0
  689. loopx/extensions/lark/presentation/message_card.py +112 -0
  690. loopx/extensions/lark/presentation/periodic_report.py +261 -0
  691. loopx/extensions/lark/presentation/projection_rows.py +600 -0
  692. loopx/extensions/lark/presentation/record_io.py +95 -0
  693. loopx/extensions/lark/presentation/sync_receipt.py +145 -0
  694. loopx/extensions/lark/private_json.py +40 -0
  695. loopx/extensions/lark/provider.py +86 -0
  696. loopx/extensions/lark/reviewer_notification.py +604 -0
  697. loopx/extensions/manifest.py +385 -0
  698. loopx/extensions/openviking_periodic_report/__init__.py +17 -0
  699. loopx/extensions/openviking_periodic_report/activation.py +173 -0
  700. loopx/extensions/openviking_periodic_report/extension.toml +17 -0
  701. loopx/extensions/openviking_periodic_report/provider.py +355 -0
  702. loopx/extensions/openviking_periodic_report/sink.py +117 -0
  703. loopx/extensions/openviking_semantic_preference/__init__.py +5 -0
  704. loopx/extensions/openviking_semantic_preference/extension.toml +16 -0
  705. loopx/extensions/openviking_semantic_preference/history_export.py +484 -0
  706. loopx/extensions/openviking_semantic_preference/project_peer.py +68 -0
  707. loopx/extensions/openviking_semantic_preference/provider.py +312 -0
  708. loopx/extensions/presentation.py +979 -0
  709. loopx/extensions/process_runtime.py +204 -0
  710. loopx/extensions/readiness.py +168 -0
  711. loopx/extensions/runtime.py +931 -0
  712. loopx/extensions/scaffold.py +335 -0
  713. loopx/feedback.py +581 -0
  714. loopx/file_lock.py +382 -0
  715. loopx/global_registry.py +842 -0
  716. loopx/global_risks.py +970 -0
  717. loopx/global_todos.py +568 -0
  718. loopx/handoff_budget.py +28 -0
  719. loopx/heartbeat_prequota.py +80 -0
  720. loopx/heartbeat_prompt.py +159 -0
  721. loopx/help_surface.py +516 -0
  722. loopx/history.py +1507 -0
  723. loopx/host_loop_activation.py +1311 -0
  724. loopx/host_mode_planner.py +991 -0
  725. loopx/install_contract.py +1 -0
  726. loopx/interface_budget.py +196 -0
  727. loopx/long_task_cadence.py +208 -0
  728. loopx/materials.py +185 -0
  729. loopx/ml_experiment.py +3 -0
  730. loopx/onboarding.py +214 -0
  731. loopx/opencode2_goal_mode/README.md +81 -0
  732. loopx/opencode2_goal_mode/__init__.py +9 -0
  733. loopx/opencode2_goal_mode/opencode2-goal-worker.mjs +1018 -0
  734. loopx/opencode_goal_mode/README.md +99 -0
  735. loopx/opencode_goal_mode/__init__.py +13 -0
  736. loopx/opencode_goal_mode/goal-bridge-runtime.mjs +858 -0
  737. loopx/opencode_goal_mode/loopx-goal.js +8 -0
  738. loopx/operator_gate.py +420 -0
  739. loopx/orchestration.py +127 -0
  740. loopx/paths.py +59 -0
  741. loopx/pi_goal_mode/README.md +67 -0
  742. loopx/pi_goal_mode/__init__.py +13 -0
  743. loopx/pi_goal_mode/loopx-goal.ts +254 -0
  744. loopx/pi_goal_mode/pi-goal-loop-runtime.mjs +574 -0
  745. loopx/pr_review.py +1206 -0
  746. loopx/presentation/__init__.py +1 -0
  747. loopx/presentation/explore_views.py +1334 -0
  748. loopx/presentation/markdown.py +61 -0
  749. loopx/presentation/projection_source_reconcile.py +140 -0
  750. loopx/presentation/public_safety.py +42 -0
  751. loopx/presentation/renderers/__init__.py +17 -0
  752. loopx/presentation/renderers/goal_channel_html.py +269 -0
  753. loopx/presentation/renderers/periodic_report_html.py +786 -0
  754. loopx/presentation/renderers/periodic_report_markdown.py +184 -0
  755. loopx/presentation/renderers/quota_event_markdown.py +116 -0
  756. loopx/presentation/renderers/quota_markdown.py +1112 -0
  757. loopx/presentation/renderers/status_markdown.py +1570 -0
  758. loopx/presentation/renderers/trajectory_hygiene_markdown.py +39 -0
  759. loopx/presentation/renderers/turn_envelope_markdown.py +33 -0
  760. loopx/presentation/sinks/__init__.py +5 -0
  761. loopx/presentation/sinks/openviking_periodic_report.py +7 -0
  762. loopx/presentation/static_site.py +691 -0
  763. loopx/presets.py +369 -0
  764. loopx/project_alias.py +217 -0
  765. loopx/project_map.py +589 -0
  766. loopx/project_prompt.py +1153 -0
  767. loopx/project_skill_cli.py +125 -0
  768. loopx/project_skill_delivery.py +470 -0
  769. loopx/project_uninstall.py +462 -0
  770. loopx/promotion_gate.py +197 -0
  771. loopx/quota.py +1197 -0
  772. loopx/ready_score.py +413 -0
  773. loopx/registry.py +621 -0
  774. loopx/registry_writability.py +64 -0
  775. loopx/release_candidate.py +148 -0
  776. loopx/release_manifest.py +316 -0
  777. loopx/repository_identity.py +100 -0
  778. loopx/review_packet.py +1024 -0
  779. loopx/rollout_event_log.py +505 -0
  780. loopx/runtime.py +112 -0
  781. loopx/self_update.py +750 -0
  782. loopx/session_runtime.py +418 -0
  783. loopx/skill_install_readback.py +500 -0
  784. loopx/slash_command_install.py +1393 -0
  785. loopx/slash_commands.py +264 -0
  786. loopx/state_backup.py +573 -0
  787. loopx/state_migration.py +350 -0
  788. loopx/state_projection.py +809 -0
  789. loopx/state_refresh.py +1416 -0
  790. loopx/status.py +1383 -0
  791. loopx/status_server.py +935 -0
  792. loopx/summary_all.py +725 -0
  793. loopx/terminal_bench_agent.py +2056 -0
  794. loopx/thread_agent_binding.py +408 -0
  795. loopx/todo_followups.py +168 -0
  796. loopx/todo_suggestion_prompt.py +204 -0
  797. loopx/todos.py +2229 -0
  798. loopx/turn_identity.py +17 -0
  799. loopx/upgrade.py +1083 -0
  800. loopx/visible_governance.py +667 -0
  801. loopx/visible_multi_agent_launcher.py +1253 -0
  802. loopx/visible_multi_agent_tmux.py +429 -0
  803. loopx/worker_bridge.py +1574 -0
  804. loopx-0.4.8.dist-info/METADATA +708 -0
  805. loopx-0.4.8.dist-info/RECORD +811 -0
  806. loopx-0.4.8.dist-info/WHEEL +5 -0
  807. loopx-0.4.8.dist-info/entry_points.txt +5 -0
  808. loopx-0.4.8.dist-info/licenses/LICENSE +202 -0
  809. loopx-0.4.8.dist-info/licenses/LICENSE-MIT +21 -0
  810. loopx-0.4.8.dist-info/licenses/NOTICE +6 -0
  811. loopx-0.4.8.dist-info/top_level.txt +1 -0
@@ -0,0 +1,991 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from .control_plane.scheduler.execution_context import (
6
+ GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
7
+ scheduler_execution_context_for_turn,
8
+ )
9
+ from .control_plane.turn_driver.driver import SUPPORTED_HOSTS
10
+ from .host_loop_activation import _identity_state
11
+ from .project_prompt import render_quota_guard_command, shell_arg
12
+
13
+
14
+ SCHEMA_VERSION = "host_mode_plan_v0"
15
+
16
+ # Host-mode selector modes. These are user/operator choices that map onto the
17
+ # shipped connector catalog and LoopX Turn contract; they are not a second
18
+ # runtime authority.
19
+ MODE_VISIBLE_TUI = "visible_tui"
20
+ MODE_ISOLATED_HEADLESS_TURN = "isolated_headless_turn"
21
+ MODE_IM_GATEWAY = "im_gateway"
22
+ MODE_SHELL_SERVICE = "shell_service"
23
+ MODE_HYBRID_HANDOFF = "hybrid_handoff"
24
+
25
+ CAP_VISIBLE_SESSION = "visible_session"
26
+ CAP_LOOPX_TURN = "loopx_turn"
27
+ CAP_TYPED_HOST_ADAPTER = "typed_host_adapter"
28
+ CAP_INDEPENDENT_VALIDATOR = "independent_validator"
29
+ CAP_CHAT_GATEWAY = "chat_gateway"
30
+ CAP_SERVICE_TIMER = "service_timer"
31
+ CAP_SHELL = "shell"
32
+
33
+ INTENT_WATCH_EACH_TURN = "watch_each_turn"
34
+ INTENT_CONTINUE_WITHOUT_UI = "continue_without_ui"
35
+ INTENT_INTAKE_FROM_CHAT = "intake_from_chat"
36
+ INTENT_TIMER_KEEPALIVE = "timer_keepalive"
37
+ INTENT_ESCALATE_BETWEEN_MODES = "escalate_between_modes"
38
+
39
+ _SHARED_PROOFS = [
40
+ "scoped_agent_identity",
41
+ "quota_guard_before_run",
42
+ "turn_envelope_or_connector_contract_preserved",
43
+ "readiness_probe",
44
+ ]
45
+ _TURN_PROOFS = [
46
+ "loopx_turn_plan_preview",
47
+ "typed_host_adapter",
48
+ "independent_validation",
49
+ "validated_writeback_before_quota_spend",
50
+ ]
51
+
52
+ _MODE_METADATA: dict[str, dict[str, Any]] = {
53
+ MODE_VISIBLE_TUI: {
54
+ "intent": INTENT_WATCH_EACH_TURN,
55
+ "connector_id": "codex_cli_tui",
56
+ "turn_host": "codex-cli",
57
+ "turn_execution_mode": "interactive-visible",
58
+ "scheduler_owner": "agent_cli_loop",
59
+ "required_capabilities": [CAP_VISIBLE_SESSION],
60
+ "summary": "Keep work in a visible Codex/agent TUI so the user can watch or steer each turn.",
61
+ "extra_proofs": ["visible_surface_preserved", "clear_gate_handling"],
62
+ },
63
+ MODE_ISOLATED_HEADLESS_TURN: {
64
+ "intent": INTENT_CONTINUE_WITHOUT_UI,
65
+ "connector_id": "loopx_turn",
66
+ "turn_host": "generic-cli",
67
+ "turn_execution_mode": "isolated-headless",
68
+ "scheduler_owner": "outer_controller",
69
+ "required_capabilities": [CAP_LOOPX_TURN, CAP_TYPED_HOST_ADAPTER, CAP_INDEPENDENT_VALIDATOR],
70
+ "summary": "Use the shipped LoopX Turn path for bounded headless execution with typed results.",
71
+ "extra_proofs": [*_TURN_PROOFS, "opaque_session_handle_not_public"],
72
+ },
73
+ MODE_IM_GATEWAY: {
74
+ "intent": INTENT_INTAKE_FROM_CHAT,
75
+ "connector_id": "http_webhook",
76
+ "turn_host": None,
77
+ "turn_execution_mode": None,
78
+ "scheduler_owner": None,
79
+ "required_capabilities": [CAP_CHAT_GATEWAY],
80
+ "summary": "Use a chat/webhook gateway only for durable intake, then hand execution to LoopX state.",
81
+ "extra_proofs": ["durable_todo_intake", "text_or_card_fallback", "source_boundary"],
82
+ },
83
+ MODE_SHELL_SERVICE: {
84
+ "intent": INTENT_TIMER_KEEPALIVE,
85
+ "connector_id": "shell_worker",
86
+ "turn_host": "generic-cli",
87
+ "turn_execution_mode": "isolated-headless",
88
+ "scheduler_owner": "outer_controller",
89
+ "required_capabilities": [CAP_SERVICE_TIMER, CAP_SHELL, CAP_LOOPX_TURN, CAP_TYPED_HOST_ADAPTER, CAP_INDEPENDENT_VALIDATOR],
90
+ "summary": "Let a shell, cron, launchd, or service timer wake bounded LoopX Turn previews/runs.",
91
+ "extra_proofs": [*_TURN_PROOFS, "scheduler_hint_backoff", "no_spend_quiet_monitor"],
92
+ },
93
+ MODE_HYBRID_HANDOFF: {
94
+ "intent": INTENT_ESCALATE_BETWEEN_MODES,
95
+ "connector_id": "hybrid_handoff",
96
+ "turn_host": None,
97
+ "turn_execution_mode": None,
98
+ "scheduler_owner": None,
99
+ "required_capabilities": [],
100
+ "summary": "Plan an explicit transition between visible, intake, timer, and headless Turn modes.",
101
+ "extra_proofs": [
102
+ "explicit_transition_event",
103
+ "target_mode_readiness",
104
+ "shared_agent_id_writeback",
105
+ "visible_escalation_for_user_gate",
106
+ ],
107
+ },
108
+ }
109
+
110
+ CANONICAL_MODES = list(_MODE_METADATA)
111
+ SUPPORTED_INTENTS = [meta["intent"] for meta in _MODE_METADATA.values()]
112
+ SUPPORTED_HOST_CAPABILITIES = [
113
+ CAP_VISIBLE_SESSION,
114
+ CAP_LOOPX_TURN,
115
+ CAP_TYPED_HOST_ADAPTER,
116
+ CAP_INDEPENDENT_VALIDATOR,
117
+ CAP_CHAT_GATEWAY,
118
+ CAP_SERVICE_TIMER,
119
+ CAP_SHELL,
120
+ ]
121
+ _INTENT_PRIMARY_MODE = {meta["intent"]: mode for mode, meta in _MODE_METADATA.items()}
122
+
123
+ # Host identities that map to a concrete visible Turn host. Visible TUI mapping
124
+ # requires an explicit host identity so Codex CLI, Claude Code, and generic CLI
125
+ # sessions keep their real host binding instead of a hard-coded Codex default.
126
+ SUPPORTED_TURN_HOST_IDENTITIES = sorted(SUPPORTED_HOSTS)
127
+
128
+ # Typed host identity -> runtime connector catalog id. Only identities with a
129
+ # registered catalog connector may emit a host-specific visible mapping; any
130
+ # other identity fails closed instead of fabricating a connector id.
131
+ VISIBLE_HOST_CONNECTOR_IDS: dict[str, str] = {
132
+ "codex-cli": "codex_cli_tui",
133
+ "claude-code": "claude_code_loop",
134
+ # OpenCode runs its visible goal loop through the generic-cli Turn host;
135
+ # parity lives in the selector/catalog mapping, not a new Turn host kind.
136
+ "generic-cli": "opencode_goal_loop",
137
+ }
138
+
139
+ # Host identities that are valid visible selections but map to the OpenCode
140
+ # goal loop connector rather than their own Turn host kind.
141
+ VISIBLE_OPENCODE_ALIASES: dict[str, str] = {
142
+ "opencode": "generic-cli",
143
+ "open-code": "generic-cli",
144
+ "opencode2": "generic-cli",
145
+ "opencode-2": "generic-cli",
146
+ }
147
+
148
+ # Pi runs its visible goal loop through the generic-cli Turn host, same as
149
+ # OpenCode, but keeps its own goal-loop connector identity.
150
+ VISIBLE_PI_ALIASES: dict[str, str] = {
151
+ "pi": "generic-cli",
152
+ }
153
+
154
+ # Connector id override for source host identities that alias onto the
155
+ # generic-cli Turn host. The normalized Turn host stays generic-cli while the
156
+ # visible connector keeps naming the real host loop.
157
+ VISIBLE_CONNECTOR_OVERRIDES: dict[str, str] = {
158
+ "pi": "pi_goal_loop",
159
+ }
160
+
161
+
162
+ CAPABILITY_GUIDANCE = {
163
+ CAP_VISIBLE_SESSION: "A visible session is required so the user can watch, steer, or take over safely.",
164
+ CAP_LOOPX_TURN: "LoopX Turn support is required before headless execution can be trusted.",
165
+ CAP_TYPED_HOST_ADAPTER: "A typed host adapter is required so the host returns one machine-checkable result instead of free-form prose.",
166
+ CAP_INDEPENDENT_VALIDATOR: "An independent validator is required so LoopX does not trust the host's own completion claim.",
167
+ CAP_CHAT_GATEWAY: "A chat or webhook gateway is required before work can be created from an external intake surface.",
168
+ CAP_SERVICE_TIMER: "A service timer, cron, launchd, or equivalent wake mechanism is required for timer keepalive.",
169
+ CAP_SHELL: "Shell execution is required so the host can run LoopX preview and guard commands.",
170
+ }
171
+
172
+
173
+ class HostModePlanError(ValueError):
174
+ """Raised for unknown/empty intent, capability, or host selector input."""
175
+
176
+ def __init__(self, *, reason: str, field: str, suggestions: list[str] | None = None) -> None:
177
+ self.reason = reason
178
+ self.field = field
179
+ self.suggestions = suggestions or []
180
+ super().__init__(reason)
181
+
182
+ def to_payload(self) -> dict[str, Any]:
183
+ return {
184
+ "ok": False,
185
+ "schema_version": "host_mode_plan_error_v0",
186
+ "error_kind": "invalid_workflow_intent_or_capability",
187
+ "field": self.field,
188
+ "reason": self.reason,
189
+ "suggestions": self.suggestions,
190
+ }
191
+
192
+
193
+ def _normalize_tokens(values: Any) -> list[str]:
194
+ if values is None:
195
+ return []
196
+ if isinstance(values, str):
197
+ values = [values]
198
+ tokens: list[str] = []
199
+ for value in values:
200
+ token = str(value or "").strip().lower().replace("-", "_")
201
+ if token and token not in tokens:
202
+ tokens.append(token)
203
+ return tokens
204
+
205
+
206
+ def _normalize_supported(values: Any, *, field: str, supported: list[str], required: bool) -> list[str]:
207
+ tokens = _normalize_tokens(values)
208
+ if required and not tokens:
209
+ raise HostModePlanError(reason=f"{field} is required", field=field, suggestions=supported)
210
+ unknown = [token for token in tokens if token not in supported]
211
+ if unknown:
212
+ raise HostModePlanError(
213
+ reason=f"unsupported {field}: {', '.join(unknown)}",
214
+ field=field,
215
+ suggestions=supported,
216
+ )
217
+ return tokens
218
+
219
+
220
+ def _missing_mode_capabilities(mode: str, host_capabilities: list[str]) -> list[str]:
221
+ if mode == MODE_HYBRID_HANDOFF:
222
+ return []
223
+ required = _MODE_METADATA[mode]["required_capabilities"]
224
+ return [capability for capability in required if capability not in host_capabilities]
225
+
226
+
227
+ def _ready_concrete_modes(
228
+ host_capabilities: list[str], host_identity: str | None = None
229
+ ) -> list[str]:
230
+ return [
231
+ candidate
232
+ for candidate in CANONICAL_MODES
233
+ if candidate != MODE_HYBRID_HANDOFF
234
+ and not _missing_mode_capabilities(candidate, host_capabilities)
235
+ and not (
236
+ candidate == MODE_VISIBLE_TUI
237
+ and host_identity not in VISIBLE_HOST_CONNECTOR_IDS
238
+ )
239
+ ]
240
+
241
+
242
+ def _mode_capability_ready(
243
+ mode: str, host_capabilities: list[str], host_identity: str | None = None
244
+ ) -> bool:
245
+ if mode == MODE_VISIBLE_TUI and host_identity not in VISIBLE_HOST_CONNECTOR_IDS:
246
+ # Without an explicit catalog-registered host identity, a generic
247
+ # visible_session cannot establish which host is present; fail closed.
248
+ return False
249
+ if mode == MODE_HYBRID_HANDOFF:
250
+ return len(_ready_concrete_modes(host_capabilities, host_identity)) >= 2
251
+ return not _missing_mode_capabilities(mode, host_capabilities)
252
+
253
+
254
+ def _turn_plan_command(
255
+ *,
256
+ goal_id: str,
257
+ agent_id: str | None,
258
+ mode: str,
259
+ cli_bin: str,
260
+ available_capabilities: list[str] | None,
261
+ host_identity: str | None,
262
+ ) -> str | None:
263
+ meta = _MODE_METADATA[mode]
264
+ turn_host = meta.get("turn_host")
265
+ execution_mode = meta.get("turn_execution_mode")
266
+ if not turn_host or not execution_mode:
267
+ return None
268
+ if mode == MODE_VISIBLE_TUI:
269
+ # Visible mode requires an explicit, catalog-registered host identity.
270
+ # Without it, a coarse `visible_session` capability cannot distinguish
271
+ # Codex CLI, Claude Code, or OpenCode, so claiming any concrete host
272
+ # would fabricate attribution.
273
+ if not host_identity:
274
+ raise HostModePlanError(
275
+ reason=(
276
+ "visible_tui requires an explicit host_identity; a generic "
277
+ "visible_session capability cannot identify the actual host"
278
+ ),
279
+ field="host_identity",
280
+ suggestions=sorted(VISIBLE_HOST_CONNECTOR_IDS),
281
+ )
282
+ if host_identity not in VISIBLE_HOST_CONNECTOR_IDS:
283
+ raise HostModePlanError(
284
+ reason=(
285
+ f"host_identity {host_identity!r} has no registered catalog "
286
+ "connector for visible mode"
287
+ ),
288
+ field="host_identity",
289
+ suggestions=sorted(VISIBLE_HOST_CONNECTOR_IDS),
290
+ )
291
+ turn_host = host_identity
292
+ if turn_host not in SUPPORTED_HOSTS:
293
+ raise HostModePlanError(
294
+ reason=f"unsupported Turn host mapped by {mode}: {turn_host}",
295
+ field="mode",
296
+ suggestions=CANONICAL_MODES,
297
+ )
298
+ agent_arg = f" --agent-id {shell_arg(agent_id)}" if agent_id else ""
299
+ capability_args = "".join(
300
+ f" --available-capability {shell_arg(capability)}"
301
+ for capability in (available_capabilities or [])
302
+ )
303
+ scheduler_owner = meta.get("scheduler_owner")
304
+ scheduler_arg = f" --scheduler-owner {shell_arg(scheduler_owner)}" if scheduler_owner else ""
305
+ return (
306
+ f"{shell_arg(cli_bin)} turn plan --goal-id {shell_arg(goal_id)}{agent_arg} "
307
+ f"--host {shell_arg(turn_host)} --execution-mode {shell_arg(execution_mode)}"
308
+ f"{scheduler_arg}{capability_args}"
309
+ )
310
+
311
+
312
+ def _scheduler_context(mode: str, host_identity: str | None = None) -> dict[str, Any] | None:
313
+ meta = _MODE_METADATA[mode]
314
+ turn_host = meta.get("turn_host")
315
+ execution_mode = meta.get("turn_execution_mode")
316
+ if mode == MODE_VISIBLE_TUI:
317
+ if host_identity in VISIBLE_HOST_CONNECTOR_IDS:
318
+ turn_host = host_identity
319
+ else:
320
+ # No honest host binding exists without an explicit registered
321
+ # identity; do not project a fabricated host context.
322
+ return None
323
+ if not turn_host or not execution_mode:
324
+ if mode == MODE_IM_GATEWAY:
325
+ return None
326
+ if mode == MODE_HYBRID_HANDOFF:
327
+ return None
328
+ resolution = scheduler_execution_context_for_turn(
329
+ host=str(turn_host),
330
+ execution_mode=str(execution_mode),
331
+ scheduler_owner=meta.get("scheduler_owner"),
332
+ )
333
+ return resolution.projection()
334
+
335
+
336
+ def _mode_quota_guard(
337
+ *,
338
+ mode: str,
339
+ goal_id: str,
340
+ agent_id: str | None,
341
+ cli_bin: str,
342
+ available_capabilities: list[str] | None,
343
+ host_identity: str | None = None,
344
+ ) -> str:
345
+ scheduler_context = _scheduler_context(mode, host_identity)
346
+ runtime_profile = None
347
+ if mode == MODE_IM_GATEWAY:
348
+ runtime_profile = None
349
+ elif scheduler_context is None and mode == MODE_HYBRID_HANDOFF:
350
+ scheduler_context = GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT
351
+ return render_quota_guard_command(
352
+ goal_id,
353
+ cli_bin=cli_bin,
354
+ agent_id=agent_id,
355
+ available_capabilities=available_capabilities,
356
+ runtime_profile=runtime_profile,
357
+ scheduler_execution_context=scheduler_context,
358
+ )
359
+
360
+
361
+ def _blocking_reasons(
362
+ mode: str, host_capabilities: list[str], host_identity: str | None = None
363
+ ) -> list[str]:
364
+ if mode == MODE_HYBRID_HANDOFF:
365
+ ready_modes = _ready_concrete_modes(host_capabilities, host_identity)
366
+ if len(ready_modes) >= 2:
367
+ return []
368
+ return [
369
+ "Hybrid handoff requires at least two concrete ready modes so work can move between runtime surfaces safely."
370
+ ]
371
+ return [
372
+ CAPABILITY_GUIDANCE[capability]
373
+ for capability in _missing_mode_capabilities(mode, host_capabilities)
374
+ ]
375
+
376
+
377
+ def _recommended_next_steps(
378
+ *,
379
+ mode: str,
380
+ host_capabilities: list[str],
381
+ turn_plan_command: str | None,
382
+ quota_guard_command: str,
383
+ host_identity: str | None = None,
384
+ ) -> list[dict[str, Any]]:
385
+ missing = _missing_mode_capabilities(mode, host_capabilities)
386
+ steps: list[dict[str, Any]] = []
387
+ if mode == MODE_HYBRID_HANDOFF:
388
+ ready_modes = _ready_concrete_modes(host_capabilities, host_identity)
389
+ if len(ready_modes) < 2:
390
+ steps.append(
391
+ {
392
+ "step": 1,
393
+ "kind": "stop",
394
+ "action": "Do not plan a hybrid handoff yet; make at least two concrete host modes ready first.",
395
+ "command": None,
396
+ "no_spend": True,
397
+ }
398
+ )
399
+ else:
400
+ steps.append(
401
+ {
402
+ "step": 1,
403
+ "kind": "preview",
404
+ "action": "Preview the target mode before changing host surfaces.",
405
+ "command": quota_guard_command,
406
+ "no_spend": True,
407
+ }
408
+ )
409
+ steps.append(
410
+ {
411
+ "step": len(steps) + 1,
412
+ "kind": "handoff_gate",
413
+ "action": "Escalate back to visible_tui whenever a user gate, ambiguous decision, missing validator, or risky action appears.",
414
+ "command": None,
415
+ "no_spend": True,
416
+ }
417
+ )
418
+ return steps
419
+
420
+ if missing:
421
+ steps.append(
422
+ {
423
+ "step": 1,
424
+ "kind": "stop",
425
+ "action": "Stop before attempting this host mode; fill the missing host capabilities first.",
426
+ "command": None,
427
+ "no_spend": True,
428
+ }
429
+ )
430
+ for capability in missing:
431
+ steps.append(
432
+ {
433
+ "step": len(steps) + 1,
434
+ "kind": "capability_gap",
435
+ "capability": capability,
436
+ "action": CAPABILITY_GUIDANCE[capability],
437
+ "command": None,
438
+ "no_spend": True,
439
+ }
440
+ )
441
+ steps.append(
442
+ {
443
+ "step": len(steps) + 1,
444
+ "kind": "repreview",
445
+ "action": "Re-run loopx host-mode-plan after the missing capabilities are configured.",
446
+ "command": None,
447
+ "no_spend": True,
448
+ }
449
+ )
450
+ return steps
451
+
452
+ if mode == MODE_IM_GATEWAY:
453
+ steps.append(
454
+ {
455
+ "step": 1,
456
+ "kind": "intake",
457
+ "action": "Create durable work from the chat or webhook intake surface; the gateway itself does not execute.",
458
+ "command": None,
459
+ "no_spend": True,
460
+ }
461
+ )
462
+ steps.append(
463
+ {
464
+ "step": 2,
465
+ "kind": "state_preview",
466
+ "action": "Confirm current LoopX state is safe to proceed after intake is recorded.",
467
+ "command": quota_guard_command,
468
+ "no_spend": True,
469
+ }
470
+ )
471
+ steps.append(
472
+ {
473
+ "step": 3,
474
+ "kind": "handoff_gate",
475
+ "action": "Choose an execution mode for the durable work: isolated_headless_turn for unattended runs, visible_tui when a user gate or ambiguous decision appears.",
476
+ "command": None,
477
+ "no_spend": True,
478
+ }
479
+ )
480
+ return steps
481
+
482
+ steps.append(
483
+ {
484
+ "step": 1,
485
+ "kind": "state_preview",
486
+ "action": "Confirm current LoopX state is safe to proceed before host setup.",
487
+ "command": quota_guard_command,
488
+ "no_spend": True,
489
+ }
490
+ )
491
+ if turn_plan_command:
492
+ steps.append(
493
+ {
494
+ "step": len(steps) + 1,
495
+ "kind": "turn_preview",
496
+ "action": "Preview the bounded run before launching or resuming any host execution.",
497
+ "command": turn_plan_command,
498
+ "no_spend": True,
499
+ }
500
+ )
501
+ steps.append(
502
+ {
503
+ "step": len(steps) + 1,
504
+ "kind": "handoff_gate",
505
+ "action": "Escalate back to visible_tui whenever a user gate, ambiguous decision, missing validator, or risky action appears.",
506
+ "command": None,
507
+ "no_spend": True,
508
+ }
509
+ )
510
+ return steps
511
+
512
+
513
+ def _build_mode_option(
514
+ *,
515
+ mode: str,
516
+ goal_id: str,
517
+ agent_id: str | None,
518
+ host_capabilities: list[str],
519
+ cli_bin: str,
520
+ available_capabilities: list[str] | None,
521
+ host_identity: str | None,
522
+ connector_override_identity: str | None = None,
523
+ ) -> dict[str, Any]:
524
+ meta = _MODE_METADATA[mode]
525
+ visible_unresolved = (
526
+ mode == MODE_VISIBLE_TUI and host_identity not in VISIBLE_HOST_CONNECTOR_IDS
527
+ )
528
+ turn_plan_command = (
529
+ None
530
+ if visible_unresolved
531
+ else _turn_plan_command(
532
+ goal_id=goal_id,
533
+ agent_id=agent_id,
534
+ mode=mode,
535
+ cli_bin=cli_bin,
536
+ available_capabilities=available_capabilities,
537
+ host_identity=host_identity,
538
+ )
539
+ )
540
+ quota_guard_command = _mode_quota_guard(
541
+ mode=mode,
542
+ goal_id=goal_id,
543
+ agent_id=agent_id,
544
+ cli_bin=cli_bin,
545
+ available_capabilities=available_capabilities,
546
+ host_identity=host_identity if host_identity in VISIBLE_HOST_CONNECTOR_IDS else None,
547
+ )
548
+ if mode == MODE_VISIBLE_TUI:
549
+ connector_id = VISIBLE_CONNECTOR_OVERRIDES.get(
550
+ connector_override_identity or ""
551
+ ) or VISIBLE_HOST_CONNECTOR_IDS.get(host_identity or "")
552
+ effective_turn_host = host_identity if connector_id else None
553
+ host_resolution = (
554
+ "resolved"
555
+ if connector_id
556
+ else (
557
+ "identity_required"
558
+ if host_identity is None
559
+ else "unregistered_host_identity"
560
+ )
561
+ )
562
+ else:
563
+ connector_id = meta["connector_id"]
564
+ effective_turn_host = meta.get("turn_host")
565
+ host_resolution = "resolved"
566
+ recommended_next_steps = (
567
+ [
568
+ {
569
+ "step": 1,
570
+ "kind": "stop",
571
+ "action": (
572
+ "Re-run with --host-identity set to a catalog-registered visible host "
573
+ "(" + ", ".join(sorted(VISIBLE_HOST_CONNECTOR_IDS)) + ")."
574
+ ),
575
+ "command": None,
576
+ "no_spend": True,
577
+ }
578
+ ]
579
+ if visible_unresolved
580
+ else _recommended_next_steps(
581
+ mode=mode,
582
+ host_capabilities=host_capabilities,
583
+ turn_plan_command=turn_plan_command,
584
+ quota_guard_command=quota_guard_command,
585
+ host_identity=host_identity,
586
+ )
587
+ )
588
+ return {
589
+ "mode": mode,
590
+ "summary": meta["summary"],
591
+ "connector_id": connector_id,
592
+ "host_resolution": host_resolution,
593
+ "host_identity": host_identity if mode == MODE_VISIBLE_TUI else None,
594
+ "capability_ready": _mode_capability_ready(mode, host_capabilities, host_identity),
595
+ "required_host_capabilities": list(meta["required_capabilities"]),
596
+ "missing_host_capabilities": _missing_mode_capabilities(mode, host_capabilities),
597
+ "blocking_reasons": (
598
+ _blocking_reasons(mode, host_capabilities, host_identity)
599
+ if not (mode == MODE_VISIBLE_TUI and visible_unresolved)
600
+ else [
601
+ (
602
+ "visible_tui requires an explicit host_identity "
603
+ "(--host-identity); a generic visible_session capability "
604
+ "cannot identify the actual host."
605
+ )
606
+ if host_identity is None
607
+ else (
608
+ f"host_identity {host_identity!r} has no registered catalog "
609
+ "connector for visible mode."
610
+ )
611
+ ]
612
+ ),
613
+ "recommended_next_steps": recommended_next_steps,
614
+ "turn_mapping": {
615
+ "host": effective_turn_host,
616
+ "execution_mode": meta.get("turn_execution_mode"),
617
+ "scheduler_owner": meta.get("scheduler_owner"),
618
+ "plan_command": turn_plan_command,
619
+ },
620
+ "scheduler_execution_context": _scheduler_context(mode, host_identity),
621
+ "quota_guard_command": quota_guard_command,
622
+ "required_proofs": [*_SHARED_PROOFS, *meta["extra_proofs"]],
623
+ }
624
+
625
+
626
+ def _transitions(
627
+ *,
628
+ goal_id: str,
629
+ agent_id: str | None,
630
+ cli_bin: str,
631
+ available_capabilities: list[str] | None,
632
+ host_identity: str | None,
633
+ ) -> list[dict[str, Any]]:
634
+ def target_turn_command(to_mode: str) -> str | None:
635
+ try:
636
+ return _turn_plan_command(
637
+ goal_id=goal_id,
638
+ agent_id=agent_id,
639
+ mode=to_mode,
640
+ cli_bin=cli_bin,
641
+ available_capabilities=available_capabilities,
642
+ host_identity=host_identity,
643
+ )
644
+ except HostModePlanError:
645
+ # A visible target without a catalog-registered identity has no
646
+ # honest preview command; emit none rather than a fabricated host.
647
+ return None
648
+ def transition(
649
+ transition_id: str,
650
+ from_mode: str,
651
+ to_mode: str,
652
+ trigger: str,
653
+ target_readiness: list[str],
654
+ ) -> dict[str, Any]:
655
+ return {
656
+ "transition": transition_id,
657
+ "from_mode": from_mode,
658
+ "to_mode": to_mode,
659
+ "trigger": trigger,
660
+ "preserves_agent_id": True,
661
+ "spends_quota": False,
662
+ "target_readiness": target_readiness,
663
+ "target_turn_plan_command": target_turn_command(to_mode),
664
+ "guard_command": _mode_quota_guard(
665
+ mode=to_mode,
666
+ goal_id=goal_id,
667
+ agent_id=agent_id,
668
+ cli_bin=cli_bin,
669
+ available_capabilities=available_capabilities,
670
+ host_identity=host_identity if host_identity in VISIBLE_HOST_CONNECTOR_IDS else None,
671
+ ),
672
+ }
673
+
674
+ return [
675
+ transition(
676
+ "visible_bootstrap_to_isolated_headless_turn",
677
+ MODE_VISIBLE_TUI,
678
+ MODE_ISOLATED_HEADLESS_TURN,
679
+ "the user closes the visible session but wants bounded work to continue",
680
+ [CAP_LOOPX_TURN, CAP_TYPED_HOST_ADAPTER, CAP_INDEPENDENT_VALIDATOR],
681
+ ),
682
+ transition(
683
+ "isolated_headless_turn_to_visible_tui_escalation",
684
+ MODE_ISOLATED_HEADLESS_TURN,
685
+ MODE_VISIBLE_TUI,
686
+ "a user gate or ambiguous decision requires visible steering",
687
+ [CAP_VISIBLE_SESSION, "clear_gate_handling"],
688
+ ),
689
+ transition(
690
+ "im_gateway_to_isolated_headless_turn",
691
+ MODE_IM_GATEWAY,
692
+ MODE_ISOLATED_HEADLESS_TURN,
693
+ "chat intake has created durable work that can run through LoopX Turn",
694
+ [CAP_LOOPX_TURN, CAP_TYPED_HOST_ADAPTER, CAP_INDEPENDENT_VALIDATOR],
695
+ ),
696
+ transition(
697
+ "shell_service_to_visible_tui_escalation",
698
+ MODE_SHELL_SERVICE,
699
+ MODE_VISIBLE_TUI,
700
+ "timer-owned work hits a user gate, missing validator, or risky action",
701
+ [CAP_VISIBLE_SESSION, "clear_gate_handling"],
702
+ ),
703
+ ]
704
+
705
+
706
+ def build_host_mode_plan(
707
+ *,
708
+ goal_id: str,
709
+ user_intent: Any,
710
+ host_capabilities: Any = None,
711
+ agent_id: str | None = None,
712
+ registered_agents: list[str] | None = None,
713
+ cli_bin: str = "loopx",
714
+ available_capabilities: list[str] | None = None,
715
+ host_identity: str | None = None,
716
+ ) -> dict[str, Any]:
717
+ """Build a read-only host-mode selector plan on top of LoopX Turn.
718
+
719
+ This selector chooses the user-facing host mode and prints the matching
720
+ connector/Turn preview commands. LoopX Turn, quota, todo projection, and
721
+ run history remain authoritative; the selector never launches a host, writes
722
+ state, validates work, or spends quota.
723
+ """
724
+
725
+ intents = _normalize_supported(
726
+ user_intent, field="user_intent", supported=SUPPORTED_INTENTS, required=True
727
+ )
728
+ caps = _normalize_supported(
729
+ host_capabilities,
730
+ field="host_capabilities",
731
+ supported=SUPPORTED_HOST_CAPABILITIES,
732
+ required=False,
733
+ )
734
+ available = _normalize_tokens(available_capabilities)
735
+ normalized_host_identity = None
736
+ connector_override_identity = None
737
+ if host_identity:
738
+ # Host identities are Turn host kinds and already use dashes
739
+ # (codex-cli, claude-code, generic-cli); normalize case without
740
+ # converting dashes to underscores. OpenCode and Pi alias onto the
741
+ # generic-cli Turn host while keeping their own connector identity.
742
+ raw_identity = str(host_identity).strip().lower()
743
+ candidate = VISIBLE_OPENCODE_ALIASES.get(raw_identity, raw_identity)
744
+ candidate = VISIBLE_PI_ALIASES.get(candidate, candidate)
745
+ if candidate not in SUPPORTED_TURN_HOST_IDENTITIES:
746
+ raise HostModePlanError(
747
+ reason=f"unsupported host_identity: {candidate}",
748
+ field="host_identity",
749
+ suggestions=SUPPORTED_TURN_HOST_IDENTITIES,
750
+ )
751
+ normalized_host_identity = candidate
752
+ connector_override_identity = (
753
+ raw_identity if raw_identity in VISIBLE_CONNECTOR_OVERRIDES else None
754
+ )
755
+ # Host-mode planning continues an existing goal; fresh identity creation
756
+ # belongs to the onboarding entry points that opt into that policy.
757
+ identity = _identity_state(
758
+ agent_id=agent_id,
759
+ registered_agents=registered_agents,
760
+ fresh_agent_default=False,
761
+ )
762
+ raw_scoped = identity.get("selected_agent_id")
763
+ scoped_agent_id = str(raw_scoped) if raw_scoped else None
764
+
765
+ primary_mode = _INTENT_PRIMARY_MODE[intents[0]]
766
+ ordered_modes = [primary_mode] + [mode for mode in CANONICAL_MODES if mode != primary_mode]
767
+ mode_options = [
768
+ _build_mode_option(
769
+ mode=mode,
770
+ goal_id=goal_id,
771
+ agent_id=scoped_agent_id,
772
+ host_capabilities=caps,
773
+ cli_bin=cli_bin,
774
+ available_capabilities=available,
775
+ host_identity=normalized_host_identity,
776
+ connector_override_identity=connector_override_identity,
777
+ )
778
+ for mode in ordered_modes
779
+ ]
780
+ selected = mode_options[0]
781
+
782
+ return {
783
+ "ok": True,
784
+ "schema_version": SCHEMA_VERSION,
785
+ "mode": "dry_run_host_mode_selector",
786
+ "agent_model": "peer_v1",
787
+ "goal_id": goal_id,
788
+ "agent_id": scoped_agent_id,
789
+ "user_intent": intents,
790
+ "host_capabilities": caps,
791
+ "host_identity": normalized_host_identity,
792
+ "selected_mode": primary_mode,
793
+ "selected_connector_id": selected["connector_id"],
794
+ "selected_turn_mapping": selected["turn_mapping"],
795
+ "selected_capability_ready": selected["capability_ready"],
796
+ "selected_missing_host_capabilities": selected["missing_host_capabilities"],
797
+ "selected_blocking_reasons": selected["blocking_reasons"],
798
+ "operator_next_steps": selected["recommended_next_steps"],
799
+ "mode_options": mode_options,
800
+ "identity_contract": identity,
801
+ "guard_command": selected["quota_guard_command"],
802
+ "next_preview_command": (
803
+ selected["turn_mapping"].get("plan_command")
804
+ or selected["quota_guard_command"]
805
+ if not (
806
+ primary_mode == MODE_VISIBLE_TUI
807
+ and normalized_host_identity not in VISIBLE_HOST_CONNECTOR_IDS
808
+ )
809
+ else selected["quota_guard_command"]
810
+ ),
811
+ "no_spend_policy": {
812
+ "selector_preview": True,
813
+ "turn_plan_preview": True,
814
+ "quiet_monitor_skip": True,
815
+ "cadence_only_change": True,
816
+ "readiness_or_final_check": True,
817
+ "spends_only_after_validated_delivery_writeback": True,
818
+ },
819
+ "turn_contract": {
820
+ "schema_version": "loopx_turn_v0",
821
+ "role": "execution_authority_for_headless_modes",
822
+ "plan_command_required_before_host_launch": True,
823
+ "host_result_must_be_typed": True,
824
+ "independent_validation_required": True,
825
+ "writeback_before_quota_spend": True,
826
+ "raw_session_or_transcript_publication_allowed": False,
827
+ },
828
+ "transitions": _transitions(
829
+ goal_id=goal_id,
830
+ agent_id=scoped_agent_id,
831
+ cli_bin=cli_bin,
832
+ available_capabilities=available,
833
+ host_identity=normalized_host_identity,
834
+ ),
835
+ "boundary": {
836
+ "selector_is_authoritative": False,
837
+ "turn_envelope_is_authoritative_for_execution": True,
838
+ "starts_process": False,
839
+ "writes_state": False,
840
+ "spends_quota": False,
841
+ "infers_production_permission": False,
842
+ "infers_credential_access": False,
843
+ "infers_destructive_authority": False,
844
+ "adapter_neutral": True,
845
+ },
846
+ "truth_contract": {
847
+ "source_of_truth": [
848
+ "loopx_turn_v0",
849
+ "turn_envelope",
850
+ "registry",
851
+ "quota_should_run",
852
+ "todo_projection",
853
+ "run_history",
854
+ "runtime_connector_catalog",
855
+ ],
856
+ "selector_is_authoritative": False,
857
+ "recompute_rule": "Recompute before each host setup or handoff.",
858
+ },
859
+ }
860
+
861
+
862
+ def render_host_mode_plan_markdown(payload: dict[str, Any]) -> str:
863
+ if not payload.get("ok"):
864
+ return "\n".join(
865
+ [
866
+ "# LoopX Host Mode Plan Error",
867
+ "",
868
+ f"- ok: `{payload.get('ok')}`",
869
+ f"- error_kind: `{payload.get('error_kind')}`",
870
+ f"- field: `{payload.get('field')}`",
871
+ f"- reason: {payload.get('reason')}",
872
+ f"- suggestions: `{', '.join(payload.get('suggestions') or [])}`",
873
+ ]
874
+ )
875
+ identity = payload.get("identity_contract") if isinstance(payload.get("identity_contract"), dict) else {}
876
+ lines = [
877
+ "# LoopX Host Mode Plan",
878
+ "",
879
+ "This is a read-only selector on top of LoopX Turn and the runtime connector catalog.",
880
+ "It chooses a host mode and prints preview commands; it does not execute work or write state.",
881
+ "",
882
+ f"- schema_version: `{payload.get('schema_version')}`",
883
+ f"- mode: `{payload.get('mode')}`",
884
+ f"- goal_id: `{payload.get('goal_id')}`",
885
+ f"- agent_id: `{payload.get('agent_id')}`",
886
+ f"- selected_mode: `{payload.get('selected_mode')}`",
887
+ f"- selected_connector_id: `{payload.get('selected_connector_id') or 'null'}`",
888
+ f"- selected_capability_ready: `{payload.get('selected_capability_ready')}`",
889
+ f"- host_identity: `{payload.get('host_identity')}`",
890
+ f"- selected_missing_host_capabilities: `{', '.join(payload.get('selected_missing_host_capabilities') or [])}`",
891
+ f"- user_intent: `{', '.join(payload.get('user_intent') or [])}`",
892
+ f"- host_capabilities: `{', '.join(payload.get('host_capabilities') or [])}`",
893
+ "",
894
+ ]
895
+ selected_ready = payload.get("selected_capability_ready") is True
896
+ if selected_ready:
897
+ lines.extend(
898
+ [
899
+ "## Next Preview Command",
900
+ "",
901
+ "```bash",
902
+ str(payload.get("next_preview_command") or ""),
903
+ "```",
904
+ "",
905
+ ]
906
+ )
907
+ else:
908
+ lines.extend(
909
+ [
910
+ "## Not Ready Yet",
911
+ "",
912
+ "Do not run this host mode yet. Fill the gaps below first; the preview command becomes meaningful only after the mode is ready.",
913
+ "",
914
+ ]
915
+ )
916
+ lines.extend(
917
+ [
918
+ "## Runtime Modes",
919
+ "",
920
+ "| mode | connector | ready | Turn host | execution mode | scheduler owner | summary |",
921
+ "| --- | --- | --- | --- | --- | --- | --- |",
922
+ ]
923
+ )
924
+ for option in payload.get("mode_options") or []:
925
+ if not isinstance(option, dict):
926
+ continue
927
+ turn = option.get("turn_mapping") if isinstance(option.get("turn_mapping"), dict) else {}
928
+ lines.append(
929
+ "| "
930
+ f"`{option.get('mode')}` | "
931
+ f"`{option.get('connector_id') or 'null'}` | "
932
+ f"`{option.get('capability_ready')}` | "
933
+ f"`{turn.get('host')}` | "
934
+ f"`{turn.get('execution_mode')}` | "
935
+ f"`{turn.get('scheduler_owner')}` | "
936
+ f"{option.get('summary')} |"
937
+ )
938
+ selected_mode = payload.get("selected_mode")
939
+ selected_option = next(
940
+ (
941
+ option
942
+ for option in payload.get("mode_options") or []
943
+ if isinstance(option, dict) and option.get("mode") == selected_mode
944
+ ),
945
+ None,
946
+ )
947
+ if selected_option:
948
+ lines.extend(["", "## Selected Mode Required Proofs", ""])
949
+ lines.extend(f"- {proof}" for proof in selected_option.get("required_proofs") or [])
950
+ if payload.get("selected_blocking_reasons"):
951
+ lines.extend(["", "## Blocking Reasons", ""])
952
+ lines.extend(f"- {reason}" for reason in payload.get("selected_blocking_reasons") or [])
953
+ if payload.get("operator_next_steps"):
954
+ lines.extend(["", "## Operator Next Steps", ""])
955
+ for step in payload.get("operator_next_steps") or []:
956
+ if not isinstance(step, dict):
957
+ continue
958
+ line = f"{step.get('step')}. [{step.get('kind')}] {step.get('action')}"
959
+ if step.get("command"):
960
+ line += f" — `{step.get('command')}`"
961
+ lines.append(line)
962
+ lines.extend(["", "## Handoffs", ""])
963
+ for transition in payload.get("transitions") or []:
964
+ if isinstance(transition, dict):
965
+ lines.append(
966
+ f"- `{transition.get('transition')}`: "
967
+ f"`{transition.get('from_mode')}` -> `{transition.get('to_mode')}`; "
968
+ f"target readiness: `{', '.join(transition.get('target_readiness') or [])}`"
969
+ )
970
+ if identity.get("action_required"):
971
+ lines.extend(
972
+ [
973
+ "",
974
+ "## Identity Gate",
975
+ "",
976
+ f"- reason: {identity.get('reason')}",
977
+ f"- required_cli_arg: `{identity.get('required_cli_arg')}`",
978
+ ]
979
+ )
980
+ lines.extend(
981
+ [
982
+ "",
983
+ "## Why This Helps",
984
+ "",
985
+ "- makes visible, headless, intake, timer, and hybrid host choices explicit;",
986
+ "- maps headless execution to `loopx turn plan` instead of inventing a parallel runner;",
987
+ "- keeps scoped identity, no-spend previews, independent validation, and quota spend order visible before setup;",
988
+ "- gives operators a safe escalation path back to a visible session for user gates.",
989
+ ]
990
+ )
991
+ return "\n".join(lines)