cli-agent-runner 0.2.15__tar.gz → 0.2.16__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 (339) hide show
  1. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/.vulture-whitelist.py +4 -0
  2. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/CHANGELOG.md +19 -0
  3. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/PKG-INFO +2 -2
  4. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/README.md +1 -1
  5. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/_emit.py +143 -3
  6. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/_serve_policy.py +62 -0
  7. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/_throttle.py +105 -0
  8. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/_version.py +2 -2
  9. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/api.py +6 -0
  10. cli_agent_runner-0.2.16/agent_runner/cli/_serve_round.py +442 -0
  11. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/cli/serve_cmd.py +57 -263
  12. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/config/models.py +15 -0
  13. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/config/parsers.py +17 -0
  14. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/config/validators.py +12 -0
  15. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/defenses.py +23 -2
  16. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/events.py +5 -0
  17. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/host_health.py +14 -9
  18. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/metrics.py +108 -0
  19. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/round_log.py +10 -0
  20. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/service_unit.py +7 -3
  21. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/architecture.md +15 -7
  22. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/commands.md +1 -1
  23. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/configuration.md +5 -1
  24. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/migrations/0.2.md +76 -0
  25. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/runbook.md +54 -22
  26. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/contract/test_public_api_surface.py +8 -0
  27. cli_agent_runner-0.2.16/tests/integration/test_spawn_round_mem_floor.py +441 -0
  28. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_architecture.py +107 -23
  29. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_event_kinds_ssot.py +7 -1
  30. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_module_boundaries.py +1 -1
  31. cli_agent_runner-0.2.16/tests/unit/test_cgroup_probe.py +233 -0
  32. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_config.py +75 -0
  33. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_defenses.py +2 -2
  34. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_host_health.py +13 -0
  35. cli_agent_runner-0.2.16/tests/unit/test_serve_crash_loop.py +640 -0
  36. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_serve_mem_gate.py +25 -1
  37. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_serve_policy.py +42 -0
  38. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_service_unit.py +27 -2
  39. cli_agent_runner-0.2.15/tests/integration/test_spawn_round_mem_floor.py +0 -224
  40. cli_agent_runner-0.2.15/tests/unit/test_serve_crash_loop.py +0 -226
  41. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/.codecov.yml +0 -0
  42. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  43. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  44. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  45. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  46. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/.github/workflows/ci.yml +0 -0
  47. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/.github/workflows/release.yml +0 -0
  48. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/.gitignore +0 -0
  49. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/BACKLOG.md +0 -0
  50. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/CODE_OF_CONDUCT.md +0 -0
  51. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/CONTRIBUTING.md +0 -0
  52. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/LICENSE +0 -0
  53. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/README.zh.md +0 -0
  54. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/SECURITY.md +0 -0
  55. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/__init__.py +0 -0
  56. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/_docgen.py +0 -0
  57. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/_monitor_detectors.py +0 -0
  58. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/_monitor_registry.py +0 -0
  59. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/_monitor_state.py +0 -0
  60. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/_redact.py +0 -0
  61. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/_registry.py +0 -0
  62. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/_resolve.py +0 -0
  63. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/_round_support.py +0 -0
  64. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/_substrate.py +0 -0
  65. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/agent_runtime.py +0 -0
  66. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/api_types.py +0 -0
  67. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/builtin_plugins/__init__.py +0 -0
  68. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/builtin_plugins/_constants.py +0 -0
  69. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/builtin_plugins/claude_rate_limit.py +0 -0
  70. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/builtin_plugins/codewhale.py +0 -0
  71. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/builtin_plugins/default_dirty_handler.py +0 -0
  72. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/builtin_plugins/gemini.py +0 -0
  73. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/builtin_plugins/kimi.py +0 -0
  74. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/builtin_plugins/pi.py +0 -0
  75. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/cli/__init__.py +0 -0
  76. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/cli/__main__.py +0 -0
  77. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/cli/common.py +0 -0
  78. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/cli/events_cmd.py +0 -0
  79. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/cli/init_cmd.py +0 -0
  80. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/cli/install_cmd.py +0 -0
  81. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/cli/migrate_cmd.py +0 -0
  82. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/cli/monitor_cmd.py +0 -0
  83. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/cli/peek_cmd.py +0 -0
  84. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/cli/round_cmd.py +0 -0
  85. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/cli/service_cmd.py +0 -0
  86. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/cli/upgrade_cmd.py +0 -0
  87. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/clock.py +0 -0
  88. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/config/__init__.py +0 -0
  89. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/config/errors.py +0 -0
  90. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/config/loader.py +0 -0
  91. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/context_store.py +0 -0
  92. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/hooks.py +0 -0
  93. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/http_progress.py +0 -0
  94. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/lifecycle.py +0 -0
  95. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/migrations.py +0 -0
  96. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/monitor.py +0 -0
  97. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/phase_select.py +0 -0
  98. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/presets/__init__.py +0 -0
  99. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/presets/aider.toml +0 -0
  100. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/presets/claude.toml +0 -0
  101. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/presets/codewhale.toml +0 -0
  102. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/presets/gemini.toml +0 -0
  103. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/presets/kimi.toml +0 -0
  104. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/presets/pi.toml +0 -0
  105. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/prompt_loader.py +0 -0
  106. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/remote_relay.py +0 -0
  107. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/round_view.py +0 -0
  108. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/runner.py +0 -0
  109. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/scaffold.py +0 -0
  110. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/schedule.py +0 -0
  111. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/startup_check.py +0 -0
  112. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/agent_runner/vcs_state.py +0 -0
  113. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/build.sh +0 -0
  114. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/README.md +0 -0
  115. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/events.md +0 -0
  116. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/long-running-agents.md +0 -0
  117. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/plugins.md +0 -0
  118. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/quickstart.md +0 -0
  119. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/recipes/aider.md +0 -0
  120. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/recipes/codewhale.md +0 -0
  121. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/recipes/kimi.md +0 -0
  122. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/recipes/pi.md +0 -0
  123. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/docs/thesis.md +0 -0
  124. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/pyproject.toml +0 -0
  125. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/__init__.py +0 -0
  126. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/_clock.py +0 -0
  127. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/_test_helpers.py +0 -0
  128. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/conftest.py +0 -0
  129. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/contract/__init__.py +0 -0
  130. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/e2e/__init__.py +0 -0
  131. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/e2e/conftest.py +0 -0
  132. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/e2e/test_e2e_graceful_stop.py +0 -0
  133. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/e2e/test_e2e_install_systemd.py +0 -0
  134. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/e2e/test_e2e_monitor_remote.py +0 -0
  135. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
  136. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/fixtures/cli-real-output/claude-2.1.143-assistant-tool-use.jsonl +0 -0
  137. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/fixtures/cli-real-output/claude-2.1.143-result-event.jsonl +0 -0
  138. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/fixtures/cli-real-output/gemini-0.42.0-result-event.jsonl +0 -0
  139. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/generate_vulture_whitelist.py +0 -0
  140. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/__init__.py +0 -0
  141. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_auto_stop_e2e.py +0 -0
  142. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_bounded_run.py +0 -0
  143. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_context_enricher_namespacing.py +0 -0
  144. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_dirty_handler_seam.py +0 -0
  145. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_fresh_eyes_signal.py +0 -0
  146. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_grace_kill_emission.py +0 -0
  147. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_install_dry_run.py +0 -0
  148. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_monitor_seeded.py +0 -0
  149. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_plugin_detector_loaded.py +0 -0
  150. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_plugin_owned_paths.py +0 -0
  151. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_plugin_real_flow.py +0 -0
  152. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_prompt_delivery_stdin.py +0 -0
  153. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_run_one_round_with_fake_agent.py +0 -0
  154. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_scaffold_presets.py +0 -0
  155. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_serve_loop.py +0 -0
  156. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_spawn_round_wedged.py +0 -0
  157. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_substrate_fingerprint.py +0 -0
  158. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/integration/test_transient_error_backoff.py +0 -0
  159. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/__init__.py +0 -0
  160. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/_docs.py +0 -0
  161. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/_event_scan.py +0 -0
  162. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_atomic_write_enforced.py +0 -0
  163. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_authored_facts_declared.py +0 -0
  164. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_auto_stop_policy_ssot.py +0 -0
  165. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_catalogs.py +0 -0
  166. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_classification_ssot.py +0 -0
  167. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_config_error_consistency.py +0 -0
  168. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_config_value_set_ssot.py +0 -0
  169. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_dataclass_fields_have_producers.py +0 -0
  170. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_doc_builtin_plugins.py +0 -0
  171. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_doc_claims_match_ssot.py +0 -0
  172. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_doc_cli_claims.py +0 -0
  173. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_doc_operator_surface.py +0 -0
  174. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_doc_test_pointers_exist.py +0 -0
  175. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_doc_transient_error_claims.py +0 -0
  176. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_doc_usage_event_fields.py +0 -0
  177. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_docs_generated.py +0 -0
  178. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_docs_index_complete.py +0 -0
  179. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_entry_points_resolve.py +0 -0
  180. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_event_int_coercion.py +0 -0
  181. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_event_kind_registry.py +0 -0
  182. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_events_doc_contract.py +0 -0
  183. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_gen_blocks_name_their_source.py +0 -0
  184. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_hook_contract_docs.py +0 -0
  185. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_layer_2_loop_size.py +0 -0
  186. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_migrations_registry_ssot.py +0 -0
  187. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_module_sizes.py +0 -0
  188. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_monitor_self_exclusion.py +0 -0
  189. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_no_ai_signatures.py +0 -0
  190. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_no_internal_refs.py +0 -0
  191. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
  192. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_no_raw_time.py +0 -0
  193. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_no_vacuous_invariants.py +0 -0
  194. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_peek_schema_version.py +0 -0
  195. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_phase_select_stateless.py +0 -0
  196. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_plugins_config_stable.py +0 -0
  197. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_readme_zh_is_pointer.py +0 -0
  198. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
  199. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_round_log_decode_boundary.py +0 -0
  200. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_round_num_single_source.py +0 -0
  201. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_round_result_stable.py +0 -0
  202. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_set_diff_for_auto_tool_classification.py +0 -0
  203. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
  204. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_timeout_budget_invariant.py +0 -0
  205. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_upstream_schema_canary.py +0 -0
  206. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/invariants/test_vulture_whitelist_generated.py +0 -0
  207. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/literate/__init__.py +0 -0
  208. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/literate/parser.py +0 -0
  209. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/literate/test_parser.py +0 -0
  210. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/literate/test_quickstart.py +0 -0
  211. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/__init__.py +0 -0
  212. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_agent_runtime.py +0 -0
  213. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_agent_runtime_grace.py +0 -0
  214. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_agent_runtime_marker_scan.py +0 -0
  215. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_agent_runtime_progress.py +0 -0
  216. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_agent_runtime_reap.py +0 -0
  217. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_agent_runtime_signal_name.py +0 -0
  218. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_api_assemble_prompt.py +0 -0
  219. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_api_events_stream.py +0 -0
  220. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_api_install.py +0 -0
  221. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_api_observation.py +0 -0
  222. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_api_read_round_num.py +0 -0
  223. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_api_resolve_phase.py +0 -0
  224. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_api_service.py +0 -0
  225. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_api_types.py +0 -0
  226. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_auto_stop_gating.py +0 -0
  227. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_catalogs.py +0 -0
  228. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_claude_error_detector.py +0 -0
  229. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_claude_foreign_schema.py +0 -0
  230. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_cli.py +0 -0
  231. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_cli_common.py +0 -0
  232. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_cli_init_install.py +0 -0
  233. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_cli_main_boundary.py +0 -0
  234. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_cli_monitor_http.py +0 -0
  235. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_cli_service_peek_monitor.py +0 -0
  236. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_cli_upgrade.py +0 -0
  237. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_codewhale_code_precedence.py +0 -0
  238. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_codewhale_plugin.py +0 -0
  239. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_coerce_int_poison.py +0 -0
  240. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_config_fresh_eyes.py +0 -0
  241. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_config_max_rounds.py +0 -0
  242. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_config_nested_strictness.py +0 -0
  243. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_config_prompt_delivery.py +0 -0
  244. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_config_schedule.py +0 -0
  245. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_config_stop_file.py +0 -0
  246. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_config_strictness.py +0 -0
  247. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
  248. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_config_transient_error_action.py +0 -0
  249. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_context_store.py +0 -0
  250. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_control_char_config.py +0 -0
  251. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_default_dirty_handler.py +0 -0
  252. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_detect_hung_latch.py +0 -0
  253. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_detector_isolation.py +0 -0
  254. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_detector_protocol.py +0 -0
  255. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_dirty_handlers.py +0 -0
  256. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_docgen.py +0 -0
  257. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_effective_throttle_view.py +0 -0
  258. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_emit_config_migrated.py +0 -0
  259. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_emit_schedule.py +0 -0
  260. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_emit_schedule_phase_skipped.py +0 -0
  261. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_events.py +0 -0
  262. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_events_cmd.py +0 -0
  263. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_events_cmd_rollover.py +0 -0
  264. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_events_cmd_tail.py +0 -0
  265. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_events_jsonl_decode.py +0 -0
  266. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_example_dirty_handler.py +0 -0
  267. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_example_plugin.py +0 -0
  268. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_exit_cause.py +0 -0
  269. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_exit_classifier.py +0 -0
  270. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_fresh_eyes_trigger.py +0 -0
  271. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_gemini_plugin.py +0 -0
  272. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_git_timeout.py +0 -0
  273. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_hook_failure_isolation.py +0 -0
  274. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_hook_redact_order.py +0 -0
  275. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_hooks.py +0 -0
  276. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_http_progress.py +0 -0
  277. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_init_entry_points.py +0 -0
  278. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_interruptible_sleep_should_stop.py +0 -0
  279. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_kimi_plugin.py +0 -0
  280. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_lifecycle.py +0 -0
  281. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_metrics.py +0 -0
  282. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_migrate_cmd.py +0 -0
  283. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_migrations.py +0 -0
  284. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_monitor_assembly.py +0 -0
  285. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_monitor_cmd_severity.py +0 -0
  286. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_monitor_dedup_identity.py +0 -0
  287. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_monitor_dedup_rearm.py +0 -0
  288. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
  289. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
  290. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
  291. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_monitor_detectors.py +0 -0
  292. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_monitor_event_tail.py +0 -0
  293. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_monitor_remote.py +0 -0
  294. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_monitor_round_deferred_stale.py +0 -0
  295. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_monitor_schedule_pause.py +0 -0
  296. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_network_blip_signal_guard.py +0 -0
  297. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_open_round_log.py +0 -0
  298. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_orphan_chain_single_pass.py +0 -0
  299. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_outer_round_ceiling.py +0 -0
  300. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_peek_argparse.py +0 -0
  301. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_peek_project_isolation.py +0 -0
  302. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_peek_schedule.py +0 -0
  303. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_peek_select.py +0 -0
  304. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_phase_agent_launch.py +0 -0
  305. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_phase_profiles.py +0 -0
  306. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_phase_select.py +0 -0
  307. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_pi_plugin.py +0 -0
  308. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_plugin_constants.py +0 -0
  309. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_presets.py +0 -0
  310. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_primary_prompt_file_home.py +0 -0
  311. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_prompt_loader.py +0 -0
  312. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_read_json_hardening.py +0 -0
  313. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_redact.py +0 -0
  314. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_resolve.py +0 -0
  315. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_round_cmd.py +0 -0
  316. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_round_cmd_sigterm.py +0 -0
  317. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_round_log_helpers.py +0 -0
  318. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_round_view.py +0 -0
  319. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_runner.py +0 -0
  320. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_runner_sidecar_ordering.py +0 -0
  321. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_runner_throttle.py +0 -0
  322. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_scaffold.py +0 -0
  323. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_schedule.py +0 -0
  324. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_serve_cmd_bounded.py +0 -0
  325. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_serve_config_broken.py +0 -0
  326. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_serve_phase_schedule.py +0 -0
  327. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_serve_round_log.py +0 -0
  328. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_serve_schedule_gate.py +0 -0
  329. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_serve_sentinel.py +0 -0
  330. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_serve_startup_hooks.py +0 -0
  331. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_service_mode.py +0 -0
  332. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_startup_battery_exit.py +0 -0
  333. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_startup_check.py +0 -0
  334. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_substrate.py +0 -0
  335. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_supervisor_stale_self_blind.py +0 -0
  336. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_throttle_back_off_ladder.py +0 -0
  337. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_throttle_plugin_classification.py +0 -0
  338. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_upgrade_migrate.py +0 -0
  339. {cli_agent_runner-0.2.15 → cli_agent_runner-0.2.16}/tests/unit/test_vcs_state.py +0 -0
@@ -150,6 +150,10 @@ disk_warning_pct
150
150
  disk_critical_pct
151
151
  swap_sout_noise_floor_mb
152
152
  mem_free_low_mb
153
+ psi_full_avg10_critical
154
+ psi_some_avg10_warning
155
+ mem_critical_consecutive_samples
156
+ in_round_mem_terminate
153
157
 
154
158
  # agent_runner.config.models.PhaseOverride
155
159
  round_timeout_s
@@ -5,6 +5,25 @@ 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.2.16] - 2026-09-05
9
+
10
+ ### Added
11
+ - `[monitor.host_health]` gains PSI knobs — `psi_full_avg10_critical` (default 60.0) and `psi_some_avg10_warning` (default 5.0) — plus `mem_critical_consecutive_samples` (default 3) and an `in_round_mem_terminate` off switch for the mid-round memory floor.
12
+ - `serve` probes this process's cgroup v2 memory budget at startup (`host_cgroup_memory_limit`); when `memory.max` and `memory.swap.max` are both finite, the mid-round floor defers to kernel cgroup-OOM (`mem_pressure_deferred_to_cgroup`) instead of terminating the round itself.
13
+ - `mem_loop` now escalates across restarts: recurring 3 times inside a rolling 2h window stops the service for real (`mem_loop_persistent`, exit `70`) instead of respawning into the same loop forever.
14
+ - New exit-0 no-progress breaker: 5 consecutive fast, clean rounds with no recorded usage (a CLI that exits 0 without ever reaching the model) now stop `serve` (`stalled_no_progress`, exit `75`) instead of spinning invisibly.
15
+
16
+ ### Fixed
17
+ - PSI-full critical was a single ≥1% momentary sample — a hiccup, not a coma — on every PSI-capable host. It now requires a sustained ≥60% avg10 held for `mem_critical_consecutive_samples` (default 3, ~30s) consecutive checks.
18
+ - The mid-round swap-out check is now per-tick instead of cumulative since round-start, so a host that recovers mid-round is no longer permanently flagged for the rest of the round.
19
+ - The pre-round admission gate now defers only on `critical` pressure, not on any pressure (a mere `warning`, e.g. ordinary swap churn, no longer delays the next round).
20
+
21
+ ### Notes
22
+ - New events: `host_cgroup_memory_limit`, `mem_pressure_deferred_to_cgroup`, `mem_loop_persistent`, `stalled_no_progress`, `round_mem_critical_sample`; `round_mem_terminated` gained `consecutive`/`context` fields. Not breaking: no `agent-runner migrate` needed, `peek --json` schema is unchanged.
23
+ - Re-render the systemd unit (`agent-runner install`) after upgrading: the new unit adds exit `70` to `RestartPreventExitStatus` so `mem_loop_persistent` stops the service instead of restarting into the same loop.
24
+
25
+ See `docs/migrations/0.2.md`.
26
+
8
27
  ## [0.2.15] - 2026-09-04
9
28
 
10
29
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cli-agent-runner
3
- Version: 0.2.15
3
+ Version: 0.2.16
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
@@ -94,7 +94,7 @@ Verb reference: [`docs/commands.md`](docs/commands.md).
94
94
 
95
95
  ## Defenses (built in)
96
96
 
97
- 14 named defenses, structured as data — see `agent-runner peek --select defenses`.
97
+ 15 named defenses, structured as data — see `agent-runner peek --select defenses`.
98
98
  Each carries the historical incident it codifies and the invariant test that
99
99
  guards it. Highlights:
100
100
 
@@ -58,7 +58,7 @@ Verb reference: [`docs/commands.md`](docs/commands.md).
58
58
 
59
59
  ## Defenses (built in)
60
60
 
61
- 14 named defenses, structured as data — see `agent-runner peek --select defenses`.
61
+ 15 named defenses, structured as data — see `agent-runner peek --select defenses`.
62
62
  Each carries the historical incident it codifies and the invariant test that
63
63
  guards it. Highlights:
64
64
 
@@ -19,13 +19,17 @@ __all__ = [
19
19
  "emit_anomaly_repetitive_tool",
20
20
  "emit_config_migrated",
21
21
  "emit_fresh_eyes_round_triggered",
22
+ "emit_host_cgroup_memory_limit",
22
23
  "emit_max_rounds_reached",
23
24
  "emit_mem_loop",
25
+ "emit_mem_loop_persistent",
26
+ "emit_mem_pressure_deferred_to_cgroup",
24
27
  "emit_rate_limit_stop",
25
28
  "emit_round_deferred",
26
29
  "emit_round_grace_extended",
27
30
  "emit_round_grace_kill",
28
31
  "emit_round_logs_prune_deferred",
32
+ "emit_round_mem_critical_sample",
29
33
  "emit_round_mem_terminated",
30
34
  "emit_round_progress",
31
35
  "emit_round_resumed",
@@ -36,6 +40,7 @@ __all__ = [
36
40
  "emit_schedule_phase_skipped",
37
41
  "emit_schedule_resumed",
38
42
  "emit_stale_index_lock_cleared",
43
+ "emit_stalled_no_progress",
39
44
  "emit_stop_file_detected",
40
45
  "emit_transient_error_backoff_capped",
41
46
  "emit_transient_error_detected",
@@ -110,6 +115,60 @@ def emit_mem_loop(log_dir: Path, *, consecutive: int, exit_code: int, log_path:
110
115
  _emit_giveup(log_dir, MEM_LOOP, consecutive=consecutive, exit_code=exit_code, log_path=log_path)
111
116
 
112
117
 
118
+ def emit_mem_loop_persistent(
119
+ log_dir: Path, *, consecutive: int, exit_code: int, log_path: Path
120
+ ) -> None:
121
+ """Emit mem_loop_persistent (serve STOPS for real — 0.2.16 Task 5 cross-restart
122
+ convergence: mem_loop itself kept recurring across restarts within the
123
+ escalation window, so systemd is told to stop rather than respawn into the
124
+ identical loop forever). Distinct from mem_loop: this IS a deliberate stop,
125
+ like crash_loop/config_broken, so it belongs in the unit's
126
+ RestartPreventExitStatus.
127
+
128
+ ``consecutive`` counts mem_loop EPISODES within the persistence window
129
+ (up to ``_serve_policy._MEM_LOOP_PERSIST_THRESHOLD``) — NOT mid-round
130
+ terminations within one episode, which is mem_loop's own ``consecutive``
131
+ (up to ``MEM_LOOP_THRESHOLD``). Same redacted-log-tail reason capture as
132
+ crash_loop/mem_loop."""
133
+ from agent_runner.events import MEM_LOOP_PERSISTENT
134
+
135
+ _emit_giveup(
136
+ log_dir,
137
+ MEM_LOOP_PERSISTENT,
138
+ consecutive=consecutive,
139
+ exit_code=exit_code,
140
+ log_path=log_path,
141
+ )
142
+
143
+
144
+ def emit_stalled_no_progress(
145
+ log_dir: Path, *, consecutive: int, exit_code: int, log_path: Path
146
+ ) -> None:
147
+ """Emit stalled_no_progress (serve gave up after consecutive clean-but-
148
+ no-progress rounds -- 0.2.16 Task 6). A round that exits 0 fast with no
149
+ ``agent_usage_recorded`` never reached the model (pi, and CLIs like it,
150
+ exit 0 on a provider failure) -- ``_round_ok = exit_code == 0``
151
+ (api_types.py) reads that as clean, so without this breaker it is a fast,
152
+ invisible, unclassified-failure spin, no different in kind from an
153
+ unknown short crash except for the exit code it hides behind.
154
+
155
+ Deliberately reuses ``CRASH_LOOP_EXIT`` (75), not a new exit code: this is
156
+ the SAME give-up verdict as crash_loop ("an unknown failure kept
157
+ recurring, stop for real") reached via a different signal (no usage
158
+ instead of a non-zero exit) -- not a new failure class, so it needs no new
159
+ entry in the systemd unit's ``RestartPreventExitStatus``. Same redacted-
160
+ log-tail reason capture as crash_loop/mem_loop."""
161
+ from agent_runner.events import STALLED_NO_PROGRESS
162
+
163
+ _emit_giveup(
164
+ log_dir,
165
+ STALLED_NO_PROGRESS,
166
+ consecutive=consecutive,
167
+ exit_code=exit_code,
168
+ log_path=log_path,
169
+ )
170
+
171
+
113
172
  def emit_config_migrated(
114
173
  log_dir: Path, *, applied: list[str], manual: list[str], path: str
115
174
  ) -> None:
@@ -202,15 +261,96 @@ def emit_round_resumed(log_dir: Path, *, deferred_for_s: int) -> None:
202
261
 
203
262
 
204
263
  def emit_round_mem_terminated(
205
- log_dir: Path, *, pid: int, severity: str, signal: str, message: str
264
+ log_dir: Path,
265
+ *,
266
+ pid: int,
267
+ severity: str,
268
+ signal: str,
269
+ message: str,
270
+ consecutive: int,
271
+ context: dict,
206
272
  ) -> None:
207
273
  """Emit when _spawn_round's mid-round hard floor terminated a ballooning
208
274
  round on critical host_health pressure -- the actual coma-preventer (a
209
275
  pre-round-only gate can't stop a single round mid-flight). Distinct from
210
- round_supervisor_wedged (a wall-clock ceiling breach, unrelated cause)."""
276
+ round_supervisor_wedged (a wall-clock ceiling breach, unrelated cause).
277
+
278
+ 0.2.16: ``consecutive`` (the critical_streak that crossed the threshold)
279
+ and ``context`` (Pressure.context -- the actual psi/mem numbers, e.g.
280
+ psi_full_avg10) make the kill legible from the event stream alone, so an
281
+ operator can retune host_health thresholds without SSH."""
211
282
  from agent_runner.events import ROUND_MEM_TERMINATED, emit
212
283
 
213
- emit(log_dir, ROUND_MEM_TERMINATED, pid=pid, severity=severity, signal=signal, message=message)
284
+ emit(
285
+ log_dir,
286
+ ROUND_MEM_TERMINATED,
287
+ pid=pid,
288
+ severity=severity,
289
+ signal=signal,
290
+ message=message,
291
+ consecutive=consecutive,
292
+ context=context,
293
+ )
294
+
295
+
296
+ def emit_round_mem_critical_sample(
297
+ log_dir: Path, *, round_num: int, consecutive: int, context: dict
298
+ ) -> None:
299
+ """Emit on EACH critical host_health sample inside _spawn_round's mid-round
300
+ hard floor -- deliberately NOT deduped (unlike round_mem_terminated): the
301
+ point is calibration visibility into near-misses, so an operator watching
302
+ the event stream sees the critical_streak build (1, 2, ...) even on ticks
303
+ that never reach the terminate threshold (a healthy tick resets it before
304
+ 3-in-a-row). ``context`` carries the same Pressure.context numbers as
305
+ round_mem_terminated. Fires only during critical pressure -- never during
306
+ warning/healthy -- so it adds no normal-operation noise, including while
307
+ ``defer_to_cgroup`` steps back from terminating (the streak/context is
308
+ still useful calibration there; this event stays scoped to the
309
+ sample-level signal, distinct from mem_pressure_deferred_to_cgroup's
310
+ once-per-episode terminate-vs-defer notice)."""
311
+ from agent_runner.events import ROUND_MEM_CRITICAL_SAMPLE, emit
312
+
313
+ emit(
314
+ log_dir,
315
+ ROUND_MEM_CRITICAL_SAMPLE,
316
+ round_num=round_num,
317
+ consecutive=consecutive,
318
+ context=context,
319
+ )
320
+
321
+
322
+ def emit_host_cgroup_memory_limit(
323
+ log_dir: Path, *, memory_max: int | None, memory_swap_max: int | None, cgroup_path: str | None
324
+ ) -> None:
325
+ """Emit once at serve startup: this process's cgroup v2 memory budget
326
+ (``metrics.cgroup_memory_limits``). ``None`` fields mean unlimited (or
327
+ cgroup v2 unavailable). Serve uses this once to decide whether the
328
+ mid-round hard floor can defer to kernel cgroup-OOM -- see
329
+ ``emit_mem_pressure_deferred_to_cgroup`` below."""
330
+ from agent_runner.events import HOST_CGROUP_MEMORY_LIMIT, emit
331
+
332
+ emit(
333
+ log_dir,
334
+ HOST_CGROUP_MEMORY_LIMIT,
335
+ memory_max=memory_max,
336
+ memory_swap_max=memory_swap_max,
337
+ cgroup_path=cgroup_path,
338
+ )
339
+
340
+
341
+ def emit_mem_pressure_deferred_to_cgroup(
342
+ log_dir: Path, *, pid: int, signal: str, message: str
343
+ ) -> None:
344
+ """Emit when _spawn_round's mid-round hard floor hits sustained critical
345
+ pressure but this cgroup's (mem+swap) budget is bounded end to end
346
+ (both memory.max and memory.swap.max finite) -- kernel cgroup-OOM will
347
+ contain the agent and keep the host responsive on its own, so the
348
+ cruder host-wide round-kill steps back instead of firing
349
+ round_mem_terminated. This OVERRIDES in_round_mem_terminate=True: a
350
+ bounded cgroup makes the host floor strictly worse, not just redundant."""
351
+ from agent_runner.events import MEM_PRESSURE_DEFERRED_TO_CGROUP, emit
352
+
353
+ emit(log_dir, MEM_PRESSURE_DEFERRED_TO_CGROUP, pid=pid, signal=signal, message=message)
214
354
 
215
355
 
216
356
  def emit_round_substrate_before(
@@ -54,6 +54,46 @@ MEM_LOOP_EXIT = 71
54
54
  # Consecutive mem-terminated rounds before serve gives up (break-then-restart).
55
55
  MEM_LOOP_THRESHOLD = 5
56
56
 
57
+ # Exit code for mem_loop's cross-restart escalation (0.2.16 Task 5 — field
58
+ # confirmed: NRestarts climbs, never converges). MEM_LOOP_EXIT (71) alone
59
+ # resets on every process restart, so a host stuck in sustained pressure
60
+ # respawns into the identical break-then-restart loop forever. Once mem_loop
61
+ # itself keeps recurring across restarts (see _MEM_LOOP_PERSIST_THRESHOLD),
62
+ # this is a DELIBERATE stop instead — "gave up for real, needs a human" — so,
63
+ # unlike 71, it IS listed in the unit's RestartPreventExitStatus. 70 =
64
+ # EX_SOFTWARE (sysexits); verified free (grep -rnE "= 70\b|code=70\b").
65
+ MEM_LOOP_PERSISTENT_EXIT = 70
66
+
67
+ # Escalation window/threshold for the cross-restart convergence above: prior
68
+ # mem_loop events are counted only within the last _MEM_LOOP_PERSIST_WINDOW_S
69
+ # seconds (2h) of "now" — old episodes age out on their own, so a host that
70
+ # stays healthy for a sustained window earns a fresh restartable 71 again
71
+ # instead of ratcheting toward the persistent stop forever (no state file:
72
+ # purely events-tail derived, restart-safe by construction). Once this run's
73
+ # mem_loop would be the _MEM_LOOP_PERSIST_THRESHOLD-th (3rd) occurrence inside
74
+ # the window (~2 prior + this one, ⇒ ~15 mem-terminated kills total), serve
75
+ # escalates to MEM_LOOP_PERSISTENT_EXIT instead of the usual 71.
76
+ _MEM_LOOP_PERSIST_WINDOW_S = 7200
77
+ _MEM_LOOP_PERSIST_THRESHOLD = 3
78
+
79
+
80
+ # Exit-0 no-progress breaker (0.2.16 Task 6). Some CLIs (pi -- see
81
+ # builtin_plugins/pi.py's "pi exits 0 on provider failure") exit 0 on a
82
+ # provider failure that never reaches the model: `_round_ok = exit_code == 0`
83
+ # (api_types.py) reads that as a clean round, so without this breaker an
84
+ # invalid credential (or an exhausted-retries outage) spins as a fast,
85
+ # invisible "success" loop with no breaker, no back-off, and no alert -- on a
86
+ # constrained host, that tight loop is itself a memory-pressure generator.
87
+ # "No progress" = exit 0, a SHORT round (< _NO_PROGRESS_SHORT_S), and no
88
+ # agent_usage_recorded for it (see _throttle.round_had_no_progress for the
89
+ # events-derived scoping). Deliberately reuses CRASH_LOOP_THRESHOLD /
90
+ # CRASH_LOOP_EXIT rather than minting new ones: this is the SAME give-up
91
+ # verdict as the crash-loop breaker ("an unknown failure kept recurring, stop
92
+ # for real"), reached via a different signal (no usage instead of a non-zero
93
+ # exit) -- not a new failure class needing its own systemd
94
+ # RestartPreventExitStatus entry.
95
+ _NO_PROGRESS_SHORT_S = 30
96
+
57
97
 
58
98
  class EnvironmentalError(Exception):
59
99
  """Marks a round-child failure as ENVIRONMENTAL (recoverable) rather than a
@@ -136,6 +176,28 @@ def _mem_loop_decision(
136
176
  return ("continue", consecutive)
137
177
 
138
178
 
179
+ def _no_progress_decision(
180
+ *, no_progress: bool, consecutive: int
181
+ ) -> tuple[Literal["stalled_no_progress", "continue"], int]:
182
+ """Exit-0 no-progress give-up cap (0.2.16 Task 6) -- a NEW, SEPARATE,
183
+ private counter from ``post_round_decision``'s crash-loop breaker (that one
184
+ keys on ``returncode != 0``; here returncode IS 0, so a round that never
185
+ reached the model would otherwise sail through with no breaker at all).
186
+ Same shape as :func:`_mem_loop_decision`: returns ``("stalled_no_progress",
187
+ consecutive)`` once ``consecutive`` reaches ``CRASH_LOOP_THRESHOLD`` --
188
+ reusing that threshold (and, at the call site, ``CRASH_LOOP_EXIT``) rather
189
+ than minting new ones, since this is the identical give-up verdict reached
190
+ via a different signal. A round WITH progress (usage recorded, non-zero
191
+ exit, or slow) resets the counter to 0, same reset shape as
192
+ crash-loop/mem-loop."""
193
+ if not no_progress:
194
+ return ("continue", 0)
195
+ consecutive += 1
196
+ if consecutive >= CRASH_LOOP_THRESHOLD:
197
+ return ("stalled_no_progress", consecutive)
198
+ return ("continue", consecutive)
199
+
200
+
139
201
  def classify_round_exit(exc: BaseException) -> int:
140
202
  """The round-child exit-code CLASSIFIER (Group A) — permanence, not exception
141
203
  identity, decides serve's response. Replaces the old exception *whitelist*
@@ -22,6 +22,7 @@ from agent_runner.api_types import TransientErrorState
22
22
  from agent_runner.clock import SYSTEM_CLOCK, Clock
23
23
  from agent_runner.events import (
24
24
  AGENT_USAGE_RECORDED,
25
+ MEM_LOOP,
25
26
  ROUND_MEM_TERMINATED,
26
27
  ROUND_SUBSTRATE_BEFORE,
27
28
  TRANSIENT_ERROR_DETECTED,
@@ -37,7 +38,9 @@ __all__ = [
37
38
  "_interruptible_sleep",
38
39
  "compute_adjusted_reset_at",
39
40
  "effective_throttle_view",
41
+ "mem_loop_events_in_window",
40
42
  "pending_recovered",
43
+ "round_had_no_progress",
41
44
  "round_was_mem_terminated",
42
45
  ]
43
46
 
@@ -314,6 +317,108 @@ def round_was_mem_terminated(log_dir: Path) -> bool:
314
317
  return parse_iso_ms(newest_terminated_ts) >= parse_iso_ms(newest_before_ts)
315
318
 
316
319
 
320
+ def round_had_no_progress(
321
+ log_dir: Path,
322
+ *,
323
+ returncode: int,
324
+ duration_s: float,
325
+ threshold_s: float,
326
+ throttle_active: bool = False,
327
+ ) -> bool:
328
+ """True iff the round that JUST ran exited 0, finished fast, but never
329
+ reached the model -- pi (and CLIs like it: see builtin_plugins/pi.py's
330
+ "pi exits 0 on provider failure") can exit 0 on an auth failure or an
331
+ exhausted-retries outage that ``_round_ok = exit_code == 0`` (api_types.py)
332
+ reads as clean, so this needs its own events-derived signal (feeding
333
+ ``_serve_policy._no_progress_decision``'s "clean-but-no-progress" streak)
334
+ exactly parallel to :func:`round_was_mem_terminated`'s mem-terminated one.
335
+
336
+ TWO gates guard the verdict against over-firing on healthy deployments
337
+ (0.2.16 fix-wave CRITICAL #1):
338
+
339
+ 1. ``throttle_active`` (the SAME value ``cmd()`` already computed for
340
+ ``post_round_decision`` -- caller threads it through, never
341
+ recomputed here): a provider outage that exhausts retries and exits 0
342
+ (429/503) is ``transient_error_detected`` and excused from the
343
+ crash-loop breaker; without this gate it would be double-counted here
344
+ as "no progress" and stop the loop instead of riding the back-off.
345
+ 2. Usage-capability: armed ONLY when at least one ``agent_usage_recorded``
346
+ event exists anywhere in the scanned tail. Some CLIs (kimi -- see
347
+ builtin_plugins/kimi.py; aider; any custom ``[agent] command`` with no
348
+ usage-emitting plugin) never emit usage BY DESIGN -- for those, "no
349
+ usage this round" is indistinguishable from "normal for this CLI", so
350
+ arming would stop a perfectly healthy deployment after 5 fast clean
351
+ rounds. If the CLI's plugin stack ever emits usage (pi does on a good
352
+ round), a round with none is genuine no-progress and still trips --
353
+ this makes the breaker CLI-adaptive with no config descriptor.
354
+
355
+ Short-circuits on ``returncode != 0`` or ``duration_s >= threshold_s``
356
+ without touching the events tail: a non-zero exit already has its own
357
+ crash-loop signal, and a slow round (even with no usage) is not a TIGHT
358
+ loop -- a wedged/hung round already has its own signal
359
+ (``round_supervisor_wedged``), so this floor is specifically the fast spin.
360
+
361
+ "Never reached the model" = no ``agent_usage_recorded`` event stamped at
362
+ or after this round's ``round_substrate_before`` -- the CLI plugins only
363
+ emit usage when the round actually consumed tokens (e.g.
364
+ ``builtin_plugins/pi.py``'s ``_aggregate_usage`` returns ``None`` on
365
+ all-zero usage), so an auth failure or exhausted retries leaves no such
366
+ event. Same round-scoping shape as :func:`round_was_mem_terminated`
367
+ (newest-event-timestamp comparison against the newest
368
+ ``round_substrate_before``), with the comparison inverted: no progress
369
+ means the newest usage event is EITHER absent OR older than this round's
370
+ own start."""
371
+ if throttle_active or returncode != 0 or duration_s >= threshold_s:
372
+ return False
373
+ newest_before_ts: str | None = None
374
+ newest_usage_ts: str | None = None
375
+ usage_capable = False
376
+ for path in sorted(log_dir.glob("events-*.jsonl"))[-2:]:
377
+ for ev in _iter_events(path):
378
+ kind = ev.get("event")
379
+ if kind == AGENT_USAGE_RECORDED:
380
+ usage_capable = True
381
+ ts = ev.get("ts")
382
+ if not ts:
383
+ continue
384
+ if kind == ROUND_SUBSTRATE_BEFORE:
385
+ newest_before_ts = ts
386
+ elif kind == AGENT_USAGE_RECORDED:
387
+ newest_usage_ts = ts
388
+ if not usage_capable:
389
+ return False # this CLI/plugin stack has never emitted usage -- can't arm
390
+ if newest_before_ts is None:
391
+ return False
392
+ if newest_usage_ts is None:
393
+ return True
394
+ return parse_iso_ms(newest_usage_ts) < parse_iso_ms(newest_before_ts)
395
+
396
+
397
+ def mem_loop_events_in_window(log_dir: Path, clock: Clock, window_s: int) -> int:
398
+ """Count ``mem_loop`` events in the events tail stamped within the last
399
+ ``window_s`` seconds of ``clock.epoch()`` — events-derived, no state file,
400
+ same tail-reconstruction shape as :func:`round_was_mem_terminated` above.
401
+
402
+ Feeds ``_serve_round.round_outcome_exit_code``'s cross-restart escalation
403
+ (0.2.16 Task 5): ``MEM_LOOP_EXIT`` (71) alone resets on every serve
404
+ process restart, so a host stuck in sustained pressure respawns into the
405
+ identical loop forever (field-confirmed: NRestarts climbs, never
406
+ converges). Counting PRIOR occurrences in a bounded window — rather than
407
+ an all-time total, or a restart-local counter — gives "ages out on its
408
+ own after a sustained-healthy stretch" for free: an old mem_loop episode
409
+ outside the window simply stops counting, no explicit reset needed."""
410
+ cutoff = clock.epoch() - window_s
411
+ count = 0
412
+ for path in sorted(log_dir.glob("events-*.jsonl"))[-2:]:
413
+ for ev in _iter_events(path):
414
+ if ev.get("event") != MEM_LOOP:
415
+ continue
416
+ ts = ev.get("ts")
417
+ if ts and parse_iso_ms(ts).timestamp() >= cutoff:
418
+ count += 1
419
+ return count
420
+
421
+
317
422
  def effective_throttle_view(
318
423
  log_dir: Path, *, clock: Clock = SYSTEM_CLOCK
319
424
  ) -> tuple[TransientErrorState | None, dict[str, TransientErrorState]]:
@@ -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.2.15'
22
- __version_tuple__ = version_tuple = (0, 2, 15)
21
+ __version__ = version = '0.2.16'
22
+ __version_tuple__ = version_tuple = (0, 2, 16)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -29,6 +29,7 @@ from agent_runner._serve_policy import ( # noqa: F401 — public re-export
29
29
  CRASH_LOOP_THRESHOLD,
30
30
  ENV_BATTERY_EXIT,
31
31
  MEM_LOOP_EXIT,
32
+ MEM_LOOP_PERSISTENT_EXIT,
32
33
  PERMANENT_CONFIG_EXIT,
33
34
  post_round_decision,
34
35
  )
@@ -786,13 +787,17 @@ from agent_runner._emit import ( # noqa: E402,F401 — intentional bottom re-ex
786
787
  emit_config_migrated,
787
788
  emit_crash_loop,
788
789
  emit_fresh_eyes_round_triggered,
790
+ emit_host_cgroup_memory_limit,
789
791
  emit_max_rounds_reached,
790
792
  emit_mem_loop,
793
+ emit_mem_loop_persistent,
794
+ emit_mem_pressure_deferred_to_cgroup,
791
795
  emit_rate_limit_stop,
792
796
  emit_round_deferred,
793
797
  emit_round_grace_extended,
794
798
  emit_round_grace_kill,
795
799
  emit_round_logs_prune_deferred,
800
+ emit_round_mem_critical_sample,
796
801
  emit_round_mem_terminated,
797
802
  emit_round_progress,
798
803
  emit_round_resumed,
@@ -803,6 +808,7 @@ from agent_runner._emit import ( # noqa: E402,F401 — intentional bottom re-ex
803
808
  emit_schedule_phase_skipped,
804
809
  emit_schedule_resumed,
805
810
  emit_stale_index_lock_cleared,
811
+ emit_stalled_no_progress,
806
812
  emit_stop_file_detected,
807
813
  emit_transient_error_backoff_capped,
808
814
  emit_transient_error_detected,