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,1540 @@
1
+ from __future__ import annotations
2
+
3
+ import shlex
4
+ import typing
5
+ from collections.abc import Mapping
6
+ from typing import Any
7
+
8
+ from ..agents.agent_scope_frontier import AgentScopeFrontierAction, agent_scope_frontier_action as _agent_scope_frontier_action
9
+ from ..agents.capability_gate import runtime_capabilities_for_cli_projection
10
+ from ..goals.goal_frontier import AUTONOMOUS_REPLAN_REQUIRED_MODE
11
+ from ..goals.goal_vision_wait import exact_blocked_successor_wait_state
12
+ from ..quota.settlement import (
13
+ SettlementStepKind,
14
+ build_codex_app_settlement_plan,
15
+ settlement_binding_args,
16
+ settlement_step_command,
17
+ )
18
+ from ..scheduler.execution_context import (
19
+ SchedulerExecutionContextResolution,
20
+ SchedulerRuntimeProfile,
21
+ render_scheduler_execution_args,
22
+ scheduler_runtime_profile_for_execution_context,
23
+ )
24
+ from ..todos.contract import (
25
+ TODO_TASK_CLASS_MONITOR,
26
+ TODO_TASK_CLASS_USER_ACTION,
27
+ normalize_todo_id,
28
+ )
29
+ from ..todos.projection import todo_item_task_class
30
+ from ..todos.user_gate import open_todo_count
31
+ from ..todos.write_hint import build_capability_resolution_writeback_actions
32
+ from .primary_action import (
33
+ build_primary_action_projection,
34
+ protocol_action_label as _protocol_action_label,
35
+ protocol_action_text,
36
+ protocol_first_candidate_action as _protocol_first_candidate_action,
37
+ protocol_monitor_action as _protocol_monitor_action,
38
+ )
39
+ from .runtime_capability_reentry import build_runtime_capability_reentry_packet
40
+
41
+ INTERACTION_CONTRACT_SCHEMA_VERSION = "loopx_interaction_contract_v0"
42
+ INTERACTION_RESPONSE_PLAN_SCHEMA_VERSION = "interaction_response_plan_v0"
43
+ PROTOCOL_ACTION_PACKET_SCHEMA_VERSION = "protocol_action_packet_v0"
44
+ PROTOCOL_ACTION_PACKET_LLM_POLICY = "no_api"
45
+
46
+
47
+ class _InteractionContractRequired(typing.TypedDict):
48
+ schema_version: str
49
+ mode: str
50
+ user_channel: dict[str, Any]
51
+ agent_channel: dict[str, Any]
52
+ cli_channel: dict[str, Any]
53
+
54
+
55
+ class InteractionContractPacket(_InteractionContractRequired, total=False):
56
+ response_plan: dict[str, Any]
57
+ fallback_policy: dict[str, Any]
58
+
59
+
60
+ def _blocked_successor_wait_observation_required(payload: dict[str, Any]) -> bool:
61
+ return bool(
62
+ payload.get("effective_action")
63
+ == AgentScopeFrontierAction.AGENT_SCOPE_WAIT.value
64
+ and exact_blocked_successor_wait_state(payload)
65
+ )
66
+
67
+
68
+ def _user_todo_item_is_explicitly_non_gating(item: dict[str, Any]) -> bool:
69
+ if item.get("gating") is False or item.get("non_gating") is True:
70
+ return True
71
+ if todo_item_task_class(item) in {
72
+ TODO_TASK_CLASS_MONITOR,
73
+ TODO_TASK_CLASS_USER_ACTION,
74
+ }:
75
+ return True
76
+ action_kind = str(item.get("action_kind") or "").strip().lower()
77
+ return action_kind in {
78
+ "monitor",
79
+ "observe",
80
+ "watch",
81
+ "fyi",
82
+ "informational",
83
+ "non_gating",
84
+ }
85
+
86
+
87
+ def user_channel_action_todo_actions(summary: Any, *, limit: int = 3) -> list[str]:
88
+ if not isinstance(summary, dict):
89
+ return []
90
+ first_open_items = summary.get("first_open_items")
91
+ if not isinstance(first_open_items, list):
92
+ return []
93
+ actions: list[str] = []
94
+ for item in first_open_items:
95
+ if not isinstance(item, dict):
96
+ continue
97
+ if _user_todo_item_is_explicitly_non_gating(item):
98
+ continue
99
+ text = _protocol_action_label(item.get("text"))
100
+ if not text:
101
+ continue
102
+ actions.append(text)
103
+ if len(actions) >= limit:
104
+ break
105
+ return actions
106
+
107
+
108
+ def user_channel_notice_todo_actions(summary: Any, *, limit: int = 3) -> list[str]:
109
+ if not isinstance(summary, dict):
110
+ return []
111
+ source_items = summary.get("user_action_items")
112
+ if not isinstance(source_items, list):
113
+ source_items = summary.get("first_open_items")
114
+ if not isinstance(source_items, list):
115
+ return []
116
+ actions: list[str] = []
117
+ for item in source_items:
118
+ if not isinstance(item, dict):
119
+ continue
120
+ if todo_item_task_class(item) != TODO_TASK_CLASS_USER_ACTION:
121
+ continue
122
+ text = _protocol_action_label(item.get("text"))
123
+ if not text:
124
+ continue
125
+ actions.append(text)
126
+ if len(actions) >= limit:
127
+ break
128
+ return actions
129
+
130
+
131
+ def user_channel_action_required(payload: dict[str, Any]) -> bool:
132
+ if payload.get("agent_work_mode") == "monitor_only":
133
+ return False
134
+ if _user_gate_scope_projection_repair_active(payload):
135
+ return False
136
+ if _user_gate_notification_suppressed(payload):
137
+ return False
138
+ obligation = payload.get("autonomous_replan_obligation")
139
+ if isinstance(obligation, dict) and obligation.get("required") is True:
140
+ # An active replan obligation owns the frontier; a non-blocking user
141
+ # action must not mask the agent-side work it obligates.
142
+ return False
143
+ return bool(payload.get("requires_user_action")) or bool(
144
+ user_channel_action_todo_actions(payload.get("user_todo_summary"))
145
+ ) or _user_action_owns_empty_agent_lane(payload)
146
+
147
+
148
+ def _user_action_owns_empty_agent_lane(payload: dict[str, Any]) -> bool:
149
+ """Return true when an open user-action todo is the lane's only remaining work.
150
+
151
+ A user_action todo is a notice by default, but when the agent lane has no
152
+ executable work it owns the frontier: the loop must wait quietly for the
153
+ user instead of re-running the same steering-audit turn forever.
154
+ """
155
+
156
+ return _user_action_owns_empty_agent_lane_from_summaries(
157
+ payload.get("user_todo_summary"),
158
+ payload.get("agent_todo_summary"),
159
+ )
160
+
161
+
162
+ def _user_action_owns_empty_agent_lane_from_summaries(
163
+ user_summary: Any,
164
+ agent_summary: Any,
165
+ ) -> bool:
166
+ if not isinstance(user_summary, dict) or not isinstance(agent_summary, dict):
167
+ return False
168
+ user_items = user_summary.get("user_action_items")
169
+ if not isinstance(user_items, list):
170
+ user_items = user_summary.get("first_open_items")
171
+ if not isinstance(user_items, list):
172
+ return False
173
+ has_open_user_action = any(
174
+ isinstance(item, dict)
175
+ and not item.get("done")
176
+ and str(item.get("status") or "open").strip().lower()
177
+ in {"", "open", "todo", "active", "pending"}
178
+ and todo_item_task_class(item) == TODO_TASK_CLASS_USER_ACTION
179
+ for item in user_items
180
+ )
181
+ if not has_open_user_action:
182
+ return False
183
+ first_executable = agent_summary.get("first_executable_items")
184
+ if isinstance(first_executable, list) and first_executable:
185
+ return False
186
+ return True
187
+
188
+
189
+ def _user_gate_notification_suppressed(payload: dict[str, Any]) -> bool:
190
+ cooldown = payload.get("user_gate_notification_cooldown")
191
+ return isinstance(cooldown, dict) and cooldown.get("notification_suppressed") is True
192
+
193
+
194
+ def _user_gate_scope_projection_repair_active(payload: dict[str, Any]) -> bool:
195
+ repair = payload.get("stall_self_repair")
196
+ return bool(
197
+ isinstance(repair, dict)
198
+ and repair.get("trigger")
199
+ in {
200
+ "user_gate_scope_projection_drift",
201
+ "runtime_capability_user_gate_overreach",
202
+ }
203
+ )
204
+
205
+
206
+ def _capability_resolution_user_actions(payload: dict[str, Any]) -> list[str]:
207
+ capability_gate = (
208
+ payload.get("capability_gate")
209
+ if isinstance(payload.get("capability_gate"), dict)
210
+ else {}
211
+ )
212
+ if not capability_gate.get("owner_missing"):
213
+ return []
214
+ owner_action = protocol_action_text(capability_gate.get("owner_action"))
215
+ return [owner_action] if owner_action else []
216
+
217
+
218
+ def finalize_user_gate_notification_cooldown(
219
+ payload: dict[str, Any],
220
+ *,
221
+ available_capabilities: Any = None,
222
+ scheduler_execution_context: (
223
+ Mapping[str, Any] | SchedulerExecutionContextResolution | None
224
+ ) = None,
225
+ ) -> None:
226
+ scheduler_hint = payload.get("scheduler_hint")
227
+ cooldown = (
228
+ scheduler_hint.get("user_gate_notification_cooldown")
229
+ if isinstance(scheduler_hint, dict)
230
+ else None
231
+ )
232
+ if isinstance(cooldown, dict):
233
+ payload["user_gate_notification_cooldown"] = dict(cooldown)
234
+ if _user_gate_notification_suppressed(payload):
235
+ payload["pending_user_action"] = bool(
236
+ payload.get("requires_user_action")
237
+ or user_channel_action_todo_actions(payload.get("user_todo_summary"))
238
+ )
239
+ payload["requires_user_action"] = False
240
+ payload["interaction_contract"] = build_interaction_contract(
241
+ payload,
242
+ available_capabilities=available_capabilities,
243
+ scheduler_execution_context=scheduler_execution_context,
244
+ )
245
+ attach_user_action_compat_fields(payload)
246
+
247
+
248
+ def projected_user_channel_actions(
249
+ payload: dict[str, Any],
250
+ *,
251
+ limit: int = 3,
252
+ ) -> list[str]:
253
+ if payload.get("agent_work_mode") == "monitor_only":
254
+ return []
255
+ if _user_gate_scope_projection_repair_active(payload):
256
+ return []
257
+ if _user_gate_notification_suppressed(payload):
258
+ return []
259
+ actions = user_channel_action_todo_actions(
260
+ payload.get("user_todo_summary"),
261
+ limit=limit,
262
+ )
263
+ if actions:
264
+ return actions
265
+ notices = user_channel_notice_todo_actions(
266
+ payload.get("user_todo_summary"),
267
+ limit=limit,
268
+ )
269
+ if notices:
270
+ return notices
271
+ capability_actions = _capability_resolution_user_actions(payload)
272
+ if capability_actions:
273
+ return capability_actions[:limit]
274
+ if not user_channel_action_required(payload):
275
+ return []
276
+ capability_gate = (
277
+ payload.get("capability_gate")
278
+ if isinstance(payload.get("capability_gate"), dict)
279
+ else {}
280
+ )
281
+ if capability_gate.get("owner_missing"):
282
+ owner_action = protocol_action_text(capability_gate.get("owner_action"))
283
+ if owner_action:
284
+ return [owner_action]
285
+ for key in ("gate_prompt", "operator_question", "open_todo_notify_reason"):
286
+ text = protocol_action_text(payload.get(key))
287
+ if text:
288
+ return [text]
289
+ return []
290
+
291
+
292
+ def attach_user_action_compat_fields(payload: dict[str, Any]) -> None:
293
+ action_required = user_channel_action_required(payload)
294
+ payload["requires_user_action"] = action_required
295
+ payload["action_required"] = action_required
296
+ if _user_gate_scope_projection_repair_active(payload):
297
+ for key in ("notify_user_on_gate", "gate_prompt", "open_todo_notify_reason"):
298
+ payload.pop(key, None)
299
+ payload["open_count"] = open_todo_count(payload.get("user_todo_summary"))
300
+
301
+
302
+ def protocol_action_packet_fields(payload: dict[str, Any]) -> dict[str, Any]:
303
+ """Return the ordered semantic fields rendered by protocol_action_packet_v0."""
304
+
305
+ execution_obligation = (
306
+ payload.get("execution_obligation")
307
+ if isinstance(payload.get("execution_obligation"), dict)
308
+ else {}
309
+ )
310
+ work_lane = (
311
+ payload.get("work_lane_contract")
312
+ if isinstance(payload.get("work_lane_contract"), dict)
313
+ else {}
314
+ )
315
+ automation_liveness = (
316
+ payload.get("automation_liveness")
317
+ if isinstance(payload.get("automation_liveness"), dict)
318
+ else {}
319
+ )
320
+ scheduler_hint = (
321
+ payload.get("scheduler_hint")
322
+ if isinstance(payload.get("scheduler_hint"), dict)
323
+ else {}
324
+ )
325
+ requires_user_action = user_channel_action_required(payload)
326
+ blocked_successor_wait_observation = (
327
+ _blocked_successor_wait_observation_required(payload)
328
+ )
329
+ must_attempt_work = bool(execution_obligation.get("must_attempt_work")) or (
330
+ blocked_successor_wait_observation
331
+ )
332
+ scoped_user_gate_fallback = isinstance(payload.get("scoped_user_gate_fallback"), dict)
333
+ bounded_delivery_with_user_notice = (
334
+ requires_user_action
335
+ and not scoped_user_gate_fallback
336
+ and must_attempt_work
337
+ and bool(
338
+ execution_obligation.get(
339
+ "delivery_allowed",
340
+ payload.get("normal_delivery_allowed")
341
+ or payload.get("recovery_delivery_allowed")
342
+ or payload.get("self_repair_allowed")
343
+ or payload.get("should_run"),
344
+ )
345
+ )
346
+ )
347
+ quiet_noop_allowed = (
348
+ not requires_user_action
349
+ and not must_attempt_work
350
+ and not scoped_user_gate_fallback
351
+ )
352
+
353
+ user_actions = projected_user_channel_actions(payload)
354
+
355
+ if requires_user_action and scoped_user_gate_fallback:
356
+ primary_actor = "agent_with_user_gate"
357
+ agent_action_required = True
358
+ agent_action = _protocol_first_candidate_action(payload) or (
359
+ "surface the scoped user gate, then advance one non-gated fallback"
360
+ )
361
+ elif bounded_delivery_with_user_notice:
362
+ primary_actor = "agent_with_user_gate"
363
+ agent_action_required = True
364
+ agent_action = _protocol_first_candidate_action(payload) or "advance one bounded segment"
365
+ elif requires_user_action:
366
+ primary_actor = "user"
367
+ agent_action_required = False
368
+ capability_gate = (
369
+ payload.get("capability_gate")
370
+ if isinstance(payload.get("capability_gate"), dict)
371
+ else {}
372
+ )
373
+ agent_action = (
374
+ capability_gate.get("owner_action")
375
+ if capability_gate.get("action") == "ask_owner"
376
+ and capability_gate.get("owner_action")
377
+ else "wait for user/owner action after surfacing the blocker or gate"
378
+ )
379
+ elif blocked_successor_wait_observation:
380
+ primary_actor = "agent"
381
+ agent_action_required = True
382
+ agent_action = (
383
+ "record one no-spend blocked-successor wait observation, then rerun quota"
384
+ )
385
+ elif must_attempt_work and payload.get("agent_work_mode") == "monitor_only":
386
+ primary_actor = "agent"
387
+ agent_action_required = True
388
+ agent_action = (
389
+ protocol_action_text(work_lane.get("action"), limit=220)
390
+ or "attempt the due monitor and write back only a material transition"
391
+ )
392
+ elif must_attempt_work:
393
+ primary_actor = "agent"
394
+ agent_action_required = True
395
+ if str(execution_obligation.get("kind") or "") == "outcome_floor_recovery":
396
+ agent_action = (
397
+ "produce the required outcome-floor evidence artifact or write "
398
+ "the concrete blocker"
399
+ )
400
+ else:
401
+ agent_action = _protocol_first_candidate_action(payload) or "advance one bounded segment"
402
+ else:
403
+ primary_actor = "agent"
404
+ agent_action_required = False
405
+ agent_action = _protocol_monitor_action(payload) or "quiet no-op; no material transition"
406
+
407
+ action_key = (
408
+ "agent_action"
409
+ if agent_action_required
410
+ else "user_action"
411
+ if requires_user_action
412
+ else "agent_action"
413
+ )
414
+ action_value = (
415
+ agent_action
416
+ if agent_action_required
417
+ else user_actions[0]
418
+ if requires_user_action and user_actions
419
+ else agent_action
420
+ )
421
+ fields: dict[str, Any] = {
422
+ "actor": primary_actor,
423
+ "user_action_required": requires_user_action,
424
+ "agent_action_required": agent_action_required,
425
+ "quiet_noop_allowed": quiet_noop_allowed,
426
+ }
427
+ if work_lane.get("lane"):
428
+ fields["lane"] = work_lane.get("lane")
429
+ if automation_liveness.get("automation_action"):
430
+ fields["automation"] = automation_liveness.get("automation_action")
431
+ if scheduler_hint.get("action"):
432
+ fields["scheduler"] = scheduler_hint.get("action")
433
+ if automation_liveness.get("pause_allowed") is False:
434
+ fields["pause_allowed"] = False
435
+ fields["llm"] = PROTOCOL_ACTION_PACKET_LLM_POLICY
436
+ if user_actions and (not requires_user_action or action_key != "user_action"):
437
+ fields["user_action_pending"] = True
438
+ text = protocol_action_text(user_actions[0], limit=80)
439
+ if text:
440
+ fields["user_action"] = text
441
+ text = protocol_action_text(action_value, limit=80)
442
+ if text:
443
+ fields[action_key] = text
444
+ return fields
445
+
446
+
447
+ def render_protocol_action_packet_summary(fields: dict[str, Any]) -> str:
448
+ parts: list[str] = []
449
+ for key, value in fields.items():
450
+ if isinstance(value, bool):
451
+ rendered = str(value).lower()
452
+ else:
453
+ rendered = str(value)
454
+ parts.append(f"{key}={rendered}")
455
+ return " ".join(parts)
456
+
457
+
458
+ def build_protocol_action_packet(payload: dict[str, Any]) -> dict[str, Any]:
459
+ fields = protocol_action_packet_fields(payload)
460
+ return {
461
+ "schema_version": PROTOCOL_ACTION_PACKET_SCHEMA_VERSION,
462
+ "summary": render_protocol_action_packet_summary(fields),
463
+ }
464
+
465
+
466
+ def _interaction_mode(payload: dict[str, Any]) -> str:
467
+ execution_obligation = (
468
+ payload.get("execution_obligation")
469
+ if isinstance(payload.get("execution_obligation"), dict)
470
+ else {}
471
+ )
472
+ heartbeat_recommendation = (
473
+ payload.get("heartbeat_recommendation")
474
+ if isinstance(payload.get("heartbeat_recommendation"), dict)
475
+ else {}
476
+ )
477
+ kind = str(execution_obligation.get("kind") or "")
478
+ effective_action = str(payload.get("effective_action") or "")
479
+ state = str(payload.get("state") or "")
480
+ if effective_action == "agent_monitor_only":
481
+ return "agent_monitor_only"
482
+ if effective_action == "monitor_due":
483
+ return "monitor_due"
484
+ if effective_action == "terminal_no_followup" or state == "terminal_no_followup":
485
+ return "terminal_no_followup"
486
+ if effective_action == "peer_coordination_blocked":
487
+ return effective_action
488
+ if payload.get("scoped_user_gate_fallback"):
489
+ return "scoped_user_gate_fallback"
490
+ if _user_gate_notification_suppressed(payload):
491
+ return "user_gate_cooldown_wait"
492
+ if effective_action == "automation_prompt_upgrade_required":
493
+ return "automation_prompt_upgrade"
494
+ if user_channel_action_required(payload):
495
+ if (
496
+ bool(execution_obligation.get("must_attempt_work"))
497
+ and bool(
498
+ execution_obligation.get(
499
+ "delivery_allowed",
500
+ payload.get("normal_delivery_allowed")
501
+ or payload.get("recovery_delivery_allowed")
502
+ or payload.get("self_repair_allowed")
503
+ or payload.get("should_run"),
504
+ )
505
+ )
506
+ ):
507
+ return "bounded_delivery_with_user_notice"
508
+ if payload.get("notify_user_on_gate") or state == "operator_gate":
509
+ return "user_gate"
510
+ if payload.get("notify_user_on_open_todo"):
511
+ return "user_todo_blocker_push"
512
+ return "user_action_required"
513
+ if kind == "external_evidence_observation_required":
514
+ return "external_evidence_observation"
515
+ if kind == AUTONOMOUS_REPLAN_REQUIRED_MODE:
516
+ return "autonomous_replan"
517
+ if effective_action == "coordinate_task_bundle":
518
+ return "task_orchestration"
519
+ agent_scope_action = _agent_scope_frontier_action(effective_action)
520
+ if agent_scope_action is not None:
521
+ return agent_scope_action.value
522
+ if effective_action == "monitor_quiet_skip":
523
+ return "monitor_quiet_skip"
524
+ if payload.get("recovery_delivery_allowed") or effective_action == "outcome_floor_recovery":
525
+ return "outcome_floor_recovery"
526
+ if effective_action == "capability_bridge_repair":
527
+ return "capability_bridge_repair"
528
+ if effective_action == "agent_workspace_repair":
529
+ return effective_action
530
+ if effective_action == "boundary_projection_repair":
531
+ return "boundary_projection_repair"
532
+ if payload.get("self_repair_allowed"):
533
+ return "control_plane_self_repair"
534
+ if heartbeat_recommendation.get("stop_if_unchanged"):
535
+ return "mapped_noop_if_unchanged"
536
+ if payload.get("normal_delivery_allowed") or payload.get("should_run"):
537
+ return "bounded_delivery"
538
+ if state == "blocked_health":
539
+ return "health_blocked"
540
+ if state == "throttled":
541
+ return "quota_throttled"
542
+ if state in {"waiting", "focus_wait"}:
543
+ return "blocked_wait"
544
+ return "skip"
545
+
546
+
547
+ def _scoped_cli_args(
548
+ agent_identity: dict[str, Any],
549
+ *,
550
+ available_capabilities: Any,
551
+ ) -> str:
552
+ agent_id = str(agent_identity.get("agent_id") or "").strip()
553
+ if not agent_id:
554
+ return ""
555
+ capability_args = "".join(
556
+ f" --available-capability {shlex.quote(capability)}"
557
+ for capability in runtime_capabilities_for_cli_projection(
558
+ available_capabilities
559
+ )
560
+ )
561
+ return f" --agent-id {agent_id}{capability_args}"
562
+
563
+
564
+ def _codex_app_settlement_plan(
565
+ payload: dict[str, Any],
566
+ *,
567
+ available_capabilities: Any,
568
+ scheduler_execution_context: (
569
+ Mapping[str, Any] | SchedulerExecutionContextResolution | None
570
+ ),
571
+ ) -> dict[str, Any] | None:
572
+ if (
573
+ scheduler_runtime_profile_for_execution_context(
574
+ scheduler_execution_context
575
+ )
576
+ is not SchedulerRuntimeProfile.CODEX_APP_HEARTBEAT
577
+ ):
578
+ return None
579
+ agent_identity = (
580
+ payload.get("agent_identity")
581
+ if isinstance(payload.get("agent_identity"), dict)
582
+ else {}
583
+ )
584
+ selected_todo = (
585
+ payload.get("selected_todo")
586
+ if isinstance(payload.get("selected_todo"), dict)
587
+ else {}
588
+ )
589
+ goal_id = str(payload.get("goal_id") or "").strip()
590
+ agent_id = str(agent_identity.get("agent_id") or "").strip()
591
+ todo_id = normalize_todo_id(selected_todo.get("todo_id"))
592
+ if not goal_id or not agent_id or not todo_id:
593
+ return None
594
+ scoped_cli_args = _scoped_cli_args(
595
+ agent_identity,
596
+ available_capabilities=available_capabilities,
597
+ )
598
+ return build_codex_app_settlement_plan(
599
+ goal_id=goal_id,
600
+ agent_id=agent_id,
601
+ todo_id=todo_id,
602
+ scoped_cli_args=scoped_cli_args,
603
+ lifecycle_actor_args=f" --agent-id {shlex.quote(agent_id)}",
604
+ ).as_dict()
605
+
606
+
607
+ def _quota_spend_action(
608
+ goal_id: str,
609
+ *,
610
+ scoped_cli_args: str,
611
+ payload: dict[str, Any],
612
+ settlement_plan: Mapping[str, Any] | None,
613
+ ) -> str:
614
+ typed_command = settlement_step_command(
615
+ settlement_plan,
616
+ SettlementStepKind.QUOTA_SPEND,
617
+ )
618
+ if typed_command:
619
+ return typed_command
620
+ selected = (
621
+ payload.get("selected_todo")
622
+ if isinstance(payload.get("selected_todo"), dict)
623
+ else {}
624
+ )
625
+ todo_id = normalize_todo_id(selected.get("todo_id"))
626
+ todo_arg = f" --todo-id {todo_id}" if todo_id else ""
627
+ return (
628
+ f"loopx quota spend-slot --goal-id {goal_id} --slots 1 "
629
+ f"--source heartbeat --execute{todo_arg}{scoped_cli_args}"
630
+ )
631
+
632
+
633
+ def _terminal_cli_actions(
634
+ *,
635
+ mode: str,
636
+ goal_id: str,
637
+ scoped_cli_args: str,
638
+ payload: dict[str, Any],
639
+ settlement_plan: Mapping[str, Any] | None,
640
+ capability_resolution_actions: list[str],
641
+ capability_reentry_actions: list[str],
642
+ ) -> list[str]:
643
+ if mode == "capability_bridge_repair":
644
+ return capability_reentry_actions or [
645
+ "perform the projected real task-facing capability check, then rerun "
646
+ "quota in this same turn"
647
+ ]
648
+ resolution_actions = [
649
+ *capability_resolution_actions,
650
+ *capability_reentry_actions,
651
+ ]
652
+ if mode in {
653
+ "bounded_delivery",
654
+ "outcome_floor_recovery",
655
+ "control_plane_self_repair",
656
+ "boundary_projection_repair",
657
+ "scoped_user_gate_fallback",
658
+ "bounded_delivery_with_user_notice",
659
+ }:
660
+ typed_writeback = settlement_step_command(
661
+ settlement_plan,
662
+ SettlementStepKind.DURABLE_WRITEBACK,
663
+ )
664
+ return [
665
+ *resolution_actions,
666
+ typed_writeback
667
+ or f"loopx refresh-state --goal-id {goal_id} --classification <validated_progress>{scoped_cli_args}",
668
+ _quota_spend_action(
669
+ goal_id,
670
+ scoped_cli_args=scoped_cli_args,
671
+ payload=payload,
672
+ settlement_plan=settlement_plan,
673
+ ),
674
+ ]
675
+ if mode in {"user_gate", "user_todo_blocker_push", "user_action_required"}:
676
+ return [
677
+ *resolution_actions,
678
+ "no quota spend for blocker-push/gate-notification",
679
+ ]
680
+ return ["no quota spend without validated transition/blocker writeback"]
681
+
682
+
683
+ def interaction_next_cli_actions(
684
+ payload: dict[str, Any],
685
+ *,
686
+ mode: str,
687
+ available_capabilities: Any = None,
688
+ scheduler_execution_context: (
689
+ Mapping[str, Any] | SchedulerExecutionContextResolution | None
690
+ ) = None,
691
+ capability_reentry: dict[str, Any] | None = None,
692
+ settlement_plan: Mapping[str, Any] | None = None,
693
+ ) -> list[str]:
694
+ goal_id = str(payload.get("goal_id") or "<GOAL_ID>")
695
+ agent_identity = (
696
+ payload.get("agent_identity")
697
+ if isinstance(payload.get("agent_identity"), dict)
698
+ else {}
699
+ )
700
+ scoped_cli_args = _scoped_cli_args(
701
+ agent_identity,
702
+ available_capabilities=available_capabilities,
703
+ )
704
+ lifecycle_actor_args = (
705
+ f" --agent-id {shlex.quote(str(agent_identity.get('agent_id')).strip())}"
706
+ if agent_identity.get("agent_id")
707
+ else ""
708
+ )
709
+ if settlement_plan is None:
710
+ settlement_plan = _codex_app_settlement_plan(
711
+ payload,
712
+ available_capabilities=available_capabilities,
713
+ scheduler_execution_context=scheduler_execution_context,
714
+ )
715
+ settlement_args = settlement_binding_args(settlement_plan)
716
+ try:
717
+ scheduler_args = render_scheduler_execution_args(
718
+ scheduler_execution_context=scheduler_execution_context,
719
+ )
720
+ except ValueError:
721
+ scheduler_args = ""
722
+ typed_quota_guard = (
723
+ f"loopx --format json quota should-run --goal-id {goal_id}"
724
+ f"{scoped_cli_args}{scheduler_args}"
725
+ if scheduler_args
726
+ else "rerun the typed quota_guard from the current host packet"
727
+ )
728
+ typed_monitor_poll = (
729
+ f"loopx quota monitor-poll --goal-id {goal_id}{scoped_cli_args}"
730
+ f"{scheduler_args} --execute"
731
+ if scheduler_args
732
+ else "use the current host packet's typed monitor command"
733
+ )
734
+ typed_heartbeat_receipt_retry = (
735
+ f"on missing/write_failed heartbeat_receipt only: {typed_quota_guard} "
736
+ '--turn-instance-id "${LOOPX_TURN:?}"'
737
+ if scheduler_args
738
+ else (
739
+ "on missing/write_failed heartbeat_receipt only: retry the current "
740
+ "host packet's typed quota guard with the same heartbeat turn id"
741
+ )
742
+ )
743
+ heartbeat_turn_receipt_enabled = (
744
+ scheduler_runtime_profile_for_execution_context(
745
+ scheduler_execution_context
746
+ )
747
+ is SchedulerRuntimeProfile.CODEX_APP_HEARTBEAT
748
+ )
749
+ capability_resolution_actions = build_capability_resolution_writeback_actions(
750
+ payload.get("capability_gate"),
751
+ goal_id=goal_id,
752
+ agent_id=(
753
+ str(agent_identity.get("agent_id"))
754
+ if agent_identity.get("agent_id")
755
+ else None
756
+ ),
757
+ )
758
+ if capability_reentry is None:
759
+ capability_reentry = build_runtime_capability_reentry_packet(
760
+ payload,
761
+ available_capabilities=available_capabilities,
762
+ scheduler_execution_context=scheduler_execution_context,
763
+ )
764
+ capability_reentry_actions = (
765
+ [str(candidate["command"]) for candidate in capability_reentry["candidates"]]
766
+ if capability_reentry is not None
767
+ else []
768
+ )
769
+ if mode == "terminal_no_followup":
770
+ return ["no quota spend until explicit goal resume or newly projected work"]
771
+ if mode == "agent_monitor_only":
772
+ return [
773
+ "no quota spend until a due monitor, verified direct reply, or explicit work-mode change"
774
+ ]
775
+ if mode == "automation_prompt_upgrade":
776
+ automation_prompt_upgrade = (
777
+ payload.get("automation_prompt_upgrade")
778
+ if isinstance(payload.get("automation_prompt_upgrade"), dict)
779
+ else {}
780
+ )
781
+ actions = [
782
+ str(item.get("command") or "").strip()
783
+ for item in automation_prompt_upgrade.get("agent_example_commands") or []
784
+ if isinstance(item, dict)
785
+ ]
786
+ completion_command = str(
787
+ automation_prompt_upgrade.get("completion_command") or ""
788
+ ).strip()
789
+ if completion_command:
790
+ actions.append(completion_command)
791
+ return [action for action in actions if action] or [
792
+ f"loopx heartbeat-prompt --thin --goal-id {goal_id} --agent-id <registered-agent> --agent-scope '<scope>'",
793
+ ]
794
+ if mode == "monitor_quiet_skip":
795
+ if heartbeat_turn_receipt_enabled:
796
+ return [typed_heartbeat_receipt_retry]
797
+ return [
798
+ typed_monitor_poll,
799
+ typed_quota_guard,
800
+ ]
801
+ if mode == "monitor_due":
802
+ return [
803
+ typed_monitor_poll,
804
+ typed_quota_guard,
805
+ ]
806
+ task_orchestration_contract = payload.get("task_orchestration_contract")
807
+ if (
808
+ mode == "task_orchestration"
809
+ and isinstance(task_orchestration_contract, Mapping)
810
+ and task_orchestration_contract.get("mode") == "adaptive"
811
+ ):
812
+ return [typed_quota_guard]
813
+ if mode == AgentScopeFrontierAction.SUCCESSOR_REPLAN_REQUIRED.value:
814
+ agent_scope_frontier = (
815
+ payload.get("agent_scope_frontier")
816
+ if isinstance(payload.get("agent_scope_frontier"), dict)
817
+ else {}
818
+ )
819
+ monitor_candidates = (
820
+ agent_scope_frontier.get("monitor_blocked_resume_candidates")
821
+ if isinstance(agent_scope_frontier.get("monitor_blocked_resume_candidates"), list)
822
+ else []
823
+ )
824
+ if monitor_candidates:
825
+ first_candidate = (
826
+ monitor_candidates[0]
827
+ if isinstance(monitor_candidates[0], dict)
828
+ else {}
829
+ )
830
+ monitor_todo_id = str(
831
+ first_candidate.get("blocking_monitor_todo_id") or "<monitor_todo_id>"
832
+ )
833
+ gated_todo_id = str(first_candidate.get("todo_id") or "<gated_todo_id>")
834
+ return [
835
+ f"loopx todo complete --goal-id {goal_id} --todo-id {monitor_todo_id}{lifecycle_actor_args} --evidence '<validated gate evidence>'",
836
+ f"loopx todo update --goal-id {goal_id} --todo-id {gated_todo_id}{lifecycle_actor_args} --note '<public-safe gate repair reason>'",
837
+ f"loopx refresh-state --goal-id {goal_id} --classification standing_monitor_gate_repair_recorded --delivery-batch-scale single_surface --delivery-outcome outcome_progress{settlement_args}{scoped_cli_args}",
838
+ _quota_spend_action(
839
+ goal_id,
840
+ scoped_cli_args=scoped_cli_args,
841
+ payload=payload,
842
+ settlement_plan=settlement_plan,
843
+ ),
844
+ ]
845
+ route_candidates = (
846
+ agent_scope_frontier.get("route_continuation_replan_candidates")
847
+ if isinstance(agent_scope_frontier.get("route_continuation_replan_candidates"), list)
848
+ else []
849
+ )
850
+ if route_candidates:
851
+ return [
852
+ f"loopx todo add --goal-id {goal_id} --role agent --text '<public-safe route continuation advancement todo>'",
853
+ f"loopx refresh-state --goal-id {goal_id} --classification route_continuation_replan_recorded --delivery-batch-scale single_surface --delivery-outcome outcome_progress{settlement_args}{scoped_cli_args}",
854
+ _quota_spend_action(
855
+ goal_id,
856
+ scoped_cli_args=scoped_cli_args,
857
+ payload=payload,
858
+ settlement_plan=settlement_plan,
859
+ ),
860
+ ]
861
+ candidates = (
862
+ agent_scope_frontier.get("deferred_resume_candidates")
863
+ if isinstance(agent_scope_frontier.get("deferred_resume_candidates"), list)
864
+ else []
865
+ )
866
+ first_candidate = candidates[0] if candidates and isinstance(candidates[0], dict) else {}
867
+ todo_id = str(first_candidate.get("todo_id") or "<todo_id>")
868
+ return [
869
+ (
870
+ f"loopx todo update --goal-id {goal_id} --todo-id {todo_id}"
871
+ f"{lifecycle_actor_args} --status open --clear-resume-when "
872
+ "--note '<public-safe successor replan reason>'"
873
+ ),
874
+ f"loopx refresh-state --goal-id {goal_id} --classification successor_replan_recorded --delivery-batch-scale single_surface --delivery-outcome outcome_progress{settlement_args}{scoped_cli_args}",
875
+ _quota_spend_action(
876
+ goal_id,
877
+ scoped_cli_args=scoped_cli_args,
878
+ payload=payload,
879
+ settlement_plan=settlement_plan,
880
+ ),
881
+ ]
882
+ if (
883
+ mode == AgentScopeFrontierAction.AGENT_SCOPE_WAIT.value
884
+ and _blocked_successor_wait_observation_required(payload)
885
+ ):
886
+ return [
887
+ typed_monitor_poll,
888
+ typed_quota_guard,
889
+ ]
890
+ if _agent_scope_frontier_action(mode) is not None:
891
+ return [
892
+ "no quota spend while this agent has no in-scope runnable candidate",
893
+ typed_quota_guard,
894
+ ]
895
+ if mode == "external_evidence_observation":
896
+ spend_action = _quota_spend_action(
897
+ goal_id,
898
+ scoped_cli_args=scoped_cli_args,
899
+ payload=payload,
900
+ settlement_plan=settlement_plan,
901
+ )
902
+ return [
903
+ "read approved controller/job/marker/writeback surfaces only",
904
+ (
905
+ "on a substantive transition or blocker only: "
906
+ f"loopx refresh-state --goal-id {goal_id} "
907
+ "--classification <compact_blocker_or_transition> "
908
+ "--delivery-batch-scale <scale> --delivery-outcome <outcome>"
909
+ f"{settlement_args}{scoped_cli_args}"
910
+ ),
911
+ (
912
+ "after that accountable writeback receipt only: "
913
+ f"{spend_action}; otherwise do not spend for unchanged observation"
914
+ ),
915
+ ]
916
+ if mode == "agent_workspace_repair":
917
+ return [
918
+ "create or switch to an independent git worktree/branch",
919
+ typed_quota_guard,
920
+ ]
921
+ if mode == "autonomous_replan":
922
+ packet = (
923
+ payload.get("replan_action_packet")
924
+ if isinstance(payload.get("replan_action_packet"), dict)
925
+ else {}
926
+ )
927
+ writeback_contract = (
928
+ packet.get("writeback_contract")
929
+ if isinstance(packet.get("writeback_contract"), dict)
930
+ else {}
931
+ )
932
+ successor_command = str(
933
+ writeback_contract.get("successor_command") or ""
934
+ ).strip()
935
+ successor_ref = (
936
+ "execute replan_action_packet.writeback_contract.successor_command"
937
+ if successor_command
938
+ else ""
939
+ )
940
+ successor_boundary = "on host_action=end_current_heartbeat: stop"
941
+ if successor_command:
942
+ return [successor_ref, successor_boundary]
943
+ typed_progress_args = (
944
+ "--progress-result-class "
945
+ "<advanced|blocked|exploration_exhausted|no_followup> "
946
+ "--progress-surface-id <surface-id> "
947
+ "--progress-hypothesis-id <hypothesis-id> "
948
+ "--progress-probe-kind <probe-kind> "
949
+ "--progress-evidence-id <evidence-id>"
950
+ )
951
+ refresh_command = (
952
+ f"loopx --format json refresh-state --goal-id {goal_id} "
953
+ "--progress-scope agent_lane "
954
+ "--classification bounded_replan_progress "
955
+ f"{typed_progress_args}"
956
+ f"{settlement_args}{scoped_cli_args}"
957
+ )
958
+ return [
959
+ action
960
+ for action in (
961
+ successor_ref,
962
+ refresh_command,
963
+ successor_boundary if successor_command else "",
964
+ )
965
+ if action
966
+ ]
967
+ return _terminal_cli_actions(
968
+ mode=mode,
969
+ goal_id=goal_id,
970
+ scoped_cli_args=scoped_cli_args,
971
+ payload=payload,
972
+ settlement_plan=settlement_plan,
973
+ capability_resolution_actions=capability_resolution_actions,
974
+ capability_reentry_actions=capability_reentry_actions,
975
+ )
976
+
977
+
978
+ def _interaction_required_reads(payload: dict[str, Any]) -> list[dict[str, Any]]:
979
+ reads = payload.get("required_reads")
980
+ if not isinstance(reads, list):
981
+ return []
982
+ result: list[dict[str, Any]] = []
983
+ for item in reads:
984
+ if not isinstance(item, dict):
985
+ continue
986
+ command = protocol_action_text(item.get("command"), limit=360)
987
+ if not command:
988
+ continue
989
+ result.append({**item, "command": command})
990
+ return result
991
+
992
+
993
+ def _interaction_spend_policy(
994
+ execution_obligation: dict[str, Any],
995
+ heartbeat_recommendation: dict[str, Any],
996
+ *,
997
+ mode: str,
998
+ spend_after_validation: bool,
999
+ ) -> str | None:
1000
+ if mode == "terminal_no_followup":
1001
+ return "no spend for terminal automation shutdown"
1002
+ if mode == "agent_monitor_only":
1003
+ return "no spend while advancement is paused and no monitor is due"
1004
+ if mode in {"user_gate", "user_todo_blocker_push", "user_action_required"}:
1005
+ return "no spend for gate or blocker push"
1006
+ if mode == "monitor_quiet_skip":
1007
+ return "no spend for unchanged heartbeat stall receipt"
1008
+ if mode == "monitor_due":
1009
+ return (
1010
+ "spend once only after a validated material monitor transition; "
1011
+ "unchanged monitor polls are no-spend"
1012
+ )
1013
+ if mode == AgentScopeFrontierAction.SUCCESSOR_REPLAN_REQUIRED.value:
1014
+ return "spend once after validated successor replan/todo writeback"
1015
+ if _agent_scope_frontier_action(mode) is not None:
1016
+ return "no spend while the current agent has no in-scope runnable candidate"
1017
+ if mode == "agent_workspace_repair":
1018
+ return "no spend for moving agent work into an independent worktree"
1019
+ if mode == "automation_prompt_upgrade":
1020
+ return "no spend until the host update is acknowledged and quota reruns"
1021
+ if mode == "capability_bridge_repair":
1022
+ return (
1023
+ "no spend or advancement checkpoint for capability verification; rerun "
1024
+ "quota in the same turn"
1025
+ )
1026
+ if mode == "autonomous_replan":
1027
+ return (
1028
+ "spend only after accountable replan delta; no spend for "
1029
+ "surface_only watch-lane continuation"
1030
+ )
1031
+ if spend_after_validation:
1032
+ return "spend once after validated writeback"
1033
+ raw_policy = execution_obligation.get("spend_policy") or heartbeat_recommendation.get(
1034
+ "spend_policy"
1035
+ )
1036
+ if isinstance(raw_policy, str) and len(raw_policy) <= 80:
1037
+ return raw_policy
1038
+ return "no spend without validated transition"
1039
+
1040
+
1041
+ def _blocked_priority_fallback_user_reason(payload: dict[str, Any]) -> str | None:
1042
+ fallback = (
1043
+ payload.get("blocked_priority_fallback")
1044
+ if isinstance(payload.get("blocked_priority_fallback"), dict)
1045
+ else {}
1046
+ )
1047
+ if not fallback:
1048
+ return None
1049
+ if (
1050
+ fallback.get("requires_user_action") is not True
1051
+ and fallback.get("notify_user") is not True
1052
+ ):
1053
+ return None
1054
+ reason = str(fallback.get("reason") or "").strip()
1055
+ return reason or None
1056
+
1057
+
1058
+ def _interaction_must_attempt(
1059
+ execution_obligation: dict[str, Any],
1060
+ *,
1061
+ user_required: bool,
1062
+ scoped_user_gate_fallback: bool,
1063
+ bounded_delivery_with_user_notice: bool,
1064
+ ) -> bool:
1065
+ if user_required and not (
1066
+ scoped_user_gate_fallback or bounded_delivery_with_user_notice
1067
+ ):
1068
+ return False
1069
+ return bool(execution_obligation.get("must_attempt_work"))
1070
+
1071
+
1072
+ def _interaction_delivery_allowed(
1073
+ payload: dict[str, Any],
1074
+ execution_obligation: dict[str, Any],
1075
+ *,
1076
+ mode: str,
1077
+ user_required: bool,
1078
+ scoped_user_gate_fallback: bool,
1079
+ bounded_delivery_with_user_notice: bool,
1080
+ ) -> bool:
1081
+ if mode == "mapped_noop_if_unchanged":
1082
+ return False
1083
+ if user_required and not (
1084
+ scoped_user_gate_fallback or bounded_delivery_with_user_notice
1085
+ ):
1086
+ return False
1087
+ return bool(
1088
+ execution_obligation.get(
1089
+ "delivery_allowed",
1090
+ payload.get("normal_delivery_allowed")
1091
+ or payload.get("recovery_delivery_allowed")
1092
+ or payload.get("self_repair_allowed")
1093
+ or payload.get("should_run"),
1094
+ )
1095
+ )
1096
+
1097
+
1098
+ def _interaction_quiet_noop_allowed(
1099
+ *,
1100
+ mode: str,
1101
+ user_required: bool,
1102
+ must_attempt: bool,
1103
+ ) -> bool:
1104
+ if user_required or must_attempt:
1105
+ return False
1106
+ return _agent_scope_frontier_action(mode) is not None or mode in {
1107
+ "monitor_quiet_skip",
1108
+ "mapped_noop_if_unchanged",
1109
+ "quota_throttled",
1110
+ "blocked_wait",
1111
+ "user_gate_cooldown_wait",
1112
+ "terminal_no_followup",
1113
+ "peer_coordination_blocked",
1114
+ "agent_monitor_only",
1115
+ "skip",
1116
+ }
1117
+
1118
+
1119
+ def _interaction_spend_after_validation(mode: str) -> bool:
1120
+ return mode in {
1121
+ "bounded_delivery",
1122
+ "outcome_floor_recovery",
1123
+ "autonomous_replan",
1124
+ "control_plane_self_repair",
1125
+ "boundary_projection_repair",
1126
+ "external_evidence_observation",
1127
+ "monitor_due",
1128
+ "task_orchestration",
1129
+ AgentScopeFrontierAction.SUCCESSOR_REPLAN_REQUIRED.value,
1130
+ "scoped_user_gate_fallback",
1131
+ "bounded_delivery_with_user_notice",
1132
+ }
1133
+
1134
+
1135
+ def _interaction_user_reason(payload: dict[str, Any]) -> Any:
1136
+ return (
1137
+ (
1138
+ payload.get("stall_self_repair", {}).get("reason")
1139
+ if _user_gate_scope_projection_repair_active(payload)
1140
+ and isinstance(payload.get("stall_self_repair"), dict)
1141
+ else None
1142
+ )
1143
+ or (
1144
+ payload.get("user_gate_notification_cooldown", {}).get("reason")
1145
+ if _user_gate_notification_suppressed(payload)
1146
+ else None
1147
+ )
1148
+ or payload.get("open_todo_notify_reason")
1149
+ or payload.get("gate_prompt")
1150
+ or payload.get("operator_question")
1151
+ or _blocked_priority_fallback_user_reason(payload)
1152
+ or (
1153
+ payload.get("scoped_user_gate_fallback", {}).get("reason")
1154
+ if isinstance(payload.get("scoped_user_gate_fallback"), dict)
1155
+ else None
1156
+ )
1157
+ or (
1158
+ "open user todo requires user-visible follow-up while independent "
1159
+ "agent work may continue"
1160
+ if user_channel_action_todo_actions(payload.get("user_todo_summary"))
1161
+ else None
1162
+ )
1163
+ or (
1164
+ "open non-blocking user action should be surfaced while independent "
1165
+ "agent work continues"
1166
+ if user_channel_notice_todo_actions(payload.get("user_todo_summary"))
1167
+ else None
1168
+ )
1169
+ or (
1170
+ payload.get("agent_scope_frontier", {}).get("reason")
1171
+ if isinstance(payload.get("agent_scope_frontier"), dict)
1172
+ else None
1173
+ )
1174
+ or (
1175
+ payload.get("capability_gate", {}).get("reason")
1176
+ if isinstance(payload.get("capability_gate"), dict)
1177
+ else None
1178
+ )
1179
+ )
1180
+
1181
+
1182
+ def _build_interaction_agent_channel(
1183
+ payload: dict[str, Any],
1184
+ *,
1185
+ mode: str,
1186
+ must_attempt: bool,
1187
+ delivery_allowed: bool,
1188
+ quiet_noop_allowed: bool,
1189
+ capability_reentry: Mapping[str, Any] | None = None,
1190
+ ) -> dict[str, Any]:
1191
+ channel: dict[str, Any] = {
1192
+ "must_attempt": must_attempt,
1193
+ "delivery_allowed": delivery_allowed,
1194
+ "quiet_noop_allowed": quiet_noop_allowed,
1195
+ }
1196
+ channel.update(build_primary_action_projection(payload, mode=mode))
1197
+ if capability_reentry is not None:
1198
+ candidate = capability_reentry["candidates"][0]
1199
+ target = candidate["verification_target"]
1200
+ channel["next_task_action"] = {
1201
+ "kind": "capability_verification",
1202
+ "capability": candidate["capability"],
1203
+ "todo_id": target["todo_id"],
1204
+ "action_kind": target["action_kind"],
1205
+ "operation": target["action_kind"],
1206
+ "instruction": target["instruction"],
1207
+ "preflight_allowed": False,
1208
+ "advancement_checkpoint": False,
1209
+ "settles_turn": False,
1210
+ "continuation_cli_action_index": 0,
1211
+ }
1212
+ if target.get("target_ref"):
1213
+ channel["next_task_action"]["target_ref"] = target["target_ref"]
1214
+ if _blocked_successor_wait_observation_required(payload):
1215
+ channel["primary_action"] = (
1216
+ "record one no-spend blocked-successor wait observation, rerun quota, "
1217
+ "then replan after the second unchanged frontier"
1218
+ )
1219
+ return channel
1220
+
1221
+
1222
+ def _build_interaction_user_channel(
1223
+ payload: dict[str, Any],
1224
+ heartbeat_recommendation: dict[str, Any],
1225
+ *,
1226
+ user_required: bool,
1227
+ ) -> dict[str, Any]:
1228
+ actions = projected_user_channel_actions(payload, limit=3)
1229
+ non_blocking_notice = bool(
1230
+ not user_required
1231
+ and user_channel_notice_todo_actions(payload.get("user_todo_summary"), limit=3)
1232
+ )
1233
+ channel: dict[str, Any] = {
1234
+ "action_required": user_required,
1235
+ "notify": "NOTIFY"
1236
+ if user_required or non_blocking_notice
1237
+ else "DONT_NOTIFY"
1238
+ if (
1239
+ _user_gate_notification_suppressed(payload)
1240
+ or _user_gate_scope_projection_repair_active(payload)
1241
+ )
1242
+ else heartbeat_recommendation.get("notify", "DONT_NOTIFY"),
1243
+ }
1244
+ if actions:
1245
+ channel["max_items"] = 3
1246
+ channel["actions"] = actions
1247
+ if non_blocking_notice:
1248
+ channel["non_blocking"] = True
1249
+ reason = _interaction_user_reason(payload)
1250
+ if reason:
1251
+ channel["reason"] = reason
1252
+ return channel
1253
+
1254
+
1255
+ def _build_interaction_response_plan(
1256
+ *,
1257
+ user_channel: dict[str, Any],
1258
+ agent_channel: dict[str, Any],
1259
+ ) -> dict[str, Any] | None:
1260
+ """Project the exact host-visible response for a blocking user gate."""
1261
+
1262
+ if not (
1263
+ user_channel.get("action_required") is True
1264
+ and user_channel.get("notify") == "NOTIFY"
1265
+ and agent_channel.get("must_attempt") is False
1266
+ and agent_channel.get("delivery_allowed") is False
1267
+ and agent_channel.get("quiet_noop_allowed") is False
1268
+ ):
1269
+ return None
1270
+ return {
1271
+ "schema_version": INTERACTION_RESPONSE_PLAN_SCHEMA_VERSION,
1272
+ "kind": "surface_user_gate",
1273
+ "decision": "ask_user",
1274
+ "action_sequence": ["notify", "wait"],
1275
+ "silent_wait_allowed": False,
1276
+ }
1277
+
1278
+
1279
+ def _build_interaction_cli_channel(
1280
+ payload: dict[str, Any],
1281
+ execution_obligation: dict[str, Any],
1282
+ heartbeat_recommendation: dict[str, Any],
1283
+ *,
1284
+ mode: str,
1285
+ spend_after_validation: bool,
1286
+ available_capabilities: Any = None,
1287
+ scheduler_execution_context: (
1288
+ Mapping[str, Any] | SchedulerExecutionContextResolution | None
1289
+ ) = None,
1290
+ capability_reentry: dict[str, Any] | None = None,
1291
+ ) -> dict[str, Any]:
1292
+ if capability_reentry is None:
1293
+ capability_reentry = build_runtime_capability_reentry_packet(
1294
+ payload,
1295
+ available_capabilities=available_capabilities,
1296
+ scheduler_execution_context=scheduler_execution_context,
1297
+ )
1298
+ settlement_plan = _codex_app_settlement_plan(
1299
+ payload,
1300
+ available_capabilities=available_capabilities,
1301
+ scheduler_execution_context=scheduler_execution_context,
1302
+ )
1303
+ channel = {
1304
+ "next_cli_actions": interaction_next_cli_actions(
1305
+ payload,
1306
+ mode=mode,
1307
+ available_capabilities=available_capabilities,
1308
+ scheduler_execution_context=scheduler_execution_context,
1309
+ capability_reentry=capability_reentry,
1310
+ settlement_plan=settlement_plan,
1311
+ ),
1312
+ "spend_allowed_now": False,
1313
+ "spend_after_validation": spend_after_validation,
1314
+ "spend_policy": _interaction_spend_policy(
1315
+ execution_obligation,
1316
+ heartbeat_recommendation,
1317
+ mode=mode,
1318
+ spend_after_validation=spend_after_validation,
1319
+ ),
1320
+ }
1321
+ if settlement_plan is not None and spend_after_validation:
1322
+ channel["settlement_plan"] = settlement_plan
1323
+ if capability_reentry is not None:
1324
+ channel["runtime_capability_reentry"] = capability_reentry
1325
+ selected_todo = (
1326
+ payload.get("selected_todo")
1327
+ if isinstance(payload.get("selected_todo"), Mapping)
1328
+ else {}
1329
+ )
1330
+ if spend_after_validation and selected_todo.get("task_repository"):
1331
+ channel["delivery_workspace_causality"] = {
1332
+ "schema_version": "delivery_workspace_causality_v0",
1333
+ "refresh": "delivery_workspace; otherwise --delivery-workspace-path",
1334
+ "spend": "recorded_delivery_workspace",
1335
+ "mismatch": "fail_closed",
1336
+ }
1337
+ return channel
1338
+
1339
+
1340
+ def _attach_interaction_required_reads(
1341
+ contract: dict[str, Any],
1342
+ required_reads: list[dict[str, Any]],
1343
+ ) -> None:
1344
+ if not required_reads:
1345
+ return
1346
+ contract["agent_channel"]["required_reads"] = required_reads
1347
+ contract["cli_channel"]["required_reads"] = required_reads
1348
+
1349
+
1350
+ def _attach_interaction_post_writeback_actions(
1351
+ contract: dict[str, Any], payload: dict[str, Any]
1352
+ ) -> None:
1353
+ goal_boundary = (
1354
+ payload.get("goal_boundary")
1355
+ if isinstance(payload.get("goal_boundary"), Mapping)
1356
+ else {}
1357
+ )
1358
+ actions = goal_boundary.get("post_writeback_actions")
1359
+ if isinstance(actions, list) and actions:
1360
+ contract["cli_channel"]["post_writeback_actions"] = actions
1361
+
1362
+
1363
+ def _attach_interaction_vision_continuation_audit(
1364
+ contract: dict[str, Any],
1365
+ payload: dict[str, Any],
1366
+ ) -> None:
1367
+ vision_continuation_audit = (
1368
+ payload.get("vision_continuation_audit")
1369
+ if isinstance(payload.get("vision_continuation_audit"), dict)
1370
+ else {}
1371
+ )
1372
+ if not vision_continuation_audit.get("required"):
1373
+ return
1374
+ contract["agent_channel"]["vision_continuation_audit"] = vision_continuation_audit
1375
+ vision_gap_judge = (
1376
+ vision_continuation_audit.get("vision_gap_judge")
1377
+ if isinstance(vision_continuation_audit.get("vision_gap_judge"), dict)
1378
+ else {}
1379
+ )
1380
+ contract["cli_channel"]["vision_continuation_audit"] = {
1381
+ "required": True,
1382
+ "required_before_closeout": vision_continuation_audit.get(
1383
+ "required_before_closeout"
1384
+ )
1385
+ or [],
1386
+ "recommended_action": vision_continuation_audit.get("recommended_action"),
1387
+ }
1388
+ if vision_gap_judge:
1389
+ contract["cli_channel"]["vision_continuation_audit"]["vision_gap_judge"] = {
1390
+ "done": vision_gap_judge.get("done"),
1391
+ "decision": vision_gap_judge.get("decision"),
1392
+ "reason": vision_gap_judge.get("reason"),
1393
+ "agent_judge_instruction": vision_gap_judge.get("agent_judge_instruction"),
1394
+ "evidence_read_instruction": vision_gap_judge.get(
1395
+ "evidence_read_instruction"
1396
+ ),
1397
+ "registry_read_instruction": vision_gap_judge.get(
1398
+ "registry_read_instruction"
1399
+ ),
1400
+ "done_only_when": vision_gap_judge.get("done_only_when") or [],
1401
+ "continue_when": vision_gap_judge.get("continue_when") or [],
1402
+ "otherwise": vision_gap_judge.get("otherwise"),
1403
+ }
1404
+
1405
+
1406
+ def _attach_interaction_vision_wait_state(
1407
+ contract: dict[str, Any],
1408
+ payload: dict[str, Any],
1409
+ ) -> None:
1410
+ vision_wait_state = (
1411
+ payload.get("vision_wait_state")
1412
+ if isinstance(payload.get("vision_wait_state"), dict)
1413
+ else {}
1414
+ )
1415
+ if vision_wait_state.get("state") != "waiting":
1416
+ return
1417
+ contract["agent_channel"]["vision_wait_state"] = vision_wait_state
1418
+ contract["cli_channel"]["vision_wait_state"] = {
1419
+ "state": "waiting",
1420
+ "reason_code": vision_wait_state.get("reason_code"),
1421
+ "selected_todo_id": vision_wait_state.get("selected_todo_id"),
1422
+ "resume_when": vision_wait_state.get("resume_when"),
1423
+ "automatic_resume": vision_wait_state.get("automatic_resume") is True,
1424
+ "spend_policy": "no spend while the exact resume condition is pending",
1425
+ }
1426
+
1427
+
1428
+ def _interaction_fallback_policy_required(payload: dict[str, Any], *, mode: str) -> bool:
1429
+ return mode in {
1430
+ "user_gate",
1431
+ "user_todo_blocker_push",
1432
+ "user_action_required",
1433
+ "outcome_floor_recovery",
1434
+ "external_evidence_observation",
1435
+ "scoped_user_gate_fallback",
1436
+ } or bool(payload.get("blocked_priority_fallback"))
1437
+
1438
+
1439
+ def build_interaction_contract(
1440
+ payload: dict[str, Any],
1441
+ *,
1442
+ available_capabilities: Any = None,
1443
+ scheduler_execution_context: (
1444
+ Mapping[str, Any] | SchedulerExecutionContextResolution | None
1445
+ ) = None,
1446
+ ) -> InteractionContractPacket:
1447
+ execution_obligation = (
1448
+ payload.get("execution_obligation")
1449
+ if isinstance(payload.get("execution_obligation"), dict)
1450
+ else {}
1451
+ )
1452
+ heartbeat_recommendation = (
1453
+ payload.get("heartbeat_recommendation")
1454
+ if isinstance(payload.get("heartbeat_recommendation"), dict)
1455
+ else {}
1456
+ )
1457
+ mode = _interaction_mode(payload)
1458
+ monitor_only = payload.get("agent_work_mode") == "monitor_only"
1459
+ user_required = False if monitor_only else user_channel_action_required(payload)
1460
+ scoped_user_gate_fallback = mode == "scoped_user_gate_fallback"
1461
+ bounded_delivery_with_user_notice = mode == "bounded_delivery_with_user_notice"
1462
+ must_attempt = _interaction_must_attempt(
1463
+ execution_obligation,
1464
+ user_required=user_required,
1465
+ scoped_user_gate_fallback=scoped_user_gate_fallback,
1466
+ bounded_delivery_with_user_notice=bounded_delivery_with_user_notice,
1467
+ )
1468
+ if mode == "automation_prompt_upgrade":
1469
+ must_attempt = True
1470
+ if _blocked_successor_wait_observation_required(payload):
1471
+ must_attempt = True
1472
+ delivery_allowed = _interaction_delivery_allowed(
1473
+ payload,
1474
+ execution_obligation,
1475
+ mode=mode,
1476
+ user_required=user_required,
1477
+ scoped_user_gate_fallback=scoped_user_gate_fallback,
1478
+ bounded_delivery_with_user_notice=bounded_delivery_with_user_notice,
1479
+ )
1480
+ quiet_noop_allowed = _interaction_quiet_noop_allowed(
1481
+ mode=mode,
1482
+ user_required=user_required,
1483
+ must_attempt=must_attempt,
1484
+ )
1485
+ spend_after_validation = _interaction_spend_after_validation(mode)
1486
+ required_reads = _interaction_required_reads(payload)
1487
+ capability_reentry = build_runtime_capability_reentry_packet(
1488
+ payload,
1489
+ available_capabilities=available_capabilities,
1490
+ scheduler_execution_context=scheduler_execution_context,
1491
+ )
1492
+
1493
+ user_channel = _build_interaction_user_channel(
1494
+ payload,
1495
+ heartbeat_recommendation,
1496
+ user_required=user_required,
1497
+ )
1498
+ if monitor_only:
1499
+ user_channel = {
1500
+ "action_required": False,
1501
+ "notify": "DONT_NOTIFY",
1502
+ "reason": payload.get("reason"),
1503
+ }
1504
+ agent_channel = _build_interaction_agent_channel(
1505
+ payload,
1506
+ mode=mode,
1507
+ must_attempt=must_attempt,
1508
+ delivery_allowed=delivery_allowed,
1509
+ quiet_noop_allowed=quiet_noop_allowed,
1510
+ capability_reentry=capability_reentry,
1511
+ )
1512
+ contract: dict[str, Any] = {
1513
+ "schema_version": INTERACTION_CONTRACT_SCHEMA_VERSION,
1514
+ "mode": mode,
1515
+ "user_channel": user_channel,
1516
+ "agent_channel": agent_channel,
1517
+ "cli_channel": _build_interaction_cli_channel(
1518
+ payload,
1519
+ execution_obligation,
1520
+ heartbeat_recommendation,
1521
+ mode=mode,
1522
+ spend_after_validation=spend_after_validation,
1523
+ available_capabilities=available_capabilities,
1524
+ scheduler_execution_context=scheduler_execution_context,
1525
+ capability_reentry=capability_reentry,
1526
+ ),
1527
+ }
1528
+ response_plan = _build_interaction_response_plan(
1529
+ user_channel=user_channel,
1530
+ agent_channel=agent_channel,
1531
+ )
1532
+ if response_plan is not None:
1533
+ contract["response_plan"] = response_plan
1534
+ _attach_interaction_required_reads(contract, required_reads)
1535
+ _attach_interaction_post_writeback_actions(contract, payload)
1536
+ _attach_interaction_vision_continuation_audit(contract, payload)
1537
+ _attach_interaction_vision_wait_state(contract, payload)
1538
+ if _interaction_fallback_policy_required(payload, mode=mode):
1539
+ contract["fallback_policy"] = {"do_not_cancel_on_block": True}
1540
+ return typing.cast(InteractionContractPacket, contract)