Commit Graph

6 Commits

Author SHA1 Message Date
NCBM 6ec7b4de12 cli/interrupt: route SIGINT through a session handler
A SIGINT delivered while the event loop is parked in its selector is raised in
the loop's own frame: it escapes run_until_complete, the Runner cancels the main
task, and the REPL is left half-shut-down (no prompt, and the next line the
human types is eaten by an abandoned prompt reader) until the following Ctrl+C
kills the process. Off-loop prompts (ask_user_* / wait / tool confirms run in a
worker thread via asyncio.to_thread) and the auto-retry countdown are the usual
windows.

Keep one asyncio-level SIGINT handler installed for the whole session instead:
it cancels the innermost registered target (run_cancellable registers the turn
task, sleep_cancellable the retry countdown, off_loop_prompt the waiting prompt)
and never raises. Reads that block on the main thread inside a coroutine (the
REPL entry prompt, synchronous click prompts) suspend the router and take SIGINT
as a KeyboardInterrupt. Off-loop readline prompts cannot be interrupted without
abandoning their reader thread on stdin, so they ignore SIGINT with a one-line
hint.
2026-09-22 19:16:05 +08:00
NCBM 685370d3cb cli/limits: yyy turn-scoped auto-continue for limit prompts 2026-09-09 22:44:20 +08:00
NCBM 1973ef93bb cli: keep Ctrl+C a plain KeyboardInterrupt under asyncio.run
asyncio.run (Runner) replaces SIGINT with its own handler when the
current one is signal.default_int_handler: the first Ctrl+C silently
cancels the main task (invisible at the REPL prompt, leaving the task
marked cancelled) and the second raises KeyboardInterrupt, so a Ctrl+D
exit after a cancel was cancelled mid-memory.close() (SQLAlchemy
CancelledError traceback) and the CLI died with "Aborted!".

Install a distinct KeyboardInterrupt-raising handler before asyncio.run
so Runner leaves SIGINT alone, and have run_cancellable restore the
previous handler after each turn (remove_signal_handler leaves SIG_DFL,
which would kill the process on a stray Ctrl+C between turns).
2026-08-11 21:13:17 +08:00
NCBM badcb3c9f3 core/cli: fix Ctrl+C after mid-turn prompts (SIGINT cancel)
asyncio signal handlers freeze ContextVars at install time; reinstalling
while allow_task_cancel was still False left cancel permanently disabled.
Use a process-level prompt depth counter and reinstall only after depth=0.
2026-07-18 19:31:40 +08:00
NCBM a7d9c9879b core/cli+agent: run TTY confirms off the event loop
Max-rounds and destructive-tool prompts use asyncio.to_thread with
cancel paused on the main thread so multi-tool turns no longer look
like user cancel. Hooks may be sync or async.
2026-07-15 09:41:32 +08:00
NCBM 566a06c996 core/cli: pause turn cancel during TTY confirm prompts
Max-rounds and destructive-tool confirms no longer cancel the in-flight
turn; SIGINT during the prompt aborts only the confirm.
2026-07-15 00:00:40 +08:00