cli-agent-runner 0.2.6__tar.gz → 0.2.8__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 (287) hide show
  1. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/.vulture-whitelist.py +28 -0
  2. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/CHANGELOG.md +23 -0
  3. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/PKG-INFO +10 -5
  4. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/README.md +8 -3
  5. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/_docgen.py +68 -6
  6. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/_emit.py +34 -0
  7. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/_version.py +2 -2
  8. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/api.py +4 -0
  9. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/api_types.py +1 -0
  10. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/cli/__init__.py +2 -0
  11. cli_agent_runner-0.2.8/agent_runner/cli/migrate_cmd.py +51 -0
  12. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/cli/serve_cmd.py +72 -0
  13. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/cli/upgrade_cmd.py +56 -3
  14. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/config.py +46 -4
  15. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/events.py +3 -0
  16. cli_agent_runner-0.2.8/agent_runner/migrations.py +89 -0
  17. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/monitor.py +52 -1
  18. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/presets/codewhale.toml +1 -0
  19. cli_agent_runner-0.2.8/agent_runner/schedule.py +178 -0
  20. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/architecture.md +3 -0
  21. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/commands.md +55 -25
  22. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/configuration.md +81 -1
  23. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/long-running-agents.md +1 -7
  24. cli_agent_runner-0.2.8/docs/migrations/0.2.7.md +121 -0
  25. cli_agent_runner-0.2.8/docs/migrations/0.2.8.md +79 -0
  26. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/recipes/aider.md +10 -0
  27. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/recipes/codewhale.md +12 -1
  28. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/runbook.md +56 -5
  29. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/_test_helpers.py +2 -0
  30. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_architecture.py +6 -1
  31. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_doc_claims_match_ssot.py +14 -0
  32. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_doc_cli_claims.py +6 -2
  33. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_doc_operator_surface.py +14 -2
  34. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_entry_points_resolve.py +1 -3
  35. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_config.py +23 -1
  36. cli_agent_runner-0.2.8/tests/unit/test_config_schedule.py +65 -0
  37. cli_agent_runner-0.2.8/tests/unit/test_emit_config_migrated.py +24 -0
  38. cli_agent_runner-0.2.8/tests/unit/test_emit_schedule.py +32 -0
  39. cli_agent_runner-0.2.8/tests/unit/test_migrate_cmd.py +85 -0
  40. cli_agent_runner-0.2.8/tests/unit/test_migrations.py +59 -0
  41. cli_agent_runner-0.2.8/tests/unit/test_monitor_schedule_pause.py +103 -0
  42. cli_agent_runner-0.2.8/tests/unit/test_peek_schedule.py +35 -0
  43. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_presets.py +24 -2
  44. cli_agent_runner-0.2.8/tests/unit/test_schedule.py +183 -0
  45. cli_agent_runner-0.2.8/tests/unit/test_serve_schedule_gate.py +143 -0
  46. cli_agent_runner-0.2.8/tests/unit/test_upgrade_migrate.py +91 -0
  47. cli_agent_runner-0.2.6/docs/migrations/0.1.16.md +0 -97
  48. cli_agent_runner-0.2.6/docs/migrations/0.1.17.md +0 -80
  49. cli_agent_runner-0.2.6/docs/migrations/0.1.19.md +0 -61
  50. cli_agent_runner-0.2.6/docs/migrations/0.1.20.md +0 -80
  51. cli_agent_runner-0.2.6/docs/migrations/0.1.21.md +0 -126
  52. cli_agent_runner-0.2.6/docs/migrations/0.1.22.md +0 -93
  53. cli_agent_runner-0.2.6/docs/migrations/0.1.23.md +0 -132
  54. cli_agent_runner-0.2.6/docs/migrations/0.1.24.md +0 -124
  55. cli_agent_runner-0.2.6/docs/migrations/0.1.25.md +0 -96
  56. cli_agent_runner-0.2.6/docs/migrations/0.1.26.md +0 -135
  57. cli_agent_runner-0.2.6/docs/migrations/0.1.27.md +0 -176
  58. cli_agent_runner-0.2.6/docs/migrations/0.1.29.md +0 -90
  59. cli_agent_runner-0.2.6/docs/migrations/0.1.30.md +0 -73
  60. cli_agent_runner-0.2.6/docs/migrations/0.1.31.md +0 -89
  61. cli_agent_runner-0.2.6/docs/migrations/0.1.32.md +0 -113
  62. cli_agent_runner-0.2.6/docs/migrations/0.1.33.md +0 -88
  63. cli_agent_runner-0.2.6/docs/migrations/0.1.34.md +0 -110
  64. cli_agent_runner-0.2.6/docs/migrations/0.1.35.md +0 -98
  65. cli_agent_runner-0.2.6/docs/migrations/0.1.36.md +0 -73
  66. cli_agent_runner-0.2.6/docs/migrations/0.1.37.md +0 -65
  67. cli_agent_runner-0.2.6/docs/migrations/0.1.38.md +0 -53
  68. cli_agent_runner-0.2.6/docs/migrations/0.1.39.md +0 -74
  69. cli_agent_runner-0.2.6/docs/migrations/0.1.40.md +0 -42
  70. cli_agent_runner-0.2.6/docs/migrations/0.1.42.md +0 -58
  71. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/.codecov.yml +0 -0
  72. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  73. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  74. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  75. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  76. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/.github/workflows/ci.yml +0 -0
  77. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/.github/workflows/release.yml +0 -0
  78. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/.gitignore +0 -0
  79. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/BACKLOG.md +0 -0
  80. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/CODE_OF_CONDUCT.md +0 -0
  81. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/CONTRIBUTING.md +0 -0
  82. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/LICENSE +0 -0
  83. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/README.zh.md +0 -0
  84. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/SECURITY.md +0 -0
  85. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/__init__.py +0 -0
  86. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/_redact.py +0 -0
  87. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/_registry.py +0 -0
  88. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/_substrate.py +0 -0
  89. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/_throttle.py +0 -0
  90. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/agent_runtime.py +0 -0
  91. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/__init__.py +0 -0
  92. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/_constants.py +0 -0
  93. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/claude_rate_limit.py +0 -0
  94. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/codewhale.py +0 -0
  95. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/default_dirty_handler.py +0 -0
  96. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/gemini.py +0 -0
  97. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/kimi.py +0 -0
  98. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/pi.py +0 -0
  99. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/cli/__main__.py +0 -0
  100. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/cli/common.py +0 -0
  101. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/cli/events_cmd.py +0 -0
  102. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/cli/init_cmd.py +0 -0
  103. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/cli/install_cmd.py +0 -0
  104. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/cli/monitor_cmd.py +0 -0
  105. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/cli/peek_cmd.py +0 -0
  106. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/cli/round_cmd.py +0 -0
  107. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/cli/service_cmd.py +0 -0
  108. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/context_store.py +0 -0
  109. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/defenses.py +0 -0
  110. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/hooks.py +0 -0
  111. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/http_progress.py +0 -0
  112. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/lifecycle.py +0 -0
  113. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/metrics.py +0 -0
  114. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/presets/__init__.py +0 -0
  115. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/presets/aider.toml +0 -0
  116. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/presets/claude.toml +0 -0
  117. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/presets/gemini.toml +0 -0
  118. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/presets/kimi.toml +0 -0
  119. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/presets/pi.toml +0 -0
  120. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/prompt_loader.py +0 -0
  121. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/remote_relay.py +0 -0
  122. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/round_log.py +0 -0
  123. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/round_view.py +0 -0
  124. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/runner.py +0 -0
  125. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/scaffold.py +0 -0
  126. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/service_unit.py +0 -0
  127. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/startup_check.py +0 -0
  128. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/agent_runner/vcs_state.py +0 -0
  129. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/build.sh +0 -0
  130. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/README.md +0 -0
  131. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/events.md +0 -0
  132. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/migrations/0.1.28.md +0 -0
  133. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/migrations/0.2.0.md +0 -0
  134. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/migrations/0.2.1.md +0 -0
  135. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/migrations/0.2.2.md +0 -0
  136. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/migrations/0.2.3.md +0 -0
  137. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/migrations/0.2.4.md +0 -0
  138. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/migrations/0.2.5.md +0 -0
  139. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/migrations/0.2.6.md +0 -0
  140. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/plugins.md +0 -0
  141. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/quickstart.md +0 -0
  142. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/recipes/kimi.md +0 -0
  143. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/recipes/pi.md +0 -0
  144. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/docs/thesis.md +0 -0
  145. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/pyproject.toml +0 -0
  146. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/__init__.py +0 -0
  147. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/conftest.py +0 -0
  148. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/contract/__init__.py +0 -0
  149. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/contract/test_public_api_surface.py +0 -0
  150. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/e2e/__init__.py +0 -0
  151. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/e2e/conftest.py +0 -0
  152. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/e2e/test_e2e_graceful_stop.py +0 -0
  153. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/e2e/test_e2e_install_systemd.py +0 -0
  154. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/e2e/test_e2e_monitor_remote.py +0 -0
  155. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
  156. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/fixtures/cli-real-output/claude-2.1.143-assistant-tool-use.jsonl +0 -0
  157. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/fixtures/cli-real-output/claude-2.1.143-result-event.jsonl +0 -0
  158. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/fixtures/cli-real-output/gemini-0.42.0-result-event.jsonl +0 -0
  159. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/generate_vulture_whitelist.py +0 -0
  160. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/__init__.py +0 -0
  161. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_bounded_run.py +0 -0
  162. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_context_enricher_namespacing.py +0 -0
  163. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_dirty_handler_seam.py +0 -0
  164. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_fresh_eyes_signal.py +0 -0
  165. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_grace_kill_emission.py +0 -0
  166. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_install_dry_run.py +0 -0
  167. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_monitor_seeded.py +0 -0
  168. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_plugin_detector_loaded.py +0 -0
  169. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_plugin_owned_paths.py +0 -0
  170. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_plugin_real_flow.py +0 -0
  171. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_prompt_delivery_stdin.py +0 -0
  172. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_run_one_round_with_fake_agent.py +0 -0
  173. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_scaffold_presets.py +0 -0
  174. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_serve_loop.py +0 -0
  175. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_substrate_fingerprint.py +0 -0
  176. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/integration/test_transient_error_backoff.py +0 -0
  177. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/__init__.py +0 -0
  178. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/_event_scan.py +0 -0
  179. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_atomic_write_enforced.py +0 -0
  180. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_auto_stop_policy_ssot.py +0 -0
  181. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_catalogs.py +0 -0
  182. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_classification_ssot.py +0 -0
  183. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_config_error_consistency.py +0 -0
  184. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_config_value_set_ssot.py +0 -0
  185. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_doc_builtin_plugins.py +0 -0
  186. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_doc_transient_error_claims.py +0 -0
  187. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_docs_generated.py +0 -0
  188. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_docs_index_complete.py +0 -0
  189. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_event_kind_registry.py +0 -0
  190. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_event_kinds_ssot.py +0 -0
  191. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_events_doc_contract.py +0 -0
  192. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_hook_contract_docs.py +0 -0
  193. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_layer_2_loop_size.py +0 -0
  194. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_module_boundaries.py +0 -0
  195. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_module_sizes.py +0 -0
  196. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_no_ai_signatures.py +0 -0
  197. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
  198. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_peek_schema_version.py +0 -0
  199. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_plugins_config_stable.py +0 -0
  200. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_readme_zh_is_pointer.py +0 -0
  201. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
  202. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_round_result_stable.py +0 -0
  203. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_set_diff_for_auto_tool_classification.py +0 -0
  204. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
  205. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_upstream_schema_canary.py +0 -0
  206. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/invariants/test_vulture_whitelist_generated.py +0 -0
  207. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/literate/__init__.py +0 -0
  208. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/literate/parser.py +0 -0
  209. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/literate/test_parser.py +0 -0
  210. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/literate/test_quickstart.py +0 -0
  211. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/__init__.py +0 -0
  212. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_agent_runtime.py +0 -0
  213. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_agent_runtime_grace.py +0 -0
  214. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_agent_runtime_progress.py +0 -0
  215. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_agent_runtime_signal_name.py +0 -0
  216. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_api_assemble_prompt.py +0 -0
  217. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_api_events_stream.py +0 -0
  218. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_api_install.py +0 -0
  219. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_api_observation.py +0 -0
  220. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_api_read_round_num.py +0 -0
  221. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_api_resolve_phase.py +0 -0
  222. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_api_service.py +0 -0
  223. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_api_types.py +0 -0
  224. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_auto_stop_gating.py +0 -0
  225. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_catalogs.py +0 -0
  226. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_claude_error_detector.py +0 -0
  227. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_cli.py +0 -0
  228. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_cli_common.py +0 -0
  229. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_cli_init_install.py +0 -0
  230. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_cli_monitor_http.py +0 -0
  231. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_cli_service_peek_monitor.py +0 -0
  232. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_cli_upgrade.py +0 -0
  233. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_codewhale_plugin.py +0 -0
  234. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_config_fresh_eyes.py +0 -0
  235. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_config_max_rounds.py +0 -0
  236. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_config_prompt_delivery.py +0 -0
  237. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_config_stop_file.py +0 -0
  238. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
  239. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_config_transient_error_action.py +0 -0
  240. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_context_store.py +0 -0
  241. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_default_dirty_handler.py +0 -0
  242. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_defenses.py +0 -0
  243. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_detector_protocol.py +0 -0
  244. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_dirty_handlers.py +0 -0
  245. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_docgen.py +0 -0
  246. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_events.py +0 -0
  247. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_events_cmd.py +0 -0
  248. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_events_cmd_tail.py +0 -0
  249. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_exit_cause.py +0 -0
  250. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_fresh_eyes_trigger.py +0 -0
  251. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_gemini_plugin.py +0 -0
  252. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_hook_failure_isolation.py +0 -0
  253. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_hooks.py +0 -0
  254. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_http_progress.py +0 -0
  255. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_init_entry_points.py +0 -0
  256. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_kimi_plugin.py +0 -0
  257. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_lifecycle.py +0 -0
  258. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_metrics.py +0 -0
  259. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_monitor_assembly.py +0 -0
  260. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
  261. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
  262. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
  263. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_monitor_detectors.py +0 -0
  264. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_monitor_remote.py +0 -0
  265. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_network_blip_signal_guard.py +0 -0
  266. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_peek_argparse.py +0 -0
  267. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_peek_select.py +0 -0
  268. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_pi_plugin.py +0 -0
  269. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_plugin_constants.py +0 -0
  270. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_prompt_loader.py +0 -0
  271. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_redact.py +0 -0
  272. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_round_log_helpers.py +0 -0
  273. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_round_view.py +0 -0
  274. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_runner.py +0 -0
  275. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_runner_throttle.py +0 -0
  276. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_scaffold.py +0 -0
  277. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_serve_cmd_bounded.py +0 -0
  278. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_serve_config_broken.py +0 -0
  279. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_serve_crash_loop.py +0 -0
  280. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_serve_round_log.py +0 -0
  281. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_serve_sentinel.py +0 -0
  282. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_serve_startup_hooks.py +0 -0
  283. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_service_unit.py +0 -0
  284. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_startup_check.py +0 -0
  285. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_substrate.py +0 -0
  286. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_throttle_plugin_classification.py +0 -0
  287. {cli_agent_runner-0.2.6 → cli_agent_runner-0.2.8}/tests/unit/test_vcs_state.py +0 -0
@@ -50,6 +50,7 @@ service
50
50
  recent_events
51
51
  recent_hook_failures
52
52
  recent_blips
53
+ schedule
53
54
 
54
55
  # agent_runner.api_types.RateLimitState
55
56
  throttled_until_epoch
@@ -121,6 +122,7 @@ vcs
121
122
  monitor
122
123
  phases
123
124
  plugins
125
+ schedule
124
126
 
125
127
  # agent_runner.config.MonitorConfig
126
128
  auth_fail_patterns
@@ -176,6 +178,11 @@ dry_run
176
178
  max_grace_after_result_s
177
179
  grace_kill_ignore_patterns
178
180
 
181
+ # agent_runner.config.ScheduleConfig
182
+ timezone
183
+ run_windows
184
+ pause_windows
185
+
179
186
  # agent_runner.config.VcsConfig
180
187
  stash_idempotency_s
181
188
  dirty_action
@@ -225,6 +232,16 @@ stash_idempotency_s
225
232
  # agent_runner.lifecycle.PIDFile
226
233
  path
227
234
 
235
+ # agent_runner.migrations.Migration
236
+ detect
237
+ apply
238
+ describe
239
+
240
+ # agent_runner.migrations.MigrationResult
241
+ new_text
242
+ applied
243
+ manual
244
+
228
245
  # agent_runner.monitor.LocalSource
229
246
  log_dir
230
247
 
@@ -233,6 +250,17 @@ deleted
233
250
  deferred
234
251
  existing
235
252
 
253
+ # agent_runner.schedule.PauseDecision
254
+ paused
255
+ resume_at
256
+ active_window
257
+
258
+ # agent_runner.schedule.Window
259
+ start_min
260
+ end_min
261
+ label
262
+ days
263
+
236
264
  # agent_runner.startup_check.CheckResult
237
265
  name
238
266
  ok
@@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.8] - 2026-08-25
9
+
10
+ ### Added
11
+ - `agent-runner migrate [--dry-run]` rewrites removed/renamed fields in `agent-runner.toml` to their current form, keeping a `.bak`; reports fields needing a manual change.
12
+ - `upgrade` auto-migrates the config before the package/service step and emits a `config_migrated` event; a config needing a manual change aborts the run before it stops the service. `--no-migrate` skips it.
13
+
14
+ ### Changed
15
+ - Removed-field `ConfigError` messages now point to `agent-runner migrate`.
16
+
17
+ See `docs/migrations/0.2.8.md`.
18
+
19
+ ## [0.2.7] - 2026-08-25
20
+
21
+ ### Added
22
+ - `[schedule]` config gates the serve loop between rounds: `pause_windows` / `run_windows` decide whether the next round launches now or the supervisor idle-sleeps until a window opens, then auto-resumes. Windows are `[WEEKDAYS ]HH:MM-HH:MM` (24h, end-exclusive, midnight-wrap; optional `Mon-Fri` / `Sat,Sun` weekday prefix, bare = every day). Timezone-aware (`timezone`, IANA; omit → host local). Evaluation is `run AND NOT pause`. Absent section → unchanged 7×24 behavior. An in-flight round is never interrupted; `agent-runner round` is never gated.
23
+ - `schedule_paused` / `schedule_resumed` events; `peek` surfaces the current pause state (`schedule` field).
24
+ - `serve --ignore-schedule` runs rounds regardless of the windows (catch-up / testing).
25
+
26
+ ### Fixed
27
+ - Monitor no longer raises `supervisor_stale` during an intentional schedule pause (suppressed until `resume_at` plus one staleness window).
28
+
29
+ See `docs/migrations/0.2.7.md`.
30
+
8
31
  ## [0.2.6] - 2026-07-28
9
32
 
10
33
  ### Changed
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: cli-agent-runner
3
- Version: 0.2.6
3
+ Version: 0.2.8
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
@@ -51,7 +51,7 @@ full disks, runaway memory.
51
51
  ┌──────────────────────────────────────────┐
52
52
  │ Layer 3: The Witness (monitor) │ 11 detectors + auto-stop
53
53
  ├──────────────────────────────────────────┤
54
- │ Layer 2: The Loop (serve, ~120 LOC) │ signal-trapping restart loop
54
+ │ Layer 2: The Loop (serve) │ signal-trapping restart loop
55
55
  ├──────────────────────────────────────────┤
56
56
  │ Layer 1: The Round (round) │ one agent invocation
57
57
  └──────────────────────────────────────────┘
@@ -80,14 +80,15 @@ agent-runner monitor # live anomaly detection
80
80
 
81
81
  Full walkthrough: [`docs/quickstart.md`](docs/quickstart.md).
82
82
 
83
- ## 15 verbs
83
+ ## 16 verbs
84
84
 
85
85
  | Lifecycle | Observation |
86
86
  |---|---|
87
87
  | `init` / `install` / `uninstall` | `peek` — state snapshot |
88
88
  | `start` / `stop` / `kill` | `watch` — peek in a refresh loop |
89
89
  | `restart` / `status` | `monitor` — 11 detectors, alerts, auto-stop |
90
- | `round` / `serve` / `upgrade` | `events` — query / stream events.jsonl |
90
+ | `round` / `serve` | `events` — query / stream events.jsonl |
91
+ | `upgrade` / `migrate` | |
91
92
 
92
93
  Verb reference: [`docs/commands.md`](docs/commands.md).
93
94
 
@@ -106,6 +107,10 @@ guards it. Highlights:
106
107
 
107
108
  Full list and rationale: [`docs/architecture.md`](docs/architecture.md).
108
109
 
110
+ Optionally pause the loop during off-hours: `[schedule]` run/pause windows gate
111
+ `serve` (override with `serve --ignore-schedule`) — see the `[schedule]` section
112
+ of [`docs/configuration.md`](docs/configuration.md).
113
+
109
114
  ## Monitor: 11 detectors
110
115
 
111
116
  Notify only: `timeout_rate`, `hung`, `orphan_chain`, `disk_warning`,
@@ -15,7 +15,7 @@ full disks, runaway memory.
15
15
  ┌──────────────────────────────────────────┐
16
16
  │ Layer 3: The Witness (monitor) │ 11 detectors + auto-stop
17
17
  ├──────────────────────────────────────────┤
18
- │ Layer 2: The Loop (serve, ~120 LOC) │ signal-trapping restart loop
18
+ │ Layer 2: The Loop (serve) │ signal-trapping restart loop
19
19
  ├──────────────────────────────────────────┤
20
20
  │ Layer 1: The Round (round) │ one agent invocation
21
21
  └──────────────────────────────────────────┘
@@ -44,14 +44,15 @@ agent-runner monitor # live anomaly detection
44
44
 
45
45
  Full walkthrough: [`docs/quickstart.md`](docs/quickstart.md).
46
46
 
47
- ## 15 verbs
47
+ ## 16 verbs
48
48
 
49
49
  | Lifecycle | Observation |
50
50
  |---|---|
51
51
  | `init` / `install` / `uninstall` | `peek` — state snapshot |
52
52
  | `start` / `stop` / `kill` | `watch` — peek in a refresh loop |
53
53
  | `restart` / `status` | `monitor` — 11 detectors, alerts, auto-stop |
54
- | `round` / `serve` / `upgrade` | `events` — query / stream events.jsonl |
54
+ | `round` / `serve` | `events` — query / stream events.jsonl |
55
+ | `upgrade` / `migrate` | |
55
56
 
56
57
  Verb reference: [`docs/commands.md`](docs/commands.md).
57
58
 
@@ -70,6 +71,10 @@ guards it. Highlights:
70
71
 
71
72
  Full list and rationale: [`docs/architecture.md`](docs/architecture.md).
72
73
 
74
+ Optionally pause the loop during off-hours: `[schedule]` run/pause windows gate
75
+ `serve` (override with `serve --ignore-schedule`) — see the `[schedule]` section
76
+ of [`docs/configuration.md`](docs/configuration.md).
77
+
73
78
  ## Monitor: 11 detectors
74
79
 
75
80
  Notify only: `timeout_rate`, `hung`, `orphan_chain`, `disk_warning`,
@@ -7,12 +7,16 @@ renderer registry so the substitution rule is testable in isolation.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ import argparse
10
11
  import dataclasses
12
+ import functools
13
+ import importlib.resources
11
14
  import re
12
15
  import typing
13
16
  from collections.abc import Callable
14
17
  from pathlib import Path
15
18
 
19
+ from agent_runner.cli.init_cmd import _preset_names
16
20
  from agent_runner.config import (
17
21
  AgentConfig,
18
22
  Config,
@@ -22,6 +26,7 @@ from agent_runner.config import (
22
26
  PluginsConfig,
23
27
  PromptConfig,
24
28
  RuntimeConfig,
29
+ ScheduleConfig,
25
30
  VcsConfig,
26
31
  )
27
32
  from agent_runner.defenses import catalog
@@ -45,6 +50,7 @@ _SECTIONS: list[tuple[str, type, list[tuple[str, type]]]] = [
45
50
  ("monitor", MonitorConfig, [("monitor.host_health", MonitorHostHealthConfig)]),
46
51
  ("phases", PhasesConfig, []),
47
52
  ("plugins", PluginsConfig, []),
53
+ ("schedule", ScheduleConfig, []),
48
54
  ]
49
55
 
50
56
 
@@ -150,18 +156,53 @@ def render_event_kinds_list() -> str:
150
156
  return "\n".join(f"- `{k}`" for k in sorted(KNOWN_EVENT_KINDS))
151
157
 
152
158
 
153
- def render_verb_table() -> str:
154
- """Walk the argparse subparsers and render a verb table."""
159
+ def _subparser_action() -> argparse.Action:
160
+ """The single ``_SubParsersAction`` on the top-level parser."""
155
161
  from agent_runner.cli import _build_parser
156
162
 
157
163
  parser = _build_parser()
158
- # Find the sub-parsers action there's exactly one.
159
- sub_action = next(a for a in parser._actions if a.__class__.__name__ == "_SubParsersAction")
164
+ return next(a for a in parser._actions if a.__class__.__name__ == "_SubParsersAction")
165
+
166
+
167
+ def _subparser_choices() -> dict[str, argparse.ArgumentParser]:
168
+ """Map ``verb -> subparser`` for the CLI, the SSOT both verb renderers use."""
169
+ return _subparser_action().choices
170
+
171
+
172
+ def _render_verb_flags(verb: str) -> str:
173
+ """Bullet list of a verb's own option flags, from its argparse subparser.
174
+
175
+ Parent-inherited ``--config`` / ``--json`` carry ``help=SUPPRESS`` on the
176
+ shared parent and are filtered here, so only verb-specific flags render.
177
+ """
178
+ choices = _subparser_choices()
179
+ lines: list[str] = []
180
+ for act in choices[verb]._actions:
181
+ if not act.option_strings: # positionals
182
+ continue
183
+ if isinstance(act, argparse._HelpAction): # -h/--help
184
+ continue
185
+ if act.help is argparse.SUPPRESS: # parent --config/--json
186
+ continue
187
+ name = max(act.option_strings, key=len)
188
+ if act.choices:
189
+ val = " {" + ",".join(map(str, act.choices)) + "}"
190
+ elif act.metavar:
191
+ val = f" {act.metavar}"
192
+ else:
193
+ val = ""
194
+ lines.append(f"- `{name}{val}` — {act.help}")
195
+ return "\n".join(lines)
196
+
197
+
198
+ def render_verb_table() -> str:
199
+ """Walk the argparse subparsers and render a verb table."""
200
+ sub_action = _subparser_action()
160
201
  rows = [
161
202
  "| Verb | Description |",
162
203
  "|---|---|",
163
204
  ]
164
- for verb, _sp in sub_action.choices.items():
205
+ for verb in sub_action.choices:
165
206
  # Argparse stores help text via `sub_action._choices_actions` indexed by add order.
166
207
  help_text = (
167
208
  next(
@@ -174,6 +215,18 @@ def render_verb_table() -> str:
174
215
  return "\n".join(rows)
175
216
 
176
217
 
218
+ def _render_preset(name: str) -> str:
219
+ """Fenced TOML block quoting a shipped ``agent_runner/presets/<name>.toml``.
220
+
221
+ The preset file is the SSOT; the recipe doc block is a verbatim mirror so a
222
+ flag or auth-hint edit in the preset can never drift from its recipe.
223
+ """
224
+ body = (importlib.resources.files("agent_runner.presets") / f"{name}.toml").read_text(
225
+ encoding="utf-8"
226
+ )
227
+ return "```toml\n" + body.rstrip() + "\n```"
228
+
229
+
177
230
  RENDERERS: dict[str, Callable[[], str]] = {
178
231
  "defenses-table": render_defenses_table,
179
232
  "detector-list": render_detector_list,
@@ -181,6 +234,13 @@ RENDERERS: dict[str, Callable[[], str]] = {
181
234
  "config-schema": render_config_schema_table,
182
235
  "verb-table": render_verb_table,
183
236
  }
237
+ RENDERERS.update(
238
+ {
239
+ f"flags-{v}": functools.partial(_render_verb_flags, v)
240
+ for v in ("init", "monitor", "serve", "migrate")
241
+ }
242
+ )
243
+ RENDERERS.update({f"preset-{n}": functools.partial(_render_preset, n) for n in _preset_names()})
184
244
 
185
245
  _GEN_OPEN = re.compile(r"<!-- gen:([a-z0-9-]+) -->")
186
246
 
@@ -194,7 +254,9 @@ def render(docs_dir: Path, *, write: bool = True) -> dict[Path, str]:
194
254
  Raises ``ValueError`` when a marker references an unknown renderer name.
195
255
  """
196
256
  out: dict[Path, str] = {}
197
- for md in sorted(docs_dir.glob("*.md")):
257
+ # Top-level docs plus recipes/. NOT rglob: migrations/ is frozen history and
258
+ # internal/ is gitignored — neither should be regenerated.
259
+ for md in sorted(docs_dir.glob("*.md")) + sorted((docs_dir / "recipes").glob("*.md")):
198
260
  text = md.read_text(encoding="utf-8")
199
261
  for match in _GEN_OPEN.finditer(text):
200
262
  name = match.group(1)
@@ -17,6 +17,7 @@ __all__ = [
17
17
  "emit_agent_auth_error_detected",
18
18
  "emit_agent_usage_recorded",
19
19
  "emit_anomaly_repetitive_tool",
20
+ "emit_config_migrated",
20
21
  "emit_fresh_eyes_round_triggered",
21
22
  "emit_max_rounds_reached",
22
23
  "emit_rate_limit_stop",
@@ -26,6 +27,8 @@ __all__ = [
26
27
  "emit_round_progress",
27
28
  "emit_round_substrate_after",
28
29
  "emit_round_substrate_before",
30
+ "emit_schedule_paused",
31
+ "emit_schedule_resumed",
29
32
  "emit_stop_file_detected",
30
33
  "emit_transient_error_backoff_capped",
31
34
  "emit_transient_error_detected",
@@ -70,6 +73,15 @@ def emit_crash_loop(log_dir: Path, *, consecutive: int, exit_code: int, log_path
70
73
  emit(log_dir, CRASH_LOOP, consecutive=consecutive, exit_code=exit_code, reason=reason)
71
74
 
72
75
 
76
+ def emit_config_migrated(
77
+ log_dir: Path, *, applied: list[str], manual: list[str], path: str
78
+ ) -> None:
79
+ """Emit config_migrated when `migrate`/`upgrade` rewrites the config."""
80
+ from agent_runner.events import CONFIG_MIGRATED, emit
81
+
82
+ emit(log_dir, CONFIG_MIGRATED, applied=applied, manual=manual, path=path)
83
+
84
+
73
85
  def emit_stop_file_detected(
74
86
  log_dir: Path, *, stop_file: Path, content: str, rounds_completed: int
75
87
  ) -> None:
@@ -85,6 +97,28 @@ def emit_stop_file_detected(
85
97
  )
86
98
 
87
99
 
100
+ def emit_schedule_paused(
101
+ log_dir: Path, *, active_window: str, resume_at: str, timezone: str
102
+ ) -> None:
103
+ """Emit schedule_paused when the serve loop enters a configured pause window."""
104
+ from agent_runner.events import SCHEDULE_PAUSED, emit
105
+
106
+ emit(
107
+ log_dir,
108
+ SCHEDULE_PAUSED,
109
+ active_window=active_window,
110
+ resume_at=resume_at,
111
+ timezone=timezone,
112
+ )
113
+
114
+
115
+ def emit_schedule_resumed(log_dir: Path, *, paused_for_s: int) -> None:
116
+ """Emit schedule_resumed when the serve loop exits a pause window."""
117
+ from agent_runner.events import SCHEDULE_RESUMED, emit
118
+
119
+ emit(log_dir, SCHEDULE_RESUMED, paused_for_s=paused_for_s)
120
+
121
+
88
122
  def emit_round_substrate_before(
89
123
  log_dir: Path, *, round_num: int, git_head: str | None, paths_hash: str | None
90
124
  ) -> None:
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.2.6'
22
- __version_tuple__ = version_tuple = (0, 2, 6)
21
+ __version__ = version = '0.2.8'
22
+ __version_tuple__ = version_tuple = (0, 2, 8)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -473,6 +473,7 @@ def peek(
473
473
  recent_events=recent,
474
474
  recent_hook_failures=recent_hook_failures,
475
475
  recent_blips=recent_blips,
476
+ schedule=monitor.latest_schedule_state(parsed_events),
476
477
  )
477
478
  return state if select is None else select_path(state, select)
478
479
 
@@ -736,6 +737,7 @@ from agent_runner._emit import ( # noqa: E402,F401 — intentional bottom re-ex
736
737
  emit_agent_usage_recorded,
737
738
  emit_anomaly_repetitive_tool,
738
739
  emit_config_broken,
740
+ emit_config_migrated,
739
741
  emit_crash_loop,
740
742
  emit_fresh_eyes_round_triggered,
741
743
  emit_max_rounds_reached,
@@ -746,6 +748,8 @@ from agent_runner._emit import ( # noqa: E402,F401 — intentional bottom re-ex
746
748
  emit_round_progress,
747
749
  emit_round_substrate_after,
748
750
  emit_round_substrate_before,
751
+ emit_schedule_paused,
752
+ emit_schedule_resumed,
749
753
  emit_stop_file_detected,
750
754
  emit_transient_error_backoff_capped,
751
755
  emit_transient_error_detected,
@@ -98,6 +98,7 @@ class ProjectState:
98
98
  recent_events: list[dict[str, Any]] = field(default_factory=list)
99
99
  recent_hook_failures: list[dict[str, Any]] = field(default_factory=list)
100
100
  recent_blips: list[dict[str, Any]] = field(default_factory=list)
101
+ schedule: dict | None = None
101
102
 
102
103
 
103
104
  @dataclass(frozen=True)
@@ -20,6 +20,7 @@ from agent_runner.cli import (
20
20
  events_cmd,
21
21
  init_cmd,
22
22
  install_cmd,
23
+ migrate_cmd,
23
24
  monitor_cmd,
24
25
  peek_cmd,
25
26
  round_cmd,
@@ -69,6 +70,7 @@ def _build_parser() -> argparse.ArgumentParser:
69
70
  serve_cmd.add_parser(sub, parent)
70
71
  round_cmd.add_parser(sub, parent)
71
72
  upgrade_cmd.add_parser(sub, parent)
73
+ migrate_cmd.add_parser(sub, parent)
72
74
 
73
75
  return parser
74
76
 
@@ -0,0 +1,51 @@
1
+ """agent-runner migrate — rewrite removed/renamed config fields to current form."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import tomllib
6
+ from pathlib import Path
7
+
8
+ from agent_runner import migrations
9
+ from agent_runner.cli.common import fail # `fail(msg, *, code=1)` prints to stderr, returns code
10
+
11
+
12
+ def add_parser(sub, parent) -> None:
13
+ p = sub.add_parser(
14
+ "migrate",
15
+ parents=[parent],
16
+ help="Rewrite removed/renamed fields in agent-runner.toml to current form",
17
+ )
18
+ p.add_argument("--dry-run", action="store_true", help="Show changes without writing the file")
19
+ p.set_defaults(func=cmd)
20
+
21
+
22
+ def cmd(args) -> int:
23
+ cfg_path = Path(getattr(args, "config", None) or "agent-runner.toml")
24
+ try:
25
+ text = cfg_path.read_text(encoding="utf-8")
26
+ except OSError as e:
27
+ return fail(f"cannot read {cfg_path}: {e}", code=2)
28
+ try:
29
+ parsed = tomllib.loads(text)
30
+ except tomllib.TOMLDecodeError as e:
31
+ return fail(f"{cfg_path} is not valid TOML: {e}", code=2)
32
+
33
+ result = migrations.run_migrations(text, parsed)
34
+ if not result.applied and not result.manual:
35
+ print(f"{cfg_path}: nothing to migrate")
36
+ return 0
37
+
38
+ for line in result.applied:
39
+ print(f" rewrite: {line}")
40
+ for line in result.manual:
41
+ print(f" MANUAL: {line}")
42
+
43
+ if args.dry_run:
44
+ print("(dry-run — no changes written)")
45
+ return 1 if result.manual else 0
46
+
47
+ if result.applied:
48
+ cfg_path.with_suffix(cfg_path.suffix + ".bak").write_text(text, encoding="utf-8")
49
+ cfg_path.write_text(result.new_text, encoding="utf-8")
50
+ print(f"wrote {cfg_path} (backup: {cfg_path.name}.bak)")
51
+ return 1 if result.manual else 0
@@ -17,6 +17,7 @@ import sys
17
17
  import time
18
18
  from pathlib import Path
19
19
 
20
+ from agent_runner import schedule
20
21
  from agent_runner._substrate import compute_git_head, compute_paths_hash
21
22
  from agent_runner._throttle import _check_throttle_state
22
23
  from agent_runner._throttle import reset_counters as _reset_counters
@@ -30,6 +31,8 @@ from agent_runner.api import (
30
31
  emit_round_logs_prune_deferred,
31
32
  emit_round_substrate_after,
32
33
  emit_round_substrate_before,
34
+ emit_schedule_paused,
35
+ emit_schedule_resumed,
33
36
  emit_stop_file_detected,
34
37
  post_round_decision,
35
38
  )
@@ -58,6 +61,68 @@ def _resolve_max_rounds(*, cli_value: int | None, config_value: int | None) -> i
58
61
  return effective
59
62
 
60
63
 
64
+ def _maybe_pause_for_schedule(
65
+ cfg,
66
+ log_dir,
67
+ stop,
68
+ *,
69
+ now_fn=schedule.now_in_zone,
70
+ sleep_fn=time.sleep,
71
+ chunk_s: int = 30,
72
+ ) -> bool:
73
+ """If the current time is outside the run schedule, pause until it opens.
74
+
75
+ Returns True if a pause was entered (caller should ``continue`` so the
76
+ top-of-loop guards re-run), False if runnable now. During a pause, sleeps
77
+ in <= chunk_s slices so SIGTERM / SIGINT (which set stop["requested"]) lands
78
+ within one slice.
79
+
80
+ We do NOT check the self-terminate sentinel here: no round runs during a
81
+ pause, so no new sentinel can appear, and any pre-existing one already broke
82
+ the loop at its top before this gate. schedule_resumed is emitted ONLY when
83
+ the window actually opens — an interrupted pause (stop) is a termination, not
84
+ a resume, and correctly leaves schedule_paused as the newest event."""
85
+ sched = cfg.schedule
86
+ if not sched.enabled:
87
+ return False
88
+ decision = schedule.evaluate(
89
+ run_windows=sched.run_windows,
90
+ pause_windows=sched.pause_windows,
91
+ now_local=now_fn(sched.timezone),
92
+ )
93
+ if not decision.paused:
94
+ return False
95
+
96
+ started = time.monotonic()
97
+ emit_schedule_paused(
98
+ log_dir,
99
+ active_window=decision.active_window or "",
100
+ resume_at=decision.resume_at.isoformat() if decision.resume_at else "",
101
+ timezone=sched.timezone or "local",
102
+ )
103
+ stop_file = cfg.runtime.stop_file
104
+ window_opened = False
105
+ while not stop["requested"]:
106
+ if stop_file is not None and stop_file.exists():
107
+ # Operator stop_file dropped mid-pause: break without emitting
108
+ # schedule_resumed (the window did not open). The serve loop's
109
+ # top-of-loop stop_file check then emits stop_file_detected and exits.
110
+ break
111
+ # #3: should_run (not evaluate) avoids the 8-day next_resume_at scan on
112
+ # every poll; should_run is the negation of the pre-loop paused decision.
113
+ if schedule.should_run(
114
+ now_fn(sched.timezone),
115
+ run_windows=sched.run_windows,
116
+ pause_windows=sched.pause_windows,
117
+ ):
118
+ window_opened = True
119
+ break
120
+ sleep_fn(chunk_s)
121
+ if window_opened:
122
+ emit_schedule_resumed(log_dir, paused_for_s=int(time.monotonic() - started))
123
+ return True
124
+
125
+
61
126
  def _prune_serve_round_logs(log_dir: Path, retention: int) -> None:
62
127
  """Prune the serve-level ``round-<N>.log`` family; report a deferred prune.
63
128
 
@@ -101,6 +166,11 @@ def add_parser(sub, parent) -> None:
101
166
  p = sub.add_parser("serve", parents=[parent], help="Long-running supervisor loop")
102
167
  p.add_argument("--once", action="store_true", help="Run a single round then exit (debug)")
103
168
  _add_max_rounds_arg(p)
169
+ p.add_argument(
170
+ "--ignore-schedule",
171
+ action="store_true",
172
+ help="Run rounds regardless of [schedule] pause/run windows (testing / catch-up)",
173
+ )
104
174
  p.set_defaults(func=cmd)
105
175
 
106
176
 
@@ -177,6 +247,8 @@ def cmd(args) -> int:
177
247
  max_rounds=effective_max_rounds,
178
248
  )
179
249
  break
250
+ if not args.ignore_schedule and _maybe_pause_for_schedule(cfg, log_dir, stop):
251
+ continue
180
252
  round_num = next_round_num(log_dir)
181
253
  git_head_before = compute_git_head(work_dir)
182
254
  paths_hash_before = compute_paths_hash(