cli-agent-runner 0.3.8__tar.gz → 0.3.9__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.8 → cli_agent_runner-0.3.9}/.vulture-whitelist.py +1 -30
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/CHANGELOG.md +11 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/PKG-INFO +1 -4
- cli_agent_runner-0.3.9/agent_runner/__init__.py +121 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_docgen.py +2 -3
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_emit/__init__.py +0 -14
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_monitor_registry.py +2 -17
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_notify.py +3 -8
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_observe.py +1 -6
- cli_agent_runner-0.3.9/agent_runner/_plugin_manifest.py +181 -0
- cli_agent_runner-0.3.9/agent_runner/_registry.py +38 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_version.py +2 -2
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/agent_runtime.py +39 -7
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/api.py +0 -6
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/api_types.py +3 -36
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/builtin_plugins/claude_rate_limit.py +16 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/builtin_plugins/gemini.py +1 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/builtin_plugins/pi.py +3 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/_serve_round.py +4 -189
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/common.py +10 -14
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/doctor_cmd.py +9 -25
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/monitor_cmd.py +2 -2
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/peek_cmd.py +2 -2
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/serve_cmd.py +49 -64
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/config/__init__.py +1 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/config/models.py +22 -23
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/config/parsers.py +13 -81
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/events.py +7 -47
- cli_agent_runner-0.3.9/agent_runner/hooks.py +139 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/migrations.py +212 -100
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/monitor.py +11 -43
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/runner.py +33 -117
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/vcs_state.py +133 -194
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/architecture.md +12 -61
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/commands.md +12 -4
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/configuration.md +17 -26
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/events.md +3 -3
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/long-running-agents.md +3 -3
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/memory-efficiency.md +77 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/migrations/0.3.md +31 -5
- cli_agent_runner-0.3.9/docs/plugins.md +374 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/runbook.md +5 -43
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/thesis.md +9 -9
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/pyproject.toml +9 -13
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/_test_helpers.py +6 -86
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/conftest.py +2 -17
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/contract/test_public_api_surface.py +0 -50
- cli_agent_runner-0.3.9/tests/integration/test_cooperative_stop_signal.py +126 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_run_one_round_with_fake_agent.py +27 -7
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/_event_scan.py +1 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_architecture.py +2 -10
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_doc_builtin_plugins.py +1 -2
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_doc_claims_match_ssot.py +5 -6
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_doc_cli_claims.py +0 -16
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_doc_operator_surface.py +1 -36
- cli_agent_runner-0.3.9/tests/invariants/test_event_kind_registry.py +33 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_event_kinds_ssot.py +1 -1
- cli_agent_runner-0.3.9/tests/invariants/test_hard_wall_signal.py +126 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_hook_contract_docs.py +2 -2
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_import_footprint.py +2 -11
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_layer_2_loop_size.py +1 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_module_boundaries.py +0 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_peek_schema_version.py +3 -20
- cli_agent_runner-0.3.9/tests/invariants/test_plugins_config_stable.py +38 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_timeout_budget_invariant.py +1 -12
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_api_observation.py +8 -9
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_api_resolve_phase.py +1 -10
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_apply_reap_grace_env.py +3 -3
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_config.py +23 -44
- cli_agent_runner-0.3.9/tests/unit/test_cooperative_signal_env.py +101 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_detect_hung_latch.py +9 -11
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_docgen.py +0 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_doctor_cmd.py +0 -55
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_events.py +3 -56
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_hooks.py +1 -76
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_init_entry_points.py +21 -52
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_migrate_cmd.py +32 -3
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_migrations.py +97 -8
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_peek_select.py +1 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_phase_agent_launch.py +4 -4
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_plugin_constants.py +18 -0
- cli_agent_runner-0.3.9/tests/unit/test_plugin_manifest.py +259 -0
- cli_agent_runner-0.3.9/tests/unit/test_plugins_config_fields.py +23 -0
- cli_agent_runner-0.3.8/tests/unit/test_default_dirty_handler.py → cli_agent_runner-0.3.9/tests/unit/test_resolve_dirty_tree.py +30 -35
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_runner.py +6 -223
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_vcs_state.py +0 -82
- cli_agent_runner-0.3.8/agent_runner/__init__.py +0 -224
- cli_agent_runner-0.3.8/agent_runner/_emit/plugins.py +0 -76
- cli_agent_runner-0.3.8/agent_runner/_plugin_checksum.py +0 -62
- cli_agent_runner-0.3.8/agent_runner/_plugin_manifest.py +0 -191
- cli_agent_runner-0.3.8/agent_runner/_plugin_sandbox.py +0 -615
- cli_agent_runner-0.3.8/agent_runner/_registry.py +0 -69
- cli_agent_runner-0.3.8/agent_runner/_sandbox_probe.py +0 -326
- cli_agent_runner-0.3.8/agent_runner/builtin_plugins/default_dirty_handler.py +0 -92
- cli_agent_runner-0.3.8/agent_runner/hooks.py +0 -463
- cli_agent_runner-0.3.8/docs/plugins.md +0 -731
- cli_agent_runner-0.3.8/tests/integration/test_context_enricher_namespacing.py +0 -57
- cli_agent_runner-0.3.8/tests/integration/test_dirty_handler_seam.py +0 -97
- cli_agent_runner-0.3.8/tests/integration/test_plugin_detector_loaded.py +0 -100
- cli_agent_runner-0.3.8/tests/integration/test_plugin_owned_paths.py +0 -320
- cli_agent_runner-0.3.8/tests/invariants/test_builtin_plugin_names_sync.py +0 -21
- cli_agent_runner-0.3.8/tests/invariants/test_event_kind_registry.py +0 -70
- cli_agent_runner-0.3.8/tests/invariants/test_kill_tests_not_skipped.py +0 -20
- cli_agent_runner-0.3.8/tests/invariants/test_plugins_config_stable.py +0 -40
- cli_agent_runner-0.3.8/tests/linux/test_plugin_sandbox_kill.py +0 -149
- cli_agent_runner-0.3.8/tests/unit/test_builtin_provenance.py +0 -31
- cli_agent_runner-0.3.8/tests/unit/test_collapse_spawn_decisions.py +0 -33
- cli_agent_runner-0.3.8/tests/unit/test_detector_protocol.py +0 -64
- cli_agent_runner-0.3.8/tests/unit/test_dirty_handlers.py +0 -60
- cli_agent_runner-0.3.8/tests/unit/test_dispatch_dirty_owner_branch.py +0 -261
- cli_agent_runner-0.3.8/tests/unit/test_doctor_sandbox.py +0 -75
- cli_agent_runner-0.3.8/tests/unit/test_example_dirty_handler.py +0 -49
- cli_agent_runner-0.3.8/tests/unit/test_gate_serve_boot.py +0 -90
- cli_agent_runner-0.3.8/tests/unit/test_hook_failure_isolation.py +0 -69
- cli_agent_runner-0.3.8/tests/unit/test_load_fail_closed.py +0 -165
- cli_agent_runner-0.3.8/tests/unit/test_manifest_spawn_hooks.py +0 -66
- cli_agent_runner-0.3.8/tests/unit/test_peek_sandbox_block.py +0 -154
- cli_agent_runner-0.3.8/tests/unit/test_plugin_checksum.py +0 -44
- cli_agent_runner-0.3.8/tests/unit/test_plugin_manifest.py +0 -177
- cli_agent_runner-0.3.8/tests/unit/test_plugin_name_mismatch.py +0 -71
- cli_agent_runner-0.3.8/tests/unit/test_plugin_sandbox_wire.py +0 -564
- cli_agent_runner-0.3.8/tests/unit/test_plugins_config_fields.py +0 -59
- cli_agent_runner-0.3.8/tests/unit/test_primary_prompt_file_home.py +0 -29
- cli_agent_runner-0.3.8/tests/unit/test_sandbox_probe.py +0 -74
- cli_agent_runner-0.3.8/tests/unit/test_serve_startup_hooks.py +0 -206
- cli_agent_runner-0.3.8/tests/unit/test_spawn_seam.py +0 -457
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/.codecov.yml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/.github/dependabot.yml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/.github/workflows/ci.yml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/.github/workflows/release.yml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/.gitignore +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/BACKLOG.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/CODE_OF_CONDUCT.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/CONTRIBUTING.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/LICENSE +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/README.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/README.zh.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/SECURITY.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_emit/agent.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_emit/memory.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_emit/rounds.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_emit/serve.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_install.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_lifecycle.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_monitor_detectors.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_monitor_state.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_plugin_scan.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_procwait.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_redact.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_resolve.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_round_outcome.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_round_support.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_serve_policy.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_substrate.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/_throttle.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/builtin_plugins/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/builtin_plugins/_constants.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/builtin_plugins/codewhale.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/builtin_plugins/kimi.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/__main__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/_serve_cgroup.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/events_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/init_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/install_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/migrate_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/round_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/service_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/cli/upgrade_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/clock.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/config/errors.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/config/loader.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/config/validators.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/context_store.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/defenses.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/event_log.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/host_health.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/http_progress.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/lifecycle.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/metrics.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/phase_select.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/presets/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/presets/aider.toml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/presets/claude.toml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/presets/codewhale.toml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/presets/gemini.toml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/presets/kimi.toml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/presets/pi.toml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/prompt_loader.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/remote_relay.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/round_log.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/round_view.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/scaffold.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/schedule.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/service_unit.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/agent_runner/startup_check.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/build.sh +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/deploy/Dockerfile +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/deploy/docker-compose.yml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/README.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/migrations/0.2.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/quickstart.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/recipes/aider.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/recipes/codewhale.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/recipes/container-pi.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/recipes/deploy-all-in-one.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/recipes/kimi.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/docs/recipes/pi.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/_clock.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/contract/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/e2e/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/e2e/conftest.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/e2e/test_e2e_graceful_stop.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/e2e/test_e2e_install_systemd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/e2e/test_e2e_monitor_remote.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/fixtures/cli-real-output/claude-2.1.143-assistant-tool-use.jsonl +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/fixtures/cli-real-output/claude-2.1.143-result-event.jsonl +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/fixtures/cli-real-output/gemini-0.42.0-result-event.jsonl +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/generate_vulture_whitelist.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_auto_stop_e2e.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_bounded_run.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_cooperative_grace_property.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_fresh_eyes_signal.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_grace_kill_emission.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_install_dry_run.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_monitor_seeded.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_plugin_real_flow.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_prompt_delivery_stdin.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_reap_property.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_scaffold_presets.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_serve_loop.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_spawn_round_mem_floor.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_spawn_round_wedged.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_substrate_fingerprint.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/integration/test_transient_error_backoff.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/_docs.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_atomic_write_enforced.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_authored_facts_declared.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_auto_stop_policy_ssot.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_bdd_test_conventions.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_brake_defaults_off.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_catalogs.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_classification_ssot.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_config_error_consistency.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_config_value_set_ssot.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_container_stop_bound.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_dataclass_fields_have_producers.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_doc_test_pointers_exist.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_doc_transient_error_claims.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_doc_usage_event_fields.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_docs_generated.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_docs_index_complete.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_entry_points_resolve.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_event_int_coercion.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_events_doc_contract.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_gen_blocks_name_their_source.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_migrations_registry_ssot.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_module_sizes.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_monitor_self_exclusion.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_no_ai_signatures.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_no_asyncio_select.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_no_direct_inspect_import.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_no_internal_refs.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_no_price_logic.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_no_raw_time.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_no_vacuous_invariants.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_no_version_refs.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_phase_select_stateless.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_procwait_shield.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_readme_zh_is_pointer.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_round_alloc_growth.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_round_log_decode_boundary.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_round_num_single_source.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_round_result_stable.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_set_diff_for_auto_tool_classification.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_throttle_stateless_imports.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_upstream_schema_canary.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/invariants/test_vulture_whitelist_generated.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/linux/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/linux/test_memory_high_nudge_e2e.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/literate/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/literate/parser.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/literate/test_parser.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/literate/test_quickstart.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_agent_runtime.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_agent_runtime_container.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_agent_runtime_grace.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_agent_runtime_marker_scan.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_agent_runtime_progress.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_agent_runtime_reap.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_agent_runtime_signal_name.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_api_assemble_prompt.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_api_events_stream.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_api_install.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_api_read_round_num.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_api_service.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_api_types.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_auto_stop_gating.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_catalogs.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_cgroup_probe.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_claude_error_detector.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_claude_foreign_schema.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_cli.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_cli_common.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_cli_init_install.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_cli_main_boundary.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_cli_monitor_http.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_cli_service_peek_monitor.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_cli_upgrade.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_codewhale_code_precedence.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_codewhale_plugin.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_coerce_int_poison.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_config_fresh_eyes.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_config_max_rounds.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_config_nested_strictness.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_config_prompt_delivery.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_config_schedule.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_config_stop_file.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_config_strictness.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_config_transient_error_action.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_context_store.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_control_char_config.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_defenses.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_detector_isolation.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_effective_throttle_view.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_emit_config_migrated.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_emit_schedule.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_emit_schedule_phase_skipped.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_event_log.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_events_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_events_cmd_rollover.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_events_cmd_tail.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_events_jsonl_decode.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_example_plugin.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_exec_prefix.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_exit_cause.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_exit_classifier.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_fresh_eyes_trigger.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_gemini_plugin.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_git_timeout.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_hook_redact_order.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_host_health.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_http_progress.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_http_progress_epoch.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_interruptible_sleep_should_stop.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_kill_pgroup_shielding.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_kimi_plugin.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_lifecycle.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_loader_split.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_metrics.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_metrics_cgroup_usage.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_metrics_swap.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_assembly.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_cmd_severity.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_dedup_identity.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_dedup_rearm.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_detectors.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_event_tail.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_loop.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_on_alert.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_remote.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_round_deferred_stale.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_monitor_schedule_pause.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_network_blip_signal_guard.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_notify.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_open_round_log.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_orphan_chain_single_pass.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_outer_round_ceiling.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_peek_argparse.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_peek_project_isolation.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_peek_schedule.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_phase_profiles.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_phase_select.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_pi_plugin.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_plugin_scan_parity.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_presets.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_procwait.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_prompt_loader.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_read_json_hardening.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_redact.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_resolve.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_round_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_round_cmd_sigterm.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_round_log_helpers.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_round_outcome.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_round_outcome_agent_axis.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_round_view.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_runner_sidecar_ordering.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_runner_throttle.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_scaffold.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_schedule.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_cmd_bounded.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_config_broken.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_crash_loop.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_mem_gate.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_phase_schedule.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_policy.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_round.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_round_cgroup_spine.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_round_log.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_round_oom_killed.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_round_terminate.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_round_wait_exit.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_schedule_gate.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_sentinel.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_serve_wakeup.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_service_mode.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_service_unit.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_startup_battery_exit.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_startup_check.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_substrate.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_supervisor_stale_self_blind.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_terminate_round_wait_exit.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_throttle_back_off_ladder.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_throttle_epoch_bound.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_throttle_plugin_classification.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.9}/tests/unit/test_upgrade_migrate.py +0 -0
|
@@ -15,15 +15,8 @@ buffer
|
|
|
15
15
|
|
|
16
16
|
# agent_runner._plugin_manifest.PluginManifest
|
|
17
17
|
name
|
|
18
|
-
pre_round_hooks
|
|
19
|
-
context_enrichers
|
|
20
18
|
post_round_hooks
|
|
21
|
-
|
|
22
|
-
dirty_handlers
|
|
23
|
-
spawn_hooks
|
|
24
|
-
detectors
|
|
25
|
-
event_kinds
|
|
26
|
-
sigterm_cooperative
|
|
19
|
+
cooperative_stop
|
|
27
20
|
|
|
28
21
|
# agent_runner._round_outcome.RoundOutcome
|
|
29
22
|
mem_terminated
|
|
@@ -36,12 +29,6 @@ usage_capable_by_agent
|
|
|
36
29
|
newest_usage_ts_by_agent
|
|
37
30
|
backoff_exponent_by_agent
|
|
38
31
|
|
|
39
|
-
# agent_runner._sandbox_probe.SandboxProbe
|
|
40
|
-
achieved_tier
|
|
41
|
-
landlock_abi
|
|
42
|
-
seccomp
|
|
43
|
-
unconfined_reason
|
|
44
|
-
|
|
45
32
|
# agent_runner.agent_runtime.RunResult
|
|
46
33
|
exit_code
|
|
47
34
|
duration_s
|
|
@@ -131,11 +118,6 @@ unit_file
|
|
|
131
118
|
rate_limit
|
|
132
119
|
system_managed
|
|
133
120
|
|
|
134
|
-
# agent_runner.api_types.SpawnDecision
|
|
135
|
-
action
|
|
136
|
-
defer_s
|
|
137
|
-
reason
|
|
138
|
-
|
|
139
121
|
# agent_runner.api_types.SystemMetrics
|
|
140
122
|
mem_total_mb
|
|
141
123
|
mem_available_mb
|
|
@@ -156,7 +138,6 @@ phase
|
|
|
156
138
|
checks
|
|
157
139
|
overlaps
|
|
158
140
|
plan
|
|
159
|
-
sandbox
|
|
160
141
|
cgroup
|
|
161
142
|
sigterm_grace
|
|
162
143
|
|
|
@@ -199,7 +180,6 @@ brake
|
|
|
199
180
|
|
|
200
181
|
# agent_runner.config.models.PhaseOverride
|
|
201
182
|
round_budget_s
|
|
202
|
-
disable_pre_round_hooks
|
|
203
183
|
prompt_files
|
|
204
184
|
agent
|
|
205
185
|
schedule
|
|
@@ -211,10 +191,6 @@ phase_policy
|
|
|
211
191
|
|
|
212
192
|
# agent_runner.config.models.PluginsConfig
|
|
213
193
|
disable
|
|
214
|
-
spawn_override_allow
|
|
215
|
-
sandbox
|
|
216
|
-
pin
|
|
217
|
-
raw
|
|
218
194
|
|
|
219
195
|
# agent_runner.config.models.Profile
|
|
220
196
|
agent
|
|
@@ -235,7 +211,6 @@ work_dir
|
|
|
235
211
|
log_dir
|
|
236
212
|
round_budget_s
|
|
237
213
|
restart_delay_s
|
|
238
|
-
disable_pre_round_hooks
|
|
239
214
|
round_log_retention
|
|
240
215
|
narrative_file
|
|
241
216
|
transient_error_action
|
|
@@ -316,10 +291,6 @@ anomaly_repetitive_window
|
|
|
316
291
|
anomaly_repetitive_threshold
|
|
317
292
|
vcs
|
|
318
293
|
|
|
319
|
-
# agent_runner.hooks.SpawnView
|
|
320
|
-
argv
|
|
321
|
-
env
|
|
322
|
-
|
|
323
294
|
# agent_runner.hooks.VcsHookView
|
|
324
295
|
dirty_action
|
|
325
296
|
stash_idempotency_s
|
|
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.9] - 2026-09-17
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- `PluginManifest` narrows to 3 fields (`name`, `post_round_hooks`, `cooperative_stop`) — dropped 6 zero-producer hook seams, the `DirtyHandler` seam (dirty-tree resolution is now plain core), the sandbox subsystem, and the owned-paths registry, all with zero real producers.
|
|
12
|
+
- `sigterm_cooperative: bool` is now typed `cooperative_stop: Literal["SIGTERM", "SIGINT"] | None`: `claude` now drains on SIGINT (SIGTERM abandoned its turn), `gemini`/`pi` stay on SIGTERM; the hard-wall kill path stays SIGTERM-first regardless.
|
|
13
|
+
- The `claude` builtin plugin's manifest/entry-point name changed from `claude_rate_limit` to `claude` (`agent-runner migrate` flags any `[plugins] disable` entry still naming the old value).
|
|
14
|
+
- `peek --json` schema bumps to `2.6`.
|
|
15
|
+
|
|
16
|
+
### Removed
|
|
17
|
+
- `[plugins]` `sandbox`/`pin`/`spawn_override_allow`/`raw` config keys and `[runtime]`/`[phases.<name>]` `disable_pre_round_hooks` — run `agent-runner migrate` before upgrading.
|
|
18
|
+
|
|
8
19
|
## [0.3.8] - 2026-09-16
|
|
9
20
|
|
|
10
21
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cli-agent-runner
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.9
|
|
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
|
|
@@ -34,9 +34,6 @@ Requires-Dist: ruff<0.17,>=0.16; extra == 'dev'
|
|
|
34
34
|
Requires-Dist: vulture>=2.13; extra == 'dev'
|
|
35
35
|
Provides-Extra: e2e
|
|
36
36
|
Requires-Dist: pytest>=8; extra == 'e2e'
|
|
37
|
-
Provides-Extra: sandbox
|
|
38
|
-
Requires-Dist: py-landlock==0.1.1; (sys_platform == 'linux') and extra == 'sandbox'
|
|
39
|
-
Requires-Dist: pyseccomp==0.1.2; (sys_platform == 'linux') and extra == 'sandbox'
|
|
40
37
|
Description-Content-Type: text/markdown
|
|
41
38
|
|
|
42
39
|
> **[中文](README.zh.md)** · English
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Agent Runner — restart-on-exit supervisor for autonomous CLI agents."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from agent_runner._version import __version__
|
|
7
|
+
except ImportError: # editable install before hatch-vcs has generated _version.py
|
|
8
|
+
__version__ = "0.0.0+unknown"
|
|
9
|
+
|
|
10
|
+
_PLUGIN_GROUP = "agent_runner.plugins"
|
|
11
|
+
|
|
12
|
+
# Tracks the names passed to the most recent ``apply_plugin_disable`` call.
|
|
13
|
+
# Surfaced via peek --json `plugins.disabled` for operator visibility.
|
|
14
|
+
_DISABLED_PLUGIN_NAMES: list[str] = []
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
_DISCOVERED_PLUGIN_ENTRIES: list[tuple[str, str]] = [] # (name, "module:attr"), populated once
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _discover_plugin_manifests() -> None:
|
|
21
|
+
"""Package-import-time step. Scans entry_points.txt ONLY (scan_entry_points) —
|
|
22
|
+
imports nothing, executes no plugin code.
|
|
23
|
+
|
|
24
|
+
Discovery goes through the ``entry_points.txt`` scanner (cheaper than a
|
|
25
|
+
fresh ``importlib.metadata.entry_points()`` scan per process; see
|
|
26
|
+
``_plugin_scan``), with a hard fallback to ``importlib.metadata`` on any
|
|
27
|
+
parse failure.
|
|
28
|
+
"""
|
|
29
|
+
import sys
|
|
30
|
+
|
|
31
|
+
from agent_runner._plugin_scan import scan_entry_points
|
|
32
|
+
|
|
33
|
+
global _DISCOVERED_PLUGIN_ENTRIES
|
|
34
|
+
_DISCOVERED_PLUGIN_ENTRIES = scan_entry_points(sys.path, _PLUGIN_GROUP)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
_discover_plugin_manifests()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def load_and_register_plugins(plugins_cfg, log_dir=None) -> None:
|
|
41
|
+
"""Called from config.loader.load_config once [plugins] is parsed. For
|
|
42
|
+
each discovered (name, value) NOT in plugins_cfg.disable and not already
|
|
43
|
+
registered, resolve module_path/attr_path, import + register_manifest. A
|
|
44
|
+
disabled name is never imported. A per-plugin failure isolates as a
|
|
45
|
+
UserWarning and skips that ONE plugin; it never aborts the whole load.
|
|
46
|
+
|
|
47
|
+
``name`` here is always the entry-point name (the ``agent_runner.plugins``
|
|
48
|
+
pyproject.toml key) — it's the identifier known BEFORE import. It is NOT
|
|
49
|
+
necessarily ``PluginManifest.name`` (only known after import); for every
|
|
50
|
+
builtin the two are pinned equal by
|
|
51
|
+
``test_entry_point_names_should_match_manifest_name``, but that is not
|
|
52
|
+
enforced here.
|
|
53
|
+
|
|
54
|
+
``log_dir`` is accepted for call-site stability (config.loader threads it
|
|
55
|
+
through unconditionally) though this function no longer emits anything
|
|
56
|
+
itself.
|
|
57
|
+
"""
|
|
58
|
+
import warnings
|
|
59
|
+
|
|
60
|
+
from agent_runner._plugin_manifest import loaded_manifest_names, register_manifest
|
|
61
|
+
from agent_runner._registry import resolve_entry_target
|
|
62
|
+
|
|
63
|
+
disable = set(plugins_cfg.disable)
|
|
64
|
+
already = set(loaded_manifest_names())
|
|
65
|
+
for name, value in _DISCOVERED_PLUGIN_ENTRIES:
|
|
66
|
+
if name in disable or name in already:
|
|
67
|
+
continue
|
|
68
|
+
# An entry-point value may carry a trailing extras marker
|
|
69
|
+
# (``module:attr [extra1,extra2]``); importlib.metadata.EntryPoint's
|
|
70
|
+
# own module/attr grammar excludes "[", so anything from the first
|
|
71
|
+
# "[" onward is always extras, never part of the path — strip it
|
|
72
|
+
# before resolving, or it glues onto attr_path and breaks getattr().
|
|
73
|
+
stripped = value.partition("[")[0].rstrip()
|
|
74
|
+
module_path, _, attr_path = stripped.partition(":")
|
|
75
|
+
try:
|
|
76
|
+
target = resolve_entry_target(module_path, attr_path)
|
|
77
|
+
register_manifest(target)
|
|
78
|
+
except Exception as e: # noqa: BLE001 — a broken plugin must never crash the supervisor
|
|
79
|
+
warnings.warn(f"failed to load {_PLUGIN_GROUP} plugin {name!r}: {e}", stacklevel=3)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def apply_plugin_disable(names: list[str]) -> None:
|
|
83
|
+
"""Remove plugins matching ``names`` (manifest names) from all in-memory
|
|
84
|
+
registries.
|
|
85
|
+
|
|
86
|
+
Called after config-load to honor ``[plugins] disable``. Idempotent for
|
|
87
|
+
already-removed names. Emits a UserWarning for names that match no
|
|
88
|
+
loaded manifest (typo catcher; tolerates cross-env config drift).
|
|
89
|
+
|
|
90
|
+
A disabled name is never imported by ``load_and_register_plugins`` in the
|
|
91
|
+
first place, so this is belt-and-suspenders cleanup for anything that
|
|
92
|
+
reached the registries another way (e.g. a manifest registered directly
|
|
93
|
+
via ``register_manifest``, not through entry-point discovery).
|
|
94
|
+
"""
|
|
95
|
+
import warnings
|
|
96
|
+
|
|
97
|
+
from agent_runner._plugin_manifest import unregister_by_name
|
|
98
|
+
|
|
99
|
+
if not names:
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
global _DISABLED_PLUGIN_NAMES
|
|
103
|
+
_DISABLED_PLUGIN_NAMES = list(names)
|
|
104
|
+
|
|
105
|
+
found = unregister_by_name(set(names))
|
|
106
|
+
unknown = set(names) - found
|
|
107
|
+
if unknown:
|
|
108
|
+
warnings.warn(
|
|
109
|
+
f"[plugins] disable references unknown plugin name(s): {sorted(unknown)}. "
|
|
110
|
+
f"(Names matched no loaded PluginManifest; check spelling or installed packages.)",
|
|
111
|
+
stacklevel=2,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def disabled_plugin_names() -> list[str]:
|
|
116
|
+
"""Names passed to the most recent ``apply_plugin_disable`` call.
|
|
117
|
+
|
|
118
|
+
Used by peek --json to surface ``plugins.disabled`` for operator visibility.
|
|
119
|
+
Returns the LIST (not set) preserving configured order.
|
|
120
|
+
"""
|
|
121
|
+
return list(_DISABLED_PLUGIN_NAMES)
|
|
@@ -48,9 +48,8 @@ from agent_runner.monitor import AUTO_STOP_ALERTS, KNOWN_ALERT_KINDS
|
|
|
48
48
|
# (toml_section_name, dataclass, nested_sub_sections). Nesting is an EXPLICIT
|
|
49
49
|
# registry, not reflection, on two counts:
|
|
50
50
|
# - field name != TOML key: PhaseOverride.prompt_files is written as
|
|
51
|
-
# [phases.<name>.prompt] files=[...]
|
|
52
|
-
#
|
|
53
|
-
# not exist.
|
|
51
|
+
# [phases.<name>.prompt] files=[...]. Recursing generically would document
|
|
52
|
+
# TOML keys that do not exist.
|
|
54
53
|
# - typing.get_type_hints() raises TypeError on PhasesConfig — its `list`
|
|
55
54
|
# field shadows the builtin, so `list[str] | None` resolves against
|
|
56
55
|
# None. Do not reach for it here.
|
|
@@ -42,14 +42,6 @@ from agent_runner._emit.memory import ( # noqa: F401 — public re-export
|
|
|
42
42
|
emit_round_oom_killed,
|
|
43
43
|
emit_round_resumed,
|
|
44
44
|
)
|
|
45
|
-
from agent_runner._emit.plugins import ( # noqa: F401 — public re-export
|
|
46
|
-
emit_plugin_builtin_name_squat,
|
|
47
|
-
emit_plugin_checksum_mismatch,
|
|
48
|
-
emit_plugin_sandbox_degraded,
|
|
49
|
-
emit_plugin_sandbox_kill,
|
|
50
|
-
emit_plugin_spawn_decision,
|
|
51
|
-
emit_plugin_spawn_override_ignored,
|
|
52
|
-
)
|
|
53
45
|
from agent_runner._emit.rounds import ( # noqa: F401 — public re-export
|
|
54
46
|
emit_fresh_eyes_round_triggered,
|
|
55
47
|
emit_round_container_orphan_risk,
|
|
@@ -94,12 +86,6 @@ __all__ = [
|
|
|
94
86
|
"emit_memory_high_engaged",
|
|
95
87
|
"emit_memory_high_released",
|
|
96
88
|
"emit_memory_high_write_failed",
|
|
97
|
-
"emit_plugin_builtin_name_squat",
|
|
98
|
-
"emit_plugin_checksum_mismatch",
|
|
99
|
-
"emit_plugin_sandbox_degraded",
|
|
100
|
-
"emit_plugin_sandbox_kill",
|
|
101
|
-
"emit_plugin_spawn_decision",
|
|
102
|
-
"emit_plugin_spawn_override_ignored",
|
|
103
89
|
"emit_rate_limit_stop",
|
|
104
90
|
"emit_round_cgroup_memory",
|
|
105
91
|
"emit_round_container_orphan_risk",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Alert-kind catalog
|
|
1
|
+
"""Alert-kind catalog.
|
|
2
2
|
|
|
3
3
|
Pure bookkeeping: no filesystem I/O, no clock reads. Detector *logic* lives in
|
|
4
4
|
``_monitor_detectors``; state assembly in ``_monitor_state``; the cycle-edge
|
|
@@ -7,8 +7,7 @@ wiring (``run_all_detectors``/``on_alert``) in ``monitor.py``.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
from agent_runner.
|
|
11
|
-
from agent_runner.api_types import Alert, Detector
|
|
10
|
+
from agent_runner.api_types import Alert
|
|
12
11
|
from agent_runner.config import _DEFAULT_AUTO_STOP_ON
|
|
13
12
|
from agent_runner.events import (
|
|
14
13
|
DETECTOR_ERROR,
|
|
@@ -82,17 +81,3 @@ def alert_identity(alert: Alert) -> str:
|
|
|
82
81
|
return alert.detector
|
|
83
82
|
parts = "|".join(f"{k}={alert.context.get(k)!r}" for k in fields)
|
|
84
83
|
return f"{alert.detector}:{parts}"
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
_PLUGIN_DETECTORS: list[Detector] = []
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def register_detector(detector: Detector) -> None:
|
|
91
|
-
"""Register a plugin detector. Rejects duplicate names."""
|
|
92
|
-
ensure_unique(detector.name, _PLUGIN_DETECTORS, "detector")
|
|
93
|
-
_PLUGIN_DETECTORS.append(detector)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def plugin_detectors() -> list[str]:
|
|
97
|
-
"""Sorted list of registered plugin detector names (for peek --json)."""
|
|
98
|
-
return sorted(d.name for d in _PLUGIN_DETECTORS)
|
|
@@ -78,14 +78,9 @@ def drain(fd: int) -> None:
|
|
|
78
78
|
``select()`` on it instantly ready -- an undrained fd busy-spins its
|
|
79
79
|
caller at 100% CPU forever after the first wake, since a level-triggered
|
|
80
80
|
fd stays readable until read. Advisory: the bytes carry no payload and are
|
|
81
|
-
discarded.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
(``_plugin_sandbox._run_child_process``, P3) drains the CALLER-OWNED wake fd
|
|
85
|
-
it was handed on the ``"woken"`` branch of its exit wait, before re-checking
|
|
86
|
-
``should_stop()`` -- that fd is the serve listener's, watched by
|
|
87
|
-
``wait_exit`` for the duration of the hook, so the trampoline must drain it
|
|
88
|
-
itself rather than leave the next wait instantly ready.
|
|
81
|
+
discarded. :meth:`Listener.wait` self-drains its own fd on the wake branch
|
|
82
|
+
(the pause/sleep waits ``_interruptible_sleep``/``_pause_poll`` are the
|
|
83
|
+
consumers of a live ``Listener``).
|
|
89
84
|
"""
|
|
90
85
|
while True:
|
|
91
86
|
try:
|
|
@@ -155,7 +155,7 @@ def _poll_once(
|
|
|
155
155
|
events = monitor.parse_events_from_jsonl_files(src.events_files())
|
|
156
156
|
metrics = monitor.parse_events_from_jsonl_files(src.metrics_files())
|
|
157
157
|
log_tails = monitor.load_round_log_tails(src.rounds_dir())
|
|
158
|
-
|
|
158
|
+
return monitor.run_all_detectors(
|
|
159
159
|
events=events,
|
|
160
160
|
metrics=metrics,
|
|
161
161
|
log_tails=log_tails,
|
|
@@ -167,11 +167,6 @@ def _poll_once(
|
|
|
167
167
|
host_health_cfg=cfg.monitor.host_health,
|
|
168
168
|
log_dir=cfg.runtime.log_dir,
|
|
169
169
|
)
|
|
170
|
-
if not monitor._PLUGIN_DETECTORS:
|
|
171
|
-
return builtin # skip ProjectState assembly when no plugins to feed
|
|
172
|
-
state = monitor.assemble_project_state(src, project=_project_name(work_dir))
|
|
173
|
-
plugin = monitor.run_plugin_detectors(state)
|
|
174
|
-
return builtin + plugin
|
|
175
170
|
|
|
176
171
|
|
|
177
172
|
def monitor_loop(
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""The plugin ABI: one typed, declared capability manifest per plugin, read
|
|
2
|
+
by the loader (agent_runner/__init__.py::load_and_register_plugins) instead
|
|
3
|
+
of relying on import-time register_*() side effects. One entry-point group,
|
|
4
|
+
`agent_runner.plugins`, each entry resolving to a module-level
|
|
5
|
+
`PLUGIN = PluginManifest(...)`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import signal
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Literal
|
|
13
|
+
|
|
14
|
+
from agent_runner._registry import ensure_unique
|
|
15
|
+
from agent_runner.hooks import PostRoundHook
|
|
16
|
+
|
|
17
|
+
# THE single, pinned string->signal.Signals table for cooperative_stop. Nothing
|
|
18
|
+
# else in the codebase may turn a cooperative-stop string into a signal (no
|
|
19
|
+
# `getattr(signal, arbitrary_string)`, which could reach SIGKILL=zero-grace or
|
|
20
|
+
# SIGSTOP=freeze). Keys ARE the full set of legal cooperative_stop values, so
|
|
21
|
+
# __post_init__ validates against these same keys.
|
|
22
|
+
_COOPERATIVE_SIGNALS: dict[str, signal.Signals] = {
|
|
23
|
+
"SIGTERM": signal.SIGTERM,
|
|
24
|
+
"SIGINT": signal.SIGINT,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class PluginManifest:
|
|
30
|
+
"""A plugin's complete, declared capability set. `name` is the plugin's
|
|
31
|
+
own identity — `[plugins] disable` keys on THIS, not on any individual
|
|
32
|
+
hook's own `.name`."""
|
|
33
|
+
|
|
34
|
+
name: str
|
|
35
|
+
post_round_hooks: tuple[PostRoundHook, ...] = ()
|
|
36
|
+
cooperative_stop: Literal["SIGTERM", "SIGINT"] | None = None
|
|
37
|
+
"""How this preset's CLI cooperatively drains/cleans up when the supervisor
|
|
38
|
+
stops the round: the signal it drains on (``"SIGTERM"`` = gemini/pi,
|
|
39
|
+
``"SIGINT"`` = claude/codex), or ``None`` = no cooperative drain (the hard
|
|
40
|
+
path). Source-verified per preset, NEVER assumed from a CLI's general
|
|
41
|
+
reputation. Resolved to a ``signal.Signals`` ONLY by
|
|
42
|
+
``resolve_cooperative_signal``; a ``None`` cooperative_stop maps to
|
|
43
|
+
SIGTERM-first at the reap site (today's behavior), never "skip the first
|
|
44
|
+
signal"."""
|
|
45
|
+
|
|
46
|
+
def __post_init__(self) -> None:
|
|
47
|
+
# Errors-unlikely-by-construction: an unknown signal name (or a
|
|
48
|
+
# freeze/zero-grace one like SIGSTOP/SIGKILL) is unrepresentable -- it
|
|
49
|
+
# raises at construction, not at kill time.
|
|
50
|
+
if self.cooperative_stop is not None and self.cooperative_stop not in _COOPERATIVE_SIGNALS:
|
|
51
|
+
raise ValueError(
|
|
52
|
+
"cooperative_stop must be one of {None, 'SIGTERM', 'SIGINT'}, "
|
|
53
|
+
f"got {self.cooperative_stop!r}"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
_LOADED_MANIFESTS: list[PluginManifest] = []
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def loaded_manifest_names() -> list[str]:
|
|
61
|
+
"""Names of every currently-registered manifest (order-preserving).
|
|
62
|
+
Mirrors the ``list(_LOADED_MANIFESTS)`` pattern; used by the loader for
|
|
63
|
+
idempotent re-load skipping."""
|
|
64
|
+
return [m.name for m in _LOADED_MANIFESTS]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _cooperative_stop_for_name(agent_binary: str | None) -> str | None:
|
|
68
|
+
"""The ``cooperative_stop`` field of the registered manifest named
|
|
69
|
+
`agent_binary`, or None when no such manifest is registered (or it declares
|
|
70
|
+
no cooperative stop). The single registry lookup the resolvers below share."""
|
|
71
|
+
for m in _LOADED_MANIFESTS:
|
|
72
|
+
if m.name == agent_binary:
|
|
73
|
+
return m.cooperative_stop
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def cooperative_stop_by_name() -> dict[str, str]:
|
|
78
|
+
"""`{manifest name: cooperative_stop signal name}` for every registered
|
|
79
|
+
manifest declaring a cooperative_stop (order-preserving). Mirrors
|
|
80
|
+
loaded_manifest_names; used by peek/doctor -- an operator sees not just
|
|
81
|
+
WHICH presets cooperate but the SIGNAL each drains on."""
|
|
82
|
+
return {m.name: m.cooperative_stop for m in _LOADED_MANIFESTS if m.cooperative_stop is not None}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def is_cooperative_agent(agent_binary: str | None) -> bool:
|
|
86
|
+
"""Whether the agent's preset declares a cooperative_stop. Routes through
|
|
87
|
+
the same single-scan `resolve_cooperative_signal` uses: `cooperative_stop`
|
|
88
|
+
is always in {None, "SIGTERM", "SIGINT"} (`__post_init__`-guaranteed) and
|
|
89
|
+
`_COOPERATIVE_SIGNALS` keys are exactly {"SIGTERM", "SIGINT"}, so a
|
|
90
|
+
resolved signal exists iff the named manifest declares a cooperative_stop."""
|
|
91
|
+
return resolve_cooperative_signal(agent_binary) is not None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def resolve_cooperative_signal(agent_binary: str | None) -> signal.Signals | None:
|
|
95
|
+
"""The signal the agent named `agent_binary` cooperatively drains on, or
|
|
96
|
+
None when its preset declares no cooperative_stop. THE ONLY place a
|
|
97
|
+
cooperative_stop string becomes a ``signal.Signals`` (via the pinned
|
|
98
|
+
``_COOPERATIVE_SIGNALS`` table) -- sibling to resolve_sigterm_grace_s, and
|
|
99
|
+
the SIGNAL half of the same anti-skew single-source: serve resolves it once
|
|
100
|
+
per phase from its own registry and publishes the result via env, so the
|
|
101
|
+
round child never re-derives (and skews) it."""
|
|
102
|
+
return _COOPERATIVE_SIGNALS.get(_cooperative_stop_for_name(agent_binary))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def cooperative_signal_from_name(name: str | None) -> signal.Signals | None:
|
|
106
|
+
"""Map a published cooperative-stop signal NAME (e.g. from
|
|
107
|
+
AGENT_RUNNER_COOPERATIVE_STOP_SIGNAL) back to a ``signal.Signals`` through
|
|
108
|
+
the SAME pinned ``_COOPERATIVE_SIGNALS`` table. An unknown/absent name -> None
|
|
109
|
+
(the reap site then defaults to SIGTERM-first, today's behavior). Never
|
|
110
|
+
getattr(signal, ...): a leaked/hostile "SIGKILL"/"SIGSTOP" is simply not a
|
|
111
|
+
table key and falls back safely."""
|
|
112
|
+
return _COOPERATIVE_SIGNALS.get(name) if name is not None else None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def resolve_sigterm_grace_s(agent_binary: str | None, sigterm_grace_s: int) -> int:
|
|
116
|
+
"""The SIGTERM->SIGKILL grace this round's agent actually gets:
|
|
117
|
+
`sigterm_grace_s` (config's [agent] sigterm_grace_s, boot-capped at
|
|
118
|
+
_ROUND_TERM_GRACE_S) when `agent_binary` names a manifest declaring a
|
|
119
|
+
cooperative_stop, else agent_runtime.REAP_GRACE_S (5s) unchanged. One
|
|
120
|
+
function, two callers: cli.serve_cmd._apply_reap_grace_env (what the agent
|
|
121
|
+
actually gets) and doctor/peek (what an operator sees)."""
|
|
122
|
+
from agent_runner.agent_runtime import REAP_GRACE_S
|
|
123
|
+
|
|
124
|
+
if is_cooperative_agent(agent_binary):
|
|
125
|
+
return sigterm_grace_s
|
|
126
|
+
return REAP_GRACE_S
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def register_manifest(manifest: PluginManifest) -> None:
|
|
130
|
+
"""Register every capability a manifest declares into its own registry.
|
|
131
|
+
No import-time side effects — the loader calls this explicitly after
|
|
132
|
+
resolving a plugin's `PLUGIN` attribute.
|
|
133
|
+
|
|
134
|
+
Raises ``ValueError`` up front if ``manifest.name`` collides with an
|
|
135
|
+
already-registered manifest's ``.name`` — this is checked HERE, not by
|
|
136
|
+
the underlying registries' own ``ensure_unique`` calls below, which only
|
|
137
|
+
catch two manifests sharing one of THEIR sub-capabilities' ``.name``
|
|
138
|
+
(e.g. two different plugins each declaring a post_round_hook named
|
|
139
|
+
``"foo"``); they have no visibility into the manifest's own top-level name, so two
|
|
140
|
+
manifests named e.g. ``"codewhale"`` with disjoint capability names would
|
|
141
|
+
otherwise both register, and ``unregister_by_name``/``[plugins] disable``
|
|
142
|
+
would then strip both when the operator meant to disable one.
|
|
143
|
+
|
|
144
|
+
Appends to ``_LOADED_MANIFESTS`` only after every sub-registration
|
|
145
|
+
succeeds — a duplicate CAPABILITY-name registration (e.g. a stray
|
|
146
|
+
double-scan) raises via the underlying registries' own ``ensure_unique``
|
|
147
|
+
checks before the manifest is recorded, so a failed call leaves no
|
|
148
|
+
partial trace for ``unregister_by_name`` to later find and no-op
|
|
149
|
+
against. This guarantee covers ``_LOADED_MANIFESTS`` only: a failure
|
|
150
|
+
partway through the loop below (e.g. the 3rd of 5 declared
|
|
151
|
+
post_round_hooks raises) still leaves the first 2 registered in their
|
|
152
|
+
own per-family registry — matching the prior import-time register_*()
|
|
153
|
+
semantics, where a mid-module exception left every earlier top-level
|
|
154
|
+
call's side effect in place too.
|
|
155
|
+
"""
|
|
156
|
+
ensure_unique(manifest.name, _LOADED_MANIFESTS, "plugin manifest")
|
|
157
|
+
|
|
158
|
+
from agent_runner import hooks
|
|
159
|
+
|
|
160
|
+
for h in manifest.post_round_hooks:
|
|
161
|
+
hooks.register_post_round_hook(h)
|
|
162
|
+
_LOADED_MANIFESTS.append(manifest)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _remove_by_identity(registry: list, items: tuple) -> None:
|
|
166
|
+
registry[:] = [x for x in registry if x not in items]
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def unregister_by_name(names: set[str]) -> set[str]:
|
|
170
|
+
"""Remove every capability declared by manifests whose `.name` is in
|
|
171
|
+
`names`. Returns the subset of `names` actually matched (for the
|
|
172
|
+
caller's unknown-name warning)."""
|
|
173
|
+
from agent_runner import hooks
|
|
174
|
+
|
|
175
|
+
found: set[str] = set()
|
|
176
|
+
for manifest in list(_LOADED_MANIFESTS):
|
|
177
|
+
if manifest.name not in names:
|
|
178
|
+
continue
|
|
179
|
+
found.add(manifest.name)
|
|
180
|
+
_remove_by_identity(hooks._POST_ROUND_HOOKS, manifest.post_round_hooks)
|
|
181
|
+
return found
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Shared registry helpers for plugin-extension surfaces.
|
|
2
|
+
|
|
3
|
+
The `name`-keyed unique-registration check (:func:`ensure_unique`) is shared
|
|
4
|
+
across every hook family in :mod:`agent_runner.hooks` (post_round_hooks) and
|
|
5
|
+
by :mod:`agent_runner._plugin_manifest`'s own manifest-name check. This
|
|
6
|
+
module is its single source of truth.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def resolve_entry_target(module_path: str, attr_path: str) -> Any:
|
|
15
|
+
"""Import ``module_path`` and walk ``attr_path`` (dot-separated, empty
|
|
16
|
+
segments ignored) via ``getattr``, returning the final target — the
|
|
17
|
+
module itself when ``attr_path`` is empty.
|
|
18
|
+
|
|
19
|
+
The single resolution recipe for an entry-point's ``module:attr`` value,
|
|
20
|
+
used by the plugin loader (``agent_runner.load_and_register_plugins``).
|
|
21
|
+
"""
|
|
22
|
+
import importlib
|
|
23
|
+
|
|
24
|
+
target: Any = importlib.import_module(module_path)
|
|
25
|
+
for attr in filter(None, attr_path.split(".")):
|
|
26
|
+
target = getattr(target, attr)
|
|
27
|
+
return target
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def ensure_unique(name: str, existing: list, kind: str) -> None:
|
|
31
|
+
"""Raise ValueError if any item in ``existing`` already has ``.name == name``.
|
|
32
|
+
|
|
33
|
+
``kind`` is a short label embedded in the error message (e.g. ``"post_round_hook"``,
|
|
34
|
+
``"plugin manifest"``).
|
|
35
|
+
"""
|
|
36
|
+
for item in existing:
|
|
37
|
+
if getattr(item, "name", None) == name:
|
|
38
|
+
raise ValueError(f"{kind} {name!r} already registered; refusing to add a second")
|
|
@@ -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.9'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 3, 9)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|