Conversation
Bind browser subresource calls to a browser session's base_url and expose raw HTTP through request and stream helpers so metro-routed access feels like normal httpx usage. Made-with: Cursor
Prevent browser-scoped raw HTTP helpers from letting user params override internal routing query keys, and clean up wording around browser session base_url routing. Made-with: Cursor
Keep the browser-scoped request helpers aligned with repo linting and reserve internal raw-request query keys without exposing implementation details. Made-with: Cursor
Keep the browser-scoped test file aligned with the repo lint configuration so the follow-up typing fixes pass CI. Made-with: Cursor
Tighten browser-scoped helper typing and test casts so the Python SDK passes the repository's lint and pyright checks cleanly. Made-with: Cursor
Replace the handwritten Python browser-scoped façade with deterministic generated bindings from the browser resource graph, and enforce regeneration during lint. Made-with: Cursor
Keep the browser-scoped Python generator compatible with the repo lint pipeline by suppressing strict pyright diagnostics that are not meaningful for the AST-walking build script. Made-with: Cursor
Keep the Python generator and generated browser-scoped façade aligned with pyright and mypy so the deterministic regeneration path passes the repo lint pipeline. Made-with: Cursor
Sort the generator script imports and keep the deterministic browser-scoped generation path aligned with the repo lint pipeline. Made-with: Cursor
Turn the browser-scoped Python example into a runnable demonstration of both process execution and /curl/raw-backed request and stream usage. Made-with: Cursor
Move browser raw HTTP and direct-to-VM routing onto the main browsers resource so the SDK uses the shared browser route cache instead of a generated wrapper layer. Made-with: Cursor
Remove the public cache priming helpers, keep jwt-required routes, and rename the example and tests so the python browser routing diff stays focused on cache-backed direct-to-VM behavior. Made-with: Cursor
Shorten the browser_routing allowlist field to subresources so the direct-to-VM configuration stays concise while keeping the same routing behavior. Made-with: Cursor
Move the handwritten routing helpers out of the old browser_scoped package, delete the unused browser session clone helper, warm the async browser list cache, and drop the generated type churn from the branch. Made-with: Cursor
Preserve browser routing settings across copy(), skip cache warming for raw response wrappers, and clean up the handwritten routing files so lint can pass on the current branch. Made-with: Cursor
Make the browser routing helpers type-check cleanly in CI, keep copy() signatures aligned with __init__, and avoid cache-warming errors on raw response wrappers. Made-with: Cursor
Encode string request bodies before building raw /curl/raw request options so the browser routing helpers satisfy CI type checks while preserving the public request API. Made-with: Cursor
|
Firetiger deploy monitoring skipped This PR didn't match the auto-monitor filter configured on your GitHub connection:
Reason: This is an automated release PR for the Python SDK with only internal chores, not a change to kernel API endpoints or Temporal workflows. To monitor this PR anyway, reply with |
|
🧪 Testing To try out this version of the SDK: Expires at: Sun, 24 May 2026 21:16:10 GMT |
Remove the public browser routing constructor config and derive direct-to-VM subresource routing from KERNEL_BROWSER_ROUTING_SUBRESOURCES instead, defaulting to curl while keeping the raw request helpers direct to the browser.
Store browser routes under a normalized session ID so cache lookups and deletes stay consistent when route data includes surrounding whitespace. Add a regression test to lock in the normalization behavior. Made-with: Cursor
5a6d7c8 to
cfb54ec
Compare
cfb54ec to
d66ba1b
Compare
Move browser route cache warming into the shared sync and async response hooks so browser metadata endpoints populate the cache consistently, including raw responses. Remove the handwritten browsers resource priming and cover the narrowed sniffing behavior with focused routing tests. Made-with: Cursor
Drop cached browser routes after successful DELETE /browsers/{id} responses so stale direct-to-VM session URLs are not reused. Cover both the success and failure paths with focused browser routing regressions.
Made-with: Cursor
Remove the leftover result/page wrappers from browser create, retrieve, update, and list now that route cache warming lives in the shared response hooks. Keep the actual curl and raw HTTP browser routing surface unchanged. Made-with: Cursor
Keep the browser route cache in sync for pool acquire and release flows so leased sessions can use direct VM routing without resource-specific cache handling. Made-with: Cursor
feat: add browser routing cache
d66ba1b to
c0728c8
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 4 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c0728c8. Configure here.
| populate_browser_route_cache_from_value(response.json(), cache=cache) | ||
| except Exception: | ||
| # Ignore malformed JSON in routing cache population. | ||
| return |
There was a problem hiding this comment.
Cache populated from error responses
Low Severity
maybe_populate_browser_route_cache_from_response runs before super()._process_response, and unlike the eviction helper it never checks response.is_success. A 4xx/5xx JSON error body returned from /browsers, /browsers/{id}, or /browser_pools/{id}/acquire is still walked by populate_browser_route_cache_from_value, and any dict nested in the error payload that happens to expose session_id, base_url, and cdp_ws_url will be cached as a live route. Mirroring the is_success guard used for eviction would keep the cache in sync with actual successful allocations.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit c0728c8. Configure here.
| params.update(options.params) | ||
| params["jwt"] = route.jwt | ||
| rewritten.params = params or options.params | ||
| return rewritten |
There was a problem hiding this comment.
Dead fallback in params rewrite
Low Severity
In rewrite_direct_vm_options, params is built fresh as an empty dict, copied from options.params, and then always has jwt assigned, so params is unconditionally truthy at the point of rewritten.params = params or options.params. The or options.params fallback can never be taken and only obscures intent. Dropping the dead branch (or explicitly handling a non-mapping options.params) would make the rewrite easier to reason about.
Reviewed by Cursor Bugbot for commit c0728c8. Configure here.
| params.update(options.params) | ||
| params["jwt"] = route.jwt | ||
| rewritten.params = params or options.params | ||
| return rewritten |
There was a problem hiding this comment.
Implicit rewrite leaks url query override
Low Severity
For the raw HTTP helpers, sanitize_curl_raw_params strips url and jwt from user-supplied params so the target URL and token cannot be overridden. The implicit path in rewrite_direct_vm_options only overwrites jwt but forwards any caller-supplied url query parameter as-is to the direct-VM endpoint. If a subresource on the browser VM ever interprets a url query parameter (today curl/raw does; future subresources might), a caller-supplied value would silently reach the VM instead of being reserved. Sharing the CURL_RAW_RESERVED_QUERY_KEYS filter here would keep the two routing paths consistent.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit c0728c8. Configure here.
| json_data=json, | ||
| timeout=_normalize_timeout(timeout), | ||
| ) | ||
| return cast(httpx.Response, parent.request(httpx.Response, options)) |
There was a problem hiding this comment.
Direct-VM request helper silently disables client timeout
High Severity
When callers invoke browsers.request (and the async variant) without specifying timeout, the helper passes _normalize_timeout(not_given) which collapses to None and stores it on FinalRequestOptions.timeout. In _build_request, the client-level self.timeout is only consulted when options.timeout is NotGiven, so the explicit None bypasses the configured client timeout and httpx is handed no timeout at all — requests can hang indefinitely. The stream helper correctly captures parent.timeout before normalizing, but the non-streaming path does not.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit c0728c8. Configure here.


Automated Release PR
0.51.0 (2026-04-24)
Full Changelog: v0.50.0...v0.51.0
Features
Bug Fixes
Chores
Documentation
Refactors
This pull request is managed by Stainless's GitHub App.
The semver version number is based on included commit messages. Alternatively, you can manually set the version number in the title of this pull request.
For a better experience, it is recommended to use either rebase-merge or squash-merge when merging this pull request.
🔗 Stainless website
📚 Read the docs
🙋 Reach out for help or questions
Note
Medium Risk
Adds request/response rewriting and auth-header stripping in the core client to support direct-to-VM browser routing, which could affect how browser subresource requests are sent. While scoped behind cached routes and an env allowlist, misrouting or cache edge cases could impact browser operations.
Overview
Bumps the SDK to
0.51.0and updates release metadata/changelog.Introduces browser-scoped routing support: the client now maintains a
BrowserRouteCache, rewrites eligible/browsers/{session_id}/{subresource}requests to the browser VM (allowlisted viaKERNEL_BROWSER_ROUTING_SUBRESOURCES, defaulting tocurl), injects per-browserjwtquery auth, and strips the normalAuthorizationheader for direct-to-VM requests. The cache is automatically warmed from successful JSON responses for browser metadata endpoints (including pool acquire) and evicted on successful browser deletion or pool release.Adds new
browsers.request()andbrowsers.stream()helpers (sync + async) to perform raw HTTP via/curl/raw, including protections to prevent userparamsfrom overriding reservedurl/jwt, plus an example and a comprehensive new routing test suite. Also exposesbrowser_session_idon the managed auth connection type and slightly hardensscripts/bootstrapenv var handling.Reviewed by Cursor Bugbot for commit c0728c8. Bugbot is set up for automated code reviews on this repo. Configure here.