cli-agent-runner 0.1.36__tar.gz → 0.1.37__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 (222) hide show
  1. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/CHANGELOG.md +13 -0
  2. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/PKG-INFO +1 -1
  3. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/_version.py +2 -2
  4. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/upgrade_cmd.py +147 -20
  5. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/events.py +1 -0
  6. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/architecture.md +1 -0
  7. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/commands.md +19 -1
  8. cli_agent_runner-0.1.37/docs/migrations/0.1.37.md +65 -0
  9. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/runbook.md +31 -23
  10. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_catalogs.py +7 -0
  11. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_cli_upgrade.py +235 -0
  12. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.codecov.yml +0 -0
  13. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  14. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  15. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  16. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  17. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.github/workflows/ci.yml +0 -0
  18. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.github/workflows/release.yml +0 -0
  19. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.gitignore +0 -0
  20. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.vulture-whitelist.py +0 -0
  21. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/CODE_OF_CONDUCT.md +0 -0
  22. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/CONTRIBUTING.md +0 -0
  23. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/LICENSE +0 -0
  24. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/README.md +0 -0
  25. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/README.zh.md +0 -0
  26. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/SECURITY.md +0 -0
  27. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/__init__.py +0 -0
  28. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/_docgen.py +0 -0
  29. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/_emit.py +0 -0
  30. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/_registry.py +0 -0
  31. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/_substrate.py +0 -0
  32. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/_throttle.py +0 -0
  33. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/agent_runtime.py +0 -0
  34. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/api.py +0 -0
  35. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/api_types.py +0 -0
  36. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/builtin_plugins/__init__.py +0 -0
  37. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/builtin_plugins/_constants.py +0 -0
  38. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/builtin_plugins/claude_rate_limit.py +0 -0
  39. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/builtin_plugins/gemini.py +0 -0
  40. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/__init__.py +0 -0
  41. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/__main__.py +0 -0
  42. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/common.py +0 -0
  43. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/events_cmd.py +0 -0
  44. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/init_cmd.py +0 -0
  45. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/install_cmd.py +0 -0
  46. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/monitor_cmd.py +0 -0
  47. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/peek_cmd.py +0 -0
  48. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/round_cmd.py +0 -0
  49. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/serve_cmd.py +0 -0
  50. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/service_cmd.py +0 -0
  51. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/config.py +0 -0
  52. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/context_store.py +0 -0
  53. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/defenses.py +0 -0
  54. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/detector_helpers.py +0 -0
  55. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/hooks.py +0 -0
  56. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/http_progress.py +0 -0
  57. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/lifecycle.py +0 -0
  58. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/metrics.py +0 -0
  59. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/monitor.py +0 -0
  60. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/presets/__init__.py +0 -0
  61. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/presets/aider.toml +0 -0
  62. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/presets/claude.toml +0 -0
  63. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/presets/gemini.toml +0 -0
  64. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/prompt_loader.py +0 -0
  65. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/round_log.py +0 -0
  66. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/round_view.py +0 -0
  67. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/runner.py +0 -0
  68. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/scaffold.py +0 -0
  69. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/service_unit.py +0 -0
  70. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/startup_check.py +0 -0
  71. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/vcs_state.py +0 -0
  72. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/build.sh +0 -0
  73. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/deploy/example-agent-runner.toml +0 -0
  74. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/deploy/launchd.plist.tmpl +0 -0
  75. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/deploy/run-loop.sh +0 -0
  76. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/deploy/systemd.service.tmpl +0 -0
  77. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/README.md +0 -0
  78. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/configuration.md +0 -0
  79. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/events.md +0 -0
  80. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/long-running-agents.md +0 -0
  81. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/marketing/README.md +0 -0
  82. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/marketing/promo-cn.html +0 -0
  83. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.16.md +0 -0
  84. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.17.md +0 -0
  85. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.19.md +0 -0
  86. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.20.md +0 -0
  87. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.21.md +0 -0
  88. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.22.md +0 -0
  89. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.23.md +0 -0
  90. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.24.md +0 -0
  91. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.25.md +0 -0
  92. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.26.md +0 -0
  93. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.27.md +0 -0
  94. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.28.md +0 -0
  95. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.29.md +0 -0
  96. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.30.md +0 -0
  97. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.31.md +0 -0
  98. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.32.md +0 -0
  99. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.33.md +0 -0
  100. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.34.md +0 -0
  101. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.35.md +0 -0
  102. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.36.md +0 -0
  103. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/plugins.md +0 -0
  104. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/quickstart.md +0 -0
  105. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/recipes/aider.md +0 -0
  106. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/thesis.md +0 -0
  107. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/pyproject.toml +0 -0
  108. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/__init__.py +0 -0
  109. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/_test_helpers.py +0 -0
  110. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/conftest.py +0 -0
  111. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/contract/__init__.py +0 -0
  112. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/contract/test_public_api_surface.py +0 -0
  113. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/e2e/__init__.py +0 -0
  114. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/e2e/conftest.py +0 -0
  115. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/e2e/test_e2e_graceful_stop.py +0 -0
  116. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/e2e/test_e2e_install_systemd.py +0 -0
  117. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/e2e/test_e2e_monitor_remote.py +0 -0
  118. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
  119. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/fixtures/cli-real-output/claude-2.1.143-assistant-tool-use.jsonl +0 -0
  120. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/fixtures/cli-real-output/claude-2.1.143-result-event.jsonl +0 -0
  121. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/fixtures/cli-real-output/gemini-0.42.0-result-event.jsonl +0 -0
  122. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/__init__.py +0 -0
  123. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_bounded_run.py +0 -0
  124. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_context_enricher_namespacing.py +0 -0
  125. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_fresh_eyes_signal.py +0 -0
  126. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_grace_kill_emission.py +0 -0
  127. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_install_dry_run.py +0 -0
  128. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_monitor_seeded.py +0 -0
  129. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_plugin_detector_loaded.py +0 -0
  130. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_plugin_owned_paths.py +0 -0
  131. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_plugin_real_flow.py +0 -0
  132. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_run_one_round_with_fake_agent.py +0 -0
  133. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_scaffold_presets.py +0 -0
  134. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_serve_loop.py +0 -0
  135. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_substrate_fingerprint.py +0 -0
  136. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_transient_error_backoff.py +0 -0
  137. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/__init__.py +0 -0
  138. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_architecture.py +0 -0
  139. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_atomic_write_enforced.py +0 -0
  140. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_classification_ssot.py +0 -0
  141. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_docs_generated.py +0 -0
  142. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_entry_points_resolve.py +0 -0
  143. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_event_kind_registry.py +0 -0
  144. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_event_kinds_ssot.py +0 -0
  145. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_events_doc_contract.py +0 -0
  146. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_layer_2_loop_size.py +0 -0
  147. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_module_boundaries.py +0 -0
  148. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_module_sizes.py +0 -0
  149. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_no_ai_signatures.py +0 -0
  150. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
  151. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_peek_schema_version.py +0 -0
  152. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
  153. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_round_result_stable.py +0 -0
  154. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
  155. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_upstream_schema_canary.py +0 -0
  156. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/literate/__init__.py +0 -0
  157. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/literate/parser.py +0 -0
  158. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/literate/test_parser.py +0 -0
  159. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/literate/test_quickstart.py +0 -0
  160. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/__init__.py +0 -0
  161. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_agent_runtime.py +0 -0
  162. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_agent_runtime_grace.py +0 -0
  163. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_agent_runtime_progress.py +0 -0
  164. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_assemble_prompt.py +0 -0
  165. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_events_stream.py +0 -0
  166. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_install.py +0 -0
  167. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_observation.py +0 -0
  168. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_read_round_num.py +0 -0
  169. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_resolve_phase.py +0 -0
  170. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_service.py +0 -0
  171. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_types.py +0 -0
  172. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_auto_stop_gating.py +0 -0
  173. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_claude_error_detector.py +0 -0
  174. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_cli.py +0 -0
  175. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_cli_common.py +0 -0
  176. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_cli_init_install.py +0 -0
  177. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_cli_monitor_http.py +0 -0
  178. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_cli_service_peek_monitor.py +0 -0
  179. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_config.py +0 -0
  180. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_config_fresh_eyes.py +0 -0
  181. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_config_max_rounds.py +0 -0
  182. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_config_stop_file.py +0 -0
  183. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
  184. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_config_transient_error_action.py +0 -0
  185. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_context_store.py +0 -0
  186. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_defenses.py +0 -0
  187. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_detector_helpers.py +0 -0
  188. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_detector_protocol.py +0 -0
  189. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_docgen.py +0 -0
  190. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_events.py +0 -0
  191. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_events_cmd.py +0 -0
  192. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_fresh_eyes_trigger.py +0 -0
  193. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_gemini_plugin.py +0 -0
  194. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_hook_failure_isolation.py +0 -0
  195. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_hooks.py +0 -0
  196. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_http_progress.py +0 -0
  197. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_init_entry_points.py +0 -0
  198. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_lifecycle.py +0 -0
  199. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_metrics.py +0 -0
  200. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_monitor_assembly.py +0 -0
  201. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
  202. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
  203. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
  204. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_monitor_detectors.py +0 -0
  205. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_monitor_remote.py +0 -0
  206. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_peek_argparse.py +0 -0
  207. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_peek_select.py +0 -0
  208. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_presets.py +0 -0
  209. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_prompt_loader.py +0 -0
  210. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_round_log_helpers.py +0 -0
  211. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_round_view.py +0 -0
  212. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_runner.py +0 -0
  213. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_runner_throttle.py +0 -0
  214. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_scaffold.py +0 -0
  215. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_serve_cmd_bounded.py +0 -0
  216. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_serve_round_log.py +0 -0
  217. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_serve_sentinel.py +0 -0
  218. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_serve_startup_hooks.py +0 -0
  219. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_service_unit.py +0 -0
  220. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_startup_check.py +0 -0
  221. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_substrate.py +0 -0
  222. {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_vcs_state.py +0 -0
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.37] - 2026-05-22
11
+
12
+ ### Fixed
13
+ - `upgrade` no longer crashes when run from a directory without `agent-runner.toml` — it upgrades the package and falls back to package-only mode.
14
+ - `upgrade` handles PEP 668 externally-managed environments (Debian 12 etc.): retries pip with `--break-system-packages` (and `--user` for user-site installs) when not in a venv.
15
+
16
+ ### Changed
17
+ - `upgrade` only stop/start-orchestrates the `systemd --user` service it installed. For a self-managed service (e.g. a systemd system unit) it does package-only upgrade + smoke and prints the restart command to run yourself — no more silent no-op, and no more `agent-runner start` suggestion (which could spawn a conflicting second supervisor).
18
+ - New `--no-restart` flag forces package-only upgrade.
19
+
20
+ ### Added
21
+ - New event `package_upgraded` (on-disk package changed; restart deferred to the operator), distinct from `service_upgraded` (the live service is now on the new version).
22
+
10
23
  ## [0.1.36] - 2026-05-21
11
24
 
12
25
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cli-agent-runner
3
- Version: 0.1.36
3
+ Version: 0.1.37
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
@@ -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.1.36'
22
- __version_tuple__ = version_tuple = (0, 1, 36)
21
+ __version__ = version = '0.1.37'
22
+ __version_tuple__ = version_tuple = (0, 1, 37)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -18,7 +18,9 @@ import sys
18
18
  import time
19
19
  from pathlib import Path
20
20
 
21
+ import agent_runner
21
22
  from agent_runner import __version__, api, events
23
+ from agent_runner.api_types import ServiceMode
22
24
  from agent_runner.cli.common import cfg_from_args, fail, info
23
25
  from agent_runner.config import Config
24
26
 
@@ -28,8 +30,8 @@ def add_parser(sub, parent) -> None:
28
30
  "upgrade",
29
31
  parents=[parent],
30
32
  help=(
31
- "Round-boundary upgrade: stop pip install → smoke → start"
32
- " (auto-rollback on smoke fail)"
33
+ "Package upgrade with service-mode gate: orchestrated stop/start"
34
+ " for systemd --user; package-only otherwise"
33
35
  ),
34
36
  )
35
37
  p.add_argument(
@@ -40,24 +42,69 @@ def add_parser(sub, parent) -> None:
40
42
  help="Pin a specific version (e.g. 0.1.13). Default: latest from PyPI. "
41
43
  "Use to roll back: `--target <previous-version>`.",
42
44
  )
45
+ p.add_argument(
46
+ "--no-restart",
47
+ action="store_true",
48
+ help="Upgrade the package + smoke only; do not stop/start the service "
49
+ "(you restart it yourself).",
50
+ )
43
51
  p.set_defaults(func=cmd)
44
52
 
45
53
 
46
54
  def cmd(args) -> int:
47
- cfg = cfg_from_args(args)
48
- return _run_upgrade(cfg, target=args.target, cfg_path=args.config)
55
+ cfg = _try_load_cfg(args)
56
+ return _run_upgrade(
57
+ cfg,
58
+ target=args.target,
59
+ cfg_path=args.config,
60
+ no_restart=getattr(args, "no_restart", False),
61
+ )
49
62
 
50
63
 
51
- def _pip_install(spec: str, *, force_reinstall: bool = False) -> subprocess.CompletedProcess:
52
- """Invoke pip install with the given spec. Returns CompletedProcess (rc check by caller).
64
+ def _try_load_cfg(args) -> Config | None:
65
+ """Load the project config if present; None when absent (package-only)."""
66
+ try:
67
+ return cfg_from_args(args)
68
+ except FileNotFoundError:
69
+ return None
70
+
71
+
72
+ def _pip_env_flags() -> list[str]:
73
+ """Extra pip flags for the current install under PEP 668.
53
74
 
54
- Uses ``sys.executable -m pip`` to match the smoke functions and guarantee
55
- we install into the same interpreter we will smoke-test against.
75
+ Inside a venv: none (pip is unrestricted). Otherwise (system/user
76
+ interpreter on an externally-managed distro) the caller retries with these.
77
+ ``--user`` is added only when agent_runner lives in user-site, matching
78
+ where the existing install actually is.
79
+ """
80
+ import sys
81
+
82
+ if sys.prefix != sys.base_prefix: # inside a venv → no PEP 668
83
+ return []
84
+ import site
85
+
86
+ flags = ["--break-system-packages"]
87
+ user_site = site.getusersitepackages()
88
+ if str(Path(agent_runner.__file__)).startswith(str(Path(user_site))):
89
+ flags.insert(0, "--user")
90
+ return flags
91
+
92
+
93
+ def _pip_install(spec: str, *, force_reinstall: bool = False) -> subprocess.CompletedProcess:
94
+ """pip install --upgrade <spec>, retrying once with PEP668 flags on an
95
+ externally-managed environment. Returns CompletedProcess (rc check by caller).
56
96
  """
57
- cmd = [sys.executable, "-m", "pip", "install", "--upgrade", spec]
97
+ base = [sys.executable, "-m", "pip", "install", "--upgrade", spec]
58
98
  if force_reinstall:
59
- cmd.insert(4, "--force-reinstall")
60
- return subprocess.run(cmd, capture_output=True, text=True, check=False)
99
+ base.insert(4, "--force-reinstall")
100
+ r = subprocess.run(base, capture_output=True, text=True, check=False)
101
+ if r.returncode == 0 or "externally-managed-environment" not in (r.stderr or ""):
102
+ return r
103
+ extra = _pip_env_flags()
104
+ if not extra:
105
+ return r
106
+ info(f"externally-managed env detected; retrying pip with {' '.join(extra)}")
107
+ return subprocess.run(base + extra, capture_output=True, text=True, check=False)
61
108
 
62
109
 
63
110
  def _smoke_version() -> tuple[int, str]:
@@ -93,18 +140,40 @@ def _smoke_peek(cfg_path: Path) -> tuple[int, str]:
93
140
  return 0, ""
94
141
 
95
142
 
96
- def _run_upgrade(cfg: Config, *, target: str | None, cfg_path: Path) -> int:
97
- """Orchestrate the full upgrade flow.
98
-
99
- Returns exit code (0 success, 1 user-recoverable, 2 critical).
100
- """
143
+ def _run_upgrade(
144
+ cfg: Config | None,
145
+ *,
146
+ target: str | None,
147
+ cfg_path: Path,
148
+ no_restart: bool = False,
149
+ ) -> int:
150
+ """Dispatch: full orchestration for the systemd --user service we installed;
151
+ package-only everywhere else."""
101
152
  if target is not None and not target.strip():
102
153
  return fail("--target must be a non-empty version string (e.g. 0.1.13)")
154
+ from_version = __version__
155
+ if _orchestrate_capable(cfg, no_restart):
156
+ return _orchestrated_upgrade(
157
+ cfg, target=target, cfg_path=cfg_path, from_version=from_version
158
+ )
159
+ return _package_only_upgrade(cfg, target=target, from_version=from_version)
160
+
161
+
162
+ def _orchestrate_capable(cfg: Config | None, no_restart: bool) -> bool:
163
+ if cfg is None or no_restart:
164
+ return False
165
+ pname = api._resolve_project(cfg.runtime.work_dir)
166
+ return api.detect_service_mode(pname, log_dir=cfg.runtime.log_dir) == ServiceMode.SYSTEMD_USER
103
167
 
168
+
169
+ def _orchestrated_upgrade(
170
+ cfg: Config, *, target: str | None, cfg_path: Path, from_version: str
171
+ ) -> int:
172
+ """Full stop → pip → smoke(--version + peek) → start → emit service_upgraded,
173
+ with auto-rollback on smoke failure. Only reached for the systemd --user
174
+ service agent-runner installed (api.start works there)."""
104
175
  log_dir = cfg.runtime.log_dir
105
176
  log_dir.mkdir(parents=True, exist_ok=True)
106
-
107
- from_version = __version__
108
177
  t0 = time.monotonic()
109
178
 
110
179
  info("stopping service...")
@@ -155,14 +224,13 @@ def _run_upgrade(cfg: Config, *, target: str | None, cfg_path: Path) -> int:
155
224
  started_at=t0,
156
225
  cfg_path=cfg_path,
157
226
  )
158
-
159
227
  info(f"smoke OK (now at {to_version})")
160
228
 
161
229
  info("starting service...")
162
230
  t_start = time.monotonic()
163
231
  try:
164
232
  api.start(cfg.runtime.work_dir)
165
- except Exception as e: # noqa: BLE001 — new version installed but service stopped; no safe auto-rollback
233
+ except Exception as e: # noqa: BLE001 — new version installed but service stopped
166
234
  return _rollback_failed(
167
235
  log_dir,
168
236
  to_version,
@@ -183,6 +251,65 @@ def _run_upgrade(cfg: Config, *, target: str | None, cfg_path: Path) -> int:
183
251
  return 0
184
252
 
185
253
 
254
+ def _package_only_upgrade(cfg: Config | None, *, target: str | None, from_version: str) -> int:
255
+ """Upgrade the on-disk package + smoke (--version), with pip-level rollback.
256
+ Never touches the service — the operator restarts it. Used for any deployment
257
+ not managed as a systemd --user service (system unit, foreground, none, no
258
+ config, or --no-restart)."""
259
+ spec = "cli-agent-runner" if target is None else f"cli-agent-runner=={target}"
260
+ info(f"package-only upgrade (service not managed by agent-runner); installing {spec}...")
261
+ pip_result = _pip_install(spec)
262
+ if pip_result.returncode != 0:
263
+ return fail(
264
+ f"pip install failed (rc={pip_result.returncode}): "
265
+ f"{pip_result.stderr.strip()[:200]}; "
266
+ f"package unchanged, your service keeps running the current version"
267
+ )
268
+
269
+ rc_v, version_or_err = _smoke_version()
270
+ if rc_v != 0:
271
+ attempted = target or "latest"
272
+ info(f"smoke failed at {attempted} ({version_or_err}); reinstalling {from_version}...")
273
+ rb = _pip_install(f"cli-agent-runner=={from_version}", force_reinstall=True)
274
+ if rb.returncode != 0:
275
+ return fail(
276
+ f"package smoke failed AND rollback reinstall failed (rc={rb.returncode}): "
277
+ f"{rb.stderr.strip()[:200]}; run: "
278
+ f"pip install --force-reinstall cli-agent-runner=={from_version}"
279
+ )
280
+ return fail(
281
+ f"package smoke failed at {attempted}; reinstalled {from_version}; service untouched"
282
+ )
283
+ to_version = version_or_err
284
+
285
+ if cfg is not None:
286
+ log_dir = cfg.runtime.log_dir
287
+ log_dir.mkdir(parents=True, exist_ok=True)
288
+ events.emit(
289
+ log_dir,
290
+ events.PACKAGE_UPGRADED,
291
+ from_version=from_version,
292
+ to_version=to_version,
293
+ restart_deferred=True,
294
+ )
295
+ info(f"package upgraded {from_version} → {to_version}. Restart your supervisor to load it:")
296
+ info(_restart_hint(cfg))
297
+ return 0
298
+
299
+
300
+ def _restart_hint(cfg: Config | None) -> str:
301
+ """Mode-correct restart command. Never suggests `agent-runner start`
302
+ (which would spawn a conflicting supervisor on a system-unit host)."""
303
+ if cfg is not None:
304
+ pname = api._resolve_project(cfg.runtime.work_dir)
305
+ if api.detect_service_mode(pname, log_dir=cfg.runtime.log_dir) == ServiceMode.SYSTEMD_USER:
306
+ return f" systemctl --user restart {api.serve_unit_filename(pname)}"
307
+ return (
308
+ " sudo systemctl restart <your-unit> # if run by a systemd system unit\n"
309
+ " (agent-runner can't know a service it didn't install; substitute your unit name)"
310
+ )
311
+
312
+
186
313
  def _rollback(
187
314
  cfg: Config,
188
315
  log_dir: Path,
@@ -46,6 +46,7 @@ MONITOR_STARTED = "monitor_started"
46
46
  ORPHAN_IDEMPOTENT_SKIP = "orphan_idempotent_skip"
47
47
  ORPHAN_STASH_FAILED = "orphan_stash_failed"
48
48
  ORPHAN_STASHED = "orphan_stashed"
49
+ PACKAGE_UPGRADED = "package_upgraded"
49
50
  PROMPT_OVERWRITTEN = "prompt_overwritten"
50
51
  ROUND_END = "round_end"
51
52
  ROUND_GRACE_KILL = "round_grace_kill"
@@ -165,6 +165,7 @@ hook (vs ALL pre-round hooks), use `[plugins] disable = ["that_entry_point_name"
165
165
  - `orphan_idempotent_skip`
166
166
  - `orphan_stash_failed`
167
167
  - `orphan_stashed`
168
+ - `package_upgraded`
168
169
  - `prompt_overwritten`
169
170
  - `round_end`
170
171
  - `round_grace_kill`
@@ -24,7 +24,7 @@ are shared between `peek`, `watch`, and `monitor`.
24
24
  | `monitor` | Anomaly detection, narrate/events stream, or HTTP progress page |
25
25
  | `serve` | Long-running supervisor loop |
26
26
  | `round` | Run one round and exit |
27
- | `upgrade` | Round-boundary upgrade: stop pip install smoke start (auto-rollback on smoke fail) |
27
+ | `upgrade` | Package upgrade with service-mode gate: orchestrated stop/start for systemd --user; package-only otherwise |
28
28
  <!-- /gen:verb-table -->
29
29
 
30
30
  ## Lifecycle
@@ -76,6 +76,24 @@ Long-running supervisor loop. Traps SIGTERM (graceful stop), SIGINT (graceful),
76
76
  SIGUSR1 (cancel — forwards SIGINT to current round). Writes `serve.pid` and
77
77
  `round.pid`. `--once` runs a single round then exits (debug).
78
78
 
79
+ ### `agent-runner upgrade [--target VERSION] [--no-restart] [--config PATH]`
80
+
81
+ Upgrade the agent-runner package. Behavior depends on the detected service mode:
82
+
83
+ - **systemd --user service** (installed via `agent-runner install`): full
84
+ orchestrated flow — stop → pip install → smoke (`--version` + `peek`) →
85
+ start → emit `service_upgraded`. Auto-rollback on smoke failure.
86
+ - **Anything else** (system unit, foreground, no config): package-only —
87
+ PEP 668-aware pip + `--version` smoke + pip-level rollback, emits
88
+ `package_upgraded`, prints the restart command. Never touches your running
89
+ service, never runs `sudo`.
90
+
91
+ `--config` is optional: when omitted (or the file is absent), `upgrade` falls
92
+ back to package-only mode automatically.
93
+
94
+ `--no-restart` forces package-only even on a systemd --user host (upgrade the
95
+ package now, restart your service yourself).
96
+
79
97
  ## Observation
80
98
 
81
99
  ### `agent-runner peek [flags]`
@@ -0,0 +1,65 @@
1
+ # Migrating to 0.1.37
2
+
3
+ ## TL;DR
4
+
5
+ ```bash
6
+ pip install --upgrade cli-agent-runner==0.1.37
7
+ ```
8
+
9
+ If you run agent-runner as a `systemd --user` service (the kind `agent-runner
10
+ install` creates), `agent-runner upgrade` works end-to-end as before. If you run
11
+ it any other way (a systemd **system** unit, a foreground process, etc.), see
12
+ "Upgrading a self-managed service" below.
13
+
14
+ ## What was fixed
15
+
16
+ - `upgrade` no longer crashes when run from a directory without
17
+ `agent-runner.toml`.
18
+ - `upgrade` handles Debian 12 / PEP 668 `externally-managed-environment`:
19
+ outside a venv it retries pip with `--break-system-packages` (plus `--user`
20
+ when the install lives in user-site).
21
+ - `upgrade` no longer silently no-ops on a non-user-managed service, and never
22
+ suggests `agent-runner start` (which would start a second, conflicting
23
+ supervisor next to your real one).
24
+
25
+ ## How `upgrade` behaves now
26
+
27
+ - **systemd --user service** → full flow: stop → pip → smoke → start →
28
+ `service_upgraded`, with auto-rollback on smoke failure. Unchanged.
29
+ - **Anything else** (system unit / foreground / no config / `--no-restart`) →
30
+ **package-only**: PEP668-aware pip + `--version` smoke + pip-level rollback,
31
+ then it emits `package_upgraded` and prints the restart command for you to run.
32
+ It does not touch your running service.
33
+
34
+ ## Upgrading a self-managed service (e.g. systemd system unit)
35
+
36
+ agent-runner never runs `sudo` and does not manage a unit it did not create.
37
+ The canonical recipe:
38
+
39
+ ```bash
40
+ # 1. Upgrade the package (PEP 668: --break-system-packages only touches ~/.local)
41
+ python3 -m pip install --user --break-system-packages --upgrade cli-agent-runner==0.1.37
42
+ # (or: agent-runner upgrade --target 0.1.37 --no-restart — does the pip + smoke for you)
43
+
44
+ # 2. Verify
45
+ agent-runner --version # → 0.1.37
46
+ python3 -c "import your_plugin" # if you use a plugin
47
+
48
+ # 3. Restart your supervisor so the long-running process loads the new code
49
+ sudo systemctl restart <your-unit>
50
+ ```
51
+
52
+ Do **not** run `agent-runner start` on a system-unit host — it would spawn a
53
+ second supervisor alongside the one systemd manages.
54
+
55
+ ## `package_upgraded` vs `service_upgraded`
56
+
57
+ - `service_upgraded` — the live service is now running the new version (emitted
58
+ only in the orchestrated systemd --user flow).
59
+ - `package_upgraded` — the on-disk package changed but the running supervisor
60
+ still runs the old code until you restart it (`restart_deferred: true`).
61
+
62
+ ## What did NOT change
63
+
64
+ - The systemd --user happy path (the 0.1.13 flow) and its auto-rollback events.
65
+ - Smoke design (fresh-subprocess `--version`, plus `peek` in the orchestrated flow).
@@ -149,28 +149,35 @@ RestartSec=5
149
149
 
150
150
  ## Upgrading agent-runner
151
151
 
152
- ### Recommended: single command
152
+ `upgrade` detects the deployment topology and takes the safe path for it:
153
153
 
154
- ```
155
- agent-runner upgrade [--target X.Y.Z] --config /path/to/agent-runner.toml
156
- ```
154
+ ### Path 1 — systemd --user service (installed via `agent-runner install`)
155
+
156
+ agent-runner upgrade --target 0.1.37
157
+
158
+ Does stop → pip → smoke → start, with auto-rollback on smoke failure.
157
159
 
158
160
  `--target` defaults to the latest version on PyPI. To pin a specific
159
161
  version (or roll back), pass `--target X.Y.Z`.
160
162
 
161
- ### What it does
163
+ ### Path 2 — self-managed service (systemd system unit, foreground, etc.)
164
+
165
+ `agent-runner upgrade` detects it does not manage your service and does a
166
+ package-only upgrade (pip + smoke + rollback), then prints the restart command.
167
+ It never runs `sudo` and never starts a service it didn't install. Restart your
168
+ supervisor yourself:
169
+
170
+ python3 -m pip install --user --break-system-packages --upgrade cli-agent-runner==0.1.37
171
+ agent-runner --version
172
+ sudo systemctl restart <your-unit>
173
+
174
+ > Do NOT run `agent-runner start` on a system-unit host — it spawns a second
175
+ > supervisor next to the one systemd manages.
176
+
177
+ Use `--no-restart` to force package-only mode even on a systemd --user host
178
+ (upgrade the package now, restart later):
162
179
 
163
- 1. Capture the currently-installed version via `agent_runner.__version__`
164
- 2. Graceful stop (waits for the current round to finish)
165
- 3. `pip install --upgrade cli-agent-runner[==<target>]`
166
- 4. Smoke check the new binary in a fresh subprocess: `agent-runner --version`
167
- + `agent-runner peek --json --config <path>`
168
- 5. If smoke passes: start service. Emit `service_upgraded` event.
169
- 6. If smoke fails: roll back to the previous version via
170
- `pip install --force-reinstall cli-agent-runner==<previous>`, sanity-smoke,
171
- start service, emit `service_upgrade_rolled_back` event. Exit code 1.
172
- 7. If rollback itself fails (rare): emit `service_upgrade_rollback_failed`
173
- event. Service stopped. Exit code 2. Manual intervention required.
180
+ agent-runner upgrade --target 0.1.37 --no-restart
174
181
 
175
182
  ### Manual rollback
176
183
 
@@ -188,19 +195,20 @@ fetch from there. To verify your index before upgrading: `pip config list`.
188
195
 
189
196
  | Symptom | Recovery |
190
197
  |---|---|
191
- | Stop is stuck | `agent-runner kill` → manual `pip install --upgrade ...` → `agent-runner start` |
192
- | pip install fails (network / no PyPI) | Service is left stopped. Run `agent-runner start` to resume the previous version. Retry upgrade later. |
193
- | Smoke fails, rollback succeeds | Service running on previous version. Investigate via `journalctl --user -u agent-runner@<project>` and the `service_upgrade_rolled_back` event's `failure_reason` field. File a bug report. |
194
- | Smoke fails, rollback ALSO fails (rare) | Service stopped. `service_upgrade_rollback_failed` event written (best-effort). Manually: `pip install --force-reinstall cli-agent-runner==<known-good>` then `systemctl restart agent-runner@<project>`. |
198
+ | Stop is stuck (user mode) | `agent-runner kill` → manual `pip install --upgrade ...` → `agent-runner start` |
199
+ | pip install fails (network / no PyPI) | Orchestrated: service left stopped, run `agent-runner start`. Package-only: service untouched, retry upgrade later. |
200
+ | Smoke fails, rollback succeeds | Orchestrated: service running on previous version. Package-only: on-disk package restored. |
201
+ | Smoke fails, rollback ALSO fails (rare) | Orchestrated: `service_upgrade_rollback_failed` event (service stopped). Manually: `pip install --force-reinstall cli-agent-runner==<known-good>` then `systemctl restart agent-runner@<project>`. |
195
202
 
196
203
  ### Postmortem trail
197
204
 
198
205
  Grep events.jsonl for upgrade history:
199
206
  ```
200
- grep -E "service_upgrad" {log_dir}/events-*.jsonl | jq .
207
+ grep -E "service_upgrad|package_upgraded" {log_dir}/events-*.jsonl | jq .
201
208
  ```
202
- Three event kinds are interesting:
203
- - `service_upgraded` — clean upgrade
209
+ Event kinds:
210
+ - `service_upgraded` — clean orchestrated upgrade (live service on new version)
211
+ - `package_upgraded` — package updated, restart deferred to operator
204
212
  - `service_upgrade_rolled_back` — attempted upgrade reverted (safety net fired)
205
213
  - `service_upgrade_rollback_failed` — critical: needs manual intervention
206
214
 
@@ -213,3 +213,10 @@ def test_given_agent_usage_recorded_constant_when_exported_then_matches_kind():
213
213
 
214
214
  assert AGENT_USAGE_RECORDED == "agent_usage_recorded"
215
215
  assert AGENT_USAGE_RECORDED in _BUILTIN_KINDS
216
+
217
+
218
+ def test_given_package_upgraded_kind_when_registered_then_in_known_event_kinds() -> None:
219
+ from agent_runner.events import _BUILTIN_KINDS, PACKAGE_UPGRADED
220
+
221
+ assert PACKAGE_UPGRADED == "package_upgraded"
222
+ assert PACKAGE_UPGRADED in _BUILTIN_KINDS