UNPKG

@anthropic-ai/sdk

Version:
207 lines (187 loc) • 7.93 kB
/** * The browser member registry: one row per member, from which most per-member tables derive. * * Member names and input types come from the API's `tool_use` union (`inputs.ts`). What is kept by * hand here is what the API types lack: each member's result kind, the confirmation text the * model reads after a pure action, which members the API disables by default, and which members render * only a `browser_state` block. A test diffs the member set and the default-disabled set against the * published toolset definition so the two cannot drift silently. */ import type { BetaBrowserStateTabEntry, BetaBrowserToolsetConfigs } from '../../../resources/beta'; import { buildMembers, type BetaScreenshotResult, isEnabled, memberOf, type KindResultOf, type MemberRow, type Registry, type ResultKind, type ToolsetMember, type TypedResultOf, } from './core'; import type { BrowserMemberInputs, BetaBrowserMemberName } from './inputs'; import type { BetaBrowserNavigateResult } from './results'; export const FAMILY = 'browser' as const; /** * Members whose calls wait for a person unless the application says otherwise: page content can steer what the * model asks them to do, and what they do reaches outside the page. */ export const CONFIRM_REQUIRED: ReadonlySet<string> = /* @__PURE__ */ new Set([ 'javascript_exec', 'file_upload', ]); export const TOOLSET_TYPE = 'browser_toolset_20260801' as const; /** * What each browser member's implementation returns. A pure action returns nothing, or one line of text the model reads * in a text block of its own after the SDK's acknowledgment. */ export interface BrowserMemberResults { navigate: BetaBrowserNavigateResult; screenshot: BetaScreenshotResult; zoom: BetaScreenshotResult; left_click: void | string; right_click: void | string; middle_click: void | string; double_click: void | string; triple_click: void | string; hover: void | string; left_click_drag: void | string; left_mouse_down: void | string; left_mouse_up: void | string; mouse_move: void | string; scroll: void | string; scroll_to: void | string; type: void | string; key: void | string; hold_key: void | string; form_input: void | string; read_page: string; find: string; get_page_text: string; wait: void | string; file_upload: void | string; read_console: string; read_network: string; javascript_exec: string; new_tab: BetaBrowserStateTabEntry; list_tabs: BetaBrowserStateTabEntry[]; switch_tab: BetaBrowserStateTabEntry; close_tab: void; } // The result map must cover exactly the member names the API declares: a member in one without the // other fails here. type AssertSameKeys<A, B> = [keyof A] extends [B] ? [B] extends [keyof A] ? true : never : never; const _browserKeys: AssertSameKeys<BrowserMemberResults, BetaBrowserMemberName> = true; void _browserKeys; /** The type-level registry: what the model sends and what the implementation returns, per member. */ export type BrowserMembers = { [K in BetaBrowserMemberName]: { input: BrowserMemberInputs[K]; result: BrowserMemberResults[K] }; }; /** One member of the browser toolset. */ export type Member = ToolsetMember<BetaBrowserMemberName, BrowserResultKind>; // Hand-kept: result kinds, confirmation templates and default-disabled flags. Everything else about a // member comes from the API types. A member missing here (or an extra one) fails to compile. const ROWS = { navigate: ['navigate', null, true], screenshot: ['screenshot', null, true], zoom: ['screenshot', null, true], left_click: ['none', 'Clicked.', true], right_click: ['none', 'Right-clicked.', true], middle_click: ['none', 'Middle-clicked.', true], double_click: ['none', 'Double-clicked.', true], triple_click: ['none', 'Triple-clicked.', true], hover: ['none', 'Hovered.', true], left_click_drag: ['none', 'Dragged.', true], left_mouse_down: ['none', 'Mouse button pressed.', true], left_mouse_up: ['none', 'Mouse button released.', true], mouse_move: ['none', 'Moved the mouse.', true], scroll: ['none', 'Scrolled {scroll_direction}.', true], scroll_to: ['none', 'Scrolled to {ref}.', true], type: ['none', 'Typed.', true], key: ['none', 'Pressed {text}.', true], hold_key: ['none', 'Held {text} for {duration}s.', true], form_input: ['none', 'Set the value of {ref}.', true], read_page: ['text', null, true], find: ['text', null, true], get_page_text: ['text', null, true], wait: ['none', 'Waited {duration}s.', true], file_upload: ['none', 'Uploaded.', false], read_console: ['text', null, false], read_network: ['text', null, false], javascript_exec: ['text', null, false], new_tab: ['tab', null, true], list_tabs: ['tabs', null, true], switch_tab: ['tab', null, true], close_tab: ['none', null, true], } as const satisfies { readonly [K in BetaBrowserMemberName]: MemberRow }; /** The result kinds the rows above use. */ export type BrowserResultKind = (typeof ROWS)[BetaBrowserMemberName][0]; /** The declared result (`BrowserMemberResults`) of every member the rows above give result kind `R`. */ export type KindResult<R extends ResultKind> = KindResultOf< BetaBrowserMemberName, typeof ROWS, BrowserMemberResults, R >; /** A browser member's result with its result kind, which says how to read it. */ export type TypedResult<R extends BrowserResultKind = BrowserResultKind> = TypedResultOf< BetaBrowserMemberName, typeof ROWS, BrowserMemberResults, R >; /** * `value` typed as what a member of result kind `kind` returns: the one place the driver's declared result type is * taken at its word. */ export function typedResult<R extends BrowserResultKind>(kind: R, value: KindResult<R>): TypedResult<R> { return { kind, value }; } type RowName = keyof typeof ROWS; /** * Every browser member name, in the order of `ROWS`. Built from the table, not imported from the generated runtime list, * so the toolset modules do not load `resources/beta/messages/messages` (the runner loads them from there). */ export const BROWSER_MEMBER_NAMES: ReadonlyArray<RowName> = /* @__PURE__ */ Object.keys(ROWS) as RowName[]; /** Every browser member, in the order of `ROWS`. */ export const BROWSER_MEMBERS: ReadonlyMap<BetaBrowserMemberName, Member> = /* @__PURE__ */ buildMembers( BROWSER_MEMBER_NAMES, ROWS, ); /** Members the API withholds unless `configs` enables them. */ export const BROWSER_DEFAULT_DISABLED_MEMBERS: ReadonlySet<string> = /* @__PURE__ */ new Set( /* @__PURE__ */ [...BROWSER_MEMBERS.values()].filter((m) => !m.enabledByDefault).map((m) => m.name), ); /** The browser registry as the shared toolset core reads it. */ export const BROWSER_REGISTRY: Registry<BetaBrowserMemberName, Member> = { family: FAMILY, names: BROWSER_MEMBER_NAMES, members: BROWSER_MEMBERS, defaultDisabled: BROWSER_DEFAULT_DISABLED_MEMBERS, }; export function getMember(name: string): Member | undefined { return memberOf(BROWSER_REGISTRY, name); } /** * The tab members, which render no content of their own, so their non-error result is exactly one `browser_state` * block, and a line meant for the model (a refused navigation) waits for the next result that can hold it. Listed by * name rather than derived from `ROWS`, so a tab member that gains a confirmation line stays in the set. */ export const STATE_ONLY_MEMBERS: ReadonlySet<BetaBrowserMemberName> = /* @__PURE__ */ new Set([ 'new_tab', 'list_tabs', 'switch_tab', 'close_tab', ]); /** The wire entry's `configs`, read as `{ [member]: { enabled? } }`. */ export type MemberConfigs = BetaBrowserToolsetConfigs | undefined; /** Whether `configs` leave the browser member `name` enabled (`isEnabled` over the browser defaults). */ export function memberEnabled(name: string, configs: MemberConfigs): boolean { return isEnabled(name, configs, BROWSER_DEFAULT_DISABLED_MEMBERS); }