From f19d767b0fbfa7cf73058f8661c27328d563a300 Mon Sep 17 00:00:00 2001 From: lihua Date: Tue, 28 Apr 2026 10:08:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9D=83=E9=99=90=E9=94=99=E8=AF=AF=E8=A6=81?= =?UTF-8?q?=E6=89=93=E6=96=AD=E5=BE=AA=E7=8E=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nanobot/agent/runner.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/nanobot/agent/runner.py b/nanobot/agent/runner.py index bba644bd..7dab2ede 100644 --- a/nanobot/agent/runner.py +++ b/nanobot/agent/runner.py @@ -791,6 +791,17 @@ class AgentRunner: "status": "error", "detail": result.replace("\n", " ").strip()[:120], } + + # check the outside workspace error and break loop + if self._is_workspace_violation(result): + logger.warning( + "Tool {} blocked by workspace/safety guard; aborting turn: {}", + tool_call.name, + result.replace("\n", " ").strip()[:200], + ) + event["detail"] = ("workspace_violation: " + + result.replace("\n", " ").strip())[:160] + return result, event, RuntimeError(result) if spec.fail_on_tool_error: return result + hint, event, RuntimeError(result) return result + hint, event, None @@ -803,6 +814,24 @@ class AgentRunner: detail = detail[:120] + "..." return result, {"name": tool_call.name, "status": "ok", "detail": detail}, None + # Markers identifying tool results that represent a workspace / safety boundary rejection. + _WORKSPACE_BLOCK_MARKERS: tuple[str, ...] = ( + "blocked by safety guard", + "outside the configured workspace", + "outside allowed directory", + "working_dir is outside", + "working_dir could not be resolved", + "path traversal detected", + "path outside working dir", + ) + + @classmethod + def _is_workspace_violation(cls, text: str) -> bool: + if not text: + return False + lowered = text.lower() + return any(marker in lowered for marker in cls._WORKSPACE_BLOCK_MARKERS) + async def _emit_checkpoint( self, spec: AgentRunSpec,