fix(agent): preserve external cancellation in message loop

Made-with: Cursor
This commit is contained in:
Xubin Ren
2026-03-20 11:24:05 +00:00
committed by Xubin Ren
parent d83ba36800
commit aacbb95313
2 changed files with 15 additions and 3 deletions

View File

@@ -265,9 +265,9 @@ class AgentLoop:
except asyncio.TimeoutError: except asyncio.TimeoutError:
continue continue
except asyncio.CancelledError: except asyncio.CancelledError:
# anyio/MCP cancel scopes surface as CancelledError (a BaseException subclass). # Preserve real task cancellation so shutdown can complete cleanly.
# Re-raise only if the loop itself is being shut down; otherwise keep running. # Only ignore non-task CancelledError signals that may leak from integrations.
if not self._running: if not self._running or asyncio.current_task().cancelling():
raise raise
continue continue
except Exception as e: except Exception as e:

View File

@@ -65,6 +65,18 @@ class TestRestartCommand:
mock_handle.assert_called_once() mock_handle.assert_called_once()
@pytest.mark.asyncio
async def test_run_propagates_external_cancellation(self):
"""External task cancellation should not be swallowed by the inbound wait loop."""
loop, _bus = _make_loop()
run_task = asyncio.create_task(loop.run())
await asyncio.sleep(0.1)
run_task.cancel()
with pytest.raises(asyncio.CancelledError):
await asyncio.wait_for(run_task, timeout=1.0)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_help_includes_restart(self): async def test_help_includes_restart(self):
loop, bus = _make_loop() loop, bus = _make_loop()