[AP-2925] Enable server-side caching by default in v4 - #2964
sameelarif wants to merge 2 commits into
Conversation
v3 cached every eligible act/observe/extract unless the call opted out via `serverCache: false` — the server decided, gated on the per-project LaunchDarkly flag. v4 added a client-side gate that defaults to off, so `withCache` returns before it ever calls /v1/cache/get and metadata reports DISABLED. Anyone who upgraded without passing `cache` silently lost caching, including projects with the LaunchDarkly flag already enabled. Flip the default so v4 matches v3: caching is on unless the instance or the call opts out. The server still gates every lookup on `stagehand-api-server-caching`, so this only decides whether we ask. No existing test covered the default — baseArgs() always passed `caching: true` — which is how the inversion shipped. Added three: the buildCacheContext default, an explicit opt-out, and a lookup with neither the request nor the instance setting `cache`. Docs said caching was opt-in, which this makes wrong. Also corrects the v3 migration guide, which mapped `enableCaching` (a v2-era local option that v3 had already replaced with `cacheDir`) to `cache`, and never mentioned `serverCache` — the option v3 users actually had. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
|
There was a problem hiding this comment.
All reported issues were addressed across 4 files
Architecture diagram
sequenceDiagram
participant App as Client App
participant SH as Stagehand Client v4
participant CacheSvc as Cache Service
participant API as Stagehand API
participant LD as LaunchDarkly
participant Cache as Cache Store
Note over App, Cache: Caching Flow
App->>SH: create({ browser, apiKey })
SH->>CacheSvc: buildCacheContext(initParams)
CacheSvc->>CacheSvc: defaultCaching = cache ?? true
App->>SH: act("...") or observe() or extract()
SH->>CacheSvc: withCache({ caching, context, execute })
alt Request or instance sets cache: false
CacheSvc->>CacheSvc: resolvedCaching = false
CacheSvc->>SH: execute() directly
SH-->>App: Result with cache.status = "DISABLED"
else Default path (no explicit cache option)
CacheSvc->>CacheSvc: resolvedCaching = defaultCaching (true)
CacheSvc->>CacheSvc: cachePage = asCachePage(page)
CacheSvc->>API: GET /v1/cache/get
API->>LD: Check stagehand-api-server-caching flag
alt Flag disabled per project
LD-->>API: false
API-->>CacheSvc: No cache lookup
CacheSvc->>CacheSvc: execute()
SH-->>App: Result with cache.status = "DISABLED"
else Flag enabled per project
LD-->>API: true
API->>Cache: Lookup by cache key
alt Cache hit
Cache-->>API: Cached result
API-->>CacheSvc: Cache hit
CacheSvc->>CacheSvc: onHit()
SH-->>App: Result with cache.status = "HIT"
else Cache miss
Cache-->>API: Not found
API-->>CacheSvc: Miss
CacheSvc->>CacheSvc: execute()
API->>Cache: Store result
SH-->>App: Result with cache.status = "MISS"
end
end
end
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
- Locator-scoped calls bypass the cache entirely (shouldBypassCacheForLocatorScope gates act/observe/extract on `locator` or a non-empty `ignoreLocators`, and withCache returns before any read or write). The intro claimed every call is cached, contradicting the page's own note further down. Qualify it and add the exclusion to Limitations, which is where the other caching caveats live. - Drop the two em dashes, prohibited by .cubic/docs-style-guide.md:38. - `page.act` is not a v4 API and `page` was never declared in that snippet, so the opt-out example failed when copied. Use `stagehand.act`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Confidence score: 5/5
packages/docs/v4/best-practices/caching.mdxuses passive voice in the added limitation, which may make the caching behavior less clear to readers; name the actor explicitly, such as stating that Stagehand does not cache the relevant content.
Prompt for AI agents (unresolved 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/docs/v4/best-practices/caching.mdx">
<violation number="1" location="packages/docs/v4/best-practices/caching.mdx:338">
P2: Custom agent: **Stagehand docs prose guide**
This added limitation uses passive voice in “are not cached” and “is keyed” without naming the actor. Rewrite it with explicit actors, such as “Stagehand does not cache calls that set `locator` or `ignoreLocators`. The cache contract uses unscoped requests as keys, so Stagehand skips both reads and writes and reports a `DISABLED` cache status, even when caching is enabled.”</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| - If the page content or structure changes, the action won't get a cache `HIT` and Stagehand calls the LLM. Subsequent actions will attempt to hit the resulting cache entry. | ||
| - Caching is best-effort. If the cache is unreachable, Stagehand falls back to normal inference rather than failing your run. | ||
| - Stagehand replays a cached `act()` result deterministically with self-healing turned off. If the recorded selector no longer resolves, Stagehand falls back to full inference. | ||
| - Calls that set `locator` or `ignoreLocators` are not cached. The cache contract is keyed on unscoped requests, so Stagehand skips both reads and writes and reports a `DISABLED` cache status, even when caching is enabled. |
There was a problem hiding this comment.
P2: Custom agent: Stagehand docs prose guide
This added limitation uses passive voice in “are not cached” and “is keyed” without naming the actor. Rewrite it with explicit actors, such as “Stagehand does not cache calls that set locator or ignoreLocators. The cache contract uses unscoped requests as keys, so Stagehand skips both reads and writes and reports a DISABLED cache status, even when caching is enabled.”
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/docs/v4/best-practices/caching.mdx, line 338:
<comment>This added limitation uses passive voice in “are not cached” and “is keyed” without naming the actor. Rewrite it with explicit actors, such as “Stagehand does not cache calls that set `locator` or `ignoreLocators`. The cache contract uses unscoped requests as keys, so Stagehand skips both reads and writes and reports a `DISABLED` cache status, even when caching is enabled.”</comment>
<file context>
@@ -335,6 +335,7 @@ Cache behavior is also visible in the [Browserbase session replay dashboard](htt
- If the page content or structure changes, the action won't get a cache `HIT` and Stagehand calls the LLM. Subsequent actions will attempt to hit the resulting cache entry.
- Caching is best-effort. If the cache is unreachable, Stagehand falls back to normal inference rather than failing your run.
- Stagehand replays a cached `act()` result deterministically with self-healing turned off. If the recorded selector no longer resolves, Stagehand falls back to full inference.
+- Calls that set `locator` or `ignoreLocators` are not cached. The cache contract is keyed on unscoped requests, so Stagehand skips both reads and writes and reports a `DISABLED` cache status, even when caching is enabled.
### Best practices
</file context>
| - Calls that set `locator` or `ignoreLocators` are not cached. The cache contract is keyed on unscoped requests, so Stagehand skips both reads and writes and reports a `DISABLED` cache status, even when caching is enabled. | |
| - Stagehand does not cache calls that set `locator` or `ignoreLocators`. The cache contract uses unscoped requests as keys, so Stagehand skips both reads and writes and reports a `DISABLED` cache status, even when caching is enabled. |
v4 shipped with server-side caching opt-in, where v3 had it on by default. Anyone who upgraded without passing
cachelost caching without an error.Change
Both defaults flip to
true, so v4 matches v3: caching runs unless the instance or the call opts out.Docs
v4/best-practices/caching.mdxdescribed opt-in, which this makes wrong.The v3 migration guide also mapped
enableCaching→cache.enableCachingwas a v2-era local option that v3 had already replaced withcacheDir; the server-side option v3 users actually had wasserverCache, which appeared nowhere in the v4 docs. Corrected the mapping and the diff example.🤖 Generated with Claude Code
Summary by cubic
Enables server-side caching by default in v4 so upgrading from v3 no longer silently loses caching (v3 cached unless a call opted out; v4 shipped opt-in).
trueso caching runs unless the instance or call opts out.cacheset.serverCachemaps tocache(still on by default), andenableCaching/cacheDirno longer exist.DISABLED, and fixes the opt-out example to usestagehand.act.Written for commit b948484. Summary will update on new commits.