@hashgraphonline/conversational-agent
Version:
Hashgraph Online conversational AI agent implementing HCS-10 communication, HCS-2 registries, and content inscription on Hedera. https://hol.org
1 lines • 704 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../../src/plugins/hcs-10/HCS10Plugin.ts","../../src/plugins/hcs-2/HCS2Plugin.ts","../../src/forms/field-guidance-registry.ts","../../src/plugins/inscribe/InscribePlugin.ts","../../src/plugins/hbar/AccountBuilder.ts","../../src/plugins/hbar/TransferHbarTool.ts","../../src/plugins/hbar/AirdropToolWrapper.ts","../../src/plugins/hbar/HbarPlugin.ts","../../src/plugins/community/swarm/constants.ts","../../src/plugins/community/swarm/utils.ts","../../src/plugins/community/swarm/tools/ListPostageStampsTool.ts","../../src/plugins/community/swarm/tools/UploadDataTool.ts","../../src/plugins/community/swarm/tools/DownloadDataTool.ts","../../src/plugins/community/swarm/tools/CreatePostageStampTool.ts","../../src/plugins/community/swarm/tools/ExtendPostageStampTool.ts","../../src/plugins/community/swarm/tools/QueryUploadProgressTool.ts","../../src/plugins/community/swarm/tools/DownloadFilesTool.ts","../../src/plugins/community/swarm/tools/GetPostageStampTool.ts","../../src/plugins/community/swarm/tools/ReadFeedTool.ts","../../src/plugins/community/swarm/tools/UpdateFeedTool.ts","../../src/plugins/community/swarm/tools/UploadFileTool.ts","../../src/plugins/community/swarm/tools/UploadFolderTool.ts","../../src/plugins/community/swarm/SwarmPlugin.ts","../../src/forms/field-type-registry.ts","../../src/constants/messages.ts","../../src/services/formatters/types.ts","../../src/constants/entity-references.ts","../../src/constants/form-priorities.ts","../../src/forms/form-generator.ts","../../src/forms/form-engine.ts","../../src/utils/response-formatter.ts","../../src/langchain/form-aware-agent-executor.ts","../../src/base-agent.ts","../../src/mcp/content-processor.ts","../../src/mcp/mcp-client-manager.ts","../../src/mcp/adapters/langchain.ts","../../src/memory/token-counter.ts","../../src/memory/memory-window.ts","../../src/memory/reference-id-generator.ts","../../src/types/content-reference.ts","../../src/memory/content-storage.ts","../../src/memory/smart-memory-manager.ts","../../src/langchain/form-validating-tool-wrapper.ts","../../src/core/tool-registry.ts","../../src/execution/execution-pipeline.ts","../../src/langchain/langchain-agent.ts","../../src/agent-factory.ts","../../src/signers/browser-signer.ts","../../src/providers.ts","../../src/runtime/wallet-bridge.ts","../../src/plugins/web-browser/WebBrowserPlugin.ts","../../src/config/system-message.ts","../../src/services/content-store-manager.ts","../../src/tools/entity-resolver-tool.ts","../../src/services/formatters/format-converter-registry.ts","../../src/services/formatters/converters/topic-id-to-hrl-converter.ts","../../src/services/formatters/converters/string-normalization-converter.ts","../../src/services/parameter-service.ts","../../src/conversational-agent.ts","../../src/services/attachment-processor.ts","../../src/services/entity-resolver.ts","../../src/mcp/helpers.ts"],"sourcesContent":["import {\n GenericPluginContext,\n HederaTool,\n BasePlugin,\n HederaAgentKit,\n} from 'hedera-agent-kit';\nimport {\n IStateManager,\n OpenConvaiState,\n HCS10Builder,\n RegisterAgentTool,\n FindRegistrationsTool,\n InitiateConnectionTool,\n ListConnectionsTool,\n SendMessageToConnectionTool,\n CheckMessagesTool,\n ConnectionMonitorTool,\n ManageConnectionRequestsTool,\n AcceptConnectionRequestTool,\n RetrieveProfileTool,\n ListUnapprovedConnectionRequestsTool,\n RegisteredAgent,\n} from '@hashgraphonline/standards-agent-kit';\nimport { HCS10Client } from '@hashgraphonline/standards-sdk';\nimport { PrivateKey } from 'node_modules/@hashgraph/sdk/lib/Mnemonic';\n\ninterface HCS10ClientManager {\n initializeConnectionsManager(client: HCS10Client): void;\n}\n\nfunction hasInitializeConnectionsManager(\n stateManager: IStateManager\n): stateManager is IStateManager & HCS10ClientManager {\n return (\n typeof stateManager === 'object' &&\n stateManager !== null &&\n 'initializeConnectionsManager' in stateManager &&\n typeof stateManager.initializeConnectionsManager === 'function'\n );\n}\n\nexport class HCS10Plugin extends BasePlugin {\n id = 'hcs-10';\n name = 'HCS-10 Plugin';\n description =\n 'HCS-10 agent tools for decentralized agent registration, connections, and messaging on Hedera';\n version = '1.0.0';\n author = 'Hashgraph Online';\n namespace = 'hcs10';\n\n private stateManager?: IStateManager;\n private tools: any[] = [];\n appConfig?: Record<string, unknown>;\n\n override async initialize(context: GenericPluginContext): Promise<void> {\n await super.initialize(context);\n\n const hederaKit = context.config.hederaKit as HederaAgentKit;\n if (!hederaKit) {\n this.context.logger.warn(\n 'HederaKit not found in context. HCS-10 tools will not be available.'\n );\n return;\n }\n\n try {\n this.stateManager =\n (context.stateManager as IStateManager) ||\n (context.config.stateManager as IStateManager) ||\n (this.appConfig?.stateManager as IStateManager) ||\n new OpenConvaiState();\n\n const accountId = hederaKit.signer.getAccountId().toString();\n const isBytesMode =\n String(hederaKit.operationalMode || 'returnBytes') === 'returnBytes';\n let inboundTopicId = '';\n let outboundTopicId = '';\n\n let operatorPrivateKeyRef: PrivateKey =\n hederaKit.signer.getOperatorPrivateKey();\n let operatorPrivateKeySerialized: string | undefined;\n\n try {\n const resolved =\n typeof operatorPrivateKeyRef?.toString === 'function'\n ? operatorPrivateKeyRef.toString()\n : '';\n\n operatorPrivateKeySerialized = resolved;\n\n const hcs10Client = new HCS10Client({\n network: hederaKit.network as 'mainnet' | 'testnet',\n operatorId: accountId,\n operatorPrivateKey: operatorPrivateKeyRef,\n logLevel: 'error',\n });\n\n const profileResponse = await hcs10Client.retrieveProfile(accountId);\n if (profileResponse.success && profileResponse.topicInfo) {\n inboundTopicId = profileResponse.topicInfo.inboundTopic;\n outboundTopicId = profileResponse.topicInfo.outboundTopic;\n }\n } catch (profileError) {\n this.context.logger.warn(\n 'Skipping profile topic discovery',\n profileError\n );\n }\n\n const agentRecord: Record<string, unknown> = {\n name: `Agent ${accountId}`,\n accountId: accountId,\n inboundTopicId,\n outboundTopicId,\n };\n if (!isBytesMode && operatorPrivateKeySerialized) {\n agentRecord.privateKey = operatorPrivateKeySerialized;\n }\n this.stateManager.setCurrentAgent(\n agentRecord as unknown as RegisteredAgent\n );\n\n this.context.logger.info(\n `Set current agent: ${accountId} with topics ${inboundTopicId}/${outboundTopicId}`\n );\n\n if (\n !isBytesMode &&\n this.stateManager &&\n !this.stateManager.getConnectionsManager()\n ) {\n try {\n const hcs10Client = new HCS10Client({\n network: hederaKit.network as 'mainnet' | 'testnet',\n operatorId: accountId,\n operatorPrivateKey: operatorPrivateKeyRef ?? '',\n logLevel: 'error',\n });\n\n if (hasInitializeConnectionsManager(this.stateManager)) {\n this.stateManager.initializeConnectionsManager(hcs10Client);\n } else {\n this.context.logger.warn(\n 'StateManager does not support connection manager initialization'\n );\n }\n this.context.logger.info(\n 'ConnectionsManager initialized in HCS10Plugin'\n );\n } catch (cmError) {\n this.context.logger.warn(\n 'Could not initialize ConnectionsManager:',\n cmError\n );\n }\n }\n\n this.initializeTools();\n this.context.logger.info('HCS-10 Plugin initialized successfully');\n } catch (error) {\n this.context.logger.error('Failed to initialize HCS-10 plugin:', error);\n }\n }\n\n private initializeTools(): void {\n if (!this.stateManager) {\n throw new Error('StateManager must be initialized before creating tools');\n }\n\n const hederaKit = this.context.config.hederaKit as HederaAgentKit;\n if (!hederaKit) {\n throw new Error('HederaKit not found in context config');\n }\n\n const hcs10Builder = new HCS10Builder(hederaKit, this.stateManager);\n\n this.tools = [\n new RegisterAgentTool({\n hederaKit: hederaKit,\n hcs10Builder: hcs10Builder,\n logger: this.context.logger,\n }),\n new FindRegistrationsTool({\n hederaKit: hederaKit,\n hcs10Builder: hcs10Builder,\n logger: this.context.logger,\n }),\n new RetrieveProfileTool({\n hederaKit: hederaKit,\n hcs10Builder: hcs10Builder,\n logger: this.context.logger,\n }),\n new InitiateConnectionTool({\n hederaKit: hederaKit,\n hcs10Builder: hcs10Builder,\n logger: this.context.logger,\n }),\n new ListConnectionsTool({\n hederaKit: hederaKit,\n hcs10Builder: hcs10Builder,\n logger: this.context.logger,\n }),\n new SendMessageToConnectionTool({\n hederaKit: hederaKit,\n hcs10Builder: hcs10Builder,\n logger: this.context.logger,\n }),\n new CheckMessagesTool({\n hederaKit: hederaKit,\n hcs10Builder: hcs10Builder,\n logger: this.context.logger,\n }),\n new ConnectionMonitorTool({\n hederaKit: hederaKit,\n hcs10Builder: hcs10Builder,\n logger: this.context.logger,\n }),\n new ManageConnectionRequestsTool({\n hederaKit: hederaKit,\n hcs10Builder: hcs10Builder,\n logger: this.context.logger,\n }),\n new AcceptConnectionRequestTool({\n hederaKit: hederaKit,\n hcs10Builder: hcs10Builder,\n logger: this.context.logger,\n }),\n new ListUnapprovedConnectionRequestsTool({\n hederaKit: hederaKit,\n hcs10Builder: hcs10Builder,\n logger: this.context.logger,\n }),\n ];\n }\n\n getTools(): HederaTool[] {\n return this.tools;\n }\n\n getStateManager(): IStateManager | undefined {\n return this.stateManager;\n }\n\n override async cleanup(): Promise<void> {\n this.tools = [];\n delete this.stateManager;\n if (this.context?.logger) {\n this.context.logger.info('HCS-10 Plugin cleaned up');\n }\n }\n}\n","import {\n GenericPluginContext,\n HederaTool,\n BasePlugin,\n HederaAgentKit,\n} from 'hedera-agent-kit';\nimport {\n HCS2Builder,\n CreateRegistryTool,\n RegisterEntryTool,\n UpdateEntryTool,\n DeleteEntryTool,\n MigrateRegistryTool,\n QueryRegistryTool,\n} from '@hashgraphonline/standards-agent-kit';\n\n/**\n * Plugin providing HCS-2 registry management tools\n */\nexport class HCS2Plugin extends BasePlugin {\n id = 'hcs-2';\n name = 'HCS-2 Plugin';\n description =\n 'HCS-2 registry management tools for decentralized registries on Hedera';\n version = '1.0.0';\n author = 'Hashgraph Online';\n namespace = 'hcs2';\n\n private tools: any[] = [];\n\n override async initialize(context: GenericPluginContext): Promise<void> {\n await super.initialize(context);\n\n const hederaKit = context.config.hederaKit as HederaAgentKit;\n if (!hederaKit) {\n this.context.logger.warn(\n 'HederaKit not found in context. HCS-2 tools will not be available.'\n );\n return;\n }\n\n try {\n this.initializeTools();\n\n this.context.logger.info(\n 'HCS-2 Plugin initialized successfully'\n );\n } catch (error) {\n this.context.logger.error(\n 'Failed to initialize HCS-2 plugin:',\n error\n );\n }\n }\n\n private initializeTools(): void {\n const hederaKit = this.context.config.hederaKit as HederaAgentKit;\n if (!hederaKit) {\n throw new Error('HederaKit not found in context config');\n }\n\n const hcs2Builder = new HCS2Builder(hederaKit);\n\n this.tools = [\n new CreateRegistryTool({\n hederaKit: hederaKit,\n hcs2Builder: hcs2Builder,\n logger: this.context.logger,\n }),\n new RegisterEntryTool({\n hederaKit: hederaKit,\n hcs2Builder: hcs2Builder,\n logger: this.context.logger,\n }),\n new UpdateEntryTool({\n hederaKit: hederaKit,\n hcs2Builder: hcs2Builder,\n logger: this.context.logger,\n }),\n new DeleteEntryTool({\n hederaKit: hederaKit,\n hcs2Builder: hcs2Builder,\n logger: this.context.logger,\n }),\n new MigrateRegistryTool({\n hederaKit: hederaKit,\n hcs2Builder: hcs2Builder,\n logger: this.context.logger,\n }),\n new QueryRegistryTool({\n hederaKit: hederaKit,\n hcs2Builder: hcs2Builder,\n logger: this.context.logger,\n }),\n ];\n }\n\n getTools(): HederaTool[] {\n return this.tools;\n }\n\n override async cleanup(): Promise<void> {\n this.tools = [];\n if (this.context?.logger) {\n this.context.logger.info('HCS-2 Plugin cleaned up');\n }\n }\n}\n","import type { FormFieldType, FieldOption } from './types';\nimport { Logger } from '@hashgraphonline/standards-sdk';\n\n/**\n * Field guidance configuration for providing contextual help and suggestions\n */\nexport interface FieldGuidance {\n /**\n * Suggestions to show as placeholder or examples\n */\n suggestions?: string[];\n\n /**\n * Predefined options for select fields\n */\n predefinedOptions?: FieldOption[];\n\n /**\n * Warning messages for specific patterns to avoid\n */\n warnings?: {\n pattern: RegExp;\n message: string;\n }[];\n\n /**\n * Validation rules specific to the field context\n */\n validationRules?: {\n /**\n * Patterns that should be rejected\n */\n rejectPatterns?: {\n pattern: RegExp;\n reason: string;\n }[];\n\n /**\n * Minimum quality requirements\n */\n qualityChecks?: {\n minNonTechnicalWords?: number;\n requireSpecificTerms?: string[];\n forbidTechnicalTerms?: string[];\n };\n };\n\n /**\n * Field type override for specific contexts\n */\n fieldTypeOverride?: FormFieldType;\n\n /**\n * Help text specific to the tool context\n */\n contextualHelpText?: string;\n}\n\n/**\n * Tool-specific field configurations\n */\nexport interface ToolFieldConfiguration {\n /**\n * Tool name or pattern to match\n */\n toolPattern: string | RegExp;\n\n /**\n * Field-specific guidance\n */\n fields: Record<string, FieldGuidance>;\n\n /**\n * Global guidance for all fields in this tool\n */\n globalGuidance?: {\n /**\n * General warnings to show\n */\n warnings?: string[];\n\n /**\n * Quality standards for this tool\n */\n qualityStandards?: string[];\n };\n}\n\n/**\n * Registry for field guidance configurations\n */\nclass FieldGuidanceRegistry {\n private configurations: ToolFieldConfiguration[] = [];\n private providers: Array<{\n id: string;\n priority: number;\n pattern: string | RegExp;\n provider: FieldGuidanceProvider;\n order: number;\n }> = [];\n private registerOrderCounter = 0;\n private logger: Logger;\n\n constructor() {\n this.logger = new Logger({ module: 'FieldGuidanceRegistry' });\n }\n\n /**\n * Register field guidance for a specific tool\n */\n registerToolConfiguration(config: ToolFieldConfiguration): void {\n this.configurations.push(config);\n }\n\n /**\n * Register a provider for dynamic field/global guidance\n */\n registerToolProvider(\n toolPattern: string | RegExp,\n provider: FieldGuidanceProvider,\n options?: { id?: string; priority?: number }\n ): string {\n const id = options?.id ?? `provider-${this.providers.length + 1}`;\n const priority = options?.priority ?? 0;\n if (this.providers.some((p) => p.id === id)) {\n this.logger.error('Duplicate provider id', { id });\n throw new Error('DUPLICATE_PROVIDER_ID');\n }\n this.providers.push({\n id,\n priority,\n pattern: toolPattern,\n provider,\n order: this.registerOrderCounter++,\n });\n return id;\n }\n\n /** Unregister a provider by id */\n unregisterProvider(id: string): void {\n this.providers = this.providers.filter((p) => p.id !== id);\n }\n\n /** List registered providers */\n listProviders(): Array<{\n id: string;\n priority: number;\n pattern: string | RegExp;\n }> {\n return this.providers.map(({ id, priority, pattern }) => ({\n id,\n priority,\n pattern,\n }));\n }\n\n /**\n * Get field guidance for a specific tool and field\n */\n getFieldGuidance(toolName: string, fieldName: string): FieldGuidance | null {\n if (process.env.CA_FORM_GUIDANCE_ENABLED === 'false') {\n return null;\n }\n for (const config of this.configurations) {\n const matches =\n typeof config.toolPattern === 'string'\n ? toolName.toLowerCase().includes(config.toolPattern.toLowerCase())\n : config.toolPattern.test(toolName);\n\n if (matches && config.fields[fieldName]) {\n const staticGuidance = config.fields[fieldName];\n const providers = this.pickMatchingProviders(toolName);\n if (providers.length === 0) return staticGuidance;\n let merged: FieldGuidance = { ...staticGuidance };\n for (const p of [...providers].reverse()) {\n const fromProvider = this.safeGetFieldGuidance(\n p,\n fieldName,\n toolName\n );\n if (fromProvider) {\n merged = this.mergeGuidance(merged, fromProvider);\n }\n }\n return merged;\n }\n }\n const providers = this.pickMatchingProviders(toolName);\n if (providers.length > 0) {\n let merged: FieldGuidance = {};\n for (const p of [...providers].reverse()) {\n const g = this.safeGetFieldGuidance(p, fieldName, toolName);\n if (g) merged = this.mergeGuidance(merged, g);\n }\n return Object.keys(merged).length > 0 ? merged : null;\n }\n return null;\n }\n\n /**\n * Get global guidance for a tool\n */\n getGlobalGuidance(\n toolName: string\n ): ToolFieldConfiguration['globalGuidance'] | null {\n if (process.env.CA_FORM_GUIDANCE_ENABLED === 'false') {\n return null;\n }\n for (const config of this.configurations) {\n const matches =\n typeof config.toolPattern === 'string'\n ? toolName.toLowerCase().includes(config.toolPattern.toLowerCase())\n : config.toolPattern.test(toolName);\n\n if (matches && config.globalGuidance) {\n const base = config.globalGuidance;\n const providers = this.pickMatchingProviders(toolName);\n if (providers.length === 0) return base;\n let mergedWarnings: string[] | undefined = base.warnings;\n let mergedQuality: string[] | undefined = base.qualityStandards;\n for (const p of [...providers].reverse()) {\n const fromProvider = this.safeGetGlobalGuidance(p, toolName);\n if (fromProvider) {\n mergedWarnings = fromProvider.warnings ?? mergedWarnings;\n mergedQuality = fromProvider.qualityStandards ?? mergedQuality;\n }\n }\n const result: NonNullable<ToolFieldConfiguration['globalGuidance']> =\n {};\n if (mergedWarnings !== undefined) result.warnings = mergedWarnings;\n if (mergedQuality !== undefined)\n result.qualityStandards = mergedQuality;\n return result;\n }\n }\n const providers = this.pickMatchingProviders(toolName);\n if (providers.length > 0) {\n let mergedWarnings: string[] | undefined;\n let mergedQuality: string[] | undefined;\n for (const p of [...providers].reverse()) {\n const g = this.safeGetGlobalGuidance(p, toolName);\n if (g) {\n mergedWarnings = g.warnings ?? mergedWarnings;\n mergedQuality = g.qualityStandards ?? mergedQuality;\n }\n }\n const result: NonNullable<ToolFieldConfiguration['globalGuidance']> = {};\n if (mergedWarnings !== undefined) result.warnings = mergedWarnings;\n if (mergedQuality !== undefined) result.qualityStandards = mergedQuality;\n return Object.keys(result).length > 0 ? result : null;\n }\n return null;\n }\n\n /**\n * Validate field value against guidance rules\n */\n validateFieldValue(\n toolName: string,\n fieldName: string,\n value: unknown\n ): {\n isValid: boolean;\n warnings: string[];\n errors: string[];\n } {\n const guidance = this.getFieldGuidance(toolName, fieldName);\n const warnings: string[] = [];\n const errors: string[] = [];\n\n if (!guidance || typeof value !== 'string') {\n return { isValid: true, warnings, errors };\n }\n\n if (guidance.warnings) {\n for (const warning of guidance.warnings) {\n if (warning.pattern.test(value)) {\n warnings.push(warning.message);\n }\n }\n }\n\n if (guidance.validationRules) {\n const { rejectPatterns, qualityChecks } = guidance.validationRules;\n\n if (rejectPatterns) {\n for (const reject of rejectPatterns) {\n if (reject.pattern.test(value)) {\n errors.push(`Rejected: ${reject.reason}`);\n }\n }\n }\n\n if (qualityChecks) {\n if (qualityChecks.forbidTechnicalTerms) {\n const lowerValue = value.toLowerCase();\n for (const term of qualityChecks.forbidTechnicalTerms) {\n if (lowerValue.includes(term.toLowerCase())) {\n errors.push(\n `Avoid technical terms like \"${term}\" in NFT metadata`\n );\n }\n }\n }\n\n if (qualityChecks.requireSpecificTerms) {\n const lowerValue = value.toLowerCase();\n const hasRequired = qualityChecks.requireSpecificTerms.some((term) =>\n lowerValue.includes(term.toLowerCase())\n );\n if (!hasRequired) {\n warnings.push(\n `Consider including terms like: ${qualityChecks.requireSpecificTerms.join(\n ', '\n )}`\n );\n }\n }\n\n if (qualityChecks.minNonTechnicalWords) {\n const words = value.split(/\\s+/).filter((word) => word.length > 2);\n if (words.length < qualityChecks.minNonTechnicalWords) {\n warnings.push(\n `Consider providing more descriptive content (at least ${qualityChecks.minNonTechnicalWords} meaningful words)`\n );\n }\n }\n }\n }\n\n return {\n isValid: errors.length === 0,\n warnings,\n errors,\n };\n }\n\n /**\n * Clear all configurations (useful for testing)\n */\n clear(): void {\n this.configurations = [];\n this.providers = [];\n this.registerOrderCounter = 0;\n }\n\n /** Choose matching provider by priority then last-in wins */\n private pickMatchingProviders(toolName: string): Array<{\n id: string;\n provider: FieldGuidanceProvider;\n priority: number;\n order: number;\n }> {\n const matches = this.providers.filter((p) =>\n typeof p.pattern === 'string'\n ? toolName.toLowerCase().includes((p.pattern as string).toLowerCase())\n : (p.pattern as RegExp).test(toolName)\n );\n const sorted = matches.sort((a, b) => {\n if (b.priority !== a.priority) return b.priority - a.priority;\n return b.order - a.order; // last-in wins when equal priority\n });\n return sorted.map((m) => ({\n id: m.id,\n provider: m.provider,\n priority: m.priority,\n order: m.order,\n }));\n }\n\n private safeGetFieldGuidance(\n winner: { id: string; provider: FieldGuidanceProvider },\n fieldName: string,\n toolName: string\n ): FieldGuidance | null {\n try {\n return winner.provider.getFieldGuidance(fieldName, { toolName }) ?? null;\n } catch (err) {\n this.logger.warn('Provider getFieldGuidance failed', {\n id: winner.id,\n err,\n });\n return null;\n }\n }\n\n private safeGetGlobalGuidance(\n winner: { id: string; provider: FieldGuidanceProvider },\n toolName: string\n ): ToolFieldConfiguration['globalGuidance'] | null {\n try {\n return winner.provider.getGlobalGuidance?.(toolName) ?? null;\n } catch (err) {\n this.logger.warn('Provider getGlobalGuidance failed', {\n id: winner.id,\n err,\n });\n return null;\n }\n }\n\n private mergeGuidance(\n base: FieldGuidance,\n over: FieldGuidance\n ): FieldGuidance {\n const out: FieldGuidance = {};\n const suggestions = over.suggestions ?? base.suggestions;\n if (suggestions !== undefined) out.suggestions = suggestions;\n const predefinedOptions = over.predefinedOptions ?? base.predefinedOptions;\n if (predefinedOptions !== undefined)\n out.predefinedOptions = predefinedOptions;\n const warnings = over.warnings ?? base.warnings;\n if (warnings !== undefined) out.warnings = warnings;\n const validationRules = over.validationRules ?? base.validationRules;\n if (validationRules !== undefined) out.validationRules = validationRules;\n const fieldTypeOverride = over.fieldTypeOverride ?? base.fieldTypeOverride;\n if (fieldTypeOverride !== undefined)\n out.fieldTypeOverride = fieldTypeOverride;\n const contextualHelpText =\n over.contextualHelpText ?? base.contextualHelpText;\n if (contextualHelpText !== undefined)\n out.contextualHelpText = contextualHelpText;\n return out;\n }\n}\n\nexport const fieldGuidanceRegistry = new FieldGuidanceRegistry();\n\n/**\n * Provider interface (optional, for dynamic guidance)\n */\nexport interface FieldGuidanceProvider {\n getFieldGuidance(\n fieldName: string,\n ctx: { toolName: string }\n ): FieldGuidance | null;\n getGlobalGuidance?(\n toolName: string\n ): ToolFieldConfiguration['globalGuidance'] | null;\n}\n","import {\n GenericPluginContext,\n HederaTool,\n BasePlugin,\n HederaAgentKit,\n} from 'hedera-agent-kit';\nimport {\n InscriberBuilder,\n InscribeFromUrlTool,\n InscribeFromFileTool,\n InscribeFromBufferTool,\n InscribeHashinalTool,\n RetrieveInscriptionTool,\n} from '@hashgraphonline/standards-agent-kit';\nimport { fieldGuidanceRegistry, type FieldGuidance } from '../../forms/field-guidance-registry';\n\n/**\n * Plugin providing content inscription tools for Hedera\n */\nexport class InscribePlugin extends BasePlugin {\n id = 'inscribe';\n name = 'Inscribe Plugin';\n description =\n 'Content inscription tools for storing data on Hedera Consensus Service';\n version = '1.0.0';\n author = 'Hashgraph Online';\n namespace = 'inscribe';\n\n private tools: any[] = [];\n private providerId: string | null = null;\n\n override async initialize(context: GenericPluginContext): Promise<void> {\n await super.initialize(context);\n\n const hederaKit = context.config.hederaKit as HederaAgentKit;\n if (!hederaKit) {\n this.context.logger.warn(\n 'HederaKit not found in context. Inscription tools will not be available.'\n );\n return;\n }\n\n try {\n this.initializeTools();\n\n try {\n const provider = {\n getFieldGuidance: (fieldName: string): FieldGuidance | null => {\n if (fieldName === 'name') {\n return {\n suggestions: [\n 'Sunset Landscape #42',\n 'Digital Abstract Art',\n ],\n contextualHelpText:\n 'Create a distinctive name that collectors will find appealing',\n };\n }\n if (fieldName === 'description') {\n return {\n fieldTypeOverride: 'textarea',\n suggestions: ['A beautiful piece representing...'],\n };\n }\n return null;\n },\n getGlobalGuidance: () => ({\n qualityStandards: [\n 'Use meaningful names that describe the artwork or content',\n ],\n }),\n };\n this.providerId = fieldGuidanceRegistry.registerToolProvider(\n /hashinal/i,\n provider,\n { id: 'inscribe:hashinal:provider', priority: 1 }\n );\n } catch (e) {\n this.context.logger.warn('Could not register Inscribe field guidance provider');\n }\n\n this.context.logger.info(\n 'Inscribe Plugin initialized successfully'\n );\n } catch (error) {\n this.context.logger.error(\n 'Failed to initialize Inscribe plugin:',\n error\n );\n }\n }\n\n private initializeTools(): void {\n const hederaKit = this.context.config.hederaKit as HederaAgentKit;\n if (!hederaKit) {\n throw new Error('HederaKit not found in context config');\n }\n\n const inscriberBuilder = new InscriberBuilder(hederaKit);\n\n this.tools = [\n new InscribeFromUrlTool({\n hederaKit: hederaKit,\n inscriberBuilder: inscriberBuilder,\n logger: this.context.logger,\n }),\n new InscribeFromFileTool({\n hederaKit: hederaKit,\n inscriberBuilder: inscriberBuilder,\n logger: this.context.logger,\n }),\n new InscribeFromBufferTool({\n hederaKit: hederaKit,\n inscriberBuilder: inscriberBuilder,\n logger: this.context.logger,\n }),\n new InscribeHashinalTool({\n hederaKit: hederaKit,\n inscriberBuilder: inscriberBuilder,\n logger: this.context.logger,\n }),\n new RetrieveInscriptionTool({\n hederaKit: hederaKit,\n inscriberBuilder: inscriberBuilder,\n logger: this.context.logger,\n }),\n ];\n }\n\n getTools(): HederaTool[] {\n return this.tools;\n }\n\n override async cleanup(): Promise<void> {\n this.tools = [];\n if (this.providerId) {\n try {\n fieldGuidanceRegistry.unregisterProvider(this.providerId);\n } catch {}\n this.providerId = null;\n }\n if (this.context?.logger) {\n this.context.logger.info('Inscribe Plugin cleaned up');\n }\n }\n}\n","import { AccountId, Hbar, TransferTransaction } from '@hashgraph/sdk';\nimport BigNumber from 'bignumber.js';\nimport { HederaAgentKit, BaseServiceBuilder } from 'hedera-agent-kit';\nimport { HbarTransferParams } from './types';\n\n/**\n * Custom AccountBuilder that properly handles HBAR decimal conversion\n */\nexport class AccountBuilder extends BaseServiceBuilder {\n constructor(hederaKit: HederaAgentKit) {\n super(hederaKit);\n }\n\n /**\n * Transfers HBAR between accounts with proper decimal handling\n */\n public transferHbar(\n params: HbarTransferParams,\n isUserInitiated: boolean = true\n ): this {\n this.clearNotes();\n const transaction = new TransferTransaction();\n\n if (!params.transfers || params.transfers.length === 0) {\n throw new Error('HbarTransferParams must include at least one transfer.');\n }\n\n let netZeroInTinybars = new BigNumber(0);\n let userTransferProcessedForScheduling = false;\n\n if (\n isUserInitiated &&\n this.kit.userAccountId &&\n (this.kit.operationalMode as string) === 'provideBytes' &&\n params.transfers.length === 1\n ) {\n const receiverTransfer = params.transfers[0];\n const amountValue =\n typeof receiverTransfer.amount === 'string' ||\n typeof receiverTransfer.amount === 'number'\n ? receiverTransfer.amount\n : receiverTransfer.amount.toString();\n\n const amountBigNum = new BigNumber(amountValue);\n\n if (amountBigNum.isPositive()) {\n const recipientAccountId =\n typeof receiverTransfer.accountId === 'string'\n ? AccountId.fromString(receiverTransfer.accountId)\n : receiverTransfer.accountId;\n\n const roundedAmount = amountBigNum.toFixed(8, BigNumber.ROUND_DOWN);\n const sdkHbarAmount = Hbar.fromString(roundedAmount);\n\n this.logger.info(\n `[AccountBuilder.transferHbar] Configuring user-initiated scheduled transfer: ${sdkHbarAmount.toString()} from ${\n this.kit.userAccountId\n } to ${recipientAccountId.toString()}`\n );\n\n this.addNote(\n `Configured HBAR transfer from your account (${\n this.kit.userAccountId\n }) to ${recipientAccountId.toString()} for ${sdkHbarAmount.toString()}.`\n );\n\n transaction.addHbarTransfer(recipientAccountId, sdkHbarAmount);\n transaction.addHbarTransfer(\n AccountId.fromString(this.kit.userAccountId),\n sdkHbarAmount.negated()\n );\n\n userTransferProcessedForScheduling = true;\n }\n }\n\n if (!userTransferProcessedForScheduling) {\n const processedTransfers: Array<{\n accountId: AccountId;\n amount: BigNumber;\n hbar: Hbar;\n }> = [];\n\n for (const transferInput of params.transfers) {\n const accountId =\n typeof transferInput.accountId === 'string'\n ? AccountId.fromString(transferInput.accountId)\n : transferInput.accountId;\n\n const amountValue =\n typeof transferInput.amount === 'string' ||\n typeof transferInput.amount === 'number'\n ? transferInput.amount\n : transferInput.amount.toString();\n\n const amountBigNum = new BigNumber(amountValue);\n const roundedAmount = amountBigNum.toFixed(8, BigNumber.ROUND_DOWN);\n\n this.logger.info(\n `Processing transfer: ${amountValue} HBAR (rounded to ${roundedAmount}) for account ${accountId.toString()}`\n );\n\n const sdkHbarAmount = Hbar.fromString(roundedAmount);\n processedTransfers.push({\n accountId,\n amount: amountBigNum,\n hbar: sdkHbarAmount,\n });\n\n const tinybarsContribution = sdkHbarAmount.toTinybars();\n netZeroInTinybars = netZeroInTinybars.plus(\n tinybarsContribution.toString()\n );\n }\n\n if (!netZeroInTinybars.isZero()) {\n this.logger.warn(\n `Transfer sum not zero: ${netZeroInTinybars.toString()} tinybars off. Adjusting last transfer.`\n );\n\n if (processedTransfers.length > 0) {\n const lastTransfer =\n processedTransfers[processedTransfers.length - 1];\n const adjustment = netZeroInTinybars.dividedBy(-100000000);\n const adjustedAmount = lastTransfer.amount.plus(adjustment);\n const adjustedRounded = adjustedAmount.toFixed(\n 8,\n BigNumber.ROUND_DOWN\n );\n lastTransfer.hbar = Hbar.fromString(adjustedRounded);\n\n this.logger.info(\n `Adjusted last transfer for ${lastTransfer.accountId.toString()} to ${adjustedRounded} HBAR`\n );\n }\n }\n\n for (const transfer of processedTransfers) {\n transaction.addHbarTransfer(transfer.accountId, transfer.hbar);\n }\n }\n\n if (typeof params.memo !== 'undefined') {\n if (params.memo === null) {\n this.logger.warn('Received null for memo in transferHbar.');\n } else {\n transaction.setTransactionMemo(params.memo);\n }\n }\n\n this.setCurrentTransaction(transaction);\n return this;\n }\n}\n","import { z } from 'zod';\nimport { HbarTransferParams } from './types';\nimport { AccountBuilder } from './AccountBuilder';\nimport { BaseHederaTransactionTool, BaseServiceBuilder } from 'hedera-agent-kit';\n\nconst HbarTransferInputSchema = z.object({\n accountId: z\n .string()\n .describe('Account ID for the transfer (e.g., \"0.0.xxxx\").'),\n amount: z\n .union([z.number(), z.string()])\n .describe(\n 'HBAR amount in decimal format (e.g., 1 for 1 HBAR, 0.5 for 0.5 HBAR). Positive for credit, negative for debit. DO NOT multiply by 10^8 for tinybars - just use the HBAR amount directly.'\n ),\n});\n\nconst TransferHbarZodSchemaCore = z.object({\n transfers: z\n .array(HbarTransferInputSchema)\n .min(1)\n .describe(\n 'Array of transfers. For simple transfers from your operator account, just include the recipient with positive amount: [{accountId: \"0.0.800\", amount: 1}]. For complex multi-party transfers, include all parties with negative amounts for senders and positive for receivers.'\n ),\n memo: z.string().optional().describe('Optional. Memo for the transaction.'),\n});\n\n/**\n * A Hedera transaction tool for transferring HBAR between accounts.\n * Supports single and multi-party transfers with automatic balance validation.\n * Extends BaseHederaTransactionTool to handle HBAR transfer transactions on the Hedera Hashgraph.\n */\nexport class TransferHbarTool extends BaseHederaTransactionTool<\n typeof TransferHbarZodSchemaCore\n> {\n name = 'hedera-account-transfer-hbar-v2';\n description =\n 'PRIMARY TOOL FOR HBAR TRANSFERS: Transfers HBAR between accounts. For simple transfers from the operator account, just specify the recipient with a positive amount (e.g., [{accountId: \"0.0.800\", amount: 1}] to send 1 HBAR to 0.0.800). The sender will be automatically added. For multi-party transfers (e.g., \"A sends 5 HBAR to C and B sends 3 HBAR to C\"), include ALL transfers with their amounts (negative for senders, positive for receivers).';\n specificInputSchema = TransferHbarZodSchemaCore;\n namespace = 'account';\n\n\n /**\n * Creates and returns the service builder for account operations.\n * \n * @returns BaseServiceBuilder instance configured for account operations\n */\n protected getServiceBuilder(): BaseServiceBuilder {\n return new AccountBuilder(this.hederaKit) as BaseServiceBuilder;\n }\n\n /**\n * Executes the HBAR transfer using the provided builder and arguments.\n * Validates that all transfers sum to zero before execution.\n * \n * @param builder - The service builder instance for executing transactions\n * @param specificArgs - The validated transfer parameters including transfers array and optional memo\n * @returns Promise that resolves when the transfer is complete\n */\n protected async callBuilderMethod(\n builder: BaseServiceBuilder,\n specificArgs: z.infer<typeof TransferHbarZodSchemaCore>\n ): Promise<void> {\n await (builder as AccountBuilder).transferHbar(\n specificArgs as unknown as HbarTransferParams\n );\n }\n}","import { StructuredTool } from '@langchain/core/tools';\nimport { z } from 'zod';\nimport { HederaAgentKit } from 'hedera-agent-kit';\nimport { Logger } from '@hashgraphonline/standards-sdk';\n\ninterface TokenInfo {\n decimals: number;\n [key: string]: unknown;\n}\n\ninterface ToolWithCall {\n _call(input: unknown): Promise<string>;\n}\n\ninterface AgentKitWithMirrorNode {\n mirrorNode?: {\n getTokenInfo(tokenId: string): Promise<TokenInfo>;\n };\n network: string;\n}\n\nexport class AirdropToolWrapper extends StructuredTool {\n name = 'hedera-hts-airdrop-token';\n description =\n 'Airdrops fungible tokens to multiple recipients. Automatically converts human-readable amounts to smallest units based on token decimals.';\n\n schema = z.object({\n tokenId: z\n .string()\n .describe('The ID of the fungible token to airdrop (e.g., \"0.0.yyyy\").'),\n recipients: z\n .array(\n z.object({\n accountId: z\n .string()\n .describe('Recipient account ID (e.g., \"0.0.xxxx\").'),\n amount: z\n .union([z.number(), z.string()])\n .describe(\n 'Amount in human-readable format (e.g., \"10\" for 10 tokens).'\n ),\n })\n )\n .min(1)\n .describe('Array of recipient objects, each with accountId and amount.'),\n memo: z.string().optional().describe('Optional. Memo for the transaction.'),\n });\n\n private originalTool: StructuredTool & ToolWithCall;\n private agentKit: HederaAgentKit & AgentKitWithMirrorNode;\n private logger: Logger;\n\n constructor(originalTool: StructuredTool, agentKit: unknown) {\n super();\n this.originalTool = originalTool as StructuredTool & ToolWithCall;\n this.agentKit = agentKit as HederaAgentKit & AgentKitWithMirrorNode;\n this.logger = new Logger({ module: 'AirdropToolWrapper' });\n }\n\n async _call(input: z.infer<typeof this.schema>): Promise<string> {\n try {\n this.logger.info(\n `Processing airdrop request for token ${input.tokenId} with ${input.recipients.length} recipients`\n );\n\n const tokenInfo = await this.getTokenInfo(input.tokenId);\n const decimals = tokenInfo.decimals || 0;\n\n this.logger.info(`Token ${input.tokenId} has ${decimals} decimal places`);\n\n const convertedRecipients = input.recipients.map((recipient) => {\n const humanAmount =\n typeof recipient.amount === 'string'\n ? parseFloat(recipient.amount)\n : recipient.amount;\n const smallestUnitAmount = this.convertToSmallestUnits(\n humanAmount,\n decimals\n );\n\n this.logger.info(\n `Converting amount for ${recipient.accountId}: ${humanAmount} tokens → ${smallestUnitAmount} smallest units`\n );\n\n return {\n ...recipient,\n amount: smallestUnitAmount.toString(),\n };\n });\n\n const convertedInput = {\n ...input,\n recipients: convertedRecipients,\n };\n\n this.logger.info(`Calling original airdrop tool with converted amounts`);\n return await this.originalTool._call(convertedInput);\n } catch (error) {\n this.logger.error('Error in airdrop tool wrapper:', error);\n throw error;\n }\n }\n\n private convertToSmallestUnits(amount: number, decimals: number): number {\n return Math.floor(amount * Math.pow(10, decimals));\n }\n\n private async getTokenInfo(tokenId: string): Promise<TokenInfo> {\n try {\n return await this.queryTokenInfo(tokenId);\n } catch (error) {\n throw error;\n }\n }\n\n private async queryTokenInfo(tokenId: string): Promise<TokenInfo> {\n try {\n this.logger.info('Querying token info using mirror node');\n const mirrorNode = this.agentKit.mirrorNode;\n if (!mirrorNode) {\n this.logger.info(\n 'MirrorNode not found in agentKit, attempting to access via fetch'\n );\n const network = this.agentKit.network || 'testnet';\n const mirrorNodeUrl =\n network === 'mainnet'\n ? 'https://mainnet.mirrornode.hedera.com'\n : 'https://testnet.mirrornode.hedera.com';\n\n const response = await fetch(\n `${mirrorNodeUrl}/api/v1/tokens/${tokenId}`\n );\n if (response.ok) {\n const tokenData = (await response.json()) as Record<string, unknown>;\n const decimals = parseInt(String(tokenData.decimals || '0'));\n this.logger.info(\n `Token ${tokenId} found with ${decimals} decimals via API`\n );\n return { ...tokenData, decimals };\n }\n } else {\n const tokenData = await mirrorNode.getTokenInfo(tokenId);\n\n if (tokenData && typeof tokenData.decimals !== 'undefined') {\n const decimals = parseInt(tokenData.decimals.toString()) || 0;\n this.logger.info(`Token ${tokenId} found with ${decimals} decimals`);\n return { ...tokenData, decimals };\n }\n }\n\n throw new Error(`Token data not found or missing decimals field`);\n } catch (error) {\n this.logger.warn(`Failed to query token info for ${tokenId}:`, error);\n\n this.logger.info(\n 'Falling back to assumed 0 decimal places (smallest units)'\n );\n return { decimals: 0 };\n }\n }\n}\n","import {\n GenericPluginContext,\n HederaTool,\n BasePlugin,\n HederaAgentKit,\n HederaAirdropTokenTool,\n} from 'hedera-agent-kit';\nimport { TransferHbarTool } from './TransferHbarTool';\nimport { AirdropToolWrapper } from './AirdropToolWrapper';\nimport { StructuredTool } from '@langchain/core/tools';\n\nexport class HbarPlugin extends BasePlugin {\n id = 'hbar';\n name = 'HBAR Plugin';\n description =\n 'HBAR operations: transfer tool with robust decimal handling and compatibility with airdrop improvements';\n version = '1.0.0';\n author = 'Hashgraph Online';\n namespace = 'account';\n\n private tools: (HederaTool | AirdropToolWrapper)[] = [];\n private originalAirdropTool: StructuredTool | null = null;\n\n override async initialize(context: GenericPluginContext): Promise<void> {\n await super.initialize(context);\n\n const hederaKit = context.config.hederaKit as HederaAgentKit;\n if (!hederaKit) {\n this.context.logger.warn(\n 'HederaKit not found in context. HBAR tools will not be available.'\n );\n return;\n }\n\n try {\n this.initializeTools();\n\n this.context.logger.info('HBAR Plugin initialized successfully');\n } catch (error) {\n this.context.logger.error('Failed to initialize HBAR plugin:', error);\n }\n }\n\n private initializeTools(): void {\n const hederaKit = this.context.config.hederaKit as HederaAgentKit;\n if (!hederaKit) {\n throw new Error('HederaKit not found in context config');\n }\n\n const transfer = new TransferHbarTool({\n hederaKit: hederaKit,\n logger: this.context.logger,\n });\n\n this.tools = [transfer];\n\n try {\n this.context.logger.info(\n 'Creating wrapper for passed original airdrop tool'\n );\n\n const airdropTool = new HederaAirdropTokenTool({\n hederaKit: hederaKit,\n logger: this.context.logger,\n });\n const wrappedAirdropTool = new AirdropToolWrapper(airdropTool, hederaKit);\n this.tools.push(wrappedAirdropTool);\n this.context.logger.info('Added wrapped airdrop tool to HBAR Plugin');\n } catch (error) {\n this.context.logger.error('Error creating airdrop tool wrapper:', error);\n }\n\n this.context.logger.info(\n `HBAR Plugin tools initialized with ${this.tools.length} tools`\n );\n }\n\n override getTools(): HederaTool[] {\n return this.tools as unknown as HederaTool[];\n }\n\n async shutdown(): Promise<void> {\n this.tools = [];\n }\n}\n","export const NOT_FOUND_STATUS = 404;\r\nexport const BAD_REQUEST_STATUS = 400;\r\nexport const GATEWAY_STAMP_ERROR_MESSAGE =\r\n \"Endpoint not found. If using Swarm Gateway, postage stamp management endpoints are not available.\";\r\nexport const GATEWAY_TAG_ERROR_MESSAGE =\r\n \"If using Swarm Gateway, tag endpoints are not available.\";\r\nexport const POSTAGE_CREATE_TIMEOUT_MESSAGE =\r\n \"Purchase of postage batch is in progress, it may take a few minutes. Please list you batches after a few minutes to find it.\";\r\nexport const CALL_TIMEOUT = 30000;\r\nexport const DEFAULT_DEFERRED_UPLOAD_SIZE_THRESHOLD_MB = 5;\r\nexport const DEFAULT_GATEWAY_BATCH_ID =\r\n \"0000000000000000000000000000000000000000000000000000000000000000\";\r\n","import { Bee, PostageBatch } from \"@ethersphere/bee-js\";\r\nimport { PostageBatchCurated, PostageBatchSummary } from \"./model\";\r\nimport { SwarmConfig } from \"./config\";\r\nimport { DEFAULT_GATEWAY_BATCH_ID, NOT_FOUND_STATUS } from \"./constants\";\r\n\r\nexport interface ToolResponse {\r\n [x: string]: unknown;\r\n tools?: { [x: string]: unknown; name: string /* other properties */ };\r\n _meta?: { [x: string]: unknown };\r\n}\r\n\r\nexport function hexToBytes(hex: string): Uint8Array {\r\n const bytes = new Uint8Array(hex.length / 2);\r\n for (let i = 0; i < hex.length; i += 2) {\r\n bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);\r\n }\r\n return bytes;\r\n}\r\n\r\nexport const getBatchSummary = (\r\n batch: PostageBatch | PostageBatchCurated\r\n): PostageBatchSummary => ({\r\n stampID:\r\n typeof batch.batchID === \"string\" ? batch.batchID : batch.batchID.toHex(),\r\n usage: batch.usageText,\r\n capacity: `${batch.remainingSize.toFormattedString()} remaining out of ${batch.size.toFormattedString()}`,\r\n immutable: batch.immutableFlag,\r\n ttl: `${batch.duration.represent()} (${batch.duration\r\n .toEndDate()\r\n .toDateString()})`,\r\n});\r\n\r\nexport const getResponseWithStructuredContent = <T>(data: T): ToolResponse => ({\r\n content: [\r\n {\r\n type: \"text\",\r\n text: JSON.stringify(data, null, 2),\r\n },\r\n ],\r\n structuredContent: data,\r\n});\r\n\r\nexport const errorHasStatus = (error: unknown, status: number) => {\r\n if (typeof error === \"object\" && error !== null && \"status\" in error) {\r\n return error.status === status;\r\n }\r\n\r\n return false;\r\n};\r\n\r\nexport const getErrorMessage = (error: unknown) => {\r\n if (\r\n typeof error === \"object\" &&\r\n error !== null &&\r\n \"responseBody\" in error &&\r\n typeof error.responseBody === \"object\" &&\r\n error.responseBody !== null &&\r\n \"message\" in error.responseBody\r\n ) {\r\n return error.responseBody.message as string;\r\n }\r\n\r\n return \"\";\r\n};\r\n\r\nexport const runWithTimeout = async <T>(\r\n asyncAction: Promise<T>,\r\n timeout: number\r\n): Promise<[unknown, boolean]> => {\r\n let hasTimedOut = false;\r\n\r\n const timeoutPromise = new Promi