cli-agent-runner 0.2.2__tar.gz → 0.2.4__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 (270) hide show
  1. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/CHANGELOG.md +48 -15
  2. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/PKG-INFO +11 -11
  3. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/README.md +10 -10
  4. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/_emit.py +37 -1
  5. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/_version.py +2 -2
  6. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/agent_runtime.py +39 -1
  7. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/api.py +21 -1
  8. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/api_types.py +9 -0
  9. cli_agent_runner-0.2.4/agent_runner/builtin_plugins/_constants.py +125 -0
  10. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/builtin_plugins/claude_rate_limit.py +11 -27
  11. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/builtin_plugins/codewhale.py +16 -36
  12. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/builtin_plugins/gemini.py +6 -28
  13. cli_agent_runner-0.2.4/agent_runner/builtin_plugins/kimi.py +93 -0
  14. cli_agent_runner-0.2.4/agent_runner/builtin_plugins/pi.py +279 -0
  15. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/cli/monitor_cmd.py +5 -1
  16. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/config.py +7 -1
  17. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/events.py +1 -0
  18. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/hooks.py +10 -6
  19. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/monitor.py +82 -11
  20. cli_agent_runner-0.2.4/agent_runner/presets/kimi.toml +38 -0
  21. cli_agent_runner-0.2.4/agent_runner/presets/pi.toml +41 -0
  22. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/round_log.py +30 -0
  23. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/runner.py +7 -2
  24. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/service_unit.py +7 -1
  25. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/startup_check.py +19 -4
  26. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/README.md +4 -2
  27. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/architecture.md +10 -6
  28. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/commands.md +8 -7
  29. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/configuration.md +8 -1
  30. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/long-running-agents.md +6 -4
  31. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.20.md +10 -0
  32. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.23.md +1 -1
  33. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.25.md +1 -1
  34. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.29.md +1 -1
  35. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.30.md +1 -1
  36. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.35.md +1 -1
  37. cli_agent_runner-0.2.4/docs/migrations/0.2.3.md +44 -0
  38. cli_agent_runner-0.2.4/docs/migrations/0.2.4.md +66 -0
  39. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/plugins.md +81 -8
  40. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/quickstart.md +1 -1
  41. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/recipes/aider.md +5 -3
  42. cli_agent_runner-0.2.4/docs/recipes/kimi.md +119 -0
  43. cli_agent_runner-0.2.4/docs/recipes/pi.md +113 -0
  44. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/runbook.md +63 -22
  45. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/thesis.md +3 -2
  46. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/pyproject.toml +2 -0
  47. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/_test_helpers.py +33 -5
  48. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_prompt_delivery_stdin.py +4 -0
  49. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_run_one_round_with_fake_agent.py +2 -0
  50. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_scaffold_presets.py +3 -1
  51. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_doc_claims_match_ssot.py +6 -1
  52. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_agent_runtime.py +47 -0
  53. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_agent_runtime_grace.py +6 -0
  54. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_agent_runtime_progress.py +3 -0
  55. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_api_observation.py +47 -6
  56. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_claude_error_detector.py +52 -21
  57. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_cli_service_peek_monitor.py +27 -0
  58. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_codewhale_plugin.py +14 -12
  59. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_config.py +59 -0
  60. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_events.py +39 -0
  61. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_gemini_plugin.py +12 -12
  62. cli_agent_runner-0.2.4/tests/unit/test_kimi_plugin.py +147 -0
  63. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_monitor_detectors.py +57 -0
  64. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_monitor_remote.py +9 -0
  65. cli_agent_runner-0.2.4/tests/unit/test_pi_plugin.py +449 -0
  66. cli_agent_runner-0.2.4/tests/unit/test_plugin_constants.py +61 -0
  67. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_presets.py +12 -1
  68. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_round_log_helpers.py +60 -0
  69. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_service_unit.py +9 -0
  70. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_startup_check.py +29 -0
  71. cli_agent_runner-0.2.2/agent_runner/builtin_plugins/_constants.py +0 -56
  72. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/.codecov.yml +0 -0
  73. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  74. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  75. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  76. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  77. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/.github/workflows/ci.yml +0 -0
  78. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/.github/workflows/release.yml +0 -0
  79. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/.gitignore +0 -0
  80. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/.vulture-whitelist.py +0 -0
  81. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/BACKLOG.md +0 -0
  82. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/CODE_OF_CONDUCT.md +0 -0
  83. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/CONTRIBUTING.md +0 -0
  84. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/LICENSE +0 -0
  85. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/README.zh.md +0 -0
  86. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/SECURITY.md +0 -0
  87. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/__init__.py +0 -0
  88. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/_docgen.py +0 -0
  89. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/_redact.py +0 -0
  90. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/_registry.py +0 -0
  91. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/_substrate.py +0 -0
  92. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/_throttle.py +0 -0
  93. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/builtin_plugins/__init__.py +0 -0
  94. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/builtin_plugins/default_dirty_handler.py +0 -0
  95. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/cli/__init__.py +0 -0
  96. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/cli/__main__.py +0 -0
  97. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/cli/common.py +0 -0
  98. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/cli/events_cmd.py +0 -0
  99. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/cli/init_cmd.py +0 -0
  100. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/cli/install_cmd.py +0 -0
  101. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/cli/peek_cmd.py +0 -0
  102. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/cli/round_cmd.py +0 -0
  103. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/cli/serve_cmd.py +0 -0
  104. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/cli/service_cmd.py +0 -0
  105. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/cli/upgrade_cmd.py +0 -0
  106. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/context_store.py +0 -0
  107. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/defenses.py +0 -0
  108. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/http_progress.py +0 -0
  109. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/lifecycle.py +0 -0
  110. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/metrics.py +0 -0
  111. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/presets/__init__.py +0 -0
  112. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/presets/aider.toml +0 -0
  113. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/presets/claude.toml +0 -0
  114. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/presets/codewhale.toml +0 -0
  115. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/presets/gemini.toml +0 -0
  116. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/prompt_loader.py +0 -0
  117. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/round_view.py +0 -0
  118. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/scaffold.py +0 -0
  119. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/agent_runner/vcs_state.py +0 -0
  120. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/build.sh +0 -0
  121. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/events.md +0 -0
  122. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.16.md +0 -0
  123. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.17.md +0 -0
  124. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.19.md +0 -0
  125. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.21.md +0 -0
  126. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.22.md +0 -0
  127. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.24.md +0 -0
  128. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.26.md +0 -0
  129. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.27.md +0 -0
  130. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.28.md +0 -0
  131. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.31.md +0 -0
  132. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.32.md +0 -0
  133. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.33.md +0 -0
  134. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.34.md +0 -0
  135. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.36.md +0 -0
  136. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.37.md +0 -0
  137. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.38.md +0 -0
  138. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.39.md +0 -0
  139. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.40.md +0 -0
  140. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.1.42.md +0 -0
  141. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.2.0.md +0 -0
  142. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.2.1.md +0 -0
  143. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/migrations/0.2.2.md +0 -0
  144. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/docs/recipes/codewhale.md +0 -0
  145. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/__init__.py +0 -0
  146. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/conftest.py +0 -0
  147. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/contract/__init__.py +0 -0
  148. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/contract/test_public_api_surface.py +0 -0
  149. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/e2e/__init__.py +0 -0
  150. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/e2e/conftest.py +0 -0
  151. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/e2e/test_e2e_graceful_stop.py +0 -0
  152. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/e2e/test_e2e_install_systemd.py +0 -0
  153. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/e2e/test_e2e_monitor_remote.py +0 -0
  154. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
  155. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/fixtures/cli-real-output/claude-2.1.143-assistant-tool-use.jsonl +0 -0
  156. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/fixtures/cli-real-output/claude-2.1.143-result-event.jsonl +0 -0
  157. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/fixtures/cli-real-output/gemini-0.42.0-result-event.jsonl +0 -0
  158. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/generate_vulture_whitelist.py +0 -0
  159. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/__init__.py +0 -0
  160. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_bounded_run.py +0 -0
  161. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_context_enricher_namespacing.py +0 -0
  162. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_dirty_handler_seam.py +0 -0
  163. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_fresh_eyes_signal.py +0 -0
  164. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_grace_kill_emission.py +0 -0
  165. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_install_dry_run.py +0 -0
  166. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_monitor_seeded.py +0 -0
  167. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_plugin_detector_loaded.py +0 -0
  168. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_plugin_owned_paths.py +0 -0
  169. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_plugin_real_flow.py +0 -0
  170. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_serve_loop.py +0 -0
  171. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_substrate_fingerprint.py +0 -0
  172. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/integration/test_transient_error_backoff.py +0 -0
  173. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/__init__.py +0 -0
  174. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/_event_scan.py +0 -0
  175. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_architecture.py +0 -0
  176. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_atomic_write_enforced.py +0 -0
  177. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_auto_stop_policy_ssot.py +0 -0
  178. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_catalogs.py +0 -0
  179. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_classification_ssot.py +0 -0
  180. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_config_error_consistency.py +0 -0
  181. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_config_value_set_ssot.py +0 -0
  182. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_doc_builtin_plugins.py +0 -0
  183. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_doc_cli_claims.py +0 -0
  184. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_doc_operator_surface.py +0 -0
  185. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_doc_transient_error_claims.py +0 -0
  186. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_docs_generated.py +0 -0
  187. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_docs_index_complete.py +0 -0
  188. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_entry_points_resolve.py +0 -0
  189. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_event_kind_registry.py +0 -0
  190. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_event_kinds_ssot.py +0 -0
  191. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_events_doc_contract.py +0 -0
  192. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_hook_contract_docs.py +0 -0
  193. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_layer_2_loop_size.py +0 -0
  194. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_module_boundaries.py +0 -0
  195. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_module_sizes.py +0 -0
  196. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_no_ai_signatures.py +0 -0
  197. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
  198. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_peek_schema_version.py +0 -0
  199. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_plugins_config_stable.py +0 -0
  200. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_readme_zh_is_pointer.py +0 -0
  201. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
  202. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_round_result_stable.py +0 -0
  203. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_set_diff_for_auto_tool_classification.py +0 -0
  204. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
  205. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_upstream_schema_canary.py +0 -0
  206. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/invariants/test_vulture_whitelist_generated.py +0 -0
  207. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/literate/__init__.py +0 -0
  208. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/literate/parser.py +0 -0
  209. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/literate/test_parser.py +0 -0
  210. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/literate/test_quickstart.py +0 -0
  211. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/__init__.py +0 -0
  212. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_agent_runtime_signal_name.py +0 -0
  213. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_api_assemble_prompt.py +0 -0
  214. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_api_events_stream.py +0 -0
  215. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_api_install.py +0 -0
  216. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_api_read_round_num.py +0 -0
  217. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_api_resolve_phase.py +0 -0
  218. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_api_service.py +0 -0
  219. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_api_types.py +0 -0
  220. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_auto_stop_gating.py +0 -0
  221. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_catalogs.py +0 -0
  222. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_cli.py +0 -0
  223. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_cli_common.py +0 -0
  224. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_cli_init_install.py +0 -0
  225. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_cli_monitor_http.py +0 -0
  226. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_cli_upgrade.py +0 -0
  227. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_config_fresh_eyes.py +0 -0
  228. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_config_max_rounds.py +0 -0
  229. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_config_prompt_delivery.py +0 -0
  230. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_config_stop_file.py +0 -0
  231. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
  232. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_config_transient_error_action.py +0 -0
  233. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_context_store.py +0 -0
  234. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_default_dirty_handler.py +0 -0
  235. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_defenses.py +0 -0
  236. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_detector_protocol.py +0 -0
  237. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_dirty_handlers.py +0 -0
  238. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_docgen.py +0 -0
  239. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_events_cmd.py +0 -0
  240. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_events_cmd_tail.py +0 -0
  241. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_exit_cause.py +0 -0
  242. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_fresh_eyes_trigger.py +0 -0
  243. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_hook_failure_isolation.py +0 -0
  244. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_hooks.py +0 -0
  245. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_http_progress.py +0 -0
  246. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_init_entry_points.py +0 -0
  247. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_lifecycle.py +0 -0
  248. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_metrics.py +0 -0
  249. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_monitor_assembly.py +0 -0
  250. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
  251. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
  252. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
  253. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_network_blip_signal_guard.py +0 -0
  254. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_peek_argparse.py +0 -0
  255. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_peek_select.py +0 -0
  256. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_prompt_loader.py +0 -0
  257. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_redact.py +0 -0
  258. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_round_view.py +0 -0
  259. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_runner.py +0 -0
  260. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_runner_throttle.py +0 -0
  261. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_scaffold.py +0 -0
  262. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_serve_cmd_bounded.py +0 -0
  263. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_serve_config_broken.py +0 -0
  264. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_serve_crash_loop.py +0 -0
  265. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_serve_round_log.py +0 -0
  266. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_serve_sentinel.py +0 -0
  267. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_serve_startup_hooks.py +0 -0
  268. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_substrate.py +0 -0
  269. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_throttle_plugin_classification.py +0 -0
  270. {cli_agent_runner-0.2.2 → cli_agent_runner-0.2.4}/tests/unit/test_vcs_state.py +0 -0
@@ -5,6 +5,35 @@ 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.4] - 2026-07-27
9
+
10
+ ### Added
11
+ - `kimi_error_detector` plugin — classifies transient errors from Kimi Code CLI rounds (`turn.step.retrying` records) so a rate-limited round backs off instead of hot-restarting. No usage events: the CLI's stream-json output carries no token counters.
12
+ - `agent_auth_error_detected` event — a plugin reports an auth failure named by the agent CLI's own structured output, and the `oauth_fail` detector counts those rounds directly. Makes pi's 401 loop visible (pi exits 0); the text-heuristic path keeps its nonzero-exit gate unchanged.
13
+ - `pi_error_detector` plugin — emits per-round token usage (summed across the round's assistant messages, since pi reports usage per message) and classifies transient errors from Pi Coding Agent rounds. pi exits 0 on provider failure, so the classifier reads the final message's `stopReason`/`errorMessage` rather than the exit code.
14
+
15
+ ### Fixed
16
+ - Round-log tail scanning hardened: plugin JSONL parsers filter non-JSON chatter *before* windowing (a stderr burst of any size can no longer evict the terminal event), monitor detectors share the same 200-line window (was 50 — the oauth/network text scans had the identical eviction risk), and each monitor poll reads only the newest 20 round logs instead of every log ever written.
17
+ - A relative `runtime.work_dir` (and every path derived from it) now resolves against the config file's directory instead of the caller's cwd, so `--config /abs/proj/agent-runner.toml` drives `/abs/proj` no matter where the supervisor was launched from.
18
+ - Remote monitor (`monitor --host <alias>`) was silently observing an empty world and reporting healthy — it listed remote filenames over ssh but read every path locally. It now fails loudly at startup (exit 1, with guidance to run the monitor on the host) until remote reads are implemented.
19
+ - Agent round logs (`{log_dir}/rounds/R*-*.log`) are now pruned under `runtime.round_log_retention` at the start of every round — previously that family grew unboundedly, since only the serve-level `round-<N>.log` family was pruned, and only at serve startup.
20
+ - Startup validation resolves the agent command exactly as the spawn does (child's PATH — `[agent.env]` may override it — and `work_dir` base); `PWD` now stays pinned to `work_dir` even if `[agent.env]` sets it. The stdout+stderr merge is now pinned by a behavioral test, not just prose.
21
+ - Generated serve systemd units now set `KillMode=mixed`: with systemd's default `control-group`, `systemctl stop` SIGTERMed the whole cgroup — agent child included — making the graceful round drain structurally ineffective. Existing installs: re-run `agent-runner install` (or add a drop-in) to pick this up.
22
+
23
+ See `docs/migrations/0.2.4.md`.
24
+
25
+ ## [0.2.3] - 2026-07-26
26
+
27
+ ### Added
28
+ - `--preset kimi` — a preset for [Kimi Code CLI](https://github.com/MoonshotAI/kimi-code) (`kimi -p --output-format stream-json`). `docs/recipes/kimi.md` also documents running Kimi K3 through the existing `claude` preset via Moonshot's Anthropic-compatible endpoint.
29
+ - `--preset pi` — a preset for [Pi Coding Agent](https://github.com/earendil-works/pi) (`pi -p -na --mode json --model …`; `-na` pins project trust off for unattended runs). `docs/recipes/pi.md` documents driving Kimi K3 through pi via a Moonshot OpenAI-compatible provider.
30
+
31
+ ### Fixed
32
+ - The agent subprocess now runs in `runtime.work_dir` (`cwd=` on spawn). Previously it inherited the supervisor's cwd and only launch conventions (systemd `WorkingDirectory=`, relative `--config`) kept the two aligned — fatal for agent CLIs with no working-directory flag of their own (e.g. `pi`). The startup check now also validates a relative `agent.command[0]` against `work_dir`, matching where it executes.
33
+ - Plugin round-log tail window widened 50 → 200 lines: a stderr burst after the agent's terminal JSONL event could evict it from the scan window, silently dropping usage/transient classification. The round log's merged stdout+stderr contract is now documented (`HookContext.agent_log_path`, `docs/long-running-agents.md`) — the merge is deliberate; auth/network detection reads stderr text from it.
34
+
35
+ See `docs/migrations/0.2.3.md`.
36
+
8
37
  ## [0.2.2] - 2026-07-18
9
38
 
10
39
  ### Removed
@@ -386,7 +415,7 @@ See `docs/migrations/0.1.17.md`.
386
415
 
387
416
  ### Acknowledgements
388
417
 
389
- Argus Gateway's post-Q1-audit feedback (2026-05-14) surfaced the per-phase sub-table need (S3) and the multi-file prompt pattern (S1). Their R721 frontmatter-strip lesson informed making the (already-shipping) strip behavior an explicit opt-out config flag rather than hardcoded.
418
+ A downstream integrator's post-Q1-audit feedback (2026-05-14) surfaced the per-phase sub-table need (S3) and the multi-file prompt pattern (S1). Their R721 frontmatter-strip lesson informed making the (already-shipping) strip behavior an explicit opt-out config flag rather than hardcoded.
390
419
 
391
420
  ## [0.1.15] - 2026-05-14
392
421
 
@@ -418,10 +447,10 @@ independent components bundled into one release because both serve the same
418
447
 
419
448
  ### Acknowledgements
420
449
 
421
- Two of six nice-to-have items surfaced in Argus Gateway's v0.1.12
450
+ Two of six nice-to-have items surfaced in a downstream integrator's v0.1.12
422
451
  production-evaluation report (2026-05-14). An earlier scope also included
423
452
  per-phase runtime override; spec review caught that `runtime.round_timeout_per_phase`
424
- already covers Argus's stated need, so that component was deferred to a future
453
+ already covers the integrator's stated need, so that component was deferred to a future
425
454
  release where a second per-phase field surfaces. Other items in the report
426
455
  (detector helper, hot-reload, replay) remain intentionally out of scope.
427
456
 
@@ -440,7 +469,7 @@ release where a second per-phase field surfaces. Other items in the report
440
469
 
441
470
  ### Acknowledgements
442
471
 
443
- Thanks to the Argus Gateway team — this release answers their imminent
472
+ Thanks to our downstream integrators — this release answers their imminent
444
473
  production-deployment requirement for upgrade-without-disruption. After
445
474
  confirming round duration (10-40 min) fits within graceful-stop tolerance,
446
475
  this release scopes to round-boundary upgrade UX (Level 1). Mid-round
@@ -465,7 +494,7 @@ real production need.
465
494
 
466
495
  ### Acknowledgements
467
496
 
468
- Thanks to the Argus Gateway team for the deep v0.1.10 audit-session feedback
497
+ Thanks to our downstream integrators for the deep v0.1.10 audit-session feedback
469
498
  (6 items, 3-round real-run testing on ARMv8 Pi). This release reframes those
470
499
  items into a coherent "Plugin & Operator Transparency" theme across three
471
500
  layers: transparency (see what plugins do), operator override (escape hatches
@@ -487,16 +516,16 @@ for audit/debug), and diagnostic quality (errors point at the next debug step).
487
516
 
488
517
  ### Migration notes
489
518
 
490
- - `Config.plugins` type change is breaking for any caller reading the field as a dict (`cfg.plugins.get("foo")`). Plugin authors using `[plugins.argus_*]`-style keys: read them from `cfg.plugins.raw.get("argus_*")` instead.
519
+ - `Config.plugins` type change is breaking for any caller reading the field as a dict (`cfg.plugins.get("foo")`). Plugin authors using `[plugins.acme_*]`-style keys: read them from `cfg.plugins.raw.get("acme_*")` instead.
491
520
  - `LockHeldError` message format changed (now includes holder info: `"another agent-runner is holding /path (held by PID N, age Ns, cmd: ...)"` or stale/missing variants). Operators grepping the exact format string need to update.
492
- - For Argus's P5 confusion: see new `docs/architecture.md` section "Plugin injection: two paths" — `inject_context` and `disable_pre_round_hooks` are INDEPENDENT flags. Setting one does not affect the other.
521
+ - For the integrator's P5 confusion: see new `docs/architecture.md` section "Plugin injection: two paths" — `inject_context` and `disable_pre_round_hooks` are INDEPENDENT flags. Setting one does not affect the other.
493
522
  - **Known limitation**: `[plugins] disable` removes named plugins from the hook / context-enricher / detector / event-kind registries, but does NOT remove a disabled plugin's owned VCS paths (the `register_plugin_owned_paths` registry has no name attribution today). Mostly inert. If this becomes a real issue, file a GitHub issue.
494
523
 
495
524
  ## [0.1.11] - 2026-05-13
496
525
 
497
526
  ### Acknowledgements
498
527
 
499
- Thanks again to the Argus Gateway team — this release closes the
528
+ Thanks again to our downstream integrators — this release closes the
500
529
  network-resilience gap that 0.1.10's `MonitorRemoteError` propagation
501
530
  exposed, plus adds per-occurrence agent network blip observability requested
502
531
  during 0.1.10 handover review.
@@ -525,7 +554,7 @@ For network-related failures, the events index points at the diagnostic body:
525
554
 
526
555
  ### Acknowledgements
527
556
 
528
- Thanks to the Argus Gateway team for the Phase 4 second-pass production feedback that drove every change in this release. Six audit memos across 50 minutes of validated runtime surfaced four specific gaps; this release closes them.
557
+ Thanks to our downstream integrators for the Phase 4 second-pass production feedback that drove every change in this release. Six audit memos across 50 minutes of validated runtime surfaced four specific gaps; this release closes them.
529
558
 
530
559
  ### Added
531
560
 
@@ -553,7 +582,7 @@ Thanks to the Argus Gateway team for the Phase 4 second-pass production feedback
553
582
 
554
583
  ### Acknowledgements
555
584
 
556
- Thanks to the argus-gateway team for the dev/qa/product wall-time data
585
+ Thanks to our downstream integrators for the dev/qa/product wall-time data
557
586
  (Phase 4 feedback §3.1) that drove this API shape. Their three-role
558
587
  distribution made the case for per-phase overrides concrete.
559
588
 
@@ -579,7 +608,7 @@ documented plugin-author public surface.
579
608
 
580
609
  ### Acknowledgements
581
610
 
582
- Thanks to the argus-gateway team for Phase 4 dogfooding feedback that drove
611
+ Thanks to our downstream integrators for Phase 4 dogfooding feedback that drove
583
612
  every item in this release. 3 audit memos (~90KB) silently swept into an
584
613
  orphan stash is a real-world failure mode; this release closes that loop.
585
614
 
@@ -608,7 +637,7 @@ orphan stash is a real-world failure mode; this release closes that loop.
608
637
 
609
638
  - Plugin outputs in plugin-declared paths (e.g. `proposals/`,
610
639
  `logs/plugins/my_plugin/`) no longer silently swept into orphan stashes
611
- by `process_orphan_wip`. Previously: 90KB Argus audit memos invisible
640
+ by `process_orphan_wip`. Previously: 90KB audit memos invisible
612
641
  after Phase 4 round; required stash archaeology to recover.
613
642
 
614
643
  ### Migration
@@ -651,7 +680,7 @@ Or regenerate cleanly:
651
680
  agent-runner init --preset claude --force
652
681
  ```
653
682
 
654
- Plugin authors (Argus Gateway, etc.): no public API was renamed or removed
683
+ Plugin authors (downstream integrators, etc.): no public API was renamed or removed
655
684
  from your import surface. The deleted symbols (`agent_runner.agent_runtime.CRITICAL_ENV_DEFAULTS`,
656
685
  `agent_runner.agent_runtime.merge_critical_envs`) were internal — not part of
657
686
  the documented plugin API. A new public-API contract test
@@ -882,7 +911,7 @@ Initial public release on PyPI as `cli-agent-runner`.
882
911
 
883
912
  ### Added
884
913
  - Three-layer model: Round / Loop / Witness.
885
- - 13 CLI verbs: `init`, `install`, `uninstall`, `start`, `stop`, `kill`,
914
+ - 14 CLI verbs: `init`, `install`, `uninstall`, `start`, `stop`, `kill`,
886
915
  `cancel`, `restart`, `status`, `round`, `serve`, `peek`, `watch`, `monitor`.
887
916
  - 11 named defenses (round timeout, process group isolation, orphan stash
888
917
  with SHA lock, set-diff classification, smoke check, flock concurrency,
@@ -909,7 +938,11 @@ Initial public release on PyPI as `cli-agent-runner`.
909
938
  - Tag-triggered release publishing to PyPI via Trusted Publishing OIDC,
910
939
  gated by a manual approval on the `pypi` GitHub environment.
911
940
 
912
- [Unreleased]: https://github.com/wan9yu/cli-agent-runner/compare/v0.2.0...HEAD
941
+ [Unreleased]: https://github.com/wan9yu/cli-agent-runner/compare/v0.2.4...HEAD
942
+ [0.2.4]: https://github.com/wan9yu/cli-agent-runner/compare/v0.2.3...v0.2.4
943
+ [0.2.3]: https://github.com/wan9yu/cli-agent-runner/compare/v0.2.2...v0.2.3
944
+ [0.2.2]: https://github.com/wan9yu/cli-agent-runner/compare/v0.2.1...v0.2.2
945
+ [0.2.1]: https://github.com/wan9yu/cli-agent-runner/compare/v0.2.0...v0.2.1
913
946
  [0.2.0]: https://github.com/wan9yu/cli-agent-runner/compare/v0.1.42...v0.2.0
914
947
  [0.1.26]: https://github.com/wan9yu/cli-agent-runner/compare/v0.1.25...v0.1.26
915
948
  [0.1.25]: https://github.com/wan9yu/cli-agent-runner/compare/v0.1.24...v0.1.25
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cli-agent-runner
3
- Version: 0.2.2
3
+ Version: 0.2.4
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
@@ -41,7 +41,7 @@ Description-Content-Type: text/markdown
41
41
  # agent-runner
42
42
 
43
43
  A restart-on-exit supervisor for autonomous coding CLIs. Ships presets for
44
- Claude Code, aider, gemini and codewhale; any prompt-arg CLI via custom
44
+ Claude Code, aider, gemini, codewhale, kimi and pi; any prompt-arg CLI via custom
45
45
  config. Spawn the agent round-after-round under defenses that prevent the
46
46
  failure modes
47
47
  that bite in production: stuck rounds, orphan commits, OAuth burn loops,
@@ -116,20 +116,20 @@ Notify only: `timeout_rate`, `hung`, `orphan_chain`, `disk_warning`,
116
116
  - `oauth_fail` — burning API quota on auth-rejected rounds
117
117
  - `disk_critical` — writing to a near-full disk risks corruption
118
118
 
119
- Runs locally or against a remote host via ssh:
119
+ Runs against the supervised project's local logs:
120
120
 
121
121
  ```bash
122
- agent-runner monitor # local, 30s poll
123
- agent-runner monitor --host pi # remote, 60s poll
122
+ agent-runner monitor # 30s poll
124
123
  agent-runner monitor --json | jq -c # pipe to downstream consumers
125
124
  ```
126
125
 
127
- > **SSH trust boundary**: `monitor --host <alias>` shells out via plain SSH
128
- > using your local `~/.ssh/config` (aliases, `StrictHostKeyChecking` behavior).
129
- > A monitor with `auto_stop` enabled can issue `agent-runner stop` on the
130
- > remote verify your SSH config before pointing monitor at a remote service.
131
- > See [`docs/runbook.md`](docs/runbook.md) § "Remote monitor & SSH trust" for
132
- > recommended hygiene.
126
+ > **Remote monitoring is unsupported in this version**: `monitor --host <alias>`
127
+ > cannot read a remote host's round logs or events, so it exits with an error at
128
+ > startup instead of watching an empty world and reporting healthy. Run the
129
+ > monitor on the supervised host itself (`ssh <alias>`, then `agent-runner
130
+ > monitor`). See [`docs/runbook.md`](docs/runbook.md) § "Remote monitor & SSH
131
+ > trust" — the SSH trust boundary applies to any agent-runner command you drive
132
+ > over ssh.
133
133
 
134
134
  ## Documentation
135
135
 
@@ -5,7 +5,7 @@
5
5
  # agent-runner
6
6
 
7
7
  A restart-on-exit supervisor for autonomous coding CLIs. Ships presets for
8
- Claude Code, aider, gemini and codewhale; any prompt-arg CLI via custom
8
+ Claude Code, aider, gemini, codewhale, kimi and pi; any prompt-arg CLI via custom
9
9
  config. Spawn the agent round-after-round under defenses that prevent the
10
10
  failure modes
11
11
  that bite in production: stuck rounds, orphan commits, OAuth burn loops,
@@ -80,20 +80,20 @@ Notify only: `timeout_rate`, `hung`, `orphan_chain`, `disk_warning`,
80
80
  - `oauth_fail` — burning API quota on auth-rejected rounds
81
81
  - `disk_critical` — writing to a near-full disk risks corruption
82
82
 
83
- Runs locally or against a remote host via ssh:
83
+ Runs against the supervised project's local logs:
84
84
 
85
85
  ```bash
86
- agent-runner monitor # local, 30s poll
87
- agent-runner monitor --host pi # remote, 60s poll
86
+ agent-runner monitor # 30s poll
88
87
  agent-runner monitor --json | jq -c # pipe to downstream consumers
89
88
  ```
90
89
 
91
- > **SSH trust boundary**: `monitor --host <alias>` shells out via plain SSH
92
- > using your local `~/.ssh/config` (aliases, `StrictHostKeyChecking` behavior).
93
- > A monitor with `auto_stop` enabled can issue `agent-runner stop` on the
94
- > remote verify your SSH config before pointing monitor at a remote service.
95
- > See [`docs/runbook.md`](docs/runbook.md) § "Remote monitor & SSH trust" for
96
- > recommended hygiene.
90
+ > **Remote monitoring is unsupported in this version**: `monitor --host <alias>`
91
+ > cannot read a remote host's round logs or events, so it exits with an error at
92
+ > startup instead of watching an empty world and reporting healthy. Run the
93
+ > monitor on the supervised host itself (`ssh <alias>`, then `agent-runner
94
+ > monitor`). See [`docs/runbook.md`](docs/runbook.md) § "Remote monitor & SSH
95
+ > trust" — the SSH trust boundary applies to any agent-runner command you drive
96
+ > over ssh.
97
97
 
98
98
  ## Documentation
99
99
 
@@ -14,6 +14,7 @@ from __future__ import annotations
14
14
  from pathlib import Path
15
15
 
16
16
  __all__ = [
17
+ "emit_agent_auth_error_detected",
17
18
  "emit_agent_usage_recorded",
18
19
  "emit_anomaly_repetitive_tool",
19
20
  "emit_fresh_eyes_round_triggered",
@@ -150,6 +151,38 @@ def emit_transient_error_detected(
150
151
  )
151
152
 
152
153
 
154
+ def emit_agent_auth_error_detected(
155
+ log_dir: Path,
156
+ *,
157
+ round_num: int,
158
+ agent: str,
159
+ raw: str,
160
+ ) -> None:
161
+ """Emit an authentication/authorization failure the agent itself reported.
162
+
163
+ Contract: emitted by a per-CLI plugin only when the agent's OWN structured
164
+ output names the failure (e.g. an HTTP 401 in its JSON event stream). That
165
+ is certain evidence, unlike the monitor's ``oauth_fail`` text heuristic,
166
+ which scans free-text log tails and therefore needs a nonzero-exit shield
167
+ against prose that merely mentions "401". The monitor counts a round
168
+ carrying this event without that shield — which is what makes an auth loop
169
+ visible for a CLI that exits 0 on provider failure.
170
+
171
+ No back-off partner event: an auth failure is permanent until an operator
172
+ fixes the credential, so it is deliberately not a transient classification.
173
+ """
174
+ from agent_runner._redact import redact_secrets
175
+ from agent_runner.events import AGENT_AUTH_ERROR_DETECTED, emit
176
+
177
+ emit(
178
+ log_dir,
179
+ AGENT_AUTH_ERROR_DETECTED,
180
+ round_num=round_num,
181
+ agent=agent,
182
+ raw=redact_secrets(raw),
183
+ )
184
+
185
+
153
186
  def emit_transient_error_recovered(
154
187
  log_dir: Path,
155
188
  *,
@@ -205,7 +238,10 @@ def emit_agent_usage_recorded(
205
238
  Claude: count of ``tool_use`` content blocks across all assistant events.
206
239
  Gemini: ``stats.tool_calls``.
207
240
  - ``phase``: phase label from HookContext (e.g. "planning"); empty string when None.
208
- - ``success``: True when exit_code == 0 and not timed_out.
241
+ - ``success``: the supervisor's clean-exit predicate (``RoundResult.ok``).
242
+ A plugin MAY additionally fold in the agent's own terminal verdict when
243
+ its CLI's exit code is unreliable — pi does, because pi exits 0 on
244
+ provider failure.
209
245
  """
210
246
  from agent_runner.events import AGENT_USAGE_RECORDED, emit
211
247
 
@@ -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.2'
22
- __version_tuple__ = version_tuple = (0, 2, 2)
21
+ __version__ = version = '0.2.4'
22
+ __version_tuple__ = version_tuple = (0, 2, 4)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -11,6 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  import os
13
13
  import re
14
+ import shutil
14
15
  import signal
15
16
  import subprocess # noqa: TID251 — sanctioned subprocess caller
16
17
  import threading
@@ -52,6 +53,15 @@ class RunResult:
52
53
  killed_for_grace: bool = False
53
54
  grace_kill_children: list[dict] = field(default_factory=list)
54
55
 
56
+ @property
57
+ def ok(self) -> bool:
58
+ """The supervisor's round-success predicate: clean exit, no timeout.
59
+
60
+ Single definition of "did this round fail" at the supervisor level. A
61
+ plugin MAY narrow it further when its CLI's exit code is unreliable.
62
+ """
63
+ return self.exit_code == 0 and not self.timed_out
64
+
55
65
 
56
66
  def _build_argv(command: list[str], prompt_arg_template: list[str], prompt: str) -> list[str]:
57
67
  """Build full argv: command + prompt args (with {prompt} substituted)."""
@@ -130,6 +140,24 @@ def _live_children(
130
140
  _RESULT_MARKER = b'"type":"result"'
131
141
 
132
142
 
143
+ def resolve_exec_target(command0: str, work_dir: Path, env_path: str | None = None) -> str | None:
144
+ """Model of Popen's POSIX exec resolution for argv[0].
145
+
146
+ Kept beside the ``Popen`` in :func:`run` so validation and exec cannot
147
+ drift: a slash-containing argv[0] resolves against the child's cwd
148
+ (``work_dir``); a bare name is looked up on the CHILD's PATH
149
+ (``env_path`` — pass ``[agent.env]``'s PATH override if set; ``None``
150
+ falls back to the supervisor's, matching env inheritance). Returns the
151
+ resolved executable path, or ``None`` if it would not exec.
152
+ """
153
+ if "/" in command0:
154
+ candidate = Path(command0)
155
+ if not candidate.is_absolute():
156
+ candidate = work_dir / candidate
157
+ return shutil.which(str(candidate))
158
+ return shutil.which(command0, path=env_path)
159
+
160
+
133
161
  def run(
134
162
  *,
135
163
  command: list[str],
@@ -137,6 +165,7 @@ def run(
137
165
  prompt: str,
138
166
  prompt_delivery: str = "argv",
139
167
  timeout_s: int,
168
+ work_dir: Path,
140
169
  log_path: Path,
141
170
  env_extra: dict[str, str],
142
171
  max_grace_after_result_s: int = 0,
@@ -149,6 +178,10 @@ def run(
149
178
 
150
179
  Wall-clock timeout (R1128). On timeout: SIGTERM pgroup → REAP_GRACE_S → SIGKILL.
151
180
 
181
+ work_dir: the agent child's working directory; callers pass the
182
+ already-absolute cfg.runtime.work_dir. CLIs with no --cwd flag of their
183
+ own (e.g. pi) depend on this.
184
+
152
185
  max_grace_after_result_s: when > 0, start a countdown after the first
153
186
  type=result event is detected in the log. After it elapses, reap the
154
187
  process group only if the agent has no live worker processes left (a
@@ -175,16 +208,21 @@ def run(
175
208
  if stdin_mode
176
209
  else _build_argv(command, prompt_arg_template, prompt)
177
210
  )
178
- env = {**os.environ, **env_extra}
211
+ # PWD pinned last — it mirrors cwd= (a correctness pin, not a knob), so
212
+ # an [agent.env] PWD cannot silently diverge from where the child runs.
213
+ env = {**os.environ, **env_extra, "PWD": str(work_dir)}
179
214
  log_path.parent.mkdir(parents=True, exist_ok=True)
180
215
  log_file = log_path.open("w", encoding="utf-8")
181
216
  start = time.time()
182
217
  last_progress_at = start
183
218
  proc = subprocess.Popen(
184
219
  argv,
220
+ cwd=work_dir,
185
221
  env=env,
186
222
  stdin=subprocess.PIPE if stdin_mode else subprocess.DEVNULL,
187
223
  stdout=log_file,
224
+ # Merged on purpose: oauth_fail / network_fail / network-blip detection
225
+ # regex-scan stderr text out of this log (see hooks.agent_log_path).
188
226
  stderr=subprocess.STDOUT,
189
227
  start_new_session=True,
190
228
  )
@@ -486,6 +486,8 @@ def _poll_once(project: str | Path, *, host: str | None) -> list[monitor.Alert]:
486
486
  if host is None:
487
487
  src = monitor.LocalSource(log_dir=cfg.runtime.log_dir)
488
488
  else:
489
+ # Dormant: monitor_loop rejects --host before any poll, so this branch
490
+ # is unreachable today. See monitor.MonitorRemoteUnsupportedError.
489
491
  src = monitor.RemoteSource(host=host, project=_project_name(work_dir))
490
492
  events = monitor.parse_events_from_jsonl_files(src.events_files())
491
493
  metrics = monitor.parse_events_from_jsonl_files(src.metrics_files())
@@ -525,12 +527,29 @@ def monitor_loop(
525
527
  to that kind as the canonical "supervision is up" signal (monitor is otherwise
526
528
  silent during healthy operation by design).
527
529
 
530
+ ``host`` (remote mode) raises ``MonitorRemoteUnsupportedError`` immediately:
531
+ remote reads are unimplemented, so a remote loop would poll an empty world
532
+ and report healthy forever. The check is eager — this wrapper validates
533
+ before handing back the generator, so the failure lands at startup rather
534
+ than at the first ``next()``.
535
+ """
536
+ if host is not None:
537
+ raise monitor.MonitorRemoteUnsupportedError(host)
538
+ return _monitor_loop_iter(project, host=host, interval_s=interval_s)
539
+
540
+
541
+ def _monitor_loop_iter(
542
+ project: str | Path | None = None, *, host: str | None = None, interval_s: int = 30
543
+ ) -> Iterator[monitor.Alert]:
544
+ """Polling generator behind ``monitor_loop``.
545
+
528
546
  Tolerates transient ``MonitorRemoteError`` failures (from ``--host`` ssh)
529
547
  for up to ``cfg.monitor.remote_failure_tolerance_s`` seconds with exponential
530
548
  backoff (1s → 2s → 4s → ... → 30s cap). Each retry emits ``monitor_remote_blip``;
531
549
  crossing the cap emits one ``monitor_remote_giveup`` and propagates the error
532
550
  (CLI exits 1; systemd restarts the process). Setting tolerance to 0 preserves
533
- the 0.1.10 immediate-propagate behavior with no blip events emitted.
551
+ the 0.1.10 immediate-propagate behavior with no blip events emitted. That
552
+ path is dormant while remote mode is rejected at startup.
534
553
  """
535
554
  import json as _json
536
555
 
@@ -771,6 +790,7 @@ def check_self_terminated_sentinel(log_dir: Path) -> bool:
771
790
  # Re-export emit_* wrappers from _emit module (extracted for size hygiene).
772
791
  # Preserves the public import surface: `from agent_runner.api import emit_*` continues to work.
773
792
  from agent_runner._emit import ( # noqa: E402,F401 — intentional bottom re-export
793
+ emit_agent_auth_error_detected,
774
794
  emit_agent_usage_recorded,
775
795
  emit_anomaly_repetitive_tool,
776
796
  emit_config_broken,
@@ -173,6 +173,15 @@ class RoundResult:
173
173
  dirty_outcome: DirtyOutcome | None = None
174
174
  killed_for_grace: bool = False
175
175
 
176
+ @property
177
+ def ok(self) -> bool:
178
+ """The supervisor's round-success predicate: clean exit, no timeout.
179
+
180
+ Single definition of "did this round fail" at the supervisor level. A
181
+ plugin MAY narrow it further when its CLI's exit code is unreliable.
182
+ """
183
+ return self.exit_code == 0 and not self.timed_out
184
+
176
185
 
177
186
  @dataclass(frozen=True)
178
187
  class InitResult:
@@ -0,0 +1,125 @@
1
+ """Shared constants and tail-scan helpers for built-in CLI plugins.
2
+
3
+ Extracted to single source of truth so every round-log tail-scanner
4
+ (plugin JSONL parsers and monitor text detectors) uses the same window
5
+ size, raw-text caps, transient-error back-off defaults, and the same
6
+ HTTP-status → classification ladder.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ from collections import deque
13
+ from collections.abc import Iterator
14
+ from pathlib import Path
15
+ from typing import Any, TextIO
16
+
17
+ _TAIL_LINES: int = 200
18
+ """Number of log lines to scan from the end of a round log.
19
+
20
+ The round log is merged stdout+stderr. JSONL consumers window via
21
+ ``json_tail`` (chatter filtered before windowing); plain-text consumers
22
+ (monitor oauth/network detectors) window the raw tail — keep this generous
23
+ so a stderr burst cannot evict what they scan for."""
24
+
25
+
26
+ def json_tail(f: TextIO, maxlen: int = _TAIL_LINES) -> deque[str]:
27
+ """Last ``maxlen`` JSON-looking lines of a merged round log.
28
+
29
+ Non-JSON chatter (stderr text) is filtered BEFORE windowing, so a burst
30
+ of any size cannot evict the terminal JSONL event from the window.
31
+ """
32
+ return deque((ln for ln in f if ln.lstrip()[:1] in "{["), maxlen=maxlen)
33
+
34
+
35
+ def json_events(log_path: Path) -> Iterator[dict]:
36
+ """JSON objects in a round log's tail window, in file order.
37
+
38
+ One reader for every plugin's parse loop: window via ``json_tail``, then
39
+ per line strip / skip blank / ``json.loads`` / drop anything that is not an
40
+ object. Non-JSON lines are expected — the round log merges stdout+stderr,
41
+ and every CLI writes some plain text there.
42
+ """
43
+ with log_path.open("r", encoding="utf-8", errors="replace") as f:
44
+ tail = json_tail(f)
45
+ for line in tail:
46
+ line = line.strip()
47
+ if not line:
48
+ continue
49
+ try:
50
+ event = json.loads(line)
51
+ except json.JSONDecodeError:
52
+ continue
53
+ if isinstance(event, dict):
54
+ yield event
55
+
56
+
57
+ _RAW_CAP: int = 200
58
+ """Maximum length for ``raw`` field in transient_error_detected payload."""
59
+
60
+ # Default back-off durations (seconds) for non-precise transient classifications.
61
+ # rate_limit_account uses exact resetsAt epoch from claude; not in this table.
62
+ _BACK_OFF_DEFAULTS: dict[str, int] = {
63
+ "rate_limit_model": 60,
64
+ "api_transient_5xx": 60,
65
+ "api_timeout": 30,
66
+ }
67
+
68
+ # 5xx codes treated as transient (retry-worthy server errors per RFC 9110):
69
+ # 500=unexpected, 502=bad gateway, 503=unavailable, 504=gateway timeout,
70
+ # 529=overloaded (Anthropic's non-RFC code emitted during sustained capacity
71
+ # issues; treated as transient per Anthropic SDK behavior).
72
+ # Excluded: 501 (not implemented = permanent), 505 (HTTP version mismatch).
73
+ _5XX_STATUSES: frozenset[int] = frozenset({500, 502, 503, 504, 529})
74
+
75
+
76
+ def classify_transient_status(status: Any) -> str | None:
77
+ """Map an HTTP status from a CLI's error record to a transient bucket.
78
+
79
+ The one ladder every plugin classifier delegates to, so a status is
80
+ bucketed identically no matter which CLI reported it.
81
+
82
+ ``None`` means 'not transient — do not back off'. Notably 401 (auth) and
83
+ 404 (unknown model) land here: both are permanent until an operator fixes
84
+ configuration, which is oauth/config territory rather than something a
85
+ back-off would clear. Non-integer or missing statuses are ``None`` too;
86
+ a CLI that reports no status gets no classification.
87
+ """
88
+ if not isinstance(status, int) or isinstance(status, bool):
89
+ return None
90
+ if status == 429:
91
+ return "rate_limit_model"
92
+ if status in _5XX_STATUSES:
93
+ return "api_transient_5xx"
94
+ if status == 408:
95
+ return "api_timeout"
96
+ return None
97
+
98
+
99
+ _CLASSIFICATIONS: frozenset[str] = frozenset(
100
+ {
101
+ "rate_limit_account",
102
+ "rate_limit_model",
103
+ "api_transient_5xx",
104
+ "api_timeout",
105
+ }
106
+ )
107
+ """Canonical set of transient-error classifications.
108
+
109
+ rate_limit_account uses server-provided resetsAt (excluded from
110
+ _BACK_OFF_DEFAULTS table); others use defaults from that table.
111
+ """
112
+
113
+ _EXP_CAP: int = 5
114
+ """Maximum exponent for transient-error consecutive backoff: 2^5 = 32×.
115
+
116
+ Beyond this, the multiplier plateaus. Combined with _ABSOLUTE_CAP_S, this
117
+ prevents runaway wait times during sustained outages (max wait = 30min).
118
+ """
119
+
120
+ _ABSOLUTE_CAP_S: int = 1800
121
+ """Absolute upper bound on supervisor-applied transient back-off (30 min).
122
+
123
+ Applies after exp multiplier — even if base × 2^5 exceeds this, the wait
124
+ is clipped here. Defends against an indefinitely-stuck supervisor.
125
+ """