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.
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/CHANGELOG.md +13 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/PKG-INFO +1 -1
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/_version.py +2 -2
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/upgrade_cmd.py +147 -20
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/events.py +1 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/architecture.md +1 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/commands.md +19 -1
- cli_agent_runner-0.1.37/docs/migrations/0.1.37.md +65 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/runbook.md +31 -23
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_catalogs.py +7 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_cli_upgrade.py +235 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.codecov.yml +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.github/workflows/ci.yml +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.github/workflows/release.yml +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.gitignore +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/.vulture-whitelist.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/CODE_OF_CONDUCT.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/CONTRIBUTING.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/LICENSE +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/README.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/README.zh.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/SECURITY.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/__init__.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/_docgen.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/_emit.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/_registry.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/_substrate.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/_throttle.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/agent_runtime.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/api.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/api_types.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/builtin_plugins/__init__.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/builtin_plugins/_constants.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/builtin_plugins/claude_rate_limit.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/builtin_plugins/gemini.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/__init__.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/__main__.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/common.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/events_cmd.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/init_cmd.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/install_cmd.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/monitor_cmd.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/peek_cmd.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/round_cmd.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/serve_cmd.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/cli/service_cmd.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/config.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/context_store.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/defenses.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/detector_helpers.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/hooks.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/http_progress.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/lifecycle.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/metrics.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/monitor.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/presets/__init__.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/presets/aider.toml +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/presets/claude.toml +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/presets/gemini.toml +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/prompt_loader.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/round_log.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/round_view.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/runner.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/scaffold.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/service_unit.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/startup_check.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/agent_runner/vcs_state.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/build.sh +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/deploy/example-agent-runner.toml +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/deploy/launchd.plist.tmpl +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/deploy/run-loop.sh +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/deploy/systemd.service.tmpl +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/README.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/configuration.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/events.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/long-running-agents.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/marketing/README.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/marketing/promo-cn.html +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.16.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.17.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.19.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.20.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.21.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.22.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.23.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.24.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.25.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.26.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.27.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.28.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.29.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.30.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.31.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.32.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.33.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.34.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.35.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/migrations/0.1.36.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/plugins.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/quickstart.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/recipes/aider.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/docs/thesis.md +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/pyproject.toml +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/__init__.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/_test_helpers.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/conftest.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/contract/__init__.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/contract/test_public_api_surface.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/e2e/__init__.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/e2e/conftest.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/e2e/test_e2e_graceful_stop.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/e2e/test_e2e_install_systemd.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/e2e/test_e2e_monitor_remote.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
- {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
- {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
- {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
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/__init__.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_bounded_run.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_context_enricher_namespacing.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_fresh_eyes_signal.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_grace_kill_emission.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_install_dry_run.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_monitor_seeded.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_plugin_detector_loaded.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_plugin_owned_paths.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_plugin_real_flow.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_run_one_round_with_fake_agent.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_scaffold_presets.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_serve_loop.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_substrate_fingerprint.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/integration/test_transient_error_backoff.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/__init__.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_architecture.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_atomic_write_enforced.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_classification_ssot.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_docs_generated.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_entry_points_resolve.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_event_kind_registry.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_event_kinds_ssot.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_events_doc_contract.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_layer_2_loop_size.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_module_boundaries.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_module_sizes.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_no_ai_signatures.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_peek_schema_version.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_round_result_stable.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/invariants/test_upstream_schema_canary.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/literate/__init__.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/literate/parser.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/literate/test_parser.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/literate/test_quickstart.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/__init__.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_agent_runtime.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_agent_runtime_grace.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_agent_runtime_progress.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_assemble_prompt.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_events_stream.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_install.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_observation.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_read_round_num.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_resolve_phase.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_service.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_api_types.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_auto_stop_gating.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_claude_error_detector.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_cli.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_cli_common.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_cli_init_install.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_cli_monitor_http.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_cli_service_peek_monitor.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_config.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_config_fresh_eyes.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_config_max_rounds.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_config_stop_file.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_config_transient_error_action.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_context_store.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_defenses.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_detector_helpers.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_detector_protocol.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_docgen.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_events.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_events_cmd.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_fresh_eyes_trigger.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_gemini_plugin.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_hook_failure_isolation.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_hooks.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_http_progress.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_init_entry_points.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_lifecycle.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_metrics.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_monitor_assembly.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_monitor_detectors.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_monitor_remote.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_peek_argparse.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_peek_select.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_presets.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_prompt_loader.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_round_log_helpers.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_round_view.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_runner.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_runner_throttle.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_scaffold.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_serve_cmd_bounded.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_serve_round_log.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_serve_sentinel.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_serve_startup_hooks.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_service_unit.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_startup_check.py +0 -0
- {cli_agent_runner-0.1.36 → cli_agent_runner-0.1.37}/tests/unit/test_substrate.py +0 -0
- {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.
|
|
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.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
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
|
-
"
|
|
32
|
-
"
|
|
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 =
|
|
48
|
-
return _run_upgrade(
|
|
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
|
|
52
|
-
"""
|
|
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
|
-
|
|
55
|
-
|
|
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
|
-
|
|
97
|
+
base = [sys.executable, "-m", "pip", "install", "--upgrade", spec]
|
|
58
98
|
if force_reinstall:
|
|
59
|
-
|
|
60
|
-
|
|
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(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
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` |
|
|
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
|
-
|
|
152
|
+
`upgrade` detects the deployment topology and takes the safe path for it:
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
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) |
|
|
193
|
-
| Smoke fails, rollback succeeds |
|
|
194
|
-
| Smoke fails, rollback ALSO fails (rare) |
|
|
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
|
-
|
|
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
|