From e29c9c3906382ee2fdded82b59c9c15bc50ba624 Mon Sep 17 00:00:00 2001 From: 04cb <0x04cb@gmail.com> Date: Sat, 30 May 2026 08:10:51 +0800 Subject: [PATCH] fix(agent): acquire per-session lock in process_direct (#4080) --- nanobot/agent/loop.py | 17 ++++++++++------- tests/test_openai_api.py | 1 + 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/nanobot/agent/loop.py b/nanobot/agent/loop.py index 1573ce769..159423800 100644 --- a/nanobot/agent/loop.py +++ b/nanobot/agent/loop.py @@ -1667,14 +1667,17 @@ class AgentLoop: channel=channel, sender_id="user", chat_id=chat_id, content=content, media=media or [], ) + # Share the dispatch lock so direct calls serialize with bus turns. + lock = self._session_locks.setdefault(session_key, asyncio.Lock()) try: - return await self._process_message( - msg, - session_key=session_key, - on_progress=on_progress, - on_stream=on_stream, - on_stream_end=on_stream_end, - ) + async with lock: + return await self._process_message( + msg, + session_key=session_key, + on_progress=on_progress, + on_stream=on_stream, + on_stream_end=on_stream_end, + ) finally: if channel == "websocket": await self._webui_turns.publish_run_status(msg, "idle") diff --git a/tests/test_openai_api.py b/tests/test_openai_api.py index 59b52b191..7cb85a539 100644 --- a/tests/test_openai_api.py +++ b/tests/test_openai_api.py @@ -406,6 +406,7 @@ async def test_process_direct_accepts_media() -> None: loop = AgentLoop.__new__(AgentLoop) loop._connect_mcp = AsyncMock() + loop._session_locks = {} captured_msg = None