UNPKG

@utcp/sdk

Version:
1,289 lines (1,263 loc) 69.4 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { ApiKeyAuthSerializer: () => ApiKeyAuthSerializer, AuthSchema: () => AuthSchema, AuthSerializer: () => AuthSerializer, BasicAuthSerializer: () => BasicAuthSerializer, CallTemplateSchema: () => CallTemplateSchema, CallTemplateSerializer: () => CallTemplateSerializer, CommunicationProtocol: () => CommunicationProtocol, ConcurrentToolRepositoryConfigSerializer: () => ConcurrentToolRepositoryConfigSerializer, ConcurrentToolRepositorySchema: () => ConcurrentToolRepositorySchema, DefaultVariableSubstitutor: () => DefaultVariableSubstitutor, FilterDictPostProcessor: () => FilterDictPostProcessor, FilterDictPostProcessorSerializer: () => FilterDictPostProcessorSerializer, InMemConcurrentToolRepository: () => InMemConcurrentToolRepository, InMemConcurrentToolRepositorySerializer: () => InMemConcurrentToolRepositorySerializer, JsonSchemaSchema: () => JsonSchemaSchema, JsonSchemaSerializer: () => JsonSchemaSerializer, JsonTypeSchema: () => JsonTypeSchema, LimitStringsPostProcessor: () => LimitStringsPostProcessor, LimitStringsPostProcessorSerializer: () => LimitStringsPostProcessorSerializer, OAuth2AuthSerializer: () => OAuth2AuthSerializer, Serializer: () => Serializer, TagSearchStrategy: () => TagSearchStrategy, TagSearchStrategyConfigSerializer: () => TagSearchStrategyConfigSerializer, ToolPostProcessorConfigSerializer: () => ToolPostProcessorConfigSerializer, ToolPostProcessorSchema: () => ToolPostProcessorSchema, ToolSchema: () => ToolSchema, ToolSearchStrategyConfigSerializer: () => ToolSearchStrategyConfigSerializer, ToolSearchStrategySchema: () => ToolSearchStrategySchema, ToolSerializer: () => ToolSerializer, UTCP_PACKAGE_VERSION: () => UTCP_PACKAGE_VERSION, UtcpClient: () => UtcpClient, UtcpClientConfigSchema: () => UtcpClientConfigSchema, UtcpClientConfigSerializer: () => UtcpClientConfigSerializer, UtcpManualSchema: () => UtcpManualSchema, UtcpManualSerializer: () => UtcpManualSerializer, VariableLoaderSchema: () => VariableLoaderSchema, VariableLoaderSerializer: () => VariableLoaderSerializer, ensureCorePluginsInitialized: () => ensureCorePluginsInitialized, setPluginInitializer: () => setPluginInitializer }); module.exports = __toCommonJS(index_exports); // src/data/call_template.ts var import_zod = require("zod"); // src/interfaces/serializer.ts var ensurePluginsInitialized = null; function setPluginInitializer(fn) { ensurePluginsInitialized = fn; } var Serializer = class { constructor() { if (ensurePluginsInitialized) { ensurePluginsInitialized(); } } copy(obj) { return this.validateDict(this.toDict(obj)); } }; // src/data/call_template.ts var CallTemplateSerializer = class _CallTemplateSerializer extends Serializer { static serializers = {}; // No need for the whole plugin registry. Plugins just need to call this to register a new call template type static registerCallTemplate(callTemplateType, serializer, override = false) { if (!override && _CallTemplateSerializer.serializers[callTemplateType]) { return false; } _CallTemplateSerializer.serializers[callTemplateType] = serializer; return true; } toDict(obj) { const serializer = _CallTemplateSerializer.serializers[obj.call_template_type]; if (!serializer) { throw new Error(`No serializer found for call_template_type: ${obj.call_template_type}`); } return serializer.toDict(obj); } validateDict(obj) { const serializer = _CallTemplateSerializer.serializers[obj.call_template_type]; if (!serializer) { throw new Error(`Invalid call_template_type: ${obj.call_template_type}`); } return serializer.validateDict(obj); } }; var CallTemplateSchema = import_zod.z.custom((obj) => { try { const validated = new CallTemplateSerializer().validateDict(obj); return validated; } catch (e) { return false; } }, { message: "Invalid CallTemplate object" }); // src/data/utcp_manual.ts var import_zod3 = require("zod"); // src/data/tool.ts var import_zod2 = require("zod"); var JsonTypeSchema = import_zod2.z.lazy(() => import_zod2.z.union([ import_zod2.z.string(), import_zod2.z.number(), import_zod2.z.boolean(), import_zod2.z.null(), import_zod2.z.record(import_zod2.z.string(), JsonTypeSchema), import_zod2.z.array(JsonTypeSchema) ])); var JsonSchemaSchema = import_zod2.z.lazy(() => import_zod2.z.object({ $schema: import_zod2.z.string().optional().describe("JSON Schema version URI."), $id: import_zod2.z.string().optional().describe("A URI for the schema."), title: import_zod2.z.string().optional().describe("A short explanation about the purpose of the data described by this schema."), description: import_zod2.z.string().optional().describe("A more lengthy explanation about the purpose of the data described by this schema."), type: import_zod2.z.union([ import_zod2.z.literal("string"), import_zod2.z.literal("number"), import_zod2.z.literal("integer"), import_zod2.z.literal("boolean"), import_zod2.z.literal("object"), import_zod2.z.literal("array"), import_zod2.z.literal("null"), import_zod2.z.array(import_zod2.z.string()) ]).optional(), properties: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.lazy(() => JsonSchemaSchema)).optional(), items: import_zod2.z.union([import_zod2.z.lazy(() => JsonSchemaSchema), import_zod2.z.array(import_zod2.z.lazy(() => JsonSchemaSchema))]).optional(), required: import_zod2.z.array(import_zod2.z.string()).optional(), enum: import_zod2.z.array(JsonTypeSchema).optional(), const: JsonTypeSchema.optional(), default: JsonTypeSchema.optional(), format: import_zod2.z.string().optional(), additionalProperties: import_zod2.z.union([import_zod2.z.boolean(), import_zod2.z.lazy(() => JsonSchemaSchema)]).optional(), pattern: import_zod2.z.string().optional(), minimum: import_zod2.z.number().optional(), maximum: import_zod2.z.number().optional(), minLength: import_zod2.z.number().optional(), maxLength: import_zod2.z.number().optional() }).catchall(import_zod2.z.unknown())); var ToolSchema = import_zod2.z.object({ name: import_zod2.z.string().describe('Unique identifier for the tool, typically in format "manual_name.tool_name".'), description: import_zod2.z.string().default("").describe("Human-readable description of what the tool does."), inputs: JsonSchemaSchema.default({}).describe("JSON Schema defining the tool's input parameters."), outputs: JsonSchemaSchema.default({}).describe("JSON Schema defining the tool's return value structure."), tags: import_zod2.z.array(import_zod2.z.string()).default([]).describe("List of tags for categorization and search."), average_response_size: import_zod2.z.number().optional().describe("Optional hint about typical response size in bytes."), tool_call_template: CallTemplateSchema.describe("CallTemplate configuration for accessing this tool.") }).strict(); var JsonSchemaSerializer = class extends Serializer { toDict(obj) { return { ...obj }; } validateDict(obj) { return JsonSchemaSchema.parse(obj); } }; var ToolSerializer = class extends Serializer { toDict(obj) { return { name: obj.name, description: obj.description, inputs: obj.inputs, outputs: obj.outputs, tags: obj.tags, ...obj.average_response_size !== void 0 && { average_response_size: obj.average_response_size }, tool_call_template: obj.tool_call_template }; } validateDict(obj) { return ToolSchema.parse(obj); } }; // src/version.ts var _VERSION = "1.1.0"; var LIB_VERSION = _VERSION === "1.1.0" ? "1.0.0" : _VERSION; // src/data/utcp_manual.ts var UTCP_PACKAGE_VERSION = LIB_VERSION; var UtcpManualSchema = import_zod3.z.object({ // Use .optional() to allow missing in input, then .default() to satisfy the interface. utcp_version: import_zod3.z.string().optional().default(UTCP_PACKAGE_VERSION).describe("UTCP protocol version supported by the provider."), manual_version: import_zod3.z.string().optional().default("1.0.0").describe("Version of this specific manual."), tools: import_zod3.z.array(ToolSchema).describe("List of available tools with their complete configurations.") }).strict(); var UtcpManualSerializer = class extends Serializer { toDict(obj) { return { utcp_version: obj.utcp_version, manual_version: obj.manual_version, tools: obj.tools }; } validateDict(obj) { return UtcpManualSchema.parse(obj); } }; // src/interfaces/communication_protocol.ts var CommunicationProtocol = class { /** * Mapping of communication protocol types to their respective implementations. */ static communicationProtocols = {}; /** * Closes any persistent connections or resources held by the communication protocol. * This is a cleanup method that should be called when the client is shut down. */ async close() { } }; // src/client/utcp_client_config.ts var import_zod16 = require("zod"); // src/data/auth.ts var import_zod4 = require("zod"); var AuthSerializer = class _AuthSerializer extends Serializer { static serializers = {}; // No need for the whole plugin registry. Plugins just need to call this to register a new auth type static registerAuth(authType, serializer, override = false) { if (!override && _AuthSerializer.serializers[authType]) { return false; } _AuthSerializer.serializers[authType] = serializer; return true; } toDict(obj) { const serializer = _AuthSerializer.serializers[obj.auth_type]; if (!serializer) { throw new Error(`No serializer found for auth_type: ${obj.auth_type}`); } return serializer.toDict(obj); } validateDict(obj) { const serializer = _AuthSerializer.serializers[obj.auth_type]; if (!serializer) { throw new Error(`Invalid auth_type: ${obj.auth_type}`); } return serializer.validateDict(obj); } }; var AuthSchema = import_zod4.z.custom((obj) => { try { const validated = new AuthSerializer().validateDict(obj); return validated; } catch (e) { return false; } }, { message: "Invalid Auth object" }); // src/data/auth_implementations/api_key_auth.ts var import_zod5 = require("zod"); var ApiKeyAuthSerializer = class extends Serializer { toDict(obj) { return { ...obj }; } validateDict(obj) { return ApiKeyAuthSchema.parse(obj); } }; var ApiKeyAuthSchema = import_zod5.z.object({ auth_type: import_zod5.z.literal("api_key"), api_key: import_zod5.z.string(), var_name: import_zod5.z.string().default("X-Api-Key"), location: import_zod5.z.enum(["header", "query", "cookie"]).default("header") }); // src/data/auth_implementations/basic_auth.ts var import_zod6 = require("zod"); var BasicAuthSerializer = class extends Serializer { toDict(obj) { return { ...obj }; } validateDict(obj) { return BasicAuthSchema.parse(obj); } }; var BasicAuthSchema = import_zod6.z.object({ auth_type: import_zod6.z.literal("basic"), username: import_zod6.z.string().describe("The username for basic authentication. Recommended to use injected variables."), password: import_zod6.z.string().describe("The password for basic authentication. Recommended to use injected variables.") }).strict(); // src/data/auth_implementations/oauth2_auth.ts var import_zod7 = require("zod"); var OAuth2AuthSerializer = class extends Serializer { toDict(obj) { return { ...obj }; } validateDict(obj) { return OAuth2AuthSchema.parse(obj); } }; var OAuth2AuthSchema = import_zod7.z.object({ auth_type: import_zod7.z.literal("oauth2"), token_url: import_zod7.z.string().describe("The URL to fetch the OAuth2 access token from. Recommended to use injected variables."), client_id: import_zod7.z.string().describe("The OAuth2 client ID. Recommended to use injected variables."), client_secret: import_zod7.z.string().describe("The OAuth2 client secret. Recommended to use injected variables."), scope: import_zod7.z.string().optional().describe("Optional scope parameter to limit the access token's permissions.") }).strict(); // src/implementations/in_mem_concurrent_tool_repository.ts var import_zod8 = require("zod"); var InMemConcurrentToolRepository = class { tool_repository_type = "in_memory"; _config; // Store the config to return in toDict _toolsByName = /* @__PURE__ */ new Map(); _manuals = /* @__PURE__ */ new Map(); _manualCallTemplates = /* @__PURE__ */ new Map(); _writeMutex = new AsyncMutex(); /** * The constructor must accept the config type to match the factory signature, * even if the implementation does not use it. */ constructor(config = { tool_repository_type: "in_memory" }) { this._config = config; } /** * Converts the repository instance's configuration to a dictionary. */ toDict() { return this._config; } /** * Saves a manual's call template and its associated tools in the repository. * This operation replaces any existing manual with the same name. * @param manualCallTemplate The call template associated with the manual to save. * @param manual The complete UTCP Manual object to save. */ async saveManual(manualCallTemplate, manual) { const release = await this._writeMutex.acquire(); try { const manualName = manualCallTemplate.name; const oldManual = this._manuals.get(manualName); if (oldManual) { for (const tool of oldManual.tools) { this._toolsByName.delete(tool.name); } } this._manualCallTemplates.set(manualName, { ...manualCallTemplate }); this._manuals.set(manualName, { ...manual, tools: manual.tools.map((t) => ({ ...t })) }); for (const tool of manual.tools) { this._toolsByName.set(tool.name, { ...tool }); } } finally { release(); } } /** * Removes a manual and its tools from the repository. * @param manualName The name of the manual to remove. * @returns True if the manual was removed, False otherwise. */ async removeManual(manualName) { const release = await this._writeMutex.acquire(); try { const oldManual = this._manuals.get(manualName); if (!oldManual) { return false; } for (const tool of oldManual.tools) { this._toolsByName.delete(tool.name); } this._manuals.delete(manualName); this._manualCallTemplates.delete(manualName); return true; } finally { release(); } } /** * Removes a specific tool from the repository. * Note: This also attempts to remove the tool from any associated manual. * @param toolName The full namespaced name of the tool to remove. * @returns True if the tool was removed, False otherwise. */ async removeTool(toolName) { const release = await this._writeMutex.acquire(); try { const toolRemoved = this._toolsByName.delete(toolName); if (!toolRemoved) { return false; } const manualName = toolName.split(".")[0]; if (manualName) { const manual = this._manuals.get(manualName); if (manual) { manual.tools = manual.tools.filter((t) => t.name !== toolName); } } return true; } finally { release(); } } /** * Retrieves a tool by its full namespaced name. * @param toolName The full namespaced name of the tool to retrieve. * @returns The tool if found, otherwise undefined. */ async getTool(toolName) { const tool = this._toolsByName.get(toolName); return tool ? { ...tool } : void 0; } /** * Retrieves all tools from the repository. * @returns A list of all registered tools. */ async getTools() { return Array.from(this._toolsByName.values()).map((t) => ({ ...t })); } /** * Retrieves all tools associated with a specific manual. * @param manualName The name of the manual. * @returns A list of tools associated with the manual, or undefined if the manual is not found. */ async getToolsByManual(manualName) { const manual = this._manuals.get(manualName); return manual ? manual.tools.map((t) => ({ ...t })) : void 0; } /** * Retrieves a complete UTCP Manual object by its name. * @param manualName The name of the manual to retrieve. * @returns The manual if found, otherwise undefined. */ async getManual(manualName) { const manual = this._manuals.get(manualName); return manual ? { ...manual, tools: manual.tools.map((t) => ({ ...t })) } : void 0; } /** * Retrieves all registered manuals from the repository. * @returns A list of all registered UtcpManual objects. */ async getManuals() { return Array.from(this._manuals.values()).map((m) => ({ ...m, tools: m.tools.map((t) => ({ ...t })) })); } /** * Retrieves a manual's CallTemplate by its name. * @param manualCallTemplateName The name of the manual's CallTemplate to retrieve. * @returns The CallTemplate if found, otherwise undefined. */ async getManualCallTemplate(manualCallTemplateName) { const template = this._manualCallTemplates.get(manualCallTemplateName); return template ? { ...template } : void 0; } /** * Retrieves all registered manual CallTemplates from the repository. * @returns A list of all registered CallTemplateBase objects. */ async getManualCallTemplates() { return Array.from(this._manualCallTemplates.values()).map((t) => ({ ...t })); } }; var InMemConcurrentToolRepositorySerializer = class extends Serializer { toDict(obj) { return { tool_repository_type: obj.tool_repository_type }; } validateDict(data) { try { return new InMemConcurrentToolRepository(InMemConcurrentToolRepositoryConfigSchema.parse(data)); } catch (e) { if (e instanceof import_zod8.z.ZodError) { throw new Error(`Invalid configuration: ${e.message}`); } throw new Error("Unexpected error during validation"); } } }; var AsyncMutex = class { queue = []; locked = false; /** * Acquires the mutex. If the mutex is already locked, waits until it's released. * @returns A function to call to release the mutex. */ async acquire() { if (!this.locked) { this.locked = true; return this._release.bind(this); } else { return new Promise((resolve) => { this.queue.push(() => { this.locked = true; resolve(this._release.bind(this)); }); }); } } /** * Releases the mutex, allowing the next queued operation (if any) to proceed. */ _release() { this.locked = false; if (this.queue.length > 0) { const next = this.queue.shift(); if (next) { next(); } } } }; var InMemConcurrentToolRepositoryConfigSchema = import_zod8.z.object({ tool_repository_type: import_zod8.z.literal("in_memory") }).passthrough(); // src/interfaces/concurrent_tool_repository.ts var import_zod9 = __toESM(require("zod"), 1); var ConcurrentToolRepositoryConfigSerializer = class _ConcurrentToolRepositoryConfigSerializer extends Serializer { static implementations = {}; static default_strategy = "in_memory"; // No need for the whole plugin registry. Plugins just need to call this to register a new repository static registerRepository(type, serializer, override = false) { if (!override && _ConcurrentToolRepositoryConfigSerializer.implementations[type]) { return false; } _ConcurrentToolRepositoryConfigSerializer.implementations[type] = serializer; return true; } toDict(obj) { const serializer = _ConcurrentToolRepositoryConfigSerializer.implementations[obj.tool_repository_type]; if (!serializer) throw new Error(`No serializer for type: ${obj.tool_repository_type}`); return serializer.toDict(obj); } validateDict(data) { const serializer = _ConcurrentToolRepositoryConfigSerializer.implementations[data["tool_repository_type"]]; if (!serializer) throw new Error(`Invalid tool repository type: ${data["tool_repository_type"]}`); return serializer.validateDict(data); } }; var ConcurrentToolRepositorySchema = import_zod9.default.custom((obj) => { try { const validated = new ConcurrentToolRepositoryConfigSerializer().validateDict(obj); return validated; } catch (e) { return false; } }, { message: "Invalid ConcurrentToolRepository object" }); // src/implementations/tag_search_strategy.ts var import_zod10 = require("zod"); var TagSearchStrategy = class { tool_search_strategy_type = "tag_and_description_word_match"; descriptionWeight; tagWeight; _config; /** * Creates an instance of TagSearchStrategy. * * @param descriptionWeight The weight to apply to words found in the tool's description. * @param tagWeight The weight to apply to words found in the tool's tags. */ constructor(config) { this._config = TagSearchStrategyConfigSchema.parse(config); this.descriptionWeight = this._config.description_weight; this.tagWeight = this._config.tag_weight; } /** * Converts the search strategy instance's configuration to a dictionary. */ toDict() { return this._config; } /** * Searches for tools by matching tags and description content against a query. * * @param concurrentToolRepository The repository to search for tools. * @param query The search query string. * @param limit The maximum number of tools to return. If 0, all matched tools are returned. * @param anyOfTagsRequired Optional list of tags where one of them must be present in the tool's tags. * @returns A promise that resolves to a list of tools ordered by relevance. */ async searchTools(concurrentToolRepository, query, limit = 10, anyOfTagsRequired) { const queryLower = query.toLowerCase(); const queryWords = new Set(queryLower.match(/\w+/g) || []); let tools = await concurrentToolRepository.getTools(); if (anyOfTagsRequired && anyOfTagsRequired.length > 0) { const requiredTagsLower = new Set(anyOfTagsRequired.map((tag) => tag.toLowerCase())); tools = tools.filter( (tool) => tool.tags && tool.tags.some((tag) => requiredTagsLower.has(tag.toLowerCase())) ); } const toolScores = tools.map((tool) => { let score = 0; const toolNameLower = tool.name.toLowerCase(); const toolNameOnly = toolNameLower.includes(".") ? toolNameLower.split(".").pop() || toolNameLower : toolNameLower; if (queryLower === toolNameOnly || queryLower.includes(toolNameOnly) || toolNameOnly.includes(queryLower)) { score += this.tagWeight * 2; } const toolNameWords = new Set(toolNameOnly.match(/\w+/g) || []); for (const word of toolNameWords) { if (queryWords.has(word)) { score += this.tagWeight; } } if (tool.tags) { for (const tag of tool.tags) { const tagLower = tag.toLowerCase(); if (queryLower.includes(tagLower) || tagLower.includes(queryLower)) { score += this.tagWeight; } const tagWords = new Set(tagLower.match(/\w+/g) || []); for (const word of tagWords) { if (queryWords.has(word)) { score += this.tagWeight * 0.5; } } for (const queryWord of queryWords) { if (queryWord.length > 2) { for (const tagWord of tagWords) { if (tagWord.length > 2 && (tagWord.includes(queryWord) || queryWord.includes(tagWord))) { score += this.tagWeight * 0.3; break; } } } } } } if (tool.description) { const descriptionLower = tool.description.toLowerCase(); const descriptionWords = new Set( descriptionLower.match(/\w+/g) || [] ); for (const word of descriptionWords) { if (queryWords.has(word) && word.length > 2) { score += this.descriptionWeight; } } for (const queryWord of queryWords) { if (queryWord.length > 2) { for (const descWord of descriptionWords) { if (descWord.length > 2 && (descWord.includes(queryWord) || queryWord.includes(descWord))) { score += this.descriptionWeight * 0.5; break; } } } } } return { tool, score }; }); const sortedTools = toolScores.sort((a, b) => b.score - a.score).map((item) => item.tool); return limit > 0 ? sortedTools.slice(0, limit) : sortedTools; } }; var TagSearchStrategyConfigSerializer = class extends Serializer { toDict(obj) { return { tool_search_strategy_type: obj.tool_search_strategy_type, description_weight: obj.descriptionWeight, tag_weight: obj.tagWeight }; } validateDict(data) { try { return new TagSearchStrategy(TagSearchStrategyConfigSchema.parse(data)); } catch (e) { if (e instanceof import_zod10.z.ZodError) { throw new Error(`Invalid configuration: ${e.message}`); } throw new Error("Unexpected error during validation"); } } }; var TagSearchStrategyConfigSchema = import_zod10.z.object({ tool_search_strategy_type: import_zod10.z.literal("tag_and_description_word_match"), description_weight: import_zod10.z.number().optional().default(1), tag_weight: import_zod10.z.number().optional().default(3) }).passthrough(); // src/interfaces/tool_search_strategy.ts var import_zod11 = __toESM(require("zod"), 1); var ToolSearchStrategyConfigSerializer = class _ToolSearchStrategyConfigSerializer extends Serializer { static implementations = {}; static default_strategy = "tag_and_description_word_match"; // No need for the whole plugin registry. Plugins just need to call this to register a new strategy static registerStrategy(type, serializer, override = false) { if (!override && _ToolSearchStrategyConfigSerializer.implementations[type]) { return false; } _ToolSearchStrategyConfigSerializer.implementations[type] = serializer; return true; } toDict(obj) { const serializer = _ToolSearchStrategyConfigSerializer.implementations[obj.tool_search_strategy_type]; if (!serializer) throw new Error(`No serializer for type: ${obj.tool_search_strategy_type}`); return serializer.toDict(obj); } validateDict(data) { const serializer = _ToolSearchStrategyConfigSerializer.implementations[data["tool_search_strategy_type"]]; if (!serializer) throw new Error(`Invalid tool search strategy type: ${data["tool_search_strategy_type"]}`); return serializer.validateDict(data); } }; var ToolSearchStrategySchema = import_zod11.default.custom((obj) => { try { const validated = new ToolSearchStrategyConfigSerializer().validateDict(obj); return validated; } catch (e) { return false; } }, { message: "Invalid ToolSearchStrategy object" }); // src/implementations/post_processors/filter_dict_post_processor.ts var import_zod12 = require("zod"); var FilterDictPostProcessor = class { tool_post_processor_type = "filter_dict"; excludeKeys; onlyIncludeKeys; excludeTools; onlyIncludeTools; excludeManuals; onlyIncludeManuals; _config; constructor(config) { this._config = FilterDictPostProcessorConfigSchema.parse(config); this.excludeKeys = config.exclude_keys ? new Set(config.exclude_keys) : void 0; this.onlyIncludeKeys = config.only_include_keys ? new Set(config.only_include_keys) : void 0; this.excludeTools = config.exclude_tools ? new Set(config.exclude_tools) : void 0; this.onlyIncludeTools = config.only_include_tools ? new Set(config.only_include_tools) : void 0; this.excludeManuals = config.exclude_manuals ? new Set(config.exclude_manuals) : void 0; this.onlyIncludeManuals = config.only_include_manuals ? new Set(config.only_include_manuals) : void 0; if (this.excludeKeys && this.onlyIncludeKeys) { console.warn("FilterDictPostProcessor configured with both 'exclude_keys' and 'only_include_keys'. 'exclude_keys' will be ignored."); } if (this.excludeTools && this.onlyIncludeTools) { console.warn("FilterDictPostProcessor configured with both 'exclude_tools' and 'only_include_tools'. 'exclude_tools' will be ignored."); } if (this.excludeManuals && this.onlyIncludeManuals) { console.warn("FilterDictPostProcessor configured with both 'exclude_manuals' and 'only_include_manuals'. 'exclude_manuals' will be ignored."); } } /** * Converts the post-processor instance's configuration to a dictionary. */ toDict() { return this._config; } /** * Processes the result of a tool call, applying filtering logic. * @param caller The UTCP client instance. * @param tool The Tool object that was called. * @param manualCallTemplate The CallTemplateBase object of the manual that owns the tool. * @param result The raw result returned by the tool's communication protocol. * @returns The processed result. */ postProcess(caller, tool, manualCallTemplate, result) { if (this.shouldSkipProcessing(tool, manualCallTemplate)) { return result; } if (this.onlyIncludeKeys) { return this._filterDictOnlyIncludeKeys(result); } if (this.excludeKeys) { return this._filterDictExcludeKeys(result); } return result; } /** * Determines if processing should be skipped based on tool and manual filters. * @param tool The Tool object. * @param manualCallTemplate The CallTemplateBase object of the manual. * @returns True if processing should be skipped, false otherwise. */ shouldSkipProcessing(tool, manualCallTemplate) { if (this.onlyIncludeTools && !this.onlyIncludeTools.has(tool.name)) { return true; } if (this.excludeTools && this.excludeTools.has(tool.name)) { return true; } const manualName = manualCallTemplate.name; if (manualName) { if (this.onlyIncludeManuals && !this.onlyIncludeManuals.has(manualName)) { return true; } if (this.excludeManuals && this.excludeManuals.has(manualName)) { return true; } } return false; } /** * Recursively filters a dictionary, keeping only specified keys. * @param data The data to filter. * @returns The filtered data. */ _filterDictOnlyIncludeKeys(data) { if (typeof data !== "object" || data === null) { return data; } if (Array.isArray(data)) { return data.map((item) => this._filterDictOnlyIncludeKeys(item)).filter((item) => { if (typeof item === "object" && item !== null) { if (Array.isArray(item)) return item.length > 0; return Object.keys(item).length > 0; } return true; }); } const newObject = {}; for (const key in data) { if (Object.prototype.hasOwnProperty.call(data, key)) { if (this.onlyIncludeKeys?.has(key)) { newObject[key] = this._filterDictOnlyIncludeKeys(data[key]); } else { const processedValue = this._filterDictOnlyIncludeKeys(data[key]); if (typeof processedValue === "object" && processedValue !== null) { if (Array.isArray(processedValue) && processedValue.length > 0) { newObject[key] = processedValue; } else if (Object.keys(processedValue).length > 0) { newObject[key] = processedValue; } } } } } return newObject; } /** * Recursively filters a dictionary, excluding specified keys. * @param data The data to filter. * @returns The filtered data. */ _filterDictExcludeKeys(data) { if (typeof data !== "object" || data === null) { return data; } if (Array.isArray(data)) { return data.map((item) => this._filterDictExcludeKeys(item)).filter((item) => { if (typeof item === "object" && item !== null) { if (Array.isArray(item)) return item.length > 0; return Object.keys(item).length > 0; } return true; }); } const newObject = {}; for (const key in data) { if (Object.prototype.hasOwnProperty.call(data, key)) { if (!this.excludeKeys?.has(key)) { newObject[key] = this._filterDictExcludeKeys(data[key]); } } } return newObject; } }; var FilterDictPostProcessorConfigSchema = import_zod12.z.object({ tool_post_processor_type: import_zod12.z.literal("filter_dict"), exclude_keys: import_zod12.z.array(import_zod12.z.string()).optional(), only_include_keys: import_zod12.z.array(import_zod12.z.string()).optional(), exclude_tools: import_zod12.z.array(import_zod12.z.string()).optional(), only_include_tools: import_zod12.z.array(import_zod12.z.string()).optional(), exclude_manuals: import_zod12.z.array(import_zod12.z.string()).optional(), only_include_manuals: import_zod12.z.array(import_zod12.z.string()).optional() }).passthrough(); var FilterDictPostProcessorSerializer = class extends Serializer { toDict(obj) { const filterDictConfig = obj.toDict(); return { tool_post_processor_type: filterDictConfig.tool_post_processor_type, exclude_keys: filterDictConfig.exclude_keys, only_include_keys: filterDictConfig.only_include_keys, exclude_tools: filterDictConfig.exclude_tools, only_include_tools: filterDictConfig.only_include_tools, exclude_manuals: filterDictConfig.exclude_manuals, only_include_manuals: filterDictConfig.only_include_manuals }; } validateDict(data) { try { return new FilterDictPostProcessor(FilterDictPostProcessorConfigSchema.parse(data)); } catch (e) { if (e instanceof import_zod12.z.ZodError) { throw new Error(`Invalid configuration: ${e.message}`); } throw new Error("Unexpected error during validation"); } } }; // src/implementations/post_processors/limit_strings_post_processor.ts var import_zod13 = require("zod"); var LimitStringsPostProcessor = class { tool_post_processor_type = "limit_strings"; limit; excludeTools; onlyIncludeTools; excludeManuals; onlyIncludeManuals; _config; constructor(config) { this._config = LimitStringsPostProcessorConfigSchema.parse(config); this.limit = config.limit; this.excludeTools = config.exclude_tools ? new Set(config.exclude_tools) : void 0; this.onlyIncludeTools = config.only_include_tools ? new Set(config.only_include_tools) : void 0; this.excludeManuals = config.exclude_manuals ? new Set(config.exclude_manuals) : void 0; this.onlyIncludeManuals = config.only_include_manuals ? new Set(config.only_include_manuals) : void 0; if (this.excludeTools && this.onlyIncludeTools) { console.warn("LimitStringsPostProcessor configured with both 'exclude_tools' and 'only_include_tools'. 'exclude_tools' will be ignored."); } if (this.excludeManuals && this.onlyIncludeManuals) { console.warn("LimitStringsPostProcessor configured with both 'exclude_manuals' and 'only_include_manuals'. 'exclude_manuals' will be ignored."); } } /** * Converts the post-processor instance's configuration to a dictionary. */ toDict() { return this._config; } /** * Processes the result of a tool call, truncating string values if applicable. * @param caller The UTCP client instance. * @param tool The Tool object that was called. * @param manualCallTemplate The CallTemplateBase object of the manual that owns the tool. * @param result The raw result returned by the tool's communication protocol. * @returns The processed result. */ postProcess(caller, tool, manualCallTemplate, result) { if (this.shouldSkipProcessing(tool, manualCallTemplate)) { return result; } return this._processObject(result); } /** * Determines if processing should be skipped based on tool and manual filters. * @param tool The Tool object. * @param manualCallTemplate The CallTemplateBase object of the manual. * @returns True if processing should be skipped, false otherwise. */ shouldSkipProcessing(tool, manualCallTemplate) { if (this.onlyIncludeTools && !this.onlyIncludeTools.has(tool.name)) { return true; } if (this.excludeTools && this.excludeTools.has(tool.name)) { return true; } const manualName = manualCallTemplate.name; if (manualName) { if (this.onlyIncludeManuals && !this.onlyIncludeManuals.has(manualName)) { return true; } if (this.excludeManuals && this.excludeManuals.has(manualName)) { return true; } } return false; } /** * Recursively processes an object, truncating strings. * @param obj The object to process. * @returns The processed object. */ _processObject(obj) { if (typeof obj === "string") { return obj.length > this.limit ? obj.substring(0, this.limit) : obj; } if (Array.isArray(obj)) { return obj.map((item) => this._processObject(item)); } if (typeof obj === "object" && obj !== null) { const newObj = {}; for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = this._processObject(obj[key]); } } return newObj; } return obj; } }; var LimitStringsPostProcessorConfigSchema = import_zod13.z.object({ tool_post_processor_type: import_zod13.z.literal("limit_strings"), limit: import_zod13.z.number().int().positive().default(1e4), exclude_tools: import_zod13.z.array(import_zod13.z.string()).optional(), only_include_tools: import_zod13.z.array(import_zod13.z.string()).optional(), exclude_manuals: import_zod13.z.array(import_zod13.z.string()).optional(), only_include_manuals: import_zod13.z.array(import_zod13.z.string()).optional() }).passthrough(); var LimitStringsPostProcessorSerializer = class extends Serializer { toDict(obj) { const limitStringsConfig = obj.toDict(); return { tool_post_processor_type: limitStringsConfig.tool_post_processor_type, limit: limitStringsConfig.limit, exclude_tools: limitStringsConfig.exclude_tools, only_include_tools: limitStringsConfig.only_include_tools, exclude_manuals: limitStringsConfig.exclude_manuals, only_include_manuals: limitStringsConfig.only_include_manuals }; } validateDict(data) { try { return new LimitStringsPostProcessor(LimitStringsPostProcessorConfigSchema.parse(data)); } catch (e) { if (e instanceof import_zod13.z.ZodError) { throw new Error(`Invalid configuration: ${e.message}`); } throw new Error("Unexpected error during validation"); } } }; // src/interfaces/tool_post_processor.ts var import_zod14 = __toESM(require("zod"), 1); var ToolPostProcessorConfigSerializer = class _ToolPostProcessorConfigSerializer extends Serializer { static implementations = {}; // No need for the whole plugin registry. Plugins just need to call this to register a new post-processor static registerPostProcessor(type, serializer, override = false) { if (!override && _ToolPostProcessorConfigSerializer.implementations[type]) { return false; } _ToolPostProcessorConfigSerializer.implementations[type] = serializer; return true; } toDict(obj) { const serializer = _ToolPostProcessorConfigSerializer.implementations[obj.tool_post_processor_type]; if (!serializer) throw new Error(`No serializer for type: ${obj.tool_post_processor_type}`); return serializer.toDict(obj); } validateDict(data) { const serializer = _ToolPostProcessorConfigSerializer.implementations[data["tool_post_processor_type"]]; if (!serializer) throw new Error(`Invalid tool post-processor type: ${data["tool_post_processor_type"]}`); return serializer.validateDict(data); } }; var ToolPostProcessorSchema = import_zod14.default.custom((obj) => { try { const validated = new ToolPostProcessorConfigSerializer().validateDict(obj); return validated; } catch (e) { return false; } }, { message: "Invalid ToolPostProcessor object" }); // src/plugins/plugin_loader.ts var corePluginsInitialized = false; var initializing = false; setPluginInitializer(() => ensureCorePluginsInitialized()); function _registerCorePlugins() { AuthSerializer.registerAuth("api_key", new ApiKeyAuthSerializer()); AuthSerializer.registerAuth("basic", new BasicAuthSerializer()); AuthSerializer.registerAuth("oauth2", new OAuth2AuthSerializer()); ConcurrentToolRepositoryConfigSerializer.registerRepository("in_memory", new InMemConcurrentToolRepositorySerializer()); ToolSearchStrategyConfigSerializer.registerStrategy("tag_and_description_word_match", new TagSearchStrategyConfigSerializer()); ToolPostProcessorConfigSerializer.registerPostProcessor("filter_dict", new FilterDictPostProcessorSerializer()); ToolPostProcessorConfigSerializer.registerPostProcessor("limit_strings", new LimitStringsPostProcessorSerializer()); } function ensureCorePluginsInitialized() { if (!corePluginsInitialized && !initializing) { initializing = true; _registerCorePlugins(); corePluginsInitialized = true; initializing = false; } } // src/data/variable_loader.ts var import_zod15 = require("zod"); var VariableLoaderSerializer = class _VariableLoaderSerializer extends Serializer { static serializers = {}; /** * Registers a variable loader serializer for a specific type. * @param type The variable_loader_type identifier * @param serializer The serializer instance for this type * @param override Whether to override an existing registration * @returns true if registration succeeded, false if already exists and override is false */ static registerVariableLoader(type, serializer, override = false) { if (!override && _VariableLoaderSerializer.serializers[type]) { return false; } _VariableLoaderSerializer.serializers[type] = serializer; return true; } toDict(obj) { const serializer = _VariableLoaderSerializer.serializers[obj.variable_loader_type]; if (!serializer) { throw new Error(`No serializer found for variable_loader_type: ${obj.variable_loader_type}`); } return serializer.toDict(obj); } validateDict(obj) { const serializer = _VariableLoaderSerializer.serializers[obj.variable_loader_type]; if (!serializer) { throw new Error(`Invalid variable_loader_type: ${obj.variable_loader_type}`); } return serializer.validateDict(obj); } }; var VariableLoaderSchema = import_zod15.z.custom((obj) => { try { const validated = new VariableLoaderSerializer().validateDict(obj); return validated; } catch (e) { return false; } }, { message: "Invalid VariableLoader object" }); // src/client/utcp_client_config.ts ensureCorePluginsInitialized(); var UtcpClientConfigSchema = import_zod16.z.object({ variables: import_zod16.z.record(import_zod16.z.string(), import_zod16.z.string()).optional().default({}), load_variables_from: import_zod16.z.array(VariableLoaderSchema).nullable().optional().default(null).transform((val) => { if (val === null) return null; return val.map((item) => { if ("variable_loader_type" in item) { return new VariableLoaderSerializer().validateDict(item); } return item; }); }), tool_repository: import_zod16.z.any().transform((val) => { if (typeof val === "object" && val !== null && "tool_repository_type" in val) { return new ConcurrentToolRepositoryConfigSerializer().validateDict(val); } return val; }).optional().default(new ConcurrentToolRepositoryConfigSerializer().validateDict({ tool_repository_type: ConcurrentToolRepositoryConfigSerializer.default_strategy })), tool_search_strategy: import_zod16.z.any().transform((val) => { if (typeof val === "object" && val !== null && "tool_search_strategy_type" in val) { return new ToolSearchStrategyConfigSerializer().validateDict(val); } return val; }).optional().default(new ToolSearchStrategyConfigSerializer().validateDict({ tool_search_strategy_type: ToolSearchStrategyConfigSerializer.default_strategy })), post_processing: import_zod16.z.array(import_zod16.z.any()).transform((val) => { return val.map((item) => { if (typeof item === "object" && item !== null && "tool_post_processor_type" in item) { return new ToolPostProcessorConfigSerializer().validateDict(item); } return item; }); }).optional().default([]), manual_call_templates: import_zod16.z.array(CallTemplateSchema).transform((val) => { return val.map((item) => { if (typeof item === "object" && item !== null && "call_template_type" in item) { return new CallTemplateSerializer().validateDict(item); } return item; }); }).optional().default([]) }).strict(); var UtcpClientConfigSerializer = class extends Serializer { /** * REQUIRED * Convert a UtcpClientConfig object to a dictionary. * * @param obj The UtcpClientConfig object to convert. * @returns The dictionary converted from the UtcpClientConfig object. */ toDict(obj) { return { variables: obj.variables, load_variables_from: obj.load_variables_from === null ? null : obj.load_variables_from?.map((item) => new VariableLoaderSerializer().toDict(item)), tool_repository: new ConcurrentToolRepositoryConfigSerializer().toDict(obj.tool_repository), tool_search_strategy: new ToolSearchStrategyConfigSerializer().toDict(obj.tool_search_strategy), post_processing: obj.post_processing.map((item) => new ToolPostProcessorConfigSerializer().toDict(item)), manual_call_templates: obj.manual_call_templates.map((item) => new CallTemplateSerializer().toDict(item)) }; } /** * REQUIRED * Validate a dictionary and convert it to a UtcpClientConfig object. * * @param data The dictionary to validate and convert. * @returns The UtcpClientConfig object converted from the dictionary. * @throws Error if validation fails */ validateDict(data) { try { return UtcpClientConfigSchema.parse(data); } catch (e) { throw new Error(`Invalid UtcpClientConfig: ${e.message} ${e.stack || ""}`); } } }; // src/exceptions/utcp_variable_not_found_error.ts var UtcpVariableNotFoundError = class extends Error { variableName; /** * Initializes the exception with the missing variable name. * * @param variableName The name of the variable that could not be found. */ constructor(variableName) { super( `Variable '${variableName}' referenced in call template configuration not found. Please ensure it's defined in client.config.variables, environment variables, or a configured variable loader.` ); this.variableName = variableName; this.name = "UtcpVariableNotFoundError"; } }; // src/implementations/default_variable_substitutor.ts var DefaultVariableSubstitutor = class { /** * Retrieves a variable value from configured sources, respecting namespaces. * * @param key The variable name to look up (without namespace prefix). * @param config The UTCP client configuration. * @param namespace An optional namespace to prepend to the variable name for lookup. * @returns The resolved variable value. * @throws UtcpVariableNotFoundError if the variable cannot be found. */ async _getVariable(key, config, namespace) { let effectiveKey = key; if (namespace) { effectiveKey = namespace.replace(/_/g, "!").replace(/!/g, "__") + "_" + key; } if (config.variables && effectiveKey in config.variables) { return config.variables[effectiveKey]; } if (config.load_variables_from) { for (const varLoader of config.load_variables_from) { const varValue = await varLoader.get(effectiveKey); if (varValue) { return varValue; } } } try { const envVar = process.env[effectiveKey]; if (envVar) { return envVar; } } catch (e) { } throw new UtcpVariableNotFoundError(effectiveKey); } /** * Recursively substitutes variables in the given object. * * @param obj The object (can be string, array, or object) containing potential variable references to substitute. * @param config The UTCP client configuration containin