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.
Files changed (220) hide show
  1. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/CHANGELOG.md +12 -0
  2. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/PKG-INFO +3 -3
  3. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/README.md +2 -2
  4. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/_version.py +2 -2
  5. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/api_types.py +1 -0
  6. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/__init__.py +2 -0
  7. cli_agent_runner-0.1.34/agent_runner/cli/events_cmd.py +188 -0
  8. cli_agent_runner-0.1.34/agent_runner/cli/peek_cmd.py +85 -0
  9. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/metrics.py +28 -2
  10. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/monitor.py +1 -0
  11. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/runner.py +15 -2
  12. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/commands.md +20 -2
  13. cli_agent_runner-0.1.34/docs/migrations/0.1.34.md +110 -0
  14. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/plugins.md +113 -0
  15. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/pyproject.toml +2 -1
  16. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_architecture.py +1 -1
  17. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_module_boundaries.py +1 -0
  18. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_types.py +38 -0
  19. cli_agent_runner-0.1.34/tests/unit/test_events_cmd.py +179 -0
  20. cli_agent_runner-0.1.34/tests/unit/test_peek_select.py +41 -0
  21. cli_agent_runner-0.1.33/agent_runner/cli/peek_cmd.py +0 -156
  22. cli_agent_runner-0.1.33/tests/unit/test_peek_select.py +0 -76
  23. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.codecov.yml +0 -0
  24. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  25. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  26. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  27. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  28. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.github/workflows/ci.yml +0 -0
  29. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.github/workflows/release.yml +0 -0
  30. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.gitignore +0 -0
  31. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/.vulture-whitelist.py +0 -0
  32. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/CODE_OF_CONDUCT.md +0 -0
  33. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/CONTRIBUTING.md +0 -0
  34. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/LICENSE +0 -0
  35. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/README.zh.md +0 -0
  36. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/SECURITY.md +0 -0
  37. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/__init__.py +0 -0
  38. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/_docgen.py +0 -0
  39. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/_emit.py +0 -0
  40. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/_registry.py +0 -0
  41. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/_substrate.py +0 -0
  42. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/_throttle.py +0 -0
  43. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/agent_runtime.py +0 -0
  44. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/api.py +0 -0
  45. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/builtin_plugins/__init__.py +0 -0
  46. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/builtin_plugins/_constants.py +0 -0
  47. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/builtin_plugins/claude_rate_limit.py +0 -0
  48. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/builtin_plugins/gemini.py +0 -0
  49. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/__main__.py +0 -0
  50. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/common.py +0 -0
  51. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/init_cmd.py +0 -0
  52. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/install_cmd.py +0 -0
  53. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/monitor_cmd.py +0 -0
  54. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/round_cmd.py +0 -0
  55. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/serve_cmd.py +0 -0
  56. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/service_cmd.py +0 -0
  57. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/cli/upgrade_cmd.py +0 -0
  58. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/config.py +0 -0
  59. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/context_store.py +0 -0
  60. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/defenses.py +0 -0
  61. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/detector_helpers.py +0 -0
  62. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/events.py +0 -0
  63. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/hooks.py +0 -0
  64. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/http_progress.py +0 -0
  65. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/lifecycle.py +0 -0
  66. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/presets/__init__.py +0 -0
  67. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/presets/aider.toml +0 -0
  68. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/presets/claude.toml +0 -0
  69. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/presets/gemini.toml +0 -0
  70. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/prompt_loader.py +0 -0
  71. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/round_log.py +0 -0
  72. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/round_view.py +0 -0
  73. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/scaffold.py +0 -0
  74. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/service_unit.py +0 -0
  75. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/startup_check.py +0 -0
  76. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/agent_runner/vcs_state.py +0 -0
  77. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/build.sh +0 -0
  78. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/deploy/example-agent-runner.toml +0 -0
  79. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/deploy/launchd.plist.tmpl +0 -0
  80. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/deploy/run-loop.sh +0 -0
  81. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/deploy/systemd.service.tmpl +0 -0
  82. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/README.md +0 -0
  83. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/architecture.md +0 -0
  84. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/configuration.md +0 -0
  85. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/events.md +0 -0
  86. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/long-running-agents.md +0 -0
  87. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/marketing/README.md +0 -0
  88. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/marketing/promo-cn.html +0 -0
  89. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.16.md +0 -0
  90. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.17.md +0 -0
  91. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.19.md +0 -0
  92. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.20.md +0 -0
  93. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.21.md +0 -0
  94. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.22.md +0 -0
  95. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.23.md +0 -0
  96. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.24.md +0 -0
  97. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.25.md +0 -0
  98. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.26.md +0 -0
  99. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.27.md +0 -0
  100. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.28.md +0 -0
  101. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.29.md +0 -0
  102. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.30.md +0 -0
  103. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.31.md +0 -0
  104. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.32.md +0 -0
  105. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/migrations/0.1.33.md +0 -0
  106. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/quickstart.md +0 -0
  107. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/recipes/aider.md +0 -0
  108. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/runbook.md +0 -0
  109. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/docs/thesis.md +0 -0
  110. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/__init__.py +0 -0
  111. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/_test_helpers.py +0 -0
  112. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/conftest.py +0 -0
  113. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/contract/__init__.py +0 -0
  114. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/contract/test_public_api_surface.py +0 -0
  115. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/e2e/__init__.py +0 -0
  116. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/e2e/conftest.py +0 -0
  117. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/e2e/test_e2e_graceful_stop.py +0 -0
  118. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/e2e/test_e2e_install_systemd.py +0 -0
  119. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/e2e/test_e2e_monitor_remote.py +0 -0
  120. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
  121. {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
  122. {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
  123. {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
  124. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/__init__.py +0 -0
  125. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_bounded_run.py +0 -0
  126. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_context_enricher_namespacing.py +0 -0
  127. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_fresh_eyes_signal.py +0 -0
  128. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_grace_kill_emission.py +0 -0
  129. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_install_dry_run.py +0 -0
  130. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_monitor_seeded.py +0 -0
  131. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_plugin_detector_loaded.py +0 -0
  132. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_plugin_owned_paths.py +0 -0
  133. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_plugin_real_flow.py +0 -0
  134. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_run_one_round_with_fake_agent.py +0 -0
  135. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_scaffold_presets.py +0 -0
  136. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_serve_loop.py +0 -0
  137. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_substrate_fingerprint.py +0 -0
  138. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/integration/test_transient_error_backoff.py +0 -0
  139. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/__init__.py +0 -0
  140. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_atomic_write_enforced.py +0 -0
  141. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_catalogs.py +0 -0
  142. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_classification_ssot.py +0 -0
  143. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_docs_generated.py +0 -0
  144. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_entry_points_resolve.py +0 -0
  145. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_event_kind_registry.py +0 -0
  146. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_event_kinds_ssot.py +0 -0
  147. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_events_doc_contract.py +0 -0
  148. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_layer_2_loop_size.py +0 -0
  149. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_module_sizes.py +0 -0
  150. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_no_ai_signatures.py +0 -0
  151. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
  152. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_peek_schema_version.py +0 -0
  153. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
  154. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_round_result_stable.py +0 -0
  155. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
  156. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/invariants/test_upstream_schema_canary.py +0 -0
  157. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/literate/__init__.py +0 -0
  158. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/literate/parser.py +0 -0
  159. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/literate/test_parser.py +0 -0
  160. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/literate/test_quickstart.py +0 -0
  161. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/__init__.py +0 -0
  162. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_agent_runtime.py +0 -0
  163. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_agent_runtime_grace.py +0 -0
  164. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_agent_runtime_progress.py +0 -0
  165. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_assemble_prompt.py +0 -0
  166. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_events_stream.py +0 -0
  167. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_install.py +0 -0
  168. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_observation.py +0 -0
  169. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_read_round_num.py +0 -0
  170. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_resolve_phase.py +0 -0
  171. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_api_service.py +0 -0
  172. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_auto_stop_gating.py +0 -0
  173. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_claude_error_detector.py +0 -0
  174. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_cli.py +0 -0
  175. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_cli_common.py +0 -0
  176. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_cli_init_install.py +0 -0
  177. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_cli_monitor_http.py +0 -0
  178. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_cli_service_peek_monitor.py +0 -0
  179. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_cli_upgrade.py +0 -0
  180. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_config.py +0 -0
  181. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_config_fresh_eyes.py +0 -0
  182. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_config_max_rounds.py +0 -0
  183. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_config_stop_file.py +0 -0
  184. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
  185. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_config_transient_error_action.py +0 -0
  186. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_context_store.py +0 -0
  187. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_defenses.py +0 -0
  188. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_detector_helpers.py +0 -0
  189. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_detector_protocol.py +0 -0
  190. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_docgen.py +0 -0
  191. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_events.py +0 -0
  192. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_fresh_eyes_trigger.py +0 -0
  193. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_gemini_plugin.py +0 -0
  194. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_hook_failure_isolation.py +0 -0
  195. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_hooks.py +0 -0
  196. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_http_progress.py +0 -0
  197. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_init_entry_points.py +0 -0
  198. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_lifecycle.py +0 -0
  199. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_metrics.py +0 -0
  200. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_monitor_assembly.py +0 -0
  201. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
  202. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
  203. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_monitor_detectors.py +0 -0
  204. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_monitor_remote.py +0 -0
  205. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_peek_argparse.py +0 -0
  206. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_presets.py +0 -0
  207. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_prompt_loader.py +0 -0
  208. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_round_log_helpers.py +0 -0
  209. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_round_view.py +0 -0
  210. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_runner.py +0 -0
  211. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_runner_throttle.py +0 -0
  212. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_scaffold.py +0 -0
  213. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_serve_cmd_bounded.py +0 -0
  214. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_serve_round_log.py +0 -0
  215. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_serve_sentinel.py +0 -0
  216. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_serve_startup_hooks.py +0 -0
  217. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_service_unit.py +0 -0
  218. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_startup_check.py +0 -0
  219. {cli_agent_runner-0.1.33 → cli_agent_runner-0.1.34}/tests/unit/test_substrate.py +0 -0
  220. {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.33
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
- ## 13 verbs
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
- ## 13 verbs
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.33'
22
- __version_tuple__ = version_tuple = (0, 1, 33)
21
+ __version__ = version = '0.1.34'
22
+ __version_tuple__ = version_tuple = (0, 1, 34)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -56,6 +56,7 @@ class SystemMetrics:
56
56
  disk_free_gb: float = 0.0
57
57
  load_1m: float | None = None
58
58
  cpu_pct: float | None = None
59
+ agent_process_count: int = 0 # 0.1.34 — pgrep -xc <agent_binary>, host-wide
59
60
 
60
61
 
61
62
  @dataclass(frozen=True)
@@ -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
- metrics.log_metrics(log_dir, event="round_start", round_num=round_num, phase=phase)
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(log_dir, event="round_end", round_num=round_num, phase=phase)
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.