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

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