@hashgraphonline/conversational-agent
Version:
Hashgraph Online conversational AI agent implementing HCS-10 communication, HCS-2 registries, and content inscription on Hedera. https://hol.org
326 lines (325 loc) • 9.23 kB
JavaScript
import { z } from "zod";
import { Logger } from "@hashgraphonline/standards-sdk";
import { wrapToolWithFormValidation } from "./index33.js";
import { FormGenerator } from "./index11.js";
import { fieldGuidanceRegistry } from "./index14.js";
import { isFormValidatable } from "@hashgraphonline/standards-agent-kit";
class ToolRegistry {
constructor(logger) {
this.tools = /* @__PURE__ */ new Map();
this.formGenerator = new FormGenerator();
this.logger = logger || new Logger({ module: "ToolRegistry" });
}
/**
* Register a tool with the registry
*/
registerTool(tool, options = {}) {
const capabilities = this.analyzeToolCapabilities(tool);
const metadata = {
name: tool.name,
version: "1.0.0",
category: options.metadata?.category || "core",
description: tool.description,
capabilities,
dependencies: [],
schema: tool.schema,
...options.metadata
};
try {
if (!metadata.entityResolutionPreferences) {
const schemaRecord = tool.schema;
const rawPrefs = schemaRecord && typeof schemaRecord === "object" && schemaRecord["_entityResolutionPreferences"];
if (rawPrefs && typeof rawPrefs === "object") {
metadata.entityResolutionPreferences = rawPrefs;
}
}
} catch {
}
try {
const schemaRecord = tool.schema;
const schemaDef = schemaRecord && schemaRecord._def;
if (schemaDef?.typeName === "ZodObject") {
const shape = typeof schemaDef.shape === "function" ? schemaDef.shape?.() || {} : schemaDef.shape || {};
const metadataField = shape["metadata"];
const isStringArray = !!metadataField && metadataField._def?.typeName === "ZodArray" && metadataField._def?.type?._def?.typeName === "ZodString";
if (isStringArray && typeof tool.description === "string") {
if (!metadata.entityResolutionPreferences) {
metadata.entityResolutionPreferences = {
inscription: "hrl"
};
}
const note = " NOTE: When referencing inscriptions or media, provide canonical Hashlink Resource Locators (e.g., hcs://<standard>/<topicId>) rather than external URLs or embedded JSON.";
if (!tool.description.includes("Hashlink Resource Locators")) {
tool.description = `${tool.description}${note}`;
}
}
}
} catch {
}
let finalTool = tool;
let wrapper;
if (this.shouldWrapTool(tool, capabilities, options)) {
wrapper = wrapToolWithFormValidation(
tool,
this.formGenerator,
{
requireAllFields: false,
skipFields: ["metaOptions"],
...options.wrapperConfig
}
);
finalTool = wrapper;
}
try {
if (metadata.entityResolutionPreferences) {
finalTool["entityResolutionPreferences"] = metadata.entityResolutionPreferences;
}
} catch {
}
const entry = {
tool: finalTool,
metadata,
wrapper,
originalTool: tool,
options: {
priority: capabilities.priority,
capability: "basic",
// Default capability
enabled: true,
// All tools are enabled by default
namespace: metadata.category
}
};
this.tools.set(tool.name, entry);
try {
const metaFG = metadata.fieldGuidance;
if (metaFG) {
fieldGuidanceRegistry.registerToolConfiguration(metaFG);
}
const provider = metadata.fieldGuidanceProvider;
if (provider) {
const pattern = metaFG?.toolPattern ?? tool.name;
fieldGuidanceRegistry.registerToolProvider(pattern, provider, {
id: `${tool.name}:field-guidance-provider`,
priority: 0
});
}
} catch {
}
}
/**
* Get a tool by name
*/
getTool(name) {
return this.tools.get(name) || null;
}
/**
* Get tools by capability
*/
getToolsByCapability(capability, value) {
const results = [];
for (const entry of this.tools.values()) {
if (value !== void 0) {
if (entry.metadata.capabilities[capability] === value) {
results.push(entry);
}
} else if (entry.metadata.capabilities[capability]) {
results.push(entry);
}
}
return results;
}
/**
* Get tools by query
*/
getToolsByQuery(query) {
const results = [];
for (const entry of this.tools.values()) {
let matches = true;
if (query.name && entry.metadata.name !== query.name) {
matches = false;
}
if (query.category && entry.metadata.category !== query.category) {
matches = false;
}
if (query.capabilities) {
for (const [key, value] of Object.entries(query.capabilities)) {
if (entry.metadata.capabilities[key] !== value) {
matches = false;
break;
}
}
}
if (matches) {
results.push(entry);
}
}
return results;
}
/**
* Get all registered tools
*/
getAllTools() {
return Array.from(this.tools.values()).map((entry) => entry.tool);
}
/**
* Get all registry entries
*/
getAllRegistryEntries() {
return Array.from(this.tools.values());
}
/**
* Get all tool names
*/
getToolNames() {
return Array.from(this.tools.keys());
}
/**
* Get tools by priority
*/
getToolsByPriority(priority) {
return this.getToolsByCapability("priority", priority);
}
/**
* Get enabled tools (all tools are considered enabled by default)
*/
getEnabledTools() {
return this.getAllRegistryEntries();
}
/**
* Get tools by namespace/category
*/
getToolsByNamespace(namespace) {
if (!namespace) {
return this.getAllRegistryEntries();
}
return this.getToolsByQuery({ category: namespace });
}
/**
* Check if registry has capability
*/
hasCapability(capability) {
for (const entry of this.tools.values()) {
if (entry.metadata.capabilities[capability]) {
return true;
}
}
return false;
}
/**
* Update tool options (metadata)
*/
updateToolOptions(name, options) {
const entry = this.tools.get(name);
if (!entry) {
return false;
}
entry.metadata = { ...entry.metadata, ...options };
return true;
}
/**
* Check if a tool is registered
*/
hasTool(name) {
return this.tools.has(name);
}
/**
* Unregister a tool
*/
unregisterTool(name) {
return this.tools.delete(name);
}
/**
* Clear all tools
*/
clear() {
this.tools.clear();
}
/**
* Analyze tool capabilities
*/
analyzeToolCapabilities(tool) {
const implementsFormValidatable = isFormValidatable(tool);
const hasRenderConfig = this.hasRenderConfig(tool);
const isZodObjectLike = this.isZodObjectLike(tool.schema);
const supportsFormValidation = implementsFormValidatable || hasRenderConfig;
const requiresWrapper = supportsFormValidation && isZodObjectLike;
let priority = "medium";
let category = "core";
if (supportsFormValidation && requiresWrapper) {
priority = "critical";
} else if (supportsFormValidation) {
priority = "high";
} else if (tool.description?.toLowerCase().includes("query") || tool.description?.toLowerCase().includes("search")) {
priority = "low";
}
const toolAsAny = tool;
if (tool.constructor.name.includes("MCP") || toolAsAny.isMCPTool) {
category = "mcp";
} else if (toolAsAny.isExtension || tool.constructor.name.includes("Extension")) {
category = "extension";
}
return {
supportsFormValidation,
requiresWrapper,
priority,
category
};
}
/**
* Check if tool has render configuration
*/
hasRenderConfig(tool) {
const schema = tool.schema;
return !!(schema && schema._renderConfig);
}
/**
* Determine if tool should be wrapped
*/
shouldWrapTool(tool, capabilities, options) {
if (options.skipWrapper) {
return false;
}
if (options.forceWrapper) {
return true;
}
return capabilities.requiresWrapper;
}
/**
* Check if schema is ZodObject-like
*/
isZodObjectLike(schema) {
if (!schema || typeof schema !== "object") {
return false;
}
const schemaRecord = schema;
const schemaDef = schemaRecord._def;
return schema instanceof z.ZodObject || schemaDef?.typeName === "ZodObject" || "shape" in schemaRecord && typeof schemaRecord.shape === "object";
}
/**
* Get statistics about the registry
*/
getStatistics() {
const stats = {
totalTools: this.tools.size,
wrappedTools: 0,
unwrappedTools: 0,
categoryCounts: { core: 0, extension: 0, mcp: 0 },
priorityCounts: { low: 0, medium: 0, high: 0, critical: 0 }
};
for (const entry of this.tools.values()) {
if (entry.wrapper) {
stats.wrappedTools++;
} else {
stats.unwrappedTools++;
}
stats.categoryCounts[entry.metadata.category]++;
stats.priorityCounts[entry.metadata.capabilities.priority]++;
}
return stats;
}
}
export {
ToolRegistry
};
//# sourceMappingURL=index46.js.map