hermes-agent/tests
Ivan Tonov 930494d687 fix(cron): reap orphaned MCP stdio subprocesses after each tick
MCP stdio servers are spawned via the SDK's stdio_client, which on
Linux uses start_new_session=True (setsid).  When a cron job is
cancelled mid-way (timeout, agent finish, exception), the subprocess
often escapes the SDK's teardown and survives as a session leader.
Because setsid() detaches the child from the gateway's process group
/ cgroup tree, systemd does not reap it on service restart either —
so every cron tick that touches an MCP tool leaks a dangling server
process.

Fix:

* tools/mcp_tool.py — _run_stdio now wraps the whole stdio+session
  context in try/finally.  On any exit path (clean, exception,
  cancellation), PIDs still alive are moved from the active
  _stdio_pids set into a new _orphan_stdio_pids set.  Orphan
  detection is done via os.kill(pid, 0) — a cheap liveness probe
  that never signals the target.

* tools/mcp_tool.py — _kill_orphaned_mcp_children gains an
  include_active=False flag.  Default behaviour now only reaps the
  orphan set so concurrent sessions (other parallel cron jobs or
  live user chats) are never disrupted.  The existing shutdown path
  passes include_active=True to keep the previous "kill everything"
  semantics after the MCP loop is stopped.

* cron/scheduler.py — the cleanup hook is moved from run_job()'s
  finally (which would race with parallel siblings after #13021)
  into tick() after the ThreadPoolExecutor has joined every future.
  At that point there are no in-flight sessions from this tick, so
  sweeping the orphan set is always safe.

Net effect: zero regression for healthy sessions, and orphan MCP
servers no longer accumulate between gateway restarts.

Made-with: Cursor
2026-04-26 18:21:20 -07:00
..
acp
agent Revert "feat(onboarding): port first-touch hints to the TUI (#16054)" (#16062) 2026-04-26 06:31:37 -07:00
cli fix(cli): wire approvals in background tasks 2026-04-26 12:29:48 -07:00
cron
e2e test(discord): add guild to fake e2e messages 2026-04-25 18:25:56 -07:00
environments/benchmarks
fakes
gateway fix(slack): harden attachment handling 2026-04-26 18:20:17 -07:00
hermes_cli fix(cron): keep homeassistant toolset enabled when HASS_TOKEN is set (#16208) 2026-04-26 12:55:58 -07:00
hermes_state
honcho_plugin
integration
plugins fix(memory): close embedded Hindsight async client cleanly 2026-04-26 12:54:46 -07:00
run_agent test: update bare-agent helper for live-runtime attrs added by #16099 2026-04-26 12:45:39 -07:00
skills
tools fix(cron): reap orphaned MCP stdio subprocesses after each tick 2026-04-26 18:21:20 -07:00
tui_gateway Revert "feat(onboarding): port first-touch hints to the TUI (#16054)" (#16062) 2026-04-26 06:31:37 -07:00
__init__.py
conftest.py test: blank platform-gating env vars in hermetic fixture 2026-04-26 12:23:20 -07:00
run_interrupt_test.py
test_account_usage.py
test_base_url_hostname.py
test_batch_runner_checkpoint.py
test_cli_file_drop.py
test_cli_skin_integration.py
test_ctx_halving_fix.py
test_empty_model_fallback.py
test_evidence_store.py
test_hermes_constants.py
test_hermes_logging.py
test_hermes_state.py fix(/branch): redirect session_log_file and expose branch sessions in list (#14854) (#16150) 2026-04-26 10:28:19 -07:00
test_honcho_client_config.py
test_ipv4_preference.py
test_mcp_serve.py
test_mini_swe_runner.py
test_minimax_model_validation.py
test_minisweagent_path.py
test_model_picker_scroll.py
test_model_tools_async_bridge.py
test_model_tools.py feat(hooks): add duration_ms to post_tool_call + transform_tool_result (#15429) 2026-04-25 22:13:12 -07:00
test_ollama_num_ctx.py
test_packaging_metadata.py
test_plugin_skills.py
test_project_metadata.py
test_retry_utils.py
test_sql_injection.py
test_subprocess_home_isolation.py
test_timezone.py
test_toolset_distributions.py
test_toolsets.py
test_trajectory_compressor_async.py
test_trajectory_compressor.py
test_transform_tool_result_hook.py
test_tui_gateway_server.py fix(tui): call maybe_auto_title for TUI sessions (#15949) (#16151) 2026-04-26 10:44:22 -07:00
test_utils_truthy_values.py