mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
chore(process): mark the agent/plugin detached spawns that are not owned children, with the reason
The detached-spawn check (scripts/check_detached_spawns.py, from the owned-child API) flags every start_new_session outside spawn_owned. Five sites in this family are deliberately not owned. Each now carries a `detached-spawn: ok — <why>` marker, so the check passes without a baseline entry: - iron-proxy daemon: meant to outlive the CLI that started it; `hermes egress stop` ends it through the pidfile + nonce. - google_meet bot: reached through files across turns and stopped by meet_leave (pid in .active.json). - photon sidecar: exits on stdin EOF (PHOTON_SIDECAR_WATCH_STDIN). kill -9 probe with that exact watch block: 0 of 1 survived; the same probe with the watch off: 1 of 1 survived. - ddgs search worker: awaited under a 30 s cap that kills its group. Blocked reading its request on a stdin pipe, it exits when the owner dies (kill -9 probe: 0 of 1 survived). - secret_sources command helper: awaited under a hard timeout (default 3 s) that kills its group. Comments only; no behaviour change.
This commit is contained in:
1 parent
118712327d
commit
6ff702ec0d
5 files changed
+5
No files matched your search
@@ -734,6 +734,7 @@ def _spawn_daemon(bin_path: Path, cfg: Path, env: dict[str, str], log_path: Path
|
||||
# start_new_session is POSIX-only (Windows isn't supported anyway — no upstream binary).
|
||||
return subprocess.Popen(
|
||||
[str(bin_path), "-config", str(cfg)], env=env, stdin=subprocess.DEVNULL, stdout=log_fd, stderr=subprocess.STDOUT,
|
||||
# detached-spawn: ok — a daemon meant to outlive the CLI that started it; `hermes egress stop` ends it (pidfile + nonce)
|
||||
**({} if platform.system() == "Windows" else {"start_new_session": True}),
|
||||
)
|
||||
except OSError as exc:
|
||||
|
||||
@@ -78,6 +78,7 @@ def _run_helper(command: str, secret_key: str, timeout_seconds: float, max_outpu
|
||||
proc = subprocess.Popen(
|
||||
["/bin/sh", "-c", command], env=env, stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, # stderr captured and DISCARDED — never inherited
|
||||
# detached-spawn: ok — awaited; a hard timeout (default 3 s) kills the whole group
|
||||
start_new_session=True, # so the hard timeout can kill the whole group
|
||||
)
|
||||
except OSError as exc:
|
||||
|
||||
@@ -102,6 +102,7 @@ def start(url: str, *, out_dir: Optional[Path] = None, headed: bool = False,
|
||||
# Detach: stdout/stderr → log file, new session so parent signals don't propagate.
|
||||
with open(log_path, "ab", buffering=0) as log_fh:
|
||||
proc = subprocess.Popen([sys.executable, "-P", "-m", "plugins.google_meet.meet_bot"], stdin=subprocess.DEVNULL,
|
||||
# detached-spawn: ok — reached via files across turns, stopped by meet_leave (pid in .active.json)
|
||||
stdout=log_fh, stderr=subprocess.STDOUT, env=env, start_new_session=True,
|
||||
close_fds=True)
|
||||
record = {"pid": proc.pid, "meeting_id": meeting_id, "out_dir": str(out), "url": url,
|
||||
|
||||
@@ -1054,6 +1054,7 @@ class PhotonAdapter(BasePlatformAdapter):
|
||||
self._sidecar_proc = subprocess.Popen( # noqa: ASYNC220 -- persistent sidecar; callers use the Popen pipes and poll()
|
||||
[self._node_bin, str(_sidecar_dir() / "index.mjs")],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env,
|
||||
# detached-spawn: ok — exits on stdin EOF (PHOTON_SIDECAR_WATCH_STDIN), so it dies with this process
|
||||
start_new_session=(sys.platform != "win32"),
|
||||
# Windows: run the persistent sidecar headless so it does not open
|
||||
# (or leave) a visible console window. CREATE_NO_WINDOW only (no
|
||||
|
||||
@@ -110,6 +110,7 @@ def _spawn_worker(env: dict[str, str]) -> subprocess.Popen:
|
||||
env["PYTHONPATH"] = path_entry + os.pathsep + child_pythonpath if child_pythonpath else path_entry
|
||||
worker_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "_search_worker.py")
|
||||
# Own process group/session so terminate/kill also reach a hung primp grandchild.
|
||||
# detached-spawn: ok — awaited under a 30 s cap that kills its group; blocked on its stdin pipe, it exits on owner death
|
||||
extra_kwargs: dict[str, Any] = {"creationflags": subprocess.CREATE_NEW_PROCESS_GROUP} if sys.platform == "win32" else {"start_new_session": True}
|
||||
# stdin/stdout/stderr stay explicit keyword args so scripts/check_subprocess_stdin.py sees them
|
||||
# (TUI gateway inherits stdin). stderr=DEVNULL: a chatty child would deadlock a stdout-only drain.
|
||||
|
||||
Reference in new issue
Block a user