Conversation
🦋 Changeset detectedLatest commit: b35d29e The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Greptile SummaryThis PR adds cache bypass functionality to the SDK by introducing a
The implementation is clean and follows the existing patterns in the codebase. Cache control is appropriately scoped to the three AI-powered methods ( Confidence Score: 5/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant V3
participant StagehandAPIClient
participant API as Stagehand API
Note over User,API: Instance-level cache control
User->>V3: new V3({ disableCaching: true })
V3->>StagehandAPIClient: new StagehandAPIClient({ disableCaching: true })
Note over StagehandAPIClient: this.disableCaching = true
Note over User,API: Method-level cache control (overrides instance)
User->>V3: act(instruction, { disableCaching: false })
V3->>StagehandAPIClient: act({ options: { disableCaching: false } })
StagehandAPIClient->>StagehandAPIClient: Extract disableCaching from options
StagehandAPIClient->>StagehandAPIClient: execute({ method: "act", disableCaching: false })
StagehandAPIClient->>StagehandAPIClient: shouldSkipCache(false)
Note over StagehandAPIClient: Returns false (method override)
StagehandAPIClient->>API: POST /sessions/{id}/act<br/>(no x-bb-skip-cache header)
API-->>StagehandAPIClient: Response (from cache)
StagehandAPIClient-->>V3: ActResult
V3-->>User: ActResult
Note over User,API: Using instance-level setting
User->>V3: extract(instruction, schema)
V3->>StagehandAPIClient: extract({ options: undefined })
StagehandAPIClient->>StagehandAPIClient: Extract disableCaching (undefined)
StagehandAPIClient->>StagehandAPIClient: execute({ method: "extract", disableCaching: undefined })
StagehandAPIClient->>StagehandAPIClient: shouldSkipCache(undefined)
Note over StagehandAPIClient: Returns true (instance default)
StagehandAPIClient->>StagehandAPIClient: Add x-bb-skip-cache: true header
StagehandAPIClient->>API: POST /sessions/{id}/extract<br/>(with x-bb-skip-cache: true)
API-->>StagehandAPIClient: Response (cache bypassed)
StagehandAPIClient-->>V3: ExtractResult
V3-->>User: ExtractResult
|
Greptile's behavior is changing!From now on, if a review finishes with no comments, we will not post an additional "statistics" comment to confirm that our review found nothing to comment on. However, you can confirm that we reviewed your changes in the status check section. This feature can be toggled off in your Code Review Settings by deselecting "Create a status check for each PR". |
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name=".changeset/slow-insects-lie.md">
<violation number="1" location=".changeset/slow-insects-lie.md:5">
P3: Fix typos in the release note (`thereshold` → `threshold`, `it's inference` → `its inference`) to improve clarity.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
2 issues found across 6 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="packages/core/lib/v3/api.ts">
<violation number="1" location="packages/core/lib/v3/api.ts:59">
P1: Rule violated: **Any breaking changes to Stagehand REST API client / server implementation must be covered by an integration test under packages/server/test**
Breaking API client change lacks integration test coverage. The `serverCache` parameter (renamed from `disableCaching`) controls the `x-bb-skip-cache` header sent to the server, but no integration test in `packages/server/test` verifies this behavior.
Per the rule, breaking changes to header handling in the API client must be covered by integration tests. Consider adding a test that:
1. Verifies the `x-bb-skip-cache: true` header is sent when `serverCache: false`
2. Verifies the header is not sent when `serverCache: true` (default)</violation>
</file>
<file name=".changeset/slow-insects-lie.md">
<violation number="1" location=".changeset/slow-insects-lie.md:5">
P2: The changeset now documents `serverCache: false`, but this PR introduces `disableCaching`. This will mislead users to a non-existent option in the release notes. Align the changelog with the actual option name.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| * Defaults to true (caching enabled). | ||
| * Can be overridden per-method in act(), extract(), and observe() options. | ||
| */ | ||
| serverCache?: boolean; |
There was a problem hiding this comment.
P1: Rule violated: Any breaking changes to Stagehand REST API client / server implementation must be covered by an integration test under packages/server/test
Breaking API client change lacks integration test coverage. The serverCache parameter (renamed from disableCaching) controls the x-bb-skip-cache header sent to the server, but no integration test in packages/server/test verifies this behavior.
Per the rule, breaking changes to header handling in the API client must be covered by integration tests. Consider adding a test that:
- Verifies the
x-bb-skip-cache: trueheader is sent whenserverCache: false - Verifies the header is not sent when
serverCache: true(default)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/lib/v3/api.ts, line 59:
<comment>Breaking API client change lacks integration test coverage. The `serverCache` parameter (renamed from `disableCaching`) controls the `x-bb-skip-cache` header sent to the server, but no integration test in `packages/server/test` verifies this behavior.
Per the rule, breaking changes to header handling in the API client must be covered by integration tests. Consider adding a test that:
1. Verifies the `x-bb-skip-cache: true` header is sent when `serverCache: false`
2. Verifies the header is not sent when `serverCache: true` (default)</comment>
<file context>
@@ -51,10 +51,12 @@ interface StagehandAPIConstructorParams {
* Can be overridden per-method in act(), extract(), and observe() options.
*/
- disableCaching?: boolean;
+ serverCache?: boolean;
}
</file context>
There was a problem hiding this comment.
2 issues found across 4 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/core/lib/v3/api.ts">
<violation number="1" location="packages/core/lib/v3/api.ts:645">
P2: The cache-status handling block (~25 lines: computing `finalCacheStatus`, logging, attaching to result) is copy-pasted verbatim in two places within `execute` — the main SSE loop and the done-buffer fallback. Extract this into a private helper (e.g., `attachCacheStatus(result, method, cacheStatus, eventData)`) to avoid the maintenance risk of these blocks diverging silently.</violation>
<violation number="2" location="packages/core/lib/v3/api.ts:787">
P1: Rule violated: **Any breaking changes to Stagehand REST API client / server implementation must be covered by an integration test under packages/server/test**
Breaking API client changes (header rename `x-bb-skip-cache` → `browserbase-cache-bypass`, new `browserbase-cache-status` response header handling, and `cacheStatus` result property) have no integration test coverage under `packages/server/test/`. Per project rules, any breaking changes to the API client in `packages/core/lib/v3/api.ts` must be covered by at least one integration test. Add tests that verify:
1. The `browserbase-cache-bypass: true` header is sent when `serverCache` is disabled
2. The `browserbase-cache-status` response header is correctly read and attached as `cacheStatus` on `ActResult`/`ExtractResult`
3. Per-method `serverCache` override takes precedence over the instance-level setting</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
|
||
| // Add cache bypass header if caching is disabled | ||
| if (!this.shouldUseCache(serverCache)) { | ||
| defaultHeaders["browserbase-cache-bypass"] = "true"; |
There was a problem hiding this comment.
P1: Rule violated: Any breaking changes to Stagehand REST API client / server implementation must be covered by an integration test under packages/server/test
Breaking API client changes (header rename x-bb-skip-cache → browserbase-cache-bypass, new browserbase-cache-status response header handling, and cacheStatus result property) have no integration test coverage under packages/server/test/. Per project rules, any breaking changes to the API client in packages/core/lib/v3/api.ts must be covered by at least one integration test. Add tests that verify:
- The
browserbase-cache-bypass: trueheader is sent whenserverCacheis disabled - The
browserbase-cache-statusresponse header is correctly read and attached ascacheStatusonActResult/ExtractResult - Per-method
serverCacheoverride takes precedence over the instance-level setting
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/lib/v3/api.ts, line 787:
<comment>Breaking API client changes (header rename `x-bb-skip-cache` → `browserbase-cache-bypass`, new `browserbase-cache-status` response header handling, and `cacheStatus` result property) have no integration test coverage under `packages/server/test/`. Per project rules, any breaking changes to the API client in `packages/core/lib/v3/api.ts` must be covered by at least one integration test. Add tests that verify:
1. The `browserbase-cache-bypass: true` header is sent when `serverCache` is disabled
2. The `browserbase-cache-status` response header is correctly read and attached as `cacheStatus` on `ActResult`/`ExtractResult`
3. Per-method `serverCache` override takes precedence over the instance-level setting</comment>
<file context>
@@ -718,9 +782,9 @@ export class StagehandAPIClient {
+ // Add cache bypass header if caching is disabled
if (!this.shouldUseCache(serverCache)) {
- defaultHeaders["x-bb-skip-cache"] = "true";
+ defaultHeaders["browserbase-cache-bypass"] = "true";
}
</file context>
There was a problem hiding this comment.
2 issues found across 1 file (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/core/lib/v3/api.ts">
<violation number="1" location="packages/core/lib/v3/api.ts:712">
P1: Rule violated: **Any breaking changes to Stagehand REST API client / server implementation must be covered by an integration test under packages/server/test**
The new `attachCacheStatus` method and the broader cache bypass feature (sending `browserbase-cache-bypass` header, reading `browserbase-cache-status` from responses, attaching `cacheStatus` to results) have no integration test coverage in `packages/server/test/`. Per the rule, changes to the API client implementation in `packages/core/lib/v3/api.ts` must be covered by at least one integration test under `packages/server/test/`. Consider adding tests that verify:
1. The `browserbase-cache-bypass: true` header is sent when `serverCache` is `false`.
2. The `browserbase-cache-status` response header is correctly read and attached to `ActResult`/`ExtractResult`.
3. Method-level `serverCache` overrides the instance-level setting.</violation>
<violation number="2" location="packages/core/lib/v3/api.ts:712">
P2: Misplaced JSDoc: this comment describes `shouldUseCache` but now documents `attachCacheStatus` due to the method being inserted in between. Add a proper JSDoc for `attachCacheStatus` and move this comment back above `shouldUseCache`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| * Determine if caching should be enabled for a request. | ||
| * Method-level setting takes precedence over instance-level setting. | ||
| */ | ||
| private attachCacheStatus<T>( |
There was a problem hiding this comment.
P1: Rule violated: Any breaking changes to Stagehand REST API client / server implementation must be covered by an integration test under packages/server/test
The new attachCacheStatus method and the broader cache bypass feature (sending browserbase-cache-bypass header, reading browserbase-cache-status from responses, attaching cacheStatus to results) have no integration test coverage in packages/server/test/. Per the rule, changes to the API client implementation in packages/core/lib/v3/api.ts must be covered by at least one integration test under packages/server/test/. Consider adding tests that verify:
- The
browserbase-cache-bypass: trueheader is sent whenserverCacheisfalse. - The
browserbase-cache-statusresponse header is correctly read and attached toActResult/ExtractResult. - Method-level
serverCacheoverrides the instance-level setting.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/lib/v3/api.ts, line 712:
<comment>The new `attachCacheStatus` method and the broader cache bypass feature (sending `browserbase-cache-bypass` header, reading `browserbase-cache-status` from responses, attaching `cacheStatus` to results) have no integration test coverage in `packages/server/test/`. Per the rule, changes to the API client implementation in `packages/core/lib/v3/api.ts` must be covered by at least one integration test under `packages/server/test/`. Consider adding tests that verify:
1. The `browserbase-cache-bypass: true` header is sent when `serverCache` is `false`.
2. The `browserbase-cache-status` response header is correctly read and attached to `ActResult`/`ExtractResult`.
3. Method-level `serverCache` overrides the instance-level setting.</comment>
<file context>
@@ -757,6 +709,41 @@ export class StagehandAPIClient {
* Determine if caching should be enabled for a request.
* Method-level setting takes precedence over instance-level setting.
*/
+ private attachCacheStatus<T>(
+ result: T,
+ method: string,
</file context>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is kicking off a free Cloud Agent to fix these issues. This run is complimentary, but you can enable Autofix for all future PRs in the Cursor dashboard.
| } | ||
| // Otherwise, use instance-level setting | ||
| return this.serverCache; | ||
| } |
There was a problem hiding this comment.
Missing tests for server cache API logic
Low Severity
This PR adds business logic in packages/core/lib/v3/api.ts — new methods shouldUseCache(), attachCacheStatus(), the browserbase-cache-bypass header injection, and browserbase-cache-status header reading — but no corresponding tests cover these code paths. The new cache-variables.test.ts file tests ActCache variable handling (local caching), which is unrelated to the server-side caching feature.
Additional Locations (1)
Triggered by team rule: Tests
packages/core/lib/v3/api.ts
Outdated
| "cache-status": { value: cacheStatus, type: "string" }, | ||
| }, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Duplicate cache status logging on every request
Low Severity
For act, extract, and observe calls, the cache status is logged twice per request — once in request() (category "api", level 2) and again in attachCacheStatus() (category "cache", level 1). The request()-level log fires for all requests including non-cacheable ones, while attachCacheStatus() provides a more informative method-specific message. The generic log in request() is redundant for cached methods and adds noise.
Additional Locations (1)
|
Bugbot Autofix prepared fixes for 2 of the 2 bugs found in the latest run.
Or push these changes by commenting: Preview (12d13abd3d)diff --git a/packages/core/lib/v3/api.ts b/packages/core/lib/v3/api.ts
--- a/packages/core/lib/v3/api.ts
+++ b/packages/core/lib/v3/api.ts
@@ -877,19 +877,6 @@
},
});
- // Log cache status if present in response headers
- const cacheStatus = response.headers.get("browserbase-cache-status");
- if (cacheStatus) {
- this.logger({
- category: "api",
- message: `server cache ${cacheStatus.toLowerCase()}`,
- level: 2,
- auxiliary: {
- "cache-status": { value: cacheStatus, type: "string" },
- },
- });
- }
-
return response;
}
}
diff --git a/packages/core/tests/api-server-cache.test.ts b/packages/core/tests/api-server-cache.test.ts
new file mode 100644
--- /dev/null
+++ b/packages/core/tests/api-server-cache.test.ts
@@ -1,0 +1,469 @@
+import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
+
+function createMockResponse(
+ status: number,
+ body: string,
+ headers: Record<string, string> = {},
+): Response {
+ const headersObj = new Headers(headers);
+ return {
+ ok: status >= 200 && status < 300,
+ status,
+ headers: headersObj,
+ text: () => Promise.resolve(body),
+ json: () => Promise.resolve(JSON.parse(body)),
+ body: {
+ getReader: () => ({
+ read: vi
+ .fn()
+ .mockResolvedValueOnce({
+ value: new TextEncoder().encode(body),
+ done: false,
+ })
+ .mockResolvedValueOnce({ done: true }),
+ }),
+ },
+ } as unknown as Response;
+}
+
+function createSSEResponse(
+ result: unknown,
+ cacheStatus?: "HIT" | "MISS",
+ cacheHit?: boolean,
+): Response {
+ const eventData = {
+ type: "system",
+ data: {
+ status: "finished",
+ result,
+ cacheHit,
+ },
+ };
+ const body = `data: ${JSON.stringify(eventData)}\n\n`;
+ const headers: Record<string, string> = {};
+ if (cacheStatus) {
+ headers["browserbase-cache-status"] = cacheStatus;
+ }
+ return createMockResponse(200, body, headers);
+}
+
+const mockFetch = vi.fn().mockImplementation((url: string) => {
+ if (url.includes("/sessions/start")) {
+ return Promise.resolve(
+ createMockResponse(
+ 200,
+ JSON.stringify({
+ success: true,
+ data: { sessionId: "test-session-123", available: true },
+ }),
+ ),
+ );
+ }
+
+ return Promise.resolve(createSSEResponse({ success: true }));
+});
+
+vi.mock("fetch-cookie", () => ({
+ default: () => mockFetch,
+}));
+
+import { StagehandAPIClient } from "../lib/v3/api.js";
+
+describe("StagehandAPIClient server cache logic", () => {
+ beforeEach(() => {
+ mockFetch.mockClear();
+ });
+
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ describe("shouldUseCache behavior", () => {
+ it("does not send cache bypass header when serverCache is true (default)", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ serverCache: true,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ const initCallCount = mockFetch.mock.calls.length;
+ mockFetch.mockResolvedValueOnce(createSSEResponse({ success: true }));
+
+ await client.act({ input: "click button" });
+
+ const actHeaders = mockFetch.mock.calls[initCallCount]?.[1]
+ ?.headers as Record<string, string>;
+ expect(actHeaders).toBeDefined();
+ expect(actHeaders["browserbase-cache-bypass"]).toBeUndefined();
+ });
+
+ it("sends cache bypass header when serverCache is false at instance level", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ serverCache: false,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ const initCallCount = mockFetch.mock.calls.length;
+ mockFetch.mockResolvedValueOnce(createSSEResponse({ success: true }));
+
+ await client.act({ input: "click button" });
+
+ const actHeaders = mockFetch.mock.calls[initCallCount]?.[1]
+ ?.headers as Record<string, string>;
+ expect(actHeaders).toBeDefined();
+ expect(actHeaders["browserbase-cache-bypass"]).toBe("true");
+ });
+
+ it("method-level serverCache=false overrides instance-level serverCache=true", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ serverCache: true,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ const initCallCount = mockFetch.mock.calls.length;
+ mockFetch.mockResolvedValueOnce(createSSEResponse({ success: true }));
+
+ await client.act({
+ input: "click button",
+ options: { serverCache: false },
+ });
+
+ const actHeaders = mockFetch.mock.calls[initCallCount]?.[1]
+ ?.headers as Record<string, string>;
+ expect(actHeaders).toBeDefined();
+ expect(actHeaders["browserbase-cache-bypass"]).toBe("true");
+ });
+
+ it("method-level serverCache=true overrides instance-level serverCache=false", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ serverCache: false,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ const initCallCount = mockFetch.mock.calls.length;
+ mockFetch.mockResolvedValueOnce(createSSEResponse({ success: true }));
+
+ await client.act({
+ input: "click button",
+ options: { serverCache: true },
+ });
+
+ const actHeaders = mockFetch.mock.calls[initCallCount]?.[1]
+ ?.headers as Record<string, string>;
+ expect(actHeaders).toBeDefined();
+ expect(actHeaders["browserbase-cache-bypass"]).toBeUndefined();
+ });
+
+ it("defaults to serverCache=true when not specified", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ const initCallCount = mockFetch.mock.calls.length;
+ mockFetch.mockResolvedValueOnce(createSSEResponse({ success: true }));
+
+ await client.act({ input: "click button" });
+
+ const actHeaders = mockFetch.mock.calls[initCallCount]?.[1]
+ ?.headers as Record<string, string>;
+ expect(actHeaders).toBeDefined();
+ expect(actHeaders["browserbase-cache-bypass"]).toBeUndefined();
+ });
+ });
+
+ describe("attachCacheStatus behavior", () => {
+ it("attaches cacheStatus HIT from response header to act result", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ mockFetch.mockResolvedValueOnce(
+ createSSEResponse(
+ { success: true, message: "action performed" },
+ "HIT",
+ ),
+ );
+
+ const result = await client.act({ input: "click button" });
+
+ expect(result.cacheStatus).toBe("HIT");
+ });
+
+ it("attaches cacheStatus MISS from response header to act result", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ mockFetch.mockResolvedValueOnce(
+ createSSEResponse(
+ { success: true, message: "action performed" },
+ "MISS",
+ ),
+ );
+
+ const result = await client.act({ input: "click button" });
+
+ expect(result.cacheStatus).toBe("MISS");
+ });
+
+ it("attaches cacheStatus from SSE event cacheHit field when header is missing", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ mockFetch.mockResolvedValueOnce(
+ createSSEResponse(
+ { success: true, message: "action performed" },
+ undefined,
+ true,
+ ),
+ );
+
+ const result = await client.act({ input: "click button" });
+
+ expect(result.cacheStatus).toBe("HIT");
+ });
+
+ it("attaches cacheStatus MISS from SSE event cacheHit=false when header is missing", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ mockFetch.mockResolvedValueOnce(
+ createSSEResponse(
+ { success: true, message: "action performed" },
+ undefined,
+ false,
+ ),
+ );
+
+ const result = await client.act({ input: "click button" });
+
+ expect(result.cacheStatus).toBe("MISS");
+ });
+
+ it("logs cache status for act method", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ mockFetch.mockResolvedValueOnce(
+ createSSEResponse({ success: true }, "HIT"),
+ );
+
+ await client.act({ input: "click button" });
+
+ const cacheLogCalls = logger.mock.calls.filter(
+ (call) => call[0]?.category === "cache",
+ );
+ expect(cacheLogCalls.length).toBe(1);
+ expect(cacheLogCalls[0][0].message).toBe("act server cache hit");
+ });
+
+ it("logs cache status for extract method", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ mockFetch.mockResolvedValueOnce(
+ createSSEResponse({ extraction: "test data" }, "MISS"),
+ );
+
+ await client.extract({ instruction: "extract data" });
+
+ const cacheLogCalls = logger.mock.calls.filter(
+ (call) => call[0]?.category === "cache",
+ );
+ expect(cacheLogCalls.length).toBe(1);
+ expect(cacheLogCalls[0][0].message).toBe("extract server cache miss");
+ });
+
+ it("logs cache status for observe method", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ mockFetch.mockResolvedValueOnce(createSSEResponse([], "HIT"));
+
+ await client.observe({ instruction: "find buttons" });
+
+ const cacheLogCalls = logger.mock.calls.filter(
+ (call) => call[0]?.category === "cache",
+ );
+ expect(cacheLogCalls.length).toBe(1);
+ expect(cacheLogCalls[0][0].message).toBe("observe server cache hit");
+ });
+
+ it("does not attach cacheStatus to observe result (returns array)", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ mockFetch.mockResolvedValueOnce(createSSEResponse([], "HIT"));
+
+ const result = await client.observe({ instruction: "find buttons" });
+
+ expect(Array.isArray(result)).toBe(true);
+ expect(
+ (result as unknown as { cacheStatus?: string }).cacheStatus,
+ ).toBeUndefined();
+ });
+ });
+
+ describe("cache bypass header for different methods", () => {
+ it("applies cache bypass header to extract requests", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ serverCache: false,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ const initCallCount = mockFetch.mock.calls.length;
+ mockFetch.mockResolvedValueOnce(
+ createSSEResponse({ extraction: "data" }),
+ );
+
+ await client.extract({ instruction: "get data" });
+
+ const extractHeaders = mockFetch.mock.calls[initCallCount]?.[1]
+ ?.headers as Record<string, string>;
+ expect(extractHeaders).toBeDefined();
+ expect(extractHeaders["browserbase-cache-bypass"]).toBe("true");
+ });
+
+ it("applies cache bypass header to observe requests", async () => {
+ const logger = vi.fn();
+ const client = new StagehandAPIClient({
+ apiKey: "test-key",
+ projectId: "test-project",
+ logger,
+ serverCache: false,
+ });
+
+ await client.init({
+ modelName: "openai/gpt-4.1-mini",
+ modelApiKey: "test-model-key",
+ });
+
+ const initCallCount = mockFetch.mock.calls.length;
+ mockFetch.mockResolvedValueOnce(createSSEResponse([]));
+
+ await client.observe({ instruction: "find buttons" });
+
+ const observeHeaders = mockFetch.mock.calls[initCallCount]?.[1]
+ ?.headers as Record<string, string>;
+ expect(observeHeaders).toBeDefined();
+ expect(observeHeaders["browserbase-cache-bypass"]).toBe("true");
+ });
+ });
+}); |
There was a problem hiding this comment.
we should add the serverCache param into the stagehand constructor reference secton
There was a problem hiding this comment.
2 issues found across 4 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/core/lib/v3/types/public/methods.ts">
<violation number="1" location="packages/core/lib/v3/types/public/methods.ts:93">
P2: The `Action[] & { cacheStatus?: ... }` intersection pattern is fragile: any array operation (`.filter()`, `.map()`, spread, etc.) will silently drop the `cacheStatus` property. This differs from how `ActResult` handles the same concern (using a proper interface with an `actions` field).
Additionally, the `observe()` method in `v3.ts` still returns `Promise<Action[]>` rather than `Promise<ObserveResult>`, so TypeScript consumers won't see `cacheStatus` without an explicit cast.
Consider wrapping the result in an object (like `ActResult` does) or, if backward compatibility requires returning an array, at minimum updating the `observe()` return type to `Promise<ObserveResult>` so the property is discoverable.</violation>
</file>
<file name="packages/core/lib/v3/api.ts">
<violation number="1" location="packages/core/lib/v3/api.ts:722">
P2: Custom agent: **Any breaking changes to Stagehand REST API client / server implementation must be covered by an integration test under packages/server/test**
The new cache-status suppression behavior when `serverCache=false` is not covered by any integration test. When a request is made with caching disabled but the server still returns a `browserbase-cache-status` header or SSE `cacheHit`, the status is now intentionally suppressed (passed as `null`/empty). Add a test in `packages/server/test/integration/api-server-cache.test.ts` that verifies `result.cacheStatus` is `undefined` when `serverCache: false` even if the server responds with a cache status header.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| * `browserbase-cache-status` header so callers can tell whether the result | ||
| * was served from the server-side cache. | ||
| */ | ||
| export type ObserveResult = Action[] & { cacheStatus?: "HIT" | "MISS" }; |
There was a problem hiding this comment.
P2: The Action[] & { cacheStatus?: ... } intersection pattern is fragile: any array operation (.filter(), .map(), spread, etc.) will silently drop the cacheStatus property. This differs from how ActResult handles the same concern (using a proper interface with an actions field).
Additionally, the observe() method in v3.ts still returns Promise<Action[]> rather than Promise<ObserveResult>, so TypeScript consumers won't see cacheStatus without an explicit cast.
Consider wrapping the result in an object (like ActResult does) or, if backward compatibility requires returning an array, at minimum updating the observe() return type to Promise<ObserveResult> so the property is discoverable.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/lib/v3/types/public/methods.ts, line 93:
<comment>The `Action[] & { cacheStatus?: ... }` intersection pattern is fragile: any array operation (`.filter()`, `.map()`, spread, etc.) will silently drop the `cacheStatus` property. This differs from how `ActResult` handles the same concern (using a proper interface with an `actions` field).
Additionally, the `observe()` method in `v3.ts` still returns `Promise<Action[]>` rather than `Promise<ObserveResult>`, so TypeScript consumers won't see `cacheStatus` without an explicit cast.
Consider wrapping the result in an object (like `ActResult` does) or, if backward compatibility requires returning an array, at minimum updating the `observe()` return type to `Promise<ObserveResult>` so the property is discoverable.</comment>
<file context>
@@ -84,6 +84,14 @@ export interface ObserveOptions {
+ * `browserbase-cache-status` header so callers can tell whether the result
+ * was served from the server-side cache.
+ */
+export type ObserveResult = Action[] & { cacheStatus?: "HIT" | "MISS" };
+
export enum V3FunctionName {
</file context>
|
|
||
| // If caching was bypassed for this request, suppress cache status | ||
| // so we don't log or surface a MISS that the server emits anyway. | ||
| const cacheEnabled = this.shouldUseCache(serverCache); |
There was a problem hiding this comment.
P2: Custom agent: Any breaking changes to Stagehand REST API client / server implementation must be covered by an integration test under packages/server/test
The new cache-status suppression behavior when serverCache=false is not covered by any integration test. When a request is made with caching disabled but the server still returns a browserbase-cache-status header or SSE cacheHit, the status is now intentionally suppressed (passed as null/empty). Add a test in packages/server/test/integration/api-server-cache.test.ts that verifies result.cacheStatus is undefined when serverCache: false even if the server responds with a cache status header.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/lib/v3/api.ts, line 722:
<comment>The new cache-status suppression behavior when `serverCache=false` is not covered by any integration test. When a request is made with caching disabled but the server still returns a `browserbase-cache-status` header or SSE `cacheHit`, the status is now intentionally suppressed (passed as `null`/empty). Add a test in `packages/server/test/integration/api-server-cache.test.ts` that verifies `result.cacheStatus` is `undefined` when `serverCache: false` even if the server responds with a cache status header.</comment>
<file context>
@@ -716,11 +717,14 @@ export class StagehandAPIClient {
+ // If caching was bypassed for this request, suppress cache status
+ // so we don't log or surface a MISS that the server emits anyway.
+ const cacheEnabled = this.shouldUseCache(serverCache);
return this.attachCacheStatus(
eventData.data.result as T,
</file context>
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/server/test/integration/api-server-cache.test.ts">
<violation number="1" location="packages/server/test/integration/api-server-cache.test.ts:87">
P2: Both tests in `browserbase-cache-status response header` have conditional assertions (`if (cacheStatus !== null)`) that make them vacuous no-ops when the header is absent. These tests will pass green even if server-side caching is completely broken or never deployed, masking regressions. Consider either (a) requiring the header to be present (fail the test if it's missing) or (b) explicitly marking these tests as skipped when caching isn't available, so the test suite honestly reflects what's being validated.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| assertFetchStatus(ctx, HTTP_OK, "Extract should succeed"); | ||
|
|
||
| const cacheStatus = ctx.headers.get("browserbase-cache-status"); | ||
| if (cacheStatus !== null) { |
There was a problem hiding this comment.
P2: Both tests in browserbase-cache-status response header have conditional assertions (if (cacheStatus !== null)) that make them vacuous no-ops when the header is absent. These tests will pass green even if server-side caching is completely broken or never deployed, masking regressions. Consider either (a) requiring the header to be present (fail the test if it's missing) or (b) explicitly marking these tests as skipped when caching isn't available, so the test suite honestly reflects what's being validated.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/server/test/integration/api-server-cache.test.ts, line 87:
<comment>Both tests in `browserbase-cache-status response header` have conditional assertions (`if (cacheStatus !== null)`) that make them vacuous no-ops when the header is absent. These tests will pass green even if server-side caching is completely broken or never deployed, masking regressions. Consider either (a) requiring the header to be present (fail the test if it's missing) or (b) explicitly marking these tests as skipped when caching isn't available, so the test suite honestly reflects what's being validated.</comment>
<file context>
@@ -1,218 +1,123 @@
- fetchSpy.mockResolvedValueOnce(
- sseResponse(EXTRACT_RESULT, { "browserbase-cache-status": "HIT" }),
+ const cacheStatus = ctx.headers.get("browserbase-cache-status");
+ if (cacheStatus !== null) {
+ assert.ok(
+ cacheStatus === "HIT" || cacheStatus === "MISS",
</file context>
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @browserbasehq/stagehand@3.1.0 ### Minor Changes - [#1681](#1681) [`e3db9aa`](e3db9aa) Thanks [@tkattkat](https://github.com/tkattkat)! - Add cookie management APIs: `context.addCookies()`, `context.clearCookies()`, & `context.cookies()` - [#1672](#1672) [`b65756e`](b65756e) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add boolean keepAlive parameter to allow for configuring whether the browser should be closed when stagehand.close() is called. - [#1708](#1708) [`176d420`](176d420) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add context.setExtraHTTPHeaders() - [#1611](#1611) [`8a3c066`](8a3c066) Thanks [@monadoid](https://github.com/monadoid)! - Using `mode` enum instead of old `cua` boolean in openapi spec ### Patch Changes - [#1683](#1683) [`7584f3e`](7584f3e) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix: include shadow DOM in .count() & .nth() & support xpath predicates - [#1644](#1644) [`1e1c9c1`](1e1c9c1) Thanks [@monadoid](https://github.com/monadoid)! - Fix unhandled CDP detaches by returning the original sendCDP promise - [#1729](#1729) [`6bef890`](6bef890) Thanks [@shrey150](https://github.com/shrey150)! - fix: support Claude 4.6 (Opus and Sonnet) in CUA mode by using the correct `computer_20251124` tool version and `computer-use-2025-11-24` beta header - [#1647](#1647) [`ffd4b33`](ffd4b33) Thanks [@tkattkat](https://github.com/tkattkat)! - Fix [Agent] - Address bug causing issues with continuing a conversation from past messages in dom mode - [#1614](#1614) [`677bff5`](677bff5) Thanks [@miguelg719](https://github.com/miguelg719)! - Enforce <number>-<number> regex validation on act/observe for elementId - [#1580](#1580) [`65ff464`](65ff464) Thanks [@tkattkat](https://github.com/tkattkat)! - Add unified variables support across act and agent with a single VariableValue type - [#1666](#1666) [`101bcf2`](101bcf2) Thanks [@Kylejeong2](https://github.com/Kylejeong2)! - add support for codex models - [#1728](#1728) [`0a94301`](0a94301) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - handle potential race condition on `.close()` when using the Stagehand API - [#1664](#1664) [`b27c04d`](b27c04d) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fixes issue with context.addInitScript() where scripts were not being applied to out of process iframes (OOPIFs), and popup pages with same process iframes (SPIFs) - [#1632](#1632) [`afbd08b`](afbd08b) Thanks [@pirate](https://github.com/pirate)! - Remove automatic `.env` loading via `dotenv`. If your app relies on `.env` files, install `dotenv` and load it explicitly in your code: ```ts import dotenv from "dotenv"; dotenv.config({ path: ".env" }); ``` - [#1624](#1624) [`0e8d569`](0e8d569) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix issue where screenshot masks were not being applied to dialog elements - [#1596](#1596) [`ff0f979`](ff0f979) Thanks [@tkattkat](https://github.com/tkattkat)! - Update usage/metrics handling in agent - [#1631](#1631) [`2d89d2b`](2d89d2b) Thanks [@miguelg719](https://github.com/miguelg719)! - Add right and middle click support to act and observe - [#1697](#1697) [`aac9a19`](aac9a19) Thanks [@shrey150](https://github.com/shrey150)! - fix: support `<frame>` elements in XPath frame boundary detection so `act()` works on legacy `<frameset>` pages - [#1692](#1692) [`06de50f`](06de50f) Thanks [@shrey150](https://github.com/shrey150)! - fix: skip piercer injection for chrome-extension:// and other non-HTML targets - [#1613](#1613) [`aa4d981`](aa4d981) Thanks [@miguelg719](https://github.com/miguelg719)! - SupportedUnderstudyAction Enum validation for 'method' on act/observe inference - [#1652](#1652) [`18b1e3b`](18b1e3b) Thanks [@miguelg719](https://github.com/miguelg719)! - Add support for gemini 3 flash and pro in hybrid/cua agent - [#1706](#1706) [`957d82b`](957d82b) Thanks [@chrisreadsf](https://github.com/chrisreadsf)! - Add GLM to prompt-based JSON fallback for models without native structured output support - [#1633](#1633) [`22e371a`](22e371a) Thanks [@tkattkat](https://github.com/tkattkat)! - Add warning when incorrect models are used with agents hybrid mode - [#1673](#1673) [`d29b91f`](d29b91f) Thanks [@miguelg719](https://github.com/miguelg719)! - Add multi-region support for Stagehand API with region-specific endpoints - [#1695](#1695) [`7b4f817`](7b4f817) Thanks [@tkattkat](https://github.com/tkattkat)! - Fix: zod bug when pinning zod to v3 and using structured output in agent - [#1609](#1609) [`3f9ca4d`](3f9ca4d) Thanks [@miguelg719](https://github.com/miguelg719)! - Add SupportedUnderstudyActions to observe system prompt - [#1581](#1581) [`49ead1e`](49ead1e) Thanks [@sameelarif](https://github.com/sameelarif)! - **Server-side caching is now available.** When running `env: "BROWSERBASE"`, Stagehand automatically caches `act()`, `extract()`, and `observe()` results server-side — repeated calls with the same inputs return instantly without consuming LLM tokens. Caching is enabled by default and can be disabled via `serverCache: false` on the Stagehand instance or per individual call. Check out the [browserbase blog](https://www.browserbase.com/blog/stagehand-caching) for more details. - [#1642](#1642) [`3673369`](3673369) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix issue where scripts added via context.addInitScripts() were not being injected into new pages that were opened via popups (eg, clicking a link that opens a new page) and/or calling context.newPage(url) - [#1735](#1735) [`c465e87`](c465e87) Thanks [@monadoid](https://github.com/monadoid)! - Supports request header authentication with connectToMCPServer - [#1705](#1705) [`ae533e4`](ae533e4) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - include error cause in UnderstudyCommandException - [#1636](#1636) [`ea33052`](ea33052) Thanks [@miguelg719](https://github.com/miguelg719)! - Include executionModel on the AgentConfigSchema - [#1679](#1679) [`5764ede`](5764ede) Thanks [@shrey150](https://github.com/shrey150)! - fix issue where locator.count() was not working with xpaths that have attribute predicates - [#1646](#1646) [`f09b184`](f09b184) Thanks [@miguelg719](https://github.com/miguelg719)! - Add user-agent to CDP connections - [#1637](#1637) [`a7d29de`](a7d29de) Thanks [@miguelg719](https://github.com/miguelg719)! - Improve error and warning message for legacy model format - [#1685](#1685) [`d334399`](d334399) Thanks [@tkattkat](https://github.com/tkattkat)! - Bump ai sdk & google provider version - [#1662](#1662) [`44416da`](44416da) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix issue where locator.fill() was not working on elements that require direct value setting - [#1612](#1612) [`bdd8b4e`](bdd8b4e) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix issue where screenshot mask was only being applied to the first element that the locator resolved to. masks now apply to all matching elements. ## @browserbasehq/stagehand-server@3.6.0 ### Minor Changes - [#1611](#1611) [`8a3c066`](8a3c066) Thanks [@monadoid](https://github.com/monadoid)! - Using `mode` enum instead of old `cua` boolean in openapi spec ### Patch Changes - [#1604](#1604) [`4753078`](4753078) Thanks [@miguelg719](https://github.com/miguelg719)! - Enable bedrock - [#1636](#1636) [`ea33052`](ea33052) Thanks [@miguelg719](https://github.com/miguelg719)! - Include executionModel on the AgentConfigSchema - [#1602](#1602) [`22a0502`](22a0502) Thanks [@miguelg719](https://github.com/miguelg719)! - Include vertex as a supported provider - Updated dependencies \[[`7584f3e`](7584f3e), [`1e1c9c1`](1e1c9c1), [`6bef890`](6bef890), [`ffd4b33`](ffd4b33), [`677bff5`](677bff5), [`65ff464`](65ff464), [`101bcf2`](101bcf2), [`0a94301`](0a94301), [`b27c04d`](b27c04d), [`afbd08b`](afbd08b), [`e3db9aa`](e3db9aa), [`0e8d569`](0e8d569), [`ff0f979`](ff0f979), [`2d89d2b`](2d89d2b), [`aac9a19`](aac9a19), [`06de50f`](06de50f), [`aa4d981`](aa4d981), [`18b1e3b`](18b1e3b), [`957d82b`](957d82b), [`b65756e`](b65756e), [`22e371a`](22e371a), [`d29b91f`](d29b91f), [`7b4f817`](7b4f817), [`176d420`](176d420), [`3f9ca4d`](3f9ca4d), [`8a3c066`](8a3c066), [`49ead1e`](49ead1e), [`3673369`](3673369), [`c465e87`](c465e87), [`ae533e4`](ae533e4), [`ea33052`](ea33052), [`5764ede`](5764ede), [`f09b184`](f09b184), [`a7d29de`](a7d29de), [`d334399`](d334399), [`44416da`](44416da), [`bdd8b4e`](bdd8b4e)]: - @browserbasehq/stagehand@3.1.0 ## @browserbasehq/stagehand-evals@1.1.8 ### Patch Changes - Updated dependencies \[[`7584f3e`](7584f3e), [`1e1c9c1`](1e1c9c1), [`6bef890`](6bef890), [`ffd4b33`](ffd4b33), [`677bff5`](677bff5), [`65ff464`](65ff464), [`101bcf2`](101bcf2), [`0a94301`](0a94301), [`b27c04d`](b27c04d), [`afbd08b`](afbd08b), [`e3db9aa`](e3db9aa), [`0e8d569`](0e8d569), [`ff0f979`](ff0f979), [`2d89d2b`](2d89d2b), [`aac9a19`](aac9a19), [`06de50f`](06de50f), [`aa4d981`](aa4d981), [`18b1e3b`](18b1e3b), [`957d82b`](957d82b), [`b65756e`](b65756e), [`22e371a`](22e371a), [`d29b91f`](d29b91f), [`7b4f817`](7b4f817), [`176d420`](176d420), [`3f9ca4d`](3f9ca4d), [`8a3c066`](8a3c066), [`49ead1e`](49ead1e), [`3673369`](3673369), [`c465e87`](c465e87), [`ae533e4`](ae533e4), [`ea33052`](ea33052), [`5764ede`](5764ede), [`f09b184`](f09b184), [`a7d29de`](a7d29de), [`d334399`](d334399), [`44416da`](44416da), [`bdd8b4e`](bdd8b4e)]: - @browserbasehq/stagehand@3.1.0 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>



why
Once API-side caching is deployed, the feature will be opt-out. Users should have a way to disable the cache from the client.
what changed
Added a
serverCacheparameter to the constructor which adds thex-bb-skip-cacheheader to all outbound API requests. This parameter is also present on act, extract, and observe functions to allow for further configuration.test plan
Summary by cubic
Adds global and per-request control of server-side caching for act/extract/observe in Browserbase mode. Caching is on by default; you can opt out. HIT/MISS is logged, and results include cacheStatus (Linear STG-1182).
New Features
Bug Fixes
Written for commit b35d29e. Summary will update on new commits. Review in cubic
Note
Medium Risk
Touches the Browserbase API request/response path (new headers, SSE parsing behavior, and result type shape), which could affect caching behavior or backwards compatibility for consumers relying on exact response objects.
Overview
Adds server-side caching support for Browserbase API mode with a new
serverCacheflag onV3Options(instance default) and per-call overrides inact(),extract(), andobserve().StagehandAPIClientnow sendsbrowserbase-cache-bypass: truewhen caching is disabled, readsbrowserbase-cache-status(and SSEcacheHit) to log HIT/MISS, and attachescacheStatus?: "HIT" | "MISS"ontoActResultandExtractResult.Updates public types/tests, adds new unit tests for
ActCachevariable-key behavior, and refreshes docs + changeset to announce server-side caching and the opt-out mechanism.Written by Cursor Bugbot for commit b5ce917. This will update automatically on new commits. Configure here.