cli-agent-runner 0.2.10__tar.gz → 0.2.12__tar.gz

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 (316) hide show
  1. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/.vulture-whitelist.py +6 -1
  2. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/CHANGELOG.md +37 -0
  3. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/PKG-INFO +1 -1
  4. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/_docgen.py +9 -4
  5. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/_emit.py +23 -0
  6. cli_agent_runner-0.2.12/agent_runner/_serve_policy.py +89 -0
  7. cli_agent_runner-0.2.12/agent_runner/_throttle.py +551 -0
  8. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/_version.py +2 -2
  9. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/agent_runtime.py +74 -51
  10. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/api.py +153 -87
  11. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/api_types.py +14 -6
  12. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/builtin_plugins/_constants.py +6 -3
  13. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/builtin_plugins/claude_rate_limit.py +66 -18
  14. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/builtin_plugins/codewhale.py +9 -9
  15. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/cli/__init__.py +14 -1
  16. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/cli/common.py +1 -1
  17. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/cli/events_cmd.py +26 -17
  18. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/cli/monitor_cmd.py +3 -1
  19. cli_agent_runner-0.2.12/agent_runner/cli/round_cmd.py +54 -0
  20. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/cli/serve_cmd.py +294 -73
  21. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/cli/service_cmd.py +6 -5
  22. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/cli/upgrade_cmd.py +81 -8
  23. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/config.py +90 -11
  24. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/context_store.py +4 -3
  25. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/defenses.py +3 -2
  26. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/events.py +4 -0
  27. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/hooks.py +18 -12
  28. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/http_progress.py +2 -2
  29. cli_agent_runner-0.2.12/agent_runner/lifecycle.py +108 -0
  30. cli_agent_runner-0.2.12/agent_runner/migrations.py +401 -0
  31. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/monitor.py +179 -51
  32. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/phase_select.py +8 -1
  33. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/round_log.py +54 -6
  34. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/round_view.py +3 -1
  35. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/runner.py +48 -68
  36. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/scaffold.py +5 -2
  37. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/service_unit.py +14 -1
  38. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/startup_check.py +50 -9
  39. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/vcs_state.py +88 -15
  40. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/architecture.md +4 -0
  41. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/commands.md +23 -1
  42. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/configuration.md +12 -8
  43. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/migrations/0.2.md +72 -0
  44. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/plugins.md +4 -3
  45. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/runbook.md +32 -19
  46. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/_clock.py +5 -0
  47. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/_test_helpers.py +7 -1
  48. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/contract/test_public_api_surface.py +132 -0
  49. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_fresh_eyes_signal.py +13 -12
  50. cli_agent_runner-0.2.12/tests/integration/test_spawn_round_wedged.py +92 -0
  51. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_substrate_fingerprint.py +4 -0
  52. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_transient_error_backoff.py +3 -0
  53. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_architecture.py +17 -3
  54. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_atomic_write_enforced.py +3 -0
  55. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_doc_test_pointers_exist.py +14 -1
  56. cli_agent_runner-0.2.12/tests/invariants/test_event_int_coercion.py +56 -0
  57. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_event_kinds_ssot.py +1 -1
  58. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_module_boundaries.py +48 -37
  59. cli_agent_runner-0.2.12/tests/invariants/test_monitor_self_exclusion.py +22 -0
  60. cli_agent_runner-0.2.12/tests/invariants/test_no_vacuous_invariants.py +43 -0
  61. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_peek_schema_version.py +7 -2
  62. cli_agent_runner-0.2.12/tests/invariants/test_round_log_decode_boundary.py +45 -0
  63. cli_agent_runner-0.2.12/tests/invariants/test_round_num_single_source.py +43 -0
  64. cli_agent_runner-0.2.12/tests/invariants/test_stash_uses_sha_not_index.py +78 -0
  65. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_agent_runtime_grace.py +42 -5
  66. cli_agent_runner-0.2.12/tests/unit/test_agent_runtime_marker_scan.py +84 -0
  67. cli_agent_runner-0.2.12/tests/unit/test_agent_runtime_reap.py +64 -0
  68. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_api_observation.py +96 -1
  69. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_api_service.py +100 -0
  70. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_api_types.py +25 -0
  71. cli_agent_runner-0.2.12/tests/unit/test_claude_foreign_schema.py +133 -0
  72. cli_agent_runner-0.2.12/tests/unit/test_cli_main_boundary.py +27 -0
  73. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_cli_service_peek_monitor.py +29 -0
  74. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_cli_upgrade.py +220 -60
  75. cli_agent_runner-0.2.12/tests/unit/test_codewhale_code_precedence.py +17 -0
  76. cli_agent_runner-0.2.12/tests/unit/test_coerce_int_poison.py +94 -0
  77. cli_agent_runner-0.2.12/tests/unit/test_config_strictness.py +96 -0
  78. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_context_store.py +11 -0
  79. cli_agent_runner-0.2.12/tests/unit/test_detect_hung_latch.py +31 -0
  80. cli_agent_runner-0.2.12/tests/unit/test_detector_isolation.py +25 -0
  81. cli_agent_runner-0.2.12/tests/unit/test_effective_throttle_view.py +68 -0
  82. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_events.py +1 -1
  83. cli_agent_runner-0.2.12/tests/unit/test_events_cmd_rollover.py +42 -0
  84. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_exit_cause.py +10 -2
  85. cli_agent_runner-0.2.12/tests/unit/test_git_timeout.py +139 -0
  86. cli_agent_runner-0.2.12/tests/unit/test_hook_redact_order.py +33 -0
  87. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_http_progress.py +48 -0
  88. cli_agent_runner-0.2.12/tests/unit/test_interruptible_sleep_should_stop.py +47 -0
  89. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_lifecycle.py +53 -0
  90. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_migrate_cmd.py +13 -0
  91. cli_agent_runner-0.2.12/tests/unit/test_migrations.py +203 -0
  92. cli_agent_runner-0.2.12/tests/unit/test_monitor_cmd_severity.py +19 -0
  93. cli_agent_runner-0.2.12/tests/unit/test_monitor_dedup_identity.py +125 -0
  94. cli_agent_runner-0.2.12/tests/unit/test_monitor_dedup_rearm.py +42 -0
  95. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_monitor_detectors.py +19 -0
  96. cli_agent_runner-0.2.12/tests/unit/test_monitor_event_tail.py +20 -0
  97. cli_agent_runner-0.2.12/tests/unit/test_open_round_log.py +24 -0
  98. cli_agent_runner-0.2.12/tests/unit/test_orphan_chain_single_pass.py +26 -0
  99. cli_agent_runner-0.2.12/tests/unit/test_outer_round_ceiling.py +39 -0
  100. cli_agent_runner-0.2.12/tests/unit/test_peek_project_isolation.py +38 -0
  101. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_phase_select.py +19 -0
  102. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_plugin_constants.py +20 -0
  103. cli_agent_runner-0.2.12/tests/unit/test_primary_prompt_file_home.py +23 -0
  104. cli_agent_runner-0.2.12/tests/unit/test_round_cmd.py +57 -0
  105. cli_agent_runner-0.2.12/tests/unit/test_round_cmd_sigterm.py +33 -0
  106. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_round_log_helpers.py +88 -0
  107. cli_agent_runner-0.2.12/tests/unit/test_runner_sidecar_ordering.py +34 -0
  108. cli_agent_runner-0.2.12/tests/unit/test_runner_throttle.py +654 -0
  109. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_scaffold.py +29 -0
  110. cli_agent_runner-0.2.12/tests/unit/test_serve_config_broken.py +47 -0
  111. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_serve_crash_loop.py +41 -19
  112. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_serve_phase_schedule.py +132 -34
  113. cli_agent_runner-0.2.12/tests/unit/test_serve_policy.py +29 -0
  114. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_serve_round_log.py +15 -24
  115. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_serve_schedule_gate.py +7 -4
  116. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_serve_sentinel.py +30 -30
  117. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_serve_startup_hooks.py +4 -8
  118. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_service_unit.py +21 -1
  119. cli_agent_runner-0.2.12/tests/unit/test_startup_battery_exit.py +52 -0
  120. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_startup_check.py +38 -0
  121. cli_agent_runner-0.2.12/tests/unit/test_supervisor_stale_self_blind.py +31 -0
  122. cli_agent_runner-0.2.12/tests/unit/test_throttle_back_off_ladder.py +95 -0
  123. cli_agent_runner-0.2.10/agent_runner/_throttle.py +0 -220
  124. cli_agent_runner-0.2.10/agent_runner/cli/round_cmd.py +0 -25
  125. cli_agent_runner-0.2.10/agent_runner/lifecycle.py +0 -67
  126. cli_agent_runner-0.2.10/agent_runner/migrations.py +0 -115
  127. cli_agent_runner-0.2.10/tests/invariants/test_stash_uses_sha_not_index.py +0 -21
  128. cli_agent_runner-0.2.10/tests/unit/test_migrations.py +0 -83
  129. cli_agent_runner-0.2.10/tests/unit/test_runner_throttle.py +0 -427
  130. cli_agent_runner-0.2.10/tests/unit/test_serve_config_broken.py +0 -33
  131. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/.codecov.yml +0 -0
  132. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  133. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  134. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  135. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  136. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/.github/workflows/ci.yml +0 -0
  137. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/.github/workflows/release.yml +0 -0
  138. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/.gitignore +0 -0
  139. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/BACKLOG.md +0 -0
  140. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/CODE_OF_CONDUCT.md +0 -0
  141. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/CONTRIBUTING.md +0 -0
  142. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/LICENSE +0 -0
  143. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/README.md +0 -0
  144. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/README.zh.md +0 -0
  145. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/SECURITY.md +0 -0
  146. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/__init__.py +0 -0
  147. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/_redact.py +0 -0
  148. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/_registry.py +0 -0
  149. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/_substrate.py +0 -0
  150. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/builtin_plugins/__init__.py +0 -0
  151. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/builtin_plugins/default_dirty_handler.py +0 -0
  152. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/builtin_plugins/gemini.py +0 -0
  153. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/builtin_plugins/kimi.py +0 -0
  154. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/builtin_plugins/pi.py +0 -0
  155. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/cli/__main__.py +0 -0
  156. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/cli/init_cmd.py +0 -0
  157. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/cli/install_cmd.py +0 -0
  158. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/cli/migrate_cmd.py +0 -0
  159. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/cli/peek_cmd.py +0 -0
  160. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/clock.py +0 -0
  161. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/metrics.py +0 -0
  162. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/presets/__init__.py +0 -0
  163. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/presets/aider.toml +0 -0
  164. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/presets/claude.toml +0 -0
  165. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/presets/codewhale.toml +0 -0
  166. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/presets/gemini.toml +0 -0
  167. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/presets/kimi.toml +0 -0
  168. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/presets/pi.toml +0 -0
  169. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/prompt_loader.py +0 -0
  170. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/remote_relay.py +0 -0
  171. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/agent_runner/schedule.py +0 -0
  172. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/build.sh +0 -0
  173. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/README.md +0 -0
  174. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/events.md +0 -0
  175. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/long-running-agents.md +0 -0
  176. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/quickstart.md +0 -0
  177. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/recipes/aider.md +0 -0
  178. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/recipes/codewhale.md +0 -0
  179. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/recipes/kimi.md +0 -0
  180. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/recipes/pi.md +0 -0
  181. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/docs/thesis.md +0 -0
  182. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/pyproject.toml +0 -0
  183. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/__init__.py +0 -0
  184. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/conftest.py +0 -0
  185. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/contract/__init__.py +0 -0
  186. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/e2e/__init__.py +0 -0
  187. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/e2e/conftest.py +0 -0
  188. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/e2e/test_e2e_graceful_stop.py +0 -0
  189. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/e2e/test_e2e_install_systemd.py +0 -0
  190. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/e2e/test_e2e_monitor_remote.py +0 -0
  191. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
  192. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/fixtures/cli-real-output/claude-2.1.143-assistant-tool-use.jsonl +0 -0
  193. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/fixtures/cli-real-output/claude-2.1.143-result-event.jsonl +0 -0
  194. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/fixtures/cli-real-output/gemini-0.42.0-result-event.jsonl +0 -0
  195. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/generate_vulture_whitelist.py +0 -0
  196. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/__init__.py +0 -0
  197. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_bounded_run.py +0 -0
  198. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_context_enricher_namespacing.py +0 -0
  199. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_dirty_handler_seam.py +0 -0
  200. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_grace_kill_emission.py +0 -0
  201. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_install_dry_run.py +0 -0
  202. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_monitor_seeded.py +0 -0
  203. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_plugin_detector_loaded.py +0 -0
  204. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_plugin_owned_paths.py +0 -0
  205. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_plugin_real_flow.py +0 -0
  206. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_prompt_delivery_stdin.py +0 -0
  207. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_run_one_round_with_fake_agent.py +0 -0
  208. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_scaffold_presets.py +0 -0
  209. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/integration/test_serve_loop.py +0 -0
  210. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/__init__.py +0 -0
  211. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/_docs.py +0 -0
  212. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/_event_scan.py +0 -0
  213. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_authored_facts_declared.py +0 -0
  214. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_auto_stop_policy_ssot.py +0 -0
  215. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_catalogs.py +0 -0
  216. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_classification_ssot.py +0 -0
  217. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_config_error_consistency.py +0 -0
  218. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_config_value_set_ssot.py +0 -0
  219. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_doc_builtin_plugins.py +0 -0
  220. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_doc_claims_match_ssot.py +0 -0
  221. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_doc_cli_claims.py +0 -0
  222. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_doc_operator_surface.py +0 -0
  223. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_doc_transient_error_claims.py +0 -0
  224. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_doc_usage_event_fields.py +0 -0
  225. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_docs_generated.py +0 -0
  226. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_docs_index_complete.py +0 -0
  227. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_entry_points_resolve.py +0 -0
  228. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_event_kind_registry.py +0 -0
  229. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_events_doc_contract.py +0 -0
  230. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_gen_blocks_name_their_source.py +0 -0
  231. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_hook_contract_docs.py +0 -0
  232. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_layer_2_loop_size.py +0 -0
  233. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_module_sizes.py +0 -0
  234. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_no_ai_signatures.py +0 -0
  235. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
  236. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_no_raw_time.py +0 -0
  237. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_phase_select_stateless.py +0 -0
  238. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_plugins_config_stable.py +0 -0
  239. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_readme_zh_is_pointer.py +0 -0
  240. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
  241. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_round_result_stable.py +0 -0
  242. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_set_diff_for_auto_tool_classification.py +0 -0
  243. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_upstream_schema_canary.py +0 -0
  244. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/invariants/test_vulture_whitelist_generated.py +0 -0
  245. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/literate/__init__.py +0 -0
  246. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/literate/parser.py +0 -0
  247. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/literate/test_parser.py +0 -0
  248. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/literate/test_quickstart.py +0 -0
  249. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/__init__.py +0 -0
  250. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_agent_runtime.py +0 -0
  251. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_agent_runtime_progress.py +0 -0
  252. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_agent_runtime_signal_name.py +0 -0
  253. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_api_assemble_prompt.py +0 -0
  254. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_api_events_stream.py +0 -0
  255. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_api_install.py +0 -0
  256. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_api_read_round_num.py +0 -0
  257. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_api_resolve_phase.py +0 -0
  258. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_auto_stop_gating.py +0 -0
  259. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_catalogs.py +0 -0
  260. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_claude_error_detector.py +0 -0
  261. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_cli.py +0 -0
  262. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_cli_common.py +0 -0
  263. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_cli_init_install.py +0 -0
  264. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_cli_monitor_http.py +0 -0
  265. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_codewhale_plugin.py +0 -0
  266. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_config.py +0 -0
  267. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_config_fresh_eyes.py +0 -0
  268. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_config_max_rounds.py +0 -0
  269. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_config_prompt_delivery.py +0 -0
  270. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_config_schedule.py +0 -0
  271. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_config_stop_file.py +0 -0
  272. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
  273. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_config_transient_error_action.py +0 -0
  274. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_default_dirty_handler.py +0 -0
  275. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_defenses.py +0 -0
  276. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_detector_protocol.py +0 -0
  277. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_dirty_handlers.py +0 -0
  278. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_docgen.py +0 -0
  279. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_emit_config_migrated.py +0 -0
  280. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_emit_schedule.py +0 -0
  281. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_emit_schedule_phase_skipped.py +0 -0
  282. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_events_cmd.py +0 -0
  283. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_events_cmd_tail.py +0 -0
  284. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_example_dirty_handler.py +0 -0
  285. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_example_plugin.py +0 -0
  286. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_fresh_eyes_trigger.py +0 -0
  287. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_gemini_plugin.py +0 -0
  288. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_hook_failure_isolation.py +0 -0
  289. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_hooks.py +0 -0
  290. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_init_entry_points.py +0 -0
  291. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_kimi_plugin.py +0 -0
  292. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_metrics.py +0 -0
  293. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_monitor_assembly.py +0 -0
  294. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
  295. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
  296. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
  297. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_monitor_remote.py +0 -0
  298. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_monitor_schedule_pause.py +0 -0
  299. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_network_blip_signal_guard.py +0 -0
  300. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_peek_argparse.py +0 -0
  301. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_peek_schedule.py +0 -0
  302. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_peek_select.py +0 -0
  303. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_phase_agent_launch.py +0 -0
  304. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_phase_profiles.py +0 -0
  305. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_pi_plugin.py +0 -0
  306. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_presets.py +0 -0
  307. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_prompt_loader.py +0 -0
  308. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_redact.py +0 -0
  309. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_round_view.py +0 -0
  310. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_runner.py +0 -0
  311. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_schedule.py +0 -0
  312. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_serve_cmd_bounded.py +0 -0
  313. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_substrate.py +0 -0
  314. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_throttle_plugin_classification.py +0 -0
  315. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_upgrade_migrate.py +0 -0
  316. {cli_agent_runner-0.2.10 → cli_agent_runner-0.2.12}/tests/unit/test_vcs_state.py +0 -0
@@ -12,7 +12,6 @@ duration_s
12
12
  timed_out
13
13
  pid
14
14
  killed_for_grace
15
- grace_kill_children
16
15
 
17
16
  # agent_runner.api_types.Alert
18
17
  severity
@@ -58,6 +57,7 @@ limit_type
58
57
  agent
59
58
  since_round
60
59
  phase
60
+ throttled_agents
61
61
 
62
62
  # agent_runner.api_types.RoundResult
63
63
  round_num
@@ -256,6 +256,10 @@ manual
256
256
  # agent_runner.monitor.LocalSource
257
257
  log_dir
258
258
 
259
+ # agent_runner.monitor._EventTail
260
+ offsets
261
+ buffer
262
+
259
263
  # agent_runner.phase_select.Selection
260
264
  phase
261
265
  paused
@@ -286,6 +290,7 @@ name
286
290
  ok
287
291
  reason
288
292
  how_to_fix
293
+ permanent
289
294
 
290
295
  # agent_runner.vcs_state.StashRef
291
296
  sha
@@ -5,6 +5,43 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.12] - 2026-08-31
9
+
10
+ ### Breaking
11
+ - Config validation now hard-rejects footguns that used to load silently: a scalar where a list is required (`phases.list`, `[agent] command`, `prompt_arg_template`, `[prompt] files`), an empty `[agent] command` / empty top-level `[prompt] files`, unknown keys in `[schedule]`/`[prompt]`, and `threshold > window`. **Run `agent-runner migrate` BEFORE `upgrade`** — the orchestrated upgrade runs the old binary's migrate, which cannot self-cross this break. Every new error names `agent-runner migrate`.
12
+ - systemd `serve` unit adds `StartLimitIntervalSec`/`StartLimitBurst`; re-render or hand-edit existing units.
13
+
14
+ ### Fixed
15
+ - The monitor no longer dies on a non-UTF-8 byte or an odd plugin severity; each builtin detector is isolated (`detector_error` event) and a hung round is TERM→grace→killpg'd (`round_supervisor_wedged`) instead of orphaning the agent or latching `hung`.
16
+ - Environmental startup failures (ENOSPC, mount hiccup, unclassified) exit 76 and keep serve retrying at a fixed back-off until they heal, instead of tripping the crash-loop breaker; deterministic config failures give up by name (exit 78).
17
+ - A git-commit timeout leaves no surviving `.git/index.lock` (self-caused only; `stale_index_lock_cleared`); a stop-file lands within one back-off chunk.
18
+ - `status`/`kill`/`restart` report and act on real service state: liveness falls back to `serve.pid` when `systemctl` is absent, and `restart` refuses on non-service modes before stopping.
19
+ - `upgrade` runs the new binary's `migrate` before the smoke check, restores the config on rollback, and emits `upgrade_start_failed` with a remedy when the new version installs but won't start.
20
+ - `json_tail` no longer lets a blank-line flood evict the terminal round-log record; `init` commits only its scaffold files.
21
+
22
+ ### Notes
23
+ - New event kinds: `detector_error`, `round_supervisor_wedged`, `stale_index_lock_cleared`, `upgrade_start_failed`. `RestartPreventExitStatus` stays `78 75` — environmental failures (round exit 76) are retried inside serve rather than stopping it.
24
+
25
+ See `docs/migrations/0.2.md`.
26
+
27
+ ## [0.2.11] - 2026-08-29
28
+
29
+ ### Changed
30
+ - systemd `serve` unit now uses `Restart=on-failure` with `RestartPreventExitStatus=78 75`: a deliberate stop (sentinel / stop-file / max-rounds / SIGTERM) and a config give-up stay stopped, while a genuine crash still restarts. Re-render or hand-edit existing units.
31
+ - Throttle-aware skip is keyed on the **agent** (binary basename of `command[0]`), not the phase: every phase sharing a rate-limited agent is stepped over together, so a multi-provider rotation no longer hammers one throttled key while a healthy provider keeps running.
32
+
33
+ ### Fixed
34
+ - `round` exits non-zero on a genuine crash, so the crash-loop breaker (`crash_loop`, exit 75) sees crashes instead of a masked exit 0; a permanent config failure gives up with exit 78 (`config_broken`) instead of restart-looping.
35
+ - Round deadlines and sleeps are measured on the monotonic clock — an NTP step no longer mis-fires or defers a hard-wall timeout.
36
+ - Back-off and restart sleeps are chunked, so a SIGTERM lands within one chunk instead of after a multi-hour throttle window; an interrupted back-off leaves the throttle active (no false recovered).
37
+ - A grace-killed round still dispatches its dirty work and is no longer miscounted as a timeout.
38
+ - Only one `serve` loop runs per project (loop-lifetime lock); `serve.pid` records a start-time token so `stop`/`kill` never signals a recycled PID.
39
+
40
+ ### Notes
41
+ - `peek --json` schema `1.10`: `rate_limit.throttled_agents` lists all currently-throttled agents. Additive; no config change.
42
+
43
+ See `docs/migrations/0.2.md`.
44
+
8
45
  ## [0.2.10] - 2026-08-29
9
46
 
10
47
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cli-agent-runner
3
- Version: 0.2.10
3
+ Version: 0.2.12
4
4
  Summary: Restart-on-exit supervisor for autonomous CLI agents
5
5
  Project-URL: Homepage, https://github.com/wan9yu/cli-agent-runner
6
6
  Project-URL: Documentation, https://github.com/wan9yu/cli-agent-runner#readme
@@ -157,10 +157,15 @@ def render_event_kinds_list() -> str:
157
157
 
158
158
 
159
159
  def _render_migrate_transforms() -> str:
160
- """Bullet list of the transforms `agent-runner migrate` applies, from the registry."""
161
- from agent_runner.migrations import MIGRATIONS
162
-
163
- return "\n".join(f"- {m.describe}" for m in MIGRATIONS)
160
+ """Bullet list of the transforms `agent-runner migrate` applies, from the registry.
161
+ A few entries carry a `parsed -> str` describe (so the manual report can name the
162
+ exact offending key found in a real config); rendered here with `{}`, which degrades
163
+ to the same generic instruction since there's no config in hand."""
164
+ from agent_runner.migrations import MIGRATIONS, _describe
165
+
166
+ # No config in hand here, so pass an empty parsed dict: a `parsed -> str`
167
+ # describe degrades to its generic instruction, same rule as the manual report.
168
+ return "\n".join(f"- {_describe(m, {})}" for m in MIGRATIONS)
164
169
 
165
170
 
166
171
  def _subparser_action() -> argparse.Action:
@@ -27,9 +27,11 @@ __all__ = [
27
27
  "emit_round_progress",
28
28
  "emit_round_substrate_after",
29
29
  "emit_round_substrate_before",
30
+ "emit_round_supervisor_wedged",
30
31
  "emit_schedule_paused",
31
32
  "emit_schedule_phase_skipped",
32
33
  "emit_schedule_resumed",
34
+ "emit_stale_index_lock_cleared",
33
35
  "emit_stop_file_detected",
34
36
  "emit_transient_error_backoff_capped",
35
37
  "emit_transient_error_detected",
@@ -98,6 +100,15 @@ def emit_stop_file_detected(
98
100
  )
99
101
 
100
102
 
103
+ def emit_stale_index_lock_cleared(log_dir: Path, *, lock_path: str, round_num: int) -> None:
104
+ """Emit when serve removed a .git/index.lock that its own timed-out+killed git
105
+ call orphaned. Single-writer: serve holds the round lock, so any lock surviving
106
+ our kill is ours to clear."""
107
+ from agent_runner.events import STALE_INDEX_LOCK_CLEARED, emit
108
+
109
+ emit(log_dir, STALE_INDEX_LOCK_CLEARED, lock_path=lock_path, round_num=round_num)
110
+
111
+
101
112
  def emit_schedule_paused(
102
113
  log_dir: Path, *, active_window: str, resume_at: str, timezone: str, phase: str = ""
103
114
  ) -> None:
@@ -168,6 +179,18 @@ def emit_round_substrate_after(
168
179
  )
169
180
 
170
181
 
182
+ def emit_round_supervisor_wedged(
183
+ log_dir: Path, *, pid: int, timeout_s: int, log_path: Path
184
+ ) -> None:
185
+ """Emit when the round subprocess blew past the outer ceiling and serve had to
186
+ TERM/kill it (the round supervisor was wedged — not doing its own bounded
187
+ post-round cleanup). Distinct from round_timeout_kill (the AGENT hit the inner
188
+ wall inside a healthy round)."""
189
+ from agent_runner.events import ROUND_SUPERVISOR_WEDGED, emit
190
+
191
+ emit(log_dir, ROUND_SUPERVISOR_WEDGED, pid=pid, timeout_s=timeout_s, log_path=str(log_path))
192
+
193
+
171
194
  def emit_fresh_eyes_round_triggered(log_dir: Path, *, round_num: int, every_n: int) -> None:
172
195
  """Emit fresh-eyes signal trigger event (only on triggered rounds)."""
173
196
  from agent_runner.events import FRESH_EYES_ROUND_TRIGGERED, emit
@@ -0,0 +1,89 @@
1
+ """Pure serve restart policy — no I/O, no clock, no events read.
2
+
3
+ Extracted from api.py (0.2.12 Group G): serve_cmd imports the decision + exit
4
+ codes from here directly (see the architecture allowlist); api.py re-exports
5
+ them for external back-compat. Kept dependency-free so the serve loop stays a
6
+ thin dispatcher and the policy is unit-testable in isolation.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Literal
12
+
13
+ # Exit code for a permanent (no-retry) startup-battery failure. A broken config
14
+ # does not self-heal between rounds, so serve STOPS rather than respawning it
15
+ # forever. 78 = EX_CONFIG (sysexits) — avoids argparse's 2 and the generic 1.
16
+ PERMANENT_CONFIG_EXIT = 78
17
+
18
+ # Exit code serve returns when the crash-loop breaker trips (a give-up stop, like
19
+ # config_broken). Distinct from 78 so `systemctl status` / SuccessExitStatus can
20
+ # tell the two "needs intervention" stops apart. 75 = EX_TEMPFAIL (sysexits).
21
+ # The systemd unit lists both in RestartPreventExitStatus so a deliberate stop
22
+ # stays stopped (and visibly failed) while an unexpected supervisor crash restarts.
23
+ CRASH_LOOP_EXIT = 75
24
+
25
+ # Exit code for an ENVIRONMENTAL startup-battery failure (ENOSPC, mount hiccup,
26
+ # an unclassified check): recoverable, unlike a permanent config break. NOT in
27
+ # the unit's RestartPreventExitStatus (stays "78 75") so 76 restarts; treated
28
+ # exactly like an active throttle (post_round_decision), so serve keeps
29
+ # retrying at a fixed doubled delay with the crash-loop breaker disarmed,
30
+ # staying alive until the environment heals. StartLimit bounds only repeated
31
+ # serve-PROCESS death, not this round-level path. 76 = EX_PROTOCOL (sysexits).
32
+ ENV_BATTERY_EXIT = 76
33
+
34
+ # Crash-loop circuit breaker (b12). The serve loop escalates the restart delay
35
+ # on consecutive UNKNOWN short crashes (non-zero exit, short duration, no
36
+ # classified transient) and STOPS after CRASH_LOOP_THRESHOLD of them — the Run 6
37
+ # ~100-empty-rounds scar. Recoverable-slow failures (rate limit / 5h quota / 5xx
38
+ # / timeout) are already handled by the transient-error throttle and never reach
39
+ # this path. A clean (exit 0), long, or classified-transient round resets the run.
40
+ CRASH_LOOP_THRESHOLD = 5
41
+ CRASH_LOOP_SHORT_EXIT_S = 60 # mirrors monitor.SHORT_EXIT_THRESHOLD_S
42
+ CRASH_LOOP_MAX_DELAY_S = 1800 # cap the escalating restart delay (30 min)
43
+
44
+
45
+ # The action strings below are the restart-action enum, not events.py kinds:
46
+ # "continue" has no constant, and a constant cannot sit inside Literal[...].
47
+ def post_round_decision(
48
+ *,
49
+ returncode: int,
50
+ duration_s: float,
51
+ throttle_active: bool,
52
+ consecutive: int,
53
+ restart_delay_s: int,
54
+ ) -> tuple[Literal["config_broken", "crash_loop", "continue"], int, int]:
55
+ """Restart policy after one round — keeps the serve loop a thin dispatcher.
56
+
57
+ Returns ``(action, delay_s, consecutive)`` where action is:
58
+ - ``"config_broken"`` — permanent startup failure (b18): stop.
59
+ - ``"crash_loop"`` — CRASH_LOOP_THRESHOLD consecutive unknown short crashes
60
+ (b12): stop. An unknown short crash is a non-zero, fast exit with no
61
+ classified transient (rate-limit/5xx/timeout are handled by the throttle).
62
+ - ``"continue"`` — sleep ``delay_s`` then run the next round.
63
+
64
+ A clean (exit 0), long, or transient round resets ``consecutive`` to 0; an
65
+ unknown short crash escalates the delay (restart × 2ⁿ, capped) until the stop.
66
+ An ``ENV_BATTERY_EXIT`` (76, environmental startup-battery failure) is treated
67
+ exactly like an active throttle: it takes the flat ``restart_delay_s * 2``
68
+ delay below (never escalating) and resets ``consecutive`` to 0, so it never
69
+ counts toward the crash-loop breaker — a short environmental outage (ENOSPC,
70
+ mount hiccup) never trips ``crash_loop``. This always returns ``"continue"``,
71
+ so serve itself never exits 76 and systemd's StartLimit window never engages
72
+ for this path; a *persistent* environmental failure keeps serve alive
73
+ retrying at the doubled delay until the environment heals. StartLimit bounds
74
+ only repeated serve-PROCESS death, independent of this round-level path.
75
+ """
76
+ if returncode == PERMANENT_CONFIG_EXIT:
77
+ return ("config_broken", 0, consecutive)
78
+ throttle_active = throttle_active or returncode == ENV_BATTERY_EXIT
79
+ unknown_short_crash = (
80
+ returncode != 0 and duration_s < CRASH_LOOP_SHORT_EXIT_S and not throttle_active
81
+ )
82
+ if unknown_short_crash:
83
+ consecutive += 1
84
+ if consecutive >= CRASH_LOOP_THRESHOLD:
85
+ return ("crash_loop", 0, consecutive)
86
+ delay = min(restart_delay_s * 2**consecutive, CRASH_LOOP_MAX_DELAY_S)
87
+ return ("continue", delay, consecutive)
88
+ delay = restart_delay_s if returncode == 0 else restart_delay_s * 2
89
+ return ("continue", delay, 0)