From 0f962ef99b82207239e5dacb399be3cabce0ece3 Mon Sep 17 00:00:00 2001 From: amankumarsingh77 Date: Sat, 2 May 2026 16:05:09 +0530 Subject: feat(domains): migrate browser_click to defineBrowserTool — vertical slice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements Task 9: adds src/domains/click.ts with the new architecture, updates registry.ts to register clickTool, removes the legacy browser_click block from tools.ts, and wires BrowserClient alongside BrowserDaemon in index.ts so both systems coexist during migration. Also fixes AnyBrowserToolDefinition variance: uses never-arg renderCall in the erased type and a boundary cast in registerBrowserTool so concrete tool definitions (with specific Static args) are assignable to the registry array without widening. --- src/util/tool.ts | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'src/util') diff --git a/src/util/tool.ts b/src/util/tool.ts index fc1c5e2..d269ba2 100644 --- a/src/util/tool.ts +++ b/src/util/tool.ts @@ -56,9 +56,14 @@ export type BrowserToolDefinition = { readonly ensureAlive?: boolean; }; -// Type-erased entry for arrays. AnyBrowserToolDefinition lets the registry -// hold a heterogeneous list without losing the per-tool Static binding. -export type AnyBrowserToolDefinition = BrowserToolDefinition; +// Type-erased entry for the registry's heterogeneous TOOLS array. +// renderCall accepts `never` so any concrete args type satisfies it via +// function-parameter contravariance (a handler that takes SpecificT is +// assignable to one that takes never, because never is a subtype of everything). +export type AnyBrowserToolDefinition = Omit, "renderCall" | "renderResult"> & { + readonly renderCall?: (args: never, theme: Theme) => Component; + readonly renderResult?: (result: AgentToolResult, expanded: boolean, theme: Theme) => Component; +}; export const defineBrowserTool = ( def: BrowserToolDefinition, @@ -94,11 +99,15 @@ const toToolResult = ( }; }; -export const registerBrowserTool = ( +export const registerBrowserTool = ( pi: ExtensionAPI, client: BrowserClient, - def: BrowserToolDefinition, + def: AnyBrowserToolDefinition, ): void => { + // AnyBrowserToolDefinition is type-erased; we recover the schema-typed surface + // by casting def to the generic form. The cast is safe because defineBrowserTool + // ensures the handler, parameters, and renderCall were all created with the same S. + type S = TSchema; const td: ToolDefinition = { name: def.name, label: def.label, @@ -110,7 +119,9 @@ export const registerBrowserTool = ( ? { renderCall: (args: Static, theme: Theme, _ctx: Parameters["renderCall"]>>[2]) => // Non-null assertion safe: this branch only runs when renderCall was provided. - def.renderCall!(args, theme), + // Cast from never->Component to unknown->Component: safe because at runtime + // args is always the concrete Static value the tool was defined with. + (def.renderCall as (args: unknown, theme: Theme) => Component)(args, theme), } : {}), ...(def.renderResult -- cgit v1.2.3