@anthropic-ai/sdk
Version:
The official TypeScript library for the Anthropic API
207 lines (187 loc) • 7.93 kB
text/typescript
/**
* 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);
}