From e6a2a78955d737b35956b67bdccfc1c865e2f82c Mon Sep 17 00:00:00 2001 From: ci-test Date: Mon, 26 Jan 2026 13:25:12 -0700 Subject: [PATCH 1/5] Using 'mode' enum instead of old 'cua' boolean --- .changeset/shy-clouds-type.md | 6 ++++++ packages/core/lib/v3/api.ts | 2 +- packages/core/lib/v3/types/public/api.ts | 6 +++--- packages/server/openapi.v3.yaml | 24 ++++++++++++++++-------- 4 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 .changeset/shy-clouds-type.md diff --git a/.changeset/shy-clouds-type.md b/.changeset/shy-clouds-type.md new file mode 100644 index 000000000..9f70f9516 --- /dev/null +++ b/.changeset/shy-clouds-type.md @@ -0,0 +1,6 @@ +--- +"@browserbasehq/stagehand-server": minor +"@browserbasehq/stagehand": minor +--- + +Using `mode` enum instead of old `cua` boolean in openapi spec diff --git a/packages/core/lib/v3/api.ts b/packages/core/lib/v3/api.ts index 2352cad2f..1e66c7c3f 100644 --- a/packages/core/lib/v3/api.ts +++ b/packages/core/lib/v3/api.ts @@ -360,7 +360,7 @@ export class StagehandAPIClient { const wireAgentConfig: Api.AgentExecuteRequest["agentConfig"] = { systemPrompt: agentConfig.systemPrompt, - cua: agentConfig.cua, + mode: agentConfig.mode, model: agentConfig.model ? (this.prepareModelConfig( agentConfig.model as unknown as ModelConfiguration, diff --git a/packages/core/lib/v3/types/public/api.ts b/packages/core/lib/v3/types/public/api.ts index 0140d4a0d..90bb727c7 100644 --- a/packages/core/lib/v3/types/public/api.ts +++ b/packages/core/lib/v3/types/public/api.ts @@ -600,9 +600,9 @@ export const AgentConfigSchema = z systemPrompt: z.string().optional().meta({ description: "Custom system prompt for the agent", }), - cua: z.boolean().optional().meta({ - description: "Enable Computer Use Agent mode", - example: true, + mode: z.enum(["dom", "hybrid", "cua"]).optional().meta({ + description: "Tool mode for the agent (dom, hybrid, cua)", + example: "cua", }), }) .meta({ id: "AgentConfig" }); diff --git a/packages/server/openapi.v3.yaml b/packages/server/openapi.v3.yaml index 015c11c79..71ad1dc0b 100644 --- a/packages/server/openapi.v3.yaml +++ b/packages/server/openapi.v3.yaml @@ -663,10 +663,14 @@ components: systemPrompt: description: Custom system prompt for the agent type: string - cua: - description: Enable Computer Use Agent mode - example: true - type: boolean + mode: + description: Tool mode for the agent (dom, hybrid, cua) + example: cua + type: string + enum: + - dom + - hybrid + - cua AgentAction: type: object properties: @@ -1518,10 +1522,14 @@ components: systemPrompt: description: Custom system prompt for the agent type: string - cua: - description: Enable Computer Use Agent mode - example: true - type: boolean + mode: + description: Tool mode for the agent (dom, hybrid, cua) + example: cua + type: string + enum: + - dom + - hybrid + - cua additionalProperties: false AgentUsageOutput: type: object From 2c977161dcc109b772f2bad72d4ff35df4a32602 Mon Sep 17 00:00:00 2001 From: ci-test Date: Mon, 26 Jan 2026 13:46:40 -0700 Subject: [PATCH 2/5] Made cua mode deprecated instead of removing it entirely --- .../utils/validateExperimentalFeatures.ts | 5 ++- packages/core/lib/v3/api.ts | 1 + packages/core/lib/v3/cache/AgentCache.ts | 4 +- packages/core/lib/v3/types/public/api.ts | 8 +++- packages/core/lib/v3/v3.ts | 3 +- packages/server/openapi.v3.yaml | 16 ++++++- .../test/integration/v3/agentExecute.test.ts | 44 +++++++++++++++++++ 7 files changed, 75 insertions(+), 6 deletions(-) diff --git a/packages/core/lib/v3/agent/utils/validateExperimentalFeatures.ts b/packages/core/lib/v3/agent/utils/validateExperimentalFeatures.ts index 18b43a3ba..04d816e81 100644 --- a/packages/core/lib/v3/agent/utils/validateExperimentalFeatures.ts +++ b/packages/core/lib/v3/agent/utils/validateExperimentalFeatures.ts @@ -35,7 +35,10 @@ export function validateExperimentalFeatures( const { isExperimental, agentConfig, executeOptions, isStreaming } = options; // Check if CUA mode is enabled (via mode: "cua" or deprecated cua: true) - const isCuaMode = agentConfig?.mode === "cua" || agentConfig?.cua === true; + const isCuaMode = + agentConfig?.mode !== undefined + ? agentConfig.mode === "cua" + : agentConfig?.cua === true; // CUA-specific validation: certain features are not available at all if (isCuaMode) { diff --git a/packages/core/lib/v3/api.ts b/packages/core/lib/v3/api.ts index 1e66c7c3f..1cef14d1a 100644 --- a/packages/core/lib/v3/api.ts +++ b/packages/core/lib/v3/api.ts @@ -361,6 +361,7 @@ export class StagehandAPIClient { const wireAgentConfig: Api.AgentExecuteRequest["agentConfig"] = { systemPrompt: agentConfig.systemPrompt, mode: agentConfig.mode, + cua: agentConfig.mode === undefined ? agentConfig.cua : undefined, model: agentConfig.model ? (this.prepareModelConfig( agentConfig.model as unknown as ModelConfiguration, diff --git a/packages/core/lib/v3/cache/AgentCache.ts b/packages/core/lib/v3/cache/AgentCache.ts index a3cc1eb41..13aaa2f4f 100644 --- a/packages/core/lib/v3/cache/AgentCache.ts +++ b/packages/core/lib/v3/cache/AgentCache.ts @@ -117,7 +117,9 @@ export class AgentCache { ); const isCuaMode = - agentOptions?.mode === "cua" || agentOptions?.cua === true; + agentOptions?.mode !== undefined + ? agentOptions.mode === "cua" + : agentOptions?.cua === true; return JSON.stringify({ v3Model: this.getBaseModelName(), diff --git a/packages/core/lib/v3/types/public/api.ts b/packages/core/lib/v3/types/public/api.ts index 90bb727c7..f58d3da39 100644 --- a/packages/core/lib/v3/types/public/api.ts +++ b/packages/core/lib/v3/types/public/api.ts @@ -600,8 +600,14 @@ export const AgentConfigSchema = z systemPrompt: z.string().optional().meta({ description: "Custom system prompt for the agent", }), + cua: z.boolean().optional().meta({ + description: + "Deprecated. Use mode: 'cua' instead. If both are provided, mode takes precedence.", + example: true, + }), mode: z.enum(["dom", "hybrid", "cua"]).optional().meta({ - description: "Tool mode for the agent (dom, hybrid, cua)", + description: + "Tool mode for the agent (dom, hybrid, cua). If set, overrides cua.", example: "cua", }), }) diff --git a/packages/core/lib/v3/v3.ts b/packages/core/lib/v3/v3.ts index fe648d2db..a21b5f820 100644 --- a/packages/core/lib/v3/v3.ts +++ b/packages/core/lib/v3/v3.ts @@ -1734,7 +1734,8 @@ export class V3 { ) => Promise; } { // Determine if CUA mode is enabled (via mode: "cua" or deprecated cua: true) - const isCuaMode = options?.mode === "cua" || options?.cua === true; + const isCuaMode = + options?.mode !== undefined ? options.mode === "cua" : options?.cua === true; // Emit deprecation warning for cua: true if (options?.cua === true) { diff --git a/packages/server/openapi.v3.yaml b/packages/server/openapi.v3.yaml index 71ad1dc0b..cbcef382a 100644 --- a/packages/server/openapi.v3.yaml +++ b/packages/server/openapi.v3.yaml @@ -663,8 +663,14 @@ components: systemPrompt: description: Custom system prompt for the agent type: string + cua: + description: + "Deprecated. Use mode: 'cua' instead. If both are provided, mode + takes precedence." + example: true + type: boolean mode: - description: Tool mode for the agent (dom, hybrid, cua) + description: Tool mode for the agent (dom, hybrid, cua). If set, overrides cua. example: cua type: string enum: @@ -1522,8 +1528,14 @@ components: systemPrompt: description: Custom system prompt for the agent type: string + cua: + description: + "Deprecated. Use mode: 'cua' instead. If both are provided, mode + takes precedence." + example: true + type: boolean mode: - description: Tool mode for the agent (dom, hybrid, cua) + description: Tool mode for the agent (dom, hybrid, cua). If set, overrides cua. example: cua type: string enum: diff --git a/packages/server/test/integration/v3/agentExecute.test.ts b/packages/server/test/integration/v3/agentExecute.test.ts index a6c8ff85f..25aa03169 100644 --- a/packages/server/test/integration/v3/agentExecute.test.ts +++ b/packages/server/test/integration/v3/agentExecute.test.ts @@ -585,6 +585,50 @@ describe("POST /v1/sessions/:id/agentExecute (V3) - CUA flag compatibility", () assertFetchOk(ctx.body !== null, "Response body should be parseable", ctx); assertFetchOk(!ctx.body.success, "Response should indicate failure", ctx); }); + + it("should prefer mode over cua when both are provided", async () => { + const url = getBaseUrl(); + const openaiApiKey = requireEnv("OPENAI_API_KEY", OPENAI_API_KEY); + + const ctx = await fetchWithContext<{ + success: boolean; + data?: { result: unknown; actionId?: string }; + }>(`${url}/v1/sessions/${sessionId}/agentExecute`, { + method: "POST", + headers, + body: JSON.stringify({ + agentConfig: { + cua: true, + mode: "dom", + model: { + modelName: "openai/gpt-4.1-nano", + apiKey: openaiApiKey, + }, + }, + executeOptions: { + instruction: "What is the title of this page?", + }, + }), + }); + + assertFetchStatus( + ctx, + HTTP_OK, + "V3 agent execute with mode: dom and cua: true should succeed", + ); + assertFetchOk(ctx.body !== null, "Response body should be parseable", ctx); + assertFetchOk(ctx.body.success, "Response should indicate success", ctx); + assertFetchOk( + ctx.body.data !== undefined, + "Response should have data", + ctx, + ); + assertFetchOk( + ctx.body.data.result !== undefined, + "Response should have result", + ctx, + ); + }); }); // ============================================================================= From 10e7f3df87eb63200ebc3ed23f44a5d3a62112bf Mon Sep 17 00:00:00 2001 From: ci-test Date: Mon, 26 Jan 2026 13:54:09 -0700 Subject: [PATCH 3/5] lint fix --- packages/core/lib/v3/v3.ts | 4 +++- packages/server/package.json | 1 + pnpm-lock.yaml | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/core/lib/v3/v3.ts b/packages/core/lib/v3/v3.ts index a21b5f820..10f644c36 100644 --- a/packages/core/lib/v3/v3.ts +++ b/packages/core/lib/v3/v3.ts @@ -1735,7 +1735,9 @@ export class V3 { } { // Determine if CUA mode is enabled (via mode: "cua" or deprecated cua: true) const isCuaMode = - options?.mode !== undefined ? options.mode === "cua" : options?.cua === true; + options?.mode !== undefined + ? options.mode === "cua" + : options?.cua === true; // Emit deprecation warning for cua: true if (options?.cua === true) { diff --git a/packages/server/package.json b/packages/server/package.json index bca3728a7..10fda6aa9 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -39,6 +39,7 @@ "@types/node": "22.13.1", "esbuild": "0.27.2", "eslint": "^9.7.0", + "eslint-plugin-security": "^3.0.1", "openai": "4.87.1", "postject": "1.0.0-alpha.6", "prettier": "^3.2.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4b825bec3..64cab04e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -309,6 +309,9 @@ importers: eslint: specifier: ^9.7.0 version: 9.25.1(jiti@1.21.7) + eslint-plugin-security: + specifier: ^3.0.1 + version: 3.0.1 openai: specifier: 4.87.1 version: 4.87.1(ws@8.18.3(bufferutil@4.0.9))(zod@4.2.1) From dbf45cfb11c458fd5d632fef2f5cd36a289a06a6 Mon Sep 17 00:00:00 2001 From: ci-test Date: Mon, 26 Jan 2026 14:01:37 -0700 Subject: [PATCH 4/5] Properly using cua field as fallback for mode field --- packages/core/lib/v3/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/lib/v3/api.ts b/packages/core/lib/v3/api.ts index 1cef14d1a..8bb29a565 100644 --- a/packages/core/lib/v3/api.ts +++ b/packages/core/lib/v3/api.ts @@ -360,7 +360,7 @@ export class StagehandAPIClient { const wireAgentConfig: Api.AgentExecuteRequest["agentConfig"] = { systemPrompt: agentConfig.systemPrompt, - mode: agentConfig.mode, + mode: agentConfig.mode ?? (agentConfig.cua === true ? "cua" : undefined), cua: agentConfig.mode === undefined ? agentConfig.cua : undefined, model: agentConfig.model ? (this.prepareModelConfig( From 9efc0db26760b9ca3d168cb189147f34b32dd8e0 Mon Sep 17 00:00:00 2001 From: ci-test Date: Mon, 26 Jan 2026 15:44:15 -0700 Subject: [PATCH 5/5] Added test --- .../test/integration/v3/agentExecute.test.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/server/test/integration/v3/agentExecute.test.ts b/packages/server/test/integration/v3/agentExecute.test.ts index 25aa03169..46e89853f 100644 --- a/packages/server/test/integration/v3/agentExecute.test.ts +++ b/packages/server/test/integration/v3/agentExecute.test.ts @@ -1033,4 +1033,32 @@ describe("POST /v1/sessions/:id/agentExecute - validation errors (V3)", () => { ctx, ); }); + + it("should return 400 for invalid agentConfig.mode", async () => { + const url = getBaseUrl(); + + const ctx = await fetchWithContext<{ + success?: boolean; + error?: string; + message?: string; + }>(`${url}/v1/sessions/${sessionId}/agentExecute`, { + method: "POST", + headers, + body: JSON.stringify({ + agentConfig: { + mode: "invalid-mode", + }, + executeOptions: { + instruction: "Do something", + }, + }), + }); + + assertFetchStatus(ctx, HTTP_BAD_REQUEST); + assertFetchOk( + !ctx.body?.success || ctx.body.error !== undefined, + "Response should indicate failure", + ctx, + ); + }); });