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