Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-anthropic-cua-triple-click.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@browserbasehq/stagehand": patch
---

Fix Anthropic CUA `triple_click` action mapping.
11 changes: 11 additions & 0 deletions packages/core/lib/v3/agent/AnthropicCUAClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,17 @@ export class AnthropicCUAClient extends AgentClient {
(input.coordinate ? (input.coordinate as number[])[1] : 0),
...input,
};
} else if (action === "triple_click" || action === "tripleClick") {
return {
type: "tripleClick",
x:
(input.x as number) ||
(input.coordinate ? (input.coordinate as number[])[0] : 0),
y:
(input.y as number) ||
(input.coordinate ? (input.coordinate as number[])[1] : 0),
...input,
};
} else if (action === "scroll") {
// Convert Anthropic's coordinate, scroll_amount and scroll_direction into scroll_x and scroll_y
const x =
Expand Down
1 change: 1 addition & 0 deletions packages/core/lib/v3/handlers/v3CuaAgentHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ export class V3CuaAgentHandler {
}
return { success: true };
}
case "triple_click":
case "tripleClick": {
const { x, y } = action;
if (recording) {
Expand Down
99 changes: 99 additions & 0 deletions packages/core/tests/unit/anthropic-cua-triple-click.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { describe, expect, it, vi, beforeEach } from "vitest";
import { AnthropicCUAClient } from "../../lib/v3/agent/AnthropicCUAClient.js";
import Anthropic from "@anthropic-ai/sdk";

vi.mock("@anthropic-ai/sdk", () => {
const mockCreate = vi.fn();

return {
default: class MockAnthropic {
beta = {
messages: {
create: mockCreate,
},
};
},
};
});

describe("AnthropicCUAClient triple_click handling", () => {
let mockCreate: ReturnType<typeof vi.fn>;
let client: AnthropicCUAClient;
let executedActions: Array<Record<string, unknown>>;

beforeEach(() => {
vi.clearAllMocks();
const anthropic = new Anthropic({ apiKey: "test" });
mockCreate = anthropic.beta.messages.create as ReturnType<typeof vi.fn>;

client = new AnthropicCUAClient("anthropic", "claude-sonnet-4-5-20250929", undefined, {
apiKey: "test-key",
});
client.setViewport(1280, 720);
client.setScreenshotProvider(async () => "fake-base64-screenshot");

executedActions = [];
client.setActionHandler(async (action) => {
executedActions.push({ ...action });
});
});

it("should convert triple_click with coordinate array to tripleClick action", async () => {
mockCreate.mockResolvedValue({
id: "test-id",
content: [
{
type: "tool_use",
id: "tool-1",
name: "computer",
input: {
action: "triple_click",
coordinate: [640, 360],
},
},
],
usage: { input_tokens: 10, output_tokens: 20 },
});

const logger = vi.fn();
await client.executeStep(
[{ role: "user", content: "triple click the paragraph" }],
logger,
);

expect(executedActions).toHaveLength(1);
expect(executedActions[0].type).toBe("tripleClick");
expect(executedActions[0].x).toBe(640);
expect(executedActions[0].y).toBe(360);
});

it("should convert triple_click with x/y fields to tripleClick action", async () => {
mockCreate.mockResolvedValue({
id: "test-id",
content: [
{
type: "tool_use",
id: "tool-2",
name: "computer",
input: {
action: "triple_click",
x: 100,
y: 200,
},
},
],
usage: { input_tokens: 10, output_tokens: 20 },
});

const logger = vi.fn();
await client.executeStep(
[{ role: "user", content: "triple click the line" }],
logger,
);

expect(executedActions).toHaveLength(1);
expect(executedActions[0].type).toBe("tripleClick");
expect(executedActions[0].x).toBe(100);
expect(executedActions[0].y).toBe(200);
});
});
Loading