import { vi } from "vitest"; import type { ComputerUseCapabilityDescriptor, ComputerUseV2ActionName, } from "../../plugins/computer-use-contract.js"; const mocks = vi.hoisted(() => ({ listNodesMock: vi.fn(), callGatewayToolMock: vi.fn(), sleepMock: vi.fn(), gatewayComputerStatusMock: vi.fn(), })); export const listNodesMock = mocks.listNodesMock; export const callGatewayToolMock = mocks.callGatewayToolMock; export const sleepMock = mocks.sleepMock; export const gatewayComputerStatusMock = mocks.gatewayComputerStatusMock; export const TINY_PNG_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII="; export const COMPUTER_ACT_COMMAND = "computer.act"; vi.mock("./nodes-utils.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, listNodes: listNodesMock }; }); vi.mock("./gateway.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, callGatewayTool: callGatewayToolMock }; }); vi.mock("../../utils/sleep.js", () => ({ sleep: sleepMock })); vi.mock("./computer-tool-gateway.js", () => ({ loadGatewayComputerStatus: gatewayComputerStatusMock, bindGatewayComputerCleanup: async () => undefined, })); export const { createComputerTool, invalidateComputerFrameIfMissing } = await import("./computer-tool.js"); export const { loadPairedComputerUseAvailabilityForSurface } = await import("../computer-use-node-capabilities.js"); const { DEFAULT_IMAGE_MAX_DIMENSION_PX } = await import("../image-sanitization.js"); // With no config the reference width is capped at the default sanitization limit. export const EFFECTIVE_REF_WIDTH = Math.min(1280, DEFAULT_IMAGE_MAX_DIMENSION_PX); export function macComputerNode(overrides?: Record) { return { nodeId: "mac-1", displayName: "Studio", platform: "macos", connected: true, commands: ["screen.snapshot", "computer.act"], ...overrides, }; } export function v2Descriptor( actions: ComputerUseV2ActionName[], overrides: Partial = {}, ): ComputerUseCapabilityDescriptor { return { contractVersion: 2 as const, provider: { id: "fixture", label: "Fixture", generation: "generation-1" }, actions, targets: ["screen", "window", "element", "browser"] as const, deliveryModes: ["background", "foreground"] as const, observations: ["image", "accessibility", "browser"] as const, features: { recording: false, agentCursor: false, multiDisplay: false }, ...overrides, }; } export type ComputerTool = ReturnType; export type ComputerToolOptions = NonNullable[0]>; export type ComputerActBody = { nodeId?: string; command?: string; idempotencyKey?: string; params?: Record; }; export function readActionEnum(tool: ComputerTool): string[] { const schema = tool.parameters as { properties?: { action?: { enum?: string[] } } }; return schema.properties?.action?.enum ?? []; } export function screenshotPayload(screenIndex = 0, base64 = TINY_PNG_BASE64) { return { payload: { format: "png", base64, displayFrameId: `display-${screenIndex}-frame`, width: 1280, height: 800, screenIndex, }, }; } export function readFrameId(result: { details?: unknown }): string { const frameId = (result.details as { frameId?: unknown } | undefined)?.frameId; if (typeof frameId !== "string") { throw new Error("missing frameId"); } return frameId; } export function readLastComputerActParams( action?: ComputerUseV2ActionName, ): Record { const call = callGatewayToolMock.mock.calls.findLast((entry) => { const body = entry[2] as ComputerActBody; return body.command === COMPUTER_ACT_COMMAND && (!action || body.params?.action === action); }); const body = call?.[2] as { params?: Record } | undefined; if (!body?.params) { throw new Error("missing computer.act request"); } const { executionId: _executionId, ...params } = body.params; return params; } export function createVisionComputerTool(options: ComputerToolOptions = {}) { return createComputerTool({ modelHasVision: true, ...options }); } export function resetComputerToolMocks() { listNodesMock.mockReset(); callGatewayToolMock.mockReset(); gatewayComputerStatusMock.mockReset(); gatewayComputerStatusMock.mockResolvedValue({ configured: false, available: false }); sleepMock.mockReset(); sleepMock.mockImplementation((ms: number, signal?: AbortSignal) => { if (signal?.aborted) { return Promise.reject(new Error("Aborted")); } if (ms === 500 || !signal) { return Promise.resolve(); } return new Promise((_resolve, reject) => { signal.addEventListener("abort", () => reject(new Error("Aborted")), { once: true }); }); }); listNodesMock.mockResolvedValue([macComputerNode()]); callGatewayToolMock.mockImplementation(async (_method, _opts, body) => (body as ComputerActBody).command === COMPUTER_ACT_COMMAND ? { payload: { ok: true } } : screenshotPayload(), ); }