cli-agent-runner 0.2.7__tar.gz → 0.2.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/.vulture-whitelist.py +10 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/CHANGELOG.md +11 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/PKG-INFO +9 -4
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/README.md +8 -3
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/_docgen.py +66 -6
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/_emit.py +10 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/_version.py +2 -2
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/api.py +1 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/cli/__init__.py +2 -0
- cli_agent_runner-0.2.8/agent_runner/cli/migrate_cmd.py +51 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/cli/upgrade_cmd.py +56 -3
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/config.py +3 -4
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/events.py +1 -0
- cli_agent_runner-0.2.8/agent_runner/migrations.py +89 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/presets/codewhale.toml +1 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/architecture.md +1 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/commands.md +55 -25
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/configuration.md +1 -1
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/long-running-agents.md +1 -7
- cli_agent_runner-0.2.8/docs/migrations/0.2.8.md +79 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/recipes/aider.md +10 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/recipes/codewhale.md +12 -1
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/runbook.md +9 -4
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_architecture.py +3 -1
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_doc_claims_match_ssot.py +14 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_doc_cli_claims.py +6 -2
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_doc_operator_surface.py +14 -2
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_entry_points_resolve.py +1 -3
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_config.py +23 -1
- cli_agent_runner-0.2.8/tests/unit/test_emit_config_migrated.py +24 -0
- cli_agent_runner-0.2.8/tests/unit/test_migrate_cmd.py +85 -0
- cli_agent_runner-0.2.8/tests/unit/test_migrations.py +59 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_presets.py +24 -2
- cli_agent_runner-0.2.8/tests/unit/test_upgrade_migrate.py +91 -0
- cli_agent_runner-0.2.7/docs/migrations/0.1.16.md +0 -97
- cli_agent_runner-0.2.7/docs/migrations/0.1.17.md +0 -80
- cli_agent_runner-0.2.7/docs/migrations/0.1.19.md +0 -61
- cli_agent_runner-0.2.7/docs/migrations/0.1.20.md +0 -80
- cli_agent_runner-0.2.7/docs/migrations/0.1.21.md +0 -126
- cli_agent_runner-0.2.7/docs/migrations/0.1.22.md +0 -93
- cli_agent_runner-0.2.7/docs/migrations/0.1.23.md +0 -132
- cli_agent_runner-0.2.7/docs/migrations/0.1.24.md +0 -124
- cli_agent_runner-0.2.7/docs/migrations/0.1.25.md +0 -96
- cli_agent_runner-0.2.7/docs/migrations/0.1.26.md +0 -135
- cli_agent_runner-0.2.7/docs/migrations/0.1.27.md +0 -176
- cli_agent_runner-0.2.7/docs/migrations/0.1.29.md +0 -90
- cli_agent_runner-0.2.7/docs/migrations/0.1.30.md +0 -73
- cli_agent_runner-0.2.7/docs/migrations/0.1.31.md +0 -89
- cli_agent_runner-0.2.7/docs/migrations/0.1.32.md +0 -113
- cli_agent_runner-0.2.7/docs/migrations/0.1.33.md +0 -88
- cli_agent_runner-0.2.7/docs/migrations/0.1.34.md +0 -110
- cli_agent_runner-0.2.7/docs/migrations/0.1.35.md +0 -98
- cli_agent_runner-0.2.7/docs/migrations/0.1.36.md +0 -73
- cli_agent_runner-0.2.7/docs/migrations/0.1.37.md +0 -65
- cli_agent_runner-0.2.7/docs/migrations/0.1.38.md +0 -53
- cli_agent_runner-0.2.7/docs/migrations/0.1.39.md +0 -74
- cli_agent_runner-0.2.7/docs/migrations/0.1.40.md +0 -42
- cli_agent_runner-0.2.7/docs/migrations/0.1.42.md +0 -58
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/.codecov.yml +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/.github/workflows/ci.yml +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/.github/workflows/release.yml +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/.gitignore +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/BACKLOG.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/CODE_OF_CONDUCT.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/CONTRIBUTING.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/LICENSE +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/README.zh.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/SECURITY.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/__init__.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/_redact.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/_registry.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/_substrate.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/_throttle.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/agent_runtime.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/api_types.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/__init__.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/_constants.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/claude_rate_limit.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/codewhale.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/default_dirty_handler.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/gemini.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/kimi.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/builtin_plugins/pi.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/cli/__main__.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/cli/common.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/cli/events_cmd.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/cli/init_cmd.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/cli/install_cmd.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/cli/monitor_cmd.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/cli/peek_cmd.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/cli/round_cmd.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/cli/serve_cmd.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/cli/service_cmd.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/context_store.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/defenses.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/hooks.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/http_progress.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/lifecycle.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/metrics.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/monitor.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/presets/__init__.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/presets/aider.toml +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/presets/claude.toml +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/presets/gemini.toml +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/presets/kimi.toml +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/presets/pi.toml +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/prompt_loader.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/remote_relay.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/round_log.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/round_view.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/runner.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/scaffold.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/schedule.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/service_unit.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/startup_check.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/agent_runner/vcs_state.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/build.sh +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/README.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/events.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/migrations/0.1.28.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/migrations/0.2.0.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/migrations/0.2.1.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/migrations/0.2.2.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/migrations/0.2.3.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/migrations/0.2.4.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/migrations/0.2.5.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/migrations/0.2.6.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/migrations/0.2.7.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/plugins.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/quickstart.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/recipes/kimi.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/recipes/pi.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/docs/thesis.md +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/pyproject.toml +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/__init__.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/_test_helpers.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/conftest.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/contract/__init__.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/contract/test_public_api_surface.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/e2e/__init__.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/e2e/conftest.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/e2e/test_e2e_graceful_stop.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/e2e/test_e2e_install_systemd.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/e2e/test_e2e_monitor_remote.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/fixtures/cli-real-output/claude-2.1.143-assistant-tool-use.jsonl +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/fixtures/cli-real-output/claude-2.1.143-result-event.jsonl +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/fixtures/cli-real-output/gemini-0.42.0-result-event.jsonl +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/generate_vulture_whitelist.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/__init__.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_bounded_run.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_context_enricher_namespacing.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_dirty_handler_seam.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_fresh_eyes_signal.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_grace_kill_emission.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_install_dry_run.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_monitor_seeded.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_plugin_detector_loaded.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_plugin_owned_paths.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_plugin_real_flow.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_prompt_delivery_stdin.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_run_one_round_with_fake_agent.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_scaffold_presets.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_serve_loop.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_substrate_fingerprint.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/integration/test_transient_error_backoff.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/__init__.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/_event_scan.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_atomic_write_enforced.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_auto_stop_policy_ssot.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_catalogs.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_classification_ssot.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_config_error_consistency.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_config_value_set_ssot.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_doc_builtin_plugins.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_doc_transient_error_claims.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_docs_generated.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_docs_index_complete.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_event_kind_registry.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_event_kinds_ssot.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_events_doc_contract.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_hook_contract_docs.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_layer_2_loop_size.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_module_boundaries.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_module_sizes.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_no_ai_signatures.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_peek_schema_version.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_plugins_config_stable.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_readme_zh_is_pointer.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_round_result_stable.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_set_diff_for_auto_tool_classification.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_upstream_schema_canary.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/invariants/test_vulture_whitelist_generated.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/literate/__init__.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/literate/parser.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/literate/test_parser.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/literate/test_quickstart.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/__init__.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_agent_runtime.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_agent_runtime_grace.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_agent_runtime_progress.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_agent_runtime_signal_name.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_api_assemble_prompt.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_api_events_stream.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_api_install.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_api_observation.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_api_read_round_num.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_api_resolve_phase.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_api_service.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_api_types.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_auto_stop_gating.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_catalogs.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_claude_error_detector.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_cli.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_cli_common.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_cli_init_install.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_cli_monitor_http.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_cli_service_peek_monitor.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_cli_upgrade.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_codewhale_plugin.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_config_fresh_eyes.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_config_max_rounds.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_config_prompt_delivery.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_config_schedule.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_config_stop_file.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_config_transient_error_action.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_context_store.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_default_dirty_handler.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_defenses.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_detector_protocol.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_dirty_handlers.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_docgen.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_emit_schedule.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_events.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_events_cmd.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_events_cmd_tail.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_exit_cause.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_fresh_eyes_trigger.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_gemini_plugin.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_hook_failure_isolation.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_hooks.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_http_progress.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_init_entry_points.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_kimi_plugin.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_lifecycle.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_metrics.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_monitor_assembly.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_monitor_detectors.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_monitor_remote.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_monitor_schedule_pause.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_network_blip_signal_guard.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_peek_argparse.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_peek_schedule.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_peek_select.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_pi_plugin.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_plugin_constants.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_prompt_loader.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_redact.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_round_log_helpers.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_round_view.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_runner.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_runner_throttle.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_scaffold.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_schedule.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_serve_cmd_bounded.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_serve_config_broken.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_serve_crash_loop.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_serve_round_log.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_serve_schedule_gate.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_serve_sentinel.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_serve_startup_hooks.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_service_unit.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_startup_check.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_substrate.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_throttle_plugin_classification.py +0 -0
- {cli_agent_runner-0.2.7 → cli_agent_runner-0.2.8}/tests/unit/test_vcs_state.py +0 -0
|
@@ -232,6 +232,16 @@ stash_idempotency_s
|
|
|
232
232
|
# agent_runner.lifecycle.PIDFile
|
|
233
233
|
path
|
|
234
234
|
|
|
235
|
+
# agent_runner.migrations.Migration
|
|
236
|
+
detect
|
|
237
|
+
apply
|
|
238
|
+
describe
|
|
239
|
+
|
|
240
|
+
# agent_runner.migrations.MigrationResult
|
|
241
|
+
new_text
|
|
242
|
+
applied
|
|
243
|
+
manual
|
|
244
|
+
|
|
235
245
|
# agent_runner.monitor.LocalSource
|
|
236
246
|
log_dir
|
|
237
247
|
|
|
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.8] - 2026-08-25
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `agent-runner migrate [--dry-run]` rewrites removed/renamed fields in `agent-runner.toml` to their current form, keeping a `.bak`; reports fields needing a manual change.
|
|
12
|
+
- `upgrade` auto-migrates the config before the package/service step and emits a `config_migrated` event; a config needing a manual change aborts the run before it stops the service. `--no-migrate` skips it.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- Removed-field `ConfigError` messages now point to `agent-runner migrate`.
|
|
16
|
+
|
|
17
|
+
See `docs/migrations/0.2.8.md`.
|
|
18
|
+
|
|
8
19
|
## [0.2.7] - 2026-08-25
|
|
9
20
|
|
|
10
21
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cli-agent-runner
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: Restart-on-exit supervisor for autonomous CLI agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/wan9yu/cli-agent-runner
|
|
6
6
|
Project-URL: Documentation, https://github.com/wan9yu/cli-agent-runner#readme
|
|
@@ -51,7 +51,7 @@ full disks, runaway memory.
|
|
|
51
51
|
┌──────────────────────────────────────────┐
|
|
52
52
|
│ Layer 3: The Witness (monitor) │ 11 detectors + auto-stop
|
|
53
53
|
├──────────────────────────────────────────┤
|
|
54
|
-
│ Layer 2: The Loop (serve
|
|
54
|
+
│ Layer 2: The Loop (serve) │ signal-trapping restart loop
|
|
55
55
|
├──────────────────────────────────────────┤
|
|
56
56
|
│ Layer 1: The Round (round) │ one agent invocation
|
|
57
57
|
└──────────────────────────────────────────┘
|
|
@@ -80,14 +80,15 @@ agent-runner monitor # live anomaly detection
|
|
|
80
80
|
|
|
81
81
|
Full walkthrough: [`docs/quickstart.md`](docs/quickstart.md).
|
|
82
82
|
|
|
83
|
-
##
|
|
83
|
+
## 16 verbs
|
|
84
84
|
|
|
85
85
|
| Lifecycle | Observation |
|
|
86
86
|
|---|---|
|
|
87
87
|
| `init` / `install` / `uninstall` | `peek` — state snapshot |
|
|
88
88
|
| `start` / `stop` / `kill` | `watch` — peek in a refresh loop |
|
|
89
89
|
| `restart` / `status` | `monitor` — 11 detectors, alerts, auto-stop |
|
|
90
|
-
| `round` / `serve`
|
|
90
|
+
| `round` / `serve` | `events` — query / stream events.jsonl |
|
|
91
|
+
| `upgrade` / `migrate` | |
|
|
91
92
|
|
|
92
93
|
Verb reference: [`docs/commands.md`](docs/commands.md).
|
|
93
94
|
|
|
@@ -106,6 +107,10 @@ guards it. Highlights:
|
|
|
106
107
|
|
|
107
108
|
Full list and rationale: [`docs/architecture.md`](docs/architecture.md).
|
|
108
109
|
|
|
110
|
+
Optionally pause the loop during off-hours: `[schedule]` run/pause windows gate
|
|
111
|
+
`serve` (override with `serve --ignore-schedule`) — see the `[schedule]` section
|
|
112
|
+
of [`docs/configuration.md`](docs/configuration.md).
|
|
113
|
+
|
|
109
114
|
## Monitor: 11 detectors
|
|
110
115
|
|
|
111
116
|
Notify only: `timeout_rate`, `hung`, `orphan_chain`, `disk_warning`,
|
|
@@ -15,7 +15,7 @@ full disks, runaway memory.
|
|
|
15
15
|
┌──────────────────────────────────────────┐
|
|
16
16
|
│ Layer 3: The Witness (monitor) │ 11 detectors + auto-stop
|
|
17
17
|
├──────────────────────────────────────────┤
|
|
18
|
-
│ Layer 2: The Loop (serve
|
|
18
|
+
│ Layer 2: The Loop (serve) │ signal-trapping restart loop
|
|
19
19
|
├──────────────────────────────────────────┤
|
|
20
20
|
│ Layer 1: The Round (round) │ one agent invocation
|
|
21
21
|
└──────────────────────────────────────────┘
|
|
@@ -44,14 +44,15 @@ agent-runner monitor # live anomaly detection
|
|
|
44
44
|
|
|
45
45
|
Full walkthrough: [`docs/quickstart.md`](docs/quickstart.md).
|
|
46
46
|
|
|
47
|
-
##
|
|
47
|
+
## 16 verbs
|
|
48
48
|
|
|
49
49
|
| Lifecycle | Observation |
|
|
50
50
|
|---|---|
|
|
51
51
|
| `init` / `install` / `uninstall` | `peek` — state snapshot |
|
|
52
52
|
| `start` / `stop` / `kill` | `watch` — peek in a refresh loop |
|
|
53
53
|
| `restart` / `status` | `monitor` — 11 detectors, alerts, auto-stop |
|
|
54
|
-
| `round` / `serve`
|
|
54
|
+
| `round` / `serve` | `events` — query / stream events.jsonl |
|
|
55
|
+
| `upgrade` / `migrate` | |
|
|
55
56
|
|
|
56
57
|
Verb reference: [`docs/commands.md`](docs/commands.md).
|
|
57
58
|
|
|
@@ -70,6 +71,10 @@ guards it. Highlights:
|
|
|
70
71
|
|
|
71
72
|
Full list and rationale: [`docs/architecture.md`](docs/architecture.md).
|
|
72
73
|
|
|
74
|
+
Optionally pause the loop during off-hours: `[schedule]` run/pause windows gate
|
|
75
|
+
`serve` (override with `serve --ignore-schedule`) — see the `[schedule]` section
|
|
76
|
+
of [`docs/configuration.md`](docs/configuration.md).
|
|
77
|
+
|
|
73
78
|
## Monitor: 11 detectors
|
|
74
79
|
|
|
75
80
|
Notify only: `timeout_rate`, `hung`, `orphan_chain`, `disk_warning`,
|
|
@@ -7,12 +7,16 @@ renderer registry so the substitution rule is testable in isolation.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import argparse
|
|
10
11
|
import dataclasses
|
|
12
|
+
import functools
|
|
13
|
+
import importlib.resources
|
|
11
14
|
import re
|
|
12
15
|
import typing
|
|
13
16
|
from collections.abc import Callable
|
|
14
17
|
from pathlib import Path
|
|
15
18
|
|
|
19
|
+
from agent_runner.cli.init_cmd import _preset_names
|
|
16
20
|
from agent_runner.config import (
|
|
17
21
|
AgentConfig,
|
|
18
22
|
Config,
|
|
@@ -152,18 +156,53 @@ def render_event_kinds_list() -> str:
|
|
|
152
156
|
return "\n".join(f"- `{k}`" for k in sorted(KNOWN_EVENT_KINDS))
|
|
153
157
|
|
|
154
158
|
|
|
155
|
-
def
|
|
156
|
-
"""
|
|
159
|
+
def _subparser_action() -> argparse.Action:
|
|
160
|
+
"""The single ``_SubParsersAction`` on the top-level parser."""
|
|
157
161
|
from agent_runner.cli import _build_parser
|
|
158
162
|
|
|
159
163
|
parser = _build_parser()
|
|
160
|
-
|
|
161
|
-
|
|
164
|
+
return next(a for a in parser._actions if a.__class__.__name__ == "_SubParsersAction")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _subparser_choices() -> dict[str, argparse.ArgumentParser]:
|
|
168
|
+
"""Map ``verb -> subparser`` for the CLI, the SSOT both verb renderers use."""
|
|
169
|
+
return _subparser_action().choices
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _render_verb_flags(verb: str) -> str:
|
|
173
|
+
"""Bullet list of a verb's own option flags, from its argparse subparser.
|
|
174
|
+
|
|
175
|
+
Parent-inherited ``--config`` / ``--json`` carry ``help=SUPPRESS`` on the
|
|
176
|
+
shared parent and are filtered here, so only verb-specific flags render.
|
|
177
|
+
"""
|
|
178
|
+
choices = _subparser_choices()
|
|
179
|
+
lines: list[str] = []
|
|
180
|
+
for act in choices[verb]._actions:
|
|
181
|
+
if not act.option_strings: # positionals
|
|
182
|
+
continue
|
|
183
|
+
if isinstance(act, argparse._HelpAction): # -h/--help
|
|
184
|
+
continue
|
|
185
|
+
if act.help is argparse.SUPPRESS: # parent --config/--json
|
|
186
|
+
continue
|
|
187
|
+
name = max(act.option_strings, key=len)
|
|
188
|
+
if act.choices:
|
|
189
|
+
val = " {" + ",".join(map(str, act.choices)) + "}"
|
|
190
|
+
elif act.metavar:
|
|
191
|
+
val = f" {act.metavar}"
|
|
192
|
+
else:
|
|
193
|
+
val = ""
|
|
194
|
+
lines.append(f"- `{name}{val}` — {act.help}")
|
|
195
|
+
return "\n".join(lines)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def render_verb_table() -> str:
|
|
199
|
+
"""Walk the argparse subparsers and render a verb table."""
|
|
200
|
+
sub_action = _subparser_action()
|
|
162
201
|
rows = [
|
|
163
202
|
"| Verb | Description |",
|
|
164
203
|
"|---|---|",
|
|
165
204
|
]
|
|
166
|
-
for verb
|
|
205
|
+
for verb in sub_action.choices:
|
|
167
206
|
# Argparse stores help text via `sub_action._choices_actions` indexed by add order.
|
|
168
207
|
help_text = (
|
|
169
208
|
next(
|
|
@@ -176,6 +215,18 @@ def render_verb_table() -> str:
|
|
|
176
215
|
return "\n".join(rows)
|
|
177
216
|
|
|
178
217
|
|
|
218
|
+
def _render_preset(name: str) -> str:
|
|
219
|
+
"""Fenced TOML block quoting a shipped ``agent_runner/presets/<name>.toml``.
|
|
220
|
+
|
|
221
|
+
The preset file is the SSOT; the recipe doc block is a verbatim mirror so a
|
|
222
|
+
flag or auth-hint edit in the preset can never drift from its recipe.
|
|
223
|
+
"""
|
|
224
|
+
body = (importlib.resources.files("agent_runner.presets") / f"{name}.toml").read_text(
|
|
225
|
+
encoding="utf-8"
|
|
226
|
+
)
|
|
227
|
+
return "```toml\n" + body.rstrip() + "\n```"
|
|
228
|
+
|
|
229
|
+
|
|
179
230
|
RENDERERS: dict[str, Callable[[], str]] = {
|
|
180
231
|
"defenses-table": render_defenses_table,
|
|
181
232
|
"detector-list": render_detector_list,
|
|
@@ -183,6 +234,13 @@ RENDERERS: dict[str, Callable[[], str]] = {
|
|
|
183
234
|
"config-schema": render_config_schema_table,
|
|
184
235
|
"verb-table": render_verb_table,
|
|
185
236
|
}
|
|
237
|
+
RENDERERS.update(
|
|
238
|
+
{
|
|
239
|
+
f"flags-{v}": functools.partial(_render_verb_flags, v)
|
|
240
|
+
for v in ("init", "monitor", "serve", "migrate")
|
|
241
|
+
}
|
|
242
|
+
)
|
|
243
|
+
RENDERERS.update({f"preset-{n}": functools.partial(_render_preset, n) for n in _preset_names()})
|
|
186
244
|
|
|
187
245
|
_GEN_OPEN = re.compile(r"<!-- gen:([a-z0-9-]+) -->")
|
|
188
246
|
|
|
@@ -196,7 +254,9 @@ def render(docs_dir: Path, *, write: bool = True) -> dict[Path, str]:
|
|
|
196
254
|
Raises ``ValueError`` when a marker references an unknown renderer name.
|
|
197
255
|
"""
|
|
198
256
|
out: dict[Path, str] = {}
|
|
199
|
-
|
|
257
|
+
# Top-level docs plus recipes/. NOT rglob: migrations/ is frozen history and
|
|
258
|
+
# internal/ is gitignored — neither should be regenerated.
|
|
259
|
+
for md in sorted(docs_dir.glob("*.md")) + sorted((docs_dir / "recipes").glob("*.md")):
|
|
200
260
|
text = md.read_text(encoding="utf-8")
|
|
201
261
|
for match in _GEN_OPEN.finditer(text):
|
|
202
262
|
name = match.group(1)
|
|
@@ -17,6 +17,7 @@ __all__ = [
|
|
|
17
17
|
"emit_agent_auth_error_detected",
|
|
18
18
|
"emit_agent_usage_recorded",
|
|
19
19
|
"emit_anomaly_repetitive_tool",
|
|
20
|
+
"emit_config_migrated",
|
|
20
21
|
"emit_fresh_eyes_round_triggered",
|
|
21
22
|
"emit_max_rounds_reached",
|
|
22
23
|
"emit_rate_limit_stop",
|
|
@@ -72,6 +73,15 @@ def emit_crash_loop(log_dir: Path, *, consecutive: int, exit_code: int, log_path
|
|
|
72
73
|
emit(log_dir, CRASH_LOOP, consecutive=consecutive, exit_code=exit_code, reason=reason)
|
|
73
74
|
|
|
74
75
|
|
|
76
|
+
def emit_config_migrated(
|
|
77
|
+
log_dir: Path, *, applied: list[str], manual: list[str], path: str
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Emit config_migrated when `migrate`/`upgrade` rewrites the config."""
|
|
80
|
+
from agent_runner.events import CONFIG_MIGRATED, emit
|
|
81
|
+
|
|
82
|
+
emit(log_dir, CONFIG_MIGRATED, applied=applied, manual=manual, path=path)
|
|
83
|
+
|
|
84
|
+
|
|
75
85
|
def emit_stop_file_detected(
|
|
76
86
|
log_dir: Path, *, stop_file: Path, content: str, rounds_completed: int
|
|
77
87
|
) -> None:
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.8'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 8)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -737,6 +737,7 @@ from agent_runner._emit import ( # noqa: E402,F401 — intentional bottom re-ex
|
|
|
737
737
|
emit_agent_usage_recorded,
|
|
738
738
|
emit_anomaly_repetitive_tool,
|
|
739
739
|
emit_config_broken,
|
|
740
|
+
emit_config_migrated,
|
|
740
741
|
emit_crash_loop,
|
|
741
742
|
emit_fresh_eyes_round_triggered,
|
|
742
743
|
emit_max_rounds_reached,
|
|
@@ -20,6 +20,7 @@ from agent_runner.cli import (
|
|
|
20
20
|
events_cmd,
|
|
21
21
|
init_cmd,
|
|
22
22
|
install_cmd,
|
|
23
|
+
migrate_cmd,
|
|
23
24
|
monitor_cmd,
|
|
24
25
|
peek_cmd,
|
|
25
26
|
round_cmd,
|
|
@@ -69,6 +70,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
69
70
|
serve_cmd.add_parser(sub, parent)
|
|
70
71
|
round_cmd.add_parser(sub, parent)
|
|
71
72
|
upgrade_cmd.add_parser(sub, parent)
|
|
73
|
+
migrate_cmd.add_parser(sub, parent)
|
|
72
74
|
|
|
73
75
|
return parser
|
|
74
76
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""agent-runner migrate — rewrite removed/renamed config fields to current form."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import tomllib
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from agent_runner import migrations
|
|
9
|
+
from agent_runner.cli.common import fail # `fail(msg, *, code=1)` prints to stderr, returns code
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def add_parser(sub, parent) -> None:
|
|
13
|
+
p = sub.add_parser(
|
|
14
|
+
"migrate",
|
|
15
|
+
parents=[parent],
|
|
16
|
+
help="Rewrite removed/renamed fields in agent-runner.toml to current form",
|
|
17
|
+
)
|
|
18
|
+
p.add_argument("--dry-run", action="store_true", help="Show changes without writing the file")
|
|
19
|
+
p.set_defaults(func=cmd)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def cmd(args) -> int:
|
|
23
|
+
cfg_path = Path(getattr(args, "config", None) or "agent-runner.toml")
|
|
24
|
+
try:
|
|
25
|
+
text = cfg_path.read_text(encoding="utf-8")
|
|
26
|
+
except OSError as e:
|
|
27
|
+
return fail(f"cannot read {cfg_path}: {e}", code=2)
|
|
28
|
+
try:
|
|
29
|
+
parsed = tomllib.loads(text)
|
|
30
|
+
except tomllib.TOMLDecodeError as e:
|
|
31
|
+
return fail(f"{cfg_path} is not valid TOML: {e}", code=2)
|
|
32
|
+
|
|
33
|
+
result = migrations.run_migrations(text, parsed)
|
|
34
|
+
if not result.applied and not result.manual:
|
|
35
|
+
print(f"{cfg_path}: nothing to migrate")
|
|
36
|
+
return 0
|
|
37
|
+
|
|
38
|
+
for line in result.applied:
|
|
39
|
+
print(f" rewrite: {line}")
|
|
40
|
+
for line in result.manual:
|
|
41
|
+
print(f" MANUAL: {line}")
|
|
42
|
+
|
|
43
|
+
if args.dry_run:
|
|
44
|
+
print("(dry-run — no changes written)")
|
|
45
|
+
return 1 if result.manual else 0
|
|
46
|
+
|
|
47
|
+
if result.applied:
|
|
48
|
+
cfg_path.with_suffix(cfg_path.suffix + ".bak").write_text(text, encoding="utf-8")
|
|
49
|
+
cfg_path.write_text(result.new_text, encoding="utf-8")
|
|
50
|
+
print(f"wrote {cfg_path} (backup: {cfg_path.name}.bak)")
|
|
51
|
+
return 1 if result.manual else 0
|
|
@@ -16,13 +16,14 @@ import re
|
|
|
16
16
|
import subprocess
|
|
17
17
|
import sys
|
|
18
18
|
import time
|
|
19
|
+
import tomllib
|
|
19
20
|
from pathlib import Path
|
|
20
21
|
|
|
21
22
|
import agent_runner
|
|
22
|
-
from agent_runner import __version__, api, events
|
|
23
|
+
from agent_runner import __version__, api, events, migrations
|
|
23
24
|
from agent_runner.api_types import ServiceMode
|
|
24
25
|
from agent_runner.cli.common import cfg_from_args, fail, info
|
|
25
|
-
from agent_runner.config import Config
|
|
26
|
+
from agent_runner.config import Config, ConfigError
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
def add_parser(sub, parent) -> None:
|
|
@@ -48,11 +49,28 @@ def add_parser(sub, parent) -> None:
|
|
|
48
49
|
help="Upgrade the package + smoke only; do not stop/start the service "
|
|
49
50
|
"(you restart it yourself).",
|
|
50
51
|
)
|
|
52
|
+
p.add_argument(
|
|
53
|
+
"--no-migrate",
|
|
54
|
+
action="store_true",
|
|
55
|
+
help="Skip automatic config migration during upgrade",
|
|
56
|
+
)
|
|
51
57
|
p.set_defaults(func=cmd)
|
|
52
58
|
|
|
53
59
|
|
|
54
60
|
def cmd(args) -> int:
|
|
61
|
+
cfg_path = Path(args.config)
|
|
62
|
+
applied, manual = _migrate_config_file(cfg_path, no_migrate=getattr(args, "no_migrate", False))
|
|
63
|
+
if manual:
|
|
64
|
+
return fail(
|
|
65
|
+
"config needs manual migration before upgrade:\n "
|
|
66
|
+
+ "\n ".join(manual)
|
|
67
|
+
+ "\nfix it (or run `agent-runner migrate`) then retry"
|
|
68
|
+
) # aborts BEFORE any stop/pip — fail fast
|
|
55
69
|
cfg = _try_load_cfg(args)
|
|
70
|
+
if applied and cfg is not None:
|
|
71
|
+
api.emit_config_migrated(
|
|
72
|
+
cfg.runtime.log_dir, applied=applied, manual=[], path=str(cfg_path)
|
|
73
|
+
)
|
|
56
74
|
return _run_upgrade(
|
|
57
75
|
cfg,
|
|
58
76
|
target=args.target,
|
|
@@ -61,12 +79,47 @@ def cmd(args) -> int:
|
|
|
61
79
|
)
|
|
62
80
|
|
|
63
81
|
|
|
82
|
+
def _migrate_config_file(cfg_path: Path, *, no_migrate: bool) -> tuple[list[str], list[str]]:
|
|
83
|
+
"""Auto-migrate the raw config BEFORE load_config (which rejects old keys).
|
|
84
|
+
|
|
85
|
+
Only mutates when the migration is COMPLETE. If any transform needs a manual
|
|
86
|
+
fix, the caller aborts the upgrade — so we write NOTHING (no `.bak`, no
|
|
87
|
+
rewrite) and return ``([], manual)`` report-only, leaving the file untouched
|
|
88
|
+
for the user to run `agent-runner migrate` and re-upgrade. Only when there is
|
|
89
|
+
no manual remainder do we write the `.bak` + rewrite and return the applied
|
|
90
|
+
changes."""
|
|
91
|
+
if no_migrate or not cfg_path.exists():
|
|
92
|
+
return [], []
|
|
93
|
+
text = cfg_path.read_text(encoding="utf-8")
|
|
94
|
+
try:
|
|
95
|
+
parsed = tomllib.loads(text)
|
|
96
|
+
except tomllib.TOMLDecodeError:
|
|
97
|
+
return [], [] # broken TOML — let the normal load path report it
|
|
98
|
+
result = migrations.run_migrations(text, parsed)
|
|
99
|
+
if result.manual:
|
|
100
|
+
return [], result.manual # doomed upgrade → don't mutate the config
|
|
101
|
+
if result.applied:
|
|
102
|
+
cfg_path.with_suffix(cfg_path.suffix + ".bak").write_text(text, encoding="utf-8")
|
|
103
|
+
cfg_path.write_text(result.new_text, encoding="utf-8")
|
|
104
|
+
return result.applied, []
|
|
105
|
+
return [], []
|
|
106
|
+
|
|
107
|
+
|
|
64
108
|
def _try_load_cfg(args) -> Config | None:
|
|
65
|
-
"""Load the project config if present; None when absent (package-only).
|
|
109
|
+
"""Load the project config if present; None when absent (package-only).
|
|
110
|
+
|
|
111
|
+
Also degrades to None on a ConfigError (a still-un-migratable legacy config)
|
|
112
|
+
or a broken-TOML file: the package-only path is a safe fallback, and crashing
|
|
113
|
+
`upgrade` with a traceback over a removed key or a syntax error would be worse
|
|
114
|
+
than upgrading without a restart.
|
|
115
|
+
"""
|
|
66
116
|
try:
|
|
67
117
|
return cfg_from_args(args)
|
|
68
118
|
except FileNotFoundError:
|
|
69
119
|
return None
|
|
120
|
+
except (ConfigError, tomllib.TOMLDecodeError):
|
|
121
|
+
info("config did not load; proceeding with a package-only upgrade (no restart)")
|
|
122
|
+
return None
|
|
70
123
|
|
|
71
124
|
|
|
72
125
|
def _pip_env_flags() -> list[str]:
|
|
@@ -444,14 +444,14 @@ def load_config(toml_path: Path) -> Config:
|
|
|
444
444
|
if "round_timeout_per_phase" in runtime_d:
|
|
445
445
|
raise ConfigError(
|
|
446
446
|
"runtime.round_timeout_per_phase removed in 0.1.16; "
|
|
447
|
-
"use [phases.<name>] round_timeout_s = X
|
|
447
|
+
"use [phases.<name>] round_timeout_s = X. Run `agent-runner migrate`."
|
|
448
448
|
)
|
|
449
449
|
|
|
450
450
|
if runtime_d.get("rate_limit_action") is not None:
|
|
451
451
|
raise ConfigError(
|
|
452
452
|
"runtime.rate_limit_action was removed in 0.1.29; use "
|
|
453
453
|
"runtime.transient_error_action (same allowed values: "
|
|
454
|
-
"back_off / skip / stop).
|
|
454
|
+
"back_off / skip / stop). Run `agent-runner migrate`."
|
|
455
455
|
)
|
|
456
456
|
|
|
457
457
|
transient_error_action_raw = runtime_d.get("transient_error_action")
|
|
@@ -544,8 +544,7 @@ def load_config(toml_path: Path) -> Config:
|
|
|
544
544
|
vcs_d = raw.get("vcs", {})
|
|
545
545
|
if "orphan_action" in vcs_d:
|
|
546
546
|
raise ConfigError(
|
|
547
|
-
"vcs.orphan_action removed in 0.1.18; use vcs.dirty_action
|
|
548
|
-
"see docs/migrations/0.1.17.md"
|
|
547
|
+
"vcs.orphan_action removed in 0.1.18; use vcs.dirty_action. Run `agent-runner migrate`."
|
|
549
548
|
)
|
|
550
549
|
dirty_action = str(vcs_d.get("dirty_action", "stash"))
|
|
551
550
|
if dirty_action not in _VALID_DIRTY_ACTIONS:
|
|
@@ -34,6 +34,7 @@ AGENT_NETWORK_BLIP = "agent_network_blip"
|
|
|
34
34
|
AGENT_SPAWN = "agent_spawn"
|
|
35
35
|
AGENT_USAGE_RECORDED = "agent_usage_recorded"
|
|
36
36
|
CONFIG_BROKEN = "config_broken"
|
|
37
|
+
CONFIG_MIGRATED = "config_migrated"
|
|
37
38
|
CRASH_LOOP = "crash_loop"
|
|
38
39
|
DIRTY_AUTO_COMMITTED = "dirty_auto_committed"
|
|
39
40
|
DIRTY_COMMIT_FAILED = "dirty_commit_failed"
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Executable config migrations. A registry of transforms; each detects a
|
|
2
|
+
removed/renamed key in the parsed TOML and either renames it on the raw file
|
|
3
|
+
text (auto) or reports a manual instruction. Rewriting is targeted regex on the
|
|
4
|
+
raw text so comments and formatting are preserved — no TOML writer dependency."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
import tomllib
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class Migration:
|
|
16
|
+
detect: Callable[[dict], bool] # old key present in the parsed TOML?
|
|
17
|
+
apply: Callable[[str], str] | None # raw-text rewrite; None = manual-only
|
|
18
|
+
describe: str # report line (auto) or instruction (manual)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class MigrationResult:
|
|
23
|
+
new_text: str
|
|
24
|
+
applied: list[str]
|
|
25
|
+
manual: list[str]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _rename_key(old: str, new: str) -> Callable[[str], str]:
|
|
29
|
+
"""Rename a bare TOML assignment `old = ...` to `new = ...`, preserving indent,
|
|
30
|
+
spacing, value, and inline comment. Anchored to line-start assignments, so a
|
|
31
|
+
commented-out `# old = ...` or `old` inside a value/string is never matched."""
|
|
32
|
+
pat = re.compile(rf"(?m)^(?P<indent>[ \t]*){re.escape(old)}(?P<sp>[ \t]*=)")
|
|
33
|
+
return lambda text: pat.sub(rf"\g<indent>{new}\g<sp>", text)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
MIGRATIONS: list[Migration] = [
|
|
37
|
+
Migration(
|
|
38
|
+
detect=lambda p: "rate_limit_action" in p.get("runtime", {}),
|
|
39
|
+
apply=_rename_key("rate_limit_action", "transient_error_action"),
|
|
40
|
+
describe="runtime.rate_limit_action → runtime.transient_error_action",
|
|
41
|
+
),
|
|
42
|
+
Migration(
|
|
43
|
+
detect=lambda p: "orphan_action" in p.get("vcs", {}),
|
|
44
|
+
apply=_rename_key("orphan_action", "dirty_action"),
|
|
45
|
+
describe="vcs.orphan_action → vcs.dirty_action",
|
|
46
|
+
),
|
|
47
|
+
Migration(
|
|
48
|
+
detect=lambda p: "round_timeout_per_phase" in p.get("runtime", {}),
|
|
49
|
+
apply=None,
|
|
50
|
+
describe=(
|
|
51
|
+
"runtime.round_timeout_per_phase (removed 0.1.16) must be moved "
|
|
52
|
+
"manually to [phases.<name>] round_timeout_s"
|
|
53
|
+
),
|
|
54
|
+
),
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def run_migrations(text: str, parsed: dict) -> MigrationResult:
|
|
59
|
+
new_text = text
|
|
60
|
+
applied: list[str] = []
|
|
61
|
+
manual: list[str] = []
|
|
62
|
+
for m in MIGRATIONS:
|
|
63
|
+
if not m.detect(parsed):
|
|
64
|
+
continue
|
|
65
|
+
if m.apply is None:
|
|
66
|
+
manual.append(m.describe)
|
|
67
|
+
continue
|
|
68
|
+
rewritten = m.apply(new_text)
|
|
69
|
+
if rewritten == new_text:
|
|
70
|
+
# Detected but the line-anchored rename matched nothing (e.g. a
|
|
71
|
+
# dotted-key `runtime.rate_limit_action = ...` at top level) —
|
|
72
|
+
# route to manual so we never report "applied" on an unchanged file.
|
|
73
|
+
manual.append(m.describe)
|
|
74
|
+
continue
|
|
75
|
+
try:
|
|
76
|
+
tomllib.loads(rewritten)
|
|
77
|
+
except tomllib.TOMLDecodeError:
|
|
78
|
+
# The rename would collide with a target key that is already present
|
|
79
|
+
# (e.g. both `orphan_action` and `dirty_action` set), producing a
|
|
80
|
+
# duplicate assignment — invalid TOML. Never adopt it; hand the user
|
|
81
|
+
# a manual instruction instead so `migrate`/`upgrade` don't corrupt
|
|
82
|
+
# the file and then traceback on the next load.
|
|
83
|
+
manual.append(
|
|
84
|
+
m.describe + " (target key already present — remove the deprecated key manually)"
|
|
85
|
+
)
|
|
86
|
+
continue
|
|
87
|
+
new_text = rewritten
|
|
88
|
+
applied.append(m.describe)
|
|
89
|
+
return MigrationResult(new_text=new_text, applied=applied, manual=manual)
|
|
@@ -199,6 +199,7 @@ See `docs/plugins.md` for the `DirtyHandler` protocol and override recipe.
|
|
|
199
199
|
- `agent_usage_recorded`
|
|
200
200
|
- `anomaly_repetitive_tool`
|
|
201
201
|
- `config_broken`
|
|
202
|
+
- `config_migrated`
|
|
202
203
|
- `crash_loop`
|
|
203
204
|
- `dirty_auto_committed`
|
|
204
205
|
- `dirty_commit_failed`
|