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,858 @@
1
+ // <!-- loopx-managed-slash-command:v1 command=/goal surface=opencode-runtime -->
2
+ import { execFile as execFileCallback } from "node:child_process"
3
+ import { promises as fs } from "node:fs"
4
+ import os from "node:os"
5
+ import path from "node:path"
6
+ import { promisify } from "node:util"
7
+
8
+
9
+ const execFile = promisify(execFileCallback)
10
+ const BRIDGE_SCHEMA_VERSION = "loopx_opencode_goal_bridge_v0"
11
+ const TERMINAL_STATE_SCHEMA_VERSION = "goal_terminal_state_v0"
12
+ const SOURCE_COMPLETENESS_SCHEMA_VERSION = "goal_terminal_source_completeness_v0"
13
+ const DEFAULT_RETRY_MINUTES = 3
14
+ const MAX_RETRY_MINUTES = 30
15
+ const STANDBY_MINUTES = 5
16
+ const BINDING_LOCK_STALE_MS = 90_000
17
+ const LOOPX_GOAL_LIMITS = {
18
+ maxTurns: 10000,
19
+ maxDurationMs: 30 * 24 * 60 * 60 * 1000,
20
+ maxTokens: 1_000_000_000,
21
+ }
22
+
23
+
24
+ function sessionIDFromEvent(event) {
25
+ return (
26
+ event?.properties?.sessionID ||
27
+ event?.properties?.info?.sessionID ||
28
+ event?.data?.sessionID ||
29
+ event?.data?.info?.sessionID ||
30
+ null
31
+ )
32
+ }
33
+
34
+
35
+ function isIdleEvent(event) {
36
+ return (
37
+ event?.type === "session.idle" ||
38
+ (event?.type === "session.status" && event?.properties?.status?.type === "idle")
39
+ )
40
+ }
41
+
42
+
43
+ function shouldPauseForHostEvent(event) {
44
+ if (event?.type === "session.error") return true
45
+ if (event?.type !== "permission.replied") return false
46
+ const reply = String(
47
+ event?.properties?.reply ??
48
+ event?.properties?.response ??
49
+ event?.data?.reply ??
50
+ event?.data?.response ??
51
+ "",
52
+ )
53
+ return /^(?:reject(?:ed)?|deny|denied)$/i.test(reply)
54
+ }
55
+
56
+
57
+ function textFromParts(parts) {
58
+ return (parts || [])
59
+ .filter((part) => part?.type === "text" && !part?.ignored)
60
+ .map((part) => part.text || "")
61
+ .join("\n")
62
+ .trim()
63
+ }
64
+
65
+
66
+ function parseToolResult(value) {
67
+ if (typeof value !== "string") return null
68
+ try {
69
+ return JSON.parse(value)
70
+ } catch {
71
+ return null
72
+ }
73
+ }
74
+
75
+
76
+ function toolResultSucceeded(value, legacyPrefixes = []) {
77
+ const parsed = parseToolResult(value)
78
+ if (parsed?.ok === true) return true
79
+ if (typeof value !== "string") return false
80
+ const normalized = value.toLowerCase()
81
+ return legacyPrefixes.some((prefix) => normalized.startsWith(prefix.toLowerCase()))
82
+ }
83
+
84
+
85
+ function toolResultHasError(value, error) {
86
+ const parsed = parseToolResult(value)
87
+ return parsed?.ok === false && parsed?.error === error
88
+ }
89
+
90
+
91
+ function sanitizedSessionID(sessionID) {
92
+ const value = String(sessionID || "").replace(/[^A-Za-z0-9_-]/g, "_")
93
+ if (!value) throw new Error("OpenCode session id is required")
94
+ return value.slice(0, 160)
95
+ }
96
+
97
+
98
+ function defaultStateRoot() {
99
+ if (process.env.LOOPX_OPENCODE_STATE_DIR) {
100
+ return path.resolve(process.env.LOOPX_OPENCODE_STATE_DIR)
101
+ }
102
+ const xdgState = process.env.XDG_STATE_HOME || path.join(os.homedir(), ".local", "state")
103
+ return path.join(xdgState, "loopx", "opencode")
104
+ }
105
+
106
+
107
+ export function createFileBindingStore({ root = defaultStateRoot() } = {}) {
108
+ const target = (sessionID) => path.join(root, `${sanitizedSessionID(sessionID)}.json`)
109
+ return {
110
+ async read(sessionID) {
111
+ try {
112
+ const payload = JSON.parse(await fs.readFile(target(sessionID), "utf8"))
113
+ if (payload?.schemaVersion !== BRIDGE_SCHEMA_VERSION || payload?.sessionID !== sessionID) {
114
+ throw new Error("invalid LoopX OpenCode binding")
115
+ }
116
+ return payload
117
+ } catch (error) {
118
+ if (error?.code === "ENOENT") return null
119
+ throw error
120
+ }
121
+ },
122
+ async write(sessionID, binding) {
123
+ await fs.mkdir(root, { recursive: true, mode: 0o700 })
124
+ const destination = target(sessionID)
125
+ const temporary = `${destination}.${process.pid}.${Date.now()}.tmp`
126
+ const payload = {
127
+ schemaVersion: BRIDGE_SCHEMA_VERSION,
128
+ sessionID,
129
+ ...binding,
130
+ updatedAt: new Date().toISOString(),
131
+ }
132
+ await fs.writeFile(temporary, `${JSON.stringify(payload, null, 2)}\n`, {
133
+ encoding: "utf8",
134
+ mode: 0o600,
135
+ })
136
+ await fs.rename(temporary, destination)
137
+ await fs.chmod(destination, 0o600)
138
+ return payload
139
+ },
140
+ async remove(sessionID) {
141
+ try {
142
+ await fs.unlink(target(sessionID))
143
+ } catch (error) {
144
+ if (error?.code !== "ENOENT") throw error
145
+ }
146
+ },
147
+ }
148
+ }
149
+
150
+
151
+ export function probeRetryPlan(binding) {
152
+ const count = Number(binding?.probeRetryCount || 0)
153
+ const minutes = Math.min(DEFAULT_RETRY_MINUTES * 2 ** Math.min(count, 4), MAX_RETRY_MINUTES)
154
+ return { minutes, probeRetryCount: count + 1 }
155
+ }
156
+
157
+
158
+ export function pidIsAlive(pid) {
159
+ try {
160
+ process.kill(pid, 0)
161
+ return true
162
+ } catch (error) {
163
+ return error?.code === "EPERM"
164
+ }
165
+ }
166
+
167
+
168
+ export async function leaseIsLive(lock, { staleMs = BINDING_LOCK_STALE_MS, now = () => Date.now() } = {}) {
169
+ const heartbeatAt = lock?.heartbeatAt ? Date.parse(lock.heartbeatAt) : NaN
170
+ if (Number.isFinite(heartbeatAt) && now() - heartbeatAt < staleMs) return true
171
+ return pidIsAlive(lock?.pid)
172
+ }
173
+
174
+
175
+ export function createFileLockStore({ root = defaultStateRoot() } = {}) {
176
+ const target = (sessionID) => path.join(root, `${sanitizedSessionID(sessionID)}.lock`)
177
+ return {
178
+ async read(sessionID) {
179
+ try {
180
+ const payload = JSON.parse(await fs.readFile(target(sessionID), "utf8"))
181
+ if (!Number.isInteger(payload?.pid)) return null
182
+ return payload
183
+ } catch (error) {
184
+ if (error?.code === "ENOENT") return null
185
+ return null
186
+ }
187
+ },
188
+ async acquire(sessionID, { staleMs = BINDING_LOCK_STALE_MS } = {}) {
189
+ const lockPath = target(sessionID)
190
+ const existing = await this.read(sessionID)
191
+ if (existing && (await leaseIsLive(existing, { staleMs }))) {
192
+ return { acquired: false, holder: existing }
193
+ }
194
+ await fs.mkdir(root, { recursive: true, mode: 0o700 })
195
+ const temporary = `${lockPath}.${process.pid}.${Date.now()}.tmp`
196
+ await fs.writeFile(
197
+ temporary,
198
+ `${JSON.stringify({ pid: process.pid, heartbeatAt: new Date().toISOString() })}\n`,
199
+ { encoding: "utf8", mode: 0o600 },
200
+ )
201
+ await fs.rename(temporary, lockPath)
202
+ await fs.chmod(lockPath, 0o600)
203
+ return {
204
+ acquired: true,
205
+ holder: { pid: process.pid },
206
+ release: async () => {
207
+ try {
208
+ await fs.unlink(lockPath)
209
+ } catch (error) {
210
+ if (error?.code !== "ENOENT") throw error
211
+ }
212
+ },
213
+ }
214
+ },
215
+ }
216
+ }
217
+
218
+
219
+ export async function probeLoopxQuota(binding, { directory, execFileImpl = execFile } = {}) {
220
+ const { stdout } = await runQuotaProbe(binding, directory, execFileImpl)
221
+ const decision = JSON.parse(stdout || "{}")
222
+ if (!decision || typeof decision !== "object" || Array.isArray(decision)) {
223
+ throw new Error("loopx quota should-run returned a non-object payload")
224
+ }
225
+ return decision
226
+ }
227
+
228
+
229
+ async function runQuotaProbe(binding, directory, execFileImpl) {
230
+ const args = [
231
+ ...(binding.registryPath ? ["--registry", binding.registryPath] : []),
232
+ "--format",
233
+ "json",
234
+ "quota",
235
+ "should-run",
236
+ "--goal-id",
237
+ binding.goalId,
238
+ "--runtime-profile",
239
+ "generic_cli",
240
+ "--include-detail",
241
+ "scheduler",
242
+ "--record-host-poll",
243
+ ]
244
+ if (binding.agentId) args.push("--agent-id", binding.agentId)
245
+ for (const capability of binding.availableCapabilities || []) {
246
+ args.push("--available-capability", capability)
247
+ }
248
+ try {
249
+ return await execFileImpl(process.env.LOOPX_BIN || "loopx", args, {
250
+ cwd: binding.directory || directory,
251
+ timeout: 20_000,
252
+ maxBuffer: 4 * 1024 * 1024,
253
+ })
254
+ } catch (error) {
255
+ // A denied or gated decision exits non-zero but still carries the
256
+ // authoritative JSON payload on stdout. Only fail closed when the
257
+ // payload itself is unavailable.
258
+ if (typeof error?.stdout === "string" && error.stdout.trim()) {
259
+ return { stdout: error.stdout }
260
+ }
261
+ throw error
262
+ }
263
+ }
264
+
265
+
266
+ export function isTerminalNoFollowup(decision) {
267
+ const frontier = decision?.goal_frontier_projection
268
+ const terminal = frontier?.terminal_state
269
+ const completeness = frontier?.source_completeness
270
+ return Boolean(
271
+ decision?.should_run === false &&
272
+ decision?.effective_action === "terminal_no_followup" &&
273
+ terminal?.schema_version === TERMINAL_STATE_SCHEMA_VERSION &&
274
+ terminal?.kind === "no_followup" &&
275
+ terminal?.derived === true &&
276
+ terminal?.source === "validated_goal_closure" &&
277
+ completeness?.schema_version === SOURCE_COMPLETENESS_SCHEMA_VERSION &&
278
+ completeness?.user_todos === "valid" &&
279
+ completeness?.agent_todos === "valid",
280
+ )
281
+ }
282
+
283
+
284
+ function shouldRunNow(decision) {
285
+ return decision?.scheduler_hint?.action === "run_now" || decision?.should_run === true
286
+ }
287
+
288
+
289
+ function waitPlan(decision, binding) {
290
+ const local = decision?.scheduler_hint?.unchanged_poll?.local_scheduler
291
+ if (!local || local === "stop") return { stop: true }
292
+ const token = decision?.scheduler_hint?.reset_policy?.reset_token || ""
293
+ const sameIdentity = token && token === binding.schedulerToken
294
+ const unchangedPolls = sameIdentity ? Number(binding.unchangedPolls || 0) : 0
295
+ const limit = Number.isInteger(local.unchanged_poll_limit) ? local.unchanged_poll_limit : null
296
+ if (limit !== null && unchangedPolls >= limit) return { stop: true }
297
+ const progression = Array.isArray(local.example_progression_minutes)
298
+ ? local.example_progression_minutes.filter((value) => Number(value) > 0)
299
+ : []
300
+ const fallback = Number(local.recommended_interval_minutes || DEFAULT_RETRY_MINUTES)
301
+ const minutes = progression.length
302
+ ? Number(progression[Math.min(unchangedPolls, progression.length - 1)])
303
+ : fallback
304
+ return {
305
+ stop: false,
306
+ minutes: Number.isFinite(minutes) && minutes > 0 ? minutes : DEFAULT_RETRY_MINUTES,
307
+ schedulerToken: token,
308
+ unchangedPolls: unchangedPolls + 1,
309
+ }
310
+ }
311
+
312
+
313
+ function toolContext(context, sessionID) {
314
+ return {
315
+ sessionID,
316
+ directory: context.directory,
317
+ worktree: context.worktree,
318
+ }
319
+ }
320
+
321
+
322
+ async function executeTool(definition, args, context) {
323
+ if (!definition || typeof definition.execute !== "function") {
324
+ throw new Error("required opencode-goal-plugin tool is unavailable")
325
+ }
326
+ return definition.execute(args, context)
327
+ }
328
+
329
+
330
+ function commandKind(argumentsText) {
331
+ const normalized = String(argumentsText || "").trim().toLowerCase()
332
+ if (!normalized || normalized === "status" || normalized === "history" || normalized === "list") {
333
+ return "inspect"
334
+ }
335
+ if (["clear", "stop", "off", "reset", "none", "cancel"].includes(normalized)) return "clear"
336
+ if (normalized === "resume") return "resume"
337
+ if (normalized === "pause") return "pause"
338
+ if (normalized.startsWith("edit ")) return "edit"
339
+ return "replace"
340
+ }
341
+
342
+
343
+ export function createLoopxGoalPlugin({
344
+ GoalPlugin,
345
+ tool,
346
+ bindingStore = createFileBindingStore(),
347
+ lockStore = createFileLockStore(),
348
+ quotaProbe = probeLoopxQuota,
349
+ setTimer = setTimeout,
350
+ clearTimer = clearTimeout,
351
+ } = {}) {
352
+ if (typeof GoalPlugin !== "function") throw new Error("GoalPlugin factory is required")
353
+ if (typeof tool !== "function" || !tool.schema) throw new Error("OpenCode tool helper is required")
354
+
355
+ return async function LoopXGoalPlugin(context) {
356
+ const timers = new Map()
357
+ const evaluations = new Map()
358
+ const pendingGoalCommands = new Map()
359
+ // Tracks sessions whose persisted base goal is already active in this process.
360
+ const initializedSessions = new Set()
361
+ let disposed = false
362
+
363
+ const log = async (level, message, extra = {}) => {
364
+ try {
365
+ await context.client?.app?.log?.({
366
+ body: { service: "loopx-opencode-goal", level, message, extra },
367
+ })
368
+ } catch {
369
+ // Logging must never change the continuation policy.
370
+ }
371
+ }
372
+
373
+ const cancelScheduled = (sessionID) => {
374
+ const timer = timers.get(sessionID)
375
+ if (timer !== undefined) clearTimer(timer)
376
+ timers.delete(sessionID)
377
+ }
378
+
379
+ const detachBinding = async (sessionID) => {
380
+ await bindingStore.remove(sessionID)
381
+ cancelScheduled(sessionID)
382
+ initializedSessions.delete(sessionID)
383
+ }
384
+
385
+ const readBinding = async (sessionID) => bindingStore.read(sessionID)
386
+ const updateBinding = async (sessionID, changes) => {
387
+ const current = await readBinding(sessionID)
388
+ if (!current) return null
389
+ return bindingStore.write(sessionID, { ...current, ...changes })
390
+ }
391
+
392
+ const auditor = async ({ sessionID }) => {
393
+ const binding = await readBinding(sessionID)
394
+ if (!binding) return { approved: true }
395
+ try {
396
+ const decision = await quotaProbe(binding, { directory: context.directory })
397
+ return isTerminalNoFollowup(decision)
398
+ ? { approved: true }
399
+ : {
400
+ approved: false,
401
+ reason: "LoopX has not derived terminal no-follow-up from a complete empty frontier.",
402
+ }
403
+ } catch (error) {
404
+ return {
405
+ approved: false,
406
+ reason: `LoopX terminal audit unavailable: ${error?.message || String(error)}`,
407
+ }
408
+ }
409
+ }
410
+
411
+ const base = await GoalPlugin(context, { auditor })
412
+ const baseTools = base.tool || {}
413
+ const baseGoalSet = baseTools.goal_set
414
+ const baseGoalResume = baseTools.goal_resume
415
+ const baseGoalPause = baseTools.goal_pause
416
+ const baseEvent = base.event
417
+ const baseChatMessage = base["chat.message"]
418
+ const baseCommand = base["command.execute.before"]
419
+
420
+ const scheduleEvaluation = (sessionID, minutes) => {
421
+ if (disposed) return
422
+ cancelScheduled(sessionID)
423
+ const timer = setTimer(async () => {
424
+ timers.delete(sessionID)
425
+ if (disposed) return
426
+ try {
427
+ await evaluateIdle(sessionID, {
428
+ event: { type: "session.idle", properties: { sessionID } },
429
+ })
430
+ } catch (error) {
431
+ await log("error", "Scheduled LoopX quota evaluation failed closed", {
432
+ sessionID,
433
+ error: error?.message || String(error),
434
+ })
435
+ }
436
+ }, Math.max(1, minutes) * 60_000)
437
+ if (typeof timer?.unref === "function") timer.unref()
438
+ timers.set(sessionID, timer)
439
+ }
440
+
441
+ const evaluateIdleOnce = async (sessionID, input) => {
442
+ if (disposed) return
443
+ let binding
444
+ try {
445
+ binding = await readBinding(sessionID)
446
+ } catch (error) {
447
+ cancelScheduled(sessionID)
448
+ await log("error", "Invalid LoopX OpenCode binding; continuation failed closed", {
449
+ sessionID,
450
+ error: error?.message || String(error),
451
+ })
452
+ return
453
+ }
454
+ if (!binding) {
455
+ await baseEvent?.(input)
456
+ return
457
+ }
458
+ if (binding.autoResume === false) {
459
+ cancelScheduled(sessionID)
460
+ return
461
+ }
462
+
463
+ const lock = await lockStore.acquire(sessionID)
464
+ if (!lock.acquired) {
465
+ await log("warn", "another process holds the LoopX session lock; retrying later", {
466
+ goalId: binding.goalId,
467
+ sessionID,
468
+ holderPid: lock.holder?.pid,
469
+ })
470
+ scheduleEvaluation(sessionID, DEFAULT_RETRY_MINUTES)
471
+ return
472
+ }
473
+ try {
474
+ await evaluateIdleLocked(sessionID, input, binding)
475
+ } finally {
476
+ await lock.release()
477
+ }
478
+ }
479
+
480
+ const evaluateIdleLocked = async (sessionID, input, initialBinding) => {
481
+ let binding = initialBinding
482
+ if (binding.userMessagePending) {
483
+ await bindingStore.write(sessionID, { ...binding, userMessagePending: false })
484
+ if (typeof baseGoalResume?.execute === "function") {
485
+ try {
486
+ await executeTool(baseGoalResume, {}, toolContext(context, sessionID))
487
+ await log("info", "resumed the OpenCode goal after user input", {
488
+ goalId: binding.goalId,
489
+ sessionID,
490
+ })
491
+ } catch (error) {
492
+ await log("warn", "OpenCode goal resume probe failed after user input", {
493
+ goalId: binding.goalId,
494
+ sessionID,
495
+ error: error?.message || String(error),
496
+ })
497
+ }
498
+ }
499
+ }
500
+ let decision
501
+ try {
502
+ decision = await quotaProbe(binding, { directory: context.directory })
503
+ if (Number(binding.probeRetryCount || 0) > 0) {
504
+ await updateBinding(sessionID, { probeRetryCount: 0 })
505
+ }
506
+ } catch (error) {
507
+ const plan = probeRetryPlan(binding)
508
+ await updateBinding(sessionID, { probeRetryCount: plan.probeRetryCount })
509
+ await log("warn", "LoopX quota probe failed closed; scheduling a bounded retry", {
510
+ goalId: binding.goalId,
511
+ sessionID,
512
+ error: error?.message || String(error),
513
+ retryMinutes: plan.minutes,
514
+ })
515
+ scheduleEvaluation(sessionID, plan.minutes)
516
+ return
517
+ }
518
+ if (disposed) return
519
+
520
+ const currentBinding = await readBinding(sessionID)
521
+ if (
522
+ !currentBinding ||
523
+ currentBinding.autoResume === false ||
524
+ currentBinding.goalId !== binding.goalId
525
+ ) {
526
+ cancelScheduled(sessionID)
527
+ return
528
+ }
529
+ binding = currentBinding
530
+
531
+ if (isTerminalNoFollowup(decision)) {
532
+ // Validated closure means the current work is done, not that the loop
533
+ // should end. Stand by and keep the goal active so new work resumes
534
+ // automatically instead of pausing after one task.
535
+ cancelScheduled(sessionID)
536
+ const standbyPolls = Number(binding.standbyPolls || 0) + 1
537
+ await bindingStore.write(sessionID, {
538
+ ...binding,
539
+ phase: "standby",
540
+ standbyPolls,
541
+ schedulerToken: "",
542
+ unchangedPolls: 0,
543
+ })
544
+ await log("info", "LoopX validated terminal closure; standing by for new work", {
545
+ goalId: binding.goalId,
546
+ sessionID,
547
+ standbyPolls,
548
+ })
549
+ scheduleEvaluation(sessionID, STANDBY_MINUTES)
550
+ return
551
+ }
552
+
553
+ if (shouldRunNow(decision)) {
554
+ cancelScheduled(sessionID)
555
+ let pendingResume = Boolean(binding.userMessagePending)
556
+ await bindingStore.write(sessionID, {
557
+ ...binding,
558
+ phase: "working",
559
+ standbyPolls: 0,
560
+ schedulerToken: "",
561
+ unchangedPolls: 0,
562
+ probeRetryCount: 0,
563
+ userMessagePending: false,
564
+ })
565
+ if (pendingResume || !initializedSessions.has(sessionID)) {
566
+ initializedSessions.add(sessionID)
567
+ try {
568
+ await executeTool(baseGoalResume, {}, toolContext(context, sessionID))
569
+ } catch (error) {
570
+ await log("warn", "OpenCode goal resume probe failed before continuation", {
571
+ goalId: binding.goalId,
572
+ sessionID,
573
+ error: error?.message || String(error),
574
+ })
575
+ }
576
+ }
577
+ if (disposed) return
578
+ await baseEvent?.(input)
579
+ return
580
+ }
581
+
582
+ const wait = waitPlan(decision, binding)
583
+ if (wait.stop) {
584
+ cancelScheduled(sessionID)
585
+ await updateBinding(sessionID, {
586
+ autoResume: false,
587
+ lastPausedReason: "unchanged_poll_limit",
588
+ })
589
+ if (typeof baseGoalPause?.execute === "function") {
590
+ try {
591
+ await executeTool(baseGoalPause, {}, toolContext(context, sessionID))
592
+ await log("info", "LoopX unchanged-poll limit paused the OpenCode goal", {
593
+ goalId: binding.goalId,
594
+ sessionID,
595
+ })
596
+ } catch (error) {
597
+ await log("warn", "OpenCode goal pause failed after the poll limit", {
598
+ goalId: binding.goalId,
599
+ sessionID,
600
+ error: error?.message || String(error),
601
+ })
602
+ }
603
+ } else {
604
+ await log("info", "LoopX unchanged-poll limit stopped the OpenCode timer", {
605
+ goalId: binding.goalId,
606
+ sessionID,
607
+ })
608
+ }
609
+ return
610
+ }
611
+ await bindingStore.write(sessionID, {
612
+ ...binding,
613
+ schedulerToken: wait.schedulerToken,
614
+ unchangedPolls: wait.unchangedPolls,
615
+ })
616
+ scheduleEvaluation(sessionID, wait.minutes)
617
+ }
618
+
619
+ const evaluateIdle = (sessionID, input) => {
620
+ if (disposed) return Promise.resolve()
621
+ const existing = evaluations.get(sessionID)
622
+ if (existing) return existing
623
+ const evaluation = evaluateIdleOnce(sessionID, input).finally(() => {
624
+ if (evaluations.get(sessionID) === evaluation) evaluations.delete(sessionID)
625
+ })
626
+ evaluations.set(sessionID, evaluation)
627
+ return evaluation
628
+ }
629
+
630
+ const activateTool = tool({
631
+ description:
632
+ "Activate a LoopX-backed OpenCode goal after LoopX start-goal has written todos and produced a heartbeat task_body.",
633
+ args: {
634
+ goalId: tool.schema.string(),
635
+ objective: tool.schema.string(),
636
+ agentId: tool.schema.string().optional(),
637
+ registryPath: tool.schema.string().optional(),
638
+ availableCapabilities: tool.schema.array(tool.schema.string()).optional(),
639
+ },
640
+ async execute(args, toolCtx) {
641
+ const sessionID = toolCtx?.sessionID || toolCtx?.session_id || toolCtx?.session?.id
642
+ if (!sessionID) {
643
+ return JSON.stringify({
644
+ version: 1,
645
+ operation: "loopx_activate",
646
+ ok: false,
647
+ error: "missing_session",
648
+ message: "No OpenCode session id is available.",
649
+ })
650
+ }
651
+ const result = await executeTool(
652
+ baseGoalSet,
653
+ {
654
+ objective: args.objective,
655
+ successCriteria:
656
+ "LoopX derives terminal no-follow-up from complete todo sources and an empty frontier.",
657
+ constraints:
658
+ "Run every continuation through LoopX quota should-run and do not self-declare terminal closure.",
659
+ ...LOOPX_GOAL_LIMITS,
660
+ },
661
+ toolCtx,
662
+ )
663
+ if (!toolResultSucceeded(result)) return result
664
+ const binding = await bindingStore.write(sessionID, {
665
+ directory: context.directory,
666
+ goalId: String(args.goalId),
667
+ agentId: args.agentId ? String(args.agentId) : "",
668
+ registryPath: args.registryPath ? String(args.registryPath) : "",
669
+ availableCapabilities: Array.isArray(args.availableCapabilities)
670
+ ? args.availableCapabilities.map(String)
671
+ : [],
672
+ autoResume: true,
673
+ schedulerToken: "",
674
+ unchangedPolls: 0,
675
+ })
676
+ initializedSessions.add(sessionID)
677
+ cancelScheduled(sessionID)
678
+ return JSON.stringify({
679
+ version: 1,
680
+ operation: "loopx_activate",
681
+ ok: true,
682
+ message: "LoopX-backed OpenCode goal activated.",
683
+ data: { goalId: binding.goalId, sessionID },
684
+ })
685
+ },
686
+ })
687
+
688
+ const tools = { ...baseTools, loopx_goal_activate: activateTool }
689
+
690
+ const wrapTool = (name, after) => {
691
+ const original = tools[name]
692
+ if (!original || typeof original.execute !== "function") return
693
+ tools[name] = {
694
+ ...original,
695
+ execute: async (args, toolCtx) => {
696
+ const result = await original.execute(args, toolCtx)
697
+ const sessionID = toolCtx?.sessionID || toolCtx?.session_id || toolCtx?.session?.id
698
+ if (sessionID) await after(sessionID, args || {}, result)
699
+ return result
700
+ },
701
+ }
702
+ }
703
+
704
+ for (const name of ["goal_set", "set_goal"]) {
705
+ wrapTool(name, async (sessionID, _args, result) => {
706
+ const legacyPrefixes = name === "set_goal" ? ["New active goal:"] : []
707
+ if (toolResultSucceeded(result, legacyPrefixes)) await detachBinding(sessionID)
708
+ })
709
+ }
710
+ for (const name of ["goal_complete", "clear_goal"]) {
711
+ wrapTool(name, async (sessionID, _args, result) => {
712
+ const legacyPrefixes = name === "clear_goal" ? ["Goal cleared."] : []
713
+ if (toolResultSucceeded(result, legacyPrefixes)) await detachBinding(sessionID)
714
+ })
715
+ }
716
+ wrapTool("goal_resume", async (sessionID, _args, result) => {
717
+ if (toolResultSucceeded(result)) {
718
+ await updateBinding(sessionID, { autoResume: true, lastPausedReason: "" })
719
+ initializedSessions.add(sessionID)
720
+ }
721
+ })
722
+ for (const name of ["goal_pause", "goal_block"]) {
723
+ wrapTool(name, async (sessionID, _args, result) => {
724
+ if (toolResultSucceeded(result)) {
725
+ await updateBinding(sessionID, {
726
+ autoResume: false,
727
+ lastPausedReason: name === "goal_pause" ? "goal_pause" : "goal_block",
728
+ })
729
+ cancelScheduled(sessionID)
730
+ }
731
+ })
732
+ }
733
+ wrapTool("update_goal", async (sessionID, args, result) => {
734
+ if (
735
+ !toolResultSucceeded(result, [
736
+ "Objective updated:",
737
+ "Goal marked blocked.",
738
+ "Goal paused.",
739
+ "Goal resumed with fresh limits.",
740
+ "Goal marked complete and archived.",
741
+ ])
742
+ ) return
743
+ if (typeof args.objective === "string" && args.objective.trim()) {
744
+ await detachBinding(sessionID)
745
+ return
746
+ }
747
+ const status = String(args.status || "").trim().toLowerCase()
748
+ if (status === "complete") {
749
+ await detachBinding(sessionID)
750
+ } else if (status === "paused" || status === "blocked") {
751
+ await updateBinding(sessionID, {
752
+ autoResume: false,
753
+ lastPausedReason: status === "paused" ? "goal_status_paused" : "goal_status_blocked",
754
+ })
755
+ cancelScheduled(sessionID)
756
+ } else if (status === "resumed") {
757
+ await updateBinding(sessionID, { autoResume: true, lastPausedReason: "" })
758
+ initializedSessions.add(sessionID)
759
+ }
760
+ })
761
+
762
+ return {
763
+ ...base,
764
+ tool: tools,
765
+ config: async (config) => {
766
+ if (config && typeof config === "object") {
767
+ config.command ||= {}
768
+ config.command.goal ||= {
769
+ description: "Set a session-scoped goal and auto-continue until complete.",
770
+ template: "$ARGUMENTS",
771
+ agent: "build",
772
+ }
773
+ }
774
+ await base.config?.(config)
775
+ },
776
+ "command.execute.before": async (input, output) => {
777
+ await baseCommand?.(input, output)
778
+ if (input?.command !== "goal" || !input?.sessionID) return
779
+ const sessionID = input.sessionID
780
+ const args = String(input.arguments || "").trim()
781
+ pendingGoalCommands.set(sessionID, { args, recordedAt: Date.now() })
782
+ const kind = commandKind(args)
783
+ if (kind === "clear" || kind === "replace") {
784
+ await detachBinding(sessionID)
785
+ } else if (kind === "resume") {
786
+ await updateBinding(sessionID, { autoResume: true, lastPausedReason: "" })
787
+ initializedSessions.add(sessionID)
788
+ } else if (kind === "pause") {
789
+ await updateBinding(sessionID, {
790
+ autoResume: false,
791
+ lastPausedReason: "goal_pause",
792
+ })
793
+ cancelScheduled(sessionID)
794
+ }
795
+ },
796
+ "chat.message": async (input, output) => {
797
+ const sessionID = input?.sessionID
798
+ if (!sessionID) {
799
+ await baseChatMessage?.(input, output)
800
+ return
801
+ }
802
+ const pending = pendingGoalCommands.get(sessionID)
803
+ if (pending) {
804
+ pendingGoalCommands.delete(sessionID)
805
+ const reflected = textFromParts(output?.parts)
806
+ if (Date.now() - pending.recordedAt < 10_000 && reflected === pending.args) {
807
+ return
808
+ }
809
+ }
810
+ const text = textFromParts(output?.parts)
811
+ if (text === "/goal" || text.startsWith("/goal ")) {
812
+ await baseChatMessage?.(input, output)
813
+ return
814
+ }
815
+ const binding = await readBinding(sessionID)
816
+ if (binding) {
817
+ // User input never pauses the loop. Stop any scheduled timer wake
818
+ // so it cannot fire mid-answer, and remember that the goal needs a
819
+ // resume probe on the next idle event (the wrapped goal plugin may
820
+ // pause its goal when the user chats).
821
+ await bindingStore.write(sessionID, {
822
+ ...binding,
823
+ userMessagePending: true,
824
+ })
825
+ cancelScheduled(sessionID)
826
+ }
827
+ await baseChatMessage?.(input, output)
828
+ },
829
+ event: async (input) => {
830
+ const event = input?.event
831
+ const sessionID = sessionIDFromEvent(event)
832
+ if (!isIdleEvent(event)) {
833
+ if (sessionID && event?.type === "session.deleted") {
834
+ await detachBinding(sessionID)
835
+ pendingGoalCommands.delete(sessionID)
836
+ } else if (sessionID && shouldPauseForHostEvent(event)) {
837
+ await updateBinding(sessionID, {
838
+ userMessagePending: true,
839
+ })
840
+ cancelScheduled(sessionID)
841
+ }
842
+ await baseEvent?.(input)
843
+ return
844
+ }
845
+ if (sessionID) await evaluateIdle(sessionID, input)
846
+ },
847
+ dispose: async () => {
848
+ disposed = true
849
+ for (const timer of timers.values()) clearTimer(timer)
850
+ timers.clear()
851
+ evaluations.clear()
852
+ initializedSessions.clear()
853
+ pendingGoalCommands.clear()
854
+ await base.dispose?.()
855
+ },
856
+ }
857
+ }
858
+ }