From 3d555d700e9854a2ec1d095d484d916abea4541e Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 19:10:48 +0000 Subject: [PATCH 1/8] feat: Publish iframe and worker browser target types Stainless-Generated-From: 84c2ba1598b099a044f45d52e07c3ca1cabcee9c --- src/resources/browsers/telemetry.ts | 36 +++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/resources/browsers/telemetry.ts b/src/resources/browsers/telemetry.ts index 100f182e..aec75bdf 100644 --- a/src/resources/browsers/telemetry.ts +++ b/src/resources/browsers/telemetry.ts @@ -2979,7 +2979,14 @@ export interface BrowserEventContext { /** * CDP target type of the page that produced the event. */ - target_type?: 'page' | 'background_page' | 'service_worker' | 'shared_worker' | 'other'; + target_type?: + | 'page' + | 'iframe' + | 'worker' + | 'background_page' + | 'service_worker' + | 'shared_worker' + | 'other'; /** * URL relevant to this event — page URL for navigation and page events, request @@ -3769,7 +3776,14 @@ export namespace BrowserPageCrashedEvent { /** * CDP target type of the page that produced the event. */ - target_type: 'page' | 'background_page' | 'service_worker' | 'shared_worker' | 'other'; + target_type: + | 'page' + | 'iframe' + | 'worker' + | 'background_page' + | 'service_worker' + | 'shared_worker' + | 'other'; /** * URL the page was on when its renderer process crashed. @@ -4135,7 +4149,14 @@ export namespace BrowserPageNavigationEvent { /** * CDP target type of the page that produced the event. */ - target_type?: 'page' | 'background_page' | 'service_worker' | 'shared_worker' | 'other'; + target_type?: + | 'page' + | 'iframe' + | 'worker' + | 'background_page' + | 'service_worker' + | 'shared_worker' + | 'other'; /** * URL navigated to. @@ -4222,7 +4243,14 @@ export namespace BrowserPageTabOpenedEvent { /** * CDP target type of the page that produced the event. */ - target_type?: 'page' | 'background_page' | 'service_worker' | 'shared_worker' | 'other'; + target_type?: + | 'page' + | 'iframe' + | 'worker' + | 'background_page' + | 'service_worker' + | 'shared_worker' + | 'other'; /** * Initial page title of the new tab. From 809b9c8be2eb8919389bc5369e8a8a3cc5590182 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 23:06:04 +0000 Subject: [PATCH 2/8] feat: Correct monitor_disconnected description on computed state Stainless-Generated-From: 292847796a6ae9d3913a3c78aa61b84d488b1bd9 --- src/resources/browsers/telemetry.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/resources/browsers/telemetry.ts b/src/resources/browsers/telemetry.ts index aec75bdf..5988ad92 100644 --- a/src/resources/browsers/telemetry.ts +++ b/src/resources/browsers/telemetry.ts @@ -3290,8 +3290,12 @@ export namespace BrowserLiveViewDisconnectEvent { /** * The CDP connection to Chrome was lost. Telemetry events may be dropped until - * monitor_reconnected arrives. Treat any in-progress computed state (network_idle, - * page_layout_settled) as unreliable until then. + * monitor_reconnected arrives. In-progress computed state is discarded rather than + * paused, so computed events still pending for the current navigation + * (network_idle, page_layout_settled, page_navigation_settled) never fire. + * monitor_reconnected does not restore them. After reattachment a fresh state + * machine starts, so computed events can resume before the next navigation and + * carry empty navigation context until one occurs. */ export interface BrowserMonitorDisconnectedEvent { category: 'monitor'; From dc46ab213c0333cd16510c789a3fc10cb4bc0383 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2026 19:10:47 +0000 Subject: [PATCH 3/8] feat: Filter archived telemetry events by type Stainless-Generated-From: bffdddcb6c7e050cf71c47c97ad694cfd807a313 --- src/resources/browsers/telemetry.ts | 16 +++++++++++----- tests/api-resources/browsers/telemetry.test.ts | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/resources/browsers/telemetry.ts b/src/resources/browsers/telemetry.ts index 5988ad92..675d17fe 100644 --- a/src/resources/browsers/telemetry.ts +++ b/src/resources/browsers/telemetry.ts @@ -16,8 +16,9 @@ export class Telemetry extends APIResource { /** * Reads a page of telemetry events for the browser session. To page through * results, pass the X-Next-Offset value from the previous response as offset and - * repeat while X-Has-More is true. Returns an empty list when telemetry data is - * unavailable. + * repeat while X-Has-More is true. The category and type filters apply within each + * page, so a filtered page may be empty while X-Has-More is true. Returns an empty + * list when telemetry data is unavailable. * * @example * ```ts @@ -4862,9 +4863,7 @@ export interface TelemetryEventsParams extends OffsetPaginationParams { * Read direction. asc (default) reads oldest first, starting from since or the * offset cursor. desc reads newest first: each request returns one page of up to * limit records ending at the offset cursor (or until, or the newest archived - * event); combining desc with since is rejected with a 400. In either direction - * the category filter applies within the page, so a filtered page may be empty - * while X-Has-More is true. + * event); combining desc with since is rejected with a 400. */ order?: string; @@ -4874,6 +4873,13 @@ export interface TelemetryEventsParams extends OffsetPaginationParams { */ since?: string; + /** + * Restrict results to these event types, such as page_crashed or + * captcha_challenge_result. Repeat the parameter for multiple values. Combines + * with category: when both are set an event must match both. + */ + type?: Array; + /** * End of the window (exclusive): an RFC-3339 timestamp, or a duration like 5m * meaning that long ago. diff --git a/tests/api-resources/browsers/telemetry.test.ts b/tests/api-resources/browsers/telemetry.test.ts index 797d2539..1485ed1b 100644 --- a/tests/api-resources/browsers/telemetry.test.ts +++ b/tests/api-resources/browsers/telemetry.test.ts @@ -32,6 +32,7 @@ describe('resource telemetry', () => { offset: 0, order: 'order', since: 'since', + type: ['string'], until: 'until', }, { path: '/_stainless_unknown_path' }, From 0b0f348c33f9e9c372b31dbcfd19b505a26057ef Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2026 22:25:25 +0000 Subject: [PATCH 4/8] feat: Add profiles to browser pool acquire Stainless-Generated-From: c39d272cb7cd201ac01a26c116d52222989d4556 --- src/resources/browser-pools.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/resources/browser-pools.ts b/src/resources/browser-pools.ts index 7444b45a..2f71c1ba 100644 --- a/src/resources/browser-pools.ts +++ b/src/resources/browser-pools.ts @@ -1066,6 +1066,13 @@ export interface BrowserPoolAcquireParams { */ name?: string; + /** + * Profile selection for the browser session. Provide either id or name. If + * specified, the matching profile will be loaded into the browser session. + * Profiles must be created beforehand. + */ + profile?: Shared.BrowserProfile; + /** * Optional URL to navigate the acquired browser to. Overrides the pool's start_url * for this acquire only. Best-effort: failures to navigate do not fail the @@ -1200,7 +1207,8 @@ export interface BrowserPoolReleaseParams { * Defaults to true. A reused browser keeps the configuration it was created with, * so it does not pick up pool configuration changes made while it was in use. * Release with `reuse: false`, or flush the pool afterward, to rebuild it with the - * current configuration. + * current configuration. Browsers loaded with an acquire-time profile are always + * destroyed and replaced, even when reuse is true. */ reuse?: boolean; } From 68ff158188ce06e5f7526a324217d945e1970cc3 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2026 15:13:16 +0000 Subject: [PATCH 5/8] feat: Expose Search rollout access in org entitlements Stainless-Generated-From: 80d2d6eb34035a69b825f2bae03d2fae06aa2003 --- src/resources/organization/entitlements.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/resources/organization/entitlements.ts b/src/resources/organization/entitlements.ts index 3c29cf59..ee0ad2c8 100644 --- a/src/resources/organization/entitlements.ts +++ b/src/resources/organization/entitlements.ts @@ -58,6 +58,12 @@ export namespace OrgEntitlements { proxy_bypass_hosts: Features.ProxyBypassHosts; + /** + * Whether the Search API is enabled for the organization by its rollout feature + * flag. + */ + search: Features.Search; + /** * Whether the organization can access vaults, using the same access check as vault * API routes. @@ -183,6 +189,17 @@ export namespace OrgEntitlements { enabled: boolean; } + /** + * Whether the Search API is enabled for the organization by its rollout feature + * flag. + */ + export interface Search { + /** + * Whether the organization is entitled to use this feature. + */ + enabled: boolean; + } + /** * Whether the organization can access vaults, using the same access check as vault * API routes. From 27108aa04eb3be3f307a696421e33c45071a5584 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2026 18:33:33 +0000 Subject: [PATCH 6/8] feat: Route browser destinations through selected proxies Stainless-Generated-From: e1016a15f4f2aaf3891086b5142859bbcf5d62d1 --- src/resources/browsers/browsers.ts | 46 +++++++++++++++++++ src/resources/browsers/telemetry.ts | 13 +++--- tests/api-resources/browser-pools.test.ts | 10 +++- tests/api-resources/browsers/browsers.test.ts | 10 +++- 4 files changed, 71 insertions(+), 8 deletions(-) diff --git a/src/resources/browsers/browsers.ts b/src/resources/browsers/browsers.ts index b6663d54..9a0b2a04 100644 --- a/src/resources/browsers/browsers.ts +++ b/src/resources/browsers/browsers.ts @@ -375,6 +375,52 @@ export interface BrowserNetworkConfig { * upstream-proxy and Kernel-managed direct egress and cannot reach into a VPN. */ private_hosts?: Array; + + /** + * Per-destination proxy routes for a browser session. After setup, a destination + * hostname is matched against every route's hosts, regardless of port; route order + * does not matter. An exact hostname beats a wildcard, and a longer wildcard + * suffix beats a shorter one (for a.b.example.com: "a.b.example.com" > + * "_.b.example.com" > "_.example.com"). A host pattern may appear in only one + * route. "\*.example.com" matches subdomains only, not example.com. A matched + * request selects the route's proxy instead of the session's top-level proxy + * (including mode: direct); the route proxy's own bypass_hosts still apply. If the + * route proxy becomes unavailable, matched requests fail closed without falling + * back. Requests that match no route use the session's default egress from the + * top-level proxy field (or the browser default when proxy is omitted: stealth + * proxy or direct egress). Routes take effect once the session is created; + * start_url and other traffic during browser setup use the top-level proxy. + * Setting routes requires proxy v3. Not supported on browser pools. + */ + proxy_routes?: Array; +} + +export namespace BrowserNetworkConfig { + export interface ProxyRoute { + /** + * Exact hostnames or leading \*. wildcard patterns (subdomains only); patterns + * cannot include ports, and matching ignores the destination port. Hosts not + * matched by any route use the session's top-level proxy (or the browser default + * when proxy is omitted). + */ + hosts: Array; + + /** + * Select an active non-direct proxy by ID or name. Responses always use ID. + */ + proxy: ProxyRoute.Proxy; + } + + export namespace ProxyRoute { + /** + * Select an active non-direct proxy by ID or name. Responses always use ID. + */ + export interface Proxy { + id?: string; + + name?: string; + } + } } /** diff --git a/src/resources/browsers/telemetry.ts b/src/resources/browsers/telemetry.ts index 675d17fe..99f6d4d4 100644 --- a/src/resources/browsers/telemetry.ts +++ b/src/resources/browsers/telemetry.ts @@ -4370,15 +4370,16 @@ export namespace BrowserProxyErrorEvent { /** * Proxy-layer error code: the X-Kernel-Proxy-Error response header value from a * branded 5xx error page served by the metro egress host-proxy. Values mirror what - * the proxy emits: destination_blocked, provider_blacklisted, - * provider_unreachable, provider_rejected, origin_tls_timeout, - * origin_response_incomplete, proxy_unavailable, restricted_route_unavailable, - * upstream_timeout, upstream_dns_failure, upstream_connect_failed. A header value - * the browser image does not recognize is reported as unknown, with the header - * value in raw_code. + * the proxy emits: destination_blocked, destination_route_unavailable, + * provider_blacklisted, provider_unreachable, provider_rejected, + * origin_tls_timeout, origin_response_incomplete, proxy_unavailable, + * restricted_route_unavailable, upstream_timeout, upstream_dns_failure, + * upstream_connect_failed. A header value the browser image does not recognize is + * reported as unknown, with the header value in raw_code. */ code: | 'destination_blocked' + | 'destination_route_unavailable' | 'provider_blacklisted' | 'provider_unreachable' | 'provider_rejected' diff --git a/tests/api-resources/browser-pools.test.ts b/tests/api-resources/browser-pools.test.ts index 1b27e325..15dbfc24 100644 --- a/tests/api-resources/browser-pools.test.ts +++ b/tests/api-resources/browser-pools.test.ts @@ -30,7 +30,15 @@ describe('resource browserPools', () => { headless: false, kiosk_mode: true, name: 'my-pool', - network: { private_hosts: ['*.example.ts.net', '100.64.0.0/10'] }, + network: { + private_hosts: ['*.example.ts.net', '100.64.0.0/10'], + proxy_routes: [ + { + hosts: ['string'], + proxy: { id: 'x', name: 'x' }, + }, + ], + }, profile: { id: 'id', name: 'name' }, proxy_id: 'proxy_id', refresh_on_profile_update: true, diff --git a/tests/api-resources/browsers/browsers.test.ts b/tests/api-resources/browsers/browsers.test.ts index e126aaff..bc364170 100644 --- a/tests/api-resources/browsers/browsers.test.ts +++ b/tests/api-resources/browsers/browsers.test.ts @@ -34,7 +34,15 @@ describe('resource browsers', () => { kiosk_mode: true, memory: '8GiB', name: 'checkout-flow-1', - network: { private_hosts: ['*.example.ts.net', '100.64.0.0/10'] }, + network: { + private_hosts: ['*.example.ts.net', '100.64.0.0/10'], + proxy_routes: [ + { + hosts: ['string'], + proxy: { id: 'x', name: 'x' }, + }, + ], + }, profile: { id: 'id', name: 'name', From 44e5665b3edd75f94605cca72479e89029225b38 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2026 19:14:59 +0000 Subject: [PATCH 7/8] feat: Proxy custom WebMCP tools in the control plane Stainless-Generated-From: 83f6535711408f6676bc3c1d8e5824c0120066e1 --- .stats.yml | 2 +- api.md | 37 ++- src/resources/browsers/browsers.ts | 32 +- src/resources/browsers/index.ts | 5 +- src/resources/browsers/webmcp.ts | 205 +----------- src/resources/browsers/webmcp/custom-tools.ts | 160 ++++++++++ src/resources/browsers/webmcp/index.ts | 26 ++ src/resources/browsers/webmcp/webmcp.ts | 295 ++++++++++++++++++ .../browsers/webmcp/custom-tools.test.ts | 67 ++++ .../browsers/{ => webmcp}/webmcp.test.ts | 12 + 10 files changed, 612 insertions(+), 229 deletions(-) create mode 100644 src/resources/browsers/webmcp/custom-tools.ts create mode 100644 src/resources/browsers/webmcp/index.ts create mode 100644 src/resources/browsers/webmcp/webmcp.ts create mode 100644 tests/api-resources/browsers/webmcp/custom-tools.test.ts rename tests/api-resources/browsers/{ => webmcp}/webmcp.test.ts (78%) diff --git a/.stats.yml b/.stats.yml index e8ac4eb6..ec91ee34 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 169 +configured_endpoints: 172 diff --git a/api.md b/api.md index bca08e38..dee34db6 100644 --- a/api.md +++ b/api.md @@ -307,19 +307,36 @@ Methods: Types: -- InvocationFailure -- InvocationResult -- InvokeRequest -- Tool -- ToolAnnotations -- ToolFrame -- ToolSource -- ToolsResponse +- CustomToolSource +- InvocationFailure +- InvocationResult +- InvokeRequest +- Tool +- ToolAnnotations +- ToolFrame +- ToolMetadata +- ToolSource +- ToolsResponse Methods: -- client.browsers.webmcp.invokeTool(idOrName, { ...params }) -> InvocationResult -- client.browsers.webmcp.listTools(idOrName) -> ToolsResponse +- client.browsers.webmcp.invokeTool(idOrName, { ...params }) -> InvocationResult +- client.browsers.webmcp.listTools(idOrName, { ...params }) -> ToolsResponse + +### CustomTools + +Types: + +- AddRequest +- CustomToolsResponse +- Definition +- Match + +Methods: + +- client.browsers.webmcp.customTools.list(idOrName) -> CustomToolsResponse +- client.browsers.webmcp.customTools.add(idOrName, { ...params }) -> CustomToolsResponse +- client.browsers.webmcp.customTools.remove(id, { ...params }) -> void # Profiles diff --git a/src/resources/browsers/browsers.ts b/src/resources/browsers/browsers.ts index 9a0b2a04..63e8380b 100644 --- a/src/resources/browsers/browsers.ts +++ b/src/resources/browsers/browsers.ts @@ -109,19 +109,6 @@ import { TelemetryStreamParams, TelemetryStreamResponse, } from './telemetry'; -import * as WebmcpAPI from './webmcp'; -import { - InvocationFailure, - InvocationResult, - InvokeRequest, - Tool, - ToolAnnotations, - ToolFrame, - ToolSource, - ToolsResponse, - Webmcp, - WebmcpInvokeToolParams, -} from './webmcp'; import * as FsAPI from './fs/fs'; import { FCreateDirectoryParams, @@ -140,6 +127,22 @@ import { FWriteFileParams, Fs, } from './fs/fs'; +import * as WebmcpAPI from './webmcp/webmcp'; +import { + CustomToolSource, + InvocationFailure, + InvocationResult, + InvokeRequest, + Tool, + ToolAnnotations, + ToolFrame, + ToolMetadata, + ToolSource, + ToolsResponse, + Webmcp, + WebmcpInvokeToolParams, + WebmcpListToolsParams, +} from './webmcp/webmcp'; import { APIPromise } from '../../core/api-promise'; import { OffsetPagination, type OffsetPaginationParams, PagePromise } from '../../core/pagination'; import { type Uploadable } from '../../core/uploads'; @@ -2065,14 +2068,17 @@ export declare namespace Browsers { export { Webmcp as Webmcp, + type CustomToolSource as CustomToolSource, type InvocationFailure as InvocationFailure, type InvocationResult as InvocationResult, type InvokeRequest as InvokeRequest, type Tool as Tool, type ToolAnnotations as ToolAnnotations, type ToolFrame as ToolFrame, + type ToolMetadata as ToolMetadata, type ToolSource as ToolSource, type ToolsResponse as ToolsResponse, type WebmcpInvokeToolParams as WebmcpInvokeToolParams, + type WebmcpListToolsParams as WebmcpListToolsParams, }; } diff --git a/src/resources/browsers/index.ts b/src/resources/browsers/index.ts index 19edac39..ed0d11c3 100644 --- a/src/resources/browsers/index.ts +++ b/src/resources/browsers/index.ts @@ -152,13 +152,16 @@ export { } from './telemetry'; export { Webmcp, + type CustomToolSource, type InvocationFailure, type InvocationResult, type InvokeRequest, type Tool, type ToolAnnotations, type ToolFrame, + type ToolMetadata, type ToolSource, type ToolsResponse, type WebmcpInvokeToolParams, -} from './webmcp'; + type WebmcpListToolsParams, +} from './webmcp/index'; diff --git a/src/resources/browsers/webmcp.ts b/src/resources/browsers/webmcp.ts index b88f7655..851ddc76 100644 --- a/src/resources/browsers/webmcp.ts +++ b/src/resources/browsers/webmcp.ts @@ -1,206 +1,3 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import { APIResource } from '../../core/resource'; -import { APIPromise } from '../../core/api-promise'; -import { RequestOptions } from '../../internal/request-options'; -import { path } from '../../internal/utils/path'; - -/** - * Discover and invoke native page tools across the browser instance. - */ -export class Webmcp extends APIResource { - /** - * Invokes the exact live registration identified by tool_ref. Non-autosubmit - * declarative form tools return after their fields are populated with an - * awaiting_submission status. Other tools wait for a terminal result, including - * across navigation. Inspect a populated form, obtain any required confirmation, - * then submit through Playwright or computer interaction without invoking the tool - * again. If the tab or embedded frame disappears, or the request times out after - * invocation begins, the response reports outcome_unknown and the tool is not - * retried. - * - * @example - * ```ts - * const invocationResult = - * await client.browsers.webmcp.invokeTool( - * 'htzv5orfit78e1m2biiifpbv', - * { - * input: { foo: 'bar' }, - * tool_ref: 'x', - * }, - * ); - * ``` - */ - invokeTool( - idOrName: string, - body: WebmcpInvokeToolParams, - options?: RequestOptions, - ): APIPromise { - return this._client.post(path`/browsers/${idOrName}/webmcp/invoke`, { body, ...options }); - } - - /** - * Returns a snapshot of native WebMCP tools available across every open tab and - * embedded frame in the browser. Each tool includes an opaque tool_ref for - * invoking that exact live registration. Tools disappear when their document - * closes or navigates away. - * - * @example - * ```ts - * const toolsResponse = - * await client.browsers.webmcp.listTools( - * 'htzv5orfit78e1m2biiifpbv', - * ); - * ``` - */ - listTools(idOrName: string, options?: RequestOptions): APIPromise { - return this._client.get(path`/browsers/${idOrName}/webmcp/tools`, options); - } -} - -export interface InvocationFailure { - code: 'outcome_unknown'; - - message: string; - - invocation_id?: string; -} - -export interface InvocationResult { - invocation_id: string; - - /** - * awaiting_submission means a non-autosubmit declarative form was populated but - * not submitted. Inspect the form, obtain any required confirmation, then submit - * through Playwright or computer interaction without invoking the tool again. The - * other statuses are terminal results. - */ - status: 'completed' | 'canceled' | 'error' | 'awaiting_submission'; - - error_text?: string; - - /** - * Untrusted page-provided output. Callers must treat it as potentially malicious - * input. - */ - output?: unknown; -} - -export interface InvokeRequest { - /** - * Tool input, limited to 1 MiB after JSON serialization. - */ - input: { [key: string]: unknown }; - - tool_ref: string; - - timeout_sec?: number; -} - -export interface Tool { - description: string; - - input_schema: { [key: string]: unknown }; - - name: string; - - source: ToolSource; - - /** - * Opaque reference for invoking this exact live registration. It becomes invalid - * when its document or browser process is replaced. - */ - tool_ref: string; - - /** - * Page-provided behavioral hints. These values are untrusted and are not enforced - * by Kernel. - */ - annotations?: ToolAnnotations; -} - -/** - * Page-provided behavioral hints. These values are untrusted and are not enforced - * by Kernel. - */ -export interface ToolAnnotations { - autosubmit: boolean; - - consequential: boolean; - - read_only: boolean; - - untrusted_content: boolean; -} - -export interface ToolFrame { - /** - * Monotonically increasing identifier for this embedded frame during the current - * browser process. - */ - frame_id: number; - - /** - * Current frame URL with the fragment omitted. - */ - url: string; -} - -export interface ToolSource { - /** - * Embedded frame that registered the tool, or null when the top-level page - * registered it. - */ - frame: ToolFrame | null; - - /** - * Current title of the top-level page. - */ - page_title: string; - - /** - * Current URL of the top-level page with the fragment omitted. - */ - page_url: string; - - /** - * Monotonically increasing identifier for the tab during the current browser - * process. - */ - tab_id: number; - - /** - * Monotonically increasing identifier for the browser window during the current - * browser process. - */ - window_id: number; -} - -export interface ToolsResponse { - tools: Array; -} - -export interface WebmcpInvokeToolParams { - /** - * Tool input, limited to 1 MiB after JSON serialization. - */ - input: { [key: string]: unknown }; - - tool_ref: string; - - timeout_sec?: number; -} - -export declare namespace Webmcp { - export { - type InvocationFailure as InvocationFailure, - type InvocationResult as InvocationResult, - type InvokeRequest as InvokeRequest, - type Tool as Tool, - type ToolAnnotations as ToolAnnotations, - type ToolFrame as ToolFrame, - type ToolSource as ToolSource, - type ToolsResponse as ToolsResponse, - type WebmcpInvokeToolParams as WebmcpInvokeToolParams, - }; -} +export * from './webmcp/index'; diff --git a/src/resources/browsers/webmcp/custom-tools.ts b/src/resources/browsers/webmcp/custom-tools.ts new file mode 100644 index 00000000..cd0f4f69 --- /dev/null +++ b/src/resources/browsers/webmcp/custom-tools.ts @@ -0,0 +1,160 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../../core/resource'; +import * as WebmcpAPI from './webmcp'; +import { APIPromise } from '../../../core/api-promise'; +import { buildHeaders } from '../../../internal/headers'; +import { RequestOptions } from '../../../internal/request-options'; +import { path } from '../../../internal/utils/path'; + +/** + * Discover and invoke native page tools across the browser instance. + */ +export class CustomTools extends APIResource { + /** + * Returns every registered custom tool with its generated ID, namespace, matcher, + * and MCP tool metadata. + * + * @example + * ```ts + * const customToolsResponse = + * await client.browsers.webmcp.customTools.list( + * 'id_or_name', + * ); + * ``` + */ + list(idOrName: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/browsers/${idOrName}/webmcp/custom-tools`, options); + } + + /** + * Add a namespaced batch of custom tools. A custom tool can be page-backed or + * CDP-backed. Page-backed tools execute in the page via JavaScript. CDP-backed + * tools execute via CDP and can use all browser REPL tools (see `/repl`). The + * source must evaluate to a non-empty array of definitions with URL matchers, tool + * metadata (including an optional output schema), and execute functions. The batch + * is added atomically. Matchers apply to top-level documents and nested frames, + * including out-of-process iframes; each matching tool is exposed once on the + * tab's top-level document and appears in the WebMCP tool snapshot. + * + * To update one tool, list the tools, delete its ID, and add its replacement. Set + * force_overwrite_namespace to replace every existing tool in the namespace + * atomically; omitted or false adds tools without replacing existing ones. + * Existing invocations continue. + * + * @example + * ```ts + * const customToolsResponse = + * await client.browsers.webmcp.customTools.add( + * 'id_or_name', + * { namespace: 'namespace', source: 'source' }, + * ); + * ``` + */ + add( + idOrName: string, + body: CustomToolAddParams, + options?: RequestOptions, + ): APIPromise { + return this._client.post(path`/browsers/${idOrName}/webmcp/custom-tools`, { body, ...options }); + } + + /** + * Removes one custom tool by generated ID. An invocation already in progress is + * not canceled. + * + * @example + * ```ts + * await client.browsers.webmcp.customTools.remove( + * 'ct_n10b9798ad53ecc4y69z31e1', + * { id_or_name: 'id_or_name' }, + * ); + * ``` + */ + remove(id: string, params: CustomToolRemoveParams, options?: RequestOptions): APIPromise { + const { id_or_name } = params; + return this._client.delete(path`/browsers/${id_or_name}/webmcp/custom-tools/${id}`, { + ...options, + headers: buildHeaders([{ Accept: '*/*' }, options?.headers]), + }); + } +} + +export interface AddRequest { + namespace: string; + + /** + * JavaScript expression that evaluates to a non-empty array of custom tool + * definitions. Limited to 8,000,000 bytes when UTF-8 encoded, so multi-byte + * characters reduce the allowed character count. + */ + source: string; + + /** + * Atomically replace all existing tools in this namespace with this batch when + * true. + */ + force_overwrite_namespace?: boolean; +} + +export interface CustomToolsResponse { + tools: Array; +} + +export interface Definition { + id: string; + + kind: string; + + match: Match; + + namespace: string; + + /** + * Tool metadata follows the + * [MCP Tool definition](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool) + * and the + * [WebMCP RegisteredTool definition](https://webmachinelearning.github.io/webmcp/#dictdef-registeredtool). + * outputSchema is optional for page and custom tools. + */ + tool: WebmcpAPI.ToolMetadata; +} + +export interface Match { + url_patterns: Array; +} + +export interface CustomToolAddParams { + namespace: string; + + /** + * JavaScript expression that evaluates to a non-empty array of custom tool + * definitions. Limited to 8,000,000 bytes when UTF-8 encoded, so multi-byte + * characters reduce the allowed character count. + */ + source: string; + + /** + * Atomically replace all existing tools in this namespace with this batch when + * true. + */ + force_overwrite_namespace?: boolean; +} + +export interface CustomToolRemoveParams { + /** + * Browser session ID or name + */ + id_or_name: string; +} + +export declare namespace CustomTools { + export { + type AddRequest as AddRequest, + type CustomToolsResponse as CustomToolsResponse, + type Definition as Definition, + type Match as Match, + type CustomToolAddParams as CustomToolAddParams, + type CustomToolRemoveParams as CustomToolRemoveParams, + }; +} diff --git a/src/resources/browsers/webmcp/index.ts b/src/resources/browsers/webmcp/index.ts new file mode 100644 index 00000000..31bd12f2 --- /dev/null +++ b/src/resources/browsers/webmcp/index.ts @@ -0,0 +1,26 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { + CustomTools, + type AddRequest, + type CustomToolsResponse, + type Definition, + type Match, + type CustomToolAddParams, + type CustomToolRemoveParams, +} from './custom-tools'; +export { + Webmcp, + type CustomToolSource, + type InvocationFailure, + type InvocationResult, + type InvokeRequest, + type Tool, + type ToolAnnotations, + type ToolFrame, + type ToolMetadata, + type ToolSource, + type ToolsResponse, + type WebmcpInvokeToolParams, + type WebmcpListToolsParams, +} from './webmcp'; diff --git a/src/resources/browsers/webmcp/webmcp.ts b/src/resources/browsers/webmcp/webmcp.ts new file mode 100644 index 00000000..139f9124 --- /dev/null +++ b/src/resources/browsers/webmcp/webmcp.ts @@ -0,0 +1,295 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../../core/resource'; +import * as CustomToolsAPI from './custom-tools'; +import { + AddRequest, + CustomToolAddParams, + CustomToolRemoveParams, + CustomTools, + CustomToolsResponse, + Definition, + Match, +} from './custom-tools'; +import { APIPromise } from '../../../core/api-promise'; +import { RequestOptions } from '../../../internal/request-options'; +import { path } from '../../../internal/utils/path'; + +/** + * Discover and invoke native page tools across the browser instance. + */ +export class Webmcp extends APIResource { + customTools: CustomToolsAPI.CustomTools = new CustomToolsAPI.CustomTools(this._client); + + /** + * Invokes the exact live registration identified by tool_ref. Non-autosubmit + * declarative form tools return after their fields are populated with an + * awaiting_submission status. Other tools wait for a terminal result, including + * across navigation. Inspect a populated form, obtain any required confirmation, + * then submit through Playwright or computer interaction without invoking the tool + * again. If the tab or embedded frame disappears, or the request times out after + * invocation begins, the response reports outcome_unknown and the tool is not + * retried. CDP-backed custom tool outputs above 240 KiB return an error rather + * than a truncated result. + * + * @example + * ```ts + * const invocationResult = + * await client.browsers.webmcp.invokeTool( + * 'htzv5orfit78e1m2biiifpbv', + * { + * input: { foo: 'bar' }, + * tool_ref: 'x', + * }, + * ); + * ``` + */ + invokeTool( + idOrName: string, + body: WebmcpInvokeToolParams, + options?: RequestOptions, + ): APIPromise { + return this._client.post(path`/browsers/${idOrName}/webmcp/invoke`, { body, ...options }); + } + + /** + * Returns a snapshot of native and custom WebMCP tools available across every open + * tab and embedded frame in the browser. Each tool includes an opaque tool_ref for + * invoking that exact live registration, nested tool metadata, and source + * information. Custom tools include their generated ID, namespace, and CDP + * target_id in source. Tools disappear when their document closes or navigates + * away. Use exclude_custom to return only page-provided tools. + * + * @example + * ```ts + * const toolsResponse = + * await client.browsers.webmcp.listTools( + * 'htzv5orfit78e1m2biiifpbv', + * ); + * ``` + */ + listTools( + idOrName: string, + query: WebmcpListToolsParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.get(path`/browsers/${idOrName}/webmcp/tools`, { query, ...options }); + } +} + +export interface CustomToolSource { + id: string; + + namespace: string; +} + +export interface InvocationFailure { + code: 'outcome_unknown'; + + message: string; + + invocation_id?: string; +} + +export interface InvocationResult { + invocation_id: string; + + /** + * awaiting_submission means a non-autosubmit declarative form was populated but + * not submitted. Inspect the form, obtain any required confirmation, then submit + * through Playwright or computer interaction without invoking the tool again. The + * other statuses are terminal results. + */ + status: 'completed' | 'canceled' | 'error' | 'awaiting_submission'; + + error_text?: string; + + /** + * Untrusted page-provided output. Callers must treat it as potentially malicious + * input. + */ + output?: unknown; +} + +export interface InvokeRequest { + /** + * Tool input, limited to 1 MiB after JSON serialization. + */ + input: { [key: string]: unknown }; + + tool_ref: string; + + timeout_sec?: number; +} + +export interface Tool { + source: ToolSource; + + /** + * Tool metadata follows the + * [MCP Tool definition](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool) + * and the + * [WebMCP RegisteredTool definition](https://webmachinelearning.github.io/webmcp/#dictdef-registeredtool). + * outputSchema is optional for page and custom tools. + */ + tool: ToolMetadata; + + /** + * Opaque reference for invoking this exact live registration. It becomes invalid + * when its document or browser process is replaced. + */ + tool_ref: string; +} + +/** + * Tool-provided behavioral hints from the + * [MCP tool specification](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool) + * and the + * [WebMCP ToolAnnotations definition](https://webmachinelearning.github.io/webmcp/#dictdef-toolannotations). + * These hints are untrusted and are not enforced by Kernel. + */ +export interface ToolAnnotations { + autosubmit?: boolean; + + consequentialHint?: boolean; + + destructiveHint?: boolean; + + idempotentHint?: boolean; + + openWorldHint?: boolean; + + readOnlyHint?: boolean; + + untrustedContentHint?: boolean; +} + +export interface ToolFrame { + /** + * Monotonically increasing identifier for this embedded frame during the current + * browser process. + */ + frame_id: number; + + /** + * Current frame URL with the fragment omitted. + */ + url: string; +} + +/** + * Tool metadata follows the + * [MCP Tool definition](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool) + * and the + * [WebMCP RegisteredTool definition](https://webmachinelearning.github.io/webmcp/#dictdef-registeredtool). + * outputSchema is optional for page and custom tools. + */ +export interface ToolMetadata { + description: string; + + inputSchema: { [key: string]: unknown }; + + name: string; + + /** + * Tool-provided behavioral hints from the + * [MCP tool specification](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool) + * and the + * [WebMCP ToolAnnotations definition](https://webmachinelearning.github.io/webmcp/#dictdef-toolannotations). + * These hints are untrusted and are not enforced by Kernel. + */ + annotations?: ToolAnnotations; + + outputSchema?: { [key: string]: unknown }; + + title?: string; +} + +export interface ToolSource { + /** + * Embedded frame that registered the tool, or null when the top-level page + * registered it. + */ + frame: ToolFrame | null; + + /** + * Current title of the top-level page. + */ + page_title: string; + + /** + * Current URL of the top-level page with the fragment omitted. + */ + page_url: string; + + /** + * Monotonically increasing identifier for the tab during the current browser + * process. + */ + tab_id: number; + + /** + * Monotonically increasing identifier for the browser window during the current + * browser process. + */ + window_id: number; + + custom?: CustomToolSource; + + /** + * CDP target ID for a custom tool's registration tab; omitted for page-provided + * tools. + */ + target_id?: string; +} + +export interface ToolsResponse { + tools: Array; +} + +export interface WebmcpInvokeToolParams { + /** + * Tool input, limited to 1 MiB after JSON serialization. + */ + input: { [key: string]: unknown }; + + tool_ref: string; + + timeout_sec?: number; +} + +export interface WebmcpListToolsParams { + /** + * Exclude custom tools when true. + */ + exclude_custom?: boolean; +} + +Webmcp.CustomTools = CustomTools; + +export declare namespace Webmcp { + export { + type CustomToolSource as CustomToolSource, + type InvocationFailure as InvocationFailure, + type InvocationResult as InvocationResult, + type InvokeRequest as InvokeRequest, + type Tool as Tool, + type ToolAnnotations as ToolAnnotations, + type ToolFrame as ToolFrame, + type ToolMetadata as ToolMetadata, + type ToolSource as ToolSource, + type ToolsResponse as ToolsResponse, + type WebmcpInvokeToolParams as WebmcpInvokeToolParams, + type WebmcpListToolsParams as WebmcpListToolsParams, + }; + + export { + CustomTools as CustomTools, + type AddRequest as AddRequest, + type CustomToolsResponse as CustomToolsResponse, + type Definition as Definition, + type Match as Match, + type CustomToolAddParams as CustomToolAddParams, + type CustomToolRemoveParams as CustomToolRemoveParams, + }; +} diff --git a/tests/api-resources/browsers/webmcp/custom-tools.test.ts b/tests/api-resources/browsers/webmcp/custom-tools.test.ts new file mode 100644 index 00000000..339c97eb --- /dev/null +++ b/tests/api-resources/browsers/webmcp/custom-tools.test.ts @@ -0,0 +1,67 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Kernel from '@onkernel/sdk'; + +const client = new Kernel({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource customTools', () => { + // Mock server tests are disabled + test.skip('list', async () => { + const responsePromise = client.browsers.webmcp.customTools.list('id_or_name'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Mock server tests are disabled + test.skip('add: only required params', async () => { + const responsePromise = client.browsers.webmcp.customTools.add('id_or_name', { + namespace: 'namespace', + source: 'source', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Mock server tests are disabled + test.skip('add: required and optional params', async () => { + const response = await client.browsers.webmcp.customTools.add('id_or_name', { + namespace: 'namespace', + source: 'source', + force_overwrite_namespace: true, + }); + }); + + // Mock server tests are disabled + test.skip('remove: only required params', async () => { + const responsePromise = client.browsers.webmcp.customTools.remove('ct_n10b9798ad53ecc4y69z31e1', { + id_or_name: 'id_or_name', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Mock server tests are disabled + test.skip('remove: required and optional params', async () => { + const response = await client.browsers.webmcp.customTools.remove('ct_n10b9798ad53ecc4y69z31e1', { + id_or_name: 'id_or_name', + }); + }); +}); diff --git a/tests/api-resources/browsers/webmcp.test.ts b/tests/api-resources/browsers/webmcp/webmcp.test.ts similarity index 78% rename from tests/api-resources/browsers/webmcp.test.ts rename to tests/api-resources/browsers/webmcp/webmcp.test.ts index 7bfb8db4..0d1c4d21 100644 --- a/tests/api-resources/browsers/webmcp.test.ts +++ b/tests/api-resources/browsers/webmcp/webmcp.test.ts @@ -43,4 +43,16 @@ describe('resource webmcp', () => { expect(dataAndResponse.data).toBe(response); expect(dataAndResponse.response).toBe(rawResponse); }); + + // Mock server tests are disabled + test.skip('listTools: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.browsers.webmcp.listTools( + 'htzv5orfit78e1m2biiifpbv', + { exclude_custom: true }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Kernel.NotFoundError); + }); }); From 2d8fe34522d2e86f8376a9ac6a025ab2be9d2639 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2026 19:19:50 +0000 Subject: [PATCH 8/8] release: 0.112.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ package.json | 2 +- src/version.ts | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6d4c016b..333e70a1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.111.0" + ".": "0.112.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d1c8026..bee4127d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## [0.112.0](https://github.com/kernel/kernel-node-sdk/compare/v0.111.0...v0.112.0) (2026-09-24) + + +### Features + +* Add profiles to browser pool acquire ([0b0f348](https://github.com/kernel/kernel-node-sdk/commit/0b0f348c33f9e9c372b31dbcfd19b505a26057ef)) +* Correct monitor_disconnected description on computed state ([809b9c8](https://github.com/kernel/kernel-node-sdk/commit/809b9c8be2eb8919389bc5369e8a8a3cc5590182)) +* Expose Search rollout access in org entitlements ([68ff158](https://github.com/kernel/kernel-node-sdk/commit/68ff158188ce06e5f7526a324217d945e1970cc3)) +* Filter archived telemetry events by type ([dc46ab2](https://github.com/kernel/kernel-node-sdk/commit/dc46ab213c0333cd16510c789a3fc10cb4bc0383)) +* Proxy custom WebMCP tools in the control plane ([44e5665](https://github.com/kernel/kernel-node-sdk/commit/44e5665b3edd75f94605cca72479e89029225b38)) +* Publish iframe and worker browser target types ([3d555d7](https://github.com/kernel/kernel-node-sdk/commit/3d555d700e9854a2ec1d095d484d916abea4541e)) +* Route browser destinations through selected proxies ([27108aa](https://github.com/kernel/kernel-node-sdk/commit/27108aa04eb3be3f307a696421e33c45071a5584)) + ## [0.111.0](https://github.com/kernel/kernel-node-sdk/compare/v0.110.0...v0.111.0) (2026-09-22) diff --git a/package.json b/package.json index 0930d37e..539bd6d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onkernel/sdk", - "version": "0.111.0", + "version": "0.112.0", "description": "The official TypeScript library for the Kernel API", "author": "Kernel <>", "types": "dist/index.d.ts", diff --git a/src/version.ts b/src/version.ts index 36c76795..142b029c 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '0.111.0'; // x-release-please-version +export const VERSION = '0.112.0'; // x-release-please-version