cli-agent-runner 0.1.33__tar.gz → 0.1.34__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.1.33 → cli_agent_runner-0.1.34}/CHANGELOG.md +12 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/PKG-INFO +3 -3
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/README.md +2 -2
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/_version.py +2 -2
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/api_types.py +1 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/__init__.py +2 -0
- cli_agent_runner-0.1.34/agent_runner/cli/events_cmd.py +188 -0
- cli_agent_runner-0.1.34/agent_runner/cli/peek_cmd.py +85 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/metrics.py +28 -2
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/monitor.py +1 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/runner.py +15 -2
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/commands.md +20 -2
- cli_agent_runner-0.1.34/docs/migrations/0.1.34.md +110 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/plugins.md +113 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/pyproject.toml +2 -1
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_architecture.py +1 -1
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_module_boundaries.py +1 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_types.py +38 -0
- cli_agent_runner-0.1.34/tests/unit/test_events_cmd.py +179 -0
- cli_agent_runner-0.1.34/tests/unit/test_peek_select.py +41 -0
- cli_agent_runner-0.1.33/agent_runner/cli/peek_cmd.py +0 -156
- cli_agent_runner-0.1.33/tests/unit/test_peek_select.py +0 -76
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.codecov.yml +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.github/workflows/ci.yml +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.github/workflows/release.yml +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.gitignore +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.vulture-whitelist.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/CODE_OF_CONDUCT.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/CONTRIBUTING.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/LICENSE +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/README.zh.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/SECURITY.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/__init__.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/_docgen.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/_emit.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/_registry.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/_substrate.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/_throttle.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/agent_runtime.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/api.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/builtin_plugins/__init__.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/builtin_plugins/_constants.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/builtin_plugins/claude_rate_limit.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/builtin_plugins/gemini.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/__main__.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/common.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/init_cmd.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/install_cmd.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/monitor_cmd.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/round_cmd.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/serve_cmd.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/service_cmd.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/upgrade_cmd.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/config.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/context_store.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/defenses.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/detector_helpers.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/events.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/hooks.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/http_progress.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/lifecycle.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/presets/__init__.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/presets/aider.toml +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/presets/claude.toml +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/presets/gemini.toml +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/prompt_loader.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/round_log.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/round_view.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/scaffold.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/service_unit.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/startup_check.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/vcs_state.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/build.sh +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/deploy/example-agent-runner.toml +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/deploy/launchd.plist.tmpl +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/deploy/run-loop.sh +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/deploy/systemd.service.tmpl +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/README.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/architecture.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/configuration.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/events.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/long-running-agents.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/marketing/README.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/marketing/promo-cn.html +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.16.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.17.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.19.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.20.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.21.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.22.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.23.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.24.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.25.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.26.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.27.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.28.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.29.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.30.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.31.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.32.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.33.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/quickstart.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/recipes/aider.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/runbook.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/thesis.md +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/__init__.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/_test_helpers.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/conftest.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/contract/__init__.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/contract/test_public_api_surface.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/e2e/__init__.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/e2e/conftest.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/e2e/test_e2e_graceful_stop.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/e2e/test_e2e_install_systemd.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/e2e/test_e2e_monitor_remote.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/fixtures/cli-real-output/claude-2.1.143-assistant-tool-use.jsonl +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/fixtures/cli-real-output/claude-2.1.143-result-event.jsonl +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/fixtures/cli-real-output/gemini-0.42.0-result-event.jsonl +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/__init__.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_bounded_run.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_context_enricher_namespacing.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_fresh_eyes_signal.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_grace_kill_emission.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_install_dry_run.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_monitor_seeded.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_plugin_detector_loaded.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_plugin_owned_paths.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_plugin_real_flow.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_run_one_round_with_fake_agent.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_scaffold_presets.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_serve_loop.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_substrate_fingerprint.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_transient_error_backoff.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/__init__.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_atomic_write_enforced.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_catalogs.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_classification_ssot.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_docs_generated.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_entry_points_resolve.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_event_kind_registry.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_event_kinds_ssot.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_events_doc_contract.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_layer_2_loop_size.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_module_sizes.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_no_ai_signatures.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_peek_schema_version.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_round_result_stable.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_upstream_schema_canary.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/literate/__init__.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/literate/parser.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/literate/test_parser.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/literate/test_quickstart.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/__init__.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_agent_runtime.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_agent_runtime_grace.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_agent_runtime_progress.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_assemble_prompt.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_events_stream.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_install.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_observation.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_read_round_num.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_resolve_phase.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_service.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_auto_stop_gating.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_claude_error_detector.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_cli.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_cli_common.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_cli_init_install.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_cli_monitor_http.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_cli_service_peek_monitor.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_cli_upgrade.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_config.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_config_fresh_eyes.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_config_max_rounds.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_config_stop_file.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_config_transient_error_action.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_context_store.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_defenses.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_detector_helpers.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_detector_protocol.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_docgen.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_events.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_fresh_eyes_trigger.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_gemini_plugin.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_hook_failure_isolation.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_hooks.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_http_progress.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_init_entry_points.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_lifecycle.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_metrics.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_monitor_assembly.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_monitor_detectors.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_monitor_remote.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_peek_argparse.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_presets.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_prompt_loader.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_round_log_helpers.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_round_view.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_runner.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_runner_throttle.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_scaffold.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_serve_cmd_bounded.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_serve_round_log.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_serve_sentinel.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_serve_startup_hooks.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_service_unit.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_startup_check.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_substrate.py +0 -0
- {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_vcs_state.py +0 -0
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.34] - 2026-05-20
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- New verb `agent-runner events --kind K[,K2,...] [--window N] [--tail]` for event-stream observation. One-shot or streaming mode (JSON Lines output). Verbs: 13 → 14.
|
|
14
|
+
- `SystemMetrics.agent_process_count: int` — `pgrep -xc` of agent binary basename, host-wide. Surfaces orphan agent processes in peek output.
|
|
15
|
+
- `docs/plugins.md` worked example for custom monitor detector with plugin-emitted exempt flag — pattern for project-specific detectors that exclude exempt-by-design rounds.
|
|
16
|
+
|
|
17
|
+
### Removed
|
|
18
|
+
- `peek --select events.<kind>` selector (mis-placed in state-snapshot verb). Use `events --kind <kind>` instead. 1-line `s///` migration.
|
|
19
|
+
|
|
20
|
+
See `docs/migrations/0.1.34.md`.
|
|
21
|
+
|
|
10
22
|
## [0.1.33] - 2026-05-19
|
|
11
23
|
|
|
12
24
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cli-agent-runner
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.34
|
|
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
|
|
@@ -80,14 +80,14 @@ agent-runner monitor # live anomaly detection
|
|
|
80
80
|
|
|
81
81
|
Full walkthrough: [`docs/quickstart.md`](docs/quickstart.md).
|
|
82
82
|
|
|
83
|
-
##
|
|
83
|
+
## 14 verbs
|
|
84
84
|
|
|
85
85
|
| Lifecycle | Observation |
|
|
86
86
|
|---|---|
|
|
87
87
|
| `init` / `install` / `uninstall` | `peek` — state snapshot |
|
|
88
88
|
| `start` / `stop` / `kill` / `cancel` | `watch` — peek in a refresh loop |
|
|
89
89
|
| `restart` / `status` | `monitor` — 11 detectors, alerts, auto-stop |
|
|
90
|
-
| `round` / `serve` | |
|
|
90
|
+
| `round` / `serve` / `upgrade` | `events` — query / stream events.jsonl |
|
|
91
91
|
|
|
92
92
|
Verb reference: [`docs/commands.md`](docs/commands.md).
|
|
93
93
|
|
|
@@ -43,14 +43,14 @@ agent-runner monitor # live anomaly detection
|
|
|
43
43
|
|
|
44
44
|
Full walkthrough: [`docs/quickstart.md`](docs/quickstart.md).
|
|
45
45
|
|
|
46
|
-
##
|
|
46
|
+
## 14 verbs
|
|
47
47
|
|
|
48
48
|
| Lifecycle | Observation |
|
|
49
49
|
|---|---|
|
|
50
50
|
| `init` / `install` / `uninstall` | `peek` — state snapshot |
|
|
51
51
|
| `start` / `stop` / `kill` / `cancel` | `watch` — peek in a refresh loop |
|
|
52
52
|
| `restart` / `status` | `monitor` — 11 detectors, alerts, auto-stop |
|
|
53
|
-
| `round` / `serve` | |
|
|
53
|
+
| `round` / `serve` / `upgrade` | `events` — query / stream events.jsonl |
|
|
54
54
|
|
|
55
55
|
Verb reference: [`docs/commands.md`](docs/commands.md).
|
|
56
56
|
|
|
@@ -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.1.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
21
|
+
__version__ = version = '0.1.34'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 34)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -17,6 +17,7 @@ from pathlib import Path
|
|
|
17
17
|
|
|
18
18
|
from agent_runner import __version__
|
|
19
19
|
from agent_runner.cli import (
|
|
20
|
+
events_cmd,
|
|
20
21
|
init_cmd,
|
|
21
22
|
install_cmd,
|
|
22
23
|
monitor_cmd,
|
|
@@ -63,6 +64,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
63
64
|
install_cmd.add_parser(sub, parent)
|
|
64
65
|
service_cmd.add_parser(sub, parent)
|
|
65
66
|
peek_cmd.add_parser(sub, parent)
|
|
67
|
+
events_cmd.add_parser(sub, parent)
|
|
66
68
|
monitor_cmd.add_parser(sub, parent)
|
|
67
69
|
serve_cmd.add_parser(sub, parent)
|
|
68
70
|
round_cmd.add_parser(sub, parent)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""agent-runner events — event-stream observation verb (0.1.34+).
|
|
2
|
+
|
|
3
|
+
One-shot (--window N) or streaming (--tail) query against events.jsonl.
|
|
4
|
+
JSON Lines output (one JSON object per line, no pretty-print).
|
|
5
|
+
|
|
6
|
+
Current-month scope only. Tail mode follows month rollover via per-poll glob.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import json
|
|
13
|
+
import signal
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
from datetime import UTC, datetime
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
# Sentinel for "user did not explicitly set --window" so we can detect
|
|
20
|
+
# --window + --tail combinations. argparse mutually-exclusive group would
|
|
21
|
+
# be cleaner but argparse doesn't support "exclusive only when X has value Y".
|
|
22
|
+
_WINDOW_DEFAULT_SENTINEL = -1
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _positive_int(s: str) -> int:
|
|
26
|
+
"""Parse positive integer (duplicate of peek_cmd._positive_int; KISS)."""
|
|
27
|
+
try:
|
|
28
|
+
n = int(s)
|
|
29
|
+
except ValueError as e:
|
|
30
|
+
raise argparse.ArgumentTypeError(f"expects positive int, got {s!r}") from e
|
|
31
|
+
if n <= 0:
|
|
32
|
+
raise argparse.ArgumentTypeError(f"expects positive int (> 0), got {n}")
|
|
33
|
+
return n
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _parse_kinds(raw: str) -> set[str]:
|
|
37
|
+
"""Parse comma-separated kinds; strip whitespace; reject empty."""
|
|
38
|
+
parts = [k.strip() for k in (raw or "").split(",") if k.strip()]
|
|
39
|
+
return set(parts)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def add_parser(sub, parent) -> None:
|
|
43
|
+
p = sub.add_parser(
|
|
44
|
+
"events",
|
|
45
|
+
parents=[parent],
|
|
46
|
+
help="Query / stream events from events.jsonl by kind",
|
|
47
|
+
)
|
|
48
|
+
p.add_argument(
|
|
49
|
+
"--kind",
|
|
50
|
+
type=str,
|
|
51
|
+
required=True,
|
|
52
|
+
metavar="K[,K2,...]",
|
|
53
|
+
help="Comma-separated event kinds (OR-filtered). At least one required.",
|
|
54
|
+
)
|
|
55
|
+
p.add_argument(
|
|
56
|
+
"--window",
|
|
57
|
+
type=_positive_int,
|
|
58
|
+
default=_WINDOW_DEFAULT_SENTINEL,
|
|
59
|
+
metavar="N",
|
|
60
|
+
help="One-shot mode: emit last N matching events (default 10).",
|
|
61
|
+
)
|
|
62
|
+
p.add_argument(
|
|
63
|
+
"--tail",
|
|
64
|
+
action="store_true",
|
|
65
|
+
help=("Streaming mode: emit each new matching event as it fires (blocks until SIGINT)."),
|
|
66
|
+
)
|
|
67
|
+
p.set_defaults(func=cmd_events)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _resolve_log_dir(args) -> Path:
|
|
71
|
+
"""Resolve log_dir from --config (used by both cmd_events and tests)."""
|
|
72
|
+
if getattr(args, "_log_dir_override", None) is not None:
|
|
73
|
+
return args._log_dir_override
|
|
74
|
+
from agent_runner.cli.common import work_dir_from_args
|
|
75
|
+
from agent_runner.config import load_config
|
|
76
|
+
|
|
77
|
+
cfg = load_config(work_dir_from_args(args) / "agent-runner.toml")
|
|
78
|
+
return cfg.runtime.log_dir
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def cmd_events(args) -> int:
|
|
82
|
+
kind_set = _parse_kinds(args.kind)
|
|
83
|
+
if not kind_set:
|
|
84
|
+
print(
|
|
85
|
+
"Error: --kind requires at least one non-empty event kind",
|
|
86
|
+
file=sys.stderr,
|
|
87
|
+
)
|
|
88
|
+
return 2
|
|
89
|
+
|
|
90
|
+
window_explicit = getattr(args, "_window_explicit", False) or (
|
|
91
|
+
args.window != _WINDOW_DEFAULT_SENTINEL
|
|
92
|
+
)
|
|
93
|
+
if args.tail and window_explicit:
|
|
94
|
+
print(
|
|
95
|
+
"Error: --window and --tail are mutually exclusive",
|
|
96
|
+
file=sys.stderr,
|
|
97
|
+
)
|
|
98
|
+
return 2
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
log_dir = _resolve_log_dir(args)
|
|
102
|
+
except FileNotFoundError as e:
|
|
103
|
+
print(f"Error: config not found: {e}", file=sys.stderr)
|
|
104
|
+
return 1
|
|
105
|
+
|
|
106
|
+
if args.tail:
|
|
107
|
+
return _tail_events(log_dir, kind_set)
|
|
108
|
+
|
|
109
|
+
window = args.window if args.window != _WINDOW_DEFAULT_SENTINEL else 10
|
|
110
|
+
return _query_events(log_dir, kind_set, window)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _current_month_events_file(log_dir: Path) -> Path:
|
|
114
|
+
month = datetime.now(UTC).strftime("%Y-%m")
|
|
115
|
+
return log_dir / f"events-{month}.jsonl"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _query_events(log_dir: Path, kind_set: set[str], window: int) -> int:
|
|
119
|
+
"""One-shot: read current-month events.jsonl, filter, print last N."""
|
|
120
|
+
events_file = _current_month_events_file(log_dir)
|
|
121
|
+
if not events_file.exists():
|
|
122
|
+
return 0
|
|
123
|
+
|
|
124
|
+
matches: list[str] = []
|
|
125
|
+
try:
|
|
126
|
+
with events_file.open("r", encoding="utf-8") as f:
|
|
127
|
+
for line in f:
|
|
128
|
+
line = line.strip()
|
|
129
|
+
if not line:
|
|
130
|
+
continue
|
|
131
|
+
try:
|
|
132
|
+
evt = json.loads(line)
|
|
133
|
+
except json.JSONDecodeError:
|
|
134
|
+
continue
|
|
135
|
+
if evt.get("event") in kind_set:
|
|
136
|
+
matches.append(line)
|
|
137
|
+
except OSError as e:
|
|
138
|
+
print(f"Error: events file unreadable: {e}", file=sys.stderr)
|
|
139
|
+
return 1
|
|
140
|
+
|
|
141
|
+
for line in matches[-window:]:
|
|
142
|
+
print(line)
|
|
143
|
+
return 0
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _tail_events(log_dir: Path, kind_set: set[str]) -> int:
|
|
147
|
+
"""Streaming: poll current-month events.jsonl at 1s interval; emit each
|
|
148
|
+
new matching line as it fires. Blocks until SIGINT (KeyboardInterrupt).
|
|
149
|
+
Follows month rollover via per-poll glob.
|
|
150
|
+
"""
|
|
151
|
+
last_size = 0
|
|
152
|
+
current_file: Path | None = None
|
|
153
|
+
|
|
154
|
+
def _handle_sigint(_signum, _frame):
|
|
155
|
+
raise KeyboardInterrupt()
|
|
156
|
+
|
|
157
|
+
signal.signal(signal.SIGINT, _handle_sigint)
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
while True:
|
|
161
|
+
events_file = _current_month_events_file(log_dir)
|
|
162
|
+
if events_file != current_file:
|
|
163
|
+
# Month rollover OR first iteration: reset offset
|
|
164
|
+
current_file = events_file
|
|
165
|
+
last_size = events_file.stat().st_size if events_file.exists() else 0
|
|
166
|
+
|
|
167
|
+
if events_file.exists():
|
|
168
|
+
size = events_file.stat().st_size
|
|
169
|
+
if size > last_size:
|
|
170
|
+
with events_file.open("r", encoding="utf-8") as f:
|
|
171
|
+
f.seek(last_size)
|
|
172
|
+
for line in f:
|
|
173
|
+
line = line.strip()
|
|
174
|
+
if not line:
|
|
175
|
+
continue
|
|
176
|
+
try:
|
|
177
|
+
evt = json.loads(line)
|
|
178
|
+
except json.JSONDecodeError:
|
|
179
|
+
continue
|
|
180
|
+
if evt.get("event") in kind_set:
|
|
181
|
+
print(line, flush=True)
|
|
182
|
+
last_size = size
|
|
183
|
+
elif size < last_size:
|
|
184
|
+
# File truncated / rotated underneath us; reset
|
|
185
|
+
last_size = 0
|
|
186
|
+
time.sleep(1.0)
|
|
187
|
+
except KeyboardInterrupt:
|
|
188
|
+
return 0
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""peek and watch subcommands — snapshot + auto-refresh."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
import time
|
|
8
|
+
|
|
9
|
+
from agent_runner import api
|
|
10
|
+
from agent_runner.cli.common import emit, fail, work_dir_from_args
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _round_arg(s: str) -> int | str:
|
|
14
|
+
if s == "latest":
|
|
15
|
+
return s
|
|
16
|
+
try:
|
|
17
|
+
return int(s)
|
|
18
|
+
except ValueError as e:
|
|
19
|
+
raise argparse.ArgumentTypeError(f"--round expects int or 'latest', got {s!r}") from e
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def add_parser(sub, parent) -> None:
|
|
23
|
+
for verb, fn in (("peek", cmd_peek), ("watch", cmd_watch)):
|
|
24
|
+
p = sub.add_parser(
|
|
25
|
+
verb, parents=[parent], help=f"{verb} project state with optional drill-down"
|
|
26
|
+
)
|
|
27
|
+
p.add_argument(
|
|
28
|
+
"--round",
|
|
29
|
+
type=_round_arg,
|
|
30
|
+
default=None,
|
|
31
|
+
metavar="N",
|
|
32
|
+
help="Drill into round N (int or 'latest')",
|
|
33
|
+
)
|
|
34
|
+
p.add_argument("--log", action="store_true", help="Include current round's log tail")
|
|
35
|
+
p.add_argument(
|
|
36
|
+
"--events", type=int, default=None, metavar="N", help="Include last N events"
|
|
37
|
+
)
|
|
38
|
+
p.add_argument(
|
|
39
|
+
"--select",
|
|
40
|
+
type=str,
|
|
41
|
+
default=None,
|
|
42
|
+
help=(
|
|
43
|
+
"Selector: dot-path (e.g. system.disk_used_pct) extracts a subtree from "
|
|
44
|
+
"peek state. For event-stream queries, use the dedicated `events` verb."
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
if verb == "watch":
|
|
48
|
+
p.add_argument(
|
|
49
|
+
"--interval",
|
|
50
|
+
type=int,
|
|
51
|
+
default=2,
|
|
52
|
+
metavar="SECONDS",
|
|
53
|
+
help="Refresh interval (default 2)",
|
|
54
|
+
)
|
|
55
|
+
p.set_defaults(func=fn)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def cmd_peek(args) -> int:
|
|
59
|
+
select = args.select
|
|
60
|
+
try:
|
|
61
|
+
result = api.peek(
|
|
62
|
+
work_dir_from_args(args),
|
|
63
|
+
round=args.round,
|
|
64
|
+
log=args.log,
|
|
65
|
+
events=args.events,
|
|
66
|
+
select=select,
|
|
67
|
+
)
|
|
68
|
+
except KeyError as e:
|
|
69
|
+
return fail(str(e))
|
|
70
|
+
except FileNotFoundError as e:
|
|
71
|
+
return fail(f"config not found: {e}")
|
|
72
|
+
emit(result, json_mode=getattr(args, "json", False))
|
|
73
|
+
return 0
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def cmd_watch(args) -> int:
|
|
77
|
+
while True:
|
|
78
|
+
sys.stdout.write("\x1b[2J\x1b[H")
|
|
79
|
+
rc = cmd_peek(args)
|
|
80
|
+
if rc != 0:
|
|
81
|
+
return rc
|
|
82
|
+
try:
|
|
83
|
+
time.sleep(args.interval)
|
|
84
|
+
except KeyboardInterrupt:
|
|
85
|
+
return 0
|
|
@@ -7,6 +7,7 @@ from __future__ import annotations
|
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
9
|
import os
|
|
10
|
+
import subprocess
|
|
10
11
|
from datetime import UTC, datetime
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
from typing import Any
|
|
@@ -16,7 +17,7 @@ import psutil
|
|
|
16
17
|
from agent_runner.events import now_iso_ms
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
def collect(disk_path: Path) -> dict[str, Any]:
|
|
20
|
+
def collect(disk_path: Path, *, agent_binary: str | None = None) -> dict[str, Any]:
|
|
20
21
|
vm = psutil.virtual_memory()
|
|
21
22
|
du = psutil.disk_usage(str(disk_path))
|
|
22
23
|
out: dict[str, Any] = {
|
|
@@ -38,15 +39,40 @@ def collect(disk_path: Path) -> dict[str, Any]:
|
|
|
38
39
|
out["cpu_pct"] = round(psutil.cpu_percent(interval=None), 1)
|
|
39
40
|
except Exception:
|
|
40
41
|
pass
|
|
42
|
+
if agent_binary:
|
|
43
|
+
out["agent_process_count"] = _count_agent_processes(agent_binary)
|
|
41
44
|
return out
|
|
42
45
|
|
|
43
46
|
|
|
47
|
+
def _count_agent_processes(agent_binary: str) -> int:
|
|
48
|
+
"""Run `pgrep -xc <agent_binary>`; return count or 0 on error.
|
|
49
|
+
|
|
50
|
+
Host-wide intentional — catches orphan agent processes not parented
|
|
51
|
+
by us, which is the diagnostic value of this metric.
|
|
52
|
+
"""
|
|
53
|
+
try:
|
|
54
|
+
result = subprocess.run(
|
|
55
|
+
["pgrep", "-xc", agent_binary],
|
|
56
|
+
capture_output=True,
|
|
57
|
+
text=True,
|
|
58
|
+
timeout=2,
|
|
59
|
+
)
|
|
60
|
+
# pgrep -c returns exit 1 with output "0" when no matches; exit 0
|
|
61
|
+
# with count otherwise. Both are valid; non-int output → 0.
|
|
62
|
+
if result.returncode in (0, 1):
|
|
63
|
+
return int(result.stdout.strip() or "0")
|
|
64
|
+
except (subprocess.SubprocessError, ValueError, FileNotFoundError, OSError):
|
|
65
|
+
pass
|
|
66
|
+
return 0
|
|
67
|
+
|
|
68
|
+
|
|
44
69
|
def log_metrics(
|
|
45
70
|
log_dir: Path,
|
|
46
71
|
*,
|
|
47
72
|
event: str = "periodic",
|
|
48
73
|
round_num: int | None = None,
|
|
49
74
|
phase: str | None = None,
|
|
75
|
+
agent_binary: str | None = None,
|
|
50
76
|
) -> None:
|
|
51
77
|
"""Append one metrics sample to metrics-YYYY-MM.jsonl (UTC).
|
|
52
78
|
|
|
@@ -59,7 +85,7 @@ def log_metrics(
|
|
|
59
85
|
payload: dict[str, Any] = {
|
|
60
86
|
"ts": now_iso_ms(),
|
|
61
87
|
"event": event,
|
|
62
|
-
**collect(log_dir),
|
|
88
|
+
**collect(log_dir, agent_binary=agent_binary),
|
|
63
89
|
}
|
|
64
90
|
if round_num is not None:
|
|
65
91
|
payload["round_num"] = round_num
|
|
@@ -515,6 +515,7 @@ def assemble_project_state(source: StateSource, *, project: str) -> ProjectState
|
|
|
515
515
|
disk_free_gb=float(latest.get("disk_free_gb", 0.0)),
|
|
516
516
|
load_1m=latest.get("load_1m"),
|
|
517
517
|
cpu_pct=latest.get("cpu_pct"),
|
|
518
|
+
agent_process_count=int(latest.get("agent_process_count", 0)),
|
|
518
519
|
)
|
|
519
520
|
return ProjectState(
|
|
520
521
|
project=project,
|
|
@@ -441,7 +441,14 @@ def _run_one_round_inner(cfg: Config, *, phase_override: str | None = None) -> R
|
|
|
441
441
|
context_store.atomic_write_json(log_dir / context_store.CONTEXT_FILE, enriched_ctx)
|
|
442
442
|
|
|
443
443
|
events.emit(log_dir, "round_start", round_num=round_num, phase=phase)
|
|
444
|
-
|
|
444
|
+
_agent_binary = Path(cfg.agent.command[0]).name if cfg.agent.command else None
|
|
445
|
+
metrics.log_metrics(
|
|
446
|
+
log_dir,
|
|
447
|
+
event="round_start",
|
|
448
|
+
round_num=round_num,
|
|
449
|
+
phase=phase,
|
|
450
|
+
agent_binary=_agent_binary,
|
|
451
|
+
)
|
|
445
452
|
|
|
446
453
|
prompt = _api_assemble_prompt(cfg, phase=phase, context=enriched_ctx)
|
|
447
454
|
|
|
@@ -564,7 +571,13 @@ def _run_one_round_inner(cfg: Config, *, phase_override: str | None = None) -> R
|
|
|
564
571
|
phase_index=phase_idx,
|
|
565
572
|
),
|
|
566
573
|
)
|
|
567
|
-
metrics.log_metrics(
|
|
574
|
+
metrics.log_metrics(
|
|
575
|
+
log_dir,
|
|
576
|
+
event="round_end",
|
|
577
|
+
round_num=round_num,
|
|
578
|
+
phase=phase,
|
|
579
|
+
agent_binary=_agent_binary,
|
|
580
|
+
)
|
|
568
581
|
events.emit(log_dir, "round_end", round_num=round_num)
|
|
569
582
|
|
|
570
583
|
round_result = RoundResult(
|
|
@@ -20,6 +20,7 @@ are shared between `peek`, `watch`, and `monitor`.
|
|
|
20
20
|
| `status` | Show current service state |
|
|
21
21
|
| `peek` | peek project state with optional drill-down |
|
|
22
22
|
| `watch` | watch project state with optional drill-down |
|
|
23
|
+
| `events` | Query / stream events from events.jsonl by kind |
|
|
23
24
|
| `monitor` | Anomaly detection, narrate/events stream, or HTTP progress page |
|
|
24
25
|
| `serve` | Long-running supervisor loop |
|
|
25
26
|
| `round` | Run one round and exit |
|
|
@@ -87,12 +88,29 @@ agent-runner peek
|
|
|
87
88
|
agent-runner peek --json
|
|
88
89
|
agent-runner peek --select system.disk_used_pct
|
|
89
90
|
agent-runner peek --select defenses
|
|
90
|
-
agent-runner peek --select events.agent_usage_recorded --window 5 # 0.1.32+: native event-kind query
|
|
91
|
-
agent-runner peek --select events.transient_error_detected --window 20
|
|
92
91
|
agent-runner peek --round 42 --log # drill into round 42, include log tail
|
|
93
92
|
agent-runner peek --events 50 # last 50 events
|
|
94
93
|
```
|
|
95
94
|
|
|
95
|
+
### `agent-runner events --kind K[,K2,...] [--window N] [--tail]`
|
|
96
|
+
|
|
97
|
+
Query or stream events.jsonl by kind. Output is always JSON Lines (one event
|
|
98
|
+
JSON per line). Current-month events.jsonl scope only.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# One-shot: last 5 usage records
|
|
102
|
+
agent-runner events --kind agent_usage_recorded --window 5
|
|
103
|
+
|
|
104
|
+
# Multi-kind OR filter
|
|
105
|
+
agent-runner events --kind round_end,hook_failed --window 20
|
|
106
|
+
|
|
107
|
+
# Streaming: emit each new matching event as it fires; blocks until SIGINT
|
|
108
|
+
agent-runner events --kind transient_error_backoff_capped --tail
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`--window N` and `--tail` are mutually exclusive. Exit codes: 0 normal,
|
|
112
|
+
2 invalid arguments, 1 unreadable events file.
|
|
113
|
+
|
|
96
114
|
### `agent-runner watch [--interval N] [peek-flags]`
|
|
97
115
|
|
|
98
116
|
`peek` in a clear-and-refresh loop. Default 2s interval. Stop with Ctrl-C.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# 0.1.34 — `events` verb + plugin pattern + `agent_process_count`
|
|
2
|
+
|
|
3
|
+
**Date**: 2026-05-20
|
|
4
|
+
|
|
5
|
+
## What changed
|
|
6
|
+
|
|
7
|
+
Three additive improvements + one hard-cut cleanup:
|
|
8
|
+
|
|
9
|
+
1. **New `events` verb** for event-stream observation. One-shot query
|
|
10
|
+
(`--window N`) or streaming (`--tail`) modes. Multi-kind OR filter via
|
|
11
|
+
`--kind K[,K2,...]`. JSON Lines output, machine consumption ready.
|
|
12
|
+
2. **`SystemMetrics.agent_process_count`** — `pgrep -xc <agent binary>`
|
|
13
|
+
snapshot, host-wide. Surfaces orphan agent processes in peek output.
|
|
14
|
+
3. **`docs/plugins.md` worked example** for project-specific monitor
|
|
15
|
+
detectors with plugin-emitted exempt flag. Pattern reference for
|
|
16
|
+
detectors like "no-commit-rounds stuck" or "wall-time trend" that
|
|
17
|
+
need to exclude exempt-by-design rounds.
|
|
18
|
+
4. **Removed `peek --select events.<kind>` selector** from 0.1.32. The
|
|
19
|
+
events.<kind> form was mis-placed in `peek` (state snapshot verb); the
|
|
20
|
+
new `events` verb owns event-stream queries. Other peek selectors
|
|
21
|
+
(`system.*`, `defenses`, `plugins.*`) unchanged.
|
|
22
|
+
|
|
23
|
+
## Migration
|
|
24
|
+
|
|
25
|
+
### Consumer using `peek --select events.<kind>` (added 0.1.32, 2 days ago)
|
|
26
|
+
|
|
27
|
+
One-line `s///`:
|
|
28
|
+
|
|
29
|
+
```diff
|
|
30
|
+
- agent-runner peek --select events.agent_usage_recorded --window 5
|
|
31
|
+
+ agent-runner events --kind agent_usage_recorded --window 5
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Same scope (current-month events.jsonl), same default window (10).
|
|
35
|
+
|
|
36
|
+
### Why hard-cut instead of deprecation window
|
|
37
|
+
|
|
38
|
+
Consumers are still in test-mode integration; the 0.1.32 selector shipped
|
|
39
|
+
2 days ago. Hard-cutting now (before production lock-in) is cheaper than
|
|
40
|
+
carrying back-compat indefinitely. Same rationale as 0.1.29 alias removal.
|
|
41
|
+
|
|
42
|
+
## `events` verb usage
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# One-shot: last 5 events of one kind (default window=10)
|
|
46
|
+
agent-runner events --kind agent_usage_recorded --window 5
|
|
47
|
+
|
|
48
|
+
# Multi-kind OR filter
|
|
49
|
+
agent-runner events --kind round_end,hook_failed,transient_error_backoff_capped --window 20
|
|
50
|
+
|
|
51
|
+
# Streaming: emit each new matching event as it fires; blocks until SIGINT
|
|
52
|
+
agent-runner events --kind anomaly_repetitive_tool --tail
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`--window N` and `--tail` are mutually exclusive (exit 2 on combination).
|
|
56
|
+
Tail mode polls events.jsonl at 1s interval and follows next-month rollover
|
|
57
|
+
via per-poll glob (no need to restart at month boundary).
|
|
58
|
+
|
|
59
|
+
Pipe to `jq` for richer filtering / projection:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
agent-runner events --kind transient_error_backoff_capped --tail \
|
|
63
|
+
| jq -c '{ts, classification, consecutive_count, applied_reset_at_epoch}'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## `agent_process_count` in peek output
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
agent-runner peek --json | jq .system.agent_process_count
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Host-wide count of processes matching the agent binary basename
|
|
73
|
+
(`pgrep -xc <basename of agent.command[0]>`). Catches orphan agent
|
|
74
|
+
processes not parented by agent-runner — diagnostic value for "is there
|
|
75
|
+
a stuck claude lingering?"
|
|
76
|
+
|
|
77
|
+
If `pgrep` is unavailable or errors, count is reported as `0`.
|
|
78
|
+
|
|
79
|
+
## Plugin authors: project-specific detectors
|
|
80
|
+
|
|
81
|
+
See `docs/plugins.md` § "Worked example: project-specific monitor detector
|
|
82
|
+
with plugin-emitted exempt flag" for an end-to-end pattern. Use it for:
|
|
83
|
+
|
|
84
|
+
- No-commit-rounds stuck detection (count commits per round, exempt
|
|
85
|
+
short / nothing-to-do iterations)
|
|
86
|
+
- Wall-time trend detection (avg recent vs older, exempt rounds where
|
|
87
|
+
trend signal is irrelevant)
|
|
88
|
+
- Any other project-specific detector where some rounds should be
|
|
89
|
+
excluded from the detection criterion
|
|
90
|
+
|
|
91
|
+
The pattern: plugin emits `<plugin>_round_exempt` event for exempt
|
|
92
|
+
rounds; custom detector reads events.jsonl tail, builds exempt set,
|
|
93
|
+
filters, applies its own logic.
|
|
94
|
+
|
|
95
|
+
## What did NOT change
|
|
96
|
+
|
|
97
|
+
- `events.jsonl` schema / event kinds — unchanged
|
|
98
|
+
- All other `peek` selectors (`system.*`, `defenses`, `plugins.*`,
|
|
99
|
+
`current_round.*`, etc.) — unchanged
|
|
100
|
+
- `agent-runner monitor` — unchanged (its detector loop is separate from
|
|
101
|
+
the new `events` verb)
|
|
102
|
+
- Plugin extension points (`register_post_round_hook`,
|
|
103
|
+
`register_custom_detector`, etc.) — unchanged
|
|
104
|
+
- Public Python API beyond the new field on SystemMetrics — unchanged
|
|
105
|
+
- Config schema — no new TOML keys
|
|
106
|
+
- `_BUILTIN_KINDS` — unchanged (no new event kinds in this release)
|
|
107
|
+
|
|
108
|
+
## No consumer action required (except the 1-line `s///` above)
|
|
109
|
+
|
|
110
|
+
If you don't use `peek --select events.<kind>`, no migration step.
|