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,212 @@
1
+ #!/usr/bin/env python3
2
+ """PreToolUse policy gate for goal-mode — OPTIONAL deterministic hardening.
3
+
4
+ In the native-`/loop` design, Claude Code's `/loop` is the runtime and loopx's
5
+ MCP `should_run` is the per-tick gate. This hook is NOT installed by default —
6
+ it is opt-in (`install.py --harden` / `connect.py --harden`), project-scoped, and
7
+ adds a per-TOOL-CALL gate on top of the loop:
8
+
9
+ - should_run gate (fail-closed) -> may the agent spend this turn right now?
10
+ - Edit/Write -> write_scope -> deny file edits outside the goal's scope
11
+ - Bash -> destructive denylist -> deny obviously dangerous commands
12
+
13
+ It only acts in a project where loopx is armed (a `.claude/loop.md` exists).
14
+
15
+ Limitation (by design): Bash is gated only by a denylist, so a determined shell
16
+ command can still write outside write_scope or reach the network. For STRONG
17
+ isolation (untrusted code / unattended high-stakes), run Claude Code inside a dev
18
+ container or VM, which contains the whole process at the environment level.
19
+
20
+ Behavior (only while goal-mode is ARMED for the event's project):
21
+ - no goal here / goal-mode off -> {} (no-op; normal permission flow)
22
+ - read-only/safe tools -> allow
23
+ - should_run == false (quota/gate closed) -> deny
24
+ - should_run probe unavailable (error) -> deny for non-read-only tools (FAIL-CLOSED)
25
+ - should_run == true:
26
+ * Edit/Write outside write_scope -> deny
27
+ * Edit/Write within write_scope -> allow
28
+ * destructive Bash -> deny
29
+ * Bash / other -> allow
30
+
31
+ Fail-closed: if the deterministic ``should_run`` probe cannot be reached, a
32
+ non-read-only tool is denied rather than allowed, so a flaky/broken control
33
+ plane pauses delivery instead of running ungated. Read-only tools stay allowed.
34
+
35
+ Identity-aware: when the active goal registered an agent (loopx
36
+ ``coordination.registered_agents``), the context carries ``agent_id`` and this
37
+ hook forwards it as ``--agent-id`` to ``loopx quota should-run``. Without it
38
+ loopx returns ``automation_prompt_upgrade_required`` and should_run=false — the
39
+ Claude Code analogue of Codex's identity-scoped heartbeat prompt.
40
+ """
41
+ from __future__ import annotations
42
+
43
+ import json
44
+ import shutil
45
+ import subprocess
46
+ import sys
47
+ from pathlib import Path
48
+
49
+ # Goal context is resolved PER PROJECT from the event's cwd via the registry, and
50
+ # "armed" = the project's .claude/loop.md exists (see goal_state.py). The registry
51
+ # is the single source of truth; there is no separate active-state file.
52
+ from goal_state import active_context
53
+
54
+ # Read-only tools are always allowed under goal-mode. Edit/Write are scoped to
55
+ # write_scope; Bash is gated by a destructive-command denylist. (No OS sandbox in
56
+ # this design — the hook is the whole gate; see the module docstring.)
57
+ # NOTE: `Task` is deliberately NOT here. It can launch a subagent that performs
58
+ # writes, so allowing it unconditionally would bypass the gate when
59
+ # should_run=false. It must go through the gate: denied when should_run=false,
60
+ # otherwise deferred to Claude Code's normal permission flow (unknown tool).
61
+ READONLY_TOOLS = {"Read", "Glob", "Grep", "NotebookRead", "TodoWrite", "WebFetch", "WebSearch", "ToolSearch"}
62
+ WRITE_TOOLS = {"Edit", "Write", "MultiEdit", "NotebookEdit"}
63
+ DESTRUCTIVE = (
64
+ "rm -rf", "rm -fr", "mkfs", "dd if=", ":(){", "shutdown", "reboot",
65
+ "git push --force", "git reset --hard", "> /dev/sd", "format ",
66
+ )
67
+ CLAUDE_RUNTIME_PROFILE_ARGS = ["--runtime-profile", "claude_code"]
68
+ CLAUDE_LEGACY_SCHEDULER_ARGS = [
69
+ "--host-surface", "claude_code",
70
+ "--scheduler-owner", "agent_cli_loop",
71
+ "--execution-mode", "interactive",
72
+ ]
73
+
74
+
75
+ def within(path: str, root: str) -> bool:
76
+ """True if `path` resolves to `root` or a descendant of it."""
77
+ try:
78
+ p = Path(path).resolve()
79
+ r = Path(root).resolve()
80
+ return r == p or r in p.parents
81
+ except Exception:
82
+ return False
83
+
84
+
85
+ def _gh_prefix():
86
+ _exe = shutil.which("loopx")
87
+ return [_exe] if _exe else [__import__("sys").executable, "-m", "loopx.cli"]
88
+
89
+
90
+ def emit(decision=None, reason=""):
91
+ if decision is None:
92
+ sys.stdout.write("{}") # no-op: defer to normal permission flow
93
+ return
94
+ sys.stdout.write(json.dumps({
95
+ "hookSpecificOutput": {
96
+ "hookEventName": "PreToolUse",
97
+ "permissionDecision": decision,
98
+ "permissionDecisionReason": reason,
99
+ }
100
+ }))
101
+
102
+
103
+ def _runtime_profile_flag_is_unsupported(out: subprocess.CompletedProcess) -> bool:
104
+ diagnostic = str(out.stderr or "").lower()
105
+ return (
106
+ out.returncode == 2
107
+ and "--runtime-profile" in diagnostic
108
+ and (
109
+ "unrecognized arguments" in diagnostic
110
+ or "invalid choice" in diagnostic
111
+ )
112
+ )
113
+
114
+
115
+ def should_run(registry, goal_id, agent_id=None) -> bool | None:
116
+ """Return True/False from loopx quota should-run, or None if unknown."""
117
+ if not goal_id:
118
+ return None
119
+ cmd = list(_gh_prefix())
120
+ if registry:
121
+ cmd += ["--registry", registry]
122
+ cmd += ["--format", "json", "quota", "should-run", "--goal-id", goal_id]
123
+ if agent_id:
124
+ cmd += ["--agent-id", agent_id]
125
+ try:
126
+ out = subprocess.run(
127
+ [*cmd, *CLAUDE_RUNTIME_PROFILE_ARGS],
128
+ capture_output=True,
129
+ text=True,
130
+ timeout=10,
131
+ )
132
+ if _runtime_profile_flag_is_unsupported(out):
133
+ out = subprocess.run(
134
+ [*cmd, *CLAUDE_LEGACY_SCHEDULER_ARGS],
135
+ capture_output=True,
136
+ text=True,
137
+ timeout=10,
138
+ )
139
+ except Exception:
140
+ return None # caller fails CLOSED on None for non-read-only tools
141
+ try:
142
+ data = json.loads(out.stdout or "{}")
143
+ except (json.JSONDecodeError, TypeError, ValueError):
144
+ return None
145
+ should_run_value = data.get("should_run")
146
+ return should_run_value if isinstance(should_run_value, bool) else None
147
+
148
+
149
+ def decide(ev: dict) -> dict:
150
+ """Pure policy: given a PreToolUse event, return the hookSpecificOutput dict
151
+ (or {} for no-op). Reused by the CLI hook AND the Agent SDK in-process hook.
152
+
153
+ The hook is the whole gate (no OS sandbox): should_run (fail-closed) +
154
+ Edit/Write write_scope + a destructive-Bash denylist."""
155
+ # Claude Code feeds the session's working directory on the event; resolve the
156
+ # goal from THAT project's registry, and gate only while goal-mode is armed
157
+ # (the project's .claude/loop.md exists). Unrelated/unarmed projects -> no-op.
158
+ cwd = ev.get("cwd") or (ev.get("workspace") or {}).get("current_dir")
159
+ ctx = active_context(cwd)
160
+ if not ctx:
161
+ return {} # no goal here, or goal-mode off -> defer to normal flow
162
+
163
+ goal_id = ctx.get("goal_id")
164
+ registry = ctx.get("registry")
165
+ agent_id = ctx.get("agent_id")
166
+ scope = ctx.get("write_scope") or []
167
+ tool = ev.get("tool_name", "")
168
+ ti = ev.get("tool_input", {}) or {}
169
+
170
+ def d(decision, reason):
171
+ return {"hookSpecificOutput": {
172
+ "hookEventName": "PreToolUse",
173
+ "permissionDecision": decision,
174
+ "permissionDecisionReason": reason}}
175
+
176
+ if tool in READONLY_TOOLS:
177
+ return d("allow", "read-only/safe tool under goal-mode")
178
+
179
+ sr = should_run(registry, goal_id, agent_id) # True / False / None
180
+ if sr is False:
181
+ return d("deny", f"goal '{goal_id}' should_run=false (quota/gate closed)")
182
+ if sr is None:
183
+ # deterministic should_run probe unavailable -> fail closed (non-read-only tool)
184
+ return d("deny", "loopx should_run probe unavailable — failing closed under goal-mode")
185
+
186
+ # should_run == True. Scope the Edit/Write file tools to write_scope, and
187
+ # gate Bash with the destructive denylist (the hook is the whole gate).
188
+ if tool in WRITE_TOOLS:
189
+ fp = ti.get("file_path") or ti.get("notebook_path") or ""
190
+ if scope and not any(within(fp, s) for s in scope):
191
+ return d("deny", f"'{fp}' outside goal write_scope {scope}")
192
+ return d("allow", "write within goal scope")
193
+ if tool == "Bash":
194
+ low = (ti.get("command") or "").lower()
195
+ if any(tok in low for tok in DESTRUCTIVE):
196
+ return d("deny", "destructive command blocked by goal policy")
197
+ return d("allow", "bash permitted under goal policy")
198
+ return {} # unknown tool: defer to normal flow
199
+
200
+
201
+ def main():
202
+ raw = sys.stdin.read() or "{}"
203
+ try:
204
+ ev = json.loads(raw)
205
+ except Exception:
206
+ ev = {}
207
+ result = decide(ev)
208
+ sys.stdout.write(json.dumps(result) if result else "{}")
209
+
210
+
211
+ if __name__ == "__main__":
212
+ main()
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env python3
2
+ """Goal-mode context resolution — registry-driven, mirroring the Codex model.
3
+
4
+ LoopX's registry (``.loopx/registry.json``) is the single source of truth for the
5
+ project's goal (goal_id / agent / scope), exactly as on Codex. We resolve it by
6
+ walking up from cwd to the nearest ``.loopx/`` (or legacy ``.goal-harness/``)
7
+ registry — so two sessions in different projects are independent, and two
8
+ sessions in the SAME project share the one goal.
9
+
10
+ "Goal-mode ON / armed" for a project = a ``.claude/loop.md`` exists (written by
11
+ ``/loopx <task>``). That file is the per-iteration protocol that Claude Code's
12
+ native ``/loop`` runs; its presence means loopx is driving this project. The
13
+ OPTIONAL PreToolUse hook + statusline gate on this, so they only act where loopx
14
+ is actually active. ``LOOPX_GOAL_FORCE=1`` forces armed=true for tests.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import json
19
+ import os
20
+ import re
21
+ from pathlib import Path
22
+
23
+ REGISTRY_DIRS = (".loopx", ".goal-harness") # prefer loopx; fall back to legacy
24
+
25
+ _ARMED_RE = re.compile(r"<!--\s*loopx:armed\s*(\{.*?\})\s*-->")
26
+
27
+
28
+ def find_registry(cwd) -> Path | None:
29
+ """Nearest ancestor of cwd (inclusive) holding a registry.json, else None."""
30
+ try:
31
+ cur = Path(cwd).resolve()
32
+ except Exception:
33
+ return None
34
+ for d in [cur, *cur.parents]:
35
+ for sub in REGISTRY_DIRS:
36
+ cand = d / sub / "registry.json"
37
+ if cand.exists():
38
+ return cand
39
+ return None
40
+
41
+
42
+ def project_root_for(cwd) -> Path | None:
43
+ reg = find_registry(cwd)
44
+ return reg.parent.parent if reg else None
45
+
46
+
47
+ def _agent_of(goal: dict):
48
+ coord = goal.get("coordination") or {}
49
+ for entry in coord.get("registered_agents") or []:
50
+ if isinstance(entry, dict):
51
+ val = entry.get("id") or entry.get("agent_id") or entry.get("name")
52
+ if val:
53
+ return str(val)
54
+ elif entry:
55
+ return str(entry)
56
+ return None
57
+
58
+
59
+ def loop_md_path(project_root) -> Path:
60
+ return Path(project_root) / ".claude" / "loop.md"
61
+
62
+
63
+ def read_armed_goal(project_root) -> dict | None:
64
+ """The goal_id/agent_id that `/loopx` armed for THIS project, parsed from the
65
+ `loopx:armed` marker it writes at the top of `.claude/loop.md`. None if the
66
+ project isn't armed or the marker is absent/unparseable.
67
+
68
+ This is what lets goal_context resolve the *armed* goal rather than guessing
69
+ by registry order, so a multi-goal repo gates/claims/completes the goal the
70
+ user actually set up."""
71
+ if not project_root:
72
+ return None
73
+ try:
74
+ text = loop_md_path(project_root).read_text(encoding="utf-8")
75
+ except Exception:
76
+ return None
77
+ m = _ARMED_RE.search(text)
78
+ if not m:
79
+ return None
80
+ try:
81
+ d = json.loads(m.group(1))
82
+ except Exception:
83
+ return None
84
+ return d if isinstance(d, dict) and d.get("goal_id") else None
85
+
86
+
87
+ def goal_context(cwd) -> dict | None:
88
+ """The current project's goal, read live from the registry. None if no goal.
89
+
90
+ Returns goal_id, registry path, agent_id, write_scope (the goal's repo), and
91
+ project_root — everything the hooks / MCP / statusline need. When the project
92
+ is armed (`.claude/loop.md` carries a `loopx:armed` marker), the marker's goal
93
+ is authoritative; otherwise we fall back to the first registry goal."""
94
+ reg = find_registry(cwd)
95
+ if not reg:
96
+ return None
97
+ try:
98
+ data = json.loads(reg.read_text(encoding="utf-8"))
99
+ except Exception:
100
+ return None
101
+ goals = data.get("goals") or []
102
+ if not goals:
103
+ return None
104
+ root = reg.parent.parent
105
+ # Prefer the goal /loopx armed for this project; only fall back to registry
106
+ # order when there is no armed marker or it points at a goal not in the registry.
107
+ armed = read_armed_goal(root)
108
+ chosen = None
109
+ armed_agent = None
110
+ if armed:
111
+ chosen = next((g for g in goals if g.get("id") == armed.get("goal_id")), None)
112
+ if chosen is not None:
113
+ armed_agent = armed.get("agent_id")
114
+ if chosen is None:
115
+ chosen = goals[0]
116
+ repo = chosen.get("repo")
117
+ scope = [str(repo).replace("\\", "/")] if repo else [str(root).replace("\\", "/")]
118
+ return {
119
+ "goal_id": chosen.get("id"),
120
+ "registry": str(reg).replace("\\", "/"),
121
+ "agent_id": armed_agent or _agent_of(chosen),
122
+ "write_scope": scope,
123
+ "project_root": str(root).replace("\\", "/"),
124
+ }
125
+
126
+
127
+ def is_armed(project_root) -> bool:
128
+ """Armed = the project has a `.claude/loop.md` (loopx is driving), or forced."""
129
+ if os.environ.get("LOOPX_GOAL_FORCE") == "1":
130
+ return True
131
+ return bool(project_root) and loop_md_path(project_root).exists()
132
+
133
+
134
+ def active_context(cwd) -> dict | None:
135
+ """goal_context for cwd, but only when goal-mode is armed; else None."""
136
+ ctx = goal_context(cwd)
137
+ if not ctx or not is_armed(ctx.get("project_root")):
138
+ return None
139
+ return ctx
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env python3
2
+ """Minimal MCP server exposing loopx state to the agent.
3
+
4
+ Tools: list_todos, claim_task, complete_task, should_run.
5
+ Each shells out to the loopx CLI (deterministic control plane).
6
+
7
+ Requires: pip install mcp
8
+ Config (registry/goal/agent) is resolved PER PROJECT from this process's cwd via
9
+ the `.loopx/registry.json` (see goal_state.py): each Claude Code session launches
10
+ its own stdio MCP subprocess inheriting the session's working directory, so the
11
+ goal scoping is per-project — no global state shared across sessions.
12
+
13
+ These tools are the loopx control-plane protocol that native `/loop` drives each
14
+ iteration (via `.claude/loop.md`): should_run -> claim_task -> bounded work ->
15
+ verify -> complete_task (which also spends one quota slot after writeback).
16
+ """
17
+ from __future__ import annotations
18
+
19
+ import json
20
+ import shutil
21
+ import subprocess
22
+ import sys
23
+ from pathlib import Path
24
+
25
+ try:
26
+ from mcp.server.fastmcp import FastMCP
27
+ except Exception as e: # pragma: no cover
28
+ raise SystemExit("MCP SDK not installed. Run: pip install mcp\n" + str(e))
29
+
30
+ # share the registry-driven resolver with the hooks (sibling hooks/ dir)
31
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "hooks"))
32
+ from goal_state import goal_context
33
+
34
+ mcp = FastMCP("loopx")
35
+ CLAUDE_RUNTIME_PROFILE_ARGS = ["--runtime-profile", "claude_code"]
36
+ CLAUDE_LEGACY_SCHEDULER_ARGS = [
37
+ "--host-surface", "claude_code",
38
+ "--scheduler-owner", "agent_cli_loop",
39
+ "--execution-mode", "interactive",
40
+ ]
41
+ def _gh_prefix():
42
+ _exe = shutil.which("loopx")
43
+ return [_exe] if _exe else [__import__("sys").executable, "-m", "loopx.cli"]
44
+
45
+ def _state() -> dict:
46
+ # registry-derived context for THIS project (cwd) — the control-plane tools
47
+ # work whenever a goal exists, independent of the optional hook/loop.md.
48
+ return goal_context(Path.cwd()) or {}
49
+
50
+
51
+ def _ctx():
52
+ st = _state()
53
+ return st.get("goal_id"), st.get("registry")
54
+
55
+
56
+ def _agent_id():
57
+ return _state().get("agent_id")
58
+
59
+
60
+ def _runtime_profile_flag_is_unsupported(out: subprocess.CompletedProcess) -> bool:
61
+ diagnostic = str(out.stderr or "").lower()
62
+ return (
63
+ out.returncode == 2
64
+ and "--runtime-profile" in diagnostic
65
+ and (
66
+ "unrecognized arguments" in diagnostic
67
+ or "invalid choice" in diagnostic
68
+ )
69
+ )
70
+
71
+
72
+ def _gh(args: list[str], *, legacy_args: list[str] | None = None) -> str:
73
+ goal_id, registry = _ctx()
74
+ cmd = list(_gh_prefix())
75
+ if registry:
76
+ cmd += ["--registry", registry]
77
+ out = subprocess.run([*cmd, *args], capture_output=True, text=True, timeout=30)
78
+ if (
79
+ legacy_args is not None
80
+ and _runtime_profile_flag_is_unsupported(out)
81
+ ):
82
+ out = subprocess.run(
83
+ [*cmd, *legacy_args], capture_output=True, text=True, timeout=30
84
+ )
85
+ return (out.stdout or "") + (("\n" + out.stderr) if out.returncode else "")
86
+
87
+
88
+ def _should_run_args(goal_id: str, agent_id: str | None) -> tuple[list[str], list[str]]:
89
+ common = ["--format", "json", "quota", "should-run", "--goal-id", goal_id]
90
+ if agent_id:
91
+ common += ["--agent-id", agent_id]
92
+ return (
93
+ [*common, *CLAUDE_RUNTIME_PROFILE_ARGS],
94
+ [*common, *CLAUDE_LEGACY_SCHEDULER_ARGS],
95
+ )
96
+
97
+
98
+ def _no_goal_msg() -> str:
99
+ return json.dumps({"error": "goal-mode is not active in this project; run /loopx <task> first"})
100
+
101
+
102
+ @mcp.tool()
103
+ def should_run() -> str:
104
+ """Whether the active goal should run now (quota/gate)."""
105
+ gid, _ = _ctx()
106
+ if not gid:
107
+ return _no_goal_msg()
108
+ args, legacy_args = _should_run_args(gid, _agent_id())
109
+ return _gh(args, legacy_args=legacy_args)
110
+
111
+
112
+ @mcp.tool()
113
+ def list_todos() -> str:
114
+ """List the active goal's open agent todos (with todo_id + claimed_by)."""
115
+ gid, _ = _ctx()
116
+ if not gid:
117
+ return _no_goal_msg()
118
+ args, legacy_args = _should_run_args(gid, _agent_id())
119
+ return _gh(args, legacy_args=legacy_args)
120
+
121
+
122
+ @mcp.tool()
123
+ def claim_task(todo_id: str, agent_id: str) -> str:
124
+ """Soft-claim a todo for an agent."""
125
+ gid, _ = _ctx()
126
+ return _gh(["todo", "claim", "--goal-id", gid, "--todo-id", todo_id, "--claimed-by", agent_id])
127
+
128
+
129
+ @mcp.tool()
130
+ def complete_task(
131
+ todo_id: str,
132
+ agent_id: str,
133
+ evidence: str,
134
+ next_agent_todo: str = "",
135
+ task_lease_idempotency_key: str = "",
136
+ ) -> str:
137
+ """Complete a todo (with evidence), then spend one quota slot.
138
+
139
+ Mirrors the Codex heartbeat contract: spend exactly once AFTER validated
140
+ writeback. The slot is accounted only when the completion reports ok, with
141
+ source=heartbeat so it lines up with Codex spend accounting."""
142
+ gid, _ = _ctx()
143
+ args = ["todo", "complete", "--goal-id", gid, "--todo-id", todo_id,
144
+ "--claimed-by", agent_id, "--evidence", evidence]
145
+ if task_lease_idempotency_key:
146
+ args += ["--task-lease-idempotency-key", task_lease_idempotency_key]
147
+ if next_agent_todo:
148
+ # Don't hard-code a next claimer. With --next-agent-todo and no
149
+ # --next-claimed-by, LoopX assigns the new todo using its own completion
150
+ # semantics — to the completing agent for same-agent non-delivery, or to
151
+ # an explicitly claimed peer for an independent handoff.
152
+ # Hard-coding an unregistered id
153
+ # like `cc-controller` would fail registration.
154
+ args += ["--next-agent-todo", next_agent_todo]
155
+ out = _gh(args)
156
+ # spend-slot only on a validated completion (ok), matching "spend after writeback"
157
+ if "ok: `True`" in out or "ok=True" in out or '"ok": true' in out.lower():
158
+ spend = ["quota", "spend-slot", "--goal-id", gid, "--slots", "1",
159
+ "--source", "heartbeat", "--execute"]
160
+ if agent_id:
161
+ spend += ["--agent-id", agent_id]
162
+ out += "\n--- spend-slot ---\n" + _gh(spend)
163
+ return out
164
+
165
+
166
+ if __name__ == "__main__":
167
+ mcp.run()
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env python3
2
+ """Connect a project to loopx for Claude Code (PROJECT-scoped).
3
+
4
+ Thin wrapper: (1) `loopx connect` the repo, (2) mark `agent_backends += claude`
5
+ in the project registry, (3) install the Claude adapter at PROJECT scope (the
6
+ loopx MCP + the `/loopx` command into this project's `.claude/`) by delegating to
7
+ install.py. No global config, no OS sandbox. The run loop is native `/loop`.
8
+
9
+ python connect.py --project DIR --goal-id G [--objective "…"] [--harden]
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ import json
15
+ import shutil
16
+ import subprocess
17
+ import sys
18
+ from pathlib import Path
19
+
20
+ PLUGIN_ROOT = Path(__file__).resolve().parents[1]
21
+
22
+
23
+ def _gh_prefix():
24
+ exe = shutil.which("loopx")
25
+ return [exe] if exe else [sys.executable, "-m", "loopx.cli"]
26
+
27
+
28
+ def main():
29
+ ap = argparse.ArgumentParser()
30
+ ap.add_argument("--project", required=True)
31
+ ap.add_argument("--goal-id", required=True)
32
+ ap.add_argument("--objective", default=None)
33
+ ap.add_argument("--registry", default=None)
34
+ ap.add_argument("--harden", action="store_true",
35
+ help="also install the optional PreToolUse should_run gate + statusline (project scope)")
36
+ ap.add_argument("--dry-run", action="store_true")
37
+ a = ap.parse_args()
38
+ proj = Path(a.project).resolve()
39
+ dry = a.dry_run
40
+
41
+ # 1. connect the repo to loopx (optional, when an objective is given)
42
+ if a.objective:
43
+ cmd = list(_gh_prefix()) + (["--registry", a.registry] if a.registry else []) + \
44
+ ["connect", "--goal-id", a.goal_id, "--objective", a.objective,
45
+ "--state-file", f".claude/goals/{a.goal_id}/ACTIVE_GOAL_STATE.md"]
46
+ print("[connect]", " ".join(cmd))
47
+ if not dry:
48
+ try:
49
+ rc = subprocess.run(cmd, cwd=str(proj), timeout=120)
50
+ except Exception as e:
51
+ print(f"[connect] FAILED to run loopx connect: {e}")
52
+ sys.exit(1)
53
+ if rc.returncode != 0:
54
+ # Fail closed: a non-zero connect (e.g. a global route collision)
55
+ # must stop the helper rather than print a misleading "next steps".
56
+ print(f"[connect] FAILED (exit {rc.returncode}) — not continuing. "
57
+ "Resolve the error above and re-run.")
58
+ sys.exit(rc.returncode)
59
+
60
+ # 2. mark agent_backends += claude in the project registry (additive)
61
+ if a.registry:
62
+ reg = Path(a.registry)
63
+ else:
64
+ reg = proj / ".loopx" / "registry.json"
65
+ if not reg.exists() and (proj / ".goal-harness" / "registry.json").exists():
66
+ reg = proj / ".goal-harness" / "registry.json"
67
+ if reg.exists():
68
+ try:
69
+ data = json.loads(reg.read_text(encoding="utf-8"))
70
+ data.setdefault("agent_backends", [])
71
+ if "claude" not in data["agent_backends"]:
72
+ data["agent_backends"].append("claude")
73
+ print(f"[registry] mark agent_backends += claude ({reg})")
74
+ if not dry:
75
+ reg.write_text(json.dumps(data, indent=2), encoding="utf-8")
76
+ except Exception as e:
77
+ print(" (registry annotate skipped:", e, ")")
78
+ else:
79
+ print(f"[registry] {reg} not found yet (run with --objective to connect first)")
80
+
81
+ # 3. install the Claude adapter at PROJECT scope (MCP + /loopx command [+ --harden])
82
+ installer = PLUGIN_ROOT / "scripts" / "install.py"
83
+ inst = [sys.executable, str(installer), "--scope", "project", "--project", str(proj)]
84
+ if a.harden:
85
+ inst.append("--harden")
86
+ if dry:
87
+ inst.append("--dry-run")
88
+ print("[install]", " ".join(inst))
89
+ ri = subprocess.run(inst, cwd=str(proj))
90
+ if ri.returncode != 0:
91
+ # Fail closed: don't claim success / print next steps if the adapter
92
+ # install failed.
93
+ print(f"[install] FAILED (exit {ri.returncode}).")
94
+ sys.exit(ri.returncode)
95
+
96
+ print("\nNext: open Claude Code in the project, then:")
97
+ print(" /loopx <task> # set the goal, write .claude/loop.md, do the first segment")
98
+ print(" /loop # drive the loop (native; loopx should_run gates each tick)")
99
+ print(" /loopx off # remove the loop protocol")
100
+
101
+
102
+ if __name__ == "__main__":
103
+ main()