9e18197745
A Bot Mode agent invoked by a handoff runs as a short-lived `hermes -p <bot> chat -Q --query-file ...` process. When it dispatches its reply via message_agent / bot_relay — spawned as terminal(background=true, notify_on_complete=true) per the Bot Chat protocol — the one-shot parent exits as soon as the turn ends. The reply child writes to a stdout pipe owned by the dying parent and is destroyed a few seconds later, so the handoff reply is silently lost while the sender waits for a notification that can never come (#90879). Fix (class-wide, not DM-specific): before the one-shot exit paths tear down, the parent now lingers — bounded by the new terminal.oneshot_completion_wait_seconds config (default 600s, 0 disables) — for every tracked background process spawned with notify_on_complete=true. Plain background processes (servers, daemons, watch-pattern monitors) carry no completion contract and are never waited on. - tools/process_registry.py: ProcessRegistry.wait_for_pending_completions() — bounded, interrupt-safe wait over pending notify_on_complete sessions; reconciles orphaned-pipe exits (#17327) each pass so a wedged reader cannot burn the full bound; KeyboardInterrupt aborts the linger without skipping the caller's durable teardown. - cli.py: _finalize_single_query() lingers first, before the durable session flush / cleanup (covers -q and -Q, i.e. the DM recipient shape and bot_relay waiter spawns from one-shot agents). - hermes_cli/oneshot.py: same linger before agent.close() (which kill_all()s the task's processes) on the -z path. - hermes_cli/config_defaults.py: terminal.oneshot_completion_wait_seconds. Tests: tests/tools/test_oneshot_completion_linger.py — unit coverage of the wait semantics (no-op, completion, timeout, task filter, disable, config fallback, reconcile path), exit-path ordering contracts, and a real-process E2E: a short-lived python parent spawns a delivery child through the real ProcessRegistry, lingers, exits, and the delivery completes; sabotaging the linger makes the same E2E reproduce the destroyed-delivery symptom. Fixes #90879