cli-agent-runner 0.3.8__tar.gz → 0.3.9__tar.gz

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