fix: close MCP stdio transports from agent task

This commit is contained in:
Xubin Ren
2026-06-22 18:59:50 +08:00
parent 6efef2700a
commit fbaa85117b
4 changed files with 227 additions and 78 deletions
+97
View File
@@ -1960,6 +1960,103 @@ def test_gateway_health_endpoint_binds_and_serves_expected_responses(
assert missing_response.endswith("\r\n\r\nNot Found")
def test_gateway_shutdown_lets_agent_task_own_mcp_cleanup(
monkeypatch,
tmp_path: Path,
) -> None:
config_file = _write_instance_config(tmp_path)
config = Config()
config.gateway.port = 18791
seen: dict[str, object] = {}
class _FakeSessionManager:
def flush_all(self) -> int:
return 0
class _FakeAgentLoop:
@classmethod
def from_config(cls, config, bus=None, **extra):
return cls(**extra)
def __init__(self, **_kwargs) -> None:
self.model = "test-model"
self.provider = object()
self.sessions = _FakeSessionManager()
def llm_runtime(self) -> None:
return None
async def run(self) -> None:
try:
await asyncio.Event().wait()
finally:
seen["agent_task_cleaned_up"] = True
async def close_mcp(self) -> None:
raise AssertionError("gateway must not close MCP from the outer task")
def stop(self) -> None:
seen["agent_stopped"] = True
class _FakeChannelManager:
def __init__(self, _config, _bus, **_kwargs) -> None:
self.enabled_channels = ["telegram"]
async def start_all(self) -> None:
await asyncio.Event().wait()
async def stop_all(self) -> None:
seen["channels_stopped"] = True
class _FakeCronService:
def __init__(self, _store_path: Path) -> None:
self.on_job = None
async def start(self) -> None:
return None
def stop(self) -> None:
seen["cron_stopped"] = True
def status(self) -> dict[str, int]:
return {"jobs": 0}
def register_system_job(self, _job) -> None:
return None
class _FakeServer:
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, tb) -> bool:
return False
async def serve_forever(self) -> None:
raise _StopGatewayError("stop")
async def _fake_start_server(_handler, _host: str, _port: int):
return _FakeServer()
_patch_cli_command_runtime(
monkeypatch,
config,
message_bus=lambda: object(),
session_manager=lambda _workspace: object(),
)
monkeypatch.setattr("nanobot.cli.commands.AgentLoop", _FakeAgentLoop)
monkeypatch.setattr("nanobot.channels.manager.ChannelManager", _FakeChannelManager)
monkeypatch.setattr("nanobot.cron.service.CronService", _FakeCronService)
monkeypatch.setattr("asyncio.start_server", _fake_start_server)
result = runner.invoke(app, ["gateway", "--config", str(config_file)])
assert result.exit_code == 0
assert seen["agent_stopped"] is True
assert seen["agent_task_cleaned_up"] is True
assert seen["channels_stopped"] is True
assert seen["cron_stopped"] is True
def test_serve_uses_api_config_defaults_and_workspace_override(
monkeypatch, tmp_path: Path
) -> None: