mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
fix(browser): describe supported browser tool actions (#111381)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -223,6 +223,14 @@ describe("browser plugin", () => {
|
||||
expect(tool.description).toContain("action=profiles");
|
||||
expect(tool.description).not.toContain('profile="user"');
|
||||
expect(tool.outputSchema).toBe(BrowserToolOutputSchema);
|
||||
const properties = (
|
||||
tool.parameters as {
|
||||
properties: Record<string, { description?: string }>;
|
||||
}
|
||||
).properties;
|
||||
expect(properties.actions?.description).toContain("batch");
|
||||
expect(properties.doubleClick?.description).toContain("clickCoords");
|
||||
expect(properties.labels?.description).toContain("snapshot");
|
||||
expect(runtimeApiMocks.createBrowserTool).not.toHaveBeenCalled();
|
||||
await tool.execute("call-1", { action: "status" });
|
||||
expect(runtimeApiMocks.createBrowserTool).toHaveBeenCalledWith({
|
||||
@@ -394,6 +402,8 @@ describe("browser plugin", () => {
|
||||
expect(actions).not.toEqual(expect.arrayContaining(["pdf", "download", "waitfordownload"]));
|
||||
expect(actions).toEqual(expect.arrayContaining(["snapshot", "screenshot"]));
|
||||
expect(actKinds).not.toContain("batch");
|
||||
expect((properties.actions as { description?: string }).description).toBeUndefined();
|
||||
expect((properties.stopOnError as { description?: string }).description).toBeUndefined();
|
||||
});
|
||||
|
||||
it("rejects malformed run bindings before creating the lazy browser tool", () => {
|
||||
|
||||
@@ -89,5 +89,49 @@ describe("browser tool schema", () => {
|
||||
requireSchemaProperty(requestProperties, "kind", "browser request kind schema").enum,
|
||||
).toContain("batch");
|
||||
expect(requestProperties.actions).toBeDefined();
|
||||
|
||||
for (const name of ["kind", "actions", "stopOnError", "doubleClick", "ref"] as const) {
|
||||
const flattened = requireSchemaProperty(properties, name, `browser ${name} schema`);
|
||||
const nested = requireSchemaProperty(
|
||||
requestProperties,
|
||||
name,
|
||||
`browser request ${name} schema`,
|
||||
);
|
||||
|
||||
expect(flattened.description, name).toBeTruthy();
|
||||
expect(nested.description, name).toBe(flattened.description);
|
||||
}
|
||||
|
||||
expect(properties.actions?.description).toContain("batch");
|
||||
expect(properties.stopOnError?.description).toContain("default");
|
||||
expect(properties.doubleClick?.description).toContain("clickCoords");
|
||||
expect(properties.ref?.description).toContain("snapshot");
|
||||
expect(properties.profile?.description).toContain("default");
|
||||
expect(properties.labels?.description).toContain("snapshot");
|
||||
expect(properties.request?.description).toContain("act");
|
||||
});
|
||||
|
||||
it("preserves batch fields without advertising them on unsupported profiles", () => {
|
||||
const schema = createBrowserToolSchema(
|
||||
resolveBrowserToolCapabilities({
|
||||
profileCapabilities: {
|
||||
supportsBatchActions: false,
|
||||
supportsDownloads: false,
|
||||
supportsPdf: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const properties = schema.properties as BrowserSchemaRecord;
|
||||
const requestProperties = requireSchemaProperty(properties, "request", "browser request schema")
|
||||
.properties as BrowserSchemaRecord;
|
||||
|
||||
for (const scopedProperties of [properties, requestProperties]) {
|
||||
expect(scopedProperties.kind?.enum).not.toContain("batch");
|
||||
expect(scopedProperties.kind?.description).not.toContain("batch");
|
||||
expect(scopedProperties.actions).toBeDefined();
|
||||
expect(scopedProperties.stopOnError).toBeDefined();
|
||||
expect(scopedProperties.actions?.description).toBeUndefined();
|
||||
expect(scopedProperties.stopOnError?.description).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -104,15 +104,29 @@ export function resolveBrowserToolCapabilities(params?: {
|
||||
}
|
||||
|
||||
function createBrowserActProperties(capabilities: BrowserToolCapabilities) {
|
||||
const supportsBatch = capabilities.actKinds.includes("batch");
|
||||
return {
|
||||
// Common fields
|
||||
targetId: Type.Optional(Type.String({ description: TAB_REFERENCE_DESCRIPTION })),
|
||||
ref: Type.Optional(Type.String()),
|
||||
ref: Type.Optional(
|
||||
Type.String({ description: "Element reference from a current-tab snapshot." }),
|
||||
),
|
||||
// batch - permissive children keep the provider schema flat; runtime validates each action.
|
||||
actions: Type.Optional(Type.Array(Type.Object({}, { additionalProperties: true }))),
|
||||
stopOnError: Type.Optional(Type.Boolean()),
|
||||
actions: Type.Optional(
|
||||
Type.Array(
|
||||
Type.Object({}, { additionalProperties: true }),
|
||||
supportsBatch ? { description: "Nested act requests for kind=batch." } : {},
|
||||
),
|
||||
),
|
||||
stopOnError: Type.Optional(
|
||||
Type.Boolean(
|
||||
supportsBatch ? { description: "Stop batch at first error (default: true)." } : {},
|
||||
),
|
||||
),
|
||||
// click
|
||||
doubleClick: Type.Optional(Type.Boolean()),
|
||||
doubleClick: Type.Optional(
|
||||
Type.Boolean({ description: "Double-click for kind=click or clickCoords." }),
|
||||
),
|
||||
button: Type.Optional(Type.String()),
|
||||
modifiers: Type.Optional(Type.Array(Type.String())),
|
||||
x: optionalFiniteNumberSchema(),
|
||||
@@ -152,15 +166,27 @@ function createBrowserActProperties(capabilities: BrowserToolCapabilities) {
|
||||
/** Provider-compatible Browser tool argument schema. */
|
||||
export function createBrowserToolSchema(capabilities: BrowserToolCapabilities) {
|
||||
const actProperties = createBrowserActProperties(capabilities);
|
||||
const BrowserActSchema = Type.Object({
|
||||
kind: stringEnum(capabilities.actKinds),
|
||||
...actProperties,
|
||||
});
|
||||
const actKindDescription = capabilities.actKinds.includes("batch")
|
||||
? "Browser action kind; batch runs the nested actions array."
|
||||
: "Browser action kind.";
|
||||
const BrowserActSchema = Type.Object(
|
||||
{
|
||||
kind: stringEnum(capabilities.actKinds, { description: actKindDescription }),
|
||||
...actProperties,
|
||||
},
|
||||
{ description: "Preferred nested request for action=act." },
|
||||
);
|
||||
return Type.Object({
|
||||
action: stringEnum(capabilities.actions),
|
||||
target: optionalStringEnum(BROWSER_TARGETS),
|
||||
node: Type.Optional(Type.String()),
|
||||
profile: Type.Optional(Type.String()),
|
||||
profile: Type.Optional(
|
||||
Type.String({
|
||||
description: capabilities.tabBound
|
||||
? "Bound browser profile selected for this run."
|
||||
: "Browser profile name; omit to use the configured default.",
|
||||
}),
|
||||
),
|
||||
browser: Type.Optional(Type.String()),
|
||||
systemProfile: Type.Optional(Type.String()),
|
||||
into: Type.Optional(Type.String()),
|
||||
@@ -176,7 +202,11 @@ export function createBrowserToolSchema(capabilities: BrowserToolCapabilities) {
|
||||
compact: Type.Optional(Type.Boolean()),
|
||||
depth: optionalNonNegativeIntegerSchema(),
|
||||
frame: Type.Optional(Type.String()),
|
||||
labels: Type.Optional(Type.Boolean()),
|
||||
labels: Type.Optional(
|
||||
Type.Boolean({
|
||||
description: "Overlay snapshot or screenshot with element reference labels.",
|
||||
}),
|
||||
),
|
||||
urls: Type.Optional(Type.Boolean()),
|
||||
fullPage: Type.Optional(Type.Boolean()),
|
||||
path: Type.Optional(Type.String()),
|
||||
@@ -189,7 +219,7 @@ export function createBrowserToolSchema(capabilities: BrowserToolCapabilities) {
|
||||
accept: Type.Optional(Type.Boolean()),
|
||||
promptText: Type.Optional(Type.String()),
|
||||
// Legacy flattened act params (preferred: request={...})
|
||||
kind: Type.Optional(stringEnum(capabilities.actKinds)),
|
||||
kind: Type.Optional(stringEnum(capabilities.actKinds, { description: actKindDescription })),
|
||||
...actProperties,
|
||||
request: Type.Optional(BrowserActSchema),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user