cli-agent-runner 0.3.11__tar.gz → 0.3.12__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.3.11 → cli_agent_runner-0.3.12}/.vulture-whitelist.py +22 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/CHANGELOG.md +9 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/PKG-INFO +1 -1
- cli_agent_runner-0.3.12/agent_runner/_bounded.py +135 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_docgen.py +14 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_lifecycle.py +1 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_serve_policy.py +13 -5
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_version.py +2 -2
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/config/__init__.py +7 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/config/loader.py +107 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/config/models.py +71 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/config/parsers.py +59 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/events.py +2 -0
- cli_agent_runner-0.3.12/agent_runner/goal.py +428 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/runner.py +33 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/service_unit.py +3 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/vcs_state.py +12 -28
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/architecture.md +2 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/configuration.md +113 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/memory-efficiency.md +89 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/pyproject.toml +2 -1
- cli_agent_runner-0.3.12/tests/integration/test_goal_steering_e2e.py +479 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_config_error_consistency.py +1 -0
- cli_agent_runner-0.3.12/tests/invariants/test_goal_firewall.py +166 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_import_footprint.py +5 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_module_boundaries.py +32 -5
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_no_vacuous_invariants.py +1 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_timeout_budget_invariant.py +27 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_config.py +438 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_docgen.py +1 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_git_timeout.py +9 -4
- cli_agent_runner-0.3.12/tests/unit/test_goal_assessor.py +264 -0
- cli_agent_runner-0.3.12/tests/unit/test_goal_executor.py +222 -0
- cli_agent_runner-0.3.12/tests/unit/test_goal_firewall_behavioral.py +173 -0
- cli_agent_runner-0.3.12/tests/unit/test_goal_ledger.py +136 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_outer_round_ceiling.py +37 -0
- cli_agent_runner-0.3.12/tests/unit/test_run_bounded.py +133 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_runner.py +86 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_service_unit.py +35 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/.codecov.yml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/.github/dependabot.yml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/.github/workflows/ci.yml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/.github/workflows/release.yml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/.gitignore +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/BACKLOG.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/CODE_OF_CONDUCT.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/CONTRIBUTING.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/LICENSE +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/README.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/README.zh.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/SECURITY.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_emit/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_emit/agent.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_emit/memory.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_emit/rounds.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_emit/serve.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_install.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_monitor_detectors.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_monitor_registry.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_monitor_state.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_notify.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_observe.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_plugin_manifest.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_plugin_scan.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_procwait.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_redact.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_registry.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_resolve.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_round_outcome.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_round_support.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_substrate.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/_throttle.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/agent_runtime.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/api.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/api_types.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/builtin_plugins/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/builtin_plugins/_constants.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/builtin_plugins/claude_rate_limit.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/builtin_plugins/codewhale.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/builtin_plugins/gemini.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/builtin_plugins/kimi.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/builtin_plugins/pi.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/__main__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/_serve_cgroup.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/_serve_round.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/common.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/doctor_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/events_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/init_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/install_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/migrate_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/monitor_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/peek_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/round_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/serve_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/service_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/cli/upgrade_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/clock.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/config/errors.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/config/validators.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/context_store.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/defenses.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/event_log.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/hooks.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/host_health.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/http_progress.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/lifecycle.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/metrics.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/migrations.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/monitor.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/phase_select.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/presets/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/presets/aider.toml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/presets/claude.toml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/presets/codewhale.toml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/presets/gemini.toml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/presets/kimi.toml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/presets/pi.toml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/prompt_loader.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/remote_relay.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/round_log.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/round_view.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/scaffold.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/schedule.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/agent_runner/startup_check.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/build.sh +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/deploy/Dockerfile +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/deploy/docker-compose.yml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/README.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/commands.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/events.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/long-running-agents.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/migrations/0.2.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/migrations/0.3.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/plugins.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/quickstart.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/recipes/aider.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/recipes/codewhale.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/recipes/container-pi.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/recipes/deploy-all-in-one.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/recipes/kimi.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/recipes/pi.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/runbook.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/docs/thesis.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/_clock.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/_test_helpers.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/conftest.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/contract/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/contract/test_public_api_surface.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/e2e/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/e2e/conftest.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/e2e/test_e2e_graceful_stop.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/e2e/test_e2e_install_systemd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/e2e/test_e2e_monitor_remote.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/e2e/test_e2e_pi_resume.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/e2e/test_e2e_pre_oom.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/fixtures/cli-real-output/claude-2.1.143-assistant-tool-use.jsonl +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/fixtures/cli-real-output/claude-2.1.143-result-event.jsonl +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/fixtures/cli-real-output/gemini-0.42.0-result-event.jsonl +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/generate_vulture_whitelist.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_auto_stop_e2e.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_bounded_run.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_cooperative_grace_property.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_cooperative_stop_signal.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_fresh_eyes_signal.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_grace_kill_emission.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_install_dry_run.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_monitor_seeded.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_plugin_real_flow.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_prompt_delivery_stdin.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_reap_property.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_run_one_round_with_fake_agent.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_scaffold_presets.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_serve_loop.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_spawn_round_mem_floor.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_spawn_round_wedged.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_substrate_fingerprint.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/integration/test_transient_error_backoff.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/_docs.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/_event_scan.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_architecture.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_atomic_write_enforced.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_authored_facts_declared.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_auto_stop_policy_ssot.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_bdd_test_conventions.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_brake_defaults_off.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_catalogs.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_classification_ssot.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_config_value_set_ssot.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_container_stop_bound.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_dataclass_fields_have_producers.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_doc_builtin_plugins.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_doc_claims_match_ssot.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_doc_cli_claims.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_doc_operator_surface.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_doc_test_pointers_exist.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_doc_transient_error_claims.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_doc_usage_event_fields.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_docs_generated.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_docs_index_complete.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_entry_points_resolve.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_event_int_coercion.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_event_kind_registry.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_event_kinds_ssot.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_events_doc_contract.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_gen_blocks_name_their_source.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_hard_wall_signal.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_hook_contract_docs.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_layer_2_loop_size.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_migrations_registry_ssot.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_module_sizes.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_monitor_self_exclusion.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_no_ai_signatures.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_no_asyncio_select.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_no_direct_inspect_import.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_no_internal_refs.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_no_price_logic.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_no_raw_time.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_no_version_refs.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_peek_schema_version.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_phase_select_stateless.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_plugins_config_stable.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_procwait_shield.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_readme_zh_is_pointer.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_round_alloc_growth.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_round_log_decode_boundary.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_round_num_single_source.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_round_result_stable.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_set_diff_for_auto_tool_classification.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_throttle_stateless_imports.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_upstream_schema_canary.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/invariants/test_vulture_whitelist_generated.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/linux/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/linux/test_memory_high_nudge_e2e.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/literate/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/literate/parser.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/literate/test_parser.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/literate/test_quickstart.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_agent_runtime.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_agent_runtime_container.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_agent_runtime_grace.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_agent_runtime_marker_scan.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_agent_runtime_progress.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_agent_runtime_reap.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_agent_runtime_signal_name.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_api_assemble_prompt.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_api_events_stream.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_api_install.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_api_observation.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_api_read_round_num.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_api_resolve_phase.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_api_service.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_api_types.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_apply_reap_grace_env.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_apply_resume_env.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_auto_stop_gating.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_catalogs.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_cgroup_probe.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_claude_error_detector.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_claude_foreign_schema.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_cli.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_cli_common.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_cli_init_install.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_cli_main_boundary.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_cli_monitor_http.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_cli_service_peek_monitor.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_cli_upgrade.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_codewhale_code_precedence.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_codewhale_plugin.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_coerce_int_poison.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_config_fresh_eyes.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_config_max_rounds.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_config_nested_strictness.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_config_prompt_delivery.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_config_resume_guard.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_config_schedule.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_config_stop_file.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_config_strictness.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_config_transient_error_action.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_context_store.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_control_char_config.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_cooperative_signal_env.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_defenses.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_detect_hung_latch.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_detector_isolation.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_doctor_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_effective_throttle_view.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_emit_config_migrated.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_emit_schedule.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_emit_schedule_phase_skipped.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_event_log.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_events.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_events_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_events_cmd_rollover.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_events_cmd_tail.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_events_jsonl_decode.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_example_plugin.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_exec_prefix.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_exit_cause.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_exit_classifier.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_fresh_eyes_trigger.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_gemini_plugin.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_hook_redact_order.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_hooks.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_host_health.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_http_progress.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_http_progress_epoch.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_init_entry_points.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_interruptible_sleep_should_stop.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_kill_pgroup_shielding.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_kimi_plugin.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_lifecycle.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_loader_split.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_metrics.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_metrics_cgroup_usage.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_metrics_swap.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_migrate_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_migrations.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_assembly.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_cmd_severity.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_dedup_identity.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_dedup_rearm.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_detectors.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_event_tail.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_loop.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_on_alert.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_remote.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_round_deferred_stale.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_monitor_schedule_pause.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_network_blip_signal_guard.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_notify.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_open_round_log.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_orphan_chain_single_pass.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_peek_argparse.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_peek_project_isolation.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_peek_schedule.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_peek_select.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_phase_agent_launch.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_phase_profiles.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_phase_select.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_pi_plugin.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_plugin_constants.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_plugin_manifest.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_plugin_scan_parity.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_plugins_config_fields.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_presets.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_procwait.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_prompt_loader.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_read_json_hardening.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_redact.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_resolve.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_resolve_dirty_tree.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_round_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_round_cmd_sigterm.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_round_log_helpers.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_round_outcome.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_round_outcome_agent_axis.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_round_view.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_runner_sidecar_ordering.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_runner_throttle.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_scaffold.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_schedule.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_cmd_bounded.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_config_broken.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_crash_loop.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_mem_gate.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_phase_schedule.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_policy.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_round.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_round_cgroup_spine.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_round_log.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_round_oom_killed.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_round_terminate.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_round_wait_exit.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_schedule_gate.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_sentinel.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_serve_wakeup.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_service_mode.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_startup_battery_exit.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_startup_check.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_substrate.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_supervisor_stale_self_blind.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_terminate_round_wait_exit.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_throttle_back_off_ladder.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_throttle_epoch_bound.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_throttle_plugin_classification.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_upgrade_migrate.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.12}/tests/unit/test_vcs_state.py +0 -0
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
# bare name below marks one field "used". SSOT = the @dataclass definitions in
|
|
7
7
|
# agent_runner/; a new field is covered by regenerating, never by editing here.
|
|
8
8
|
|
|
9
|
+
# agent_runner._bounded.BoundedResult
|
|
10
|
+
rc
|
|
11
|
+
stdout
|
|
12
|
+
stderr
|
|
13
|
+
timed_out
|
|
14
|
+
|
|
9
15
|
# agent_runner._monitor_state.LocalSource
|
|
10
16
|
log_dir
|
|
11
17
|
|
|
@@ -161,6 +167,11 @@ monitor
|
|
|
161
167
|
phases
|
|
162
168
|
plugins
|
|
163
169
|
schedule
|
|
170
|
+
goal
|
|
171
|
+
|
|
172
|
+
# agent_runner.config.models.GoalConfig
|
|
173
|
+
ledger
|
|
174
|
+
checks
|
|
164
175
|
|
|
165
176
|
# agent_runner.config.models.MonitorConfig
|
|
166
177
|
auth_fail_patterns
|
|
@@ -232,6 +243,12 @@ pause_windows
|
|
|
232
243
|
stash_idempotency_s
|
|
233
244
|
dirty_action
|
|
234
245
|
|
|
246
|
+
# agent_runner.config.models._GoalCheckConfig
|
|
247
|
+
name
|
|
248
|
+
cmd
|
|
249
|
+
cwd
|
|
250
|
+
timeout_s
|
|
251
|
+
|
|
235
252
|
# agent_runner.config.models._HostHealthBrakeConfig
|
|
236
253
|
memory_high
|
|
237
254
|
memory_high_step_pct
|
|
@@ -281,6 +298,11 @@ codifies
|
|
|
281
298
|
guarded_by
|
|
282
299
|
current_state
|
|
283
300
|
|
|
301
|
+
# agent_runner.goal.Advisory
|
|
302
|
+
observation
|
|
303
|
+
question
|
|
304
|
+
confidence
|
|
305
|
+
|
|
284
306
|
# agent_runner.hooks.HookContext
|
|
285
307
|
work_dir
|
|
286
308
|
log_dir
|
|
@@ -5,6 +5,15 @@ 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.3.12] - 2026-09-18
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `[goal]` config table: a markdown lessons `ledger` (listed in `[prompt] files` at index >= 1, and — since it must survive the round's own `git stash` — required to sit under `log_dir` or outside `work_dir`) plus `[[goal.checks]]` (`name`, `cmd` argv, optional `timeout_s`) — objective, CLI-agnostic checks the round child runs after each round's agent exits, each emitting a `goal_check` event (`satisfied` bool, optional `value`).
|
|
12
|
+
- Treadmill assessor (advisory-only): across the last 3 completed rounds, if every round showed activity but at least one goal check stayed unsatisfied with an unchanged signature, serve writes one edge-triggered advisory to the ledger and emits a `goal_assessment` event (never on convergence). The ledger's `[prompt] files` membership carries it into the next round's prompt.
|
|
13
|
+
- Advisory/kill firewall: the goal loop never CAUSES a kill or give-up — the kill/give-up path reads events by kind and stays blind to `goal_check`/`goal_assessment`. A goal check's own wall time does count toward the round's duration, so a long check can DELAY (never trigger) the fast-spin `stalled_no_progress`/`crash_loop` breakers; config load warns if the total check budget reaches that window.
|
|
14
|
+
|
|
15
|
+
Check timeouts fold into the round's own timeout budget. No config migration required (v0.3.11 silently ignores an unrecognized `[goal]` table). `peek --json` schema stays `2.6`.
|
|
16
|
+
|
|
8
17
|
## [0.3.11] - 2026-09-17
|
|
9
18
|
|
|
10
19
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cli-agent-runner
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.12
|
|
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
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Sanctioned timeout-bounded subprocess primitive.
|
|
2
|
+
|
|
3
|
+
Runs a command in its OWN session under a wall-clock timeout, escalating
|
|
4
|
+
TERM -> grace -> killpg(SIGKILL) on breach so a hung process leaves no
|
|
5
|
+
descendants (a backgrounded grandchild included, since killpg targets the
|
|
6
|
+
whole process group). Returns a ``BoundedResult`` with ``timed_out=True`` on
|
|
7
|
+
breach instead of raising -- the caller decides how to react. No ``clock``
|
|
8
|
+
parameter: ``subprocess.communicate(timeout=)`` self-times, there is no raw
|
|
9
|
+
wall-clock read here to abstract.
|
|
10
|
+
|
|
11
|
+
Two hardening properties beyond the plain TERM/KILL escalation:
|
|
12
|
+
|
|
13
|
+
- **Bounded even against a session-detached pipe holder.** A descendant that
|
|
14
|
+
``setsid``-detaches out of the killed process group can still hold the
|
|
15
|
+
inherited stdout/stderr pipe open, so the final drain after ``killpg`` would
|
|
16
|
+
block forever waiting on EOF. Every drain is capped at ``_KILL_GRACE_S``; on
|
|
17
|
+
breach the read ends are closed and whatever was captured so far is returned.
|
|
18
|
+
The whole primitive therefore returns within ``timeout_s + 2*_KILL_GRACE_S``
|
|
19
|
+
(one grace for TERM->KILL, one for the post-kill drain) no matter what the
|
|
20
|
+
command spawns. A truly detached grandchild in its OWN session is NOT reaped
|
|
21
|
+
-- killpg cannot reach it; keep checks in a single process group.
|
|
22
|
+
- **Reap-on-interrupt.** A SIGTERM to the supervisor (see
|
|
23
|
+
``cli.common.install_term_handler``, which raises ``KeyboardInterrupt``)
|
|
24
|
+
propagates out of ``communicate()`` while the command runs in its own
|
|
25
|
+
session and would otherwise never be signalled. Any ``BaseException`` out of
|
|
26
|
+
a wait therefore SIGKILLs the whole group, closes the pipes, and reaps under
|
|
27
|
+
one bounded ``_KILL_GRACE_S`` wait before re-raising, mirroring
|
|
28
|
+
``agent_runtime._kill_pgroup``'s bounded-wait-after-SIGKILL.
|
|
29
|
+
|
|
30
|
+
Kept out of the startup import graph on purpose (see
|
|
31
|
+
tests/invariants/test_import_footprint.py): callers import it function-scope.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import os
|
|
37
|
+
import signal
|
|
38
|
+
import subprocess # noqa: TID251 — agent_runner._bounded is the sanctioned timeout-bounded subprocess primitive
|
|
39
|
+
from dataclasses import dataclass
|
|
40
|
+
from pathlib import Path
|
|
41
|
+
|
|
42
|
+
_KILL_GRACE_S = 3 # grace between TERM and killpg(SIGKILL), and for the post-kill drain
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class BoundedResult:
|
|
47
|
+
rc: int
|
|
48
|
+
stdout: str
|
|
49
|
+
stderr: str
|
|
50
|
+
timed_out: bool
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _kill_group(proc: subprocess.Popen) -> None:
|
|
54
|
+
try:
|
|
55
|
+
os.killpg(proc.pid, signal.SIGKILL)
|
|
56
|
+
except OSError:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _close_pipes(proc: subprocess.Popen) -> None:
|
|
61
|
+
for pipe in (proc.stdout, proc.stderr):
|
|
62
|
+
if pipe is not None:
|
|
63
|
+
try:
|
|
64
|
+
pipe.close()
|
|
65
|
+
except OSError:
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _drain_after_kill(proc: subprocess.Popen, exc: subprocess.TimeoutExpired) -> tuple[str, str]:
|
|
70
|
+
"""Best-effort drain after ``killpg(SIGKILL)`` that never blocks unbounded.
|
|
71
|
+
|
|
72
|
+
A ``setsid``-detached descendant can still hold the stdout/stderr pipe open,
|
|
73
|
+
so stop waiting on it: close the read ends and reap the (already-SIGKILLed)
|
|
74
|
+
immediate child under one more ``_KILL_GRACE_S`` cap. ``TimeoutExpired.output``
|
|
75
|
+
is ``bytes`` even on this text-mode Popen (the partial read captured at
|
|
76
|
+
timeout bypasses the text-decoding wrapper), so decode it here with the
|
|
77
|
+
same ``errors="replace"`` policy; fall back to empty strings if the
|
|
78
|
+
attribute isn't bytes at all."""
|
|
79
|
+
_close_pipes(proc)
|
|
80
|
+
try:
|
|
81
|
+
proc.wait(timeout=_KILL_GRACE_S)
|
|
82
|
+
except subprocess.TimeoutExpired:
|
|
83
|
+
pass
|
|
84
|
+
out = (
|
|
85
|
+
exc.output.decode("utf-8", errors="replace")
|
|
86
|
+
if isinstance(exc.output, (bytes, bytearray))
|
|
87
|
+
else ""
|
|
88
|
+
)
|
|
89
|
+
err = (
|
|
90
|
+
exc.stderr.decode("utf-8", errors="replace")
|
|
91
|
+
if isinstance(exc.stderr, (bytes, bytearray))
|
|
92
|
+
else ""
|
|
93
|
+
)
|
|
94
|
+
return out, err
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def run_bounded(argv: list[str], *, cwd: Path, timeout_s: int) -> BoundedResult:
|
|
98
|
+
"""Run argv in its own session under a wall-clock timeout, escalating
|
|
99
|
+
TERM -> grace -> killpg on breach so a hung command leaves no reachable
|
|
100
|
+
descendants. Returns timed_out=True on breach -- never raises on breach.
|
|
101
|
+
A ``BaseException`` out of a wait (e.g. a SIGTERM-driven KeyboardInterrupt)
|
|
102
|
+
SIGKILLs the group, closes the pipes, and reaps under one bounded
|
|
103
|
+
``_KILL_GRACE_S`` wait, then re-raises, so an interrupted check is torn
|
|
104
|
+
down (not left a zombie) rather than orphaned. ``errors="replace"`` keeps a
|
|
105
|
+
non-UTF-8 byte in the command's output from raising a UnicodeDecodeError
|
|
106
|
+
past the caller's fail-open guard."""
|
|
107
|
+
proc = subprocess.Popen(
|
|
108
|
+
argv,
|
|
109
|
+
cwd=cwd,
|
|
110
|
+
stdout=subprocess.PIPE,
|
|
111
|
+
stderr=subprocess.PIPE,
|
|
112
|
+
text=True,
|
|
113
|
+
errors="replace",
|
|
114
|
+
start_new_session=True,
|
|
115
|
+
)
|
|
116
|
+
try:
|
|
117
|
+
try:
|
|
118
|
+
out, err = proc.communicate(timeout=timeout_s)
|
|
119
|
+
return BoundedResult(rc=proc.returncode, stdout=out, stderr=err, timed_out=False)
|
|
120
|
+
except subprocess.TimeoutExpired:
|
|
121
|
+
proc.terminate()
|
|
122
|
+
try:
|
|
123
|
+
out, err = proc.communicate(timeout=_KILL_GRACE_S)
|
|
124
|
+
except subprocess.TimeoutExpired as exc:
|
|
125
|
+
_kill_group(proc)
|
|
126
|
+
out, err = _drain_after_kill(proc, exc)
|
|
127
|
+
return BoundedResult(rc=proc.returncode, stdout=out, stderr=err, timed_out=True)
|
|
128
|
+
except BaseException:
|
|
129
|
+
_kill_group(proc)
|
|
130
|
+
_close_pipes(proc)
|
|
131
|
+
try:
|
|
132
|
+
proc.wait(timeout=_KILL_GRACE_S)
|
|
133
|
+
except subprocess.TimeoutExpired:
|
|
134
|
+
pass
|
|
135
|
+
raise
|
|
@@ -21,6 +21,7 @@ from agent_runner.cli.init_cmd import _preset_names
|
|
|
21
21
|
from agent_runner.config import (
|
|
22
22
|
AgentConfig,
|
|
23
23
|
Config,
|
|
24
|
+
GoalConfig,
|
|
24
25
|
MonitorConfig,
|
|
25
26
|
MonitorHostHealthConfig,
|
|
26
27
|
PhasesConfig,
|
|
@@ -29,6 +30,7 @@ from agent_runner.config import (
|
|
|
29
30
|
RuntimeConfig,
|
|
30
31
|
ScheduleConfig,
|
|
31
32
|
VcsConfig,
|
|
33
|
+
_GoalCheckConfig,
|
|
32
34
|
_HostHealthBrakeConfig,
|
|
33
35
|
_HostHealthDiskConfig,
|
|
34
36
|
_HostHealthMemoryConfig,
|
|
@@ -72,6 +74,7 @@ _SECTIONS: list[tuple[str, type, list[tuple[str, type]]]] = [
|
|
|
72
74
|
("phases", PhasesConfig, []),
|
|
73
75
|
("plugins", PluginsConfig, []),
|
|
74
76
|
("schedule", ScheduleConfig, []),
|
|
77
|
+
("goal", GoalConfig, [("goal.checks", _GoalCheckConfig)]),
|
|
75
78
|
]
|
|
76
79
|
|
|
77
80
|
|
|
@@ -102,13 +105,23 @@ def _field_table(dc: type) -> list[str]:
|
|
|
102
105
|
return rows
|
|
103
106
|
|
|
104
107
|
|
|
108
|
+
# Nested sub-sections that are TOML arrays-of-tables (`[[name]]`), not single
|
|
109
|
+
# sub-tables (`[name]`): the parent field is a list/tuple of the sub-dataclass,
|
|
110
|
+
# so the header must double-bracket or a copy-pasted single `[goal.checks]` is
|
|
111
|
+
# rejected by the parser (_parse_goal expects a list).
|
|
112
|
+
_ARRAY_OF_TABLES_SECTIONS = frozenset({"goal.checks"})
|
|
113
|
+
|
|
114
|
+
|
|
105
115
|
def render_config_schema_table() -> str:
|
|
106
116
|
"""Markdown sub-sections per Config dataclass with field/type/default."""
|
|
107
117
|
parts: list[str] = []
|
|
108
118
|
for name, dc, nested in _SECTIONS:
|
|
109
119
|
parts += [f"### `[{name}]`", "", *_field_table(dc), ""]
|
|
110
120
|
for sub_name, sub_dc in nested:
|
|
111
|
-
|
|
121
|
+
brackets = (
|
|
122
|
+
f"[[{sub_name}]]" if sub_name in _ARRAY_OF_TABLES_SECTIONS else f"[{sub_name}]"
|
|
123
|
+
)
|
|
124
|
+
parts += [f"#### `{brackets}`", "", *_field_table(sub_dc), ""]
|
|
112
125
|
return "\n".join(parts).rstrip()
|
|
113
126
|
|
|
114
127
|
|
|
@@ -56,7 +56,7 @@ def outer_round_ceiling_s(cfg: Config, phase_arg: str | None) -> int:
|
|
|
56
56
|
(cfg.profile_for(p).runtime.round_budget_s for p in (cfg.phases.list or [])),
|
|
57
57
|
default=cfg.runtime.round_budget_s,
|
|
58
58
|
)
|
|
59
|
-
_, outer_ceiling = timeout_budget(inner)
|
|
59
|
+
_, outer_ceiling = timeout_budget(inner, goal_checks_allowance_s=cfg.goal_checks_allowance_s)
|
|
60
60
|
return outer_ceiling
|
|
61
61
|
|
|
62
62
|
|
|
@@ -303,17 +303,18 @@ _STOP_GRACE_MARGIN_S = 10 # pad above _ROUND_TERM_GRACE_S for systemd stop-requ
|
|
|
303
303
|
_ROUND_UNREAPED_RC = 137 # 128 + SIGKILL(9): reads as a kill in the crash-loop path
|
|
304
304
|
|
|
305
305
|
|
|
306
|
-
def timeout_budget(round_budget_s: int) -> tuple[int, int]:
|
|
306
|
+
def timeout_budget(round_budget_s: int, *, goal_checks_allowance_s: int = 0) -> tuple[int, int]:
|
|
307
307
|
"""Single source for the round-timeout safety budget.
|
|
308
308
|
|
|
309
309
|
Returns ``(timeout_stop_sec, outer_ceiling_s)``:
|
|
310
310
|
|
|
311
311
|
- ``outer_ceiling_s`` — the in-process outer wall-clock ceiling for the
|
|
312
312
|
round subprocess (``api.outer_round_ceiling_s``): ``round_budget_s``
|
|
313
|
-
plus reap grace + git-commit ceiling + hook allowance
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
313
|
+
plus reap grace + git-commit ceiling + hook allowance + the goal-check
|
|
314
|
+
executor's own allowance, so it only trips when the round supervisor
|
|
315
|
+
itself is wedged, never during its own bounded post-round cleanup (nor
|
|
316
|
+
a slow-but-still-bounded goal check). The reap margin must cover the
|
|
317
|
+
widest grace ANY round can be configured with -- a cooperative agent's
|
|
317
318
|
``sigterm_grace_s`` is boot-capped at ``_ROUND_TERM_GRACE_S``
|
|
318
319
|
(``config/models.py``'s mirror of this module's single source), not
|
|
319
320
|
the non-cooperative default ``_REAP_GRACE_S``, so the margin is
|
|
@@ -324,12 +325,19 @@ def timeout_budget(round_budget_s: int) -> tuple[int, int]:
|
|
|
324
325
|
SIGTERM to reach and drain the round (``_ROUND_TERM_GRACE_S``) plus a
|
|
325
326
|
stop-request overhead pad, so `systemctl stop` never SIGKILLs a round
|
|
326
327
|
that is draining normally.
|
|
328
|
+
|
|
329
|
+
``goal_checks_allowance_s`` (default 0, byte-identical to the old
|
|
330
|
+
signature when omitted) is the caller's ``GoalConfig.checks_allowance_s``
|
|
331
|
+
when ``[goal]`` is configured -- folded into this ONE ceiling rather than
|
|
332
|
+
a second, separate one, so the goal-check path can never itself trip
|
|
333
|
+
``round_supervisor_wedged``.
|
|
327
334
|
"""
|
|
328
335
|
outer_ceiling_s = (
|
|
329
336
|
round_budget_s
|
|
330
337
|
+ max(_REAP_GRACE_S, _ROUND_TERM_GRACE_S)
|
|
331
338
|
+ _GIT_COMMIT_TIMEOUT_S
|
|
332
339
|
+ _HOOK_ALLOWANCE_S
|
|
340
|
+
+ goal_checks_allowance_s
|
|
333
341
|
)
|
|
334
342
|
timeout_stop_sec = outer_ceiling_s + _ROUND_TERM_GRACE_S + _STOP_GRACE_MARGIN_S
|
|
335
343
|
return timeout_stop_sec, outer_ceiling_s
|
|
@@ -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.3.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 3,
|
|
21
|
+
__version__ = version = '0.3.12'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 3, 12)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -40,6 +40,7 @@ from agent_runner.config.models import ( # noqa: F401 — public re-export
|
|
|
40
40
|
_VCS_ALLOWED_FIELDS,
|
|
41
41
|
AgentConfig,
|
|
42
42
|
Config,
|
|
43
|
+
GoalConfig,
|
|
43
44
|
MonitorConfig,
|
|
44
45
|
MonitorHostHealthConfig,
|
|
45
46
|
PhaseOverride,
|
|
@@ -50,17 +51,22 @@ from agent_runner.config.models import ( # noqa: F401 — public re-export
|
|
|
50
51
|
RuntimeConfig,
|
|
51
52
|
ScheduleConfig,
|
|
52
53
|
VcsConfig,
|
|
54
|
+
_GoalCheckConfig,
|
|
53
55
|
_HostHealthBrakeConfig,
|
|
54
56
|
_HostHealthDiskConfig,
|
|
55
57
|
_HostHealthMemoryConfig,
|
|
56
58
|
_HostHealthPressureConfig,
|
|
57
59
|
)
|
|
58
|
-
from agent_runner.config.validators import
|
|
60
|
+
from agent_runner.config.validators import ( # noqa: F401 — public re-export
|
|
61
|
+
_reject_control_chars,
|
|
62
|
+
_resolve_against_work_dir,
|
|
63
|
+
)
|
|
59
64
|
|
|
60
65
|
__all__ = [
|
|
61
66
|
"AgentConfig",
|
|
62
67
|
"Config",
|
|
63
68
|
"ConfigError",
|
|
69
|
+
"GoalConfig",
|
|
64
70
|
"MonitorConfig",
|
|
65
71
|
"MonitorHostHealthConfig",
|
|
66
72
|
"PhaseOverride",
|
|
@@ -7,12 +7,14 @@ Thin glue by design — every table's own validation lives in ``parsers.py``.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import tomllib
|
|
10
|
+
import warnings
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
|
|
12
13
|
from agent_runner.config.errors import ConfigError
|
|
13
14
|
from agent_runner.config.models import Config, PhasesConfig
|
|
14
15
|
from agent_runner.config.parsers import (
|
|
15
16
|
_parse_agent,
|
|
17
|
+
_parse_goal,
|
|
16
18
|
_parse_monitor,
|
|
17
19
|
_parse_phase_overrides,
|
|
18
20
|
_parse_plugins,
|
|
@@ -56,6 +58,102 @@ def _reject_static_resume_flag(cfg) -> None:
|
|
|
56
58
|
)
|
|
57
59
|
|
|
58
60
|
|
|
61
|
+
def _reject_goal_ledger_not_listed(cfg: Config) -> None:
|
|
62
|
+
"""A [goal].ledger doesn't exist at cold start -- it's written by the agent
|
|
63
|
+
mid-run -- so it must ride in [prompt] files at index >= 1: index 0 is a
|
|
64
|
+
fatal-on-missing read (prompt_loader.py), and the single `file=` form can
|
|
65
|
+
never carry a second entry at all. Checks every configured phase's own
|
|
66
|
+
effective files list, mirroring Config.profile_for's override-or-base
|
|
67
|
+
resolution rule for prompt_files (no override -> the base [prompt] table)."""
|
|
68
|
+
if cfg.goal is None:
|
|
69
|
+
return
|
|
70
|
+
ledger = cfg.goal.ledger
|
|
71
|
+
phase_names: list[str | None] = list(cfg.phases.list) if cfg.phases.list else [None]
|
|
72
|
+
for phase_name in phase_names:
|
|
73
|
+
where = "[prompt]" if phase_name is None else f"[phases.{phase_name}.prompt]"
|
|
74
|
+
prompt_files = cfg.profile_for(phase_name).prompt_files
|
|
75
|
+
# No override -> [] via cfg.prompt.files, which is already [] when the
|
|
76
|
+
# single `file=` form is in play (parser-enforced): never carries a
|
|
77
|
+
# second entry.
|
|
78
|
+
files = (
|
|
79
|
+
[str(p) for p in prompt_files]
|
|
80
|
+
if prompt_files is not None
|
|
81
|
+
else [str(p) for p in cfg.prompt.files]
|
|
82
|
+
)
|
|
83
|
+
if ledger not in files or files.index(ledger) == 0:
|
|
84
|
+
raise ConfigError(
|
|
85
|
+
f"{where}: [goal] ledger {ledger!r} is not listed in the resolved "
|
|
86
|
+
"prompt files at index >= 1 (the single `file=` form can't carry it, "
|
|
87
|
+
f"and index 0 doesn't exist at cold start); list {ledger!r} in "
|
|
88
|
+
"[prompt] files at index >= 1"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _reject_goal_ledger_in_stash_swept_tree(cfg: Config) -> None:
|
|
93
|
+
"""The ``[goal].ledger`` is supervisor-written and must PERSIST across
|
|
94
|
+
rounds, but the round's own vcs step sweeps the work tree: under the
|
|
95
|
+
DEFAULT ``[vcs] dirty_action="stash"`` a ``git stash push -u`` stashes
|
|
96
|
+
every dirty path EXCEPT ``log_dir`` (see ``vcs_state``'s exclude pathspec).
|
|
97
|
+
A ledger resolved INSIDE ``work_dir`` but OUTSIDE ``log_dir`` would be
|
|
98
|
+
stashed away as orphan work right after the round it fired for -- the steer
|
|
99
|
+
would silently last one round.
|
|
100
|
+
|
|
101
|
+
Pure path arithmetic (no git): require the resolved ledger to sit OUTSIDE
|
|
102
|
+
``work_dir``, OR UNDER ``log_dir`` (the one work-tree path the stash
|
|
103
|
+
pathspec excludes). Composes with -- and runs after --
|
|
104
|
+
``_reject_goal_ledger_not_listed``."""
|
|
105
|
+
if cfg.goal is None:
|
|
106
|
+
return
|
|
107
|
+
# .resolve() all three: an absolute log_dir (or ledger) is NOT canonicalized
|
|
108
|
+
# by _expand_and_resolve (only a relative path is), so a symlinked absolute
|
|
109
|
+
# log_dir (e.g. /tmp -> /private/tmp on macOS) would otherwise FALSE-REJECT
|
|
110
|
+
# a ledger genuinely under it -- work_dir is always canonical already
|
|
111
|
+
# (loader.load_config resolves it unconditionally), so this is a no-op there.
|
|
112
|
+
ledger = Path(cfg.goal.ledger).resolve()
|
|
113
|
+
work_dir = cfg.runtime.work_dir.resolve()
|
|
114
|
+
log_dir = cfg.runtime.log_dir.resolve()
|
|
115
|
+
if ledger.is_relative_to(work_dir) and not ledger.is_relative_to(log_dir):
|
|
116
|
+
raise ConfigError(
|
|
117
|
+
f"[goal] ledger {cfg.goal.ledger!r} resolves inside runtime.work_dir "
|
|
118
|
+
f"({work_dir}) but outside runtime.log_dir ({log_dir}): the default "
|
|
119
|
+
'[vcs] dirty_action="stash" would stash it away as orphan work after '
|
|
120
|
+
"the round it fired for, so the steer would last only one round. Put "
|
|
121
|
+
'the ledger under log_dir (e.g. ledger = "logs/lessons.md") or at an '
|
|
122
|
+
"absolute path outside work_dir."
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _warn_goal_checks_budget_disarms_fast_spin(cfg: Config) -> None:
|
|
127
|
+
"""A goal check's wall time counts inside ``round_duration_s``, the
|
|
128
|
+
discriminator for serve's fast-spin give-up breakers (``crash_loop`` counts
|
|
129
|
+
a crash only under ``CRASH_LOOP_SHORT_EXIT_S``, ``stalled_no_progress`` only
|
|
130
|
+
under ``_NO_PROGRESS_SHORT_S``). A goal-check budget at or above that short
|
|
131
|
+
window can push an otherwise fast-crashing round past the threshold and
|
|
132
|
+
DISARM the breaker. The goal loop still never CAUSES a give-up -- the
|
|
133
|
+
firewall keeps the kill/give-up path blind to ``goal_*`` events -- so this
|
|
134
|
+
is the one place a long check can only DELAY (never trigger) one. A
|
|
135
|
+
legitimate ``pytest`` goal-check may well exceed the window, so this WARNS
|
|
136
|
+
(via the same load-time ``warnings`` channel plugin-load failures use)
|
|
137
|
+
rather than rejecting."""
|
|
138
|
+
if cfg.goal is None:
|
|
139
|
+
return
|
|
140
|
+
# Function-scope import: config/ must not import _serve_policy at module
|
|
141
|
+
# scope (_serve_policy imports agent_runner.config for ConfigError -- a
|
|
142
|
+
# cycle). By load_config-call time the module is importable.
|
|
143
|
+
from agent_runner._serve_policy import _NO_PROGRESS_SHORT_S
|
|
144
|
+
|
|
145
|
+
allowance = cfg.goal.checks_allowance_s
|
|
146
|
+
if allowance >= _NO_PROGRESS_SHORT_S:
|
|
147
|
+
warnings.warn(
|
|
148
|
+
f"[goal] total goal-check budget is {allowance}s, at or above the "
|
|
149
|
+
f"{_NO_PROGRESS_SHORT_S}s fast-spin give-up window: a goal check's wall "
|
|
150
|
+
"time counts toward the round's own duration, so a long check can delay "
|
|
151
|
+
"(never trigger) the stalled_no_progress/crash_loop breakers. Keep total "
|
|
152
|
+
"check time under that window if you rely on them.",
|
|
153
|
+
stacklevel=2,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
59
157
|
def _check_schema_version(raw: dict) -> None:
|
|
60
158
|
"""Boot gate: a config's ``schema_version`` decouples the on-disk config
|
|
61
159
|
shape from the installed package version, so a config can be migrated
|
|
@@ -136,6 +234,11 @@ def load_config(toml_path: Path) -> Config:
|
|
|
136
234
|
monitor = _parse_monitor(_require_table(raw, "monitor"))
|
|
137
235
|
plugins = _parse_plugins(_require_table(raw, "plugins"))
|
|
138
236
|
schedule_cfg = _parse_schedule(_require_table(raw, "schedule"))
|
|
237
|
+
goal = (
|
|
238
|
+
_parse_goal(_require_table(raw, "goal"), project_name=project_name, work_dir=work_dir)
|
|
239
|
+
if "goal" in raw
|
|
240
|
+
else None
|
|
241
|
+
)
|
|
139
242
|
|
|
140
243
|
cfg = Config(
|
|
141
244
|
agent=agent,
|
|
@@ -146,6 +249,7 @@ def load_config(toml_path: Path) -> Config:
|
|
|
146
249
|
phases=phases_cfg,
|
|
147
250
|
plugins=plugins,
|
|
148
251
|
schedule=schedule_cfg,
|
|
252
|
+
goal=goal,
|
|
149
253
|
)
|
|
150
254
|
|
|
151
255
|
# Verify + import discovered agent_runner.plugins entry points now that
|
|
@@ -158,5 +262,8 @@ def load_config(toml_path: Path) -> Config:
|
|
|
158
262
|
apply_plugin_disable(plugins.disable)
|
|
159
263
|
|
|
160
264
|
_reject_static_resume_flag(cfg)
|
|
265
|
+
_reject_goal_ledger_not_listed(cfg)
|
|
266
|
+
_reject_goal_ledger_in_stash_swept_tree(cfg)
|
|
267
|
+
_warn_goal_checks_budget_disarms_fast_spin(cfg)
|
|
161
268
|
|
|
162
269
|
return cfg
|
|
@@ -32,6 +32,15 @@ DEFAULT_TERMINAL_MARKER = '"type":"result"'
|
|
|
32
32
|
# agent's preset gets from the round leader (see AgentConfig.sigterm_grace_s).
|
|
33
33
|
DEFAULT_SIGTERM_GRACE_S = 10
|
|
34
34
|
|
|
35
|
+
# Default [[goal.checks]] timeout_s: an objective goal-check is a lightweight
|
|
36
|
+
# scriptable sanity command (e.g. a lint/test run), not another agent round.
|
|
37
|
+
DEFAULT_GOAL_CHECK_TIMEOUT_S = 10
|
|
38
|
+
|
|
39
|
+
# Boot cap for [[goal.checks]] timeout_s: a value above this is rejected
|
|
40
|
+
# outright, not clamped, so one slow check can't silently eat the round's
|
|
41
|
+
# own time budget.
|
|
42
|
+
_MAX_GOAL_CHECK_TIMEOUT_S = 30
|
|
43
|
+
|
|
35
44
|
# Ceiling for [agent] sigterm_grace_s: sits a STRICT ~3s BELOW _serve_policy's
|
|
36
45
|
# _ROUND_TERM_GRACE_S (15, the supervisor's own wait for the round leader) --
|
|
37
46
|
# NOT equal to it. The margin buys the round LEADER's own cooperative
|
|
@@ -306,6 +315,56 @@ class ScheduleConfig:
|
|
|
306
315
|
return bool(self.run_windows or self.pause_windows)
|
|
307
316
|
|
|
308
317
|
|
|
318
|
+
@dataclass(frozen=True)
|
|
319
|
+
class _GoalCheckConfig:
|
|
320
|
+
"""One ``[[goal.checks]]`` entry: an objective, scriptable check (e.g. a
|
|
321
|
+
lint or test run) the operator trusts over the agent's own self-report.
|
|
322
|
+
``cwd`` is a raw operator string, resolved by the check runner, not here."""
|
|
323
|
+
|
|
324
|
+
name: str
|
|
325
|
+
cmd: list[str]
|
|
326
|
+
cwd: str | None = None
|
|
327
|
+
timeout_s: int = DEFAULT_GOAL_CHECK_TIMEOUT_S
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
@dataclass(frozen=True)
|
|
331
|
+
class GoalConfig:
|
|
332
|
+
"""The ``[goal]`` table: objective goal-checks plus the lessons-ledger
|
|
333
|
+
path threaded into the prompt across rounds. ``Config.goal`` stays
|
|
334
|
+
``None`` when ``[goal]`` is absent -- the steering loop is opt-in.
|
|
335
|
+
|
|
336
|
+
``ledger`` is required; ``checks`` defaults to an empty tuple (a ledger-only
|
|
337
|
+
``[goal]`` runs the treadmill assessor with nothing objective to converge
|
|
338
|
+
on -- valid, if degenerate), so it is declared last with a default to match
|
|
339
|
+
the parser's ``goal.checks`` default."""
|
|
340
|
+
|
|
341
|
+
ledger: str
|
|
342
|
+
checks: tuple[_GoalCheckConfig, ...] = ()
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def checks_allowance_s(self) -> int:
|
|
346
|
+
"""Worst-case wall-clock the goal-check executor can consume in one
|
|
347
|
+
round, so the round-timeout budget (``_serve_policy.timeout_budget``)
|
|
348
|
+
can fold it into the single outer ceiling -- a slow (or hung) check
|
|
349
|
+
must never trip ``round_supervisor_wedged`` itself.
|
|
350
|
+
|
|
351
|
+
The executor runs every check SEQUENTIALLY with no short-circuit on
|
|
352
|
+
failure/timeout, and each breach costs TWO ``_KILL_GRACE_S`` windows,
|
|
353
|
+
not one: ``run_bounded`` spends up to ``timeout_s + 2*_KILL_GRACE_S``
|
|
354
|
+
on a breach (one grace for TERM->killpg, one for the bounded post-kill
|
|
355
|
+
drain that never blocks on a session-detached pipe holder). So the
|
|
356
|
+
all-hang worst case is every check's own timeout PLUS two kill graces
|
|
357
|
+
EACH.
|
|
358
|
+
|
|
359
|
+
Local import: this module sits on the frozen startup graph
|
|
360
|
+
(``test_import_footprint.py``), and ``_bounded`` does not -- a
|
|
361
|
+
module-scope import here would pull it onto that graph.
|
|
362
|
+
"""
|
|
363
|
+
from agent_runner._bounded import _KILL_GRACE_S
|
|
364
|
+
|
|
365
|
+
return sum(c.timeout_s for c in self.checks) + len(self.checks) * 2 * _KILL_GRACE_S
|
|
366
|
+
|
|
367
|
+
|
|
309
368
|
@dataclass(frozen=True)
|
|
310
369
|
class Profile:
|
|
311
370
|
"""Fully-resolved per-phase execution profile.
|
|
@@ -333,6 +392,14 @@ class Config:
|
|
|
333
392
|
phases: PhasesConfig = field(default_factory=PhasesConfig)
|
|
334
393
|
plugins: PluginsConfig = field(default_factory=PluginsConfig)
|
|
335
394
|
schedule: ScheduleConfig = field(default_factory=ScheduleConfig)
|
|
395
|
+
goal: GoalConfig | None = None
|
|
396
|
+
|
|
397
|
+
@property
|
|
398
|
+
def goal_checks_allowance_s(self) -> int:
|
|
399
|
+
"""``goal.checks_allowance_s``, or 0 when ``[goal]`` is absent -- the
|
|
400
|
+
one-liner both ``_lifecycle.outer_round_ceiling_s`` and
|
|
401
|
+
``service_unit``'s ``TimeoutStopSec`` derivation need, deduped here."""
|
|
402
|
+
return self.goal.checks_allowance_s if self.goal is not None else 0
|
|
336
403
|
|
|
337
404
|
def profile_for(self, phase: str | None) -> Profile:
|
|
338
405
|
"""Resolve the effective execution profile for a phase.
|
|
@@ -429,3 +496,7 @@ _PROMPT_ALLOWED_FIELDS = frozenset(
|
|
|
429
496
|
|
|
430
497
|
# Keys allowed under a [schedule] table (top-level or per-phase).
|
|
431
498
|
_SCHEDULE_ALLOWED_FIELDS = frozenset({"timezone", "run_windows", "pause_windows"})
|
|
499
|
+
|
|
500
|
+
# Keys allowed under the [goal] table and each [[goal.checks]] entry.
|
|
501
|
+
_GOAL_ALLOWED_FIELDS = frozenset(f.name for f in dataclasses.fields(GoalConfig))
|
|
502
|
+
_GOAL_CHECK_ALLOWED_FIELDS = frozenset(f.name for f in dataclasses.fields(_GoalCheckConfig))
|