fix(agent): preserve uncertain browser close state

This commit is contained in:
jxxghp
2026-09-12 13:37:34 +08:00
parent 046a8abcad
commit 551fe76a54
3 changed files with 32 additions and 1 deletions

View File

@@ -109,7 +109,7 @@ class BrowseWebpageInput(BaseModel):
description="Content type for 'get_content' action: 'text' for readable text, 'html' for raw HTML",
)
timeout: Optional[int] = Field(
DEFAULT_TIMEOUT, ge=1, le=MAX_TIMEOUT,
DEFAULT_TIMEOUT,
description="Timeout in seconds for the action (default: 30, range: 1-300)"
)
cookies: Optional[str] = Field(
@@ -416,6 +416,13 @@ class BrowseWebpageTool(MoviePilotTool):
except Exception as e:
error_summary = summarize_error(e)
logger.error(f"CloakBrowser 执行失败: {error_summary}", exc_info=True)
if str(e) == "关闭浏览器标签页失败":
return self._json_response({
"success": False,
"execution_outcome": "unknown",
"error": "关闭浏览器标签页的实际状态未知。",
"recovery": "先使用 list_tabs 或 snapshot 核验当前页面,再决定是否继续操作;不要直接重试关闭动作。",
})
if browser_action == BrowserAction.SCREENSHOT:
return self._screenshot_failure("screenshot_failed", "浏览器截图执行失败")
return self._error_response("browser_operation_failed", f"CloakBrowser 执行失败: {error_summary}", "检查当前会话和页面状态后再重试。")

View File

@@ -115,4 +115,5 @@ uv run --locked --no-sync python -m scripts.evaluation \
- 浏览器与子代理状态阶段:浏览器所有成功/失败 JSON 统一带 `execution_outcome`;子代理任务的 running/cancelled 状态带明确的 `pending/failed` 语义和继续观察、取消后重新派发的恢复建议。当前阶段与文件、Shell、浏览器回归共 112 项通过;真实浏览器仍受用户保存权限限制,子代理终端共享仍需原生句柄闭环。
- 浏览器观察回执阶段:`goto`、`snapshot`、`get_content` 及交互动作统一显式返回 `success`,从而稳定映射为 `execution_outcome=succeeded/failed`;页面内容为空与浏览器执行失败可以区分。当前跨浏览器、文件、Shell、子代理回归共 125 项通过。
- 浏览器会话可靠性阶段:直接 `run()` 的参数/异常现在返回结构化恢复合同并限制 timeout 为 1–300 秒;点击等待遵守调用 deadline;关闭标签页正确调整 active index,关闭异常不再伪报成功;生成新 snapshot 前清理旧 ref,动态页面不会把过期 ref 静默指向新元素。浏览器、文件、Shell、子代理相关回归共 155 项通过。
- 浏览器状态不确定性补强:关闭标签页的 provider 异常按 `execution_outcome=unknown` 返回,要求先 `list_tabs/snapshot` 核验,避免把可能已发生的动作当作失败后重复执行;补充了 active index 和关闭异常的真实回归。
- 浏览器环境复核:启动本机 Chrome 后用同一官方 OAuth 配置重跑 `/tmp/moviepilot-agent-round-luna-oauth-native-browser-chrome-20260912.json`,原生已加载 Browser plugin、node_repl 并选择 Chrome;真实回环导航仍被保存的浏览器权限拒绝。该结果排除了“CLI 未登录或未启动 Chrome”的解释,下一次只有在用户允许对应回环地址后才继续导航、点击和 oracle 配对。

View File

@@ -550,6 +550,29 @@ def test_close_tab_before_active_page_keeps_active_index() -> None:
assert first_page.closed is True
def test_close_tab_failure_is_reported_as_unknown_state() -> None:
"""关闭动作异常时实际状态可能已发生,必须要求先核验而非盲目重试。"""
page = _FakePage("close-error")
page.close = MagicMock(side_effect=RuntimeError("provider failure"))
context = _FakeContext([page, _FakePage("remaining")])
session = _BrowserSessionState("close-error", context, context.pages, active_index=0)
tool = BrowseWebpageTool(session_id="session-1", user_id="10001")
with patch.object(
BrowserSessionHelper,
"with_session",
side_effect=lambda callback, **_kwargs: callback(session),
):
result = tool._execute_browser_action(
browser_action=BrowserAction.CLOSE_TAB, url=None, selector=None, ref=None, value=None,
script=None, content_type="text", timeout=3, cookies=None, user_agent=None,
session_key="close-error", tab_index=0, allow_private_network=False,
)
payload = json.loads(result)
assert payload["execution_outcome"] == "unknown"
assert "list_tabs" in payload["recovery"]
def test_browse_webpage_get_cookies_returns_current_domain_cookie_and_ua():
"""管理员 Cookie 动作应只返回当前页面域名的会话字段。"""
page = _FakePage()