stagehand 4.2.2a0.dev1606__tar.gz → 4.2.2a0.dev1615__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/PKG-INFO +1 -1
  2. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/pyproject.toml +1 -1
  3. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_extension/manifest.json +1 -1
  4. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_extension/service-worker.js +269 -105
  5. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_generated/input_types.py +30 -2
  6. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_generated/models.py +83 -7
  7. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_generated/protocol_version.py +1 -1
  8. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_validation.py +10 -0
  9. stagehand-4.2.2a0.dev1615/src/stagehand/browserbase_connection.py +46 -0
  10. stagehand-4.2.2a0.dev1615/src/stagehand/browserbase_rpc.py +114 -0
  11. stagehand-4.2.2a0.dev1615/src/stagehand/browserbase_transport.py +145 -0
  12. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/client_models.py +15 -2
  13. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/rpc_client.py +50 -6
  14. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/stagehand.py +26 -6
  15. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/README.md +0 -0
  16. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/__init__.py +0 -0
  17. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_extension/blank.html +0 -0
  18. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_extension/content-script.js +0 -0
  19. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_extension/offscreen/service-worker-heartbeat.html +0 -0
  20. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_extension/offscreen/service-worker-heartbeat.js +0 -0
  21. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_extension/wake-service-worker.html +0 -0
  22. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_extension/wake-service-worker.js +0 -0
  23. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_generated/__init__.py +0 -0
  24. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/_sdk_identity.py +0 -0
  25. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/browser.py +0 -0
  26. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/browser_clipboard.py +0 -0
  27. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/browser_context.py +0 -0
  28. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/browserbase_services.py +0 -0
  29. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/browserbase_session.py +0 -0
  30. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/cdp_client.py +0 -0
  31. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/client_types.py +0 -0
  32. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/extension_assets.py +0 -0
  33. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/file_upload.py +0 -0
  34. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/locator.py +0 -0
  35. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/page.py +0 -0
  36. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/py.typed +0 -0
  37. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/response.py +0 -0
  38. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/timeouts.py +0 -0
  39. {stagehand-4.2.2a0.dev1606 → stagehand-4.2.2a0.dev1615}/src/stagehand/webmcp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stagehand
3
- Version: 4.2.2a0.dev1606
3
+ Version: 4.2.2a0.dev1615
4
4
  Summary: Browserbase's SDK for building browser agents
5
5
  Keywords: ai,agent,ai-agent,browser,browser-agent,browser-automation,computer-use,headless,playwright,cdp,llm,automation,web-scraping,testing
6
6
  Author: Browserbase
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "stagehand"
3
- version = "4.2.2a0.dev1606"
3
+ version = "4.2.2a0.dev1615"
4
4
  description = "Browserbase's SDK for building browser agents"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -31,5 +31,5 @@
31
31
  }
32
32
  ],
33
33
  "options_page": "wake-service-worker.html",
34
- "version": "1.1.1"
34
+ "version": "1.2.0"
35
35
  }
@@ -7363,7 +7363,7 @@ function isRecord$1(value) {
7363
7363
  }
7364
7364
  var package_default$1 = {
7365
7365
  name: "@browserbasehq/stagehand-protocol",
7366
- version: "2.1.1",
7366
+ version: "3.0.0-alpha-c6b6f278c880d2f8b770ac0c1ecc63f248515968",
7367
7367
  "private": true,
7368
7368
  type: "module",
7369
7369
  exports: {
@@ -8039,7 +8039,7 @@ var CacheMetadataSchema = strictObject({
8039
8039
  status: CacheStatusSchema.meta({ description: "Whether server-side caching served this result, computed it, or was not consulted" }),
8040
8040
  count: number$1().int().nonnegative().optional().meta({ description: "Times this cache key has been seen, including this request; compare with threshold to see how close the key is to being served" }),
8041
8041
  threshold: number$1().int().positive().optional().meta({ description: "Hit-count threshold in effect for this key" }),
8042
- missReason: string().optional().meta({ description: "Why the cache did not serve this request; misses only. Reported by the server: \"not_found\", \"threshold\", \"empty_array\", \"timeout\", \"error\", \"bypass\", \"screenshot\", \"not_enabled\", \"no_cache_key\". Reported locally: \"read_failed\" (the cache request itself failed) and \"replay_failed\" (a cached value was found but could not be applied)" }),
8042
+ missReason: string().optional().meta({ description: "Why the cache did not serve this request; misses only. Reported by the server: \"not_found\", \"threshold\", \"empty_array\", \"timeout\", \"error\", \"bypass\", \"screenshot\", \"not_enabled\", \"no_cache_key\". Reported locally: \"timeout\" (the cache read exceeded its budget), \"read_failed\" (another cache read failure), \"payload_too_large\" (the serialized cache request exceeds 25,000,000 UTF-8 bytes), and \"replay_failed\" (a cached value was found but could not be applied)" }),
8043
8043
  tokensSaved: CacheTokenSavingsSchema.optional().meta({ description: "LLM tokens avoided by serving this request from cache; hits only" })
8044
8044
  }).meta({ id: "CacheMetadata" });
8045
8045
  var StagehandResultMetadataSchema = strictObject({
@@ -8295,6 +8295,47 @@ var LocatorDescriptorSchema = strictObject({
8295
8295
  pageId: string(),
8296
8296
  ...LocatorSchema.shape
8297
8297
  }).meta({ id: "LocatorDescriptor" });
8298
+ /** One HTTP attempt; the caller owns retries and response interpretation. */
8299
+ var BrowserbaseRequestParamsSchema = strictObject({
8300
+ operation: _enum([
8301
+ "gateway.responses",
8302
+ "cache.get",
8303
+ "cache.set"
8304
+ ]),
8305
+ headers: record(string(), string()).meta({ description: "Application headers. SDKs validate names and values, omit caller-supplied Host, Content-Length, hop-by-hop and forwarding headers, and supply x-bb-api-key and, for gateway.responses, x-bb-session-id." }),
8306
+ body: string().meta({ description: "JSON request body sent as UTF-8 text" }),
8307
+ remainingTimeoutMs: number$1().nonnegative().optional().meta({ description: "Remaining caller budget in milliseconds, including response-body buffering; omission adds no transport deadline" })
8308
+ }).meta({ id: "BrowserbaseRequestParams" });
8309
+ var BrowserbaseRequestResultSchema = discriminatedUnion("type", [strictObject({
8310
+ type: literal("response"),
8311
+ status: int().min(200).max(599),
8312
+ statusText: string(),
8313
+ headers: record(string(), string()).meta({ description: "Lowercase names with one value per name after SDK HTTP normalization; repeated fields are collapsed. Header bytes use Latin-1 code points. content-encoding and content-length are omitted." }),
8314
+ body: string().meta({ description: "Fully buffered response body decoded as UTF-8 text, with a leading BOM removed and invalid byte sequences replaced" })
8315
+ }).meta({
8316
+ id: "BrowserbaseHttpResponse",
8317
+ description: "Buffered HTTP response with SDK-normalized headers and text. Redirect responses are returned without following them."
8318
+ }), strictObject({
8319
+ type: literal("error"),
8320
+ reason: _enum([
8321
+ "network",
8322
+ "cancelled",
8323
+ "timeout",
8324
+ "unavailable",
8325
+ "disconnected"
8326
+ ]),
8327
+ message: string()
8328
+ }).meta({
8329
+ id: "BrowserbaseTransportError",
8330
+ description: "Transport failure without an HTTP response; disconnected is synthesized locally when RPC closes"
8331
+ })]).meta({ id: "BrowserbaseRequestResult" });
8332
+ var BrowserbaseCancelParamsSchema = strictObject({
8333
+ requestId: JSONRPCRequestIdSchema,
8334
+ reason: _enum(["cancelled", "timeout"])
8335
+ }).meta({
8336
+ id: "BrowserbaseCancelParams",
8337
+ description: "Cancel a browserbase.request by its JSON-RPC ID on the same connection; ignore completed or unknown IDs"
8338
+ });
8298
8339
  var ImplementationInfoSchema = strictObject({
8299
8340
  name: string().min(1),
8300
8341
  version: string().min(1)
@@ -8311,8 +8352,7 @@ var StagehandInitParamsSchema = strictObject({
8311
8352
  protocolVersion: StagehandProtocolVersionSchema,
8312
8353
  clientInfo: ImplementationInfoSchema,
8313
8354
  browserCdpUrl: string().min(1).optional(),
8314
- apiKey: string().min(1).optional(),
8315
- apiUrl: url().refine((value) => !value.includes("?") && !value.includes("#"), "Stagehand apiUrl must not include a query string or fragment").refine((value) => !new URL(value).pathname.replace(/\/+$/, "").endsWith("/v1"), { message: "Stagehand apiUrl must be a service origin without /v1" }).optional().meta({ description: "Stagehand API base URL override for managed services such as Model Gateway and server-side caching" }),
8355
+ browserbase: literal("client").optional(),
8316
8356
  browser: BrowserSessionMetadataSchema.optional(),
8317
8357
  model: union([ModelConfigSchema, ClientModelReferenceSchema]).optional().meta({ description: "Default model configuration; when omitted and a Browserbase Model Gateway session is available, Browserbase selects a model automatically for inference calls" }),
8318
8358
  telemetry: TelemetryConfigSchema.optional(),
@@ -8326,8 +8366,14 @@ var StagehandInitParamsSchema = strictObject({
8326
8366
  systemPrompt: string().optional(),
8327
8367
  selfHeal: boolean().optional(),
8328
8368
  domSettleTimeoutMs: number$1().int().positive().optional(),
8329
- cache: CachingSchema.optional().meta({ description: "Server-side caching of act/observe/extract results for this instance: a boolean toggle, or an object with an optional hit-count threshold. Requires a Browserbase apiKey and browser sessionId. Can be overridden per request via options.cache." })
8330
- }).meta({ id: "StagehandInitParams" });
8369
+ cache: CachingSchema.optional().meta({ description: "Server-side caching of act/observe/extract results for this instance: a boolean toggle, or an object with an optional hit-count threshold. Requires client Browserbase transport and a browser sessionId. Can be overridden per request via options.cache." })
8370
+ }).refine((params) => params.browserbase === void 0 || params.browser !== void 0, {
8371
+ message: "Client Browserbase transport requires browser metadata",
8372
+ path: ["browserbase"]
8373
+ }).meta({
8374
+ id: "StagehandInitParams",
8375
+ dependentRequired: { browserbase: ["browser"] }
8376
+ });
8331
8377
  var StagehandActParamsSchema = strictObject({
8332
8378
  pageId: string().min(1),
8333
8379
  instruction: union([string().min(1), ActionSchema]),
@@ -8658,6 +8704,11 @@ var StagehandMethods = {
8658
8704
  paramsWire: { opaqueKeys: ["input"] },
8659
8705
  resultWire: { opaqueKeys: ["value"] }
8660
8706
  },
8707
+ browserbaseRequest: {
8708
+ name: "browserbase.request",
8709
+ params: BrowserbaseRequestParamsSchema,
8710
+ result: BrowserbaseRequestResultSchema
8711
+ },
8661
8712
  llmGenerate: {
8662
8713
  name: "llm.generate",
8663
8714
  params: LLMGenerateParamsSchema,
@@ -9036,6 +9087,11 @@ var StagehandRpcRequestSchema = union(Object.values(StagehandMethods).map((metho
9036
9087
  params: wireSchema(method.params, "paramsWire" in method ? method.paramsWire : void 0)
9037
9088
  }))).meta({ id: "StagehandRpcRuntimeRequest" });
9038
9089
  var StagehandNotifications = {
9090
+ browserbaseCancel: {
9091
+ name: "browserbase.cancel",
9092
+ params: BrowserbaseCancelParamsSchema,
9093
+ paramsWire: void 0
9094
+ },
9039
9095
  log: {
9040
9096
  name: "stagehand.log",
9041
9097
  params: StagehandLogSchema,
@@ -9061,6 +9117,45 @@ function getStagehandMethod(name) {
9061
9117
  return stagehandMethodsByName.get(name);
9062
9118
  }
9063
9119
  //#endregion
9120
+ //#region clients/browserbaseClient.ts
9121
+ /** Requests one buffered Browserbase HTTP attempt over the connected SDK. */
9122
+ async function requestBrowserbase(client, params, signal) {
9123
+ let result;
9124
+ try {
9125
+ result = await client.send(StagehandMethods.browserbaseRequest, params, {
9126
+ signal,
9127
+ onAbort: (requestId, reason) => client.notify(StagehandNotifications.browserbaseCancel, {
9128
+ requestId,
9129
+ reason: isTimeout(reason) ? "timeout" : "cancelled"
9130
+ })
9131
+ });
9132
+ } catch (error) {
9133
+ if (signal?.aborted) throw new DOMException(isTimeout(signal.reason) ? "Browserbase request timed out" : "Browserbase request cancelled", isTimeout(signal.reason) ? "TimeoutError" : "AbortError");
9134
+ throw new Error(client.closed ? "Browserbase connection is disconnected" : "Browserbase RPC request failed", { cause: { error } });
9135
+ }
9136
+ if (result.type === "error") switch (result.reason) {
9137
+ case "network": throw new TypeError("fetch failed", { cause: result });
9138
+ case "cancelled": throw new DOMException(result.message, "AbortError");
9139
+ case "timeout": throw new DOMException(result.message, "TimeoutError");
9140
+ case "unavailable":
9141
+ case "disconnected": throw new Error(result.message, { cause: result });
9142
+ }
9143
+ const response = new Response([
9144
+ 204,
9145
+ 205,
9146
+ 304
9147
+ ].includes(result.status) ? null : result.body, {
9148
+ status: result.status,
9149
+ statusText: result.statusText,
9150
+ headers: result.headers
9151
+ });
9152
+ if (!new Headers(result.headers).has("content-type")) response.headers.delete("content-type");
9153
+ return response;
9154
+ }
9155
+ function isTimeout(reason) {
9156
+ return reason instanceof Error && reason.name === "TimeoutError";
9157
+ }
9158
+ //#endregion
9064
9159
  //#region clients/chromeRuntimeClient.ts
9065
9160
  var ChromeBindingMessageSchema = string();
9066
9161
  var ChromeBindingSchema = custom((value) => typeof value === "function", "Chrome runtime binding is not installed");
@@ -11265,7 +11360,8 @@ var RPCClient = class {
11265
11360
  this.runtime.onclose = (reason) => this.close(reason);
11266
11361
  this.runtime.onerror = (error) => this.close(error);
11267
11362
  }
11268
- async send(method, params) {
11363
+ async send(method, params, options = {}) {
11364
+ options.signal?.throwIfAborted();
11269
11365
  if (this.closed) throw new Error("RPC client is closed");
11270
11366
  const parentContext = context.active();
11271
11367
  const span = this.router.runtime.tracing.tracer.startSpan(method.name, {
@@ -11288,10 +11384,32 @@ var RPCClient = class {
11288
11384
  ...getTraceContextFields(requestContext)
11289
11385
  });
11290
11386
  span.setAttribute("jsonrpc.request.id", String(request.id));
11291
- const response = this.waitForResponse(id, method);
11292
- const [, result] = await Promise.all([this.runtime.send(request).catch((error) => {
11293
- this.rejectPending(id, asError(error));
11294
- }), response]);
11387
+ const { signal } = options;
11388
+ let dispatched = false;
11389
+ let aborted = false;
11390
+ const notifyAbort = () => {
11391
+ if (this.closed) return;
11392
+ try {
11393
+ Promise.resolve(options.onAbort?.(id, signal?.reason)).catch(() => {});
11394
+ } catch {}
11395
+ };
11396
+ const abort = () => {
11397
+ if (!this.pending.has(id)) return;
11398
+ aborted = true;
11399
+ this.rejectPending(id, asError(signal?.reason));
11400
+ if (dispatched) notifyAbort();
11401
+ };
11402
+ const response = this.waitForResponse(id, method, () => signal?.removeEventListener("abort", abort));
11403
+ signal?.addEventListener("abort", abort, { once: true });
11404
+ if (signal?.aborted) {
11405
+ abort();
11406
+ return await response;
11407
+ }
11408
+ const dispatch = this.runtime.send(request).then(() => {
11409
+ dispatched = true;
11410
+ if (aborted) notifyAbort();
11411
+ }, (error) => this.rejectPending(id, asError(error)));
11412
+ const [, result] = await Promise.all([dispatch, response]);
11295
11413
  return result;
11296
11414
  });
11297
11415
  } catch (error) {
@@ -11318,12 +11436,18 @@ var RPCClient = class {
11318
11436
  this.runtime.onerror = void 0;
11319
11437
  this.runtime.close();
11320
11438
  }
11321
- waitForResponse(id, method) {
11439
+ waitForResponse(id, method, cleanup = () => {}) {
11322
11440
  return new Promise((resolve, reject) => {
11323
11441
  this.pending.set(id, {
11324
11442
  method,
11325
- resolve,
11326
- reject
11443
+ resolve(value) {
11444
+ cleanup();
11445
+ resolve(value);
11446
+ },
11447
+ reject(error) {
11448
+ cleanup();
11449
+ reject(error);
11450
+ }
11327
11451
  });
11328
11452
  });
11329
11453
  }
@@ -11716,7 +11840,7 @@ async function normalizeFile(file) {
11716
11840
  if (!file.name) throw new TypeError("setInputFiles(): file payload name cannot be empty");
11717
11841
  if (typeof file.buffer === "string") {
11718
11842
  const nodeBuffer = globalThis.Buffer;
11719
- if (nodeBuffer ? nodeBuffer.byteLength(file.buffer, "utf8") > MAX_INPUT_FILE_BYTES : utf8ByteLengthExceeds(file.buffer, MAX_INPUT_FILE_BYTES)) throw new RangeError(`setInputFiles(): file is larger than the 50 MiB upload limit`);
11843
+ if (nodeBuffer ? nodeBuffer.byteLength(file.buffer, "utf8") > MAX_INPUT_FILE_BYTES : utf8ByteLengthExceeds$1(file.buffer, MAX_INPUT_FILE_BYTES)) throw new RangeError(`setInputFiles(): file is larger than the 50 MiB upload limit`);
11720
11844
  }
11721
11845
  const bytes = typeof file.buffer === "string" ? new TextEncoder().encode(file.buffer) : file.buffer instanceof ArrayBuffer ? new Uint8Array(file.buffer) : file.buffer;
11722
11846
  if (bytes.byteLength > MAX_INPUT_FILE_BYTES) throw new RangeError(`setInputFiles(): file is larger than the 50 MiB upload limit`);
@@ -11728,7 +11852,7 @@ async function normalizeFile(file) {
11728
11852
  ...file.lastModified === void 0 ? {} : { lastModified: file.lastModified }
11729
11853
  };
11730
11854
  }
11731
- function utf8ByteLengthExceeds(value, limit) {
11855
+ function utf8ByteLengthExceeds$1(value, limit) {
11732
11856
  let byteLength = 0;
11733
11857
  for (let index = 0; index < value.length; index++) {
11734
11858
  const codeUnit = value.charCodeAt(index);
@@ -12522,13 +12646,16 @@ var StagehandClientExtractOptionsSchema = ExtractOptionsSchema.extend({
12522
12646
  ignoreLocators: array$1(_instanceof(Locator$1)).optional(),
12523
12647
  page: _instanceof(Page$1).optional()
12524
12648
  }).meta({ id: "StagehandClientExtractOptions" });
12525
- var StagehandClientCreateConfigSchema = StagehandInitParamsSchema.omit({
12649
+ var StagehandClientCreateConfigSchema = strictObject(StagehandInitParamsSchema.shape).omit({
12650
+ browserbase: true,
12526
12651
  protocolVersion: true,
12527
12652
  clientInfo: true,
12528
12653
  browserCdpUrl: true,
12529
12654
  logLevel: true,
12530
12655
  browser: true
12531
12656
  }).extend({
12657
+ apiKey: string().min(1).optional().meta({ description: "Browserbase API key. A Browserbase browser uses the key supplied to its launch/connect factory; this option does not override that key." }),
12658
+ apiUrl: url({ protocol: /^https?$/ }).refine((value) => !value.includes("?") && !value.includes("#"), "Stagehand apiUrl must not include a query string or fragment").refine((value) => !new URL(value).pathname.replace(/\/+$/, "").endsWith("/v1"), { message: "Stagehand apiUrl must be a service origin without /v1" }).optional().meta({ description: "Stagehand API base URL override for managed services such as Model Gateway and server-side caching" }),
12532
12659
  model: union([ModelConfigSchema, ClientLLMSchema]).optional(),
12533
12660
  logging: StagehandClientLoggingConfigSchema.default({
12534
12661
  level: "info",
@@ -18248,10 +18375,10 @@ function mergeFramesIntoSnapshot(context, perFrameMaps, perFrameOutlines, absPre
18248
18375
  //#endregion
18249
18376
  //#region clients/cacheClient.ts
18250
18377
  /**
18251
- * HTTP client for the Stagehand API's stateless cache routes (POST /cache/get
18252
- * and POST /cache/set). Owns transport concerns only: endpoint resolution,
18253
- * auth headers, timeouts, and schema validation of requests going out and
18254
- * responses coming back. All caching decisions (when to read, what to write,
18378
+ * Client for the Stagehand API's stateless cache routes (POST /cache/get
18379
+ * and POST /cache/set). Sends requests through the connected SDK and owns
18380
+ * request size limits, timeouts, and response validation.
18381
+ * All caching decisions (when to read, what to write,
18255
18382
  * fallback behavior) live in services/cacheService.ts, this client's only
18256
18383
  * consumer. See stagehand-api-v3's lib/cache/statelessCache.ts for the
18257
18384
  * server-side contract.
@@ -18259,6 +18386,8 @@ function mergeFramesIntoSnapshot(context, perFrameMaps, perFrameOutlines, absPre
18259
18386
  /** Ceiling for a single cache HTTP round-trip so a slow cache can only ever
18260
18387
  * add a bounded delay to the action it fronts. */
18261
18388
  var CACHE_REQUEST_TIMEOUT_MS = 5e3;
18389
+ /** Maximum UTF-8 JSON body size for a cache read or write. */
18390
+ var CACHE_REQUEST_MAX_BYTES = 25e6;
18262
18391
  var AXNodeSchema = custom((value) => typeof value === "object" && value !== null);
18263
18392
  var CdpTreeSchema = object$1({
18264
18393
  rootFrameId: string(),
@@ -18295,7 +18424,7 @@ object$1({
18295
18424
  llmUsage: CacheLlmUsageSchema.optional()
18296
18425
  });
18297
18426
  var CacheGetResponseSchema = object$1({
18298
- /** Null only when the server could not build a key (empty tree / bad URL). */
18427
+ /** Null when no cache key is available, including a locally skipped request. */
18299
18428
  cacheKey: string().nullable(),
18300
18429
  hit: boolean(),
18301
18430
  /** Present only on a hit. */
@@ -18317,7 +18446,7 @@ var CacheGetResponseSchema = object$1({
18317
18446
  }).optional()
18318
18447
  });
18319
18448
  var CacheSetResponseSchema = object$1({
18320
- /** Null only when the server could not build a key. */
18449
+ /** Null when no cache key is available, including a locally skipped request. */
18321
18450
  cacheKey: string().nullable(),
18322
18451
  /** True when the write was issued to Redis. */
18323
18452
  written: boolean(),
@@ -18332,9 +18461,8 @@ var ApiResponseEnvelopeSchema = discriminatedUnion("success", [object$1({
18332
18461
  message: string().optional()
18333
18462
  })]);
18334
18463
  var CacheClient = class {
18335
- constructor(apiUrl, apiKey) {
18336
- this.apiUrl = apiUrl;
18337
- this.apiKey = apiKey;
18464
+ constructor(transport) {
18465
+ this.transport = transport;
18338
18466
  }
18339
18467
  async get(request) {
18340
18468
  return await this.post("/cache/get", request, CacheGetResponseSchema);
@@ -18343,51 +18471,67 @@ var CacheClient = class {
18343
18471
  return await this.post("/cache/set", request, CacheSetResponseSchema);
18344
18472
  }
18345
18473
  async post(path, request, responseSchema) {
18346
- const response = await fetch(`${this.apiUrl}${path}`, {
18347
- method: "POST",
18348
- headers: {
18349
- "content-type": "application/json",
18350
- "x-bb-api-key": this.apiKey
18351
- },
18352
- body: JSON.stringify(request),
18353
- signal: AbortSignal.timeout(CACHE_REQUEST_TIMEOUT_MS)
18354
- });
18355
- if (!response.ok) throw new Error(`Cache request ${path} failed with status ${response.status}`);
18356
- const envelope = ApiResponseEnvelopeSchema.safeParse(await response.json());
18357
- if (!envelope.success) throw new Error(`Cache request ${path} returned an unrecognized envelope`);
18358
- if (!envelope.data.success) throw new Error(`Cache request ${path} rejected: ${envelope.data.message ?? "unknown error"}`);
18359
- const parsed = responseSchema.safeParse(envelope.data.data);
18360
- if (!parsed.success) throw new Error(`Cache request ${path} returned an unexpected payload: ${parsed.error.message}`);
18361
- return parsed.data;
18474
+ const deadline = performance.now() + CACHE_REQUEST_TIMEOUT_MS;
18475
+ const controller = new AbortController();
18476
+ const timeout = () => controller.abort(new DOMException("Cache request timed out", "TimeoutError"));
18477
+ const timer = setTimeout(timeout, CACHE_REQUEST_TIMEOUT_MS);
18478
+ try {
18479
+ const body = JSON.stringify(request);
18480
+ if (utf8ByteLengthExceeds(body, CACHE_REQUEST_MAX_BYTES)) return responseSchema.parse(path === "/cache/get" ? {
18481
+ cacheKey: null,
18482
+ hit: false,
18483
+ missReason: "payload_too_large"
18484
+ } : {
18485
+ cacheKey: null,
18486
+ written: false,
18487
+ skippedReason: "payload_too_large"
18488
+ });
18489
+ const remainingTimeoutMs = Math.max(0, deadline - performance.now());
18490
+ if (remainingTimeoutMs === 0) timeout();
18491
+ controller.signal.throwIfAborted();
18492
+ if (!this.transport.request) throw new Error("Client Browserbase transport is unavailable");
18493
+ const response = await this.transport.request({
18494
+ operation: path === "/cache/get" ? "cache.get" : "cache.set",
18495
+ headers: { "content-type": "application/json" },
18496
+ body,
18497
+ remainingTimeoutMs
18498
+ }, controller.signal);
18499
+ if (!response.ok) throw new Error(`Cache request ${path} failed with status ${response.status}`);
18500
+ const envelope = ApiResponseEnvelopeSchema.safeParse(await response.json());
18501
+ if (!envelope.success) throw new Error(`Cache request ${path} returned an unrecognized envelope`);
18502
+ if (!envelope.data.success) throw new Error(`Cache request ${path} rejected: ${envelope.data.message ?? "unknown error"}`);
18503
+ const parsed = responseSchema.safeParse(envelope.data.data);
18504
+ if (!parsed.success) throw new Error(`Cache request ${path} returned an unexpected payload: ${parsed.error.message}`);
18505
+ return parsed.data;
18506
+ } finally {
18507
+ clearTimeout(timer);
18508
+ }
18362
18509
  }
18363
18510
  };
18364
- //#endregion
18365
- //#region clients/stagehandApi.ts
18366
- var REGION_API_URLS = {
18367
- "us-west-2": "https://api.stagehand.browserbase.com",
18368
- "us-east-1": "https://api.use1.stagehand.browserbase.com",
18369
- "eu-central-1": "https://api.euc1.stagehand.browserbase.com",
18370
- "ap-southeast-1": "https://api.apse1.stagehand.browserbase.com"
18371
- };
18372
- var DEFAULT_REGION = "us-west-2";
18373
- /** Resolves the regional Stagehand API endpoint for a Browserbase session. */
18374
- function apiUrlForRegion(region, apiUrl) {
18375
- return `${(apiUrl ?? REGION_API_URLS[region ?? DEFAULT_REGION]).replace(/\/+$/, "")}/v1`;
18511
+ function utf8ByteLengthExceeds(value, limit) {
18512
+ if (value.length > limit) return true;
18513
+ let byteLength = 0;
18514
+ for (let index = 0; index < value.length; index++) {
18515
+ const codeUnit = value.charCodeAt(index);
18516
+ if (codeUnit <= 127) byteLength += 1;
18517
+ else if (codeUnit <= 2047) byteLength += 2;
18518
+ else if (codeUnit >= 55296 && codeUnit <= 56319 && index + 1 < value.length && value.charCodeAt(index + 1) >= 56320 && value.charCodeAt(index + 1) <= 57343) {
18519
+ byteLength += 4;
18520
+ index += 1;
18521
+ } else byteLength += 3;
18522
+ if (byteLength > limit) return true;
18523
+ }
18524
+ return false;
18376
18525
  }
18377
18526
  //#endregion
18378
18527
  //#region services/cacheService.ts
18379
- /**
18380
- * Builds the per-instance cache context from init params. Returns undefined
18381
- * when caching is impossible: the stateless routes authenticate with the
18382
- * Browserbase API key and resolve the project from the session id, so both
18383
- * are required.
18384
- */
18385
- function buildCacheContext(initParams) {
18528
+ /** Builds a cache context when client Browserbase transport is configured. */
18529
+ function buildCacheContext(initParams, request) {
18386
18530
  const sessionId = initParams.browser?.sessionId;
18387
- if (!initParams.apiKey || !sessionId) return void 0;
18531
+ if (!sessionId || initParams.browserbase !== "client") return void 0;
18388
18532
  return {
18389
18533
  sessionId,
18390
- client: new CacheClient(apiUrlForRegion(initParams.browser?.region, initParams.apiUrl), initParams.apiKey),
18534
+ client: new CacheClient({ request }),
18391
18535
  defaultCaching: initParams.cache ?? true
18392
18536
  };
18393
18537
  }
@@ -18508,10 +18652,12 @@ async function withCache({ method, page, data, caching, bypass, context, logger,
18508
18652
  data: withCacheThreshold(data, resolvedCaching)
18509
18653
  };
18510
18654
  let getResponse = null;
18655
+ let readFailureReason = "read_failed";
18511
18656
  try {
18512
18657
  getResponse = await runLocatorStep(progress, "reading cache", () => context.client.get(baseRequest));
18513
18658
  } catch (error) {
18514
18659
  progress?.throwIfStopped();
18660
+ readFailureReason = error instanceof Error && error.name === "TimeoutError" ? "timeout" : "read_failed";
18515
18661
  logger.warn("Cache read failed; executing without cache", {
18516
18662
  category: "cache",
18517
18663
  method,
@@ -18548,12 +18694,12 @@ async function withCache({ method, page, data, caching, bypass, context, logger,
18548
18694
  cacheKey: getResponse.cacheKey ?? "",
18549
18695
  missReason: cacheMetadata.missReason ?? "unknown"
18550
18696
  });
18551
- } else cacheMetadata = missMetadata(null, "read_failed");
18697
+ } else cacheMetadata = missMetadata(null, readFailureReason);
18552
18698
  progress?.throwIfStopped();
18553
18699
  const outcome = await execute();
18554
18700
  progress?.throwIfStopped();
18555
18701
  outcome.result.metadata.cache = cacheMetadata;
18556
- if (outcome.cacheValue !== void 0 && outcome.cacheValue !== null) try {
18702
+ if (getResponse?.missReason !== "payload_too_large" && outcome.cacheValue !== void 0 && outcome.cacheValue !== null) try {
18557
18703
  const setResponse = await runLocatorStep(progress, "writing cache", () => context.client.set({
18558
18704
  ...baseRequest,
18559
18705
  value: outcome.cacheValue,
@@ -51076,43 +51222,53 @@ async function generateWithClientLlm(request, input) {
51076
51222
  }
51077
51223
  //#endregion
51078
51224
  //#region llm/gatewayClient.ts
51079
- /** Builds the credentials needed for Browserbase Model Gateway inference. */
51080
- function buildGatewayContext(initParams) {
51081
- const sessionId = initParams.browser?.sessionId;
51082
- if (!initParams.apiKey || !sessionId) return void 0;
51083
- return {
51084
- apiUrl: apiUrlForRegion(initParams.browser?.region, initParams.apiUrl),
51085
- apiKey: initParams.apiKey,
51086
- sessionId
51225
+ var CLIENT_GATEWAY_BASE_URL = "https://stagehand.invalid/llm";
51226
+ var CLIENT_GATEWAY_API_KEY = "stagehand-client-gateway";
51227
+ /** Selects the configured transport for Browserbase Model Gateway inference. */
51228
+ function buildGatewayContext(initParams, request) {
51229
+ if (!initParams.browser?.sessionId || initParams.browserbase !== "client") return void 0;
51230
+ return { request };
51231
+ }
51232
+ function withoutModel(fetch) {
51233
+ return async (input, init) => {
51234
+ if (typeof init?.body !== "string") return await fetch(input, init);
51235
+ let body;
51236
+ try {
51237
+ body = JSON.parse(init.body);
51238
+ } catch {
51239
+ return await fetch(input, init);
51240
+ }
51241
+ if (!body || typeof body !== "object" || Array.isArray(body)) return await fetch(input, init);
51242
+ const routedBody = { ...body };
51243
+ delete routedBody.model;
51244
+ return await fetch(input, {
51245
+ ...init,
51246
+ body: JSON.stringify(routedBody)
51247
+ });
51248
+ };
51249
+ }
51250
+ function clientGatewayFetch(gateway, config) {
51251
+ const hasAuthorization = Object.keys(config?.headers ?? {}).some((name) => name.toLowerCase() === "authorization");
51252
+ return async (input, init) => {
51253
+ if (input !== `${CLIENT_GATEWAY_BASE_URL}/responses` || init?.method !== "POST" || typeof init.body !== "string") throw new TypeError("Unexpected Model Gateway request");
51254
+ if (!gateway.request) throw new Error("Client Browserbase transport is unavailable");
51255
+ const headers = new Headers(init.headers);
51256
+ if (!hasAuthorization) headers.delete("authorization");
51257
+ return await gateway.request({
51258
+ operation: "gateway.responses",
51259
+ headers: Object.fromEntries(headers),
51260
+ body: init.body
51261
+ }, init.signal ?? void 0);
51087
51262
  };
51088
51263
  }
51089
- var fetchWithoutModel = async (input, init) => {
51090
- if (typeof init?.body !== "string") return await boundFetch(input, init);
51091
- let body;
51092
- try {
51093
- body = JSON.parse(init.body);
51094
- } catch {
51095
- return await boundFetch(input, init);
51096
- }
51097
- if (!body || typeof body !== "object" || Array.isArray(body)) return await boundFetch(input, init);
51098
- const routedBody = { ...body };
51099
- delete routedBody.model;
51100
- return await boundFetch(input, {
51101
- ...init,
51102
- body: JSON.stringify(routedBody)
51103
- });
51104
- };
51105
51264
  /** Creates an OpenAI Responses model backed by Browserbase Model Gateway. */
51106
51265
  function createGatewayLanguageModel(config, gateway) {
51266
+ const fetch = clientGatewayFetch(gateway, config);
51107
51267
  return createOpenAI({
51108
- apiKey: gateway.apiKey,
51109
- baseURL: `${gateway.apiUrl}/llm`,
51110
- headers: {
51111
- ...config?.headers,
51112
- "x-bb-api-key": gateway.apiKey,
51113
- "x-bb-session-id": gateway.sessionId
51114
- },
51115
- fetch: config ? boundFetch : fetchWithoutModel
51268
+ apiKey: CLIENT_GATEWAY_API_KEY,
51269
+ baseURL: CLIENT_GATEWAY_BASE_URL,
51270
+ headers: config?.headers,
51271
+ fetch: config ? fetch : withoutModel(fetch)
51116
51272
  }).responses(config?.modelName ?? "auto");
51117
51273
  }
51118
51274
  //#endregion
@@ -51726,7 +51882,7 @@ function createStagehandController(runtime, options = {}) {
51726
51882
  const state = runtime.state.getState();
51727
51883
  if (state.status !== "initialized") throw new Error("Stagehand must be initialized before acting");
51728
51884
  const model = params.options?.model ?? state.initParams.model;
51729
- const gateway = buildGatewayContext(state.initParams);
51885
+ const gateway = buildGatewayContext(state.initParams, runtime.adapters.clientBrowserbaseRequest);
51730
51886
  if (!model && !gateway) throw new Error("An LLM was not configured during Stagehand initialization");
51731
51887
  const result = await act({
51732
51888
  params,
@@ -51737,7 +51893,7 @@ function createStagehandController(runtime, options = {}) {
51737
51893
  systemPrompt: state.initParams.systemPrompt,
51738
51894
  selfHeal: state.initParams.selfHeal,
51739
51895
  domSettleTimeoutMs: state.initParams.domSettleTimeoutMs,
51740
- cache: buildCacheContext(state.initParams),
51896
+ cache: buildCacheContext(state.initParams, runtime.adapters.clientBrowserbaseRequest),
51741
51897
  gateway
51742
51898
  });
51743
51899
  runtime.metrics.record("act", result.metadata.usage);
@@ -51750,7 +51906,7 @@ function createStagehandController(runtime, options = {}) {
51750
51906
  const state = runtime.state.getState();
51751
51907
  if (state.status !== "initialized") throw new Error("Stagehand must be initialized before observing");
51752
51908
  const model = params.options?.model ?? state.initParams.model;
51753
- const gateway = buildGatewayContext(state.initParams);
51909
+ const gateway = buildGatewayContext(state.initParams, runtime.adapters.clientBrowserbaseRequest);
51754
51910
  if (!model && !gateway) throw new Error("An LLM was not configured during Stagehand initialization");
51755
51911
  const result = await observe({
51756
51912
  params,
@@ -51759,7 +51915,7 @@ function createStagehandController(runtime, options = {}) {
51759
51915
  clientLLMGenerate: runtime.adapters.clientLLMGenerate,
51760
51916
  logger,
51761
51917
  systemPrompt: state.initParams.systemPrompt,
51762
- cache: buildCacheContext(state.initParams),
51918
+ cache: buildCacheContext(state.initParams, runtime.adapters.clientBrowserbaseRequest),
51763
51919
  gateway
51764
51920
  });
51765
51921
  runtime.metrics.record("observe", result.metadata.usage);
@@ -51772,7 +51928,7 @@ function createStagehandController(runtime, options = {}) {
51772
51928
  const state = runtime.state.getState();
51773
51929
  if (state.status !== "initialized") throw new Error("Stagehand must be initialized before extracting");
51774
51930
  const model = params.options?.model ?? state.initParams.model;
51775
- const gateway = buildGatewayContext(state.initParams);
51931
+ const gateway = buildGatewayContext(state.initParams, runtime.adapters.clientBrowserbaseRequest);
51776
51932
  if (!model && !gateway) throw new Error("An LLM was not configured during Stagehand initialization");
51777
51933
  const result = await extract({
51778
51934
  params,
@@ -51781,7 +51937,7 @@ function createStagehandController(runtime, options = {}) {
51781
51937
  clientLLMGenerate: runtime.adapters.clientLLMGenerate,
51782
51938
  logger,
51783
51939
  systemPrompt: state.initParams.systemPrompt,
51784
- cache: buildCacheContext(state.initParams),
51940
+ cache: buildCacheContext(state.initParams, runtime.adapters.clientBrowserbaseRequest),
51785
51941
  gateway
51786
51942
  });
51787
51943
  runtime.metrics.record("extract", result.metadata.usage);
@@ -54300,7 +54456,7 @@ var WebTracerProvider = class extends BasicTracerProvider {
54300
54456
  };
54301
54457
  var package_default = {
54302
54458
  name: "@browserbasehq/stagehand-extension",
54303
- version: "1.1.1",
54459
+ version: "1.2.0-alpha-c6b6f278c880d2f8b770ac0c1ecc63f248515968",
54304
54460
  "private": true,
54305
54461
  type: "module",
54306
54462
  scripts: {
@@ -58185,6 +58341,9 @@ var defaultBrowserSessionFactory = async () => {
58185
58341
  };
58186
58342
  var discardLog = () => {};
58187
58343
  var discardPageCDPEvent = () => {};
58344
+ var unavailableClientBrowserbase = async () => {
58345
+ throw new Error("The connected SDK did not register a Browserbase transport");
58346
+ };
58188
58347
  var unavailableClientLLM = async () => {
58189
58348
  throw new Error("The connected SDK did not register a client-side LLM");
58190
58349
  };
@@ -58193,6 +58352,7 @@ function createStagehandRuntime(adapters = {}, tracing = createStagehandTracing(
58193
58352
  browserSessionFactory: adapters.browserSessionFactory ?? defaultBrowserSessionFactory,
58194
58353
  emitLog: adapters.emitLog ?? discardLog,
58195
58354
  clientLLMGenerate: adapters.clientLLMGenerate ?? unavailableClientLLM,
58355
+ clientBrowserbaseRequest: adapters.clientBrowserbaseRequest ?? unavailableClientBrowserbase,
58196
58356
  emitPageCDPEvent: adapters.emitPageCDPEvent ?? discardPageCDPEvent,
58197
58357
  emitPageEvent: adapters.emitPageEvent ?? discardPageCDPEvent
58198
58358
  }, tracing);
@@ -58266,7 +58426,7 @@ var StagehandRuntime = class {
58266
58426
  async generateLlm(input) {
58267
58427
  const state = this.state.getState();
58268
58428
  const model = state.status === "initialized" ? state.initParams.model : void 0;
58269
- const gateway = state.status === "initialized" ? buildGatewayContext(state.initParams) : void 0;
58429
+ const gateway = state.status === "initialized" ? buildGatewayContext(state.initParams, this.adapters.clientBrowserbaseRequest) : void 0;
58270
58430
  if (!model && !gateway) throw new Error("An LLM was not configured during Stagehand initialization");
58271
58431
  return await generate(model, input, this.adapters.clientLLMGenerate, gateway);
58272
58432
  }
@@ -60059,6 +60219,10 @@ function startStagehandServiceWorker(scope = globalThis, runtime) {
60059
60219
  console.error("[stagehand] Failed to emit page event notification", error);
60060
60220
  });
60061
60221
  },
60222
+ clientBrowserbaseRequest: async (params, signal) => {
60223
+ if (!rpcClient) throw new Error("Stagehand RPC client is not connected");
60224
+ return await requestBrowserbase(rpcClient, params, signal);
60225
+ },
60062
60226
  clientLLMGenerate: async (params) => {
60063
60227
  if (!rpcClient) throw new Error("Stagehand RPC client is not connected");
60064
60228
  return await rpcClient.send(StagehandMethods.llmGenerate, params);