cli-agent-runner 0.2.24__tar.gz → 0.3.0__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.0}/.github/workflows/ci.yml +3 -3
  2. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/.github/workflows/release.yml +3 -3
  3. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/.vulture-whitelist.py +30 -11
  4. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/CHANGELOG.md +16 -0
  5. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/PKG-INFO +2 -2
  6. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/README.md +1 -1
  7. cli_agent_runner-0.3.0/agent_runner/__init__.py +102 -0
  8. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_docgen.py +13 -1
  9. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_emit/__init__.py +0 -2
  10. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_emit/memory.py +3 -3
  11. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_emit/rounds.py +1 -1
  12. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_emit/serve.py +0 -20
  13. cli_agent_runner-0.3.0/agent_runner/_install.py +187 -0
  14. cli_agent_runner-0.3.0/agent_runner/_lifecycle.py +338 -0
  15. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_monitor_detectors.py +13 -12
  16. cli_agent_runner-0.3.0/agent_runner/_observe.py +399 -0
  17. cli_agent_runner-0.3.0/agent_runner/_plugin_manifest.py +113 -0
  18. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_plugin_scan.py +9 -8
  19. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_serve_policy.py +3 -3
  20. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_version.py +2 -2
  21. cli_agent_runner-0.3.0/agent_runner/api.py +180 -0
  22. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/api_types.py +4 -3
  23. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/builtin_plugins/claude_rate_limit.py +3 -2
  24. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/builtin_plugins/codewhale.py +3 -2
  25. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/builtin_plugins/default_dirty_handler.py +3 -2
  26. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/builtin_plugins/gemini.py +3 -2
  27. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/builtin_plugins/kimi.py +3 -2
  28. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/builtin_plugins/pi.py +3 -2
  29. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/__init__.py +9 -4
  30. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/_serve_cgroup.py +4 -3
  31. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/_serve_round.py +17 -11
  32. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/common.py +1 -1
  33. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/serve_cmd.py +6 -9
  34. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/config/__init__.py +10 -1
  35. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/config/loader.py +28 -1
  36. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/config/models.py +55 -33
  37. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/config/parsers.py +65 -48
  38. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/defenses.py +3 -3
  39. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/events.py +3 -3
  40. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/host_health.py +21 -21
  41. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/lifecycle.py +2 -2
  42. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/migrations.py +203 -19
  43. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/monitor.py +15 -17
  44. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/phase_select.py +30 -8
  45. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/presets/aider.toml +2 -1
  46. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/presets/claude.toml +2 -1
  47. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/presets/codewhale.toml +2 -1
  48. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/presets/gemini.toml +2 -1
  49. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/presets/kimi.toml +2 -1
  50. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/presets/pi.toml +2 -1
  51. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/runner.py +2 -2
  52. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/service_unit.py +4 -4
  53. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/startup_check.py +19 -1
  54. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/architecture.md +8 -7
  55. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/commands.md +23 -9
  56. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/configuration.md +59 -27
  57. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/events.md +3 -3
  58. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/long-running-agents.md +5 -6
  59. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/memory-efficiency.md +73 -4
  60. cli_agent_runner-0.3.0/docs/migrations/0.3.md +103 -0
  61. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/plugins.md +122 -96
  62. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/quickstart.md +1 -1
  63. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/recipes/aider.md +2 -1
  64. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/recipes/codewhale.md +4 -3
  65. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/recipes/container-pi.md +1 -1
  66. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/recipes/pi.md +2 -2
  67. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/runbook.md +22 -21
  68. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/pyproject.toml +11 -9
  69. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/_test_helpers.py +4 -1
  70. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/contract/test_public_api_surface.py +0 -1
  71. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/e2e/conftest.py +1 -1
  72. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_auto_stop_e2e.py +2 -2
  73. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_dirty_handler_seam.py +2 -2
  74. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_grace_kill_emission.py +5 -5
  75. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_install_dry_run.py +3 -3
  76. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_plugin_real_flow.py +1 -0
  77. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_run_one_round_with_fake_agent.py +10 -9
  78. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_serve_loop.py +7 -12
  79. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_spawn_round_mem_floor.py +33 -23
  80. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_architecture.py +10 -1
  81. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_config_error_consistency.py +4 -4
  82. cli_agent_runner-0.3.0/tests/invariants/test_doc_builtin_plugins.py +57 -0
  83. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_doc_claims_match_ssot.py +2 -2
  84. cli_agent_runner-0.3.0/tests/invariants/test_entry_points_resolve.py +71 -0
  85. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_import_footprint.py +12 -0
  86. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_module_boundaries.py +2 -0
  87. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_no_price_logic.py +2 -2
  88. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_peek_schema_version.py +1 -0
  89. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_timeout_budget_invariant.py +2 -2
  90. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_agent_runtime_grace.py +3 -3
  91. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_api_install.py +22 -17
  92. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_api_observation.py +5 -4
  93. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_api_resolve_phase.py +6 -6
  94. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_api_service.py +77 -64
  95. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_auto_stop_gating.py +6 -3
  96. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_cli.py +3 -2
  97. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_cli_common.py +2 -0
  98. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_cli_main_boundary.py +50 -1
  99. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_cli_service_peek_monitor.py +1 -1
  100. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_config.py +261 -78
  101. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_config_nested_strictness.py +1 -1
  102. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_config_schedule.py +1 -0
  103. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_config_strictness.py +3 -0
  104. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_control_char_config.py +1 -0
  105. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_defenses.py +3 -3
  106. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_detect_hung_latch.py +4 -4
  107. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_docgen.py +12 -8
  108. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_doctor_cmd.py +24 -0
  109. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_events.py +0 -4
  110. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_hooks.py +1 -1
  111. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_host_health.py +14 -5
  112. cli_agent_runner-0.3.0/tests/unit/test_init_entry_points.py +171 -0
  113. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_migrate_cmd.py +9 -8
  114. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_migrations.py +195 -24
  115. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_assembly.py +2 -2
  116. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_dedup_identity.py +3 -2
  117. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_dedup_rearm.py +5 -5
  118. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_detectors.py +34 -17
  119. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_loop.py +7 -7
  120. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_outer_round_ceiling.py +19 -2
  121. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_phase_agent_launch.py +1 -1
  122. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_phase_profiles.py +8 -8
  123. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_phase_select.py +27 -0
  124. cli_agent_runner-0.3.0/tests/unit/test_plugin_manifest.py +94 -0
  125. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_plugin_scan_parity.py +13 -12
  126. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_resolve.py +2 -0
  127. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_runner.py +6 -6
  128. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_mem_gate.py +3 -2
  129. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_round.py +8 -8
  130. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_round_log.py +1 -0
  131. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_service_unit.py +10 -10
  132. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_startup_check.py +5 -3
  133. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_upgrade_migrate.py +37 -1
  134. cli_agent_runner-0.2.24/agent_runner/__init__.py +0 -165
  135. cli_agent_runner-0.2.24/agent_runner/api.py +0 -990
  136. cli_agent_runner-0.2.24/tests/invariants/test_doc_builtin_plugins.py +0 -41
  137. cli_agent_runner-0.2.24/tests/invariants/test_entry_points_resolve.py +0 -63
  138. cli_agent_runner-0.2.24/tests/unit/test_init_entry_points.py +0 -226
  139. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/.codecov.yml +0 -0
  140. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  141. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  142. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  143. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  144. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/.github/dependabot.yml +0 -0
  145. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/.gitignore +0 -0
  146. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/BACKLOG.md +0 -0
  147. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/CODE_OF_CONDUCT.md +0 -0
  148. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/CONTRIBUTING.md +0 -0
  149. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/LICENSE +0 -0
  150. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/README.zh.md +0 -0
  151. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/SECURITY.md +0 -0
  152. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_emit/agent.py +0 -0
  153. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_monitor_registry.py +0 -0
  154. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_monitor_state.py +0 -0
  155. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_redact.py +0 -0
  156. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_registry.py +0 -0
  157. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_resolve.py +0 -0
  158. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_round_outcome.py +0 -0
  159. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_round_support.py +0 -0
  160. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_substrate.py +0 -0
  161. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/_throttle.py +0 -0
  162. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/agent_runtime.py +0 -0
  163. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/builtin_plugins/__init__.py +0 -0
  164. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/builtin_plugins/_constants.py +0 -0
  165. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/__main__.py +0 -0
  166. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/doctor_cmd.py +0 -0
  167. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/events_cmd.py +0 -0
  168. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/init_cmd.py +0 -0
  169. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/install_cmd.py +0 -0
  170. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/migrate_cmd.py +0 -0
  171. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/monitor_cmd.py +0 -0
  172. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/peek_cmd.py +0 -0
  173. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/round_cmd.py +0 -0
  174. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/service_cmd.py +0 -0
  175. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/cli/upgrade_cmd.py +0 -0
  176. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/clock.py +0 -0
  177. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/config/errors.py +0 -0
  178. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/config/validators.py +0 -0
  179. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/context_store.py +0 -0
  180. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/hooks.py +0 -0
  181. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/http_progress.py +0 -0
  182. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/metrics.py +0 -0
  183. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/presets/__init__.py +0 -0
  184. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/prompt_loader.py +0 -0
  185. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/remote_relay.py +0 -0
  186. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/round_log.py +0 -0
  187. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/round_view.py +0 -0
  188. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/scaffold.py +0 -0
  189. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/schedule.py +0 -0
  190. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/agent_runner/vcs_state.py +0 -0
  191. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/build.sh +0 -0
  192. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/deploy/Dockerfile +0 -0
  193. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/deploy/docker-compose.yml +0 -0
  194. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/README.md +0 -0
  195. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/migrations/0.2.md +0 -0
  196. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/recipes/deploy-all-in-one.md +0 -0
  197. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/recipes/kimi.md +0 -0
  198. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/docs/thesis.md +0 -0
  199. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/__init__.py +0 -0
  200. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/_clock.py +0 -0
  201. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/conftest.py +0 -0
  202. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/contract/__init__.py +0 -0
  203. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/e2e/__init__.py +0 -0
  204. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/e2e/test_e2e_graceful_stop.py +0 -0
  205. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/e2e/test_e2e_install_systemd.py +0 -0
  206. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/e2e/test_e2e_monitor_remote.py +0 -0
  207. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
  208. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/fixtures/cli-real-output/claude-2.1.143-assistant-tool-use.jsonl +0 -0
  209. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/fixtures/cli-real-output/claude-2.1.143-result-event.jsonl +0 -0
  210. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/fixtures/cli-real-output/gemini-0.42.0-result-event.jsonl +0 -0
  211. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/generate_vulture_whitelist.py +0 -0
  212. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/__init__.py +0 -0
  213. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_bounded_run.py +0 -0
  214. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_context_enricher_namespacing.py +0 -0
  215. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_fresh_eyes_signal.py +0 -0
  216. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_monitor_seeded.py +0 -0
  217. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_plugin_detector_loaded.py +0 -0
  218. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_plugin_owned_paths.py +0 -0
  219. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_prompt_delivery_stdin.py +0 -0
  220. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_scaffold_presets.py +0 -0
  221. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_spawn_round_wedged.py +0 -0
  222. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_substrate_fingerprint.py +0 -0
  223. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/integration/test_transient_error_backoff.py +0 -0
  224. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/__init__.py +0 -0
  225. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/_docs.py +0 -0
  226. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/_event_scan.py +0 -0
  227. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_atomic_write_enforced.py +0 -0
  228. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_authored_facts_declared.py +0 -0
  229. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_auto_stop_policy_ssot.py +0 -0
  230. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_bdd_test_conventions.py +0 -0
  231. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_catalogs.py +0 -0
  232. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_classification_ssot.py +0 -0
  233. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_config_value_set_ssot.py +0 -0
  234. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_container_stop_bound.py +0 -0
  235. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_dataclass_fields_have_producers.py +0 -0
  236. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_doc_cli_claims.py +0 -0
  237. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_doc_operator_surface.py +0 -0
  238. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_doc_test_pointers_exist.py +0 -0
  239. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_doc_transient_error_claims.py +0 -0
  240. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_doc_usage_event_fields.py +0 -0
  241. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_docs_generated.py +0 -0
  242. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_docs_index_complete.py +0 -0
  243. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_event_int_coercion.py +0 -0
  244. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_event_kind_registry.py +0 -0
  245. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_event_kinds_ssot.py +0 -0
  246. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_events_doc_contract.py +0 -0
  247. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_gen_blocks_name_their_source.py +0 -0
  248. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_hook_contract_docs.py +0 -0
  249. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_layer_2_loop_size.py +0 -0
  250. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_migrations_registry_ssot.py +0 -0
  251. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_module_sizes.py +0 -0
  252. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_monitor_self_exclusion.py +0 -0
  253. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_no_ai_signatures.py +0 -0
  254. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_no_internal_refs.py +0 -0
  255. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
  256. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_no_raw_time.py +0 -0
  257. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_no_vacuous_invariants.py +0 -0
  258. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_no_version_refs.py +0 -0
  259. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_phase_select_stateless.py +0 -0
  260. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_plugins_config_stable.py +0 -0
  261. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_readme_zh_is_pointer.py +0 -0
  262. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
  263. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_round_alloc_growth.py +0 -0
  264. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_round_log_decode_boundary.py +0 -0
  265. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_round_num_single_source.py +0 -0
  266. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_round_result_stable.py +0 -0
  267. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_set_diff_for_auto_tool_classification.py +0 -0
  268. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
  269. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_upstream_schema_canary.py +0 -0
  270. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/invariants/test_vulture_whitelist_generated.py +0 -0
  271. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/literate/__init__.py +0 -0
  272. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/literate/parser.py +0 -0
  273. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/literate/test_parser.py +0 -0
  274. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/literate/test_quickstart.py +0 -0
  275. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/__init__.py +0 -0
  276. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_agent_runtime.py +0 -0
  277. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_agent_runtime_container.py +0 -0
  278. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_agent_runtime_marker_scan.py +0 -0
  279. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_agent_runtime_progress.py +0 -0
  280. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_agent_runtime_reap.py +0 -0
  281. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_agent_runtime_signal_name.py +0 -0
  282. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_api_assemble_prompt.py +0 -0
  283. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_api_events_stream.py +0 -0
  284. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_api_read_round_num.py +0 -0
  285. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_api_types.py +0 -0
  286. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_catalogs.py +0 -0
  287. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_cgroup_probe.py +0 -0
  288. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_claude_error_detector.py +0 -0
  289. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_claude_foreign_schema.py +0 -0
  290. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_cli_init_install.py +0 -0
  291. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_cli_monitor_http.py +0 -0
  292. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_cli_upgrade.py +0 -0
  293. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_codewhale_code_precedence.py +0 -0
  294. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_codewhale_plugin.py +0 -0
  295. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_coerce_int_poison.py +0 -0
  296. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_config_fresh_eyes.py +0 -0
  297. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_config_max_rounds.py +0 -0
  298. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_config_prompt_delivery.py +0 -0
  299. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_config_stop_file.py +0 -0
  300. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
  301. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_config_transient_error_action.py +0 -0
  302. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_context_store.py +0 -0
  303. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_default_dirty_handler.py +0 -0
  304. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_detector_isolation.py +0 -0
  305. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_detector_protocol.py +0 -0
  306. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_dirty_handlers.py +0 -0
  307. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_effective_throttle_view.py +0 -0
  308. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_emit_config_migrated.py +0 -0
  309. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_emit_schedule.py +0 -0
  310. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_emit_schedule_phase_skipped.py +0 -0
  311. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_events_cmd.py +0 -0
  312. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_events_cmd_rollover.py +0 -0
  313. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_events_cmd_tail.py +0 -0
  314. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_events_jsonl_decode.py +0 -0
  315. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_example_dirty_handler.py +0 -0
  316. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_example_plugin.py +0 -0
  317. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_exec_prefix.py +0 -0
  318. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_exit_cause.py +0 -0
  319. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_exit_classifier.py +0 -0
  320. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_fresh_eyes_trigger.py +0 -0
  321. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_gemini_plugin.py +0 -0
  322. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_git_timeout.py +0 -0
  323. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_hook_failure_isolation.py +0 -0
  324. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_hook_redact_order.py +0 -0
  325. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_http_progress.py +0 -0
  326. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_http_progress_epoch.py +0 -0
  327. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_interruptible_sleep_should_stop.py +0 -0
  328. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_kimi_plugin.py +0 -0
  329. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_lifecycle.py +0 -0
  330. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_metrics.py +0 -0
  331. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_metrics_cgroup_usage.py +0 -0
  332. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_metrics_swap.py +0 -0
  333. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_cmd_severity.py +0 -0
  334. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
  335. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
  336. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
  337. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_event_tail.py +0 -0
  338. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_on_alert.py +0 -0
  339. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_remote.py +0 -0
  340. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_round_deferred_stale.py +0 -0
  341. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_monitor_schedule_pause.py +0 -0
  342. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_network_blip_signal_guard.py +0 -0
  343. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_open_round_log.py +0 -0
  344. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_orphan_chain_single_pass.py +0 -0
  345. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_peek_argparse.py +0 -0
  346. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_peek_project_isolation.py +0 -0
  347. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_peek_schedule.py +0 -0
  348. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_peek_select.py +0 -0
  349. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_pi_plugin.py +0 -0
  350. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_plugin_constants.py +0 -0
  351. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_presets.py +0 -0
  352. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_primary_prompt_file_home.py +0 -0
  353. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_prompt_loader.py +0 -0
  354. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_read_json_hardening.py +0 -0
  355. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_redact.py +0 -0
  356. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_round_cmd.py +0 -0
  357. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_round_cmd_sigterm.py +0 -0
  358. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_round_log_helpers.py +0 -0
  359. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_round_outcome.py +0 -0
  360. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_round_outcome_agent_axis.py +0 -0
  361. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_round_view.py +0 -0
  362. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_runner_sidecar_ordering.py +0 -0
  363. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_runner_throttle.py +0 -0
  364. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_scaffold.py +0 -0
  365. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_schedule.py +0 -0
  366. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_cmd_bounded.py +0 -0
  367. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_config_broken.py +0 -0
  368. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_crash_loop.py +0 -0
  369. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_phase_schedule.py +0 -0
  370. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_policy.py +0 -0
  371. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_round_cgroup_spine.py +0 -0
  372. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_round_oom_killed.py +0 -0
  373. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_round_terminate.py +0 -0
  374. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_schedule_gate.py +0 -0
  375. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_sentinel.py +0 -0
  376. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_serve_startup_hooks.py +0 -0
  377. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_service_mode.py +0 -0
  378. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_startup_battery_exit.py +0 -0
  379. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_substrate.py +0 -0
  380. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_supervisor_stale_self_blind.py +0 -0
  381. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_throttle_back_off_ladder.py +0 -0
  382. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_throttle_epoch_bound.py +0 -0
  383. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/tests/unit/test_throttle_plugin_classification.py +0 -0
  384. {cli_agent_runner-0.2.24 → cli_agent_runner-0.3.0}/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
@@ -164,18 +174,12 @@ round_progress_interval_s
164
174
  supervisor_stale_threshold_s
165
175
 
166
176
  # 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
177
+ disk
178
+ memory
179
+ pressure
176
180
 
177
181
  # agent_runner.config.models.PhaseOverride
178
- round_timeout_s
182
+ round_budget_s
179
183
  disable_pre_round_hooks
180
184
  prompt_files
181
185
  agent
@@ -207,7 +211,7 @@ strip_yaml_frontmatter
207
211
  # agent_runner.config.models.RuntimeConfig
208
212
  work_dir
209
213
  log_dir
210
- round_timeout_s
214
+ round_budget_s
211
215
  restart_delay_s
212
216
  disable_pre_round_hooks
213
217
  round_log_retention
@@ -230,6 +234,21 @@ pause_windows
230
234
  stash_idempotency_s
231
235
  dirty_action
232
236
 
237
+ # agent_runner.config.models._HostHealthDiskConfig
238
+ warning_pct
239
+ critical_pct
240
+
241
+ # agent_runner.config.models._HostHealthMemoryConfig
242
+ avail_min_mb
243
+ free_low_mb
244
+ swap_out_noise_floor_mb
245
+
246
+ # agent_runner.config.models._HostHealthPressureConfig
247
+ full_avg10_critical
248
+ some_avg10_warning
249
+ critical_consecutive_samples
250
+ in_round_terminate
251
+
233
252
  # agent_runner.context_store.OrphanState
234
253
  round_num
235
254
  files
@@ -5,6 +5,22 @@ 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.0] - 2026-09-13
9
+
10
+ **The one breaking release of the 0.3 line.** Run `agent-runner migrate` before upgrading — it rewrites everything below in one pass.
11
+
12
+ ### Changed
13
+ - `[monitor.host_health]`'s 9 flat keys regroup into `.disk`/`.memory`/`.pressure` sub-tables (defaults unchanged).
14
+ - `round_timeout_s` renamed to `round_budget_s` in `[runtime]` and `[phases.*]` (flat and nested forms).
15
+ - 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`.
16
+ - A phase-window overlap now refuses `serve` boot (was a warning); `doctor` reports it as a failing check.
17
+ - `peek --json` schema bumps to `2.0`.
18
+ - Config now carries a top-level `schema_version`; a pre-0.3 or unversioned config is rejected with a pointed `agent-runner migrate` instruction.
19
+ - `api.py` split into `_install`/`_lifecycle`/`_observe` behind an unchanged public facade — no external-surface change.
20
+
21
+ ### Renamed
22
+ - Tenet-3 terminology: "price-blind" → "price-agnostic" (no logic change).
23
+
8
24
  ## [0.2.24] - 2026-09-12
9
25
 
10
26
  ### 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.0
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, []),
@@ -57,7 +57,6 @@ from agent_runner._emit.serve import ( # noqa: F401 — public re-export
57
57
  emit_max_rounds_reached,
58
58
  emit_mem_loop,
59
59
  emit_mem_loop_persistent,
60
- emit_phase_window_overlap,
61
60
  emit_rate_limit_stop,
62
61
  emit_schedule_paused,
63
62
  emit_schedule_phase_skipped,
@@ -79,7 +78,6 @@ __all__ = [
79
78
  "emit_mem_loop",
80
79
  "emit_mem_loop_persistent",
81
80
  "emit_mem_pressure_deferred_to_cgroup",
82
- "emit_phase_window_overlap",
83
81
  "emit_rate_limit_stop",
84
82
  "emit_round_cgroup_memory",
85
83
  "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,7 +233,7 @@ 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
 
@@ -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