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.
This commit is contained in:
2026-07-15 09:41:32 +08:00
parent b80ebf783a
commit a7d9c9879b
10 changed files with 170 additions and 55 deletions
+27 -2
View File
@@ -2,8 +2,12 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from plyngent.cli.interrupt import allow_task_cancel, pause_task_cancel_for_prompt
from plyngent.cli.limits import prompt_continue_limit
from plyngent.cli.interrupt import (
allow_task_cancel,
pause_task_cancel_for_prompt,
run_in_prompt_thread,
)
from plyngent.cli.limits import prompt_continue_limit, prompt_continue_limit_async
if TYPE_CHECKING:
import pytest
@@ -23,3 +27,24 @@ def test_prompt_continue_limit_under_pause(monkeypatch: pytest.MonkeyPatch) -> N
monkeypatch.setattr("click.confirm", _confirm)
assert prompt_continue_limit("too many rounds") is True
async def test_run_in_prompt_thread_pauses_cancel() -> None:
"""Cancel is paused on the main thread for the whole to_thread call."""
assert allow_task_cancel() is True
def work() -> str:
return "ok"
# ContextVar may not propagate to worker threads; assert pause around the call.
result = await run_in_prompt_thread(work)
assert result == "ok"
assert allow_task_cancel() is True
async def test_prompt_continue_limit_async(monkeypatch: pytest.MonkeyPatch) -> None:
def _confirm(*_a: object, **_k: object) -> bool:
return True
monkeypatch.setattr("click.confirm", _confirm)
assert await prompt_continue_limit_async("too many rounds") is True