cli-agent-runner 0.2.24__tar.gz → 0.3.1__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 (384) hide show
  1. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/.github/workflows/ci.yml +3 -3
  2. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/.github/workflows/release.yml +3 -3
  3. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/.vulture-whitelist.py +32 -11
  4. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/CHANGELOG.md +22 -0
  5. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/PKG-INFO +2 -2
  6. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/README.md +1 -1
  7. cli_agent_runner-0.3.1/agent_runner/__init__.py +102 -0
  8. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_docgen.py +13 -1
  9. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_emit/__init__.py +2 -2
  10. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_emit/memory.py +33 -3
  11. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_emit/rounds.py +1 -1
  12. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_emit/serve.py +0 -20
  13. cli_agent_runner-0.3.1/agent_runner/_install.py +187 -0
  14. cli_agent_runner-0.3.1/agent_runner/_lifecycle.py +338 -0
  15. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_monitor_detectors.py +13 -12
  16. cli_agent_runner-0.3.1/agent_runner/_observe.py +405 -0
  17. cli_agent_runner-0.3.1/agent_runner/_plugin_manifest.py +113 -0
  18. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_plugin_scan.py +9 -8
  19. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_serve_policy.py +3 -3
  20. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_version.py +2 -2
  21. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/agent_runtime.py +20 -0
  22. cli_agent_runner-0.3.1/agent_runner/api.py +181 -0
  23. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/api_types.py +5 -3
  24. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/builtin_plugins/claude_rate_limit.py +3 -2
  25. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/builtin_plugins/codewhale.py +3 -2
  26. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/builtin_plugins/default_dirty_handler.py +3 -2
  27. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/builtin_plugins/gemini.py +3 -2
  28. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/builtin_plugins/kimi.py +3 -2
  29. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/builtin_plugins/pi.py +3 -2
  30. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/__init__.py +9 -4
  31. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/_serve_cgroup.py +4 -3
  32. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/_serve_round.py +78 -13
  33. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/common.py +1 -1
  34. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/serve_cmd.py +6 -9
  35. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/config/__init__.py +10 -1
  36. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/config/loader.py +28 -1
  37. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/config/models.py +59 -33
  38. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/config/parsers.py +70 -48
  39. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/config/validators.py +13 -0
  40. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/defenses.py +3 -3
  41. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/events.py +4 -3
  42. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/host_health.py +43 -21
  43. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/lifecycle.py +2 -2
  44. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/migrations.py +203 -19
  45. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/monitor.py +15 -17
  46. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/phase_select.py +30 -8
  47. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/presets/aider.toml +2 -1
  48. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/presets/claude.toml +2 -1
  49. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/presets/codewhale.toml +2 -1
  50. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/presets/gemini.toml +2 -1
  51. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/presets/kimi.toml +2 -1
  52. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/presets/pi.toml +2 -1
  53. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/runner.py +2 -2
  54. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/service_unit.py +4 -4
  55. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/startup_check.py +19 -1
  56. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/architecture.md +9 -7
  57. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/commands.md +23 -9
  58. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/configuration.md +60 -27
  59. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/events.md +3 -3
  60. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/long-running-agents.md +5 -6
  61. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/memory-efficiency.md +84 -4
  62. cli_agent_runner-0.3.1/docs/migrations/0.3.md +103 -0
  63. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/plugins.md +122 -96
  64. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/quickstart.md +1 -1
  65. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/recipes/aider.md +2 -1
  66. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/recipes/codewhale.md +4 -3
  67. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/recipes/container-pi.md +1 -1
  68. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/recipes/pi.md +2 -2
  69. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/runbook.md +22 -21
  70. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/pyproject.toml +11 -9
  71. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/_test_helpers.py +4 -1
  72. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/contract/test_public_api_surface.py +1 -1
  73. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/e2e/conftest.py +1 -1
  74. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_auto_stop_e2e.py +2 -2
  75. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_dirty_handler_seam.py +2 -2
  76. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_grace_kill_emission.py +5 -5
  77. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_install_dry_run.py +3 -3
  78. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_plugin_real_flow.py +1 -0
  79. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_run_one_round_with_fake_agent.py +10 -9
  80. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_serve_loop.py +7 -12
  81. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_spawn_round_mem_floor.py +33 -23
  82. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_architecture.py +15 -2
  83. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_config_error_consistency.py +4 -4
  84. cli_agent_runner-0.3.1/tests/invariants/test_doc_builtin_plugins.py +57 -0
  85. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_doc_claims_match_ssot.py +2 -2
  86. cli_agent_runner-0.3.1/tests/invariants/test_entry_points_resolve.py +71 -0
  87. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_import_footprint.py +12 -0
  88. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_module_boundaries.py +2 -0
  89. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_no_price_logic.py +2 -2
  90. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_peek_schema_version.py +1 -0
  91. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_timeout_budget_invariant.py +2 -2
  92. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_agent_runtime_grace.py +66 -3
  93. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_api_install.py +22 -17
  94. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_api_observation.py +38 -4
  95. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_api_resolve_phase.py +6 -6
  96. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_api_service.py +77 -64
  97. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_auto_stop_gating.py +6 -3
  98. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_cli.py +3 -2
  99. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_cli_common.py +2 -0
  100. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_cli_main_boundary.py +50 -1
  101. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_cli_service_peek_monitor.py +1 -1
  102. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_config.py +299 -78
  103. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_config_nested_strictness.py +1 -1
  104. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_config_schedule.py +1 -0
  105. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_config_strictness.py +3 -0
  106. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_control_char_config.py +1 -0
  107. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_defenses.py +3 -3
  108. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_detect_hung_latch.py +4 -4
  109. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_docgen.py +12 -8
  110. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_doctor_cmd.py +24 -0
  111. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_events.py +27 -4
  112. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_hooks.py +1 -1
  113. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_host_health.py +67 -5
  114. cli_agent_runner-0.3.1/tests/unit/test_init_entry_points.py +171 -0
  115. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_migrate_cmd.py +9 -8
  116. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_migrations.py +195 -24
  117. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_assembly.py +2 -2
  118. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_dedup_identity.py +3 -2
  119. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_dedup_rearm.py +5 -5
  120. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_detectors.py +34 -17
  121. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_loop.py +7 -7
  122. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_outer_round_ceiling.py +19 -2
  123. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_phase_agent_launch.py +1 -1
  124. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_phase_profiles.py +8 -8
  125. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_phase_select.py +27 -0
  126. cli_agent_runner-0.3.1/tests/unit/test_plugin_manifest.py +94 -0
  127. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_plugin_scan_parity.py +13 -12
  128. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_resolve.py +2 -0
  129. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_runner.py +6 -6
  130. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_mem_gate.py +3 -2
  131. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_round.py +8 -8
  132. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_round_cgroup_spine.py +168 -0
  133. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_round_log.py +1 -0
  134. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_service_unit.py +10 -10
  135. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_startup_check.py +5 -3
  136. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_upgrade_migrate.py +37 -1
  137. cli_agent_runner-0.2.24/agent_runner/__init__.py +0 -165
  138. cli_agent_runner-0.2.24/agent_runner/api.py +0 -990
  139. cli_agent_runner-0.2.24/tests/invariants/test_doc_builtin_plugins.py +0 -41
  140. cli_agent_runner-0.2.24/tests/invariants/test_entry_points_resolve.py +0 -63
  141. cli_agent_runner-0.2.24/tests/unit/test_init_entry_points.py +0 -226
  142. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/.codecov.yml +0 -0
  143. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  144. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  145. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  146. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  147. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/.github/dependabot.yml +0 -0
  148. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/.gitignore +0 -0
  149. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/BACKLOG.md +0 -0
  150. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/CODE_OF_CONDUCT.md +0 -0
  151. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/CONTRIBUTING.md +0 -0
  152. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/LICENSE +0 -0
  153. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/README.zh.md +0 -0
  154. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/SECURITY.md +0 -0
  155. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_emit/agent.py +0 -0
  156. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_monitor_registry.py +0 -0
  157. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_monitor_state.py +0 -0
  158. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_redact.py +0 -0
  159. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_registry.py +0 -0
  160. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_resolve.py +0 -0
  161. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_round_outcome.py +0 -0
  162. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_round_support.py +0 -0
  163. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_substrate.py +0 -0
  164. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/_throttle.py +0 -0
  165. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/builtin_plugins/__init__.py +0 -0
  166. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/builtin_plugins/_constants.py +0 -0
  167. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/__main__.py +0 -0
  168. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/doctor_cmd.py +0 -0
  169. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/events_cmd.py +0 -0
  170. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/init_cmd.py +0 -0
  171. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/install_cmd.py +0 -0
  172. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/migrate_cmd.py +0 -0
  173. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/monitor_cmd.py +0 -0
  174. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/peek_cmd.py +0 -0
  175. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/round_cmd.py +0 -0
  176. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/service_cmd.py +0 -0
  177. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/cli/upgrade_cmd.py +0 -0
  178. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/clock.py +0 -0
  179. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/config/errors.py +0 -0
  180. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/context_store.py +0 -0
  181. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/hooks.py +0 -0
  182. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/http_progress.py +0 -0
  183. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/metrics.py +0 -0
  184. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/presets/__init__.py +0 -0
  185. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/prompt_loader.py +0 -0
  186. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/remote_relay.py +0 -0
  187. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/round_log.py +0 -0
  188. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/round_view.py +0 -0
  189. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/scaffold.py +0 -0
  190. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/schedule.py +0 -0
  191. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/agent_runner/vcs_state.py +0 -0
  192. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/build.sh +0 -0
  193. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/deploy/Dockerfile +0 -0
  194. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/deploy/docker-compose.yml +0 -0
  195. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/README.md +0 -0
  196. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/migrations/0.2.md +0 -0
  197. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/recipes/deploy-all-in-one.md +0 -0
  198. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/recipes/kimi.md +0 -0
  199. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/docs/thesis.md +0 -0
  200. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/__init__.py +0 -0
  201. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/_clock.py +0 -0
  202. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/conftest.py +0 -0
  203. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/contract/__init__.py +0 -0
  204. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/e2e/__init__.py +0 -0
  205. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/e2e/test_e2e_graceful_stop.py +0 -0
  206. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/e2e/test_e2e_install_systemd.py +0 -0
  207. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/e2e/test_e2e_monitor_remote.py +0 -0
  208. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
  209. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/fixtures/cli-real-output/claude-2.1.143-assistant-tool-use.jsonl +0 -0
  210. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/fixtures/cli-real-output/claude-2.1.143-result-event.jsonl +0 -0
  211. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/fixtures/cli-real-output/gemini-0.42.0-result-event.jsonl +0 -0
  212. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/generate_vulture_whitelist.py +0 -0
  213. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/__init__.py +0 -0
  214. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_bounded_run.py +0 -0
  215. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_context_enricher_namespacing.py +0 -0
  216. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_fresh_eyes_signal.py +0 -0
  217. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_monitor_seeded.py +0 -0
  218. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_plugin_detector_loaded.py +0 -0
  219. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_plugin_owned_paths.py +0 -0
  220. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_prompt_delivery_stdin.py +0 -0
  221. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_scaffold_presets.py +0 -0
  222. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_spawn_round_wedged.py +0 -0
  223. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_substrate_fingerprint.py +0 -0
  224. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/integration/test_transient_error_backoff.py +0 -0
  225. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/__init__.py +0 -0
  226. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/_docs.py +0 -0
  227. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/_event_scan.py +0 -0
  228. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_atomic_write_enforced.py +0 -0
  229. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_authored_facts_declared.py +0 -0
  230. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_auto_stop_policy_ssot.py +0 -0
  231. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_bdd_test_conventions.py +0 -0
  232. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_catalogs.py +0 -0
  233. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_classification_ssot.py +0 -0
  234. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_config_value_set_ssot.py +0 -0
  235. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_container_stop_bound.py +0 -0
  236. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_dataclass_fields_have_producers.py +0 -0
  237. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_doc_cli_claims.py +0 -0
  238. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_doc_operator_surface.py +0 -0
  239. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_doc_test_pointers_exist.py +0 -0
  240. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_doc_transient_error_claims.py +0 -0
  241. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_doc_usage_event_fields.py +0 -0
  242. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_docs_generated.py +0 -0
  243. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_docs_index_complete.py +0 -0
  244. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_event_int_coercion.py +0 -0
  245. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_event_kind_registry.py +0 -0
  246. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_event_kinds_ssot.py +0 -0
  247. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_events_doc_contract.py +0 -0
  248. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_gen_blocks_name_their_source.py +0 -0
  249. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_hook_contract_docs.py +0 -0
  250. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_layer_2_loop_size.py +0 -0
  251. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_migrations_registry_ssot.py +0 -0
  252. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_module_sizes.py +0 -0
  253. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_monitor_self_exclusion.py +0 -0
  254. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_no_ai_signatures.py +0 -0
  255. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_no_internal_refs.py +0 -0
  256. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
  257. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_no_raw_time.py +0 -0
  258. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_no_vacuous_invariants.py +0 -0
  259. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_no_version_refs.py +0 -0
  260. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_phase_select_stateless.py +0 -0
  261. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_plugins_config_stable.py +0 -0
  262. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_readme_zh_is_pointer.py +0 -0
  263. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
  264. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_round_alloc_growth.py +0 -0
  265. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_round_log_decode_boundary.py +0 -0
  266. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_round_num_single_source.py +0 -0
  267. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_round_result_stable.py +0 -0
  268. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_set_diff_for_auto_tool_classification.py +0 -0
  269. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
  270. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_upstream_schema_canary.py +0 -0
  271. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/invariants/test_vulture_whitelist_generated.py +0 -0
  272. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/literate/__init__.py +0 -0
  273. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/literate/parser.py +0 -0
  274. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/literate/test_parser.py +0 -0
  275. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/literate/test_quickstart.py +0 -0
  276. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/__init__.py +0 -0
  277. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_agent_runtime.py +0 -0
  278. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_agent_runtime_container.py +0 -0
  279. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_agent_runtime_marker_scan.py +0 -0
  280. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_agent_runtime_progress.py +0 -0
  281. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_agent_runtime_reap.py +0 -0
  282. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_agent_runtime_signal_name.py +0 -0
  283. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_api_assemble_prompt.py +0 -0
  284. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_api_events_stream.py +0 -0
  285. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_api_read_round_num.py +0 -0
  286. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_api_types.py +0 -0
  287. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_catalogs.py +0 -0
  288. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_cgroup_probe.py +0 -0
  289. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_claude_error_detector.py +0 -0
  290. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_claude_foreign_schema.py +0 -0
  291. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_cli_init_install.py +0 -0
  292. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_cli_monitor_http.py +0 -0
  293. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_cli_upgrade.py +0 -0
  294. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_codewhale_code_precedence.py +0 -0
  295. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_codewhale_plugin.py +0 -0
  296. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_coerce_int_poison.py +0 -0
  297. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_config_fresh_eyes.py +0 -0
  298. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_config_max_rounds.py +0 -0
  299. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_config_prompt_delivery.py +0 -0
  300. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_config_stop_file.py +0 -0
  301. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
  302. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_config_transient_error_action.py +0 -0
  303. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_context_store.py +0 -0
  304. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_default_dirty_handler.py +0 -0
  305. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_detector_isolation.py +0 -0
  306. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_detector_protocol.py +0 -0
  307. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_dirty_handlers.py +0 -0
  308. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_effective_throttle_view.py +0 -0
  309. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_emit_config_migrated.py +0 -0
  310. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_emit_schedule.py +0 -0
  311. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_emit_schedule_phase_skipped.py +0 -0
  312. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_events_cmd.py +0 -0
  313. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_events_cmd_rollover.py +0 -0
  314. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_events_cmd_tail.py +0 -0
  315. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_events_jsonl_decode.py +0 -0
  316. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_example_dirty_handler.py +0 -0
  317. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_example_plugin.py +0 -0
  318. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_exec_prefix.py +0 -0
  319. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_exit_cause.py +0 -0
  320. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_exit_classifier.py +0 -0
  321. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_fresh_eyes_trigger.py +0 -0
  322. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_gemini_plugin.py +0 -0
  323. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_git_timeout.py +0 -0
  324. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_hook_failure_isolation.py +0 -0
  325. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_hook_redact_order.py +0 -0
  326. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_http_progress.py +0 -0
  327. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_http_progress_epoch.py +0 -0
  328. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_interruptible_sleep_should_stop.py +0 -0
  329. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_kimi_plugin.py +0 -0
  330. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_lifecycle.py +0 -0
  331. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_metrics.py +0 -0
  332. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_metrics_cgroup_usage.py +0 -0
  333. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_metrics_swap.py +0 -0
  334. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_cmd_severity.py +0 -0
  335. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
  336. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
  337. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
  338. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_event_tail.py +0 -0
  339. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_on_alert.py +0 -0
  340. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_remote.py +0 -0
  341. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_round_deferred_stale.py +0 -0
  342. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_monitor_schedule_pause.py +0 -0
  343. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_network_blip_signal_guard.py +0 -0
  344. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_open_round_log.py +0 -0
  345. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_orphan_chain_single_pass.py +0 -0
  346. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_peek_argparse.py +0 -0
  347. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_peek_project_isolation.py +0 -0
  348. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_peek_schedule.py +0 -0
  349. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_peek_select.py +0 -0
  350. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_pi_plugin.py +0 -0
  351. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_plugin_constants.py +0 -0
  352. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_presets.py +0 -0
  353. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_primary_prompt_file_home.py +0 -0
  354. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_prompt_loader.py +0 -0
  355. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_read_json_hardening.py +0 -0
  356. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_redact.py +0 -0
  357. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_round_cmd.py +0 -0
  358. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_round_cmd_sigterm.py +0 -0
  359. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_round_log_helpers.py +0 -0
  360. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_round_outcome.py +0 -0
  361. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_round_outcome_agent_axis.py +0 -0
  362. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_round_view.py +0 -0
  363. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_runner_sidecar_ordering.py +0 -0
  364. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_runner_throttle.py +0 -0
  365. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_scaffold.py +0 -0
  366. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_schedule.py +0 -0
  367. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_cmd_bounded.py +0 -0
  368. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_config_broken.py +0 -0
  369. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_crash_loop.py +0 -0
  370. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_phase_schedule.py +0 -0
  371. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_policy.py +0 -0
  372. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_round_oom_killed.py +0 -0
  373. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_round_terminate.py +0 -0
  374. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_schedule_gate.py +0 -0
  375. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_sentinel.py +0 -0
  376. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_serve_startup_hooks.py +0 -0
  377. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_service_mode.py +0 -0
  378. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_startup_battery_exit.py +0 -0
  379. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_substrate.py +0 -0
  380. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_supervisor_stale_self_blind.py +0 -0
  381. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_throttle_back_off_ladder.py +0 -0
  382. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_throttle_epoch_bound.py +0 -0
  383. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_throttle_plugin_classification.py +0 -0
  384. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.1}/tests/unit/test_vcs_state.py +0 -0
@@ -18,7 +18,7 @@ jobs:
18
18
  runs-on: ubuntu-latest
19
19
  timeout-minutes: 2
20
20
  steps:
21
- - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
21
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
22
22
  with:
23
23
  # PR: scan only new commits the PR introduces (base..head).
24
24
  # Push to main: scan the just-pushed commits (before..after).
@@ -74,10 +74,10 @@ jobs:
74
74
  # granularity AND because coverage requires extra pytest flags not in
75
75
  # the local `test` task. CONTRIBUTING.md states the parity intentionally.
76
76
  steps:
77
- - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
77
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
78
78
  with:
79
79
  fetch-depth: 0
80
- - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
80
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
81
81
  with:
82
82
  python-version: ${{ matrix.python }}
83
83
  cache: pip
@@ -26,10 +26,10 @@ jobs:
26
26
  runs-on: ubuntu-latest
27
27
  timeout-minutes: 10
28
28
  steps:
29
- - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
29
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
30
30
  with:
31
31
  fetch-depth: 0
32
- - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
32
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
33
33
  with:
34
34
  python-version: "3.12"
35
35
  - name: install build
@@ -72,7 +72,7 @@ jobs:
72
72
  permissions:
73
73
  contents: write
74
74
  steps:
75
- - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
75
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
76
76
  - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
77
77
  with:
78
78
  name: dist
@@ -13,6 +13,16 @@ log_dir
13
13
  offsets
14
14
  buffer
15
15
 
16
+ # agent_runner._plugin_manifest.PluginManifest
17
+ name
18
+ pre_round_hooks
19
+ context_enrichers
20
+ post_round_hooks
21
+ serve_startup_hooks
22
+ dirty_handlers
23
+ detectors
24
+ event_kinds
25
+
16
26
  # agent_runner._round_outcome.RoundOutcome
17
27
  mem_terminated
18
28
  usage_capable
@@ -67,6 +77,7 @@ service
67
77
  recent_events
68
78
  recent_hook_failures
69
79
  recent_blips
80
+ recent_cgroup_growth_warnings
70
81
  schedule
71
82
 
72
83
  # agent_runner.api_types.RateLimitState
@@ -164,18 +175,12 @@ round_progress_interval_s
164
175
  supervisor_stale_threshold_s
165
176
 
166
177
  # agent_runner.config.models.MonitorHostHealthConfig
167
- mem_avail_min_mb
168
- disk_warning_pct
169
- disk_critical_pct
170
- swap_sout_noise_floor_mb
171
- mem_free_low_mb
172
- psi_full_avg10_critical
173
- psi_some_avg10_warning
174
- mem_critical_consecutive_samples
175
- in_round_mem_terminate
178
+ disk
179
+ memory
180
+ pressure
176
181
 
177
182
  # agent_runner.config.models.PhaseOverride
178
- round_timeout_s
183
+ round_budget_s
179
184
  disable_pre_round_hooks
180
185
  prompt_files
181
186
  agent
@@ -207,7 +212,7 @@ strip_yaml_frontmatter
207
212
  # agent_runner.config.models.RuntimeConfig
208
213
  work_dir
209
214
  log_dir
210
- round_timeout_s
215
+ round_budget_s
211
216
  restart_delay_s
212
217
  disable_pre_round_hooks
213
218
  round_log_retention
@@ -230,6 +235,22 @@ pause_windows
230
235
  stash_idempotency_s
231
236
  dirty_action
232
237
 
238
+ # agent_runner.config.models._HostHealthDiskConfig
239
+ warning_pct
240
+ critical_pct
241
+
242
+ # agent_runner.config.models._HostHealthMemoryConfig
243
+ avail_min_mb
244
+ free_low_mb
245
+ swap_out_noise_floor_mb
246
+
247
+ # agent_runner.config.models._HostHealthPressureConfig
248
+ full_avg10_critical
249
+ some_avg10_warning
250
+ critical_consecutive_samples
251
+ in_round_terminate
252
+ cgroup_growth_rate_warning_mb_per_min
253
+
233
254
  # agent_runner.context_store.OrphanState
234
255
  round_num
235
256
  files
@@ -5,6 +5,28 @@ 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.3.1] - 2026-09-14
9
+
10
+ ### Added
11
+ - `monitor.host_health.pressure.cgroup_growth_rate_warning_mb_per_min` (default 512.0): a mid-round memory-growth-rate detector gives an earlier growth signal than the existing PSI/MemFree floors catch, ahead of a runaway climb reaching them. Observability only — no round action.
12
+ - `cgroup_growth_rate_warning` event, surfaced in `peek --json` as `recent_cgroup_growth_warnings` (schema `2.1`).
13
+
14
+ ## [0.3.0] - 2026-09-13
15
+
16
+ **The one breaking release of the 0.3 line.** Run `agent-runner migrate` before upgrading — it rewrites everything below in one pass.
17
+
18
+ ### Changed
19
+ - `[monitor.host_health]`'s 9 flat keys regroup into `.disk`/`.memory`/`.pressure` sub-tables (defaults unchanged).
20
+ - `round_timeout_s` renamed to `round_budget_s` in `[runtime]` and `[phases.*]` (flat and nested forms).
21
+ - The plugin ABI collapses from 7 entry-point groups to one (`agent_runner.plugins`), each resolving a typed `PluginManifest` instead of an import-time side effect. External plugins need a manual port — see `docs/migrations/0.3.md`.
22
+ - A phase-window overlap now refuses `serve` boot (was a warning); `doctor` reports it as a failing check.
23
+ - `peek --json` schema bumps to `2.0`.
24
+ - Config now carries a top-level `schema_version`; a pre-0.3 or unversioned config is rejected with a pointed `agent-runner migrate` instruction.
25
+ - `api.py` split into `_install`/`_lifecycle`/`_observe` behind an unchanged public facade — no external-surface change.
26
+
27
+ ### Renamed
28
+ - Tenet-3 terminology: "price-blind" → "price-agnostic" (no logic change).
29
+
8
30
  ## [0.2.24] - 2026-09-12
9
31
 
10
32
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cli-agent-runner
3
- Version: 0.2.24
3
+ Version: 0.3.1
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
@@ -100,7 +100,7 @@ Verb reference: [`docs/commands.md`](docs/commands.md).
100
100
  Each carries the historical incident it codifies and the invariant test that
101
101
  guards it. Highlights:
102
102
 
103
- - **round_timeout_s** — hard wall, never the agent's word on when to stop
103
+ - **round_budget_s** — hard wall, never the agent's word on when to stop
104
104
  - **process_group_isolation** — kill the round, not just the parent
105
105
  - **orphan_stash_idempotency_s** — no 3-stashes-per-second pile-ups
106
106
  - **sha_locked_stash** — `stash@{N}` indices drift; SHAs don't
@@ -62,7 +62,7 @@ Verb reference: [`docs/commands.md`](docs/commands.md).
62
62
  Each carries the historical incident it codifies and the invariant test that
63
63
  guards it. Highlights:
64
64
 
65
- - **round_timeout_s** — hard wall, never the agent's word on when to stop
65
+ - **round_budget_s** — hard wall, never the agent's word on when to stop
66
66
  - **process_group_isolation** — kill the round, not just the parent
67
67
  - **orphan_stash_idempotency_s** — no 3-stashes-per-second pile-ups
68
68
  - **sha_locked_stash** — `stash@{N}` indices drift; SHAs don't
@@ -0,0 +1,102 @@
1
+ """Agent Runner — restart-on-exit supervisor for autonomous CLI agents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ try:
6
+ from agent_runner._version import __version__
7
+ except ImportError: # editable install before hatch-vcs has generated _version.py
8
+ __version__ = "0.0.0+unknown"
9
+
10
+ _PLUGIN_GROUP = "agent_runner.plugins"
11
+
12
+ # Tracks the names passed to the most recent ``apply_plugin_disable`` call.
13
+ # Surfaced via peek --json `plugins.disabled` for operator visibility.
14
+ _DISABLED_PLUGIN_NAMES: list[str] = []
15
+
16
+
17
+ def _load_plugin_manifests() -> None:
18
+ """Discover agent_runner.plugins entry points, resolve each to a
19
+ module-level PluginManifest, and register its declared capabilities.
20
+
21
+ Called at package import. A broken plugin (bad import, missing PLUGIN
22
+ attribute, malformed manifest) must never crash the supervisor — isolated
23
+ as a UserWarning.
24
+
25
+ Discovery goes through the ``entry_points.txt`` scanner (cheaper than a
26
+ fresh ``importlib.metadata.entry_points()`` scan per process; see
27
+ ``_plugin_scan``), with a hard fallback to ``importlib.metadata`` on any
28
+ parse failure.
29
+ """
30
+ import importlib
31
+ import sys
32
+ import warnings
33
+
34
+ from agent_runner._plugin_manifest import register_manifest
35
+ from agent_runner._plugin_scan import scan_entry_points
36
+
37
+ for name, value in scan_entry_points(sys.path, _PLUGIN_GROUP):
38
+ try:
39
+ # An entry-point value may carry a trailing extras marker
40
+ # (``module:attr [extra1,extra2]``); importlib.metadata.EntryPoint's
41
+ # own module/attr grammar excludes "[", so anything from the first
42
+ # "[" onward is always extras, never part of the path — strip it
43
+ # before resolving, or it glues onto attr_path and breaks getattr().
44
+ module_path, _, attr_path = value.partition("[")[0].rstrip().partition(":")
45
+ mod = importlib.import_module(module_path)
46
+ target = mod
47
+ for attr in filter(None, attr_path.split(".")):
48
+ target = getattr(target, attr)
49
+ register_manifest(target)
50
+ except Exception as e:
51
+ warnings.warn(
52
+ f"failed to load {_PLUGIN_GROUP} plugin {name!r}: {e}",
53
+ stacklevel=3,
54
+ )
55
+
56
+
57
+ _load_plugin_manifests()
58
+
59
+
60
+ def apply_plugin_disable(names: list[str]) -> None:
61
+ """Remove plugins matching ``names`` (manifest names) from all in-memory
62
+ registries.
63
+
64
+ Called after config-load to honor ``[plugins] disable``. Idempotent for
65
+ already-removed names. Emits a UserWarning for names that match no
66
+ loaded manifest (typo catcher; tolerates cross-env config drift).
67
+
68
+ Plugin packages still load at import time — this removes from the registries
69
+ that the runner and peek consult. Side effects from loading (module-level
70
+ imports, etc.) have already happened by the time this runs.
71
+
72
+ Known limitation: vcs_state._PLUGIN_OWNED_PATHS lacks per-plugin name
73
+ attribution today, so owned-paths are NOT filtered here. Disabled plugin's
74
+ paths remain registered (mostly inert).
75
+ """
76
+ import warnings
77
+
78
+ from agent_runner._plugin_manifest import unregister_by_name
79
+
80
+ if not names:
81
+ return
82
+
83
+ global _DISABLED_PLUGIN_NAMES
84
+ _DISABLED_PLUGIN_NAMES = list(names)
85
+
86
+ found = unregister_by_name(set(names))
87
+ unknown = set(names) - found
88
+ if unknown:
89
+ warnings.warn(
90
+ f"[plugins] disable references unknown plugin name(s): {sorted(unknown)}. "
91
+ f"(Names matched no loaded PluginManifest; check spelling or installed packages.)",
92
+ stacklevel=2,
93
+ )
94
+
95
+
96
+ def disabled_plugin_names() -> list[str]:
97
+ """Names passed to the most recent ``apply_plugin_disable`` call.
98
+
99
+ Used by peek --json to surface ``plugins.disabled`` for operator visibility.
100
+ Returns the LIST (not set) preserving configured order.
101
+ """
102
+ return list(_DISABLED_PLUGIN_NAMES)
@@ -29,6 +29,9 @@ from agent_runner.config import (
29
29
  RuntimeConfig,
30
30
  ScheduleConfig,
31
31
  VcsConfig,
32
+ _HostHealthDiskConfig,
33
+ _HostHealthMemoryConfig,
34
+ _HostHealthPressureConfig,
32
35
  )
33
36
  from agent_runner.defenses import catalog
34
37
  from agent_runner.events import (
@@ -55,7 +58,16 @@ _SECTIONS: list[tuple[str, type, list[tuple[str, type]]]] = [
55
58
  ("runtime", RuntimeConfig, []),
56
59
  ("prompt", PromptConfig, []),
57
60
  ("vcs", VcsConfig, []),
58
- ("monitor", MonitorConfig, [("monitor.host_health", MonitorHostHealthConfig)]),
61
+ (
62
+ "monitor",
63
+ MonitorConfig,
64
+ [
65
+ ("monitor.host_health", MonitorHostHealthConfig),
66
+ ("monitor.host_health.disk", _HostHealthDiskConfig),
67
+ ("monitor.host_health.memory", _HostHealthMemoryConfig),
68
+ ("monitor.host_health.pressure", _HostHealthPressureConfig),
69
+ ],
70
+ ),
59
71
  ("phases", PhasesConfig, []),
60
72
  ("plugins", PluginsConfig, []),
61
73
  ("schedule", ScheduleConfig, []),
@@ -29,6 +29,7 @@ from agent_runner._emit.agent import ( # noqa: F401 — public re-export
29
29
  emit_transient_error_recovered,
30
30
  )
31
31
  from agent_runner._emit.memory import ( # noqa: F401 — public re-export
32
+ emit_cgroup_growth_rate_warning,
32
33
  emit_host_cgroup_memory_limit,
33
34
  emit_mem_pressure_deferred_to_cgroup,
34
35
  emit_round_cgroup_memory,
@@ -57,7 +58,6 @@ from agent_runner._emit.serve import ( # noqa: F401 — public re-export
57
58
  emit_max_rounds_reached,
58
59
  emit_mem_loop,
59
60
  emit_mem_loop_persistent,
60
- emit_phase_window_overlap,
61
61
  emit_rate_limit_stop,
62
62
  emit_schedule_paused,
63
63
  emit_schedule_phase_skipped,
@@ -70,6 +70,7 @@ __all__ = [
70
70
  "emit_agent_auth_error_detected",
71
71
  "emit_agent_usage_recorded",
72
72
  "emit_anomaly_repetitive_tool",
73
+ "emit_cgroup_growth_rate_warning",
73
74
  "emit_config_broken",
74
75
  "emit_config_migrated",
75
76
  "emit_crash_loop",
@@ -79,7 +80,6 @@ __all__ = [
79
80
  "emit_mem_loop",
80
81
  "emit_mem_loop_persistent",
81
82
  "emit_mem_pressure_deferred_to_cgroup",
82
- "emit_phase_window_overlap",
83
83
  "emit_rate_limit_stop",
84
84
  "emit_round_cgroup_memory",
85
85
  "emit_round_container_orphan_risk",
@@ -78,10 +78,10 @@ def emit_round_mem_critical_sample(
78
78
  sample-level signal, distinct from mem_pressure_deferred_to_cgroup's
79
79
  once-per-episode terminate-vs-defer notice).
80
80
 
81
- The caller caps this at ``2 * mem_critical_consecutive_samples``
81
+ The caller caps this at ``2 * pressure.critical_consecutive_samples``
82
82
  consecutive ticks (1..6 at the default 3) -- a sustained-critical
83
83
  don't-terminate run (cgroup-defer, or the off switch) would otherwise
84
- write one event per ~10s tick for up to a whole ``round_timeout_s`` on a
84
+ write one event per ~10s tick for up to a whole ``round_budget_s`` on a
85
85
  permanently-deferred/off host. The cap is per streak-episode, not a
86
86
  lifetime limit: any non-critical tick still resets the streak to 0, and
87
87
  sampling resumes from 1 the next time critical pressure recurs."""
@@ -233,8 +233,38 @@ def emit_mem_pressure_deferred_to_cgroup(
233
233
  (both memory.max and memory.swap.max finite) -- kernel cgroup-OOM will
234
234
  contain the agent and keep the host responsive on its own, so the
235
235
  cruder host-wide round-kill steps back instead of firing
236
- round_mem_terminated. This OVERRIDES in_round_mem_terminate=True: a
236
+ round_mem_terminated. This OVERRIDES in_round_terminate=True: a
237
237
  bounded cgroup makes the host floor strictly worse, not just redundant."""
238
238
  from agent_runner.events import MEM_PRESSURE_DEFERRED_TO_CGROUP, emit
239
239
 
240
240
  emit(log_dir, MEM_PRESSURE_DEFERRED_TO_CGROUP, pid=pid, signal=signal, message=message)
241
+
242
+
243
+ def emit_cgroup_growth_rate_warning(
244
+ log_dir: Path,
245
+ *,
246
+ round_num: int,
247
+ rate_mb_per_min: float,
248
+ threshold_mb_per_min: float,
249
+ source: str,
250
+ context: dict,
251
+ ) -> None:
252
+ """Emit on a False->True crossing of _spawn_round's mid-round growth-rate
253
+ derivative (host_health.cgroup_growth_rate_pressure) -- observability
254
+ only, no action path. ``source`` names which per-tick reading the
255
+ derivative used: "cgroup" (a finite cgroup memory.max bound exists,
256
+ memory.current is read) or "rss_sum" (no finite bound -- the psutil
257
+ children_rss_sum_bytes tree sum is used instead). Edge-triggered: fires
258
+ once per crossing episode, not once per tick, so a sustained climb does
259
+ not spam the event stream."""
260
+ from agent_runner.events import CGROUP_GROWTH_RATE_WARNING, emit
261
+
262
+ emit(
263
+ log_dir,
264
+ CGROUP_GROWTH_RATE_WARNING,
265
+ round_num=round_num,
266
+ rate_mb_per_min=rate_mb_per_min,
267
+ threshold_mb_per_min=threshold_mb_per_min,
268
+ source=source,
269
+ context=context,
270
+ )
@@ -169,7 +169,7 @@ def emit_round_grace_extended(
169
169
  ) -> None:
170
170
  """Emit when the grace-after-result timer expired but the agent still had
171
171
  live worker processes (e.g. a backgrounded build), so the round was NOT
172
- killed; it continues until it finishes or hits round_timeout_s.
172
+ killed; it continues until it finishes or hits round_budget_s.
173
173
 
174
174
  live_children: list of ``{"name": <exe basename>, "pid": <int>}`` dicts
175
175
  (previously a list of cmdline strings).
@@ -191,23 +191,3 @@ def emit_schedule_resumed(log_dir: Path, *, paused_for_s: int) -> None:
191
191
  from agent_runner.events import SCHEDULE_RESUMED, emit
192
192
 
193
193
  emit(log_dir, SCHEDULE_RESUMED, paused_for_s=paused_for_s)
194
-
195
-
196
- def emit_phase_window_overlap(
197
- log_dir: Path, *, phase_a: str, phase_b: str, window_a: str, window_b: str
198
- ) -> None:
199
- """Emit phase_window_overlap: two agent-overriding phases' own run_windows
200
- collide (a rotation footgun; a later release promotes this exact detection
201
- to a hard error). Called once at serve boot -- the collision is a property of the
202
- static config, not of any one round, so (like emit_max_rounds_reached's
203
- cgroup-probe sibling) this must not be called from the per-round path."""
204
- from agent_runner.events import PHASE_WINDOW_OVERLAP, emit
205
-
206
- emit(
207
- log_dir,
208
- PHASE_WINDOW_OVERLAP,
209
- phase_a=phase_a,
210
- phase_b=phase_b,
211
- window_a=window_a,
212
- window_b=window_b,
213
- )
@@ -0,0 +1,187 @@
1
+ """Systemd install / uninstall: ``init``, ``install``, ``uninstall``.
2
+
3
+ Depends one-directionally on ``_lifecycle.py`` for the shared
4
+ ``_project_name`` / ``_check_user_systemd_available`` / ``_SYSTEM_UNITS_DIR`` /
5
+ ``_system_unit_exists`` / ``_PID_SIGNAL_GRACE_S`` helpers — never the reverse.
6
+
7
+ Re-exported from ``agent_runner.api`` (the thin facade) so external callers
8
+ and existing internal callers keep working unchanged.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import os
14
+ import shutil
15
+ import subprocess # noqa: TID251 — install uses systemctl, subprocess
16
+ import sysconfig
17
+ from pathlib import Path
18
+
19
+ from agent_runner import _resolve, lifecycle
20
+ from agent_runner._lifecycle import (
21
+ _PID_SIGNAL_GRACE_S,
22
+ _SYSTEM_UNITS_DIR,
23
+ _check_user_systemd_available,
24
+ _project_name,
25
+ _system_unit_exists,
26
+ )
27
+ from agent_runner.api_types import InitResult, InstallResult
28
+ from agent_runner.clock import SYSTEM_CLOCK
29
+ from agent_runner.config import Config, load_config
30
+ from agent_runner.lifecycle import _systemctl_user
31
+ from agent_runner.scaffold import scaffold_project
32
+ from agent_runner.service_unit import (
33
+ monitor_unit_filename,
34
+ render_monitor_unit,
35
+ render_serve_unit,
36
+ serve_unit_filename,
37
+ )
38
+
39
+
40
+ def _agent_runner_script_path() -> Path:
41
+ """Locate the agent-runner CLI script for systemd ExecStart.
42
+
43
+ Tries shutil.which first (honors PATH). Falls back to sysconfig's
44
+ scripts dir (handles cases where PATH excludes the install dir).
45
+ Raises FileNotFoundError if neither resolves to an existing file.
46
+ """
47
+ which = shutil.which("agent-runner")
48
+ if which:
49
+ return Path(which)
50
+ scripts_dir = Path(sysconfig.get_path("scripts"))
51
+ candidate = scripts_dir / "agent-runner"
52
+ if candidate.exists():
53
+ return candidate
54
+ raise FileNotFoundError(
55
+ "agent-runner script not found in PATH or "
56
+ f"{scripts_dir}; reinstall via pip or activate the right venv"
57
+ )
58
+
59
+
60
+ def init(
61
+ work_dir: Path | None = None,
62
+ *,
63
+ preset: str = "claude",
64
+ force: bool = False,
65
+ commit: bool = True,
66
+ ) -> InitResult:
67
+ if work_dir is None:
68
+ work_dir = Path.cwd()
69
+ return scaffold_project(work_dir, preset=preset, force=force, commit=commit)
70
+
71
+
72
+ def _install_system(
73
+ cfg: Config, project: str, *, config_path: Path, with_monitor: bool
74
+ ) -> InstallResult:
75
+ if os.geteuid() != 0:
76
+ raise RuntimeError(
77
+ "--system requires sudo; run via `sudo -E agent-runner install --system`"
78
+ )
79
+ sudo_user = os.environ.get("SUDO_USER")
80
+ if not sudo_user:
81
+ raise RuntimeError(
82
+ "--system needs SUDO_USER env var; run via "
83
+ "`sudo -E agent-runner install --system` to preserve env"
84
+ )
85
+ script_path = _agent_runner_script_path()
86
+ serve_path = _SYSTEM_UNITS_DIR / serve_unit_filename(project)
87
+ serve_path.write_text(
88
+ render_serve_unit(cfg, script_path=script_path, config_path=config_path, user=sudo_user)
89
+ )
90
+ monitor_path: Path | None = None
91
+ if with_monitor:
92
+ monitor_path = _SYSTEM_UNITS_DIR / monitor_unit_filename(project)
93
+ monitor_path.write_text(
94
+ render_monitor_unit(
95
+ cfg, script_path=script_path, config_path=config_path, user=sudo_user
96
+ )
97
+ )
98
+ subprocess.run(["systemctl", "daemon-reload"], check=True)
99
+ subprocess.run(["systemctl", "enable", serve_unit_filename(project)], check=True)
100
+ if with_monitor:
101
+ subprocess.run(["systemctl", "enable", monitor_unit_filename(project)], check=True)
102
+ return InstallResult(
103
+ unit_path=serve_path,
104
+ monitor_unit_path=monitor_path,
105
+ enabled=True,
106
+ started=False,
107
+ )
108
+
109
+
110
+ def install(
111
+ work_dir: Path | None = None,
112
+ *,
113
+ system: bool = False,
114
+ with_monitor: bool = False,
115
+ force: bool = False,
116
+ ) -> InstallResult:
117
+ if work_dir is None:
118
+ work_dir = Path.cwd()
119
+ cfg_path = work_dir / "agent-runner.toml"
120
+ cfg = load_config(cfg_path)
121
+ project = _project_name(work_dir)
122
+
123
+ units_dir = _SYSTEM_UNITS_DIR if system else lifecycle._user_systemd_dir()
124
+ _resolve.guard_against_clobber(
125
+ units_dir / serve_unit_filename(project), cfg.runtime.work_dir, force=force
126
+ )
127
+
128
+ if system:
129
+ return _install_system(cfg, project, config_path=cfg_path, with_monitor=with_monitor)
130
+
131
+ _check_user_systemd_available()
132
+ script_path = _agent_runner_script_path()
133
+
134
+ units_dir.mkdir(parents=True, exist_ok=True)
135
+
136
+ serve_path = units_dir / serve_unit_filename(project)
137
+ serve_path.write_text(render_serve_unit(cfg, script_path=script_path, config_path=cfg_path))
138
+
139
+ monitor_path: Path | None = None
140
+ if with_monitor:
141
+ monitor_path = units_dir / monitor_unit_filename(project)
142
+ monitor_path.write_text(
143
+ render_monitor_unit(cfg, script_path=script_path, config_path=cfg_path)
144
+ )
145
+
146
+ _systemctl_user("daemon-reload")
147
+ _systemctl_user("enable", serve_unit_filename(project))
148
+ _systemctl_user("start", serve_unit_filename(project))
149
+ if with_monitor:
150
+ _systemctl_user("enable", monitor_unit_filename(project))
151
+ _systemctl_user("start", monitor_unit_filename(project))
152
+
153
+ return InstallResult(
154
+ unit_path=serve_path, monitor_unit_path=monitor_path, enabled=True, started=True
155
+ )
156
+
157
+
158
+ def uninstall(work_dir: Path | None = None) -> bool:
159
+ if work_dir is None:
160
+ work_dir = Path.cwd()
161
+ project = _project_name(work_dir)
162
+ if _system_unit_exists(project): # root-owned -- don't silently no-op the user-scope dir
163
+ units = [serve_unit_filename(project)]
164
+ if (_SYSTEM_UNITS_DIR / monitor_unit_filename(project)).exists():
165
+ units.append(monitor_unit_filename(project))
166
+ remedy = " && ".join(
167
+ f"sudo systemctl disable --now {u} && sudo rm {_SYSTEM_UNITS_DIR / u}" for u in units
168
+ )
169
+ print(f"{project} is managed by a system unit; run: {remedy}")
170
+ return False
171
+ units_dir = lifecycle._user_systemd_dir()
172
+ serve = units_dir / serve_unit_filename(project)
173
+ monitor = units_dir / monitor_unit_filename(project)
174
+ for p in (serve, monitor):
175
+ if p.exists():
176
+ # Drain-aware (see stop_unit_draining, the same primitive api.stop/
177
+ # api.restart use): a blocking `systemctl stop` would TimeoutExpired
178
+ # while serve drains its round -- queue it and confirm within a
179
+ # bounded window instead, same as stop(), before disabling/unlinking.
180
+ lifecycle.stop_unit_draining(p.name, clock=SYSTEM_CLOCK, confirm_s=_PID_SIGNAL_GRACE_S)
181
+ _systemctl_user("disable", p.name)
182
+ p.unlink(missing_ok=True)
183
+ try:
184
+ _systemctl_user("daemon-reload")
185
+ except (subprocess.CalledProcessError, FileNotFoundError):
186
+ pass # no user bus on this host -- units above (if any) already gone
187
+ return True