cli-agent-runner 0.3.8__tar.gz → 0.3.10__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.10}/.vulture-whitelist.py +2 -30
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/CHANGELOG.md +20 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/PKG-INFO +1 -4
- cli_agent_runner-0.3.10/agent_runner/__init__.py +121 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_docgen.py +2 -3
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_emit/__init__.py +2 -14
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_emit/serve.py +12 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_monitor_registry.py +2 -17
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_notify.py +3 -8
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_observe.py +1 -6
- cli_agent_runner-0.3.10/agent_runner/_plugin_manifest.py +207 -0
- cli_agent_runner-0.3.10/agent_runner/_registry.py +38 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_version.py +2 -2
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/agent_runtime.py +51 -9
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/api.py +1 -6
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/api_types.py +3 -36
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/builtin_plugins/claude_rate_limit.py +16 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/builtin_plugins/gemini.py +1 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/builtin_plugins/pi.py +6 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/_serve_round.py +4 -189
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/common.py +10 -14
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/doctor_cmd.py +9 -25
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/monitor_cmd.py +2 -2
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/peek_cmd.py +2 -2
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/serve_cmd.py +99 -67
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/config/__init__.py +1 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/config/loader.py +26 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/config/models.py +22 -23
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/config/parsers.py +13 -81
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/events.py +8 -47
- cli_agent_runner-0.3.10/agent_runner/hooks.py +139 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/migrations.py +212 -100
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/monitor.py +11 -43
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/runner.py +48 -117
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/vcs_state.py +133 -194
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/architecture.md +13 -61
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/commands.md +12 -4
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/configuration.md +17 -26
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/events.md +3 -3
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/long-running-agents.md +3 -3
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/memory-efficiency.md +142 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/migrations/0.3.md +42 -5
- cli_agent_runner-0.3.10/docs/plugins.md +375 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/runbook.md +5 -43
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/thesis.md +9 -9
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/pyproject.toml +9 -13
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/_test_helpers.py +6 -86
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/conftest.py +2 -17
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/contract/test_public_api_surface.py +1 -50
- cli_agent_runner-0.3.10/tests/e2e/test_e2e_pi_resume.py +344 -0
- cli_agent_runner-0.3.10/tests/integration/test_cooperative_stop_signal.py +126 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_run_one_round_with_fake_agent.py +27 -7
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/_event_scan.py +1 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_architecture.py +7 -10
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_doc_builtin_plugins.py +1 -2
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_doc_claims_match_ssot.py +5 -6
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_doc_cli_claims.py +0 -16
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_doc_operator_surface.py +1 -36
- cli_agent_runner-0.3.10/tests/invariants/test_event_kind_registry.py +33 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_event_kinds_ssot.py +1 -1
- cli_agent_runner-0.3.10/tests/invariants/test_hard_wall_signal.py +126 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_hook_contract_docs.py +2 -2
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_import_footprint.py +2 -11
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_layer_2_loop_size.py +1 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_module_boundaries.py +0 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_peek_schema_version.py +3 -20
- cli_agent_runner-0.3.10/tests/invariants/test_plugins_config_stable.py +38 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_timeout_budget_invariant.py +1 -12
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_api_observation.py +8 -9
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_api_resolve_phase.py +1 -10
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_apply_reap_grace_env.py +3 -3
- cli_agent_runner-0.3.10/tests/unit/test_apply_resume_env.py +255 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_config.py +23 -44
- cli_agent_runner-0.3.10/tests/unit/test_config_resume_guard.py +44 -0
- cli_agent_runner-0.3.10/tests/unit/test_cooperative_signal_env.py +101 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_detect_hung_latch.py +9 -11
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_docgen.py +0 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_doctor_cmd.py +0 -55
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_events.py +3 -56
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_hooks.py +1 -76
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_init_entry_points.py +21 -52
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_migrate_cmd.py +32 -3
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_migrations.py +97 -8
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_peek_select.py +1 -1
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_phase_agent_launch.py +4 -4
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_plugin_constants.py +18 -0
- cli_agent_runner-0.3.10/tests/unit/test_plugin_manifest.py +288 -0
- cli_agent_runner-0.3.10/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.10/tests/unit/test_resolve_dirty_tree.py +30 -35
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_runner.py +6 -223
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/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.10}/.codecov.yml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/.github/dependabot.yml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/.github/workflows/ci.yml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/.github/workflows/release.yml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/.gitignore +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/BACKLOG.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/CODE_OF_CONDUCT.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/CONTRIBUTING.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/LICENSE +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/README.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/README.zh.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/SECURITY.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_emit/agent.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_emit/memory.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_emit/rounds.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_install.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_lifecycle.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_monitor_detectors.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_monitor_state.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_plugin_scan.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_procwait.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_redact.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_resolve.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_round_outcome.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_round_support.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_serve_policy.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_substrate.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/_throttle.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/builtin_plugins/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/builtin_plugins/_constants.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/builtin_plugins/codewhale.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/builtin_plugins/kimi.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/__main__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/_serve_cgroup.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/events_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/init_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/install_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/migrate_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/round_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/service_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/cli/upgrade_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/clock.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/config/errors.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/config/validators.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/context_store.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/defenses.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/event_log.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/host_health.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/http_progress.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/lifecycle.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/metrics.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/phase_select.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/presets/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/presets/aider.toml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/presets/claude.toml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/presets/codewhale.toml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/presets/gemini.toml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/presets/kimi.toml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/presets/pi.toml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/prompt_loader.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/remote_relay.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/round_log.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/round_view.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/scaffold.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/schedule.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/service_unit.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/agent_runner/startup_check.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/build.sh +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/deploy/Dockerfile +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/deploy/docker-compose.yml +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/README.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/migrations/0.2.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/quickstart.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/recipes/aider.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/recipes/codewhale.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/recipes/container-pi.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/recipes/deploy-all-in-one.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/recipes/kimi.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/docs/recipes/pi.md +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/_clock.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/contract/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/e2e/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/e2e/conftest.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/e2e/test_e2e_graceful_stop.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/e2e/test_e2e_install_systemd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/e2e/test_e2e_monitor_remote.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/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.10}/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.10}/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.10}/tests/generate_vulture_whitelist.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_auto_stop_e2e.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_bounded_run.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_cooperative_grace_property.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_fresh_eyes_signal.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_grace_kill_emission.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_install_dry_run.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_monitor_seeded.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_plugin_real_flow.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_prompt_delivery_stdin.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_reap_property.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_scaffold_presets.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_serve_loop.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_spawn_round_mem_floor.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_spawn_round_wedged.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_substrate_fingerprint.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/integration/test_transient_error_backoff.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/_docs.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_atomic_write_enforced.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_authored_facts_declared.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_auto_stop_policy_ssot.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_bdd_test_conventions.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_brake_defaults_off.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_catalogs.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_classification_ssot.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_config_error_consistency.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_config_value_set_ssot.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_container_stop_bound.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_dataclass_fields_have_producers.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_doc_test_pointers_exist.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_doc_transient_error_claims.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_doc_usage_event_fields.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_docs_generated.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_docs_index_complete.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_entry_points_resolve.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_event_int_coercion.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_events_doc_contract.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_gen_blocks_name_their_source.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_migrations_registry_ssot.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_module_sizes.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_monitor_self_exclusion.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_no_ai_signatures.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_no_asyncio_select.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_no_direct_inspect_import.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_no_internal_refs.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_no_price_logic.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_no_raw_time.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_no_vacuous_invariants.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_no_version_refs.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_phase_select_stateless.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_procwait_shield.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_readme_zh_is_pointer.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_round_alloc_growth.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_round_log_decode_boundary.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_round_num_single_source.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_round_result_stable.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_set_diff_for_auto_tool_classification.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_throttle_stateless_imports.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_upstream_schema_canary.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/invariants/test_vulture_whitelist_generated.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/linux/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/linux/test_memory_high_nudge_e2e.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/literate/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/literate/parser.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/literate/test_parser.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/literate/test_quickstart.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/__init__.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_agent_runtime.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_agent_runtime_container.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_agent_runtime_grace.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_agent_runtime_marker_scan.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_agent_runtime_progress.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_agent_runtime_reap.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_agent_runtime_signal_name.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_api_assemble_prompt.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_api_events_stream.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_api_install.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_api_read_round_num.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_api_service.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_api_types.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_auto_stop_gating.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_catalogs.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_cgroup_probe.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_claude_error_detector.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_claude_foreign_schema.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_cli.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_cli_common.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_cli_init_install.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_cli_main_boundary.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_cli_monitor_http.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_cli_service_peek_monitor.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_cli_upgrade.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_codewhale_code_precedence.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_codewhale_plugin.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_coerce_int_poison.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_config_fresh_eyes.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_config_max_rounds.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_config_nested_strictness.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_config_prompt_delivery.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_config_schedule.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_config_stop_file.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_config_strictness.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_config_transient_error_action.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_context_store.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_control_char_config.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_defenses.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_detector_isolation.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_effective_throttle_view.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_emit_config_migrated.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_emit_schedule.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_emit_schedule_phase_skipped.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_event_log.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_events_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_events_cmd_rollover.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_events_cmd_tail.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_events_jsonl_decode.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_example_plugin.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_exec_prefix.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_exit_cause.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_exit_classifier.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_fresh_eyes_trigger.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_gemini_plugin.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_git_timeout.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_hook_redact_order.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_host_health.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_http_progress.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_http_progress_epoch.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_interruptible_sleep_should_stop.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_kill_pgroup_shielding.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_kimi_plugin.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_lifecycle.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_loader_split.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_metrics.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_metrics_cgroup_usage.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_metrics_swap.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_assembly.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_cmd_severity.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_dedup_identity.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_dedup_rearm.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_detectors.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_event_tail.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_loop.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_on_alert.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_remote.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_round_deferred_stale.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_monitor_schedule_pause.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_network_blip_signal_guard.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_notify.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_open_round_log.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_orphan_chain_single_pass.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_outer_round_ceiling.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_peek_argparse.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_peek_project_isolation.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_peek_schedule.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_phase_profiles.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_phase_select.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_pi_plugin.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_plugin_scan_parity.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_presets.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_procwait.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_prompt_loader.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_read_json_hardening.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_redact.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_resolve.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_round_cmd.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_round_cmd_sigterm.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_round_log_helpers.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_round_outcome.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_round_outcome_agent_axis.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_round_view.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_runner_sidecar_ordering.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_runner_throttle.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_scaffold.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_schedule.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_cmd_bounded.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_config_broken.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_crash_loop.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_mem_gate.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_phase_schedule.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_policy.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_round.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_round_cgroup_spine.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_round_log.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_round_oom_killed.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_round_terminate.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_round_wait_exit.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_schedule_gate.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_sentinel.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_serve_wakeup.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_service_mode.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_service_unit.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_startup_battery_exit.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_startup_check.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_substrate.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_supervisor_stale_self_blind.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_terminate_round_wait_exit.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_throttle_back_off_ladder.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_throttle_epoch_bound.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_throttle_plugin_classification.py +0 -0
- {cli_agent_runner-0.3.8 → cli_agent_runner-0.3.10}/tests/unit/test_upgrade_migrate.py +0 -0
|
@@ -15,15 +15,9 @@ 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
|
-
|
|
23
|
-
spawn_hooks
|
|
24
|
-
detectors
|
|
25
|
-
event_kinds
|
|
26
|
-
sigterm_cooperative
|
|
19
|
+
cooperative_stop
|
|
20
|
+
resume_flag
|
|
27
21
|
|
|
28
22
|
# agent_runner._round_outcome.RoundOutcome
|
|
29
23
|
mem_terminated
|
|
@@ -36,12 +30,6 @@ usage_capable_by_agent
|
|
|
36
30
|
newest_usage_ts_by_agent
|
|
37
31
|
backoff_exponent_by_agent
|
|
38
32
|
|
|
39
|
-
# agent_runner._sandbox_probe.SandboxProbe
|
|
40
|
-
achieved_tier
|
|
41
|
-
landlock_abi
|
|
42
|
-
seccomp
|
|
43
|
-
unconfined_reason
|
|
44
|
-
|
|
45
33
|
# agent_runner.agent_runtime.RunResult
|
|
46
34
|
exit_code
|
|
47
35
|
duration_s
|
|
@@ -131,11 +119,6 @@ unit_file
|
|
|
131
119
|
rate_limit
|
|
132
120
|
system_managed
|
|
133
121
|
|
|
134
|
-
# agent_runner.api_types.SpawnDecision
|
|
135
|
-
action
|
|
136
|
-
defer_s
|
|
137
|
-
reason
|
|
138
|
-
|
|
139
122
|
# agent_runner.api_types.SystemMetrics
|
|
140
123
|
mem_total_mb
|
|
141
124
|
mem_available_mb
|
|
@@ -156,7 +139,6 @@ phase
|
|
|
156
139
|
checks
|
|
157
140
|
overlaps
|
|
158
141
|
plan
|
|
159
|
-
sandbox
|
|
160
142
|
cgroup
|
|
161
143
|
sigterm_grace
|
|
162
144
|
|
|
@@ -199,7 +181,6 @@ brake
|
|
|
199
181
|
|
|
200
182
|
# agent_runner.config.models.PhaseOverride
|
|
201
183
|
round_budget_s
|
|
202
|
-
disable_pre_round_hooks
|
|
203
184
|
prompt_files
|
|
204
185
|
agent
|
|
205
186
|
schedule
|
|
@@ -211,10 +192,6 @@ phase_policy
|
|
|
211
192
|
|
|
212
193
|
# agent_runner.config.models.PluginsConfig
|
|
213
194
|
disable
|
|
214
|
-
spawn_override_allow
|
|
215
|
-
sandbox
|
|
216
|
-
pin
|
|
217
|
-
raw
|
|
218
195
|
|
|
219
196
|
# agent_runner.config.models.Profile
|
|
220
197
|
agent
|
|
@@ -235,7 +212,6 @@ work_dir
|
|
|
235
212
|
log_dir
|
|
236
213
|
round_budget_s
|
|
237
214
|
restart_delay_s
|
|
238
|
-
disable_pre_round_hooks
|
|
239
215
|
round_log_retention
|
|
240
216
|
narrative_file
|
|
241
217
|
transient_error_action
|
|
@@ -316,10 +292,6 @@ anomaly_repetitive_window
|
|
|
316
292
|
anomaly_repetitive_threshold
|
|
317
293
|
vcs
|
|
318
294
|
|
|
319
|
-
# agent_runner.hooks.SpawnView
|
|
320
|
-
argv
|
|
321
|
-
env
|
|
322
|
-
|
|
323
295
|
# agent_runner.hooks.VcsHookView
|
|
324
296
|
dirty_action
|
|
325
297
|
stash_idempotency_s
|
|
@@ -5,6 +5,26 @@ 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.10] - 2026-09-17
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Cross-round resume for `pi`: `PluginManifest.resume_flag` (pi = `--session-id`) lets a preset declare an idempotent session flag; `serve` mints one session id per phase and publishes it via `AGENT_RUNNER_RESUME_FLAG`/`AGENT_RUNNER_RESUME_SESSION_ID`, and the round child appends `[flag, id]` to its command. claude/gemini/codewhale/kimi are untouched (`resume_flag` stays `None`) — later release.
|
|
12
|
+
- A new `session_resumed` event records when a round reuses a prior session id.
|
|
13
|
+
- Boot guard: a preset whose static command already contains its `resume_flag` token now fails config load (agent-runner injects it per round; remove the static copy).
|
|
14
|
+
|
|
15
|
+
No config migration — `resume_flag` is a manifest field, not a TOML key. `peek --json` schema stays `2.6`.
|
|
16
|
+
|
|
17
|
+
## [0.3.9] - 2026-09-17
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- `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.
|
|
21
|
+
- `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.
|
|
22
|
+
- 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).
|
|
23
|
+
- `peek --json` schema bumps to `2.6`.
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
- `[plugins]` `sandbox`/`pin`/`spawn_override_allow`/`raw` config keys and `[runtime]`/`[phases.<name>]` `disable_pre_round_hooks` — run `agent-runner migrate` before upgrading.
|
|
27
|
+
|
|
8
28
|
## [0.3.8] - 2026-09-16
|
|
9
29
|
|
|
10
30
|
### 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.10
|
|
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,
|
|
@@ -73,6 +65,7 @@ from agent_runner._emit.serve import ( # noqa: F401 — public re-export
|
|
|
73
65
|
emit_schedule_paused,
|
|
74
66
|
emit_schedule_phase_skipped,
|
|
75
67
|
emit_schedule_resumed,
|
|
68
|
+
emit_session_resumed,
|
|
76
69
|
emit_stalled_no_progress,
|
|
77
70
|
emit_stop_file_detected,
|
|
78
71
|
)
|
|
@@ -94,12 +87,6 @@ __all__ = [
|
|
|
94
87
|
"emit_memory_high_engaged",
|
|
95
88
|
"emit_memory_high_released",
|
|
96
89
|
"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
90
|
"emit_rate_limit_stop",
|
|
104
91
|
"emit_round_cgroup_memory",
|
|
105
92
|
"emit_round_container_orphan_risk",
|
|
@@ -118,6 +105,7 @@ __all__ = [
|
|
|
118
105
|
"emit_schedule_paused",
|
|
119
106
|
"emit_schedule_phase_skipped",
|
|
120
107
|
"emit_schedule_resumed",
|
|
108
|
+
"emit_session_resumed",
|
|
121
109
|
"emit_stale_index_lock_cleared",
|
|
122
110
|
"emit_stalled_no_progress",
|
|
123
111
|
"emit_stop_file_detected",
|
|
@@ -191,3 +191,15 @@ def emit_schedule_resumed(log_dir: Path, *, paused_for_s: int) -> None:
|
|
|
191
191
|
from agent_runner.events import SCHEDULE_RESUMED, emit
|
|
192
192
|
|
|
193
193
|
emit(log_dir, SCHEDULE_RESUMED, paused_for_s=paused_for_s)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def emit_session_resumed(log_dir: Path, *, session_id: str, phase: str = "") -> None:
|
|
197
|
+
"""Emit session_resumed when serve resumes an existing agent session on a
|
|
198
|
+
non-first round of an epoch (the runtime property peek cannot show). ``phase``
|
|
199
|
+
is omitted when empty so the base (no-override) phase stays a clean payload."""
|
|
200
|
+
from agent_runner.events import SESSION_RESUMED, emit
|
|
201
|
+
|
|
202
|
+
fields = {"session_id": session_id}
|
|
203
|
+
if phase:
|
|
204
|
+
fields["phase"] = phase
|
|
205
|
+
emit(log_dir, SESSION_RESUMED, **fields)
|
|
@@ -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,207 @@
|
|
|
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
|
+
resume_flag: str | None = None
|
|
47
|
+
"""The flag that carries a supervisor-chosen session id on EVERY round for a
|
|
48
|
+
CLI whose session flag is IDEMPOTENT (create-or-resume). pi = "--session-id".
|
|
49
|
+
``None`` = no resume (today's cold start). Resolved ONLY by
|
|
50
|
+
``resolve_resume_flag``; ``__post_init__`` rejects an empty string. Source-
|
|
51
|
+
verified per preset (pi's --session-id is idempotent), NEVER assumed."""
|
|
52
|
+
|
|
53
|
+
def __post_init__(self) -> None:
|
|
54
|
+
# Errors-unlikely-by-construction: an unknown signal name (or a
|
|
55
|
+
# freeze/zero-grace one like SIGSTOP/SIGKILL) is unrepresentable -- it
|
|
56
|
+
# raises at construction, not at kill time.
|
|
57
|
+
if self.cooperative_stop is not None and self.cooperative_stop not in _COOPERATIVE_SIGNALS:
|
|
58
|
+
raise ValueError(
|
|
59
|
+
"cooperative_stop must be one of {None, 'SIGTERM', 'SIGINT'}, "
|
|
60
|
+
f"got {self.cooperative_stop!r}"
|
|
61
|
+
)
|
|
62
|
+
if self.resume_flag is not None and not self.resume_flag:
|
|
63
|
+
raise ValueError(
|
|
64
|
+
f"resume_flag must be None or a non-empty flag string, got {self.resume_flag!r}"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
_LOADED_MANIFESTS: list[PluginManifest] = []
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def loaded_manifest_names() -> list[str]:
|
|
72
|
+
"""Names of every currently-registered manifest (order-preserving).
|
|
73
|
+
Mirrors the ``list(_LOADED_MANIFESTS)`` pattern; used by the loader for
|
|
74
|
+
idempotent re-load skipping."""
|
|
75
|
+
return [m.name for m in _LOADED_MANIFESTS]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _cooperative_stop_for_name(agent_binary: str | None) -> str | None:
|
|
79
|
+
"""The ``cooperative_stop`` field of the registered manifest named
|
|
80
|
+
`agent_binary`, or None when no such manifest is registered (or it declares
|
|
81
|
+
no cooperative stop). The single registry lookup the resolvers below share."""
|
|
82
|
+
for m in _LOADED_MANIFESTS:
|
|
83
|
+
if m.name == agent_binary:
|
|
84
|
+
return m.cooperative_stop
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def cooperative_stop_by_name() -> dict[str, str]:
|
|
89
|
+
"""`{manifest name: cooperative_stop signal name}` for every registered
|
|
90
|
+
manifest declaring a cooperative_stop (order-preserving). Mirrors
|
|
91
|
+
loaded_manifest_names; used by peek/doctor -- an operator sees not just
|
|
92
|
+
WHICH presets cooperate but the SIGNAL each drains on."""
|
|
93
|
+
return {m.name: m.cooperative_stop for m in _LOADED_MANIFESTS if m.cooperative_stop is not None}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def is_cooperative_agent(agent_binary: str | None) -> bool:
|
|
97
|
+
"""Whether the agent's preset declares a cooperative_stop. Routes through
|
|
98
|
+
the same single-scan `resolve_cooperative_signal` uses: `cooperative_stop`
|
|
99
|
+
is always in {None, "SIGTERM", "SIGINT"} (`__post_init__`-guaranteed) and
|
|
100
|
+
`_COOPERATIVE_SIGNALS` keys are exactly {"SIGTERM", "SIGINT"}, so a
|
|
101
|
+
resolved signal exists iff the named manifest declares a cooperative_stop."""
|
|
102
|
+
return resolve_cooperative_signal(agent_binary) is not None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def resolve_cooperative_signal(agent_binary: str | None) -> signal.Signals | None:
|
|
106
|
+
"""The signal the agent named `agent_binary` cooperatively drains on, or
|
|
107
|
+
None when its preset declares no cooperative_stop. THE ONLY place a
|
|
108
|
+
cooperative_stop string becomes a ``signal.Signals`` (via the pinned
|
|
109
|
+
``_COOPERATIVE_SIGNALS`` table) -- sibling to resolve_sigterm_grace_s, and
|
|
110
|
+
the SIGNAL half of the same anti-skew single-source: serve resolves it once
|
|
111
|
+
per phase from its own registry and publishes the result via env, so the
|
|
112
|
+
round child never re-derives (and skews) it."""
|
|
113
|
+
return _COOPERATIVE_SIGNALS.get(_cooperative_stop_for_name(agent_binary))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def resolve_resume_flag(agent_binary: str | None) -> str | None:
|
|
117
|
+
"""The resume flag the agent named `agent_binary` declares, or None when no
|
|
118
|
+
such manifest is registered (or it declares no resume). THE registry lookup
|
|
119
|
+
for resume -- sibling to resolve_cooperative_signal, the SAME
|
|
120
|
+
``m.name == agent_binary`` join (so it stays correct whenever a preset's
|
|
121
|
+
manifest name is kept equal to its agent binary). serve resolves it once
|
|
122
|
+
per phase and publishes the id via env so the round child never
|
|
123
|
+
re-derives. No signal table: the flag string IS what serve publishes and
|
|
124
|
+
the child appends, so the scan IS the resolve."""
|
|
125
|
+
for m in _LOADED_MANIFESTS:
|
|
126
|
+
if m.name == agent_binary:
|
|
127
|
+
return m.resume_flag
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def cooperative_signal_from_name(name: str | None) -> signal.Signals | None:
|
|
132
|
+
"""Map a published cooperative-stop signal NAME (e.g. from
|
|
133
|
+
AGENT_RUNNER_COOPERATIVE_STOP_SIGNAL) back to a ``signal.Signals`` through
|
|
134
|
+
the SAME pinned ``_COOPERATIVE_SIGNALS`` table. An unknown/absent name -> None
|
|
135
|
+
(the reap site then defaults to SIGTERM-first, today's behavior). Never
|
|
136
|
+
getattr(signal, ...): a leaked/hostile "SIGKILL"/"SIGSTOP" is simply not a
|
|
137
|
+
table key and falls back safely."""
|
|
138
|
+
return _COOPERATIVE_SIGNALS.get(name) if name is not None else None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def resolve_sigterm_grace_s(agent_binary: str | None, sigterm_grace_s: int) -> int:
|
|
142
|
+
"""The SIGTERM->SIGKILL grace this round's agent actually gets:
|
|
143
|
+
`sigterm_grace_s` (config's [agent] sigterm_grace_s, boot-capped at
|
|
144
|
+
_ROUND_TERM_GRACE_S) when `agent_binary` names a manifest declaring a
|
|
145
|
+
cooperative_stop, else agent_runtime.REAP_GRACE_S (5s) unchanged. One
|
|
146
|
+
function, two callers: cli.serve_cmd._apply_reap_grace_env (what the agent
|
|
147
|
+
actually gets) and doctor/peek (what an operator sees)."""
|
|
148
|
+
from agent_runner.agent_runtime import REAP_GRACE_S
|
|
149
|
+
|
|
150
|
+
if is_cooperative_agent(agent_binary):
|
|
151
|
+
return sigterm_grace_s
|
|
152
|
+
return REAP_GRACE_S
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def register_manifest(manifest: PluginManifest) -> None:
|
|
156
|
+
"""Register every capability a manifest declares into its own registry.
|
|
157
|
+
No import-time side effects — the loader calls this explicitly after
|
|
158
|
+
resolving a plugin's `PLUGIN` attribute.
|
|
159
|
+
|
|
160
|
+
Raises ``ValueError`` up front if ``manifest.name`` collides with an
|
|
161
|
+
already-registered manifest's ``.name`` — this is checked HERE, not by
|
|
162
|
+
the underlying registries' own ``ensure_unique`` calls below, which only
|
|
163
|
+
catch two manifests sharing one of THEIR sub-capabilities' ``.name``
|
|
164
|
+
(e.g. two different plugins each declaring a post_round_hook named
|
|
165
|
+
``"foo"``); they have no visibility into the manifest's own top-level name, so two
|
|
166
|
+
manifests named e.g. ``"codewhale"`` with disjoint capability names would
|
|
167
|
+
otherwise both register, and ``unregister_by_name``/``[plugins] disable``
|
|
168
|
+
would then strip both when the operator meant to disable one.
|
|
169
|
+
|
|
170
|
+
Appends to ``_LOADED_MANIFESTS`` only after every sub-registration
|
|
171
|
+
succeeds — a duplicate CAPABILITY-name registration (e.g. a stray
|
|
172
|
+
double-scan) raises via the underlying registries' own ``ensure_unique``
|
|
173
|
+
checks before the manifest is recorded, so a failed call leaves no
|
|
174
|
+
partial trace for ``unregister_by_name`` to later find and no-op
|
|
175
|
+
against. This guarantee covers ``_LOADED_MANIFESTS`` only: a failure
|
|
176
|
+
partway through the loop below (e.g. the 3rd of 5 declared
|
|
177
|
+
post_round_hooks raises) still leaves the first 2 registered in their
|
|
178
|
+
own per-family registry — matching the prior import-time register_*()
|
|
179
|
+
semantics, where a mid-module exception left every earlier top-level
|
|
180
|
+
call's side effect in place too.
|
|
181
|
+
"""
|
|
182
|
+
ensure_unique(manifest.name, _LOADED_MANIFESTS, "plugin manifest")
|
|
183
|
+
|
|
184
|
+
from agent_runner import hooks
|
|
185
|
+
|
|
186
|
+
for h in manifest.post_round_hooks:
|
|
187
|
+
hooks.register_post_round_hook(h)
|
|
188
|
+
_LOADED_MANIFESTS.append(manifest)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _remove_by_identity(registry: list, items: tuple) -> None:
|
|
192
|
+
registry[:] = [x for x in registry if x not in items]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def unregister_by_name(names: set[str]) -> set[str]:
|
|
196
|
+
"""Remove every capability declared by manifests whose `.name` is in
|
|
197
|
+
`names`. Returns the subset of `names` actually matched (for the
|
|
198
|
+
caller's unknown-name warning)."""
|
|
199
|
+
from agent_runner import hooks
|
|
200
|
+
|
|
201
|
+
found: set[str] = set()
|
|
202
|
+
for manifest in list(_LOADED_MANIFESTS):
|
|
203
|
+
if manifest.name not in names:
|
|
204
|
+
continue
|
|
205
|
+
found.add(manifest.name)
|
|
206
|
+
_remove_by_identity(hooks._POST_ROUND_HOOKS, manifest.post_round_hooks)
|
|
207
|
+
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.10'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 3, 10)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|