fix(oneshot): join memory daemon threads before process exit (#37632) - #37635
fix(oneshot): join memory daemon threads before process exit (#37632)#37635liuhao1024 wants to merge 1 commit into
Conversation
…earch#37632) shutdown_memory_provider() was never called on the hermes -z path. Honcho's daemon threads (honcho-sync, honcho-prewarm-dialectic, honcho-context-prefetch) remained blocked in httpx I/O when the interpreter finalized, causing glibc abort() → SIGABRT → exit 134. Add a shutdown_memory_provider() call after agent.chat() in _run_agent(), wrapped in try/except since memory providers are best-effort. This joins the daemon threads with bounded timeouts before the process exits, preventing the finalization-time crash.
|
Nice catch. The The test mimics oneshot's early-return model via a nested try block: try:
return agent.chat(prompt) or ""
except Exception:
return ""That swallowed return before the fix — the early-return paths on lines 324 and 328 also skip cleanup. Might be worth adding |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for identifying the one-shot lifecycle gap. Current main still has no memory-provider teardown in _run_agent: it calls agent.run_conversation(prompt) and returns directly at hermes_cli/oneshot.py:425-426.
Problems
- The patch is stale: current main no longer uses
agent.chat(), so the proposed edit and tests do not cover the live path. - Cleanup is after the successful agent call rather than in
finally; an exception from currentrun_conversation()skips teardown and is caught only byrun_oneshot(hermes_cli/oneshot.py:232-250). - The no-argument shutdown differs from current CLI/gateway cleanup, which forwards
_session_messages(cli.py:1141,gateway/run.py:6055). The default becomeson_session_end([])(run_agent.py:3323-3329).
Suggested changes
- Salvage this around current
run_conversation()with best-effortfinallycleanup, preserving the original exception. - Forward a list-valued
_session_messagestranscript and add success, exception, and transcript-forwarding tests. - The Jun. 12 evidence in #37632 reports that a bounded join can still leave an aborting daemon thread alive; confirm deterministic provider termination before treating shutdown alone as conclusive.
Automated hermes-sweeper review.
| # still-running daemon threads via pthread_exit(), and glibc converts | ||
| # that forced unwind into abort() → SIGABRT → exit 134. (#37632) | ||
| try: | ||
| agent.shutdown_memory_provider() |
There was a problem hiding this comment.
This needs to be a finally around the agent execution. If agent.chat() raises, control escapes before this block and one-shot still exits with provider threads unshut down; current main's run_conversation() path has the same exception route.
| # that forced unwind into abort() → SIGABRT → exit 134. (#37632) | ||
| try: | ||
| agent.shutdown_memory_provider() | ||
| except Exception: |
There was a problem hiding this comment.
Please forward the agent's list-valued _session_messages here, matching cli.py:1141 and gateway/run.py:6055; no argument causes shutdown_memory_provider() to call provider on_session_end([]).
What does this PR do?
Calls
shutdown_memory_provider()afteragent.chat()in thehermes -zone-shot path, joining Honcho memory daemon threads before process exit to prevent SIGABRT (exit 134) during Python interpreter finalization.Related Issue
Fixes #37632
Type of Change
Changes Made
hermes_cli/oneshot.py: Addedagent.shutdown_memory_provider()call afteragent.chat()in_run_agent(), wrapped in try/except since memory providers are best-effort. This joins daemon threads (honcho-sync, honcho-prewarm-dialectic, honcho-context-prefetch) with bounded timeouts before the process exits.tests/cli/test_oneshot_memory_shutdown.py: Added regression tests verifying (1)shutdown_memory_provider()is called after chat, and (2) a raisingshutdown_memory_providerdoes not crash_run_agent.How to Test
hermes -z "reply with exactly: ok" --yolo > /dev/null 2>&1; echo "exit=$?"exit=134(SIGABRT). After fix:exit=0.pytest tests/cli/test_oneshot_memory_shutdown.py -v— both tests should pass.pytest tests/cli/test_cli_shutdown_memory_messages.py -v— existing tests still pass.Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/ACode Intelligence
hermes_cli/oneshot.py:_run_agent(callers: 2 —run_oneshotand Termux fast path)cli.py:959-970callsshutdown_memory_provideron interactive CLI exit;gateway/run.py:3640-3655calls it on gateway session expiry. This PR fills the missing one-shot gap.