From 05898f31ce218abae61c1199e7944957661dab35 Mon Sep 17 00:00:00 2001 From: samfinton Date: Thu, 14 May 2026 20:46:11 +0200 Subject: [PATCH 1/3] STG-1756 add Vertex auth params --- packages/core/lib/v3/types/public/agent.ts | 3 +- packages/core/lib/v3/types/public/api.ts | 65 +++++++++- packages/core/lib/v3/types/public/model.ts | 18 ++- .../tests/unit/api-variables-schema.test.ts | 56 +++++++++ packages/server-v3/openapi.v3.yaml | 119 ++++++++++++++++++ packages/server-v3/scripts/gen-openapi.ts | 3 + 6 files changed, 258 insertions(+), 6 deletions(-) diff --git a/packages/core/lib/v3/types/public/agent.ts b/packages/core/lib/v3/types/public/agent.ts index 846ebe70e8..830fb1c966 100644 --- a/packages/core/lib/v3/types/public/agent.ts +++ b/packages/core/lib/v3/types/public/agent.ts @@ -445,7 +445,8 @@ export type AgentType = | "anthropic" | "google" | "microsoft" - | "bedrock"; + | "bedrock" + | "vertex"; export const AVAILABLE_CUA_MODELS = [ "openai/gpt-5.4", diff --git a/packages/core/lib/v3/types/public/api.ts b/packages/core/lib/v3/types/public/api.ts index 73b77ad52f..63326851dd 100644 --- a/packages/core/lib/v3/types/public/api.ts +++ b/packages/core/lib/v3/types/public/api.ts @@ -56,10 +56,61 @@ export const LocalBrowserLaunchOptionsSchema = z .meta({ id: "LocalBrowserLaunchOptions" }); /** Detailed model configuration object */ +export const GoogleServiceAccountCredentialsSchema = z + .object({ + type: z.string().optional(), + project_id: z.string().optional(), + private_key_id: z.string().optional(), + private_key: z.string().optional(), + client_email: z.string().optional(), + client_id: z.string().optional(), + auth_uri: z.string().optional(), + token_uri: z.string().optional(), + auth_provider_x509_cert_url: z.string().optional(), + client_x509_cert_url: z.string().optional(), + universe_domain: z.string().optional(), + }) + .catchall(z.unknown()) + .meta({ id: "GoogleServiceAccountCredentials" }); + +export const GoogleAuthOptionsSchema = z + .object({ + apiKey: z.string().optional().meta({ + description: + "API key used by google-auth-library for Vertex express mode", + }), + keyFilename: z.string().optional().meta({ + description: "Path to a Google Cloud service account key file", + }), + keyFile: z.string().optional().meta({ + description: "Path to a Google Cloud service account key file", + }), + credentials: GoogleServiceAccountCredentialsSchema.optional().meta({ + description: "Google Cloud service account credentials", + }), + clientOptions: z.record(z.string(), z.unknown()).optional().meta({ + description: + "Additional serializable options passed to the Google auth client", + }), + scopes: z + .union([z.string(), z.array(z.string())]) + .optional() + .meta({ + description: "Google auth scopes for the desired API request", + }), + projectId: z.string().optional().meta({ + description: "Google Cloud project ID used by google-auth-library", + }), + universeDomain: z.string().optional().meta({ + description: "Google Cloud universe domain", + }), + }) + .meta({ id: "GoogleAuthOptions" }); + export const ModelConfigObjectSchema = z .object({ provider: z - .enum(["openai", "anthropic", "google", "microsoft", "bedrock"]) + .enum(["openai", "anthropic", "google", "microsoft", "bedrock", "vertex"]) .optional() .meta({ description: @@ -83,6 +134,18 @@ export const ModelConfigObjectSchema = z description: "Custom headers sent with every request to the model provider", }), + project: z.string().optional().meta({ + description: "Google Cloud project ID for Vertex AI models", + example: "my-gcp-project", + }), + location: z.string().optional().meta({ + description: "Google Cloud location for Vertex AI models", + example: "us-central1", + }), + googleAuthOptions: GoogleAuthOptionsSchema.optional().meta({ + description: + "google-auth-library options used to authenticate Vertex AI models", + }), }) .meta({ id: "ModelConfigObject" }); diff --git a/packages/core/lib/v3/types/public/model.ts b/packages/core/lib/v3/types/public/model.ts index 0bc88365a2..947eae3c63 100644 --- a/packages/core/lib/v3/types/public/model.ts +++ b/packages/core/lib/v3/types/public/model.ts @@ -18,6 +18,7 @@ export type AnthropicClientOptions = Pick< >; export interface GoogleServiceAccountCredentials { + [key: string]: unknown; type?: string; project_id?: string; private_key_id?: string; @@ -31,13 +32,22 @@ export interface GoogleServiceAccountCredentials { universe_domain?: string; } +export interface GoogleVertexAuthOptions { + apiKey?: string; + keyFilename?: string; + keyFile?: string; + credentials?: GoogleServiceAccountCredentials; + clientOptions?: Record; + scopes?: string | string[]; + projectId?: string; + universeDomain?: string; +} + export type GoogleVertexProviderSettings = Pick< GoogleVertexProviderSettingsBase, - "project" | "location" | "headers" + "project" | "location" | "headers" | "baseURL" > & { - googleAuthOptions?: { - credentials?: GoogleServiceAccountCredentials; - }; + googleAuthOptions?: GoogleVertexAuthOptions; }; export type AnthropicJsonSchemaObject = { diff --git a/packages/core/tests/unit/api-variables-schema.test.ts b/packages/core/tests/unit/api-variables-schema.test.ts index ecd33723f5..2336737ede 100644 --- a/packages/core/tests/unit/api-variables-schema.test.ts +++ b/packages/core/tests/unit/api-variables-schema.test.ts @@ -62,3 +62,59 @@ describe("API variable schemas", () => { }); }); }); + +describe("API model config schemas", () => { + const vertexModel = { + provider: "vertex", + modelName: "vertex/gemini-2.5-flash", + project: "test-gcp-project", + location: "us-central1", + googleAuthOptions: { + apiKey: "vertex-express-key", + credentials: { + audience: + "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider", + client_email: "vertex@example.iam.gserviceaccount.com", + private_key: + "-----BEGIN PRIVATE KEY-----\\ntest\\n-----END PRIVATE KEY-----", + }, + clientOptions: { + eagerRefreshThresholdMillis: 300000, + forceRefreshOnFailure: true, + }, + scopes: ["https://www.googleapis.com/auth/cloud-platform"], + projectId: "test-gcp-project", + universeDomain: "googleapis.com", + }, + }; + + it("preserves Vertex auth params for act requests", () => { + const result = Api.ActRequestSchema.safeParse({ + input: "click the search button", + options: { + model: vertexModel, + }, + }); + + expect(result.success).toBe(true); + if (!result.success) throw result.error; + expect(result.data.options?.model).toEqual(vertexModel); + }); + + it("preserves Vertex auth params for agent model configs", () => { + const result = Api.AgentExecuteRequestSchema.safeParse({ + agentConfig: { + model: vertexModel, + executionModel: vertexModel, + }, + executeOptions: { + instruction: "find the search box", + }, + }); + + expect(result.success).toBe(true); + if (!result.success) throw result.error; + expect(result.data.agentConfig.model).toEqual(vertexModel); + expect(result.data.agentConfig.executionModel).toEqual(vertexModel); + }); +}); diff --git a/packages/server-v3/openapi.v3.yaml b/packages/server-v3/openapi.v3.yaml index fdc224dc94..5bec35db46 100644 --- a/packages/server-v3/openapi.v3.yaml +++ b/packages/server-v3/openapi.v3.yaml @@ -168,6 +168,66 @@ components: acceptDownloads: type: boolean additionalProperties: false + GoogleServiceAccountCredentials: + type: object + properties: + type: + type: string + project_id: + type: string + private_key_id: + type: string + private_key: + type: string + client_email: + type: string + client_id: + type: string + auth_uri: + type: string + token_uri: + type: string + auth_provider_x509_cert_url: + type: string + client_x509_cert_url: + type: string + universe_domain: + type: string + additionalProperties: {} + GoogleAuthOptions: + type: object + properties: + apiKey: + description: API key used by google-auth-library for Vertex express mode + type: string + keyFilename: + description: Path to a Google Cloud service account key file + type: string + keyFile: + description: Path to a Google Cloud service account key file + type: string + credentials: + description: Google Cloud service account credentials + $ref: "#/components/schemas/GoogleServiceAccountCredentials" + clientOptions: + description: Additional serializable options passed to the Google auth client + type: object + propertyNames: + type: string + additionalProperties: {} + scopes: + description: Google auth scopes for the desired API request + anyOf: + - type: string + - type: array + items: + type: string + projectId: + description: Google Cloud project ID used by google-auth-library + type: string + universeDomain: + description: Google Cloud universe domain + type: string ModelConfigObject: type: object properties: @@ -181,6 +241,7 @@ components: - google - microsoft - bedrock + - vertex modelName: description: Model name string with provider prefix (e.g., 'openai/gpt-5-nano') example: openai/gpt-5.4-mini @@ -201,6 +262,17 @@ components: type: string additionalProperties: type: string + project: + description: Google Cloud project ID for Vertex AI models + example: my-gcp-project + type: string + location: + description: Google Cloud location for Vertex AI models + example: us-central1 + type: string + googleAuthOptions: + description: google-auth-library options used to authenticate Vertex AI models + $ref: "#/components/schemas/GoogleAuthOptions" required: - modelName ModelConfig: @@ -1238,6 +1310,41 @@ components: acceptDownloads: type: boolean additionalProperties: false + GoogleAuthOptionsOutput: + type: object + properties: + apiKey: + description: API key used by google-auth-library for Vertex express mode + type: string + keyFilename: + description: Path to a Google Cloud service account key file + type: string + keyFile: + description: Path to a Google Cloud service account key file + type: string + credentials: + description: Google Cloud service account credentials + $ref: "#/components/schemas/GoogleServiceAccountCredentials" + clientOptions: + description: Additional serializable options passed to the Google auth client + type: object + propertyNames: + type: string + additionalProperties: {} + scopes: + description: Google auth scopes for the desired API request + anyOf: + - type: string + - type: array + items: + type: string + projectId: + description: Google Cloud project ID used by google-auth-library + type: string + universeDomain: + description: Google Cloud universe domain + type: string + additionalProperties: false ModelConfigObjectOutput: type: object properties: @@ -1251,6 +1358,7 @@ components: - google - microsoft - bedrock + - vertex modelName: description: Model name string with provider prefix (e.g., 'openai/gpt-5-nano') example: openai/gpt-5.4-mini @@ -1271,6 +1379,17 @@ components: type: string additionalProperties: type: string + project: + description: Google Cloud project ID for Vertex AI models + example: my-gcp-project + type: string + location: + description: Google Cloud location for Vertex AI models + example: us-central1 + type: string + googleAuthOptions: + description: google-auth-library options used to authenticate Vertex AI models + $ref: "#/components/schemas/GoogleAuthOptionsOutput" required: - modelName additionalProperties: false diff --git a/packages/server-v3/scripts/gen-openapi.ts b/packages/server-v3/scripts/gen-openapi.ts index 5bcfbe4272..0dbc1beee4 100644 --- a/packages/server-v3/scripts/gen-openapi.ts +++ b/packages/server-v3/scripts/gen-openapi.ts @@ -42,6 +42,9 @@ async function main() { BrowserbaseRegion: Api.BrowserbaseRegionSchema, // Shared components LocalBrowserLaunchOptions: Api.LocalBrowserLaunchOptionsSchema, + GoogleServiceAccountCredentials: + Api.GoogleServiceAccountCredentialsSchema, + GoogleAuthOptions: Api.GoogleAuthOptionsSchema, ModelConfigObject: Api.ModelConfigObjectSchema, ModelConfig: Api.ModelConfigSchema, Action: Api.ActionSchema, From 2e103f0cb4700722b67b8cb3bac9d8a9d583e845 Mon Sep 17 00:00:00 2001 From: samfinton Date: Tue, 19 May 2026 19:18:04 +0200 Subject: [PATCH 2/3] Harden Vertex auth schema --- packages/core/lib/v3/types/public/api.ts | 33 +++------- packages/core/lib/v3/types/public/model.ts | 13 ++-- .../tests/unit/api-variables-schema.test.ts | 52 +++++++++++++-- packages/server-v3/openapi.v3.yaml | 65 ++++--------------- 4 files changed, 72 insertions(+), 91 deletions(-) diff --git a/packages/core/lib/v3/types/public/api.ts b/packages/core/lib/v3/types/public/api.ts index 63326851dd..5e552a2415 100644 --- a/packages/core/lib/v3/types/public/api.ts +++ b/packages/core/lib/v3/types/public/api.ts @@ -58,40 +58,26 @@ export const LocalBrowserLaunchOptionsSchema = z /** Detailed model configuration object */ export const GoogleServiceAccountCredentialsSchema = z .object({ - type: z.string().optional(), - project_id: z.string().optional(), + type: z.literal("service_account"), + project_id: z.string(), private_key_id: z.string().optional(), - private_key: z.string().optional(), - client_email: z.string().optional(), + private_key: z.string(), + client_email: z.string(), client_id: z.string().optional(), - auth_uri: z.string().optional(), - token_uri: z.string().optional(), - auth_provider_x509_cert_url: z.string().optional(), - client_x509_cert_url: z.string().optional(), + auth_uri: z.url().optional(), + token_uri: z.url().optional(), + auth_provider_x509_cert_url: z.url().optional(), + client_x509_cert_url: z.url().optional(), universe_domain: z.string().optional(), }) - .catchall(z.unknown()) + .strict() .meta({ id: "GoogleServiceAccountCredentials" }); export const GoogleAuthOptionsSchema = z .object({ - apiKey: z.string().optional().meta({ - description: - "API key used by google-auth-library for Vertex express mode", - }), - keyFilename: z.string().optional().meta({ - description: "Path to a Google Cloud service account key file", - }), - keyFile: z.string().optional().meta({ - description: "Path to a Google Cloud service account key file", - }), credentials: GoogleServiceAccountCredentialsSchema.optional().meta({ description: "Google Cloud service account credentials", }), - clientOptions: z.record(z.string(), z.unknown()).optional().meta({ - description: - "Additional serializable options passed to the Google auth client", - }), scopes: z .union([z.string(), z.array(z.string())]) .optional() @@ -105,6 +91,7 @@ export const GoogleAuthOptionsSchema = z description: "Google Cloud universe domain", }), }) + .strict() .meta({ id: "GoogleAuthOptions" }); export const ModelConfigObjectSchema = z diff --git a/packages/core/lib/v3/types/public/model.ts b/packages/core/lib/v3/types/public/model.ts index 947eae3c63..69de265995 100644 --- a/packages/core/lib/v3/types/public/model.ts +++ b/packages/core/lib/v3/types/public/model.ts @@ -18,12 +18,11 @@ export type AnthropicClientOptions = Pick< >; export interface GoogleServiceAccountCredentials { - [key: string]: unknown; - type?: string; - project_id?: string; + type: "service_account"; + project_id: string; private_key_id?: string; - private_key?: string; - client_email?: string; + private_key: string; + client_email: string; client_id?: string; auth_uri?: string; token_uri?: string; @@ -33,11 +32,7 @@ export interface GoogleServiceAccountCredentials { } export interface GoogleVertexAuthOptions { - apiKey?: string; - keyFilename?: string; - keyFile?: string; credentials?: GoogleServiceAccountCredentials; - clientOptions?: Record; scopes?: string | string[]; projectId?: string; universeDomain?: string; diff --git a/packages/core/tests/unit/api-variables-schema.test.ts b/packages/core/tests/unit/api-variables-schema.test.ts index 2336737ede..7eba383479 100644 --- a/packages/core/tests/unit/api-variables-schema.test.ts +++ b/packages/core/tests/unit/api-variables-schema.test.ts @@ -70,17 +70,14 @@ describe("API model config schemas", () => { project: "test-gcp-project", location: "us-central1", googleAuthOptions: { - apiKey: "vertex-express-key", credentials: { - audience: - "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider", + type: "service_account", + project_id: "test-gcp-project", + private_key_id: "test-key-id", client_email: "vertex@example.iam.gserviceaccount.com", private_key: "-----BEGIN PRIVATE KEY-----\\ntest\\n-----END PRIVATE KEY-----", - }, - clientOptions: { - eagerRefreshThresholdMillis: 300000, - forceRefreshOnFailure: true, + token_uri: "https://oauth2.googleapis.com/token", }, scopes: ["https://www.googleapis.com/auth/cloud-platform"], projectId: "test-gcp-project", @@ -117,4 +114,45 @@ describe("API model config schemas", () => { expect(result.data.agentConfig.model).toEqual(vertexModel); expect(result.data.agentConfig.executionModel).toEqual(vertexModel); }); + + it("rejects Vertex key file paths and external credential sources", () => { + for (const blockedAuthOptions of [ + { keyFilename: "/etc/passwd" }, + { keyFile: "/etc/passwd" }, + { apiKey: "vertex-express-key" }, + ]) { + const result = Api.ActRequestSchema.safeParse({ + input: "click the search button", + options: { + model: { + provider: "vertex", + modelName: "vertex/gemini-2.5-flash", + googleAuthOptions: blockedAuthOptions, + }, + }, + }); + + expect(result.success).toBe(false); + } + + const externalAccountResult = Api.ActRequestSchema.safeParse({ + input: "click the search button", + options: { + model: { + provider: "vertex", + modelName: "vertex/gemini-2.5-flash", + googleAuthOptions: { + credentials: { + type: "external_account", + credential_source: { + file: "/etc/passwd", + }, + }, + }, + }, + }, + }); + + expect(externalAccountResult.success).toBe(false); + }); }); diff --git a/packages/server-v3/openapi.v3.yaml b/packages/server-v3/openapi.v3.yaml index 5bec35db46..31b085d15b 100644 --- a/packages/server-v3/openapi.v3.yaml +++ b/packages/server-v3/openapi.v3.yaml @@ -173,6 +173,7 @@ components: properties: type: type: string + const: service_account project_id: type: string private_key_id: @@ -185,36 +186,30 @@ components: type: string auth_uri: type: string + format: uri token_uri: type: string + format: uri auth_provider_x509_cert_url: type: string + format: uri client_x509_cert_url: type: string + format: uri universe_domain: type: string - additionalProperties: {} + required: + - type + - project_id + - private_key + - client_email + additionalProperties: false GoogleAuthOptions: type: object properties: - apiKey: - description: API key used by google-auth-library for Vertex express mode - type: string - keyFilename: - description: Path to a Google Cloud service account key file - type: string - keyFile: - description: Path to a Google Cloud service account key file - type: string credentials: description: Google Cloud service account credentials $ref: "#/components/schemas/GoogleServiceAccountCredentials" - clientOptions: - description: Additional serializable options passed to the Google auth client - type: object - propertyNames: - type: string - additionalProperties: {} scopes: description: Google auth scopes for the desired API request anyOf: @@ -228,6 +223,7 @@ components: universeDomain: description: Google Cloud universe domain type: string + additionalProperties: false ModelConfigObject: type: object properties: @@ -1310,41 +1306,6 @@ components: acceptDownloads: type: boolean additionalProperties: false - GoogleAuthOptionsOutput: - type: object - properties: - apiKey: - description: API key used by google-auth-library for Vertex express mode - type: string - keyFilename: - description: Path to a Google Cloud service account key file - type: string - keyFile: - description: Path to a Google Cloud service account key file - type: string - credentials: - description: Google Cloud service account credentials - $ref: "#/components/schemas/GoogleServiceAccountCredentials" - clientOptions: - description: Additional serializable options passed to the Google auth client - type: object - propertyNames: - type: string - additionalProperties: {} - scopes: - description: Google auth scopes for the desired API request - anyOf: - - type: string - - type: array - items: - type: string - projectId: - description: Google Cloud project ID used by google-auth-library - type: string - universeDomain: - description: Google Cloud universe domain - type: string - additionalProperties: false ModelConfigObjectOutput: type: object properties: @@ -1389,7 +1350,7 @@ components: type: string googleAuthOptions: description: google-auth-library options used to authenticate Vertex AI models - $ref: "#/components/schemas/GoogleAuthOptionsOutput" + $ref: "#/components/schemas/GoogleAuthOptions" required: - modelName additionalProperties: false From a41c76dfddfebe29c2d7aa04b08eeb5606858f19 Mon Sep 17 00:00:00 2001 From: samfinton Date: Tue, 19 May 2026 19:32:19 +0200 Subject: [PATCH 3/3] Relax Vertex service account credential schema --- packages/core/lib/v3/types/public/api.ts | 4 +-- packages/core/lib/v3/types/public/model.ts | 4 +-- .../tests/unit/api-variables-schema.test.ts | 36 +++++++++++++++++++ packages/server-v3/openapi.v3.yaml | 2 -- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/packages/core/lib/v3/types/public/api.ts b/packages/core/lib/v3/types/public/api.ts index 5e552a2415..d5696852bc 100644 --- a/packages/core/lib/v3/types/public/api.ts +++ b/packages/core/lib/v3/types/public/api.ts @@ -58,8 +58,8 @@ export const LocalBrowserLaunchOptionsSchema = z /** Detailed model configuration object */ export const GoogleServiceAccountCredentialsSchema = z .object({ - type: z.literal("service_account"), - project_id: z.string(), + type: z.literal("service_account").optional(), + project_id: z.string().optional(), private_key_id: z.string().optional(), private_key: z.string(), client_email: z.string(), diff --git a/packages/core/lib/v3/types/public/model.ts b/packages/core/lib/v3/types/public/model.ts index 69de265995..8e8e8a7e39 100644 --- a/packages/core/lib/v3/types/public/model.ts +++ b/packages/core/lib/v3/types/public/model.ts @@ -18,8 +18,8 @@ export type AnthropicClientOptions = Pick< >; export interface GoogleServiceAccountCredentials { - type: "service_account"; - project_id: string; + type?: "service_account"; + project_id?: string; private_key_id?: string; private_key: string; client_email: string; diff --git a/packages/core/tests/unit/api-variables-schema.test.ts b/packages/core/tests/unit/api-variables-schema.test.ts index 7eba383479..39364520de 100644 --- a/packages/core/tests/unit/api-variables-schema.test.ts +++ b/packages/core/tests/unit/api-variables-schema.test.ts @@ -98,6 +98,42 @@ describe("API model config schemas", () => { expect(result.data.options?.model).toEqual(vertexModel); }); + it("accepts minimal Vertex service account credentials", () => { + const result = Api.ActRequestSchema.safeParse({ + input: "click the search button", + options: { + model: { + provider: "vertex", + modelName: "vertex/gemini-2.5-flash", + project: "test-gcp-project", + location: "us-central1", + googleAuthOptions: { + credentials: { + client_email: "vertex@example.iam.gserviceaccount.com", + private_key: + "-----BEGIN PRIVATE KEY-----\\ntest\\n-----END PRIVATE KEY-----", + }, + }, + }, + }, + }); + + expect(result.success).toBe(true); + if (!result.success) throw result.error; + const parsedModel = result.data.options?.model; + expect(typeof parsedModel).toBe("object"); + if (typeof parsedModel !== "object" || parsedModel === null) { + throw new Error("Expected object model config"); + } + expect(parsedModel.googleAuthOptions).toEqual({ + credentials: { + client_email: "vertex@example.iam.gserviceaccount.com", + private_key: + "-----BEGIN PRIVATE KEY-----\\ntest\\n-----END PRIVATE KEY-----", + }, + }); + }); + it("preserves Vertex auth params for agent model configs", () => { const result = Api.AgentExecuteRequestSchema.safeParse({ agentConfig: { diff --git a/packages/server-v3/openapi.v3.yaml b/packages/server-v3/openapi.v3.yaml index 31b085d15b..92be95e3e6 100644 --- a/packages/server-v3/openapi.v3.yaml +++ b/packages/server-v3/openapi.v3.yaml @@ -199,8 +199,6 @@ components: universe_domain: type: string required: - - type - - project_id - private_key - client_email additionalProperties: false