@continue-reasoning/agent
Version:
A platform-agnostic AI agent framework for building autonomous AI agents with tool execution capabilities
349 lines • 12.9 kB
JavaScript
/**
* @fileoverview Base Tool Implementation for Agent Framework
*
* This module provides base classes and utilities for creating tools that can be
* executed by AI agents. It references the core package's tool implementation
* but uses our own framework-specific types and interfaces.
*
* Key Features:
* - Abstract BaseTool class for common functionality
* - Type-safe tool parameter validation
* - Support for confirmation workflows
* - Output streaming capabilities
* - Framework-agnostic design
*/
/**
* Base implementation for tools with common functionality
*
* This abstract class provides a foundation for creating tools that can be
* executed by AI agents. It handles common patterns like parameter validation,
* confirmation workflows, and output formatting.
*
* The design is based on the core package's BaseTool but uses our own types
* and interfaces for better integration with the agent framework.
*
* @example
* ```typescript
* class CalculatorTool extends BaseTool<{ expression: string }, ToolResult> {
* constructor() {
* super(
* 'calculator',
* 'Calculator',
* 'Perform basic mathematical calculations',
* {
* type: Type.OBJECT,
* properties: {
* expression: { type: Type.STRING, description: 'Math expression' }
* },
* required: ['expression']
* }
* );
* }
*
* async execute(params: { expression: string }) {
* const result = eval(params.expression);
* return {
* llmContent: `${params.expression} = ${result}`,
* returnDisplay: `🔢 ${params.expression} = ${result}`
* };
* }
* }
* ```
*/
export class BaseTool {
name;
displayName;
description;
parameterSchema;
isOutputMarkdown;
canUpdateOutput;
/**
* 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 parameterSchema - JSON Schema defining the parameters
* @param isOutputMarkdown - Whether the tool's output should be rendered as markdown
* @param canUpdateOutput - Whether the tool supports live (streaming) output
*/
constructor(name, displayName, description, parameterSchema, isOutputMarkdown = true, canUpdateOutput = false) {
this.name = name;
this.displayName = displayName;
this.description = description;
this.parameterSchema = parameterSchema;
this.isOutputMarkdown = isOutputMarkdown;
this.canUpdateOutput = canUpdateOutput;
}
/**
* Tool declaration schema computed from name, description, and parameterSchema
*
* This property generates the schema format expected by the tool interface.
* It combines the tool's name, description, and parameter schema into a
* single ToolDeclaration.
*/
get schema() {
return {
name: this.name,
description: this.description,
parameters: this.parameterSchema,
};
}
/**
* Validates the parameters for the tool
*
* This is a default implementation that should be overridden by derived classes
* to provide specific validation logic. The method should be called from both
* `shouldConfirmExecute` and `execute` methods.
*
* @param params - Parameters to validate
* @returns An error message string if invalid, null if valid
*/
validateToolParams(params) {
// Default implementation - derived classes should override this
// to provide specific validation logic
if (!params) {
return 'Parameters are required';
}
if (typeof params !== 'object') {
return 'Parameters must be an object';
}
return null;
}
/**
* Gets a pre-execution description of the tool operation
*
* This method provides a human-readable description of what the tool
* will do with the given parameters. It's used for confirmation dialogs
* and logging purposes.
*
* @param params - Parameters for the tool execution
* @returns A string describing what the tool will do
*/
getDescription(params) {
return `Execute ${this.displayName} with: ${JSON.stringify(params)}`;
}
/**
* Determines if the tool should prompt for confirmation before execution
*
* This method allows tools to request user confirmation before executing
* potentially dangerous or significant operations. The default implementation
* returns false (no confirmation needed).
*
* @param params - Parameters for the tool execution
* @param abortSignal - Signal for canceling the confirmation check
* @returns Confirmation details if confirmation is needed, false otherwise
*/
async shouldConfirmExecute(params, _abortSignal) {
// Check if parameters are valid first
const validationError = this.validateToolParams(params);
if (validationError) {
return false; // Don't confirm if parameters are invalid
}
// Default implementation - no confirmation needed
return false;
}
/**
* Helper method to create a basic tool result
*
* This utility method helps create standardized tool results with
* consistent formatting. It's useful for simple tools that don't
* need complex result structures.
*
* @param content - The main content for LLM consumption
* @param display - The display content for users
* @param summary - Optional summary of the operation
* @returns A properly formatted ToolResult
*/
createResult(content, display, summary) {
const result = {
llmContent: content,
returnDisplay: display,
};
if (summary) {
result.summary = summary;
}
return result;
}
/**
* Helper method to create an error result
*
* This utility method creates a standardized error result when
* tool execution fails. It ensures consistent error formatting
* across all tools.
*
* @param error - The error that occurred
* @param context - Additional context about the error
* @returns A ToolResult representing the error
*/
createErrorResult(error, context) {
const errorMessage = error instanceof Error ? error.message : error;
const fullMessage = context ? `${context}: ${errorMessage}` : errorMessage;
return {
llmContent: `Error: ${fullMessage}`,
returnDisplay: `❌ Error: ${fullMessage}`,
summary: `Failed: ${errorMessage}`,
};
}
/**
* Helper method to create a file diff result
*
* This utility method creates a tool result specifically for file
* modifications, using the FileDiff format for display.
*
* @param fileName - Name of the file that was modified
* @param fileDiff - The diff content showing changes
* @param content - Content for LLM consumption
* @param summary - Optional summary of the changes
* @returns A ToolResult with file diff display
*/
createFileDiffResult(fileName, fileDiff, content, summary) {
const diffDisplay = {
fileName,
fileDiff,
};
return {
llmContent: content,
returnDisplay: diffDisplay,
summary: summary || `Modified ${fileName}`,
};
}
/**
* Helper method to validate required parameters
*
* This utility method checks if required parameters are present
* and non-empty. It's commonly used in validateToolParams implementations.
*
* @param params - Parameters to validate
* @param requiredFields - Array of required field names
* @returns Error message if validation fails, null if valid
*/
validateRequiredParams(params, requiredFields) {
for (const field of requiredFields) {
if (!(field in params)) {
return `Missing required parameter: ${field}`;
}
const value = params[field];
if (value === null || value === undefined || value === '') {
return `Parameter '${field}' cannot be empty`;
}
}
return null;
}
/**
* Helper method to validate parameter types
*
* This utility method checks if parameters have the correct types.
* It's useful for implementing type validation in validateToolParams.
*
* @param params - Parameters to validate
* @param typeMap - Map of field names to expected types
* @returns Error message if validation fails, null if valid
*/
validateParameterTypes(params, typeMap) {
for (const [field, expectedType] of Object.entries(typeMap)) {
if (field in params) {
const value = params[field];
const actualType = typeof value;
if (actualType !== expectedType) {
return `Parameter '${field}' must be of type ${expectedType}, got ${actualType}`;
}
}
}
return null;
}
/**
* Helper method to format operation progress
*
* This utility method creates formatted progress messages for
* streaming output updates. It provides consistent formatting
* across all tools.
*
* @param operation - Name of the operation being performed
* @param progress - Current progress information
* @param emoji - Optional emoji to display
* @returns Formatted progress message
*/
formatProgress(operation, progress, emoji) {
const prefix = emoji ? `${emoji} ` : '';
return `${prefix}${operation}: ${progress}`;
}
/**
* Helper method to handle abort signals
*
* This utility method provides a consistent way to handle
* cancellation signals in tool execution. It throws an
* appropriate error if the operation is cancelled.
*
* @param signal - AbortSignal to check
* @param operation - Name of the operation being checked
* @throws Error if the operation is cancelled
*/
checkAbortSignal(signal, operation) {
if (signal.aborted) {
const message = operation
? `${operation} was cancelled`
: 'Operation was cancelled';
throw new Error(message);
}
}
}
/**
* Simple tool implementation for tools that don't need complex logic
*
* This class provides a concrete implementation of BaseTool for simple
* tools that just need to execute a function and return a result.
* It's useful for prototyping and simple operations.
*
* @example
* ```typescript
* const echoTool = new SimpleTool(
* 'echo',
* 'Echo',
* 'Echo back the input',
* { type: Type.OBJECT, properties: { text: { type: Type.STRING } } },
* async (params) => ({ llmContent: params.text, returnDisplay: params.text })
* );
* ```
*/
export class SimpleTool extends BaseTool {
executor;
/**
* Creates a new SimpleTool instance
*
* @param name - Internal name of the tool
* @param displayName - User-friendly display name
* @param description - Description of what the tool does
* @param parameterSchema - JSON Schema for parameters
* @param executor - Function to execute the tool
* @param isOutputMarkdown - Whether output is markdown
* @param canUpdateOutput - Whether tool supports streaming output
*/
constructor(name, displayName, description, parameterSchema, executor, isOutputMarkdown = true, canUpdateOutput = false) {
super(name, displayName, description, parameterSchema, isOutputMarkdown, canUpdateOutput);
this.executor = executor;
}
/**
* Executes the tool using the provided executor function
*
* @param params - Parameters for the tool execution
* @param signal - AbortSignal for cancellation
* @param updateOutput - Optional callback for streaming output
* @returns Promise resolving to the tool execution result
*/
async execute(params, signal, updateOutput) {
// Validate parameters before execution
const validationError = this.validateToolParams(params);
if (validationError) {
return this.createErrorResult(validationError);
}
try {
this.checkAbortSignal(signal, 'Tool execution');
return await this.executor(params, signal, updateOutput);
}
catch (error) {
return this.createErrorResult(error instanceof Error ? error : new Error(String(error)));
}
}
}
//# sourceMappingURL=baseTool.js.map