mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-17 16:12:21 -06:00
fix(browser): allow page extraction in tab-bound runs (#116779)
* fix(browser): allow extraction in tab-bound runs * test(browser): tighten tab-bound extraction coverage --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -29,13 +29,36 @@ describe("browser tab tool binding", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects route, tab, and browser-wide action escapes", () => {
|
||||
expect(() =>
|
||||
applyBrowserTabToolBinding({ action: "snapshot", targetId: "target-b" }, binding),
|
||||
).toThrow("cannot override its run-bound tab target");
|
||||
expect(() =>
|
||||
applyBrowserTabToolBinding({ action: "snapshot", node: "other" }, binding),
|
||||
).toThrow("cannot override its run-bound node");
|
||||
it("pins page extraction to the trusted tab and browser route", () => {
|
||||
expect(
|
||||
applyBrowserTabToolBinding(
|
||||
{ action: "extract", query: "When does the release ship?" },
|
||||
binding,
|
||||
),
|
||||
).toEqual({
|
||||
action: "extract",
|
||||
query: "When does the release ship?",
|
||||
target: "node",
|
||||
node: "desktop",
|
||||
profile: "chrome",
|
||||
targetId: "target-a",
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects page extraction route escapes and browser-wide actions", () => {
|
||||
for (const [input, error] of [
|
||||
[{ targetId: "target-b" }, "cannot override its run-bound tab target"],
|
||||
[{ profile: "other" }, "cannot override its run-bound profile"],
|
||||
[{ node: "other" }, "cannot override its run-bound node"],
|
||||
[{ target: "host" }, "cannot override its run-bound target"],
|
||||
] as const) {
|
||||
expect(() =>
|
||||
applyBrowserTabToolBinding(
|
||||
{ action: "extract", query: "When does the release ship?", ...input },
|
||||
binding,
|
||||
),
|
||||
).toThrow(error);
|
||||
}
|
||||
expect(() => applyBrowserTabToolBinding({ action: "open" }, binding)).toThrow(
|
||||
"unavailable in a tab-bound run",
|
||||
);
|
||||
|
||||
@@ -52,6 +52,7 @@ const TAB_BOUND_ACTIONS = new Set([
|
||||
"console",
|
||||
"dialog",
|
||||
"download",
|
||||
"extract",
|
||||
"focus",
|
||||
"navigate",
|
||||
"pdf",
|
||||
|
||||
@@ -3318,6 +3318,57 @@ describe("browser tool extract", () => {
|
||||
beforeEach(resetBrowserToolMocks);
|
||||
afterEach(() => vi.restoreAllMocks());
|
||||
|
||||
const runToolBinding = {
|
||||
kind: "tab" as const,
|
||||
tabId: 17,
|
||||
target: "host" as const,
|
||||
profile: "openclaw",
|
||||
targetId: "target-a",
|
||||
};
|
||||
|
||||
it("extracts from the trusted tab in a tab-bound run", async () => {
|
||||
const tool = createBrowserTool({ agentId: "work", runToolBinding });
|
||||
|
||||
const result = await tool.execute?.("call-bound-extract", {
|
||||
action: "extract",
|
||||
query: "When does the release ship?",
|
||||
});
|
||||
|
||||
expect(browserActionsMocks.browserPageContent).toHaveBeenCalledWith(undefined, {
|
||||
targetId: "target-a",
|
||||
profile: "openclaw",
|
||||
timeoutMs: 60_000,
|
||||
signal: undefined,
|
||||
});
|
||||
expect(toolCommonMocks.prepareSimpleCompletionModelForAgent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
agentId: "work",
|
||||
useUtilityModel: true,
|
||||
allowMissingApiKeyModes: ["aws-sdk"],
|
||||
}),
|
||||
);
|
||||
expect(result?.content[0]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining("Friday."),
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects extraction from a foreign tab before browser or model access", async () => {
|
||||
const tool = createBrowserTool({ agentId: "work", runToolBinding });
|
||||
|
||||
await expect(
|
||||
tool.execute?.("call-bound-extract-escape", {
|
||||
action: "extract",
|
||||
query: "When does the release ship?",
|
||||
targetId: "target-b",
|
||||
}),
|
||||
).rejects.toThrow("cannot override its run-bound tab target");
|
||||
|
||||
expect(browserActionsMocks.browserPageContent).not.toHaveBeenCalled();
|
||||
expect(toolCommonMocks.prepareSimpleCompletionModelForAgent).not.toHaveBeenCalled();
|
||||
expect(toolCommonMocks.completeWithPreparedSimpleCompletionModel).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("captures, converts, and answers with the configured agent model", async () => {
|
||||
toolCommonMocks.sanitizeHtml.mockResolvedValueOnce("<main>Ships Friday.</main>");
|
||||
toolCommonMocks.htmlToMarkdown.mockReturnValueOnce({ text: "Ships **Friday**." });
|
||||
|
||||
Reference in New Issue
Block a user