cli-agent-runner 0.2.18__tar.gz → 0.2.19__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/BACKLOG.md +35 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/CHANGELOG.md +15 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/PKG-INFO +2 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/README.md +1 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/__init__.py +6 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_docgen.py +97 -1
- cli_agent_runner-0.2.19/agent_runner/_emit/__init__.py +103 -0
- cli_agent_runner-0.2.19/agent_runner/_emit/agent.py +231 -0
- cli_agent_runner-0.2.19/agent_runner/_emit/memory.py +236 -0
- cli_agent_runner-0.2.19/agent_runner/_emit/rounds.py +189 -0
- cli_agent_runner-0.2.19/agent_runner/_emit/serve.py +193 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_monitor_detectors.py +8 -8
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_monitor_state.py +9 -17
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_plugin_scan.py +22 -3
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_resolve.py +1 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_round_outcome.py +13 -13
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_round_support.py +1 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_serve_policy.py +32 -10
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_throttle.py +7 -7
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_version.py +2 -2
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/agent_runtime.py +23 -7
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/api.py +60 -24
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/api_types.py +1 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/builtin_plugins/claude_rate_limit.py +5 -5
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/builtin_plugins/default_dirty_handler.py +1 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/builtin_plugins/gemini.py +1 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/builtin_plugins/kimi.py +1 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/builtin_plugins/pi.py +3 -3
- cli_agent_runner-0.2.19/agent_runner/cli/_serve_cgroup.py +273 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/_serve_round.py +37 -253
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/common.py +23 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/events_cmd.py +1 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/monitor_cmd.py +16 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/round_cmd.py +7 -10
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/serve_cmd.py +14 -14
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/config/errors.py +1 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/config/models.py +10 -10
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/config/validators.py +1 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/hooks.py +6 -7
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/metrics.py +44 -12
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/migrations.py +3 -3
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/monitor.py +1 -2
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/remote_relay.py +18 -2
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/runner.py +3 -3
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/service_unit.py +5 -10
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/startup_check.py +1 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/vcs_state.py +3 -3
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/README.md +1 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/long-running-agents.md +9 -4
- cli_agent_runner-0.2.19/docs/memory-efficiency.md +244 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/runbook.md +31 -17
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_dirty_handler_seam.py +13 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_grace_kill_emission.py +5 -2
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_architecture.py +107 -5
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_doc_claims_match_ssot.py +20 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_import_footprint.py +5 -0
- cli_agent_runner-0.2.19/tests/invariants/test_no_version_refs.py +173 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_timeout_budget_invariant.py +10 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_agent_runtime.py +25 -17
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_agent_runtime_grace.py +82 -28
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_agent_runtime_marker_scan.py +8 -4
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_agent_runtime_progress.py +19 -6
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_agent_runtime_reap.py +47 -5
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_cgroup_probe.py +6 -6
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_cli_service_peek_monitor.py +9 -2
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_docgen.py +87 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_init_entry_points.py +19 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_metrics_cgroup_usage.py +29 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_loop.py +58 -13
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_remote.py +128 -9
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_plugin_scan_parity.py +32 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_mem_gate.py +1 -1
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_round_cgroup_spine.py +19 -19
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_round_oom_killed.py +22 -22
- cli_agent_runner-0.2.18/agent_runner/_emit.py +0 -865
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/.codecov.yml +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/.github/workflows/ci.yml +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/.github/workflows/release.yml +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/.gitignore +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/.vulture-whitelist.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/CODE_OF_CONDUCT.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/CONTRIBUTING.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/LICENSE +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/README.zh.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/SECURITY.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_monitor_registry.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_redact.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_registry.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/_substrate.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/builtin_plugins/__init__.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/builtin_plugins/_constants.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/builtin_plugins/codewhale.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/__init__.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/__main__.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/init_cmd.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/install_cmd.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/migrate_cmd.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/peek_cmd.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/service_cmd.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/cli/upgrade_cmd.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/clock.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/config/__init__.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/config/loader.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/config/parsers.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/context_store.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/defenses.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/events.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/host_health.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/http_progress.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/lifecycle.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/phase_select.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/presets/__init__.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/presets/aider.toml +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/presets/claude.toml +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/presets/codewhale.toml +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/presets/gemini.toml +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/presets/kimi.toml +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/presets/pi.toml +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/prompt_loader.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/round_log.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/round_view.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/scaffold.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/agent_runner/schedule.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/build.sh +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/architecture.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/commands.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/configuration.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/events.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/migrations/0.2.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/plugins.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/quickstart.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/recipes/aider.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/recipes/codewhale.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/recipes/kimi.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/recipes/pi.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/docs/thesis.md +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/pyproject.toml +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/__init__.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/_clock.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/_test_helpers.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/conftest.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/contract/__init__.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/contract/test_public_api_surface.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/e2e/__init__.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/e2e/conftest.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/e2e/test_e2e_graceful_stop.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/e2e/test_e2e_install_systemd.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/e2e/test_e2e_monitor_remote.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/fixtures/cli-real-output/claude-2.1.143-assistant-tool-use.jsonl +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/fixtures/cli-real-output/claude-2.1.143-result-event.jsonl +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/fixtures/cli-real-output/gemini-0.42.0-result-event.jsonl +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/generate_vulture_whitelist.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/__init__.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_auto_stop_e2e.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_bounded_run.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_context_enricher_namespacing.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_fresh_eyes_signal.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_install_dry_run.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_monitor_seeded.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_plugin_detector_loaded.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_plugin_owned_paths.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_plugin_real_flow.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_prompt_delivery_stdin.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_run_one_round_with_fake_agent.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_scaffold_presets.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_serve_loop.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_spawn_round_mem_floor.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_spawn_round_wedged.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_substrate_fingerprint.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/integration/test_transient_error_backoff.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/__init__.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/_docs.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/_event_scan.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_atomic_write_enforced.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_authored_facts_declared.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_auto_stop_policy_ssot.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_catalogs.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_classification_ssot.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_config_error_consistency.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_config_value_set_ssot.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_dataclass_fields_have_producers.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_doc_builtin_plugins.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_doc_cli_claims.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_doc_operator_surface.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_doc_test_pointers_exist.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_doc_transient_error_claims.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_doc_usage_event_fields.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_docs_generated.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_docs_index_complete.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_entry_points_resolve.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_event_int_coercion.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_event_kind_registry.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_event_kinds_ssot.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_events_doc_contract.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_gen_blocks_name_their_source.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_hook_contract_docs.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_layer_2_loop_size.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_migrations_registry_ssot.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_module_boundaries.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_module_sizes.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_monitor_self_exclusion.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_no_ai_signatures.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_no_internal_refs.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_no_price_logic.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_no_raw_time.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_no_vacuous_invariants.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_peek_schema_version.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_phase_select_stateless.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_plugins_config_stable.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_readme_zh_is_pointer.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_round_alloc_growth.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_round_log_decode_boundary.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_round_num_single_source.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_round_result_stable.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_set_diff_for_auto_tool_classification.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_upstream_schema_canary.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/invariants/test_vulture_whitelist_generated.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/literate/__init__.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/literate/parser.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/literate/test_parser.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/literate/test_quickstart.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/__init__.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_agent_runtime_signal_name.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_api_assemble_prompt.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_api_events_stream.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_api_install.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_api_observation.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_api_read_round_num.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_api_resolve_phase.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_api_service.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_api_types.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_auto_stop_gating.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_catalogs.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_claude_error_detector.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_claude_foreign_schema.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_cli.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_cli_common.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_cli_init_install.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_cli_main_boundary.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_cli_monitor_http.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_cli_upgrade.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_codewhale_code_precedence.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_codewhale_plugin.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_coerce_int_poison.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_config.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_config_fresh_eyes.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_config_max_rounds.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_config_nested_strictness.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_config_prompt_delivery.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_config_schedule.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_config_stop_file.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_config_strictness.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_config_transient_error_action.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_context_store.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_control_char_config.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_default_dirty_handler.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_defenses.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_detect_hung_latch.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_detector_isolation.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_detector_protocol.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_dirty_handlers.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_effective_throttle_view.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_emit_config_migrated.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_emit_schedule.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_emit_schedule_phase_skipped.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_events.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_events_cmd.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_events_cmd_rollover.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_events_cmd_tail.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_events_jsonl_decode.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_example_dirty_handler.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_example_plugin.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_exit_cause.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_exit_classifier.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_fresh_eyes_trigger.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_gemini_plugin.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_git_timeout.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_hook_failure_isolation.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_hook_redact_order.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_hooks.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_host_health.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_http_progress.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_http_progress_epoch.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_interruptible_sleep_should_stop.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_kimi_plugin.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_lifecycle.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_metrics.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_metrics_swap.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_migrate_cmd.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_migrations.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_assembly.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_cmd_severity.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_dedup_identity.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_dedup_rearm.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_detectors.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_event_tail.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_on_alert.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_round_deferred_stale.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_monitor_schedule_pause.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_network_blip_signal_guard.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_open_round_log.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_orphan_chain_single_pass.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_outer_round_ceiling.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_peek_argparse.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_peek_project_isolation.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_peek_schedule.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_peek_select.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_phase_agent_launch.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_phase_profiles.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_phase_select.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_pi_plugin.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_plugin_constants.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_presets.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_primary_prompt_file_home.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_prompt_loader.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_read_json_hardening.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_redact.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_resolve.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_round_cmd.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_round_cmd_sigterm.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_round_log_helpers.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_round_outcome.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_round_outcome_agent_axis.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_round_view.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_runner.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_runner_sidecar_ordering.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_runner_throttle.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_scaffold.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_schedule.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_cmd_bounded.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_config_broken.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_crash_loop.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_phase_schedule.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_policy.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_round.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_round_log.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_round_terminate.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_schedule_gate.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_sentinel.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_serve_startup_hooks.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_service_mode.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_service_unit.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_startup_battery_exit.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_startup_check.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_substrate.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_supervisor_stale_self_blind.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_throttle_back_off_ladder.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_throttle_epoch_bound.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_throttle_plugin_classification.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_upgrade_migrate.py +0 -0
- {cli_agent_runner-0.2.18 → cli_agent_runner-0.2.19}/tests/unit/test_vcs_state.py +0 -0
|
@@ -88,3 +88,38 @@ just succeeded in the same repo).
|
|
|
88
88
|
Deferred because closing it is a naming decision, not cleanup: no event kind
|
|
89
89
|
carries the meaning "stashed but ref lost" (`orphan_stash_failed` would be wrong
|
|
90
90
|
— the stash exists). Left as-is until that kind is designed.
|
|
91
|
+
|
|
92
|
+
## `_monitor_loop_iter`'s three fail-open guards are hand-duplicated
|
|
93
|
+
|
|
94
|
+
`api._monitor_loop_iter` wraps the startup `monitor_started` emit, the
|
|
95
|
+
per-tick poll, and `on_alert`'s per-alert dispatch each in its own
|
|
96
|
+
`try/except Exception: warnings.warn(...)` block so a crash in any one of the
|
|
97
|
+
three never ends monitor supervision. The three guards are structurally
|
|
98
|
+
identical (catch, format a `type(e).__name__: {e}` warning, keep the loop
|
|
99
|
+
alive) but hand-copied rather than sharing one implementation; `on_alert`'s
|
|
100
|
+
own `emit()` closure already shows the pattern for factoring a shared guard.
|
|
101
|
+
|
|
102
|
+
Collapsing them to one shared guard is behavior-preserving — it must not
|
|
103
|
+
change which failures warn vs which propagate, nor the specific fallback
|
|
104
|
+
each site takes on failure (sleep-and-retry after a poll failure,
|
|
105
|
+
`verdict = "failed"` after an `on_alert` failure). Slated for 0.2.19.
|
|
106
|
+
|
|
107
|
+
## `oom_kill_delta` is named differently on its two events — NEEDS_DESIGN, DEFERRED to 0.3
|
|
108
|
+
|
|
109
|
+
The same cgroup `memory.events.oom_kill` delta is emitted under two
|
|
110
|
+
different field names depending on which event carries it: `round_cgroup_memory`
|
|
111
|
+
reports it as `events_oom_kill_delta` (matching its sibling counters
|
|
112
|
+
`events_high_delta`/`events_max_delta`/`events_oom_delta`), while
|
|
113
|
+
`round_oom_killed` — folded from the same scan, no second sysfs read —
|
|
114
|
+
reports the identical value as bare `oom_kill_delta`. A reader correlating
|
|
115
|
+
the two events for the same round has to know the same quantity carries two
|
|
116
|
+
names.
|
|
117
|
+
|
|
118
|
+
Renaming either side is a change to a published event field name — part of
|
|
119
|
+
the peek/event JSON contract — so which name becomes canonical (and whether
|
|
120
|
+
it lands as a rename or an additive alias) needs a decision, not a silent
|
|
121
|
+
swap. Tagged NEEDS_DESIGN. Deferred to 0.3's event-model canonicalization
|
|
122
|
+
(see [[project-03-direction]]) rather than reconciled standalone in 0.2.19 —
|
|
123
|
+
0.3 is already renaming/unifying event fields wholesale, and settling this
|
|
124
|
+
name now risks a double-rename (once here, again when 0.3's canonical model
|
|
125
|
+
lands). No field name changes in code until that decision is made.
|
|
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.19] - 2026-09-09
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- `monitor --host` relay now drains cleanly on SIGTERM — tears down its ssh child group and exits 0 — instead of dying immediately, matching `serve`'s stop-signal behavior.
|
|
12
|
+
- Internal structure, behavior-identical: `_emit.py` split into an `_emit/` package (domain submodules behind a facade); the cgroup-pressure spine carved out of `cli/_serve_round.py` into `cli/_serve_cgroup.py`.
|
|
13
|
+
- The give-up exit-code table in the runbook is now generated from code instead of hand-copied; detector/defense counts and the `peek` schema version are now invariant-pinned against the same source.
|
|
14
|
+
- Internal hardening: the monitor's fail-open guards are unified into one implementation; `kill`'s systemd-user SIGKILL escalation shares the same bounded poll `serve` uses; the cgroup pressure spine resolves its bounding ancestor once per round instead of on every tick; a single-implementation `Protocol` was collapsed to its concrete type; a process's stored child-name no longer passes an attacker-rewritable `argv[0]` through unbounded; plugin discovery now scans legacy egg-info installs and handles entry points with an extras suffix.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Docs: the `agent_usage_recorded` plugin list corrected to claude/gemini/codewhale/pi (kimi's built-in plugin emits no token counters).
|
|
18
|
+
|
|
19
|
+
### Notes
|
|
20
|
+
- Not breaking: no `agent-runner migrate` step, no systemd unit change. The relay's SIGTERM-drain above is the one behavior change in this release — anyone relying on the relay's old immediate-exit-on-SIGTERM will now see it drain instead.
|
|
21
|
+
- `oom_kill_delta`'s two-different-field-names inconsistency is deferred to 0.3's event-model work rather than reconciled here, to avoid a double-rename.
|
|
22
|
+
|
|
8
23
|
## [0.2.18] - 2026-09-08
|
|
9
24
|
|
|
10
25
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cli-agent-runner
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.19
|
|
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
|
|
@@ -152,6 +152,7 @@ your laptop closed. Full relay + SSH-trust mechanics:
|
|
|
152
152
|
- [`docs/configuration.md`](docs/configuration.md) — `agent-runner.toml` schema
|
|
153
153
|
- [`docs/runbook.md`](docs/runbook.md) — operator troubleshooting (OAuth, disk, orphan)
|
|
154
154
|
- [`docs/architecture.md`](docs/architecture.md) — 3-layer model, defenses-as-data
|
|
155
|
+
- [`docs/memory-efficiency.md`](docs/memory-efficiency.md) — supervisor memory footprint, measured release over release
|
|
155
156
|
|
|
156
157
|
## Development
|
|
157
158
|
|
|
@@ -114,6 +114,7 @@ your laptop closed. Full relay + SSH-trust mechanics:
|
|
|
114
114
|
- [`docs/configuration.md`](docs/configuration.md) — `agent-runner.toml` schema
|
|
115
115
|
- [`docs/runbook.md`](docs/runbook.md) — operator troubleshooting (OAuth, disk, orphan)
|
|
116
116
|
- [`docs/architecture.md`](docs/architecture.md) — 3-layer model, defenses-as-data
|
|
117
|
+
- [`docs/memory-efficiency.md`](docs/memory-efficiency.md) — supervisor memory footprint, measured release over release
|
|
117
118
|
|
|
118
119
|
## Development
|
|
119
120
|
|
|
@@ -44,7 +44,12 @@ def _load_plugins_from_group(group: str) -> None:
|
|
|
44
44
|
|
|
45
45
|
for name, value in scan_entry_points(sys.path, group):
|
|
46
46
|
try:
|
|
47
|
-
|
|
47
|
+
# An entry-point value may carry a trailing extras marker
|
|
48
|
+
# (``module:attr [extra1,extra2]``); importlib.metadata.EntryPoint's
|
|
49
|
+
# own module/attr grammar excludes "[", so anything from the first
|
|
50
|
+
# "[" onward is always extras, never part of the path — strip it
|
|
51
|
+
# before resolving, or it glues onto attr_path and breaks getattr().
|
|
52
|
+
module_path, _, attr_path = value.partition("[")[0].rstrip().partition(":")
|
|
48
53
|
mod = importlib.import_module(module_path)
|
|
49
54
|
target = mod
|
|
50
55
|
for attr in filter(None, attr_path.split(".")):
|
|
@@ -16,6 +16,7 @@ import typing
|
|
|
16
16
|
from collections.abc import Callable
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
|
|
19
|
+
from agent_runner import _serve_policy
|
|
19
20
|
from agent_runner.cli.init_cmd import _preset_names
|
|
20
21
|
from agent_runner.config import (
|
|
21
22
|
AgentConfig,
|
|
@@ -30,7 +31,14 @@ from agent_runner.config import (
|
|
|
30
31
|
VcsConfig,
|
|
31
32
|
)
|
|
32
33
|
from agent_runner.defenses import catalog
|
|
33
|
-
from agent_runner.events import
|
|
34
|
+
from agent_runner.events import (
|
|
35
|
+
CONFIG_BROKEN,
|
|
36
|
+
CRASH_LOOP,
|
|
37
|
+
KNOWN_EVENT_KINDS,
|
|
38
|
+
MEM_LOOP,
|
|
39
|
+
MEM_LOOP_PERSISTENT,
|
|
40
|
+
STALLED_NO_PROGRESS,
|
|
41
|
+
)
|
|
34
42
|
from agent_runner.monitor import AUTO_STOP_ALERTS, KNOWN_ALERT_KINDS
|
|
35
43
|
|
|
36
44
|
# (toml_section_name, dataclass, nested_sub_sections). Nesting is an EXPLICIT
|
|
@@ -156,6 +164,84 @@ def render_event_kinds_list() -> str:
|
|
|
156
164
|
return "\n".join(f"- `{k}`" for k in sorted(KNOWN_EVENT_KINDS))
|
|
157
165
|
|
|
158
166
|
|
|
167
|
+
def _giveup_verdict_rows() -> list[tuple[str, int, bool]]:
|
|
168
|
+
"""``(verdict, exit_code, in_restart_prevent)`` rows for serve's five
|
|
169
|
+
give-up verdicts, in the order the runbook's Symptom section lists them.
|
|
170
|
+
|
|
171
|
+
The verdict->code mapping itself is CONTROL FLOW in
|
|
172
|
+
``cli/_serve_round.post_round_verdicts`` (a chain of ``if``/``return``,
|
|
173
|
+
not a dict), so it is mirrored here by hand rather than imported — the
|
|
174
|
+
four exit-code integers are imported from ``_serve_policy``, the actual
|
|
175
|
+
code SSOT, so a constant change here is caught by ``./build.sh docs``
|
|
176
|
+
producing a diff. The third column is DERIVED from
|
|
177
|
+
``_serve_policy.RESTART_PREVENT_EXIT_CODES`` (the same single source
|
|
178
|
+
``service_unit.py`` renders from) rather than hand-typed per row.
|
|
179
|
+
"""
|
|
180
|
+
verdict_codes = [
|
|
181
|
+
(CONFIG_BROKEN, _serve_policy.PERMANENT_CONFIG_EXIT),
|
|
182
|
+
(CRASH_LOOP, _serve_policy.CRASH_LOOP_EXIT),
|
|
183
|
+
(STALLED_NO_PROGRESS, _serve_policy.CRASH_LOOP_EXIT),
|
|
184
|
+
(MEM_LOOP_PERSISTENT, _serve_policy.MEM_LOOP_PERSISTENT_EXIT),
|
|
185
|
+
(MEM_LOOP, _serve_policy.MEM_LOOP_EXIT),
|
|
186
|
+
]
|
|
187
|
+
return [
|
|
188
|
+
(verdict, code, code in _serve_policy.RESTART_PREVENT_EXIT_CODES)
|
|
189
|
+
for verdict, code in verdict_codes
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def render_giveup_exit_codes_table() -> str:
|
|
194
|
+
"""Verdict -> exit-code -> ``RestartPreventExitStatus`` membership table
|
|
195
|
+
for serve's give-up breakers. 5 verdicts map to only 4 distinct codes:
|
|
196
|
+
``stalled_no_progress`` reuses ``crash_loop``'s 75 rather than minting its
|
|
197
|
+
own (same give-up verdict — "an unknown failure kept recurring, stop for
|
|
198
|
+
real" — reached via a different signal)."""
|
|
199
|
+
lines = ["| Verdict | Exit code | `RestartPreventExitStatus`? |", "|---|---|---|"]
|
|
200
|
+
seen_codes: dict[int, str] = {}
|
|
201
|
+
for verdict, code, stays_stopped in _giveup_verdict_rows():
|
|
202
|
+
shares = f" (shares `{seen_codes[code]}`'s exit code)" if code in seen_codes else ""
|
|
203
|
+
seen_codes.setdefault(code, verdict)
|
|
204
|
+
behavior = "yes — stays stopped" if stays_stopped else "no — restarts (break-then-restart)"
|
|
205
|
+
lines.append(f"| `{verdict}` | `{code}`{shares} | {behavior} |")
|
|
206
|
+
return "\n".join(lines)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def render_giveup_systemd_example() -> str:
|
|
210
|
+
"""The ``systemd unit pattern recommendations`` fenced example.
|
|
211
|
+
|
|
212
|
+
``RestartPreventExitStatus`` lists the three DELIBERATE give-up codes
|
|
213
|
+
(config_broken/crash_loop/mem_loop_persistent), derived from
|
|
214
|
+
``_serve_policy.RESTART_PREVENT_EXIT_CODES`` — the same single source
|
|
215
|
+
``service_unit.py`` renders from; ``mem_loop``'s 71 is deliberately
|
|
216
|
+
excluded so systemd respawns to retry. The second comment line's
|
|
217
|
+
leading padding is derived from the first line's own length (not
|
|
218
|
+
hand-counted) so the two ``#`` columns always line up even if a future
|
|
219
|
+
exit code changes ``prevent``'s width."""
|
|
220
|
+
prevent = " ".join(str(c) for c in _serve_policy.RESTART_PREVENT_EXIT_CODES)
|
|
221
|
+
header = f"RestartPreventExitStatus={prevent} "
|
|
222
|
+
return (
|
|
223
|
+
"```ini\n"
|
|
224
|
+
"# Prod (infinite supervisor) — current default\n"
|
|
225
|
+
"[Service]\n"
|
|
226
|
+
"ExecStart=... serve --config /etc/agent-runner.toml\n"
|
|
227
|
+
"Restart=on-failure\n"
|
|
228
|
+
f"{header}"
|
|
229
|
+
f"# config_broken ({_serve_policy.PERMANENT_CONFIG_EXIT}) / "
|
|
230
|
+
f"crash_loop ({_serve_policy.CRASH_LOOP_EXIT}) / "
|
|
231
|
+
f"mem_loop_persistent ({_serve_policy.MEM_LOOP_PERSISTENT_EXIT}) stay stopped\n"
|
|
232
|
+
f"{' ' * len(header)}"
|
|
233
|
+
f"# mem_loop ({_serve_policy.MEM_LOOP_EXIT}) is NOT listed here — it restarts\n"
|
|
234
|
+
"RestartSec=3\n"
|
|
235
|
+
"\n"
|
|
236
|
+
"# Bounded job\n"
|
|
237
|
+
"[Service]\n"
|
|
238
|
+
"ExecStart=... serve --config /etc/test.toml --max-rounds 10\n"
|
|
239
|
+
"Restart=on-failure\n"
|
|
240
|
+
"RestartSec=5\n"
|
|
241
|
+
"```"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
|
|
159
245
|
def _render_migrate_transforms() -> str:
|
|
160
246
|
"""Bullet list of the transforms `agent-runner migrate` applies, from the registry.
|
|
161
247
|
A few entries carry a `parsed -> str` describe (so the manual report can name the
|
|
@@ -265,6 +351,16 @@ RENDERERS: dict[str, Renderer] = {
|
|
|
265
351
|
"migrate-transforms": Renderer(
|
|
266
352
|
_render_migrate_transforms, "agent_runner/migrations.py MIGRATIONS"
|
|
267
353
|
),
|
|
354
|
+
"giveup-exit-codes": Renderer(
|
|
355
|
+
render_giveup_exit_codes_table,
|
|
356
|
+
"agent_runner/_serve_policy.py exit-code constants + "
|
|
357
|
+
"cli/_serve_round.py post_round_verdicts's verdict->code map",
|
|
358
|
+
),
|
|
359
|
+
"giveup-systemd-example": Renderer(
|
|
360
|
+
render_giveup_systemd_example,
|
|
361
|
+
"agent_runner/_serve_policy.py exit-code constants "
|
|
362
|
+
"(service_unit.py's RestartPreventExitStatus mirrors these same three)",
|
|
363
|
+
),
|
|
268
364
|
}
|
|
269
365
|
RENDERERS.update(
|
|
270
366
|
{
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Event emission wrappers — extracted from api.py for module-size hygiene.
|
|
2
|
+
|
|
3
|
+
Public facade over the ``_emit`` package: every sibling module in
|
|
4
|
+
``agent_runner`` (and every test) imports from ``agent_runner._emit``
|
|
5
|
+
directly, never from ``agent_runner._emit.<submodule>`` — this file is the
|
|
6
|
+
one place a name crosses that boundary. All wrappers are re-exported from
|
|
7
|
+
agent_runner.api for backward compatibility; plugins and supervisor should
|
|
8
|
+
continue importing from agent_runner.api (e.g.
|
|
9
|
+
``from agent_runner.api import emit_transient_error_detected``).
|
|
10
|
+
|
|
11
|
+
Each wrapper exists to keep cli/serve_cmd.py from importing agent_runner.events
|
|
12
|
+
directly — preserves the module-boundary architecture invariant. Local-import pattern
|
|
13
|
+
inside each wrapper body keeps agent_runner.api import-cheap.
|
|
14
|
+
|
|
15
|
+
Submodules are organised by domain: ``serve`` (supervisor control plane +
|
|
16
|
+
config/schedule lifecycle), ``rounds`` (one round's own lifecycle),
|
|
17
|
+
``memory`` (host/cgroup memory pressure), ``agent`` (the agent axis — plugin
|
|
18
|
+
reports + throttle response).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from agent_runner._emit.agent import ( # noqa: F401 — public re-export
|
|
24
|
+
emit_agent_auth_error_detected,
|
|
25
|
+
emit_agent_usage_recorded,
|
|
26
|
+
emit_anomaly_repetitive_tool,
|
|
27
|
+
emit_transient_error_backoff_capped,
|
|
28
|
+
emit_transient_error_detected,
|
|
29
|
+
emit_transient_error_recovered,
|
|
30
|
+
)
|
|
31
|
+
from agent_runner._emit.memory import ( # noqa: F401 — public re-export
|
|
32
|
+
emit_host_cgroup_memory_limit,
|
|
33
|
+
emit_mem_pressure_deferred_to_cgroup,
|
|
34
|
+
emit_round_cgroup_memory,
|
|
35
|
+
emit_round_deferred,
|
|
36
|
+
emit_round_mem_critical_sample,
|
|
37
|
+
emit_round_mem_terminated,
|
|
38
|
+
emit_round_oom_killed,
|
|
39
|
+
emit_round_resumed,
|
|
40
|
+
)
|
|
41
|
+
from agent_runner._emit.rounds import ( # noqa: F401 — public re-export
|
|
42
|
+
emit_fresh_eyes_round_triggered,
|
|
43
|
+
emit_round_grace_extended,
|
|
44
|
+
emit_round_grace_kill,
|
|
45
|
+
emit_round_logs_prune_deferred,
|
|
46
|
+
emit_round_progress,
|
|
47
|
+
emit_round_substrate_after,
|
|
48
|
+
emit_round_substrate_before,
|
|
49
|
+
emit_round_supervisor_wedged,
|
|
50
|
+
emit_stale_index_lock_cleared,
|
|
51
|
+
)
|
|
52
|
+
from agent_runner._emit.serve import ( # noqa: F401 — public re-export
|
|
53
|
+
emit_config_broken,
|
|
54
|
+
emit_config_migrated,
|
|
55
|
+
emit_crash_loop,
|
|
56
|
+
emit_max_rounds_reached,
|
|
57
|
+
emit_mem_loop,
|
|
58
|
+
emit_mem_loop_persistent,
|
|
59
|
+
emit_rate_limit_stop,
|
|
60
|
+
emit_schedule_paused,
|
|
61
|
+
emit_schedule_phase_skipped,
|
|
62
|
+
emit_schedule_resumed,
|
|
63
|
+
emit_stalled_no_progress,
|
|
64
|
+
emit_stop_file_detected,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
__all__ = [
|
|
68
|
+
"emit_agent_auth_error_detected",
|
|
69
|
+
"emit_agent_usage_recorded",
|
|
70
|
+
"emit_anomaly_repetitive_tool",
|
|
71
|
+
"emit_config_broken",
|
|
72
|
+
"emit_config_migrated",
|
|
73
|
+
"emit_crash_loop",
|
|
74
|
+
"emit_fresh_eyes_round_triggered",
|
|
75
|
+
"emit_host_cgroup_memory_limit",
|
|
76
|
+
"emit_max_rounds_reached",
|
|
77
|
+
"emit_mem_loop",
|
|
78
|
+
"emit_mem_loop_persistent",
|
|
79
|
+
"emit_mem_pressure_deferred_to_cgroup",
|
|
80
|
+
"emit_rate_limit_stop",
|
|
81
|
+
"emit_round_cgroup_memory",
|
|
82
|
+
"emit_round_deferred",
|
|
83
|
+
"emit_round_grace_extended",
|
|
84
|
+
"emit_round_grace_kill",
|
|
85
|
+
"emit_round_logs_prune_deferred",
|
|
86
|
+
"emit_round_mem_critical_sample",
|
|
87
|
+
"emit_round_mem_terminated",
|
|
88
|
+
"emit_round_oom_killed",
|
|
89
|
+
"emit_round_progress",
|
|
90
|
+
"emit_round_resumed",
|
|
91
|
+
"emit_round_substrate_after",
|
|
92
|
+
"emit_round_substrate_before",
|
|
93
|
+
"emit_round_supervisor_wedged",
|
|
94
|
+
"emit_schedule_paused",
|
|
95
|
+
"emit_schedule_phase_skipped",
|
|
96
|
+
"emit_schedule_resumed",
|
|
97
|
+
"emit_stale_index_lock_cleared",
|
|
98
|
+
"emit_stalled_no_progress",
|
|
99
|
+
"emit_stop_file_detected",
|
|
100
|
+
"emit_transient_error_backoff_capped",
|
|
101
|
+
"emit_transient_error_detected",
|
|
102
|
+
"emit_transient_error_recovered",
|
|
103
|
+
]
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Event emission wrappers — the agent axis: what a per-CLI plugin reports
|
|
2
|
+
(transient errors, auth failures, usage, anomalies) and the throttle
|
|
3
|
+
response. Re-exported from ``agent_runner._emit`` (the package facade) —
|
|
4
|
+
see its docstring.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def emit_transient_error_detected(
|
|
13
|
+
log_dir: Path,
|
|
14
|
+
*,
|
|
15
|
+
classification: str,
|
|
16
|
+
agent: str,
|
|
17
|
+
reset_at_epoch: int,
|
|
18
|
+
round_num: int,
|
|
19
|
+
raw: str,
|
|
20
|
+
phase: str = "",
|
|
21
|
+
) -> None:
|
|
22
|
+
"""Emit detection of a transient agent error (rate limit / 5xx / timeout).
|
|
23
|
+
|
|
24
|
+
``phase`` is the rotation phase the failing round ran under ("" when the
|
|
25
|
+
config has no ``[phases]``). It lets a ``phase_policy = "skip"`` serve loop
|
|
26
|
+
step over *this* phase while a healthy sibling keeps running."""
|
|
27
|
+
from agent_runner._redact import redact_secrets
|
|
28
|
+
from agent_runner.events import TRANSIENT_ERROR_DETECTED, emit
|
|
29
|
+
|
|
30
|
+
raw = redact_secrets(raw)
|
|
31
|
+
emit(
|
|
32
|
+
log_dir,
|
|
33
|
+
TRANSIENT_ERROR_DETECTED,
|
|
34
|
+
classification=classification,
|
|
35
|
+
agent=agent,
|
|
36
|
+
reset_at_epoch=reset_at_epoch,
|
|
37
|
+
round_num=round_num,
|
|
38
|
+
raw=raw,
|
|
39
|
+
phase=phase,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def emit_agent_auth_error_detected(
|
|
44
|
+
log_dir: Path,
|
|
45
|
+
*,
|
|
46
|
+
round_num: int,
|
|
47
|
+
agent: str,
|
|
48
|
+
raw: str,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Emit an authentication/authorization failure the agent itself reported.
|
|
51
|
+
|
|
52
|
+
Contract: emitted by a per-CLI plugin only when the agent's OWN structured
|
|
53
|
+
output names the failure (e.g. an HTTP 401 in its JSON event stream). That
|
|
54
|
+
is certain evidence, unlike the monitor's ``oauth_fail`` text heuristic,
|
|
55
|
+
which scans free-text log tails and therefore needs a nonzero-exit shield
|
|
56
|
+
against prose that merely mentions "401". The monitor counts a round
|
|
57
|
+
carrying this event without that shield — which is what makes an auth loop
|
|
58
|
+
visible for a CLI that exits 0 on provider failure.
|
|
59
|
+
|
|
60
|
+
No back-off partner event: an auth failure is permanent until an operator
|
|
61
|
+
fixes the credential, so it is deliberately not a transient classification.
|
|
62
|
+
"""
|
|
63
|
+
from agent_runner._redact import redact_secrets
|
|
64
|
+
from agent_runner.events import AGENT_AUTH_ERROR_DETECTED, emit
|
|
65
|
+
|
|
66
|
+
emit(
|
|
67
|
+
log_dir,
|
|
68
|
+
AGENT_AUTH_ERROR_DETECTED,
|
|
69
|
+
round_num=round_num,
|
|
70
|
+
agent=agent,
|
|
71
|
+
raw=redact_secrets(raw),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def emit_transient_error_recovered(
|
|
76
|
+
log_dir: Path,
|
|
77
|
+
*,
|
|
78
|
+
classification: str,
|
|
79
|
+
agent: str,
|
|
80
|
+
throttled_for_s: int,
|
|
81
|
+
) -> None:
|
|
82
|
+
"""Emit recovery from a transient error back-off (right before resuming)."""
|
|
83
|
+
from agent_runner.events import TRANSIENT_ERROR_RECOVERED, emit
|
|
84
|
+
|
|
85
|
+
emit(
|
|
86
|
+
log_dir,
|
|
87
|
+
TRANSIENT_ERROR_RECOVERED,
|
|
88
|
+
classification=classification,
|
|
89
|
+
agent=agent,
|
|
90
|
+
throttled_for_s=throttled_for_s,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def emit_agent_usage_recorded(
|
|
95
|
+
log_dir: Path,
|
|
96
|
+
*,
|
|
97
|
+
agent: str,
|
|
98
|
+
model: str,
|
|
99
|
+
round_num: int,
|
|
100
|
+
input_tokens: int,
|
|
101
|
+
output_tokens: int,
|
|
102
|
+
cached_tokens: int,
|
|
103
|
+
cost_usd: float | None,
|
|
104
|
+
duration_ms: int,
|
|
105
|
+
models_breakdown: dict[str, dict[str, int]] | None = None,
|
|
106
|
+
cache_creation_tokens: int = 0,
|
|
107
|
+
tool_call_count: int = 0,
|
|
108
|
+
phase: str = "",
|
|
109
|
+
success: bool = True,
|
|
110
|
+
) -> None:
|
|
111
|
+
"""Emit per-round usage record from a CLI plugin.
|
|
112
|
+
|
|
113
|
+
Raw data only — aggregation (totals, projections, budget warnings) is
|
|
114
|
+
deferred to consumers and the capability layer.
|
|
115
|
+
|
|
116
|
+
Field semantics:
|
|
117
|
+
- ``input_tokens``: net non-cached input (both claude and gemini emit net;
|
|
118
|
+
total throughput = input_tokens + cached_tokens).
|
|
119
|
+
- ``cost_usd``: USD cost for the round, or None when CLI doesn't expose it
|
|
120
|
+
(gemini has no cost field; claude exposes total_cost_usd).
|
|
121
|
+
- ``models_breakdown``: only populated when a round used multiple models
|
|
122
|
+
(gemini multi-model rounds). None for claude (always single-model).
|
|
123
|
+
- ``cache_creation_tokens``: claude only — ``usage.cache_creation_input_tokens``,
|
|
124
|
+
independent count from ``cached_tokens`` (cache_read). Priced differently
|
|
125
|
+
from fresh input by the provider; the framework records the count only and
|
|
126
|
+
never interprets cost (no price tables here — see ``cost_usd`` above for the
|
|
127
|
+
one cost figure the framework passes through verbatim). Gemini has no
|
|
128
|
+
equivalent → 0.
|
|
129
|
+
- ``tool_call_count``: number of tool invocations the agent made in the round.
|
|
130
|
+
Claude: count of ``tool_use`` content blocks across all assistant events.
|
|
131
|
+
Gemini: ``stats.tool_calls``.
|
|
132
|
+
- ``phase``: phase label from HookContext (e.g. "planning"); empty string when None.
|
|
133
|
+
- ``success``: the supervisor's clean-exit predicate (``RoundResult.ok``).
|
|
134
|
+
A plugin MAY additionally fold in the agent's own terminal verdict when
|
|
135
|
+
its CLI's exit code is unreliable — pi does, because pi exits 0 on
|
|
136
|
+
provider failure.
|
|
137
|
+
"""
|
|
138
|
+
from agent_runner.events import AGENT_USAGE_RECORDED, emit
|
|
139
|
+
|
|
140
|
+
emit(
|
|
141
|
+
log_dir,
|
|
142
|
+
AGENT_USAGE_RECORDED,
|
|
143
|
+
agent=agent,
|
|
144
|
+
model=model,
|
|
145
|
+
round_num=round_num,
|
|
146
|
+
input_tokens=input_tokens,
|
|
147
|
+
output_tokens=output_tokens,
|
|
148
|
+
cached_tokens=cached_tokens,
|
|
149
|
+
cost_usd=cost_usd,
|
|
150
|
+
duration_ms=duration_ms,
|
|
151
|
+
models_breakdown=models_breakdown,
|
|
152
|
+
cache_creation_tokens=cache_creation_tokens,
|
|
153
|
+
tool_call_count=tool_call_count,
|
|
154
|
+
phase=phase,
|
|
155
|
+
success=success,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def emit_anomaly_repetitive_tool(
|
|
160
|
+
log_dir: Path,
|
|
161
|
+
*,
|
|
162
|
+
round_num: int,
|
|
163
|
+
tool_name: str,
|
|
164
|
+
target: str | None,
|
|
165
|
+
count: int,
|
|
166
|
+
window: int,
|
|
167
|
+
) -> None:
|
|
168
|
+
"""Emit when claude plugin detects the same (tool, target) tuple repeated
|
|
169
|
+
>= threshold times in a sliding window of tool-call events.
|
|
170
|
+
|
|
171
|
+
Claude-only (gemini JSONL stats summary does not expose per-tool events).
|
|
172
|
+
Default OFF: both anomaly_repetitive_window and anomaly_repetitive_threshold
|
|
173
|
+
must be > 0 in [monitor] config to activate.
|
|
174
|
+
"""
|
|
175
|
+
from agent_runner.events import ANOMALY_REPETITIVE_TOOL, emit
|
|
176
|
+
|
|
177
|
+
emit(
|
|
178
|
+
log_dir,
|
|
179
|
+
ANOMALY_REPETITIVE_TOOL,
|
|
180
|
+
round_num=round_num,
|
|
181
|
+
tool_name=tool_name,
|
|
182
|
+
target=target,
|
|
183
|
+
count=count,
|
|
184
|
+
window=window,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def emit_transient_error_backoff_capped(
|
|
189
|
+
log_dir: Path,
|
|
190
|
+
*,
|
|
191
|
+
classification: str,
|
|
192
|
+
agent: str,
|
|
193
|
+
requested_sleep_s: int,
|
|
194
|
+
applied_sleep_s: int,
|
|
195
|
+
original_reset_at_epoch: int | None = None,
|
|
196
|
+
applied_reset_at_epoch: int | None = None,
|
|
197
|
+
consecutive_count: int | None = None,
|
|
198
|
+
capped_by_absolute_max: bool | None = None,
|
|
199
|
+
) -> None:
|
|
200
|
+
"""Emit when supervisor adjusts the plugin-emitted transient back-off.
|
|
201
|
+
|
|
202
|
+
Fires in two cases:
|
|
203
|
+
1. **Exp backoff applied**: estimated-class transient errors
|
|
204
|
+
(`rate_limit_model` / `api_transient_5xx` / `api_timeout`) doubled
|
|
205
|
+
on consecutive failures. ``consecutive_count`` > 1, multiplier > 1×.
|
|
206
|
+
2. **Defensive cap hit**: malformed `reset_at_epoch` or the
|
|
207
|
+
30-min absolute cap clipped the wait. ``capped_by_absolute_max`` True.
|
|
208
|
+
|
|
209
|
+
Fields ``original_reset_at_epoch`` / ``applied_reset_at_epoch`` /
|
|
210
|
+
``consecutive_count`` / ``capped_by_absolute_max`` are newer fields. Older
|
|
211
|
+
callers that pass only the first 4 kwargs continue to work; the new
|
|
212
|
+
fields are omitted from the payload when None.
|
|
213
|
+
"""
|
|
214
|
+
from agent_runner.events import TRANSIENT_ERROR_BACKOFF_CAPPED, emit
|
|
215
|
+
|
|
216
|
+
kwargs: dict = {
|
|
217
|
+
"classification": classification,
|
|
218
|
+
"agent": agent,
|
|
219
|
+
"requested_sleep_s": requested_sleep_s,
|
|
220
|
+
"applied_sleep_s": applied_sleep_s,
|
|
221
|
+
}
|
|
222
|
+
if original_reset_at_epoch is not None:
|
|
223
|
+
kwargs["original_reset_at_epoch"] = original_reset_at_epoch
|
|
224
|
+
if applied_reset_at_epoch is not None:
|
|
225
|
+
kwargs["applied_reset_at_epoch"] = applied_reset_at_epoch
|
|
226
|
+
if consecutive_count is not None:
|
|
227
|
+
kwargs["consecutive_count"] = consecutive_count
|
|
228
|
+
if capped_by_absolute_max is not None:
|
|
229
|
+
kwargs["capped_by_absolute_max"] = capped_by_absolute_max
|
|
230
|
+
|
|
231
|
+
emit(log_dir, TRANSIENT_ERROR_BACKOFF_CAPPED, **kwargs)
|