cli-agent-runner 0.3.11__tar.gz → 0.3.13__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.13/.ralph/items.json +86 -0
- cli_agent_runner-0.3.13/.ralph/progress.md +47 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.vulture-whitelist.py +22 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/CHANGELOG.md +22 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/PKG-INFO +1 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/SECURITY.md +6 -0
- cli_agent_runner-0.3.13/agent_runner/_bounded.py +135 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_docgen.py +14 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_emit/memory.py +27 -3
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_lifecycle.py +1 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_serve_policy.py +13 -5
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_version.py +2 -2
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/_serve_cgroup.py +9 -2
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/config/__init__.py +7 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/config/loader.py +107 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/config/models.py +71 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/config/parsers.py +59 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/events.py +2 -0
- cli_agent_runner-0.3.13/agent_runner/goal.py +428 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/metrics.py +28 -12
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/runner.py +33 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/service_unit.py +3 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/vcs_state.py +12 -28
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/architecture.md +2 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/configuration.md +113 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/memory-efficiency.md +89 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/pyproject.toml +2 -1
- cli_agent_runner-0.3.13/tests/_clock.py +95 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/conftest.py +295 -175
- cli_agent_runner-0.3.13/tests/e2e/growth_child.py +28 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/test_e2e_pre_oom.py +179 -19
- cli_agent_runner-0.3.13/tests/integration/test_goal_steering_e2e.py +479 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_spawn_round_mem_floor.py +26 -50
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_config_error_consistency.py +1 -0
- cli_agent_runner-0.3.13/tests/invariants/test_goal_firewall.py +166 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_import_footprint.py +5 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_module_boundaries.py +32 -5
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_vacuous_invariants.py +1 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_timeout_budget_invariant.py +27 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/linux/test_memory_high_nudge_e2e.py +4 -4
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cgroup_probe.py +30 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config.py +438 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_docgen.py +1 -1
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_git_timeout.py +9 -4
- cli_agent_runner-0.3.13/tests/unit/test_goal_assessor.py +264 -0
- cli_agent_runner-0.3.13/tests/unit/test_goal_executor.py +222 -0
- cli_agent_runner-0.3.13/tests/unit/test_goal_firewall_behavioral.py +173 -0
- cli_agent_runner-0.3.13/tests/unit/test_goal_ledger.py +136 -0
- cli_agent_runner-0.3.13/tests/unit/test_growth_child_constants.py +8 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_host_health.py +20 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_metrics.py +5 -2
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_outer_round_ceiling.py +37 -0
- cli_agent_runner-0.3.13/tests/unit/test_run_bounded.py +133 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_runner.py +86 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_round_cgroup_spine.py +60 -32
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_service_unit.py +35 -0
- cli_agent_runner-0.3.11/tests/_clock.py +0 -53
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.codecov.yml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/dependabot.yml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/workflows/ci.yml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/workflows/release.yml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.gitignore +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/BACKLOG.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/CODE_OF_CONDUCT.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/CONTRIBUTING.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/LICENSE +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/README.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/README.zh.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_emit/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_emit/agent.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_emit/rounds.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_emit/serve.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_install.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_monitor_detectors.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_monitor_registry.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_monitor_state.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_notify.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_observe.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_plugin_manifest.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_plugin_scan.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_procwait.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_redact.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_registry.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_resolve.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_round_outcome.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_round_support.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_substrate.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_throttle.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/agent_runtime.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/api.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/api_types.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/_constants.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/claude_rate_limit.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/codewhale.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/gemini.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/kimi.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/pi.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/__main__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/_serve_round.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/common.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/doctor_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/events_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/init_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/install_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/migrate_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/monitor_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/peek_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/round_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/serve_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/service_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/upgrade_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/clock.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/config/errors.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/config/validators.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/context_store.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/defenses.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/event_log.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/hooks.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/host_health.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/http_progress.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/lifecycle.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/migrations.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/monitor.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/phase_select.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/aider.toml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/claude.toml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/codewhale.toml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/gemini.toml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/kimi.toml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/pi.toml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/prompt_loader.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/remote_relay.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/round_log.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/round_view.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/scaffold.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/schedule.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/startup_check.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/build.sh +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/deploy/Dockerfile +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/deploy/docker-compose.yml +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/README.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/commands.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/events.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/long-running-agents.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/migrations/0.2.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/migrations/0.3.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/plugins.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/quickstart.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/recipes/aider.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/recipes/codewhale.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/recipes/container-pi.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/recipes/deploy-all-in-one.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/recipes/kimi.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/recipes/pi.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/runbook.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/thesis.md +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/_test_helpers.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/conftest.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/contract/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/contract/test_public_api_surface.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/test_e2e_graceful_stop.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/test_e2e_install_systemd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/test_e2e_monitor_remote.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/test_e2e_pi_resume.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/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.13}/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.13}/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.13}/tests/generate_vulture_whitelist.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_auto_stop_e2e.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_bounded_run.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_cooperative_grace_property.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_cooperative_stop_signal.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_fresh_eyes_signal.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_grace_kill_emission.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_install_dry_run.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_monitor_seeded.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_plugin_real_flow.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_prompt_delivery_stdin.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_reap_property.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_run_one_round_with_fake_agent.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_scaffold_presets.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_serve_loop.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_spawn_round_wedged.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_substrate_fingerprint.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_transient_error_backoff.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/_docs.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/_event_scan.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_architecture.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_atomic_write_enforced.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_authored_facts_declared.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_auto_stop_policy_ssot.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_bdd_test_conventions.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_brake_defaults_off.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_catalogs.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_classification_ssot.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_config_value_set_ssot.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_container_stop_bound.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_dataclass_fields_have_producers.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_builtin_plugins.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_claims_match_ssot.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_cli_claims.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_operator_surface.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_test_pointers_exist.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_transient_error_claims.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_usage_event_fields.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_docs_generated.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_docs_index_complete.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_entry_points_resolve.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_event_int_coercion.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_event_kind_registry.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_event_kinds_ssot.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_events_doc_contract.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_gen_blocks_name_their_source.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_hard_wall_signal.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_hook_contract_docs.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_layer_2_loop_size.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_migrations_registry_ssot.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_module_sizes.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_monitor_self_exclusion.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_ai_signatures.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_asyncio_select.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_direct_inspect_import.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_internal_refs.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_price_logic.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_raw_time.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_version_refs.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_peek_schema_version.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_phase_select_stateless.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_plugins_config_stable.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_procwait_shield.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_readme_zh_is_pointer.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_round_alloc_growth.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_round_log_decode_boundary.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_round_num_single_source.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_round_result_stable.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_set_diff_for_auto_tool_classification.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_throttle_stateless_imports.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_upstream_schema_canary.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_vulture_whitelist_generated.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/linux/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/literate/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/literate/parser.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/literate/test_parser.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/literate/test_quickstart.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/__init__.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime_container.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime_grace.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime_marker_scan.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime_progress.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime_reap.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime_signal_name.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_assemble_prompt.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_events_stream.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_install.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_observation.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_read_round_num.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_resolve_phase.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_service.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_types.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_apply_reap_grace_env.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_apply_resume_env.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_auto_stop_gating.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_catalogs.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_claude_error_detector.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_claude_foreign_schema.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli_common.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli_init_install.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli_main_boundary.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli_monitor_http.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli_service_peek_monitor.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli_upgrade.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_codewhale_code_precedence.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_codewhale_plugin.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_coerce_int_poison.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_fresh_eyes.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_max_rounds.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_nested_strictness.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_prompt_delivery.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_resume_guard.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_schedule.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_stop_file.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_strictness.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_transient_error_action.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_context_store.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_control_char_config.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cooperative_signal_env.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_defenses.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_detect_hung_latch.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_detector_isolation.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_doctor_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_effective_throttle_view.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_emit_config_migrated.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_emit_schedule.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_emit_schedule_phase_skipped.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_event_log.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_events.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_events_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_events_cmd_rollover.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_events_cmd_tail.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_events_jsonl_decode.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_example_plugin.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_exec_prefix.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_exit_cause.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_exit_classifier.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_fresh_eyes_trigger.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_gemini_plugin.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_hook_redact_order.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_hooks.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_http_progress.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_http_progress_epoch.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_init_entry_points.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_interruptible_sleep_should_stop.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_kill_pgroup_shielding.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_kimi_plugin.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_lifecycle.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_loader_split.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_metrics_cgroup_usage.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_metrics_swap.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_migrate_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_migrations.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_assembly.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_cmd_severity.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_dedup_identity.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_dedup_rearm.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_detectors.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_event_tail.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_loop.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_on_alert.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_remote.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_round_deferred_stale.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_schedule_pause.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_network_blip_signal_guard.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_notify.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_open_round_log.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_orphan_chain_single_pass.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_peek_argparse.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_peek_project_isolation.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_peek_schedule.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_peek_select.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_phase_agent_launch.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_phase_profiles.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_phase_select.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_pi_plugin.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_plugin_constants.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_plugin_manifest.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_plugin_scan_parity.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_plugins_config_fields.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_presets.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_procwait.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_prompt_loader.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_read_json_hardening.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_redact.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_resolve.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_resolve_dirty_tree.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_round_cmd.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_round_cmd_sigterm.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_round_log_helpers.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_round_outcome.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_round_outcome_agent_axis.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_round_view.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_runner_sidecar_ordering.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_runner_throttle.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_scaffold.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_schedule.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_cmd_bounded.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_config_broken.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_crash_loop.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_mem_gate.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_phase_schedule.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_policy.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_round.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_round_log.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_round_oom_killed.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_round_terminate.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_round_wait_exit.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_schedule_gate.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_sentinel.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_wakeup.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_service_mode.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_startup_battery_exit.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_startup_check.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_substrate.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_supervisor_stale_self_blind.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_terminate_round_wait_exit.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_throttle_back_off_ladder.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_throttle_epoch_bound.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_throttle_plugin_classification.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_upgrade_migrate.py +0 -0
- {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_vcs_state.py +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"runtime_contract": {
|
|
4
|
+
"source_docs": [],
|
|
5
|
+
"verification_gates": [
|
|
6
|
+
{
|
|
7
|
+
"name": "ruff",
|
|
8
|
+
"command": ".venv/bin/ruff check . && .venv/bin/ruff format --check ."
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"require_progress_append": true,
|
|
12
|
+
"require_one_item_per_iteration": true,
|
|
13
|
+
"require_commit": true
|
|
14
|
+
},
|
|
15
|
+
"items": [
|
|
16
|
+
{
|
|
17
|
+
"category": "functional",
|
|
18
|
+
"description": "metrics.sample() always includes psi_full_total, io_psi_some_avg10, and io_psi_full_avg10 (None when unread). _read_psi returns (some_avg10, full_avg10, full_total). host_health.memory_pressure verdict and context stay identical when those extra keys are injected.",
|
|
19
|
+
"steps": [
|
|
20
|
+
"Add or update tests in tests/unit/test_metrics.py for the triple return and the three sample keys.",
|
|
21
|
+
"Add tests/unit/test_host_health.py coverage that a PSI-healthy sample plus huge io_psi_* / psi_full_total yields the same Pressure as the sample without those keys.",
|
|
22
|
+
"Implement parsing of full-line total= and a second _read_psi call on /proc/pressure/io inside sample(); do not edit host_health.py.",
|
|
23
|
+
"Run .venv/bin/python -m pytest tests/unit/test_metrics.py tests/unit/test_host_health.py tests/unit/test_monitor_detectors.py -q and the ruff gate."
|
|
24
|
+
],
|
|
25
|
+
"passes": true,
|
|
26
|
+
"regression_notes": "76 passed (test_metrics + test_host_health + test_monitor_detectors); ruff check+format clean. host_health.py untouched. Darwin sample keys present as None."
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"category": "functional",
|
|
30
|
+
"description": "host_cgroup_memory_limit events carry defer as a bool equal to _probe_and_emit_cgroup_defer's return value (True when both caps are finite and plausible, including MemorySwapMax=0).",
|
|
31
|
+
"steps": [
|
|
32
|
+
"Add tests/unit/test_cgroup_probe.py cases: memory_swap_max=0 with finite memory_max under host RAM returns True and emits defer true; memory_swap_max missing returns False and emits defer false.",
|
|
33
|
+
"Add defer: bool to emit_host_cgroup_memory_limit and pass the already-computed defer flag from _probe_and_emit_cgroup_defer.",
|
|
34
|
+
"Point the existing tests/e2e/test_e2e_pre_oom.py diagnostic at host_cgroup_memory_limit.defer.",
|
|
35
|
+
"Run .venv/bin/python -m pytest tests/unit/test_cgroup_probe.py -q and the ruff gate."
|
|
36
|
+
],
|
|
37
|
+
"passes": true,
|
|
38
|
+
"regression_notes": "55 passed (test_cgroup_probe); ruff check+format clean. MemorySwapMax=0 emits defer true; missing swap.max emits defer false. D-2 diagnostic names host_cgroup_memory_limit.defer."
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"category": "functional",
|
|
42
|
+
"description": "round_cgroup_memory may carry optional flat io_psi_some_avg10, io_psi_full_avg10, and psi_full_total from metrics.sample() at emit time. They are omitted when unread. They are never copied into Pressure.context.",
|
|
43
|
+
"steps": [
|
|
44
|
+
"Extend tests/unit/test_serve_round_cgroup_spine.py to monkeypatch metrics.sample and assert the three fields on the emitted round_cgroup_memory event.",
|
|
45
|
+
"Thread optional kwargs through emit_round_cgroup_memory and _emit_round_cgroup_memory; call sample() once at emit.",
|
|
46
|
+
"Run .venv/bin/python -m pytest tests/unit/test_serve_round_cgroup_spine.py tests/unit/test_serve_round_oom_killed.py -q and the ruff gate."
|
|
47
|
+
],
|
|
48
|
+
"passes": true,
|
|
49
|
+
"regression_notes": "19 passed (test_serve_round_cgroup_spine + test_serve_round_oom_killed); ruff check+format clean. Optional PSI fields omitted when unread/None; present when sample() read them. Not copied into Pressure.context."
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"category": "functional",
|
|
53
|
+
"description": "The paced pre-OOM child lives in tests/e2e/growth_child.py (8 MiB every 2.5s, page-touch). pi_growth_script installs that file instead of an inlined string. Importing the module does not start the infinite loop.",
|
|
54
|
+
"steps": [
|
|
55
|
+
"Create tests/e2e/growth_child.py with the existing constants and main() behind if __name__.",
|
|
56
|
+
"Add a constants test that imports the module and asserts _CHUNK_BYTES and _PACE_S.",
|
|
57
|
+
"Change tests/e2e/conftest.py pi_growth_script to ship that file.",
|
|
58
|
+
"Run the constants test plus the ruff gate."
|
|
59
|
+
],
|
|
60
|
+
"passes": true,
|
|
61
|
+
"regression_notes": "1 passed (test_growth_child_constants); ruff check+format clean. pi_growth_script ships tests/e2e/growth_child.py via base64; import does not start the loop."
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"category": "functional",
|
|
65
|
+
"description": "A skip-closed control-arm e2e exists: same unit as treatment except MemorySwapMax=0, MemoryHigh=120M kept, no oom.group and no OOMPolicy=kill. Unset AGENT_RUNNER_E2E_CGROUP and AGENT_RUNNER_E2E_PI, the new test skips. Assertions in source require defer true, no round_mem_terminated, no memory_high_engaged, events_oom_kill_delta>=1 or INVALID, DEFER-FIRST or KERNEL-FIRST named, growth child gone, host still answers.",
|
|
66
|
+
"steps": [
|
|
67
|
+
"Factor the systemd unit body so swap max is a parameter. Control uses 0. Do not add OOMPolicy or memory.oom.group.",
|
|
68
|
+
"Add the control-arm test and shared assertions. Gate with existing PI flag and/or AGENT_RUNNER_E2E_CGROUP. Control wait ceiling 180s.",
|
|
69
|
+
"Without those env flags, run .venv/bin/python -m pytest tests/e2e/test_e2e_pre_oom.py -q and confirm skip (not fail, not pass). Do not export the e2e flags. Run the ruff gate."
|
|
70
|
+
],
|
|
71
|
+
"passes": true,
|
|
72
|
+
"regression_notes": "3 skipped (test_e2e_pre_oom.py without AGENT_RUNNER_E2E_CGROUP/PI); ruff check+format clean. Control unit MemorySwapMax=0, MemoryHigh=120M kept, no oom.group/OOMPolicy. Assertions in source: defer true, no round_mem_terminated, no memory_high_engaged, events_oom_kill_delta>=1 or INVALID, DEFER-FIRST or KERNEL-FIRST named, growth child gone, host still answers."
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"category": "functional",
|
|
76
|
+
"description": "Root SECURITY.md gains one paragraph: isolation is the operator's job (exec_prefix, dedicated user, no passwordless sudo, egress); agent-runner stays lifecycle-safety. CHANGELOG adds ## [0.3.13] - UNRELEASED. No docs/SECURITY.md. Optional results template under gitignored docs/internal/notes is allowed and must not be staged.",
|
|
77
|
+
"steps": [
|
|
78
|
+
"Edit SECURITY.md Containment or Threat model with the reframe paragraph. Keep existing recipe links.",
|
|
79
|
+
"Insert CHANGELOG ## [0.3.13] - UNRELEASED above 0.3.12. Do not claim uncapped-before-coma.",
|
|
80
|
+
"Confirm docs/SECURITY.md does not exist. Run tests/invariants/test_docs_index_complete.py tests/invariants/test_doc_claims_match_ssot.py and the ruff gate."
|
|
81
|
+
],
|
|
82
|
+
"passes": true,
|
|
83
|
+
"regression_notes": "4 passed (test_docs_index_complete + test_doc_claims_match_ssot); ruff check+format clean. docs/SECURITY.md absent. CHANGELOG [0.3.13] UNRELEASED; no uncapped-before-coma claim. Results template gitignored, not staged."
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
## 2026-09-18 sample() IO-PSI + psi_full_total
|
|
2
|
+
|
|
3
|
+
- item: metrics.sample() always includes psi_full_total, io_psi_some_avg10, and io_psi_full_avg10 (None when unread). _read_psi returns (some_avg10, full_avg10, full_total). host_health.memory_pressure verdict and context stay identical when those extra keys are injected.
|
|
4
|
+
- rationale: plan prefers ladder-blindness before defer-field / e2e fixture work. First unfinished item.
|
|
5
|
+
- files: agent_runner/metrics.py, tests/unit/test_metrics.py, tests/unit/test_host_health.py
|
|
6
|
+
- verification: `.venv/bin/python -m pytest tests/unit/test_metrics.py tests/unit/test_host_health.py tests/unit/test_monitor_detectors.py -q` → 76 passed; `.venv/bin/ruff check . && .venv/bin/ruff format --check .` clean
|
|
7
|
+
- notes: did not edit host_health.py or _NEW_MEM_SAMPLE_KEYS. Pre-existing pi-lens findings in cgroup_delegated / _brake_high_value left untouched (outside this item).
|
|
8
|
+
|
|
9
|
+
## 2026-09-18 host_cgroup_memory_limit.defer bool
|
|
10
|
+
|
|
11
|
+
- item: host_cgroup_memory_limit events carry defer as a bool equal to _probe_and_emit_cgroup_defer's return value (True when both caps are finite and plausible, including MemorySwapMax=0).
|
|
12
|
+
- rationale: plan prefers locking the defer-field contract before e2e fixture work. First remaining unfinished item after sample() IO-PSI.
|
|
13
|
+
- files: agent_runner/_emit/memory.py, agent_runner/cli/_serve_cgroup.py, tests/unit/test_cgroup_probe.py, tests/e2e/test_e2e_pre_oom.py
|
|
14
|
+
- verification: `.venv/bin/python -m pytest tests/unit/test_cgroup_probe.py -q` → 55 passed; `.venv/bin/ruff check . && .venv/bin/ruff format --check .` clean
|
|
15
|
+
- notes: defer is a required emit kwarg (every probe is yes/no). Did not touch runbook.md (docs item). Pre-existing pi-lens typing findings in _serve_cgroup left untouched.
|
|
16
|
+
|
|
17
|
+
## 2026-09-18 round_cgroup_memory corroborating PSI fields
|
|
18
|
+
|
|
19
|
+
- item: round_cgroup_memory may carry optional flat io_psi_some_avg10, io_psi_full_avg10, and psi_full_total from metrics.sample() at emit time. They are omitted when unread. They are never copied into Pressure.context.
|
|
20
|
+
- rationale: plan prefers locking ladder-blindness and defer-field contracts before e2e fixture work. First remaining unfinished item after host_cgroup_memory_limit.defer.
|
|
21
|
+
- files: agent_runner/_emit/memory.py, agent_runner/cli/_serve_cgroup.py, tests/unit/test_serve_round_cgroup_spine.py
|
|
22
|
+
- verification: `.venv/bin/python -m pytest tests/unit/test_serve_round_cgroup_spine.py tests/unit/test_serve_round_oom_killed.py -q` → 19 passed; `.venv/bin/ruff check . && .venv/bin/ruff format --check .` clean
|
|
23
|
+
- notes: assumed sample() runs only on the successful emit path (skip-no-op paths do not sample); each None key is omitted independently. Did not edit host_health.py, events.md, or runbook.md. Pre-existing pi-lens typing findings in _serve_cgroup left untouched.
|
|
24
|
+
|
|
25
|
+
## 2026-09-18 extract growth_child.py
|
|
26
|
+
|
|
27
|
+
- item: The paced pre-OOM child lives in tests/e2e/growth_child.py (8 MiB every 2.5s, page-touch). pi_growth_script installs that file instead of an inlined string. Importing the module does not start the infinite loop.
|
|
28
|
+
- rationale: plan prefers extracting growth_child.py after ladder-blindness/defer-field contracts, before skip-closed control tests. First remaining unfinished item.
|
|
29
|
+
- files: tests/e2e/growth_child.py, tests/e2e/conftest.py, tests/unit/test_growth_child_constants.py
|
|
30
|
+
- verification: `.venv/bin/python -m pytest tests/unit/test_growth_child_constants.py -q` → 1 passed; `.venv/bin/ruff check . && .venv/bin/ruff format --check .` clean
|
|
31
|
+
- notes: assumed the constants test lives under tests/unit because CI and build.sh --ignore=tests/e2e. pi_growth_script still base64-over-ssh (same install path), reading tests/e2e/growth_child.py bytes instead of an inlined string. Importing the module does not enter main().
|
|
32
|
+
|
|
33
|
+
## 2026-09-18 skip-closed control-arm e2e
|
|
34
|
+
|
|
35
|
+
- item: A skip-closed control-arm e2e exists: same unit as treatment except MemorySwapMax=0, MemoryHigh=120M kept, no oom.group and no OOMPolicy=kill. Unset AGENT_RUNNER_E2E_CGROUP and AGENT_RUNNER_E2E_PI, the new test skips. Assertions in source require defer true, no round_mem_terminated, no memory_high_engaged, events_oom_kill_delta>=1 or INVALID, DEFER-FIRST or KERNEL-FIRST named, growth child gone, host still answers.
|
|
36
|
+
- rationale: plan prefers skip-closed control tests after extracting growth_child.py. First remaining unfinished item.
|
|
37
|
+
- files: tests/e2e/conftest.py, tests/e2e/test_e2e_pre_oom.py
|
|
38
|
+
- verification: `.venv/bin/python -m pytest tests/e2e/test_e2e_pre_oom.py -q` → 3 skipped; `.venv/bin/ruff check . && .venv/bin/ruff format --check .` clean
|
|
39
|
+
- notes: assumed PI control pytest timeout includes +600 install budget matching treatment; local uses +120 per plan. Two thin wrappers (PI + local CGROUP) share `_assert_control`. Skip-closed (unset env => skip) is the honest local proof; did not export e2e flags or run sudo/systemctl. Did not add OOMPolicy or memory.oom.group.
|
|
40
|
+
|
|
41
|
+
## 2026-09-18 SECURITY reframe + CHANGELOG stub
|
|
42
|
+
|
|
43
|
+
- item: Root SECURITY.md gains one paragraph: isolation is the operator's job (exec_prefix, dedicated user, no passwordless sudo, egress); agent-runner stays lifecycle-safety. CHANGELOG adds ## [0.3.13] - UNRELEASED. No docs/SECURITY.md. Optional results template under gitignored docs/internal/notes is allowed and must not be staged.
|
|
44
|
+
- rationale: plan says docs last. Last remaining unfinished item.
|
|
45
|
+
- files: SECURITY.md, CHANGELOG.md
|
|
46
|
+
- verification: `.venv/bin/python -m pytest tests/invariants/test_docs_index_complete.py tests/invariants/test_doc_claims_match_ssot.py -q` → 4 passed; `.venv/bin/ruff check . && .venv/bin/ruff format --check .` clean; `docs/SECURITY.md` absent
|
|
47
|
+
- notes: assumed not naming a competitor in SECURITY.md (Ralph item asks for the operator-vs-lifecycle split). Did not edit runbook.md (not in the Ralph item). Results template written under gitignored docs/internal/notes and not staged. Did not claim uncapped-before-coma or a live two-arm run.
|
|
@@ -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,28 @@ 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.13] - 2026-09-18
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Two production cgroup modes are now checkable on the same paced growth child: treatment (`MemorySwapMax` infinity) still cooperatively terminates and reaps; control (`MemorySwapMax=0`) defers to kernel cgroup-OOM of the capped child. Both leave the host up. The uncapped-before-coma case stays inferred.
|
|
12
|
+
- `metrics.sample()` always includes corroborating `psi_full_total`, `io_psi_some_avg10`, and `io_psi_full_avg10` (`None` when unread). They are not kill rungs; `host_health.memory_pressure` stays blind to them.
|
|
13
|
+
- `host_cgroup_memory_limit` carries `defer` as a bool (`True` when both caps are finite and plausible, including `MemorySwapMax=0`).
|
|
14
|
+
- `round_cgroup_memory` may carry those PSI fields as optional flat keys at emit time; omitted when unread.
|
|
15
|
+
|
|
16
|
+
### Docs
|
|
17
|
+
- SECURITY.md: isolation is the operator's job (`exec_prefix`, dedicated user, no passwordless sudo, egress); agent-runner stays lifecycle-safety.
|
|
18
|
+
|
|
19
|
+
No config migration required. `peek --json` schema stays `2.6`.
|
|
20
|
+
|
|
21
|
+
## [0.3.12] - 2026-09-18
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- `[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`).
|
|
25
|
+
- 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.
|
|
26
|
+
- 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.
|
|
27
|
+
|
|
28
|
+
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`.
|
|
29
|
+
|
|
8
30
|
## [0.3.11] - 2026-09-17
|
|
9
31
|
|
|
10
32
|
### 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.13
|
|
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
|
|
@@ -37,6 +37,12 @@ unprivileged user, inside a container or VM, without passwordless sudo, and
|
|
|
37
37
|
with egress limits. The supervisor's defenses bound the blast radius of a
|
|
38
38
|
*misbehaving* agent, not a *hostile* one.
|
|
39
39
|
|
|
40
|
+
Isolation is the operator's job. Isolation-enforcing supervisors exist;
|
|
41
|
+
agent-runner deliberately is not one of them. Lifecycle-safety stays ours
|
|
42
|
+
(round timeouts, process-group reaping, orphan stashing, auto-stop on
|
|
43
|
+
critical alerts). Containment stays yours: `[agent] exec_prefix`, a dedicated
|
|
44
|
+
unprivileged user, no passwordless sudo, and egress limits.
|
|
45
|
+
|
|
40
46
|
## Containment
|
|
41
47
|
|
|
42
48
|
If you need to bound what the agent can reach, run it inside a container (or
|
|
@@ -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
|
|
|
@@ -112,6 +112,7 @@ def emit_host_cgroup_memory_limit(
|
|
|
112
112
|
memory_max: int | None,
|
|
113
113
|
memory_swap_max: int | None,
|
|
114
114
|
cgroup_path: str | None,
|
|
115
|
+
defer: bool,
|
|
115
116
|
swap_total_bytes: int | None = None,
|
|
116
117
|
swap_cap_pct: float | None = None,
|
|
117
118
|
memory_high: int | None = None,
|
|
@@ -120,8 +121,11 @@ def emit_host_cgroup_memory_limit(
|
|
|
120
121
|
) -> None:
|
|
121
122
|
"""Emit once at serve startup: this process's cgroup v2 memory budget
|
|
122
123
|
(``metrics.cgroup_memory_limits``). ``None`` fields mean unlimited (or
|
|
123
|
-
cgroup v2 unavailable).
|
|
124
|
-
|
|
124
|
+
cgroup v2 unavailable). ``defer`` is the already-computed mid-round-floor
|
|
125
|
+
decision (True when both caps are finite and plausible, including
|
|
126
|
+
``MemorySwapMax=0``) -- the event carries that yes/no, not just the
|
|
127
|
+
inputs. Serve uses this once to decide whether the mid-round hard floor
|
|
128
|
+
can defer to kernel cgroup-OOM -- see
|
|
125
129
|
``emit_mem_pressure_deferred_to_cgroup`` below.
|
|
126
130
|
|
|
127
131
|
This also carries an optional startup advisory
|
|
@@ -152,6 +156,7 @@ def emit_host_cgroup_memory_limit(
|
|
|
152
156
|
memory_max=memory_max,
|
|
153
157
|
memory_swap_max=memory_swap_max,
|
|
154
158
|
cgroup_path=cgroup_path,
|
|
159
|
+
defer=defer,
|
|
155
160
|
swap_total_bytes=swap_total_bytes,
|
|
156
161
|
swap_cap_pct=swap_cap_pct,
|
|
157
162
|
memory_high=memory_high,
|
|
@@ -171,6 +176,9 @@ def emit_round_cgroup_memory(
|
|
|
171
176
|
events_oom_delta: int,
|
|
172
177
|
events_oom_kill_delta: int,
|
|
173
178
|
bounding_cgroup_path: str | None,
|
|
179
|
+
io_psi_some_avg10: float | None = None,
|
|
180
|
+
io_psi_full_avg10: float | None = None,
|
|
181
|
+
psi_full_total: float | int | None = None,
|
|
174
182
|
) -> None:
|
|
175
183
|
"""Emit once per round: the round's peak cgroup ``memory.current`` (+swap) and
|
|
176
184
|
the ``memory.events`` DELTAS (high/max/oom/oom_kill) over the round, read at the
|
|
@@ -185,9 +193,24 @@ def emit_round_cgroup_memory(
|
|
|
185
193
|
process's own) cgroup; here it names the BOUNDING ANCESTOR (the one whose
|
|
186
194
|
``memory.max`` actually binds). Same key, two meanings across the two
|
|
187
195
|
events an operator correlates would be a trap -- events.md pins field
|
|
188
|
-
semantics permanently once shipped, so the distinct name is chosen now.
|
|
196
|
+
semantics permanently once shipped, so the distinct name is chosen now.
|
|
197
|
+
|
|
198
|
+
Optional corroborating PSI fields (``io_psi_some_avg10``,
|
|
199
|
+
``io_psi_full_avg10``, ``psi_full_total``) come from ``metrics.sample()``
|
|
200
|
+
at emit time. Each is omitted when unread (``None``). They ride FLAT on
|
|
201
|
+
this event -- never copied into ``Pressure.context``, so the kill ladder
|
|
202
|
+
stays blind to them."""
|
|
189
203
|
from agent_runner.events import ROUND_CGROUP_MEMORY, emit
|
|
190
204
|
|
|
205
|
+
extra = {
|
|
206
|
+
k: v
|
|
207
|
+
for k, v in (
|
|
208
|
+
("io_psi_some_avg10", io_psi_some_avg10),
|
|
209
|
+
("io_psi_full_avg10", io_psi_full_avg10),
|
|
210
|
+
("psi_full_total", psi_full_total),
|
|
211
|
+
)
|
|
212
|
+
if v is not None
|
|
213
|
+
}
|
|
191
214
|
emit(
|
|
192
215
|
log_dir,
|
|
193
216
|
ROUND_CGROUP_MEMORY,
|
|
@@ -199,6 +222,7 @@ def emit_round_cgroup_memory(
|
|
|
199
222
|
events_oom_delta=events_oom_delta,
|
|
200
223
|
events_oom_kill_delta=events_oom_kill_delta,
|
|
201
224
|
bounding_cgroup_path=bounding_cgroup_path,
|
|
225
|
+
**extra,
|
|
202
226
|
)
|
|
203
227
|
|
|
204
228
|
|
|
@@ -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.13'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 3, 13)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -128,6 +128,8 @@ def _emit_round_cgroup_memory(
|
|
|
128
128
|
def _delta(key: str) -> int:
|
|
129
129
|
return max(0, cur_ev.get(key, 0) - base_ev.get(key, 0))
|
|
130
130
|
|
|
131
|
+
# One sample() at emit -- corroborating IO/mem-PSI, omitted when unread.
|
|
132
|
+
s = metrics.sample()
|
|
131
133
|
emit_round_cgroup_memory(
|
|
132
134
|
log_dir,
|
|
133
135
|
round_num=round_num,
|
|
@@ -138,6 +140,9 @@ def _emit_round_cgroup_memory(
|
|
|
138
140
|
events_oom_delta=_delta("oom"),
|
|
139
141
|
events_oom_kill_delta=_delta("oom_kill"),
|
|
140
142
|
bounding_cgroup_path=state["bounding_cgroup_path"],
|
|
143
|
+
io_psi_some_avg10=s.get("io_psi_some_avg10"),
|
|
144
|
+
io_psi_full_avg10=s.get("io_psi_full_avg10"),
|
|
145
|
+
psi_full_total=s.get("psi_full_total"),
|
|
141
146
|
)
|
|
142
147
|
return cur, oom_kill_baseline
|
|
143
148
|
|
|
@@ -277,8 +282,9 @@ def _release_brake(log_dir: Path, round_num: int, previous: str, *, reason: str)
|
|
|
277
282
|
|
|
278
283
|
def _probe_and_emit_cgroup_defer(log_dir: Path, *, brake_memory_high: bool = False) -> bool:
|
|
279
284
|
"""Probe this process's cgroup v2 memory budget once at serve startup,
|
|
280
|
-
emit host_cgroup_memory_limit
|
|
281
|
-
|
|
285
|
+
emit host_cgroup_memory_limit (including the ``defer`` decision, not just
|
|
286
|
+
the inputs) for observability, and return whether the mid-round hard
|
|
287
|
+
floor should defer to kernel cgroup-OOM: True only when
|
|
282
288
|
BOTH memory.max and memory.swap.max are finite (the field host's
|
|
283
289
|
MemoryMax=320M + MemorySwapMax=160M shape) -- that budget is bounded end
|
|
284
290
|
to end, so cgroup-OOM WILL fire and contain the agent while the host
|
|
@@ -401,6 +407,7 @@ def _probe_and_emit_cgroup_defer(log_dir: Path, *, brake_memory_high: bool = Fal
|
|
|
401
407
|
memory_max=limits["memory_max"],
|
|
402
408
|
memory_swap_max=swap_max,
|
|
403
409
|
cgroup_path=limits["cgroup_path"],
|
|
410
|
+
defer=defer,
|
|
404
411
|
swap_total_bytes=swap_total,
|
|
405
412
|
swap_cap_pct=swap_cap_pct,
|
|
406
413
|
memory_high=memory_high,
|
|
@@ -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",
|