import type { CredentialRequirement } from "../contracts/credentialTypes"; import type { TypeToken } from "../di"; import type { AgentCanvasPresentation, ToolConfig, ToolExecuteArgs, ZodSchemaAny } from "./AiHost"; import { ZodError, type input as ZodInput, type output as ZodOutput } from "zod"; import { CallableToolKindToken } from "./CallableToolKindToken"; export type CallableToolExecuteHandler = ( args: ToolExecuteArgs, ZodInput>, ) => Promise> | ZodOutput; export type CallableToolConfigOptions< TInputSchema extends ZodSchemaAny, TOutputSchema extends ZodSchemaAny, > = Readonly<{ name: string; description?: string; presentation?: AgentCanvasPresentation; inputSchema: TInputSchema; outputSchema: TOutputSchema; credentialRequirements?: ReadonlyArray; execute: CallableToolExecuteHandler; }>; export class CallableToolConfig< TInputSchema extends ZodSchemaAny, TOutputSchema extends ZodSchemaAny, > implements ToolConfig { readonly type: TypeToken = CallableToolKindToken; readonly toolKind = "callable" as const; readonly description?: string; readonly presentation?: AgentCanvasPresentation; private readonly inputSchemaValue: TInputSchema; private readonly outputSchemaValue: TOutputSchema; private readonly credentialRequirementsValue?: ReadonlyArray; private readonly executeHandler: CallableToolExecuteHandler; constructor( public readonly name: string, options: CallableToolConfigOptions, ) { this.description = options.description; this.presentation = options.presentation; this.inputSchemaValue = options.inputSchema; this.outputSchemaValue = options.outputSchema; this.credentialRequirementsValue = options.credentialRequirements; this.executeHandler = options.execute; } getCredentialRequirements(): ReadonlyArray { return this.credentialRequirementsValue ?? []; } getInputSchema(): TInputSchema { return this.inputSchemaValue; } getOutputSchema(): TOutputSchema { return this.outputSchemaValue; } async executeTool( args: ToolExecuteArgs, ZodInput>, ): Promise> { const parsedInput = this.parseInput(args.input); const raw = await Promise.resolve( this.executeHandler({ ...args, config: this, input: parsedInput, }), ); return this.parseOutput(raw); } private parseInput(input: unknown): ZodInput { try { return this.inputSchemaValue.parse(input) as ZodInput; } catch (error) { throw this.decorateValidationError(error, "input"); } } private parseOutput(output: unknown): ZodOutput { try { return this.outputSchemaValue.parse(output) as ZodOutput; } catch (error) { throw this.decorateValidationError(error, "output"); } } private decorateValidationError(error: unknown, stage: "input" | "output"): Error { if (error instanceof ZodError) { (error as ZodError & { codemationToolValidationStage?: "input" | "output" }).codemationToolValidationStage = stage; return error; } return error instanceof Error ? error : new Error(String(error)); } }