Description
Typing /new or /clear in the CLI shows the 3-option confirmation prompt ([1] Approve Once, [2] Always Approve, [3] Cancel), but then freezes permanently. The user cannot type a choice — input() never returns.
Root Cause
_confirm_destructive_slash() in cli.py:8651 calls _prompt_text_input("Choice [1/2/3]: "). Slash commands are dispatched from process_loop (a daemon thread, cli.py:12749), not the main thread.
_prompt_text_input (cli.py:6018) checks threading.current_thread() is threading.main_thread(). When false, it falls back to direct input():
else:
_ask() # calls input() directly
But prompt_toolkit owns stdin in raw mode on the main thread. input() on a daemon thread is blocked waiting for stdin that ptk has already captured. This is a permanent hang.
What Was Tried
Attempted to use asyncio.run_coroutine_threadsafe(run_in_terminal(_ask), loop):
run_in_terminal(_ask) fails because the daemon thread has no event loop: RuntimeError: There is no current event loop in thread (Python 3.11 ensure_future requires one)
- Adding
asyncio.set_event_loop(loop) before calling run_in_terminal fixes the RuntimeError, and input() now works — but then the CLI exits after choice selection (something in run_in_terminal / prompt_toolkit state restoration causes the exit)
Suggested Fix Direction
The destructive slash confirmation should probably not use input() at all from the daemon thread. Instead, it could:
- Route the prompt through prompt_toolkit's composer/buffer system (like clarify does with
_clarify_state)
- Or use
event.app.run_in_terminal() from the main thread directly, by deferring the confirmation prompt to the main thread via call_soon_threadsafe
- Or handle
/new inline on the UI thread like /model already does (line 11108)
Environment
- macOS 26.4.1, Python 3.11.15
- Hermes CLI (not TUI)
- prompt_toolkit 3.x
- Reproducible: always
Description
Typing
/newor/clearin the CLI shows the 3-option confirmation prompt ([1] Approve Once, [2] Always Approve, [3] Cancel), but then freezes permanently. The user cannot type a choice —input()never returns.Root Cause
_confirm_destructive_slash()incli.py:8651calls_prompt_text_input("Choice [1/2/3]: "). Slash commands are dispatched fromprocess_loop(a daemon thread,cli.py:12749), not the main thread._prompt_text_input(cli.py:6018) checksthreading.current_thread() is threading.main_thread(). When false, it falls back to directinput():But prompt_toolkit owns stdin in raw mode on the main thread.
input()on a daemon thread is blocked waiting for stdin that ptk has already captured. This is a permanent hang.What Was Tried
Attempted to use
asyncio.run_coroutine_threadsafe(run_in_terminal(_ask), loop):run_in_terminal(_ask)fails because the daemon thread has no event loop:RuntimeError: There is no current event loop in thread(Python 3.11ensure_futurerequires one)asyncio.set_event_loop(loop)before callingrun_in_terminalfixes theRuntimeError, andinput()now works — but then the CLI exits after choice selection (something inrun_in_terminal/ prompt_toolkit state restoration causes the exit)Suggested Fix Direction
The destructive slash confirmation should probably not use
input()at all from the daemon thread. Instead, it could:_clarify_state)event.app.run_in_terminal()from the main thread directly, by deferring the confirmation prompt to the main thread viacall_soon_threadsafe/newinline on the UI thread like/modelalready does (line 11108)Environment