/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import { FunctionDeclaration, PartListUnion, Schema } from '@google/genai'; import { Summarizer, defaultSummarizer } from '../utils/summarizer.js'; /** * Interface representing the base Tool functionality */ export interface Tool< TParams = unknown, TResult extends ToolResult = ToolResult, > { /** * The internal name of the tool (used for API calls) */ name: string; /** * The user-friendly display name of the tool */ displayName: string; /** * Description of what the tool does */ description: string; /** * Function declaration schema from @google/genai */ schema: FunctionDeclaration; /** * Whether the tool's output should be rendered as markdown */ isOutputMarkdown: boolean; /** * Whether the tool supports live (streaming) output */ canUpdateOutput: boolean; /** * A function that summarizes the result of the tool execution. */ summarizer?: Summarizer; /** * Whether the tool's display output should be summarized */ shouldSummarizeDisplay?: boolean; /** * Validates the parameters for the tool * Should be called from both `shouldConfirmExecute` and `execute` * `shouldConfirmExecute` should return false immediately if invalid * @param params Parameters to validate * @returns An error message string if invalid, null otherwise */ validateToolParams(params: TParams): string | null; /** * Gets a pre-execution description of the tool operation * @param params Parameters for the tool execution * @returns A markdown string describing what the tool will do * Optional for backward compatibility */ getDescription(params: TParams): string; /** * Determines if the tool should prompt for confirmation before execution * @param params Parameters for the tool execution * @returns Whether execute should be confirmed. */ shouldConfirmExecute( params: TParams, abortSignal: AbortSignal, ): Promise; /** * Executes the tool with the given parameters * @param params Parameters for the tool execution * @returns Result of the tool execution */ execute( params: TParams, signal: AbortSignal, updateOutput?: (output: string) => void, ): Promise; } /** * Base implementation for tools with common functionality */ export abstract class BaseTool< TParams = unknown, TResult extends ToolResult = ToolResult, > implements Tool { /** * Creates a new instance of BaseTool * @param name Internal name of the tool (used for API calls) * @param displayName User-friendly display name of the tool * @param description Description of what the tool does * @param isOutputMarkdown Whether the tool's output should be rendered as markdown * @param canUpdateOutput Whether the tool supports live (streaming) output * @param parameterSchema JSON Schema defining the parameters * @param summarizer Function to summarize the tool's output * @param shouldSummarizeDisplay Whether the tool's display output should be summarized */ constructor( readonly name: string, readonly displayName: string, readonly description: string, readonly parameterSchema: Schema, readonly isOutputMarkdown: boolean = true, readonly canUpdateOutput: boolean = false, readonly summarizer: Summarizer = defaultSummarizer, readonly shouldSummarizeDisplay: boolean = false, ) {} /** * Function declaration schema computed from name, description, and parameterSchema */ get schema(): FunctionDeclaration { return { name: this.name, description: this.description, parameters: this.parameterSchema, }; } /** * Validates the parameters for the tool * This is a placeholder implementation and should be overridden * Should be called from both `shouldConfirmExecute` and `execute` * `shouldConfirmExecute` should return false immediately if invalid * @param params Parameters to validate * @returns An error message string if invalid, null otherwise */ // eslint-disable-next-line @typescript-eslint/no-unused-vars validateToolParams(params: TParams): string | null { // Implementation would typically use a JSON Schema validator // This is a placeholder that should be implemented by derived classes return null; } /** * Gets a pre-execution description of the tool operation * Default implementation that should be overridden by derived classes * @param params Parameters for the tool execution * @returns A markdown string describing what the tool will do */ getDescription(params: TParams): string { return JSON.stringify(params); } /** * Determines if the tool should prompt for confirmation before execution * @param params Parameters for the tool execution * @returns Whether or not execute should be confirmed by the user. */ shouldConfirmExecute( // eslint-disable-next-line @typescript-eslint/no-unused-vars params: TParams, // eslint-disable-next-line @typescript-eslint/no-unused-vars abortSignal: AbortSignal, ): Promise { return Promise.resolve(false); } /** * Abstract method to execute the tool with the given parameters * Must be implemented by derived classes * @param params Parameters for the tool execution * @param signal AbortSignal for tool cancellation * @returns Result of the tool execution */ abstract execute( params: TParams, signal: AbortSignal, updateOutput?: (output: string) => void, ): Promise; } export interface ToolResult { /** * A short, one-line summary of the tool's action and result. * e.g., "Read 5 files", "Wrote 256 bytes to foo.txt" */ summary?: string; /** * Content meant to be included in LLM history. * This should represent the factual outcome of the tool execution. */ llmContent: PartListUnion; /** * Markdown string for user display. * This provides a user-friendly summary or visualization of the result. * NOTE: This might also be considered UI-specific and could potentially be * removed or modified in a further refactor if the server becomes purely API-driven. * For now, we keep it as the core logic in ReadFileTool currently produces it. */ returnDisplay: ToolResultDisplay; } export type ToolResultDisplay = string | FileDiff; export interface FileDiff { fileDiff: string; fileName: string; } export interface ToolEditConfirmationDetails { type: 'edit'; title: string; onConfirm: ( outcome: ToolConfirmationOutcome, payload?: ToolConfirmationPayload, ) => Promise; fileName: string; fileDiff: string; isModifying?: boolean; } export interface ToolConfirmationPayload { // used to override `modifiedProposedContent` for modifiable tools in the // inline modify flow newContent: string; } export interface ToolExecuteConfirmationDetails { type: 'exec'; title: string; onConfirm: (outcome: ToolConfirmationOutcome) => Promise; command: string; rootCommand: string; } export interface ToolMcpConfirmationDetails { type: 'mcp'; title: string; serverName: string; toolName: string; toolDisplayName: string; onConfirm: (outcome: ToolConfirmationOutcome) => Promise; } export interface ToolInfoConfirmationDetails { type: 'info'; title: string; onConfirm: (outcome: ToolConfirmationOutcome) => Promise; prompt: string; urls?: string[]; } export type ToolCallConfirmationDetails = | ToolEditConfirmationDetails | ToolExecuteConfirmationDetails | ToolMcpConfirmationDetails | ToolInfoConfirmationDetails; export enum ToolConfirmationOutcome { ProceedOnce = 'proceed_once', ProceedAlways = 'proceed_always', ProceedAlwaysServer = 'proceed_always_server', ProceedAlwaysTool = 'proceed_always_tool', ModifyWithEditor = 'modify_with_editor', Cancel = 'cancel', }